root / ImageMagick / trunk / coders / mpeg.c

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