]> 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-2009 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 M P E G                                                               %
76 %                                                                             %
77 %                                                                             %
78 %                                                                             %
79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 %
81 %  IsMPEG() returns MagickTrue if the image format type, identified by the
82 %  magick string, is MPEG.
83 %
84 %  The format of the IsMPEG method is:
85 %
86 %      MagickBooleanType IsMPEG(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 IsMPEG(const unsigned char *magick,const size_t length)
96 {
97   if (length < 4)
98     return(MagickFalse);
99   if (memcmp(magick,"\000\000\001\263",4) == 0)
100     return(MagickTrue);
101   return(MagickFalse);
102 }
103 \f
104 /*
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 %                                                                             %
107 %                                                                             %
108 %                                                                             %
109 %   R e a d M P E G I m a g e                                                 %
110 %                                                                             %
111 %                                                                             %
112 %                                                                             %
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 %
115 %  ReadMPEGImage() reads an binary file in the MPEG video stream format
116 %  and returns it.  It allocates the memory necessary for the new Image
117 %  structure and returns a pointer to the new image.
118 %
119 %  The format of the ReadMPEGImage method is:
120 %
121 %      Image *ReadMPEGImage(const ImageInfo *image_info,
122 %        ExceptionInfo *exception)
123 %
124 %  A description of each parameter follows:
125 %
126 %    o image_info: the image info.
127 %
128 %    o exception: return any errors or warnings in this structure.
129 %
130 */
131 static Image *ReadMPEGImage(const ImageInfo *image_info,
132   ExceptionInfo *exception)
133 {
134 #define ReadMPEGIntermediateFormat "pam"
135
136   Image
137     *image,
138     *images;
139
140   ImageInfo
141     *read_info;
142
143   MagickBooleanType
144     status;
145
146   /*
147     Open image file.
148   */
149   assert(image_info != (const ImageInfo *) NULL);
150   assert(image_info->signature == MagickSignature);
151   if (image_info->debug != MagickFalse)
152     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
153       image_info->filename);
154   assert(exception != (ExceptionInfo *) NULL);
155   assert(exception->signature == MagickSignature);
156   image=AcquireImage(image_info);
157   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
158   if (status == MagickFalse)
159     {
160       image=DestroyImageList(image);
161       return((Image *) NULL);
162     }
163   (void) CloseBlob(image);
164   (void) DestroyImageList(image);
165   /*
166     Convert MPEG to PAM with delegate.
167   */
168   read_info=CloneImageInfo(image_info);
169   image=AcquireImage(image_info);
170   (void) InvokeDelegate(read_info,image,"mpeg:decode",(char *) NULL,exception);
171   image=DestroyImage(image);
172   (void) FormatMagickString(read_info->filename,MaxTextExtent,"%s.%s",
173     read_info->unique,ReadMPEGIntermediateFormat);
174   images=ReadImage(read_info,exception);
175   (void) RelinquishUniqueFileResource(read_info->filename);
176   read_info=DestroyImageInfo(read_info);
177   return(images);
178 }
179 \f
180 /*
181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182 %                                                                             %
183 %                                                                             %
184 %                                                                             %
185 %   R e g i s t e r M P E G I m a g e                                         %
186 %                                                                             %
187 %                                                                             %
188 %                                                                             %
189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190 %
191 %  RegisterMPEGImage() adds attributes for the MPEG image format to
192 %  the list of supported formats.  The attributes include the image format
193 %  tag, a method to read and/or write the format, whether the format
194 %  supports the saving of more than one frame to the same file or blob,
195 %  whether the format supports native in-memory I/O, and a brief
196 %  description of the format.
197 %
198 %  The format of the RegisterMPEGImage method is:
199 %
200 %      unsigned long RegisterMPEGImage(void)
201 %
202 */
203 ModuleExport unsigned long RegisterMPEGImage(void)
204 {
205   MagickInfo
206     *entry;
207
208   entry=SetMagickInfo("MOV");
209   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
210   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
211   entry->magick=(IsImageFormatHandler *) IsMPEG;
212   entry->blob_support=MagickFalse;
213   entry->description=ConstantString("MPEG Video Stream");
214   entry->module=ConstantString("MPEG");
215   (void) RegisterMagickInfo(entry);
216   entry=SetMagickInfo("MPEG");
217   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
218   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
219   entry->magick=(IsImageFormatHandler *) IsMPEG;
220   entry->blob_support=MagickFalse;
221   entry->description=ConstantString("MPEG Video Stream");
222   entry->module=ConstantString("MPEG");
223   (void) RegisterMagickInfo(entry);
224   entry=SetMagickInfo("MPG");
225   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
226   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
227   entry->magick=(IsImageFormatHandler *) IsMPEG;
228   entry->blob_support=MagickFalse;
229   entry->description=ConstantString("MPEG Video Stream");
230   entry->module=ConstantString("MPEG");
231   (void) RegisterMagickInfo(entry);
232   entry=SetMagickInfo("MP4");
233   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
234   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
235   entry->magick=(IsImageFormatHandler *) IsMPEG;
236   entry->blob_support=MagickFalse;
237   entry->description=ConstantString("MPEG-4 Video Stream");
238   entry->module=ConstantString("MPEG");
239   (void) RegisterMagickInfo(entry);
240   entry=SetMagickInfo("M2V");
241   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
242   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
243   entry->magick=(IsImageFormatHandler *) IsMPEG;
244   entry->blob_support=MagickFalse;
245   entry->description=ConstantString("MPEG Video Stream");
246   entry->module=ConstantString("MPEG");
247   (void) RegisterMagickInfo(entry);
248   entry=SetMagickInfo("M4V");
249   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
250   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
251   entry->magick=(IsImageFormatHandler *) IsMPEG;
252   entry->blob_support=MagickFalse;
253   entry->description=ConstantString("Raw MPEG-4 Video");
254   entry->module=ConstantString("MPEG");
255   (void) RegisterMagickInfo(entry);
256   entry=SetMagickInfo("WMV");
257   entry->decoder=(DecodeImageHandler *) ReadMPEGImage;
258   entry->encoder=(EncodeImageHandler *) WriteMPEGImage;
259   entry->magick=(IsImageFormatHandler *) IsMPEG;
260   entry->blob_support=MagickFalse;
261   entry->description=ConstantString("Windows Media Video");
262   entry->module=ConstantString("MPEG");
263   (void) RegisterMagickInfo(entry);
264   return(MagickImageCoderSignature);
265 }
266 \f
267 /*
268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269 %                                                                             %
270 %                                                                             %
271 %                                                                             %
272 %   U n r e g i s t e r M P E G I m a g e                                     %
273 %                                                                             %
274 %                                                                             %
275 %                                                                             %
276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277 %
278 %  UnregisterMPEGImage() removes format registrations made by the
279 %  BIM module from the list of supported formats.
280 %
281 %  The format of the UnregisterBIMImage method is:
282 %
283 %      UnregisterMPEGImage(void)
284 %
285 */
286 ModuleExport void UnregisterMPEGImage(void)
287 {
288   (void) UnregisterMagickInfo("WMV");
289   (void) UnregisterMagickInfo("M4V");
290   (void) UnregisterMagickInfo("M2V");
291   (void) UnregisterMagickInfo("MP4");
292   (void) UnregisterMagickInfo("MPG");
293   (void) UnregisterMagickInfo("MPEG");
294   (void) UnregisterMagickInfo("MOV");
295 }
296 \f
297 /*
298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
299 %                                                                             %
300 %                                                                             %
301 %                                                                             %
302 %   W r i t e M P E G I m a g e                                               %
303 %                                                                             %
304 %                                                                             %
305 %                                                                             %
306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307 %
308 %  WriteMPEGImage() writes an image to a file in MPEG video stream format.
309 %  Lawrence Livermore National Laboratory (LLNL) contributed code to adjust
310 %  the MPEG parameters to correspond to the compression quality setting.
311 %
312 %  The format of the WriteMPEGImage method is:
313 %
314 %      MagickBooleanType WriteMPEGImage(const ImageInfo *image_info,
315 %        Image *image)
316 %
317 %  A description of each parameter follows.
318 %
319 %    o image_info: the image info.
320 %
321 %    o image:  The image.
322 %
323 */
324
325 static inline double MagickMax(const double x,const double y)
326 {
327   if (x > y)
328     return(x);
329   return(y);
330 }
331
332 static inline double MagickMin(const double x,const double y)
333 {
334   if (x < y)
335     return(x);
336   return(y);
337 }
338
339 static MagickBooleanType CopyDelegateFile(const char *source,
340   const char *destination)
341 {
342   int
343     destination_file,
344     source_file;
345
346   MagickBooleanType
347     status;
348
349   register size_t
350     i;
351
352   size_t
353     length,
354     quantum;
355
356   ssize_t
357     count;
358
359   struct stat
360     attributes;
361
362   unsigned char
363     *buffer;
364
365   /*
366     Return if destination file already exists and is not empty.
367   */
368   assert(source != (const char *) NULL);
369   assert(destination != (char *) NULL);
370   status=GetPathAttributes(destination,&attributes);
371   if ((status != MagickFalse) && (attributes.st_size != 0))
372     return(MagickTrue);
373   /*
374     Copy source file to destination.
375   */
376   destination_file=open(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
377   if (destination_file == -1)
378     return(MagickFalse);
379   source_file=open(source,O_RDONLY | O_BINARY);
380   if (source_file == -1)
381     {
382       (void) close(destination_file);
383       return(MagickFalse);
384     }
385   quantum=(size_t) MagickMaxBufferExtent;
386   if ((fstat(source_file,&attributes) == 0) && (attributes.st_size != 0))
387     quantum=(size_t) MagickMin((double) attributes.st_size,
388       MagickMaxBufferExtent);
389   buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
390   if (buffer == (unsigned char *) NULL)
391     {
392       (void) close(source_file);
393       (void) close(destination_file);
394       return(MagickFalse);
395     }
396   length=0;
397   for (i=0; ; i+=count)
398   {
399     count=(ssize_t) read(source_file,buffer,quantum);
400     if (count <= 0)
401       break;
402     length=(size_t) count;
403     count=(ssize_t) write(destination_file,buffer,length);
404     if ((size_t) count != length)
405       break;
406   }
407   (void) close(destination_file);
408   (void) close(source_file);
409   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
410   return(i != 0 ? MagickTrue : MagickFalse);
411 }
412
413 static MagickBooleanType WriteMPEGImage(const ImageInfo *image_info,
414   Image *image)
415 {
416 #define WriteMPEGIntermediateFormat "jpg"
417
418   char
419     basename[MaxTextExtent],
420     filename[MaxTextExtent];
421
422   double
423     delay;
424
425   Image
426     *coalesce_image;
427
428   ImageInfo
429     *write_info;
430
431   int
432     file;
433
434   MagickBooleanType
435     status;
436
437   register Image
438     *p;
439
440   register long
441     i;
442
443   size_t
444     length;
445
446   unsigned char
447     *blob;
448
449   unsigned long
450     count,
451     scene;
452
453   /*
454     Open output image file.
455   */
456   assert(image_info != (const ImageInfo *) NULL);
457   assert(image_info->signature == MagickSignature);
458   assert(image != (Image *) NULL);
459   assert(image->signature == MagickSignature);
460   if (image->debug != MagickFalse)
461     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
462   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
463   if (status == MagickFalse)
464     return(status);
465   (void) CloseBlob(image);
466   /*
467     Write intermediate files.
468   */
469   coalesce_image=CoalesceImages(image,&image->exception);
470   if (coalesce_image == (Image *) NULL)
471     return(MagickFalse);
472   file=AcquireUniqueFileResource(basename);
473   if (file != -1)
474     file=close(file)-1;
475   (void) FormatMagickString(coalesce_image->filename,MaxTextExtent,"%s",
476     basename);
477   count=0;
478   write_info=CloneImageInfo(image_info);
479   for (p=coalesce_image; p != (Image *) NULL; p=GetNextImageInList(p))
480   {
481     char
482       previous_image[MaxTextExtent];
483
484     blob=(unsigned char *) NULL;
485     length=0;
486     scene=p->scene;
487     delay=100.0*p->delay/MagickMax(1.0*p->ticks_per_second,1.0);
488     for (i=0; i < (long) MagickMax((1.0*delay+1.0)/3.0,1.0); i++)
489     {
490       p->scene=count;
491       count++;
492       status=MagickFalse;
493       switch (i)
494       {
495         case 0:
496         {
497           Image
498             *frame;
499
500           (void) FormatMagickString(p->filename,MaxTextExtent,"%s%lu.%s",
501             basename,p->scene,WriteMPEGIntermediateFormat);
502           (void) FormatMagickString(filename,MaxTextExtent,"%s%lu.%s",
503             basename,p->scene,WriteMPEGIntermediateFormat);
504           (void) FormatMagickString(previous_image,MaxTextExtent,
505             "%s%lu.%s",basename,p->scene,WriteMPEGIntermediateFormat);
506           frame=CloneImage(p,0,0,MagickTrue,&p->exception);
507           if (frame == (Image *) NULL)
508             break;
509           status=WriteImage(write_info,frame);
510           frame=DestroyImage(frame);
511           break;
512         }
513         case 1:
514         {
515           blob=(unsigned char *) FileToBlob(previous_image,~0UL,&length,
516             &image->exception);
517         }
518         default:
519         {
520           (void) FormatMagickString(filename,MaxTextExtent,"%s%lu.%s",
521             basename,p->scene,WriteMPEGIntermediateFormat);
522           if (length > 0)
523             status=BlobToFile(filename,blob,length,&image->exception);
524           break;
525         }
526       }
527       if (image->debug != MagickFalse)
528         {
529           if (status != MagickFalse)
530             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
531               "%lu. Wrote %s file for scene %lu:",i,WriteMPEGIntermediateFormat,
532               p->scene);
533           else
534             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
535               "%lu. Failed to write %s file for scene %lu:",i,
536               WriteMPEGIntermediateFormat,p->scene);
537           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",
538             filename);
539         }
540     }
541     p->scene=scene;
542     if (blob != (unsigned char *) NULL)
543       blob=(unsigned char *) RelinquishMagickMemory(blob);
544     if (status == MagickFalse)
545       break;
546   }
547   /*
548     Convert JPEG to MPEG.
549   */
550   (void) CopyMagickString(coalesce_image->magick_filename,basename,
551     MaxTextExtent);
552   (void) CopyMagickString(coalesce_image->filename,basename,MaxTextExtent);
553   GetPathComponent(image_info->filename,ExtensionPath,coalesce_image->magick);
554   if (*coalesce_image->magick == '\0')
555     (void) CopyMagickString(coalesce_image->magick,image->magick,MaxTextExtent);
556   status=InvokeDelegate(write_info,coalesce_image,(char *) NULL,"mpeg:encode",
557     &image->exception);
558   (void) FormatMagickString(write_info->filename,MaxTextExtent,"%s.%s",
559     write_info->unique,coalesce_image->magick);
560   status=CopyDelegateFile(write_info->filename,image->filename);
561   (void) RelinquishUniqueFileResource(write_info->filename);
562   write_info=DestroyImageInfo(write_info);
563   /*
564     Relinquish resources.
565   */
566   count=0;
567   for (p=coalesce_image; p != (Image *) NULL; p=GetNextImageInList(p))
568   {
569     delay=100.0*p->delay/MagickMax(1.0*p->ticks_per_second,1.0);
570     for (i=0; i < (long) MagickMax((1.0*delay+1.0)/3.0,1.0); i++)
571     {
572       (void) FormatMagickString(p->filename,MaxTextExtent,"%s%lu.%s",
573         basename,count++,WriteMPEGIntermediateFormat);
574       (void) RelinquishUniqueFileResource(p->filename);
575     }
576     (void) CopyMagickString(p->filename,image_info->filename,MaxTextExtent);
577   }
578   (void) RelinquishUniqueFileResource(basename);
579   coalesce_image=DestroyImageList(coalesce_image);
580   if (image->debug != MagickFalse)
581     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit");
582   return(status);
583 }