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