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