]> granicus.if.org Git - imagemagick/blob - coders/mpeg.c
(no commit message)
[imagemagick] / coders / mpeg.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                        M   M  PPPP   EEEEE   GGGG                           %
7 %                        MM MM  P   P  E      G                               %
8 %                        M M M  PPPP   EEE    G  GG                           %
9 %                        M   M  P      E      G   G                           %
10 %                        M   M  P      EEEEE   GGGG                           %
11 %                                                                             %
12 %                                                                             %
13 %                       Read/Write MPEG Image Format                          %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1999                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 /*
39   Include declarations.
40 */
41 #include "magick/studio.h"
42 #include "magick/blob.h"
43 #include "magick/blob-private.h"
44 #include "magick/constitute.h"
45 #include "magick/delegate.h"
46 #include "magick/exception.h"
47 #include "magick/exception-private.h"
48 #include "magick/geometry.h"
49 #include "magick/image.h"
50 #include "magick/image-private.h"
51 #include "magick/layer.h"
52 #include "magick/list.h"
53 #include "magick/log.h"
54 #include "magick/magick.h"
55 #include "magick/memory_.h"
56 #include "magick/resource_.h"
57 #include "magick/quantum-private.h"
58 #include "magick/static.h"
59 #include "magick/string_.h"
60 #include "magick/module.h"
61 #include "magick/transform.h"
62 #include "magick/utility.h"
63 \f
64 /*
65   Forward declarations.
66 */
67 static MagickBooleanType
68   WriteMPEGImage(const ImageInfo *image_info,Image *image);
69 \f
70 /*
71 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72 %                                                                             %
73 %                                                                             %
74 %                                                                             %
75 %   I s A V I                                                                 %
76 %                                                                             %
77 %                                                                             %
78 %                                                                             %
79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 %
81 %  IsAVI() returns MagickTrue if the image format type, identified by the
82 %  magick string, is Audio/Video Interleaved file format.
83 %
84 %  The format of the IsAVI method is:
85 %
86 %      size_t IsAVI(const unsigned char *magick,const size_t length)
87 %
88 %  A description of each parameter follows:
89 %
90 %    o magick: compare image format pattern against these bytes.
91 %
92 %    o length: Specifies the length of the magick string.
93 %
94 */
95 static MagickBooleanType IsAVI(const unsigned char *magick,const size_t length)
96 {
97   if (length < 4)
98     return(MagickFalse);
99   if (memcmp(magick,"RIFF",4) == 0)
100     return(MagickTrue);
101   return(MagickFalse);
102 }
103 \f
104 /*
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 %                                                                             %
107 %                                                                             %
108 %                                                                             %
109 %   I s M P E G                                                               %
110 %                                                                             %
111 %                                                                             %
112 %                                                                             %
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 %
115 %  IsMPEG() returns MagickTrue if the image format type, identified by the
116 %  magick string, is MPEG.
117 %
118 %  The format of the IsMPEG method is:
119 %
120 %      MagickBooleanType IsMPEG(const unsigned char *magick,const size_t length)
121 %
122 %  A description of each parameter follows:
123 %
124 %    o magick: compare image format pattern against these bytes.
125 %
126 %    o length: Specifies the length of the magick string.
127 %
128 */
129 static MagickBooleanType IsMPEG(const unsigned char *magick,const size_t length)
130 {
131   if (length < 4)
132     return(MagickFalse);
133   if (memcmp(magick,"\000\000\001\263",4) == 0)
134     return(MagickTrue);
135   return(MagickFalse);
136 }
137 \f
138 /*
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 %                                                                             %
141 %                                                                             %
142 %                                                                             %
143 %   R e a d M P E G I m a g e                                                 %
144 %                                                                             %
145 %                                                                             %
146 %                                                                             %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %
149 %  ReadMPEGImage() reads an binary file in the MPEG video stream format
150 %  and returns it.  It allocates the memory necessary for the new Image
151 %  structure and returns a pointer to the new image.
152 %
153 %  The format of the ReadMPEGImage method is:
154 %
155 %      Image *ReadMPEGImage(const ImageInfo *image_info,
156 %        ExceptionInfo *exception)
157 %
158 %  A description of each parameter follows:
159 %
160 %    o image_info: the image info.
161 %
162 %    o exception: return any errors or warnings in this structure.
163 %
164 */
165 static Image *ReadMPEGImage(const ImageInfo *image_info,
166   ExceptionInfo *exception)
167 {
168 #define ReadMPEGIntermediateFormat "pam"
169
170   Image
171     *image,
172     *images;
173
174   ImageInfo
175     *read_info;
176
177   MagickBooleanType
178     status;
179
180   /*
181     Open image file.
182   */
183   assert(image_info != (const ImageInfo *) NULL);
184   assert(image_info->signature == MagickSignature);
185   if (image_info->debug != MagickFalse)
186     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
187       image_info->filename);
188   assert(exception != (ExceptionInfo *) NULL);
189   assert(exception->signature == MagickSignature);
190   image=AcquireImage(image_info);
191   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
192   if (status == MagickFalse)
193     {
194       image=DestroyImageList(image);
195       return((Image *) NULL);
196     }
197   (void) CloseBlob(image);
198   (void) DestroyImageList(image);
199   /*
200     Convert MPEG to PAM with delegate.
201   */
202   read_info=CloneImageInfo(image_info);
203   image=AcquireImage(image_info);
204   (void) InvokeDelegate(read_info,image,"mpeg:decode",(char *) NULL,exception);
205   image=DestroyImage(image);
206   (void) FormatLocaleString(read_info->filename,MaxTextExtent,"%s.%s",
207     read_info->unique,ReadMPEGIntermediateFormat);
208   images=ReadImage(read_info,exception);
209   (void) RelinquishUniqueFileResource(read_info->filename);
210   read_info=DestroyImageInfo(read_info);
211   return(images);
212 }
213 \f
214 /*
215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216 %                                                                             %
217 %                                                                             %
218 %                                                                             %
219 %   R e g i s t e r M P E G I m a g e                                         %
220 %                                                                             %
221 %                                                                             %
222 %                                                                             %
223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224 %
225 %  RegisterMPEGImage() adds attributes for the MPEG image format to
226 %  the list of supported formats.  The attributes include the image format
227 %  tag, a method to read and/or write the format, whether the format
228 %  supports the saving of more than one frame to the same file or blob,
229 %  whether the format supports native in-memory I/O, and a brief
230 %  description of the format.
231 %
232 %  The format of the RegisterMPEGImage method is:
233 %
234 %      size_t RegisterMPEGImage(void)
235 %
236 */
237 ModuleExport size_t RegisterMPEGImage(void)
238 {
239   MagickInfo
240     *entry;
241
242   entry=SetMagickInfo("AVI");
243   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
244   entry->magick=(IsImageFormatHandler *) IsAVI;
245   entry->blob_support=MagickFalse;
246   entry->description=ConstantString("Microsoft Audio/Visual Interleaved");
247   entry->module=ConstantString("MPEG");
248   (void) RegisterMagickInfo(entry);
249   entry=SetMagickInfo("MOV");
250   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
251   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
252   entry->magick=(IsImageFormatHandler *) IsMPEG;
253   entry->blob_support=MagickFalse;
254   entry->description=ConstantString("MPEG Video Stream");
255   entry->module=ConstantString("MPEG");
256   (void) RegisterMagickInfo(entry);
257   entry=SetMagickInfo("MPEG");
258   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
259   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
260   entry->magick=(IsImageFormatHandler *) IsMPEG;
261   entry->blob_support=MagickFalse;
262   entry->description=ConstantString("MPEG Video Stream");
263   entry->module=ConstantString("MPEG");
264   (void) RegisterMagickInfo(entry);
265   entry=SetMagickInfo("MPG");
266   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
267   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
268   entry->magick=(IsImageFormatHandler *) IsMPEG;
269   entry->blob_support=MagickFalse;
270   entry->description=ConstantString("MPEG Video Stream");
271   entry->module=ConstantString("MPEG");
272   (void) RegisterMagickInfo(entry);
273   entry=SetMagickInfo("MP4");
274   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
275   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
276   entry->magick=(IsImageFormatHandler *) IsMPEG;
277   entry->blob_support=MagickFalse;
278   entry->description=ConstantString("MPEG-4 Video Stream");
279   entry->module=ConstantString("MPEG");
280   (void) RegisterMagickInfo(entry);
281   entry=SetMagickInfo("M2V");
282   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
283   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
284   entry->magick=(IsImageFormatHandler *) IsMPEG;
285   entry->blob_support=MagickFalse;
286   entry->description=ConstantString("MPEG Video Stream");
287   entry->module=ConstantString("MPEG");
288   (void) RegisterMagickInfo(entry);
289   entry=SetMagickInfo("M4V");
290   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
291   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
292   entry->magick=(IsImageFormatHandler *) IsMPEG;
293   entry->blob_support=MagickFalse;
294   entry->description=ConstantString("Raw MPEG-4 Video");
295   entry->module=ConstantString("MPEG");
296   (void) RegisterMagickInfo(entry);
297   entry=SetMagickInfo("WMV");
298   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
299   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
300   entry->magick=(IsImageFormatHandler *) IsMPEG;
301   entry->blob_support=MagickFalse;
302   entry->description=ConstantString("Windows Media Video");
303   entry->module=ConstantString("MPEG");
304   (void) RegisterMagickInfo(entry);
305   return(MagickImageCoderSignature);
306 }
307 \f
308 /*
309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310 %                                                                             %
311 %                                                                             %
312 %                                                                             %
313 %   U n r e g i s t e r M P E G I m a g e                                     %
314 %                                                                             %
315 %                                                                             %
316 %                                                                             %
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318 %
319 %  UnregisterMPEGImage() removes format registrations made by the
320 %  BIM module from the list of supported formats.
321 %
322 %  The format of the UnregisterBIMImage method is:
323 %
324 %      UnregisterMPEGImage(void)
325 %
326 */
327 ModuleExport void UnregisterMPEGImage(void)
328 {
329   (void) UnregisterMagickInfo("WMV");
330   (void) UnregisterMagickInfo("M4V");
331   (void) UnregisterMagickInfo("M2V");
332   (void) UnregisterMagickInfo("MP4");
333   (void) UnregisterMagickInfo("MPG");
334   (void) UnregisterMagickInfo("MPEG");
335   (void) UnregisterMagickInfo("MOV");
336   (void) UnregisterMagickInfo("AVI");
337 }
338 \f
339 /*
340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341 %                                                                             %
342 %                                                                             %
343 %                                                                             %
344 %   W r i t e M P E G I m a g e                                               %
345 %                                                                             %
346 %                                                                             %
347 %                                                                             %
348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349 %
350 %  WriteMPEGImage() writes an image to a file in MPEG video stream format.
351 %  Lawrence Livermore National Laboratory (LLNL) contributed code to adjust
352 %  the MPEG parameters to correspond to the compression quality setting.
353 %
354 %  The format of the WriteMPEGImage method is:
355 %
356 %      MagickBooleanType WriteMPEGImage(const ImageInfo *image_info,
357 %        Image *image)
358 %
359 %  A description of each parameter follows.
360 %
361 %    o image_info: the image info.
362 %
363 %    o image:  The image.
364 %
365 */
366
367 static inline double MagickMax(const double x,const double y)
368 {
369   if (x > y)
370     return(x);
371   return(y);
372 }
373
374 static inline double MagickMin(const double x,const double y)
375 {
376   if (x < y)
377     return(x);
378   return(y);
379 }
380
381 static MagickBooleanType CopyDelegateFile(const char *source,
382   const char *destination)
383 {
384   int
385     destination_file,
386     source_file;
387
388   MagickBooleanType
389     status;
390
391   register size_t
392     i;
393
394   size_t
395     length,
396     quantum;
397
398   ssize_t
399     count;
400
401   struct stat
402     attributes;
403
404   unsigned char
405     *buffer;
406
407   /*
408     Return if destination file already exists and is not empty.
409   */
410   assert(source != (const char *) NULL);
411   assert(destination != (char *) NULL);
412   status=GetPathAttributes(destination,&attributes);
413   if ((status != MagickFalse) && (attributes.st_size != 0))
414     return(MagickTrue);
415   /*
416     Copy source file to destination.
417   */
418   destination_file=open(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
419   if (destination_file == -1)
420     return(MagickFalse);
421   source_file=open(source,O_RDONLY | O_BINARY);
422   if (source_file == -1)
423     {
424       (void) close(destination_file);
425       return(MagickFalse);
426     }
427   quantum=(size_t) MagickMaxBufferExtent;
428   if ((fstat(source_file,&attributes) == 0) && (attributes.st_size != 0))
429     quantum=(size_t) MagickMin((double) attributes.st_size,
430       MagickMaxBufferExtent);
431   buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
432   if (buffer == (unsigned char *) NULL)
433     {
434       (void) close(source_file);
435       (void) close(destination_file);
436       return(MagickFalse);
437     }
438   length=0;
439   for (i=0; ; i+=count)
440   {
441     count=(ssize_t) read(source_file,buffer,quantum);
442     if (count <= 0)
443       break;
444     length=(size_t) count;
445     count=(ssize_t) write(destination_file,buffer,length);
446     if ((size_t) count != length)
447       break;
448   }
449   (void) close(destination_file);
450   (void) close(source_file);
451   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
452   return(i != 0 ? MagickTrue : MagickFalse);
453 }
454
455 static MagickBooleanType WriteMPEGImage(const ImageInfo *image_info,
456   Image *image)
457 {
458 #define WriteMPEGIntermediateFormat "jpg"
459
460   char
461     basename[MaxTextExtent],
462     filename[MaxTextExtent];
463
464   double
465     delay;
466
467   Image
468     *coalesce_image;
469
470   ImageInfo
471     *write_info;
472
473   int
474     file;
475
476   MagickBooleanType
477     status;
478
479   register Image
480     *p;
481
482   register ssize_t
483     i;
484
485   size_t
486     count,
487     length,
488     scene;
489
490   unsigned char
491     *blob;
492
493   /*
494     Open output image file.
495   */
496   assert(image_info != (const ImageInfo *) NULL);
497   assert(image_info->signature == MagickSignature);
498   assert(image != (Image *) NULL);
499   assert(image->signature == MagickSignature);
500   if (image->debug != MagickFalse)
501     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
502   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
503   if (status == MagickFalse)
504     return(status);
505   (void) CloseBlob(image);
506   /*
507     Write intermediate files.
508   */
509   coalesce_image=CoalesceImages(image,&image->exception);
510   if (coalesce_image == (Image *) NULL)
511     return(MagickFalse);
512   file=AcquireUniqueFileResource(basename);
513   if (file != -1)
514     file=close(file)-1;
515   (void) FormatLocaleString(coalesce_image->filename,MaxTextExtent,"%s",
516     basename);
517   count=0;
518   write_info=CloneImageInfo(image_info);
519   for (p=coalesce_image; p != (Image *) NULL; p=GetNextImageInList(p))
520   {
521     char
522       previous_image[MaxTextExtent];
523
524     blob=(unsigned char *) NULL;
525     length=0;
526     scene=p->scene;
527     delay=100.0*p->delay/MagickMax(1.0*p->ticks_per_second,1.0);
528     for (i=0; i < (ssize_t) MagickMax((1.0*delay+1.0)/3.0,1.0); i++)
529     {
530       p->scene=count;
531       count++;
532       status=MagickFalse;
533       switch (i)
534       {
535         case 0:
536         {
537           Image
538             *frame;
539
540           (void) FormatLocaleString(p->filename,MaxTextExtent,"%s%.20g.%s",
541             basename,(double) p->scene,WriteMPEGIntermediateFormat);
542           (void) FormatLocaleString(filename,MaxTextExtent,"%s%.20g.%s",
543             basename,(double) p->scene,WriteMPEGIntermediateFormat);
544           (void) FormatLocaleString(previous_image,MaxTextExtent,
545             "%s%.20g.%s",basename,(double) p->scene,
546             WriteMPEGIntermediateFormat);
547           frame=CloneImage(p,0,0,MagickTrue,&p->exception);
548           if (frame == (Image *) NULL)
549             break;
550           status=WriteImage(write_info,frame);
551           frame=DestroyImage(frame);
552           break;
553         }
554         case 1:
555         {
556           blob=(unsigned char *) FileToBlob(previous_image,~0UL,&length,
557             &image->exception);
558         }
559         default:
560         {
561           (void) FormatLocaleString(filename,MaxTextExtent,"%s%.20g.%s",
562             basename,(double) p->scene,WriteMPEGIntermediateFormat);
563           if (length > 0)
564             status=BlobToFile(filename,blob,length,&image->exception);
565           break;
566         }
567       }
568       if (image->debug != MagickFalse)
569         {
570           if (status != MagickFalse)
571             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
572               "%.20g. Wrote %s file for scene %.20g:",(double) i,
573               WriteMPEGIntermediateFormat,(double) p->scene);
574           else
575             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
576               "%.20g. Failed to write %s file for scene %.20g:",(double) i,
577               WriteMPEGIntermediateFormat,(double) p->scene);
578           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",filename);
579         }
580     }
581     p->scene=scene;
582     if (blob != (unsigned char *) NULL)
583       blob=(unsigned char *) RelinquishMagickMemory(blob);
584     if (status == MagickFalse)
585       break;
586   }
587   /*
588     Convert JPEG to MPEG.
589   */
590   (void) CopyMagickString(coalesce_image->magick_filename,basename,
591     MaxTextExtent);
592   (void) CopyMagickString(coalesce_image->filename,basename,MaxTextExtent);
593   GetPathComponent(image_info->filename,ExtensionPath,coalesce_image->magick);
594   if (*coalesce_image->magick == '\0')
595     (void) CopyMagickString(coalesce_image->magick,image->magick,MaxTextExtent);
596   status=InvokeDelegate(write_info,coalesce_image,(char *) NULL,"mpeg:encode",
597     &image->exception);
598   (void) FormatLocaleString(write_info->filename,MaxTextExtent,"%s.%s",
599     write_info->unique,coalesce_image->magick);
600   status=CopyDelegateFile(write_info->filename,image->filename);
601   (void) RelinquishUniqueFileResource(write_info->filename);
602   write_info=DestroyImageInfo(write_info);
603   /*
604     Relinquish resources.
605   */
606   count=0;
607   for (p=coalesce_image; p != (Image *) NULL; p=GetNextImageInList(p))
608   {
609     delay=100.0*p->delay/MagickMax(1.0*p->ticks_per_second,1.0);
610     for (i=0; i < (ssize_t) MagickMax((1.0*delay+1.0)/3.0,1.0); i++)
611     {
612       (void) FormatLocaleString(p->filename,MaxTextExtent,"%s%.20g.%s",
613         basename,(double) count++,WriteMPEGIntermediateFormat);
614       (void) RelinquishUniqueFileResource(p->filename);
615     }
616     (void) CopyMagickString(p->filename,image_info->filename,MaxTextExtent);
617   }
618   (void) RelinquishUniqueFileResource(basename);
619   coalesce_image=DestroyImageList(coalesce_image);
620   if (image->debug != MagickFalse)
621     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit");
622   return(status);
623 }