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