2013/uns13/projects: MakeQTMovie.m

File MakeQTMovie.m, 27.6 KB (added by timmer, 5 years ago)

Make a QuickTime? movie.

Line 
1function MakeQTMovie(cmd,arg, arg2)
2% function MakeQTMovie(cmd, arg, arg2)
3% Create a QuickTime movie from a bunch of figures (and an optional sound).
4%
5% Syntax: MakeQTMovie cmd [arg]
6% The following commands are supported:
7%       addfigure - Add snapshot of current figure to movie
8%       addaxes - Add snapshot of current axes to movie
9%       addmatrix data - Add a matrix to movie (convert to jpeg with imwrite)
10%       addmatrixsc data - Add a matrix to movie (convert to jpeg with imwrite)
11%               (automatically scales image data)
12%       addsound data [sr] - Add sound to movie (only monaural for now)
13%               (third argument is the sound's sample rate.)
14%       cleanup - Remove the temporary files
15%       demo - Create a demonstration movie
16%       finish - Finish movie, write out QT file
17%       framerate fps - Set movies frame rate [Default is 10 fps]
18%       quality # - Set JPEG quality (between 0 and 1)
19%       size [# #] - Set plot size to [width height]
20%       start filename - Start creating a movie with this name
21% The start command must be called first to provide a movie name.
22% The finish command must be called last to write out the movie
23% data. All other commands can be called in any order.  Only one
24% movie can be created at a time.
25%
26% This code is published as Interval Technical Report #1999-066
27% The latest copy can be found at
28%       http://web.interval.com/papers/1999-066/
29% (c) Copyright Malcolm Slaney, Interval Research, March 1999.
30
31% This is experimental software and is being provided to Licensee
32% 'AS IS.'  Although the software has been tested on Macintosh, SGI,
33% Linux, and Windows machines, Interval makes no warranties relating
34% to the software's performance on these or any other platforms.
35%
36% Disclaimer
37% THIS SOFTWARE IS BEING PROVIDED TO YOU 'AS IS.'  INTERVAL MAKES
38% NO EXPRESS, IMPLIED OR STATUTORY WARRANTY OF ANY KIND FOR THE
39% SOFTWARE INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF
40% PERFORMANCE, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
41% IN NO EVENT WILL INTERVAL BE LIABLE TO LICENSEE OR ANY THIRD
42% PARTY FOR ANY DAMAGES, INCLUDING LOST PROFITS OR OTHER INCIDENTAL
43% OR CONSEQUENTIAL DAMAGES, EVEN IF INTERVAL HAS BEEN ADVISED OF
44% THE POSSIBLITY THEREOF.
45%
46%   This software program is owned by Interval Research
47% Corporation, but may be used, reproduced, modified and
48% distributed by Licensee.  Licensee agrees that any copies of the
49% software program will contain the same proprietary notices and
50% warranty disclaimers which appear in this software program.
51
52% This program uses the Matlab imwrite routine to convert each image
53% frame into JPEG.  After first reserving 8 bytes for a header that points
54% to the movie description, all the compressed images and the sound are
55% added to the movie file.  When the 'finish' method is called then the
56% first 8 bytes of the header are rewritten to indicate the size of the
57% movie data, and then the movie header ('moov structure') is written
58% to the output file.
59%
60% This routine creates files according to the QuickTime file format as
61% described in the appendix of
62%       "Quicktime (Inside MacIntosh)," Apple Computer Incorporated,
63%       Addison-Wesley Pub Co; ISBN: 0201622017, April 1993.
64% I appreciate help that I received from Lee Fyock (MathWorks) and Aaron
65% Hertzmann (Interval) in debugging and testing this work.
66
67% Changes:
68% July 5, 1999 - Removed stss atom since it upset PC version of QuickTime
69% November 11, 1999 - Fixed quality bug in addmatrix.  Added addmatrixsc.
70% March 7, 2000 - by Jordan Rosenthal (jr@ece.gatech.edu), Added truecolor
71%    capability when running in Matlab 5.3 changed some help comments, fixed
72%    some bugs, vectorized some code.
73% April 7, 2000 - by Malcolm.  Cleaned up axis/figure code and fixed(?) SGI
74%    playback problems.  Added user data atom to give version information.
75%    Fixed sound format problems.
76% April 10, 2000 - by Malcolm. Fixed problem with SGI (at least) and B&W
77%    addmatrix.
78
79if nargin < 1
80        fprintf('Syntax: MakeQTMovie cmd [arg]\n')
81        fprintf('The following commands are supported:\n');
82        fprintf('       addfigure - Add snapshot of current figure to movie\n')
83        fprintf('       addaxes - Add snapshot of current axes to movie\n')
84        fprintf('       addmatrix data - Add a matrix to movie ');
85                        fprintf('(convert to jpeg)\n')
86        fprintf('       addmatrixsc data - Add a matrix to movie ');
87                        fprintf('(scale and convert to jpeg)\n')
88        fprintf('       addsound data - Add sound samples ');
89                        fprintf('(with optional rate)\n')
90        fprintf('       demo - Show this program in action\n');
91        fprintf('       finish - Finish movie, write out QT file\n');
92        fprintf('       framerate # - Set movie frame rate ');
93                        fprintf('(default is 10fps)\n');
94        fprintf('       quality # - Set JPEG quality (between 0 and 1)\n');
95        fprintf('       size [# #] - Set plot size to [width height]\n');
96        fprintf('       start filename - Start making a movie with ');
97                        fprintf('this name\n');
98        return;
99end
100
101global MakeQTMovieStatus
102MakeDefaultQTMovieStatus;               % Needed first time, ignored otherwise
103
104switch lower(cmd)
105case {'addframe','addplot','addfigure','addaxes'}
106        switch lower(cmd)
107        case {'addframe','addfigure'}
108                hObj = gcf;             % Add the entire figure (with all axes)
109        otherwise
110                hObj = gca;             % Add what's inside the current axis
111        end
112        frame = getframe(hObj);
113        [I,map] = frame2im(frame);
114        if ImageSizeChanged(size(I)) > 0
115                return;
116        end
117        if isempty(map)
118                                        % RGB image
119                imwrite(I,MakeQTMovieStatus.imageTmp, 'jpg', 'Quality', ...
120                 MakeQTMovieStatus.spatialQual*100);
121        else
122                                        % Indexed image
123                writejpg_map(MakeQTMovieStatus.imageTmp, I, map);
124        end
125        [pos, len] = AddFileToMovie;
126        n = MakeQTMovieStatus.frameNumber + 1;
127        MakeQTMovieStatus.frameNumber = n;
128        MakeQTMovieStatus.frameStarts(n) = pos;
129        MakeQTMovieStatus.frameLengths(n) = len;
130   
131%% Allow images to be added by doing:
132%%      MakeQTMovie('addimage', '/path/to/file.jpg');
133%% This case adapted from addmatrix.  Thanks to
134%% Stephen Eglen <stephen@cogsci.ed.ac.uk> for this idea.
135case 'addimage'
136        if nargin < 2
137                fprintf('MakeQTMovie error: Need to specify a filename with ');
138                fprintf('the image command.\n');
139                return;
140        end
141
142        %% Check to see that the image is the correct size.  Do
143        %% this by reading in the image and then checking its size.
144        %% tim - temporary image.
145        tim = imread(arg); tim_size = size(tim);
146       
147        fprintf('Image %s size %d %d\n', arg, tim_size(1), tim_size(2));
148        if ImageSizeChanged(tim_size) > 0
149                return;
150        end
151        [pos, len] = AddFileToMovie(arg);
152        n = MakeQTMovieStatus.frameNumber + 1;
153        MakeQTMovieStatus.frameNumber = n;
154        MakeQTMovieStatus.frameStarts(n) = pos;
155        MakeQTMovieStatus.frameLengths(n) = len;
156
157case 'addmatrix'
158        if nargin < 2
159                fprintf('MakeQTMovie error: Need to specify a matrix with ');
160                fprintf('the addmatrix command.\n');
161                return;
162        end
163        if ImageSizeChanged(size(arg)) > 0
164                return;
165        end
166                                        % Work around a bug, at least on the
167                                        % SGIs, which causes JPEGs to be
168                                        % written which can't be read with the
169                                        % SGI QT.  Turn the B&W image into a
170                                        % color matrix.
171        if ndims(arg) < 3
172                arg(:,:,2) = arg;
173                arg(:,:,3) = arg(:,:,1);
174        end
175        imwrite(arg, MakeQTMovieStatus.imageTmp, 'jpg', 'Quality', ...
176                MakeQTMovieStatus.spatialQual*100);
177        [pos, len] = AddFileToMovie;
178        n = MakeQTMovieStatus.frameNumber + 1;
179        MakeQTMovieStatus.frameNumber = n;
180        MakeQTMovieStatus.frameStarts(n) = pos;
181        MakeQTMovieStatus.frameLengths(n) = len;
182       
183case 'addmatrixsc'
184        if nargin < 2
185                fprintf('MakeQTMovie error: Need to specify a matrix with ');
186                fprintf('the addmatrix command.\n');
187                return;
188        end
189        if ImageSizeChanged(size(arg)) > 0
190                return;
191        end
192        arg = arg - min(min(arg));
193        arg = arg / max(max(arg));
194                                        % Work around a bug, at least on the
195                                        % SGIs, which causes JPEGs to be
196                                        % written which can't be read with the
197                                        % SGI QT.  Turn the B&W image into a
198                                        % color matrix.
199        if ndims(arg) < 3
200                arg(:,:,2) = arg;
201                arg(:,:,3) = arg(:,:,1);
202        end
203        imwrite(arg, MakeQTMovieStatus.imageTmp, 'jpg', 'Quality', ...
204                MakeQTMovieStatus.spatialQual*100);
205        [pos, len] = AddFileToMovie;
206        n = MakeQTMovieStatus.frameNumber + 1;
207        MakeQTMovieStatus.frameNumber = n;
208        MakeQTMovieStatus.frameStarts(n) = pos;
209        MakeQTMovieStatus.frameLengths(n) = len;
210
211case 'addsound'
212        if nargin < 2
213                fprintf('MakeQTMovie error: Need to specify a sound array ');
214                fprintf('with the addsound command.\n');
215                return;
216        end
217                                        % Do stereo someday???
218        OpenMovieFile
219        MakeQTMovieStatus.soundLength = length(arg);
220        arg = round(arg/max(max(abs(arg)))*32765);
221        negs = find(arg<0);
222        arg(negs) = arg(negs) + 65536;
223
224        sound = mb16(arg);
225        MakeQTMovieStatus.soundStart = ftell(MakeQTMovieStatus.movieFp);
226        MakeQTMovieStatus.soundLen = length(sound);
227        fwrite(MakeQTMovieStatus.movieFp, sound, 'uchar');
228        if nargin < 3
229                arg2 = 22050;
230        end
231        MakeQTMovieStatus.soundRate = arg2;
232       
233case 'cleanup'
234        if isstruct(MakeQTMovieStatus)
235                if ~isempty(MakeQTMovieStatus.movieFp)
236                        fclose(MakeQTMovieStatus.movieFp);
237                        MakeQTMovieStatus.movieFp = [];
238                end
239                if ~isempty(MakeQTMovieStatus.imageTmp) & ...
240                   exist(MakeQTMovieStatus.imageTmp,'file') > 0
241                        delete(MakeQTMovieStatus.imageTmp);
242                        MakeQTMovieStatus.imageTmp = [];
243                end
244        end
245        MakeQTMovieStatus = [];
246
247case 'debug'
248        fprintf('Current Movie Data:\n');
249        fprintf('    %d frames at %d fps\n', MakeQTMovieStatus.frameNumber, ...
250                                        MakeQTMovieStatus.frameRate);
251        starts = MakeQTMovieStatus.frameStarts;
252        if length(starts) > 10, starts = starts(1:10);, end;
253        lens = MakeQTMovieStatus.frameLengths;
254        if length(lens) > 10, lens = lens(1:10);, end;
255        fprintf('         Start: %6d      Size: %6d\n', [starts; lens]);
256        fprintf('    Movie Image Size: %dx%d\n', ...
257                MakeQTMovieStatus.imageSize(2), ...);
258                MakeQTMovieStatus.imageSize(1));
259        if length(MakeQTMovieStatus.soundStart) > 0 
260                fprintf('    Sound: %d samples at %d Hz sampling rate ', ...
261                        MakeQTMovieStatus.soundLength, ...
262                        MakeQTMovieStatus.soundRate);
263                fprintf('at %d.\n', MakeQTMovieStatus.soundStart);
264        else
265                fprintf('    Sound: No sound track\n');
266        end
267        fprintf('    Temporary files for images: %s\n', ...
268                MakeQTMovieStatus.imageTmp);
269        fprintf('    Final movie name: %s\n', MakeQTMovieStatus.movieName);
270        fprintf('    Compression Quality: %g\n', ...
271                MakeQTMovieStatus.spatialQual);
272
273
274case 'demo'
275        clf
276        fps = 10;
277        movieLength = 10;
278        sr = 22050;
279        fn = 'test.mov';
280        fprintf('Creating the movie %s.\n', fn);
281        MakeQTMovie('start',fn);
282        MakeQTMovie('size', [160 120]);
283        MakeQTMovie('quality', 1.0);
284        theSound = [];
285        for i=1:movieLength
286                plot(sin((1:100)/4+i));
287                MakeQTMovie('addaxes');
288                theSound = [theSound sin(440/sr*2*pi*(2^(i/12))*(1:sr/fps))];
289        end
290        MakeQTMovie('framerate', fps);
291        MakeQTMovie('addsound', theSound, sr);
292        MakeQTMovie('finish');
293
294case {'finish','close'}
295        AddQTHeader;
296        MakeQTMovie('cleanup')                  % Remove temporary files
297
298case 'framerate'
299        if nargin < 2
300                fprintf('MakeQTMovie error: Need to specify the ');
301                fprintf('frames/second with the framerate command.\n');
302                return;
303        end
304        MakeQTMovieStatus.frameRate = arg;
305
306case 'help'
307        MakeQTMovie                             % To get help message.
308
309case 'size'
310                                                % Size is off by one on the
311                                                % Mac.
312        if nargin < 2
313                fprintf('MakeQTMovie error: Need to specify a vector with ');
314                fprintf('the size command.\n');
315                return;
316        end
317        if length(arg) ~= 2
318                error('MakeQTMovie: Error, must supply 2 element size.');
319        end
320        oldUnits = get(gcf,'units');
321        set(gcf,'units','pixels');
322        cursize = get(gcf, 'position');
323        cursize(3) = arg(1);
324        cursize(4) = arg(2);
325        set(gcf, 'position', cursize);
326        set(gcf,'units',oldUnits);
327
328case 'start'
329        if nargin < 2
330                fprintf('MakeQTMovie error: Need to specify a file name ');
331                fprintf('with start command.\n');
332                return;
333        end
334        MakeQTMovie('cleanup');
335        MakeDefaultQTMovieStatus;
336        MakeQTMovieStatus.movieName = arg;
337       
338case 'test'
339        clf
340        MakeQTMovieStatus = [];
341        MakeQTMovie('start','test.mov');
342        MakeQTMovie('size', [320 240]);
343        MakeQTMovie('quality', 1.0);
344        subplot(2,2,1);
345        for i=1:10
346                plot(sin((1:100)/4+i));
347                MakeQTMovie('addfigure');
348        end
349        MakeQTMovie('framerate', 10);
350        MakeQTMovie('addsound', sin(1:5000), 22050);
351        MakeQTMovie('debug');
352        MakeQTMovie('finish');
353       
354case 'quality'
355        if nargin < 2
356                fprintf('MakeQTMovie error: Need to specify a quality ');
357                fprintf('(between 0-1) with the quality command.\n');
358                return;
359        end
360        MakeQTMovieStatus.spatialQual = arg;
361
362otherwise
363        fprintf('MakeQTMovie: Unknown method %s.\n', cmd);
364end
365
366%%%%%%%%%%%%%%%  MakeDefaultQTMovieStatus %%%%%%%%%%%%%%%%%
367% Make the default movie status structure.
368function MakeDefaultQTMovieStatus
369global MakeQTMovieStatus
370if isempty(MakeQTMovieStatus)
371   MakeQTMovieStatus = struct(...
372      'frameRate', 10, ...      % frames per second
373      'frameStarts', [], ...  % Starting byte position
374      'frameLengths', [], ...
375      'timeScale', 10, ...      % How much faster does time run?
376      'soundRate', 22050, ... % Sound Sample Rate
377      'soundStart', [], ...     % Starting byte position
378      'soundLength', 0, ...
379      'soundChannels', 1, ...   % Number of channels
380      'frameNumber', 0, ...
381      'movieFp', [], ...                % File pointer
382      'imageTmp', tempname, ...
383      'movieName', 'output.mov', ...
384      'imageSize', [0 0], ...
385      'trackNumber', 0, ...
386      'timeScaleExpansion', 100, ...
387      'spatialQual', 1.0);      % Between 0.0 and 1.0
388end
389
390
391%%%%%%%%%%%%%%%  ImageSizeChanged %%%%%%%%%%%%%%%%%
392% Check to see if the image size has changed.  This m-file can't
393% deal with that, so we'll return an error.
394function err = ImageSizeChanged(newsize)
395global MakeQTMovieStatus
396
397newsize = newsize(1:2);                 % Don't care about RGB info, if present
398oldsize = MakeQTMovieStatus.imageSize;
399err = 0;
400
401if sum(oldsize) == 0
402        MakeQTMovieStatus.imageSize = newsize;
403else
404        if sum(newsize ~= oldsize) > 0
405                fprintf('MakeQTMovie Error: New image size');
406                fprintf('(%dx%d) doesn''t match old size (%dx%d)\n', ...
407                        newsize(1), newsize(2), oldsize(1), oldsize(2));
408                fprintf('   Can''t add this image to the movie.\n');
409                err = 1;
410        end
411end
412
413%%%%%%%%%%%%%%%  AddFileToMovie %%%%%%%%%%%%%%%%%
414% OK, we've saved out an image file.  Now add it to the end of the movie
415% file we are creating.
416% We'll copy the JPEG file in 16kbyte chunks to the end of the movie file.
417% Keep track of the start and end byte position in the file so we can put
418% the right information into the QT header.
419function [pos, len] = AddFileToMovie(imageTmp)
420global MakeQTMovieStatus
421OpenMovieFile
422if nargin < 1
423        imageTmp = MakeQTMovieStatus.imageTmp;
424end
425fp = fopen(imageTmp, 'rb');
426if fp < 0
427        error('Could not reopen QT image temporary file.');
428end
429
430len = 0;
431pos = ftell(MakeQTMovieStatus.movieFp);
432while 1
433        data = fread(fp, 1024*16, 'uchar');
434        if isempty(data)
435                break;
436        end
437        cnt = fwrite(MakeQTMovieStatus.movieFp, data, 'uchar');
438        len = len + cnt;
439end
440fclose(fp);
441
442%%%%%%%%%%%%%%%  AddQTHeader %%%%%%%%%%%%%%%%%
443% Go back and write the atom information that allows
444% QuickTime to skip the image and sound data and find
445% its movie description information.
446function AddQTHeader()
447global MakeQTMovieStatus
448
449pos = ftell(MakeQTMovieStatus.movieFp);
450header = moov_atom;
451cnt = fwrite(MakeQTMovieStatus.movieFp, header, 'uchar');
452fseek(MakeQTMovieStatus.movieFp, 0, -1);
453cnt = fwrite(MakeQTMovieStatus.movieFp, mb32(pos), 'uchar');
454fclose(MakeQTMovieStatus.movieFp);
455MakeQTMovieStatus.movieFp = [];
456
457%%%%%%%%%%%%%%%  OpenMovieFile %%%%%%%%%%%%%%%%%
458% Open a new movie file.  Write out the initial QT header.  We'll fill in
459% the correct length later.
460function OpenMovieFile
461global MakeQTMovieStatus
462if isempty(MakeQTMovieStatus.movieFp)
463        fp = fopen(MakeQTMovieStatus.movieName, 'wb');
464        if fp < 0
465                error('Could not open QT movie output file.');
466        end
467        MakeQTMovieStatus.movieFp = fp;
468        cnt = fwrite(fp, [mb32(0) mbstring('mdat')], 'uchar');
469end
470
471%%%%%%%%%%%%%%%  writejpg_map %%%%%%%%%%%%%%%%%
472% Like the imwrite routine, but first pass the image data through the indicated
473% RGB map.
474function writejpg_map(name,I,map)
475global MakeQTMovieStatus
476
477[y,x] = size(I);
478
479% Force values to be valid indexes.  This fixes a bug that occasionally
480% occurs in frame2im in Matlab 5.2 which incorrectly produces values of I
481% equal to zero.
482I = max(1,min(I,size(map,1)));
483
484rgb = zeros(y, x, 3);
485t = zeros(y,x);
486t(:) = map(I(:),1)*255; rgb(:,:,1) = t;
487t(:) = map(I(:),2)*255; rgb(:,:,2) = t;
488t(:) = map(I(:),3)*255; rgb(:,:,3) = t;
489
490imwrite(uint8(rgb),name,'jpeg','Quality',MakeQTMovieStatus.spatialQual*100);
491
492%%%%%%%%%%%%%%%  SetAtomSize %%%%%%%%%%%%%%%%%
493% Fill in the size of the atom
494function y=SetAtomSize(x)
495y = x;
496y(1:4) = mb32(length(x));
497
498%%%%%%%%%%%%%%%  mb32 %%%%%%%%%%%%%%%%%
499% Make a vector from a 32 bit integer
500function y = mb32(x)                           
501if size(x,1) > size(x,2)
502        x = x';
503end
504
505y = [bitand(bitshift(x,-24),255); ...
506     bitand(bitshift(x,-16),255); ...
507     bitand(bitshift(x, -8),255); ...
508     bitand(x,              255)];
509y = y(:)';
510
511%%%%%%%%%%%%%%%  mb16 %%%%%%%%%%%%%%%%%
512% Make a vector from a 16 bit integer
513function y = mb16(x)
514if size(x,1) > size(x,2)
515        x = x';
516end
517
518y = [bitand(bitshift(x, -8),255); ...
519     bitand(x,              255)];
520y = y(:)';
521
522%%%%%%%%%%%%%%%  mb8 %%%%%%%%%%%%%%%%%
523% Make a vector from a 8 bit integer
524function y = mb8(x)
525if size(x,1) > size(x,2)
526        x = x';
527end
528
529y = [bitand(x,              255)];
530y = y(:)';
531
532%
533% The following routines all create atoms necessary
534% to describe a QuickTime Movie. The basic idea is to
535% fill in the necessary data, all converted to 8 bit
536% characters, then fix it up later with SetAtomSize so
537% that it has the correct header.  (This is easier than
538% counting by hand.)
539
540%%%%%%%%%%%%%%%  mbstring %%%%%%%%%%%%%%%%%
541% Make a vector from a character string
542function y = mbstring(s)
543y = double(s);
544
545
546%%%%%%%%%%%%%%%  dinf_atom %%%%%%%%%%%%%%%%%
547function y = dinf_atom()
548y = SetAtomSize([mb32(0) mbstring('dinf') dref_atom]);
549
550%%%%%%%%%%%%%%%  dref_atom %%%%%%%%%%%%%%%%%
551function y = dref_atom()
552y = SetAtomSize([mb32(0) mbstring('dref') mb32(0) mb32(1) ...
553                mb32(12) mbstring('alis') mb32(1)]);
554
555%%%%%%%%%%%%%%%  edts_atom %%%%%%%%%%%%%%%%%
556function y = edts_atom(add_sound_p)
557global MakeQTMovieStatus
558fixed1 = bitshift(1,16);                        % Fixed point 1
559if add_sound_p > 0
560        duration = MakeQTMovieStatus.soundLength / ...
561                        MakeQTMovieStatus.soundRate * ...
562                        MakeQTMovieStatus.timeScale;
563else
564        duration = MakeQTMovieStatus.frameNumber / ...
565                        MakeQTMovieStatus.frameRate * ...
566                        MakeQTMovieStatus.timeScale;
567end
568duration = ceil(duration);
569
570y = [mb32(0) ...                                % Atom Size
571     mbstring('edts') ...                       % Atom Name
572     SetAtomSize([mb32(0) ...                   % Atom Size
573                  mbstring('elst') ...          % Atom Name
574                  mb32(0) ...                   % Version/Flags
575                  mb32(1) ...                   % Number of entries
576                  mb32(duration) ...            % Length of this track
577                  mb32(0) ...                   % Time
578                  mb32(fixed1)])];              % Rate
579y = SetAtomSize(y);
580
581%%%%%%%%%%%%%%%  hdlr_atom %%%%%%%%%%%%%%%%%
582function y = hdlr_atom(component_type, sub_type)
583if strcmp(sub_type, 'vide')
584        type_string = 'Apple Video Media Handler';
585elseif strcmp(sub_type, 'alis')
586        type_string = 'Apple Alias Data Handler';
587elseif strcmp(sub_type, 'soun')
588        type_string = 'Apple Sound Media Handler';
589end
590
591y = [mb32(0) ...                                % Atom Size
592     mbstring('hdlr') ...                       % Atom Name
593     mb32(0) ...                                % Version and Flags
594     mbstring(component_type) ...               % Component Name
595     mbstring(sub_type) ...                     % Sub Type Name
596     mbstring('appl') ...                       % Component manufacturer
597     mb32(0) ...                                % Component flags
598     mb32(0) ...                                % Component flag mask
599     mb8(length(type_string)) ...               % Type Name byte count
600     mbstring(type_string)];                    % Type Name
601y = SetAtomSize(y);
602
603%%%%%%%%%%%%%%%  mdhd_atom %%%%%%%%%%%%%%%%%
604function y = mdhd_atom(add_sound_p)
605global MakeQTMovieStatus
606
607if add_sound_p
608        data = [mb32(MakeQTMovieStatus.soundRate)  ...
609                mb32(MakeQTMovieStatus.soundLength)];
610else
611        data = [mb32(MakeQTMovieStatus.frameRate * ...
612                        MakeQTMovieStatus.timeScaleExpansion)  ...
613                mb32(MakeQTMovieStatus.frameNumber * ...
614                        MakeQTMovieStatus.timeScaleExpansion)];
615end
616
617y = [mb32(0) mbstring('mdhd') ...               % Atom Header
618     mb32(0) ...
619     mb32(round(now*3600*24)) ...               % Creation time
620     mb32(round(now*3600*24)) ...               % Modification time
621     data ...
622     mb16(0) mb16(0)];
623y = SetAtomSize(y);
624 
625%%%%%%%%%%%%%%%  mdia_atom %%%%%%%%%%%%%%%%%
626function y = mdia_atom(add_sound_p)
627global MakeQTMovieStatus
628 
629if add_sound_p
630        hdlr = hdlr_atom('mhlr', 'soun');
631else
632        hdlr = hdlr_atom('mhlr', 'vide');
633end
634
635y = [mb32(0) mbstring('mdia') ...               % Atom Header
636     mdhd_atom(add_sound_p) ...
637     hdlr ...                                   % Handler Atom
638     minf_atom(add_sound_p)];
639y = SetAtomSize(y);
640
641
642%%%%%%%%%%%%%%%  minf_atom %%%%%%%%%%%%%%%%%
643function y = minf_atom(add_sound_p)
644global MakeQTMovieStatus
645 
646if add_sound_p
647        data = smhd_atom;
648else
649        data = vmhd_atom;
650end
651
652y = [mb32(0) mbstring('minf') ...               % Atom Header
653     data ...
654     hdlr_atom('dhlr','alis') ...
655     dinf_atom ...
656     stbl_atom(add_sound_p)];
657y = SetAtomSize(y);
658
659%%%%%%%%%%%%%%%  moov_atom %%%%%%%%%%%%%%%%%
660function y=moov_atom
661global MakeQTMovieStatus
662MakeQTMovieStatus.timeScale = MakeQTMovieStatus.frameRate * ...
663                                MakeQTMovieStatus.timeScaleExpansion;
664
665if MakeQTMovieStatus.soundLength > 0
666        sound = trak_atom(1);
667else
668        sound = [];
669end
670
671y = [mb32(0) mbstring('moov') ...
672     mvhd_atom udat_atom sound trak_atom(0) ];
673y = SetAtomSize(y);
674
675%%%%%%%%%%%%%%%  mvhd_atom %%%%%%%%%%%%%%%%%
676function y=mvhd_atom
677global MakeQTMovieStatus
678
679fixed1 = bitshift(1,16);                        % Fixed point 1
680frac1 = bitshift(1,30);                         % Fractional 1
681if length(MakeQTMovieStatus.soundStart) > 0             
682        NumberOfTracks = 2;
683else
684        NumberOfTracks = 1;
685end
686
687                                        % Need to make sure its longer
688                                        % of movie and sound lengths
689MovieDuration = max(MakeQTMovieStatus.frameNumber / ...
690                        MakeQTMovieStatus.frameRate, ...
691                    MakeQTMovieStatus.soundLength / ...
692                        MakeQTMovieStatus.soundRate);
693MovieDuration = ceil(MovieDuration * MakeQTMovieStatus.timeScale);
694
695y = [mb32(0) ...                        % Size
696     mbstring('mvhd') ...               % Movie Data
697     mb32(0) ...                        % Version and Flags
698     mb32(0) ...                        % Creation Time (unknown)
699     mb32(0) ...                        % Modification Time (unknown)
700     mb32(MakeQTMovieStatus.timeScale) ...      % Movie's Time Scale
701     mb32(MovieDuration) ...            % Movie Duration
702     mb32(fixed1) ...                   % Preferred Rate
703     mb16(255) ...                      % Preferred Volume
704     mb16(0) ...                        % Fill
705     mb32(0) ...                        % Fill
706     mb32(0) ...                        % Fill
707     mb32(fixed1) mb32(0) mb32(0) ...   % Transformation matrix (identity)
708     mb32(0) mb32(fixed1) mb32(0) ...
709     mb32(0) mb32(0) mb32(frac1) ...
710     mb32(0) ...                        % Preview Time
711     mb32(0) ...                        % Preview Duration
712     mb32(0) ...                        % Poster Time
713     mb32(0) ...                        % Selection Time
714     mb32(0) ...                        % Selection Duration
715     mb32(0) ...                        % Current Time
716     mb32(NumberOfTracks)];             % Video and/or Sound?
717
718y = SetAtomSize(y);
719
720%%%%%%%%%%%%%%%  raw_image_description %%%%%%%%%%%%%%%%%
721function y = raw_image_description()
722global MakeQTMovieStatus
723
724fixed1 = bitshift(1,16);                        % Fixed point 1
725codec = [12 'Photo - JPEG                   '];
726
727y = [mb32(0) mbstring('jpeg') ...               % Atom Header
728     mb32(0) mb16(0) mb16(0) mb16(0) mb16(1) ...
729     mbstring('appl') ...
730     mb32(1023) ...                             % Temporal Quality (perfect)
731     mb32(floor(1023*MakeQTMovieStatus.spatialQual)) ...
732     mb16(MakeQTMovieStatus.imageSize(2)) ...
733     mb16(MakeQTMovieStatus.imageSize(1)) ...
734     mb32(fixed1 * 72) mb32(fixed1 * 72) ...
735     mb32(0) ...
736     mb16(0) ...
737     mbstring(codec) ...
738     mb16(24) mb16(65535)];
739y = SetAtomSize(y);
740
741
742%%%%%%%%%%%%%%%  raw_sound_description %%%%%%%%%%%%%%%%%
743function y = raw_sound_description()
744global MakeQTMovieStatus
745y = [mb32(0) mbstring('twos') ...               % Atom Header
746     mb32(0) mb16(0) mb16(0) mb16(0) mb16(0) ...
747     mb32(0) ...
748     mb16(MakeQTMovieStatus.soundChannels) ...
749     mb16(16) ...                               % 16 bits per sample
750     mb16(0) mb16(0) ...
751     mb32(round(MakeQTMovieStatus.soundRate*65536))];
752y = SetAtomSize(y);
753
754
755%%%%%%%%%%%%%%%  smhd_atom %%%%%%%%%%%%%%%%%
756function y = smhd_atom()
757y = SetAtomSize([mb32(0) mbstring('smhd') mb32(0) mb16(0) mb16(0)]);
758
759%%%%%%%%%%%%%%%  stbl_atom %%%%%%%%%%%%%%%%%
760% Removed the stss atom since it seems to upset the PC version of QT
761% and it is empty so it doesn't add anything.
762% Malcolm - July 5, 1999
763function y = stbl_atom(add_sound_p)
764y = [mb32(0) mbstring('stbl') ...               % Atom Header
765     stsd_atom(add_sound_p) ...
766     stts_atom(add_sound_p) ...
767     stsc_atom(add_sound_p) ...
768     stsz_atom(add_sound_p) ...
769     stco_atom(add_sound_p)];
770y = SetAtomSize(y);
771
772%%%%%%%%%%%%%%%  stco_atom %%%%%%%%%%%%%%%%%
773function y = stco_atom(add_sound_p)
774global MakeQTMovieStatus
775if add_sound_p
776        y = [mb32(0) mbstring('stco') mb32(0) mb32(1) ...
777             mb32(MakeQTMovieStatus.soundStart)];
778else
779        y = [mb32(0) mbstring('stco') mb32(0) ...
780             mb32(MakeQTMovieStatus.frameNumber) ...
781             mb32(MakeQTMovieStatus.frameStarts)];
782end
783y = SetAtomSize(y);
784
785%%%%%%%%%%%%%%%  stsc_atom %%%%%%%%%%%%%%%%%
786function y = stsc_atom(add_sound_p)
787global MakeQTMovieStatus
788if add_sound_p
789        samplesperchunk = MakeQTMovieStatus.soundLength;
790else
791        samplesperchunk = 1;
792end
793
794y = [mb32(0) mbstring('stsc') mb32(0) mb32(1)  ...
795     mb32(1) mb32(samplesperchunk) mb32(1)];
796y = SetAtomSize(y);
797
798%%%%%%%%%%%%%%%  stsd_atom %%%%%%%%%%%%%%%%%
799function y = stsd_atom(add_sound_p)
800if add_sound_p
801        desc = raw_sound_description;
802else
803        desc = raw_image_description;
804end
805
806y = [mb32(0) mbstring('stsd') mb32(0) mb32(1) desc];
807y = SetAtomSize(y);
808
809%%%%%%%%%%%%%%%  stss_atom %%%%%%%%%%%%%%%%%
810function y = stss_atom()
811y = SetAtomSize([mb32(0) mbstring('stss') mb32(0) mb32(0)]);
812
813%%%%%%%%%%%%%%%  stsz_atom %%%%%%%%%%%%%%%%%
814function y = stsz_atom(add_sound_p)
815global MakeQTMovieStatus
816if add_sound_p
817        y = [mb32(0) mbstring('stsz') mb32(0) mb32(2) ...
818             mb32(MakeQTMovieStatus.soundLength)];
819else
820        y = [mb32(0) mbstring('stsz') mb32(0) mb32(0) ...
821             mb32(MakeQTMovieStatus.frameNumber) ...
822             mb32(MakeQTMovieStatus.frameLengths)];
823end
824y = SetAtomSize(y);
825
826%%%%%%%%%%%%%%%  stts_atom %%%%%%%%%%%%%%%%%
827function y = stts_atom(add_sound_p)
828global MakeQTMovieStatus
829if add_sound_p
830        count_duration = [mb32(MakeQTMovieStatus.soundLength) mb32(1)];
831else
832        count_duration = [mb32(MakeQTMovieStatus.frameNumber) ...
833                mb32(MakeQTMovieStatus.timeScaleExpansion)];
834end
835
836y = SetAtomSize([mb32(0) mbstring('stts') mb32(0) mb32(1) count_duration]);
837
838%%%%%%%%%%%%%%%  trak_atom %%%%%%%%%%%%%%%%%
839function y = trak_atom(add_sound_p)
840global MakeQTMovieStatus
841
842y = [mb32(0) mbstring('trak') ...               % Atom Header
843        tkhd_atom(add_sound_p) ...              % Track header
844        edts_atom(add_sound_p) ...              % Edit List
845        mdia_atom(add_sound_p)];
846y = SetAtomSize(y);
847
848%%%%%%%%%%%%%%%  tkhd_atom %%%%%%%%%%%%%%%%%
849function y = tkhd_atom(add_sound_p)
850global MakeQTMovieStatus
851
852fixed1 = bitshift(1,16);                        % Fixed point 1
853frac1 = bitshift(1,30);                         % Fractional 1 (CHECK THIS)
854
855if add_sound_p > 0
856        duration = MakeQTMovieStatus.soundLength / ...
857                        MakeQTMovieStatus.soundRate * ...
858                        MakeQTMovieStatus.timeScale;
859else
860        duration = MakeQTMovieStatus.frameNumber / ...
861                        MakeQTMovieStatus.frameRate * ...
862                        MakeQTMovieStatus.timeScale;
863end
864duration = ceil(duration);
865
866y = [mb32(0) mbstring('tkhd') ...       % Atom Header
867     mb32(15) ...                       % Version and flags
868     mb32(round(now*3600*24)) ...       % Creation time
869     mb32(round(now*3600*24)) ...       % Modification time
870     mb32(MakeQTMovieStatus.trackNumber) ...
871     mb32(0) ...
872     mb32(duration) ...                 % Track duration
873     mb32(0) mb32(0) ...                % Offset and priority
874     mb16(0) mb16(0) mb16(255) mb16(0) ...      % Layer, Group, Volume, fill
875     mb32(fixed1) mb32(0) mb32(0) ...   % Transformation matrix (identity)
876     mb32(0) mb32(fixed1) mb32(0) ...
877     mb32(0) mb32(0) mb32(frac1)];
878
879if add_sound_p
880        y = [y mb32(0) mb32(0)];        % Zeros for sound
881else
882        y = [y mb32(fliplr(MakeQTMovieStatus.imageSize)*fixed1)];
883end
884y= SetAtomSize(y);
885
886MakeQTMovieStatus.trackNumber = MakeQTMovieStatus.trackNumber + 1;
887
888%%%%%%%%%%%%%%%  udat_atom %%%%%%%%%%%%%%%%%
889function y = udat_atom()
890atfmt = [64 double('fmt')];
891atday = [64 double('day')];
892
893VersionString = 'Matlab MakeQTMovie version April 7, 2000';
894
895y = [mb32(0) mbstring('udta') ...
896        SetAtomSize([mb32(0) atfmt mbstring(['Created ' VersionString])]) ...
897        SetAtomSize([mb32(0) atday '  ' date])];
898y = SetAtomSize(y);
899
900
901%%%%%%%%%%%%%%%  vmhd_atom %%%%%%%%%%%%%%%%%
902function y = vmhd_atom()
903
904y = SetAtomSize([mb32(0) mbstring('vmhd') mb32(0) ...
905    mb16(64) ...                        % Graphics Mode
906    mb16(0) mb16(0) mb16(0)]);          % Op Color