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