1 /*
2 % Copyright (C) 2003-2020 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 %
5 % This program is covered by multiple licenses, which are described in
6 % Copyright.txt. You should have received a copy of Copyright.txt with this
7 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8 %
9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10 %                                                                             %
11 %                                                                             %
12 %                                                                             %
13 %                        M   M  PPPP   EEEEE   GGGG                           %
14 %                        MM MM  P   P  E      G                               %
15 %                        M M M  PPPP   EEE    G  GG                           %
16 %                        M   M  P      E      G   G                           %
17 %                        M   M  P      EEEEE   GGGG                           %
18 %                                                                             %
19 %                                                                             %
20 %                          Write MPEG Image Format.                           %
21 %                                                                             %
22 %                                                                             %
23 %                              Software Design                                %
24 %                                John Cristy                                  %
25 %                                 July 1999                                   %
26 %                                                                             %
27 %                                                                             %
28 %                                                                             %
29 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30 %
31 %
32 */
33 /*
34   Include declarations.
35 */
36 #include "magick/studio.h"
37 #include "magick/blob.h"
38 #include "magick/constitute.h"
39 #include "magick/delegate.h"
40 #include "magick/log.h"
41 #include "magick/magick.h"
42 #include "magick/tempfile.h"
43 #include "magick/transform.h"
44 #include "magick/utility.h"
45 
46 /*
47   Forward declarations.
48 */
49 static unsigned int
50   WriteMPEGImage(const ImageInfo *image_info,Image *image);
51 
52 /*
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54 %                                                                             %
55 %                                                                             %
56 %                                                                             %
57 %   I s M P E G                                                               %
58 %                                                                             %
59 %                                                                             %
60 %                                                                             %
61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 %
63 %  Method IsMPEG returns True if the image format type, identified by the
64 %  magick string, is MPEG.
65 %
66 %  The format of the IsMPEG method is:
67 %
68 %      unsigned int IsMPEG(const unsigned char *magick,const size_t length)
69 %
70 %  A description of each parameter follows:
71 %
72 %    o status:  Method IsMPEG returns True if the image format type is MPEG.
73 %
74 %    o magick: This string is generally the first few bytes of an image file
75 %      or blob.
76 %
77 %    o length: Specifies the length of the magick string.
78 %
79 %
80 */
IsMPEG(const unsigned char * magick,const size_t length)81 static unsigned int IsMPEG(const unsigned char *magick,const size_t length)
82 {
83   if (length < 4)
84     return(False);
85   if (memcmp(magick,"\000\000\001\263",4) == 0)
86     return(True);
87   return(False);
88 }
89 
90 /*
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 %                                                                             %
93 %                                                                             %
94 %                                                                             %
95 %   R e g i s t e r M P E G I m a g e                                         %
96 %                                                                             %
97 %                                                                             %
98 %                                                                             %
99 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100 %
101 %  Method RegisterMPEGImage adds attributes for the MPEG image format to
102 %  the list of supported formats.  The attributes include the image format
103 %  tag, a method to read and/or write the format, whether the format
104 %  supports the saving of more than one frame to the same file or blob,
105 %  whether the format supports native in-memory I/O, and a brief
106 %  description of the format.
107 %
108 %  The format of the RegisterMPEGImage method is:
109 %
110 %      RegisterMPEGImage(void)
111 %
112 */
RegisterMPEGImage(void)113 ModuleExport void RegisterMPEGImage(void)
114 {
115   MagickInfo
116     *entry;
117 
118   entry=SetMagickInfo("MPEG");
119   entry->encoder=(EncoderHandler) WriteMPEGImage;
120   entry->magick=(MagickHandler) IsMPEG;
121   entry->blob_support=False;
122   entry->description="MPEG Video Stream";
123   entry->module="MPEG";
124   (void) RegisterMagickInfo(entry);
125 
126   entry=SetMagickInfo("MPG");
127   entry->encoder=(EncoderHandler) WriteMPEGImage;
128   entry->magick=(MagickHandler) IsMPEG;
129   entry->blob_support=False;
130   entry->description="MPEG Video Stream";
131   entry->module="MPEG";
132   (void) RegisterMagickInfo(entry);
133 
134   entry=SetMagickInfo("M2V");
135   entry->encoder=(EncoderHandler) WriteMPEGImage;
136   entry->magick=(MagickHandler) IsMPEG;
137   entry->blob_support=False;
138   entry->description="MPEG Video Stream";
139   entry->module="MPEG";
140   (void) RegisterMagickInfo(entry);
141 }
142 
143 /*
144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145 %                                                                             %
146 %                                                                             %
147 %                                                                             %
148 %   U n r e g i s t e r M P E G I m a g e                                     %
149 %                                                                             %
150 %                                                                             %
151 %                                                                             %
152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 %
154 %  Method UnregisterMPEGImage removes format registrations made by the
155 %  BIM module from the list of supported formats.
156 %
157 %  The format of the UnregisterBIMImage method is:
158 %
159 %      UnregisterMPEGImage(void)
160 %
161 */
UnregisterMPEGImage(void)162 ModuleExport void UnregisterMPEGImage(void)
163 {
164   (void) UnregisterMagickInfo("MPEG");
165   (void) UnregisterMagickInfo("MPG");
166   (void) UnregisterMagickInfo("M2V");
167 }
168 
169 /*
170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 %                                                                             %
172 %                                                                             %
173 %                                                                             %
174 %   W r i t e M P E G I m a g e                                               %
175 %                                                                             %
176 %                                                                             %
177 %                                                                             %
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 %
180 %  Method WriteMPEGImage writes an image to a file in MPEG video stream format.
181 %  Lawrence Livermore National Laboratory (LLNL) contributed code to adjust
182 %  the MPEG parameters to correspond to the compression quality setting.
183 %
184 %  The format of the WriteMPEGImage method is:
185 %
186 %      unsigned int WriteMPEGImage(const ImageInfo *image_info,Image *image)
187 %
188 %  A description of each parameter follows.
189 %
190 %    o status: Method WriteMPEGImage return True if the image is written.
191 %      False is returned is there is a memory shortage or if the image file
192 %      fails to write.
193 %
194 %    o image_info: Specifies a pointer to a ImageInfo structure.
195 %
196 %    o image:  A pointer to an Image structure.
197 %
198 %
199 */
200 
201 #define ThrowMPEGWriterException(code_,reason_,image_) \
202 { \
203   if (clone_info) \
204     DestroyImageInfo(clone_info);             \
205   ThrowWriterException(code_,reason_,image_); \
206 }
207 
WriteMPEGParameterFiles(const ImageInfo * image_info,Image * image)208 static unsigned int WriteMPEGParameterFiles(const ImageInfo *image_info,
209   Image *image)
210 {
211   char
212     filename[MaxTextExtent];
213 
214   double
215     q;
216 
217   FILE
218     *file,
219     *parameter_file;
220 
221   long
222     quant,
223     vertical_factor;
224 
225   register Image
226     *p;
227 
228   register long
229     i;
230 
231   static const int
232     q_matrix[]=
233     {
234        8, 16, 19, 22, 26, 27, 29, 34,
235       16, 16, 22, 24, 27, 29, 34, 37,
236       19, 22, 26, 27, 29, 34, 34, 38,
237       22, 22, 26, 27, 29, 34, 37, 40,
238       22, 26, 27, 29, 32, 35, 40, 48,
239       26, 27, 29, 32, 35, 40, 48, 58,
240       26, 27, 29, 34, 38, 46, 56, 69,
241       27, 29, 35, 38, 46, 56, 69, 83
242     };
243 
244   unsigned int
245     mpeg;
246 
247   unsigned long
248     count;
249 
250   assert(image_info != (const ImageInfo *) NULL);
251   assert(image_info->signature == MagickSignature);
252   assert(image != (Image *) NULL);
253   assert(image->signature == MagickSignature);
254 
255   /*
256     Write parameter file (see mpeg2encode documentation for details).
257   */
258   file=fopen(image_info->unique,"w");
259   if (file == (FILE *) NULL)
260     return(False);
261   (void) fprintf(file,"MPEG\n");  /* comment */
262   (void) fprintf(file,"%.1024s.%%d\n",image->filename); /* source frame file */
263   (void) fprintf(file,"-\n");  /* reconstructed frame file */
264   if (image_info->quality == DefaultCompressionQuality)
265     (void) fprintf(file,"-\n");  /* default intra quant matrix */
266   else
267     {
268       /*
269         Write intra quant matrix file.
270       */
271       FormatString(filename,"%.1024s.iqm",image_info->unique);
272       (void) fprintf(file,"%s\n",filename);
273       parameter_file=fopen(filename,"w");
274       if (parameter_file == (FILE *) NULL)
275         {
276           (void) fclose(file);
277           file=(FILE *) NULL;
278           return(False);
279         }
280       if (image_info->quality < DefaultCompressionQuality)
281         {
282           q=Max((DefaultCompressionQuality-image_info->quality)/8.0,1.0);
283           for (i=0; i < 64; i++)
284           {
285             quant=(long) Min(Max(q*q_matrix[i]+0.5,1.0),255.0);
286             (void) fprintf(parameter_file," %ld",quant);
287             if ((i % 8) == 7)
288               (void) fprintf(parameter_file,"\n");
289           }
290         }
291       else
292         {
293           q=Max((image_info->quality-DefaultCompressionQuality)*2.0,1.0);
294           for (i=0; i < 64; i++)
295           {
296             quant=(long) Min(Max(q_matrix[i]/q,1.0),255.0);
297             (void) fprintf(parameter_file," %ld",quant);
298             if ((i % 8) == 7)
299               (void) fprintf(parameter_file,"\n");
300           }
301         }
302       (void) fclose(parameter_file);
303       parameter_file=(FILE *) NULL;
304     }
305   if (image_info->quality == DefaultCompressionQuality)
306     (void) fprintf(file,"-\n");  /* default non intra quant matrix */
307   else
308     {
309       /*
310         Write non intra quant matrix file.
311       */
312       FormatString(filename,"%.1024s.niq",image_info->unique);
313       (void) fprintf(file,"%s\n",filename);
314       parameter_file=fopen(filename,"w");
315       if (parameter_file == (FILE *) NULL)
316         {
317           (void) fclose(file);
318           file=(FILE *) NULL;
319           return(False);
320         }
321       q=Min(Max(66.0-(2.0*image_info->quality)/3.0,1.0),255.0);
322       for (i=0; i < 64; i++)
323       {
324         (void) fprintf(parameter_file," %d",(int) q);
325         if ((i % 8) == 7)
326           (void) fprintf(parameter_file,"\n");
327       }
328       (void) fclose(parameter_file);
329       parameter_file=(FILE *) NULL;
330     }
331   (void) fprintf(file,"%.1024s.log\n",image_info->unique);  /* statistics log */
332   (void) fprintf(file,"1\n");  /* input picture file format */
333   count=0;
334   for (p=image; p != (Image *) NULL; p=p->next)
335     count+=Max((p->delay+1)/3,1);
336   (void) fprintf(file,"%lu\n",count); /* number of frames */
337   (void) fprintf(file,"0\n");  /* number of first frame */
338   (void) fprintf(file,"00:00:00:00\n");  /* timecode of first frame */
339   mpeg=LocaleCompare(image_info->magick,"M2V") != 0;
340   if (image_info->quality > 98)
341     (void) fprintf(file,"1\n");
342   else
343     (void) fprintf(file,"%d\n",mpeg ? 12 : 15);
344   if (image_info->quality > 98)
345     (void) fprintf(file,"1\n");
346   else
347     (void) fprintf(file,"3\n");
348   (void) fprintf(file,"%d\n",mpeg ? 1 : 0);  /* ISO/IEC 11172-2 stream */
349   (void) fprintf(file,"0\n");  /* select frame picture coding */
350   (void) fprintf(file,"%lu\n",image->columns+(image->columns & 0x01 ? 1 : 0));
351   (void) fprintf(file,"%lu\n",image->rows+(image->rows & 0x01 ? 1 : 0));
352   (void) fprintf(file,"%d\n",mpeg ? 8 : 2);  /* aspect ratio */
353   (void) fprintf(file,"%d\n",mpeg ? 3 : 5);  /* frame rate code */
354   (void) fprintf(file,"%.1f\n",mpeg ? 1152000.0 : 5000000.0);  /* bit rate */
355   (void) fprintf(file,"%d\n",mpeg ? 20 : 112);  /* vbv buffer size */
356   (void) fprintf(file,"0\n");  /* low delay */
357   (void) fprintf(file,"%d\n",mpeg ? 1 : 0);  /* constrained parameter */
358   (void) fprintf(file,"%d\n",mpeg ? 4 : 1);  /* profile ID */
359   (void) fprintf(file,"%d\n",mpeg ? 8 : 4);  /* level ID */
360   (void) fprintf(file,"%d\n",mpeg ? 1 : 0);  /* progressive sequence */
361   vertical_factor=2;
362   if (image_info->sampling_factor != (char *) NULL)
363     {
364       long
365         count,
366         horizontal_factor;
367 
368       horizontal_factor=2;
369       count=sscanf(image_info->sampling_factor,"%ldx%ld",&horizontal_factor,
370         &vertical_factor);
371       if (count != 2)
372         vertical_factor=horizontal_factor;
373       if (mpeg)
374         {
375           if ((horizontal_factor != 2) || (vertical_factor != 2))
376             {
377               (void) fclose(file);
378               file=(FILE *) NULL;
379               return(False);
380             }
381         }
382       else
383         if ((horizontal_factor != 2) ||
384             ((vertical_factor != 1) && (vertical_factor != 2)))
385           {
386             (void) fclose(file);
387             file=(FILE *) NULL;
388             return(False);
389           }
390     }
391   (void) fprintf(file,"%d\n",vertical_factor==2 ? 1 : 2); /* chroma format */
392   (void) fprintf(file,"%d\n",mpeg ? 1 : 2);  /* video format */
393   (void) fprintf(file,"5\n");  /* color primaries */
394   (void) fprintf(file,"5\n");  /* transfer characteristics */
395   (void) fprintf(file,"%d\n",mpeg ? 5 : 4);  /* matrix coefficients */
396   (void) fprintf(file,"%lu\n",image->columns+(image->columns & 0x01 ? 1 : 0));
397   (void) fprintf(file,"%lu\n",image->rows+(image->rows & 0x01 ? 1 : 0));
398   (void) fprintf(file,"0\n");  /* intra dc precision */
399   (void) fprintf(file,"%d\n",mpeg ? 0 : 1);  /* top field */
400   (void) fprintf(file,"%d %d %d\n",mpeg ? 1 : 0,mpeg ? 1 : 0, mpeg ? 1 : 0);
401   (void) fprintf(file,"0 0 0\n");  /* concealment motion vector */
402   (void) fprintf(file,"%d %d %d\n",mpeg ? 0 : 1,mpeg ? 0 : 1,mpeg ? 0 : 1);
403   (void) fprintf(file,"%d 0 0\n",mpeg ? 0 : 1);  /* intra vlc format */
404   (void) fprintf(file,"0 0 0\n");  /* alternate scan */
405   (void) fprintf(file,"0\n");  /* repeat first field */
406   (void) fprintf(file,"%d\n",mpeg ? 1 : 0);  /* progressive frame */
407   (void) fprintf(file,"0\n");  /* intra slice refresh period */
408   (void) fprintf(file,"0\n");  /* reaction parameter */
409   (void) fprintf(file,"0\n");  /* initial average activity */
410   (void) fprintf(file,"0\n");
411   (void) fprintf(file,"0\n");
412   (void) fprintf(file,"0\n");
413   (void) fprintf(file,"0\n");
414   (void) fprintf(file,"0\n");
415   (void) fprintf(file,"0\n");
416   (void) fprintf(file,"2 2 11 11\n");
417   (void) fprintf(file,"1 1 3 3\n");
418   (void) fprintf(file,"1 1 7 7\n");
419   (void) fprintf(file,"1 1 7 7\n");
420   (void) fprintf(file,"1 1 3 3\n");
421   (void) fclose(file);
422   file=(FILE *) NULL;
423   return(True);
424 }
425 
WriteMPEGImage(const ImageInfo * image_info,Image * image)426 static unsigned int WriteMPEGImage(const ImageInfo *image_info,Image *image)
427 {
428   char
429     basename[MaxTextExtent],
430     filename[MaxTextExtent];
431 
432   Image
433     *coalesce_image,
434     *next_image;
435 
436   ImageInfo
437     *clone_info = (ImageInfo *) NULL;
438 
439   register Image
440     *p;
441 
442   register long
443     i;
444 
445   size_t
446     length;
447 
448   unsigned char
449     *blob;
450 
451   unsigned int
452     logging,
453     status;
454 
455   unsigned long
456     count,
457     scene;
458 
459   /*
460     Open output image file.
461   */
462   assert(image_info != (const ImageInfo *) NULL);
463   assert(image_info->signature == MagickSignature);
464   assert(image != (Image *) NULL);
465   assert(image->signature == MagickSignature);
466   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter");
467   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
468   if (status == False)
469     ThrowMPEGWriterException(FileOpenError,UnableToOpenFile,image);
470   CloseBlob(image);
471   /*
472     Determine if the sequence of images have identical page info.
473   */
474   coalesce_image=image;
475   for (next_image=image; next_image != (Image *) NULL; )
476   {
477     if ((image->columns != next_image->columns) ||
478         (image->rows != next_image->rows))
479       break;
480     if ((image->page.x != next_image->page.x) ||
481         (image->page.y != next_image->page.y))
482       break;
483     next_image=next_image->next;
484   }
485   if (next_image != (Image *) NULL)
486     {
487       coalesce_image=CoalesceImages(image,&image->exception);
488       if (coalesce_image == (Image *) NULL)
489         return(False);
490     }
491   /*
492     Write YUV files.
493   */
494   if(!AcquireTemporaryFileName(basename))
495     {
496       DestroyImageList(coalesce_image);
497       ThrowWriterTemporaryFileException(basename);
498     }
499   FormatString(coalesce_image->filename,"%.1024s",basename);
500   clone_info=CloneImageInfo(image_info);
501   (void) strlcpy(clone_info->unique,basename,MaxTextExtent);
502   status=WriteMPEGParameterFiles(clone_info,coalesce_image);
503   if (status == False)
504     {
505       if (coalesce_image != image)
506         DestroyImageList(coalesce_image);
507       (void) LiberateTemporaryFile(basename);
508       if (image_info->quality != DefaultCompressionQuality)
509         {
510           FormatString(filename,"%.1024s.iqm",basename);
511           (void) remove(filename);
512           FormatString(filename,"%.1024s.niq",basename);
513           (void) remove(filename);
514         }
515       ThrowMPEGWriterException(CoderError,UnableToWriteMPEGParameters,image);
516     }
517   count=0;
518   clone_info->interlace=PlaneInterlace;
519   for (p=coalesce_image; p != (Image *) NULL; p=p->next)
520   {
521     char
522       previous_image[MaxTextExtent];
523 
524     blob=(unsigned char *) NULL;
525     length=0;
526     scene=p->scene;
527     for (i=0; i < (long) Max((p->delay+1)/3,1); i++)
528     {
529       p->scene=count;
530       count++;
531       status=False;
532       switch (i)
533       {
534         case 0:
535         {
536           Image
537             *frame;
538 
539           FormatString(p->filename,"%.1024s.%lu.yuv",basename,p->scene);
540           FormatString(filename,"%.1024s.%lu.yuv",basename,p->scene);
541           FormatString(previous_image,"%.1024s.%lu.yuv",basename,p->scene);
542           frame=CloneImage(p,0,0,True,&p->exception);
543           if (frame == (Image *) NULL)
544             break;
545           status=WriteImage(clone_info,frame);
546           DestroyImage(frame);
547           break;
548         }
549         case 1:
550         {
551           blob=(unsigned char *)
552             FileToBlob(previous_image,&length,&image->exception);
553           FormatString(filename,"%.1024s.%lu.yuv",basename,p->scene);
554           if (length > 0)
555             status=BlobToFile(filename,blob,length,&image->exception);
556           break;
557         }
558         default:
559         {
560           FormatString(filename,"%.1024s.%lu.yuv",basename,p->scene);
561           if (length > 0)
562             status=BlobToFile(filename,blob,length,&image->exception);
563           break;
564         }
565       }
566       if (logging)
567         {
568           if (status)
569             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
570               "%lu. Wrote YUV file for scene %lu:",i,p->scene);
571           else
572             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
573               "%lu. Failed to write YUV file for scene %lu:",i,p->scene);
574           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%.1024s",
575             filename);
576         }
577     }
578     p->scene=scene;
579     if (blob != (unsigned char *) NULL)
580       MagickFreeMemory(blob);
581     if (status == False)
582       break;
583   }
584   /*
585     Convert YUV to MPEG.
586   */
587   (void) strlcpy(coalesce_image->filename,clone_info->unique,MaxTextExtent);
588   status=InvokeDelegate(clone_info,coalesce_image,(char *) NULL,"mpeg-encode",
589     &image->exception);
590   DestroyImageInfo(clone_info);
591   /*
592     Free resources.
593   */
594   count=0;
595   for (p=coalesce_image; p != (Image *) NULL; p=p->next)
596   {
597     for (i=0; i < (long) Max((p->delay+1)/3,1); i++)
598     {
599       FormatString(p->filename,"%.1024s.%lu.yuv",basename,count++);
600       (void) remove(p->filename);
601     }
602     (void) strlcpy(p->filename,image_info->filename,MaxTextExtent);
603   }
604   FormatString(filename,"%.1024s.iqm",basename);
605   (void) remove(filename);
606   FormatString(filename,"%.1024s.niq",basename);
607   (void) remove(filename);
608   FormatString(filename,"%.1024s.log",basename);
609   (void) remove(filename);
610   (void) LiberateTemporaryFile(basename);
611   if (coalesce_image != image)
612     DestroyImageList(coalesce_image);
613   if (logging)
614     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit");
615   return(status);
616 }
617