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