OSDN Git Service

gitattirubes test 2
[handbrake-jp/handbrake-jp.git] / libhb / muxavi.c
1 /* $Id: muxavi.c,v 1.10 2005/03/30 18:17:29 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "hb.h"
8 #include "hbffmpeg.h"
9
10 #define AVIF_HASINDEX  0x10
11 #define AVIIF_KEYFRAME 0x10
12 #define FOURCC(a)      ((a[3]<<24)|(a[2]<<16)|(a[1]<<8)|a[0])
13
14 /* Structures definitions */
15 typedef struct __attribute__((__packed__))
16 {
17     uint32_t FourCC;
18     uint32_t BytesCount;
19     uint32_t MicroSecPerFrame;
20     uint32_t MaxBytesPerSec;
21     uint32_t PaddingGranularity;
22     uint32_t Flags;
23     uint32_t TotalFrames;
24     uint32_t InitialFrames;
25     uint32_t Streams;
26     uint32_t SuggestedBufferSize;
27     uint32_t Width;
28     uint32_t Height;
29     uint32_t Reserved[4];
30
31 } hb_avi_main_header_t;
32
33 typedef struct __attribute__((__packed__))
34 {
35     uint32_t FourCC;
36     uint32_t BytesCount;
37     uint32_t Type;
38     uint32_t Handler;
39     uint32_t Flags;
40     uint16_t Priority;
41     uint16_t Language;
42     uint32_t InitialFrames;
43     uint32_t Scale;
44     uint32_t Rate;
45     uint32_t Start;
46     uint32_t Length;
47     uint32_t SuggestedBufferSize;
48     uint32_t Quality;
49     uint32_t SampleSize;
50     int16_t  Left;
51     int16_t  Top;
52     int16_t  Right;
53     int16_t  Bottom;
54
55 } hb_avi_stream_header_t;
56
57 typedef struct __attribute__((__packed__))
58 {
59     uint32_t FourCC;
60     uint32_t BytesCount;
61     uint32_t VideoFormatToken;
62     uint32_t VideoStandard;
63     uint32_t dwVerticalRefreshRate;
64     uint32_t dwHTotalInT;
65     uint32_t dwVTotalInLines;
66     uint16_t dwFrameAspectRatioDen;
67     uint16_t dwFrameAspectRatioNum;
68     uint32_t dwFrameWidthInPixels;
69     uint32_t dwFrameHeightInLines;
70     uint32_t nbFieldPerFrame;
71     uint32_t CompressedBMHeight;
72     uint32_t CompressedBMWidth;
73     uint32_t ValidBMHeight;
74     uint32_t ValidBMWidth;
75     uint32_t ValidBMXOffset;
76     uint32_t ValidBMYOffset;
77     uint32_t VideoXOffsetInT;
78     uint32_t VideoYValidStartLine;
79
80 } hb_avi_vprp_info_t;
81
82 typedef struct __attribute__((__packed__))
83 {
84     uint32_t FourCC;
85     uint32_t BytesCount;
86     uint32_t Size;
87     uint32_t Width;
88     uint32_t Height;
89     uint16_t Planes;
90     uint16_t BitCount;
91     uint32_t Compression;
92     uint32_t SizeImage;
93     uint32_t XPelsPerMeter;
94     uint32_t YPelsPerMeter;
95     uint32_t ClrUsed;
96     uint32_t ClrImportant;
97
98 } hb_bitmap_info_t;
99
100 typedef struct __attribute__((__packed__))
101 {
102     uint32_t FourCC;
103     uint32_t BytesCount;
104     uint16_t FormatTag;
105     uint16_t Channels;
106     uint32_t SamplesPerSec;
107     uint32_t AvgBytesPerSec;
108     uint16_t BlockAlign;
109     uint16_t BitsPerSample;
110     uint16_t Size;
111
112 } hb_wave_formatex_t;
113
114 typedef struct __attribute__((__packed__))
115 {
116     uint16_t Id;
117     uint32_t Flags;
118     uint16_t BlockSize;
119     uint16_t FramesPerBlock;
120     uint16_t CodecDelay;
121
122 } hb_wave_mp3_t;
123
124 static void WriteBuffer( FILE * file, hb_buffer_t * buf )
125 {
126     fwrite( buf->data, buf->size, 1, file );
127 }
128
129 /* Little-endian write routines */
130
131 static void WriteInt8( FILE * file, uint8_t val )
132 {
133     fputc( val, file );
134 }
135
136 static void WriteInt16( FILE * file, uint16_t val )
137 {
138     fputc( val & 0xFF, file );
139     fputc( val >> 8, file );
140 }
141
142 static void WriteInt32( FILE * file, uint32_t val )
143 {
144     fputc( val & 0xFF, file );
145     fputc( ( val >> 8 ) & 0xFF, file );
146     fputc( ( val >> 16 ) & 0xFF, file );
147     fputc( val >> 24, file );
148 }
149
150 static void WriteBitmapInfo( FILE * file, hb_bitmap_info_t * info )
151 {
152     WriteInt32( file, info->FourCC );
153     WriteInt32( file, info->BytesCount );
154     WriteInt32( file, info->Size );
155     WriteInt32( file, info->Width );
156     WriteInt32( file, info->Height );
157     WriteInt16( file, info->Planes );
158     WriteInt16( file, info->BitCount );
159     WriteInt32( file, info->Compression );
160     WriteInt32( file, info->SizeImage );
161     WriteInt32( file, info->XPelsPerMeter );
162     WriteInt32( file, info->YPelsPerMeter );
163     WriteInt32( file, info->ClrUsed );
164     WriteInt32( file, info->ClrImportant );
165 }
166
167 static void WriteWaveFormatEx( FILE * file, hb_wave_formatex_t * format )
168 {
169     WriteInt32( file, format->FourCC );
170     WriteInt32( file, format->BytesCount );
171     WriteInt16( file, format->FormatTag );
172     WriteInt16( file, format->Channels );
173     WriteInt32( file, format->SamplesPerSec );
174     WriteInt32( file, format->AvgBytesPerSec );
175     WriteInt16( file, format->BlockAlign );
176     WriteInt16( file, format->BitsPerSample );
177     WriteInt16( file, format->Size );
178 }
179
180 static void WriteWaveMp3( FILE * file, hb_wave_mp3_t * mp3 )
181 {
182     WriteInt16( file, mp3->Id );
183     WriteInt32( file, mp3->Flags );
184     WriteInt16( file, mp3->BlockSize );
185     WriteInt16( file, mp3->FramesPerBlock );
186     WriteInt16( file, mp3->CodecDelay );
187 }
188
189 static void WriteMainHeader( FILE * file, hb_avi_main_header_t * header )
190 {
191     WriteInt32( file, header->FourCC );
192     WriteInt32( file, header->BytesCount );
193     WriteInt32( file, header->MicroSecPerFrame );
194     WriteInt32( file, header->MaxBytesPerSec );
195     WriteInt32( file, header->PaddingGranularity );
196     WriteInt32( file, header->Flags );
197     WriteInt32( file, header->TotalFrames );
198     WriteInt32( file, header->InitialFrames );
199     WriteInt32( file, header->Streams );
200     WriteInt32( file, header->SuggestedBufferSize );
201     WriteInt32( file, header->Width );
202     WriteInt32( file, header->Height );
203     WriteInt32( file, header->Reserved[0] );
204     WriteInt32( file, header->Reserved[1] );
205     WriteInt32( file, header->Reserved[2] );
206     WriteInt32( file, header->Reserved[3] );
207 }
208
209 static void WriteStreamHeader( FILE * file, hb_avi_stream_header_t * header )
210 {
211     WriteInt32( file, header->FourCC );
212     WriteInt32( file, header->BytesCount );
213     WriteInt32( file, header->Type );
214     WriteInt32( file, header->Handler );
215     WriteInt32( file, header->Flags );
216     WriteInt16( file, header->Priority );
217     WriteInt16( file, header->Language );
218     WriteInt32( file, header->InitialFrames );
219     WriteInt32( file, header->Scale );
220     WriteInt32( file, header->Rate );
221     WriteInt32( file, header->Start );
222     WriteInt32( file, header->Length );
223     WriteInt32( file, header->SuggestedBufferSize );
224     WriteInt32( file, header->Quality );
225     WriteInt32( file, header->SampleSize );
226     WriteInt16( file, header->Left );
227     WriteInt16( file, header->Top );
228     WriteInt16( file, header->Right );
229     WriteInt16( file, header->Bottom );
230 }
231
232 static void WriteVprpInfo( FILE * file, hb_avi_vprp_info_t * info )
233 {
234     WriteInt32( file, info->FourCC );
235     WriteInt32( file, info->BytesCount );
236     WriteInt32( file, info->VideoFormatToken );
237     WriteInt32( file, info->VideoStandard );
238     WriteInt32( file, info->dwVerticalRefreshRate );
239     WriteInt32( file, info->dwHTotalInT );
240     WriteInt32( file, info->dwVTotalInLines );
241     WriteInt16( file, info->dwFrameAspectRatioDen );
242     WriteInt16( file, info->dwFrameAspectRatioNum );
243     WriteInt32( file, info->dwFrameWidthInPixels );
244     WriteInt32( file, info->dwFrameHeightInLines );
245     WriteInt32( file, info->nbFieldPerFrame );
246     WriteInt32( file, info->CompressedBMHeight );
247     WriteInt32( file, info->CompressedBMWidth );
248     WriteInt32( file, info->ValidBMHeight );
249     WriteInt32( file, info->ValidBMWidth );
250     WriteInt32( file, info->ValidBMXOffset );
251     WriteInt32( file, info->ValidBMYOffset );
252     WriteInt32( file, info->VideoXOffsetInT );
253     WriteInt32( file, info->VideoYValidStartLine );
254 }
255
256 static void IndexAddInt32( hb_buffer_t * b, uint32_t val )
257 {
258     if( b->size + 16 > b->alloc )
259     {
260         hb_log( "muxavi: reallocing index (%d MB)",
261                 1 + b->alloc / 1024 / 1024 );
262         hb_buffer_realloc( b, b->alloc + 1024 * 1024 );
263     }
264
265     b->data[b->size++] = val & 0xFF;
266     b->data[b->size++] = ( val >> 8 ) & 0xFF;
267     b->data[b->size++] = ( val >> 16 ) & 0xFF;
268     b->data[b->size++] = val >> 24;
269 }
270
271 struct hb_mux_object_s
272 {
273     HB_MUX_COMMON;
274
275     hb_job_t * job;
276
277     /* Data size in bytes, not including headers */
278     unsigned               size;
279     FILE                 * file;
280     hb_buffer_t          * index;
281     hb_avi_main_header_t   main_header;
282 };
283
284 struct hb_mux_data_s
285 {
286     uint32_t                            fourcc;
287     hb_avi_stream_header_t      header;
288     hb_avi_vprp_info_t          vprp_header;
289     union
290     {
291         hb_bitmap_info_t   v;
292         struct
293         {
294             hb_wave_formatex_t f;
295             hb_wave_mp3_t      m;
296         } a;
297     } format;
298 };
299
300 static void AddIndex( hb_mux_object_t * m )
301 {
302     fseek( m->file, 0, SEEK_END );
303
304     /* Write the index at the end of the file */
305     WriteInt32( m->file, FOURCC( "idx1" ) );
306     WriteInt32( m->file, m->index->size );
307     WriteBuffer( m->file, m->index );
308
309     /* Update file size */
310     m->size += 8 + m->index->size;
311     fseek( m->file, 4, SEEK_SET );
312     WriteInt32( m->file, 2040 + m->size );
313
314     /* Update HASINDEX flag */
315     m->main_header.Flags |= AVIF_HASINDEX;
316     fseek( m->file, 24, SEEK_SET );
317     WriteMainHeader( m->file, &m->main_header );
318 }
319
320
321 /**********************************************************************
322  * AVIInit
323  **********************************************************************
324  * Allocates things, create file, initialize and write headers
325  *********************************************************************/
326 static int AVIInit( hb_mux_object_t * m )
327 {
328     hb_job_t   * job   = m->job;
329     hb_title_t * title = job->title;
330
331     hb_audio_t    * audio;
332     hb_mux_data_t * mux_data;
333
334     int audio_count = hb_list_count( title->list_audio );
335     int is_passthru = 0;
336     int is_ac3      = 0;
337     int hdrl_bytes;
338     int i;
339
340     /* Allocate index */
341     m->index       = hb_buffer_init( 1024 * 1024 );
342     m->index->size = 0;
343
344     /* Open destination file */
345     hb_log( "muxavi: opening %s", job->file );
346     m->file = fopen( job->file, "wb" );
347
348 #define m m->main_header
349     /* AVI main header */
350     m.FourCC           = FOURCC( "avih" );
351     m.BytesCount       = sizeof( hb_avi_main_header_t ) - 8;
352     m.MicroSecPerFrame = (uint64_t) 1000000 * job->vrate_base / job->vrate;
353     m.Streams          = 1 + audio_count;
354     m.Width            = job->width;
355     m.Height           = job->height;
356 #undef m
357
358     /* Video track */
359     mux_data = calloc( sizeof( hb_mux_data_t ), 1 );
360     job->mux_data = mux_data;
361
362 #define h mux_data->header
363     /* Video stream header */
364     h.FourCC     = FOURCC( "strh" );
365     h.BytesCount = sizeof( hb_avi_stream_header_t ) - 8;
366     h.Type       = FOURCC( "vids" );
367
368     if( job->vcodec == HB_VCODEC_FFMPEG )
369         h.Handler = FOURCC( "divx" );
370     else if( job->vcodec == HB_VCODEC_X264 )
371         h.Handler = FOURCC( "h264" );
372
373     h.Scale      = job->vrate_base;
374     h.Rate       = job->vrate;
375 #undef h
376
377 #define f mux_data->format.v
378     /* Video stream format */
379     f.FourCC      = FOURCC( "strf" );
380     f.BytesCount  = sizeof( hb_bitmap_info_t ) - 8;
381     f.Size        = f.BytesCount;
382     f.Width       = job->width;
383     f.Height      = job->height;
384     f.Planes      = 1;
385     f.BitCount    = 24;
386     if( job->vcodec == HB_VCODEC_FFMPEG )
387         f.Compression = FOURCC( "DX50" );
388     else if( job->vcodec == HB_VCODEC_X264 )
389         f.Compression = FOURCC( "H264" );
390 #undef f
391
392 #define g mux_data->vprp_header
393     /* Vprp video stream header */      
394     AVRational sample_aspect_ratio = ( AVRational ){ job->anamorphic.par_width, job->anamorphic.par_height };
395     AVRational dar = av_mul_q( sample_aspect_ratio, ( AVRational ){ job->width, job->height } );
396     int num, den;
397     av_reduce(&num, &den, dar.num, dar.den, 0xFFFF);
398
399     g.FourCC                = FOURCC( "vprp" );
400     g.BytesCount            = sizeof( hb_avi_vprp_info_t ) - 8;
401     g.VideoFormatToken      = 0;
402     g.VideoStandard         = 0;
403     g.dwVerticalRefreshRate = job->vrate / job->vrate_base;
404     g.dwHTotalInT           = job->width;
405     g.dwVTotalInLines       = job->height;
406     g.dwFrameAspectRatioDen = den;
407     g.dwFrameAspectRatioNum = num;
408     g.dwFrameWidthInPixels  = job->width;
409     g.dwFrameHeightInLines  = job->height;
410     g.nbFieldPerFrame       = 1;
411     g.CompressedBMHeight    = job->height;
412     g.CompressedBMWidth     = job->width;
413     g.ValidBMHeight         = job->height;
414     g.ValidBMWidth          = job->width;
415     g.ValidBMXOffset        = 0;
416     g.ValidBMYOffset        = 0;
417     g.VideoXOffsetInT       = 0;
418     g.VideoYValidStartLine  = 0;
419 #undef g
420
421     /* Audio tracks */
422     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
423     {
424         audio = hb_list_item( title->list_audio, i );
425
426         is_ac3 = (audio->config.out.codec == HB_ACODEC_AC3);
427         is_passthru = (audio->config.out.codec == HB_ACODEC_AC3) ||
428                       (audio->config.out.codec == HB_ACODEC_DCA);
429
430         mux_data = calloc( sizeof( hb_mux_data_t ), 1 );
431         audio->priv.mux_data = mux_data;
432
433 #define h mux_data->header
434 #define f mux_data->format.a.f
435 #define m mux_data->format.a.m
436         /* Audio stream header */
437         h.FourCC        = FOURCC( "strh" );
438         h.BytesCount    = sizeof( hb_avi_stream_header_t ) - 8;
439         h.Type          = FOURCC( "auds" );
440         h.InitialFrames = 1;
441         h.Scale         = 1;
442         h.Rate          = is_passthru ? ( audio->config.in.bitrate / 8 ) :
443                                    ( audio->config.out.bitrate * 1000 / 8 );
444         h.Quality       = 0xFFFFFFFF;
445         h.SampleSize    = 1;
446
447         /* Audio stream format */
448         f.FourCC         = FOURCC( "strf" );
449         if( is_passthru )
450         {
451             f.BytesCount     = sizeof( hb_wave_formatex_t ) - 8;
452             f.FormatTag      = is_ac3 ? 0x2000 : 0x2001;
453             f.Channels       = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT(audio->config.in.channel_layout);
454             f.SamplesPerSec  = audio->config.in.samplerate;
455         }
456         else
457         {
458             f.BytesCount     = sizeof( hb_wave_formatex_t ) +
459                                sizeof( hb_wave_mp3_t ) - 8;
460             f.FormatTag      = 0x55;
461             f.Channels       = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown);
462             f.SamplesPerSec  = audio->config.out.samplerate;
463         }
464         f.AvgBytesPerSec = h.Rate;
465         f.BlockAlign     = 1;
466         if( is_passthru )
467         {
468             f.Size       = 0;
469         }
470         else
471         {
472             f.Size           = sizeof( hb_wave_mp3_t );
473             m.Id             = 1;
474             m.Flags          = 2;
475             m.BlockSize      = 1152 * f.AvgBytesPerSec / audio->config.out.samplerate;
476             m.FramesPerBlock = 1;
477             m.CodecDelay     = 1393;
478         }
479 #undef h
480 #undef f
481 #undef m
482     }
483
484     hdrl_bytes =
485         /* Main header */
486         4 + sizeof( hb_avi_main_header_t ) +
487         /* strh for video + audios */
488         ( 1 + audio_count ) * ( 12 + sizeof( hb_avi_stream_header_t ) ) +
489         /* video strf */
490                 sizeof( hb_bitmap_info_t ) +
491         /* video vprp */
492         ( job->anamorphic.mode ? sizeof( hb_avi_vprp_info_t ) : 0 ) +
493         /* audios strf */
494         audio_count * ( sizeof( hb_wave_formatex_t ) +
495                         ( is_passthru ? 0 : sizeof( hb_wave_mp3_t ) ) );
496
497     /* Here we really start to write into the file */
498
499     /* Main headers */
500     WriteInt32( m->file, FOURCC( "RIFF" ) );
501     WriteInt32( m->file, 2040 );
502     WriteInt32( m->file, FOURCC( "AVI " ) );
503     WriteInt32( m->file, FOURCC( "LIST" ) );
504     WriteInt32( m->file, hdrl_bytes );
505     WriteInt32( m->file, FOURCC( "hdrl" ) );
506     WriteMainHeader( m->file, &m->main_header );
507
508     /* Video track */
509     mux_data          = job->mux_data;
510     mux_data->fourcc = FOURCC( "00dc" );
511
512     WriteInt32( m->file, FOURCC( "LIST" ) );
513     WriteInt32( m->file, 4 + sizeof( hb_avi_stream_header_t ) +
514                 sizeof( hb_bitmap_info_t )  +
515                 ( job->anamorphic.mode ? sizeof( hb_avi_vprp_info_t ) : 0 ) );
516     WriteInt32( m->file, FOURCC( "strl" ) );
517     WriteStreamHeader( m->file, &mux_data->header );
518     WriteBitmapInfo( m->file, &mux_data->format.v );
519     if( job->anamorphic.mode )
520     {
521         WriteVprpInfo( m->file, &mux_data->vprp_header );
522     }
523
524     /* Audio tracks */
525     for( i = 0; i < audio_count; i++ )
526     {
527         char fourcc[4] = "00wb";
528
529         audio    = hb_list_item( title->list_audio, i );
530         mux_data = audio->priv.mux_data;
531
532         fourcc[1] = '1' + i; /* This is fine as we don't allow more
533                                 than 8 tracks */
534         mux_data->fourcc = FOURCC( fourcc );
535
536         WriteInt32( m->file, FOURCC( "LIST" ) );
537         WriteInt32( m->file, 4 + sizeof( hb_avi_stream_header_t ) +
538                              sizeof( hb_wave_formatex_t ) +
539                              ( is_passthru ? 0 : sizeof( hb_wave_mp3_t ) ) );
540         WriteInt32( m->file, FOURCC( "strl" ) );
541         WriteStreamHeader( m->file, &mux_data->header );
542         WriteWaveFormatEx( m->file, &mux_data->format.a.f );
543         if( !is_passthru )
544         {
545             WriteWaveMp3( m->file, &mux_data->format.a.m );
546         }
547     }
548
549     WriteInt32( m->file, FOURCC( "JUNK" ) );
550     WriteInt32( m->file, 2020 - hdrl_bytes );
551     for( i = 0; i < 2020 - hdrl_bytes; i++ )
552     {
553         WriteInt8( m->file, 0 );
554     }
555     WriteInt32( m->file, FOURCC( "LIST" ) );
556     WriteInt32( m->file, 4 );
557     WriteInt32( m->file, FOURCC( "movi" ) );
558
559     return 0;
560 }
561
562 static int AVIMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
563                    hb_buffer_t * buf )
564 {
565     hb_job_t   * job   = m->job;
566     hb_title_t * title = job->title;
567
568     hb_audio_t * audio;
569     int i;
570
571     /* Update index */
572     IndexAddInt32( m->index, mux_data->fourcc );
573     IndexAddInt32( m->index, (buf->frametype & HB_FRAME_KEY) ? AVIIF_KEYFRAME : 0 );
574     IndexAddInt32( m->index, 4 + m->size );
575     IndexAddInt32( m->index, buf->size );
576
577     /* Write the chunk to the file */
578     fseek( m->file, 0, SEEK_END );
579     WriteInt32( m->file, mux_data->fourcc );
580     WriteInt32( m->file, buf->size );
581     WriteBuffer( m->file, buf );
582
583     /* Chunks must be 2-bytes aligned */
584     if( buf->size & 1 )
585     {
586         WriteInt8( m->file, 0 );
587     }
588
589     /* Update headers */
590     m->size += 8 + EVEN( buf->size );
591     mux_data->header.Length++;
592
593     /* RIFF size */
594     fseek( m->file, 4, SEEK_SET );
595     WriteInt32( m->file, 2052 + m->size );
596
597     /* Mmmmh that's not nice */
598     fseek( m->file, 140, SEEK_SET );
599     WriteInt32( m->file, job->mux_data->header.Length );
600     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
601     {
602         int is_passthru;
603         audio = hb_list_item( title->list_audio, i );
604         is_passthru = (audio->config.out.codec == HB_ACODEC_AC3) ||
605                       (audio->config.out.codec == HB_ACODEC_DCA);
606         fseek( m->file, 264 + i *
607                ( 102 + ( is_passthru ? 0 :
608                  sizeof( hb_wave_mp3_t ) ) ), SEEK_SET );
609         WriteInt32( m->file, audio->priv.mux_data->header.Length );
610     }
611
612     /* movi size */
613     fseek( m->file, 2052, SEEK_SET );
614     WriteInt32( m->file, 4 + m->size );
615     return 0;
616 }
617
618 static int AVIEnd( hb_mux_object_t * m )
619 {
620     hb_job_t * job = m->job;
621
622     hb_log( "muxavi: writing index" );
623     AddIndex( m );
624
625     hb_log( "muxavi: closing %s", job->file );
626     fclose( m->file );
627
628     hb_buffer_close( &m->index );
629
630     return 0;
631 }
632
633 hb_mux_object_t * hb_mux_avi_init( hb_job_t * job )
634 {
635     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
636     m->init      = AVIInit;
637     m->mux       = AVIMux;
638     m->end       = AVIEnd;
639     m->job       = job;
640     return m;
641 }
642