#!/usr/bin/perl

use strict;
use warnings;

use bytes;

# jpeg2qtvr, assembles six jpeg cube faces into a QTVR file
# Copyright (C) 2006  Bruno Postle <bruno at postle dot net>
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

our $VERSION = 0.02;

# http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
# http://developer.apple.com/documentation/QuickTime/InsideQT_QTVR/index.html
# http://developer.apple.com/documentation/QuickTime/QTFF/index.html
# http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
# http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt

# date is seconds since midnight, January 1, 1904

my $date = pack4B (time + 2082844800);

my $name = "Cubic panorama created by jpeg2qtvr";

# pixel size of the view window

my $width = pack4B (1024);
my $height = pack4B (768);

die "Usage: $0 front.jpg right.jpg back.jpg left.jpg up.jpg down.jpg > output.mov" unless @ARGV == 6;

# slurp in the JPEG data

local ($/, *FH);

open (FH, $ARGV[0]) or die "can't open $ARGV[0]";
my $image0 = <FH>;
close FH;

open (FH, $ARGV[1]) or die "can't open $ARGV[1]";
my $image1 = <FH>;
close FH;

open (FH, $ARGV[2]) or die "can't open $ARGV[2]";
my $image2 = <FH>;
close FH;

open (FH, $ARGV[3]) or die "can't open $ARGV[3]";
my $image3 = <FH>;
close FH;

open (FH, $ARGV[4]) or die "can't open $ARGV[4]";
my $image4 = <FH>;
close FH;

open (FH, $ARGV[5]) or die "can't open $ARGV[5]";
my $image5 = <FH>;
close FH;

my ($width_image, $height_image) = JPEGsize ($image0);
die 'Can\'t determine JPEG dimensions' unless ($width_image == $height_image);

# these will be filled later

my $offsetA = '????';
my $offsetB = '????';
my $offset0 = '????';
my $offset1 = '????';
my $offset2 = '????';
my $offset3 = '????';
my $offset4 = '????';
my $offset5 = '????';

my $lengthA = '????';
my $lengthB = '????';

# build the mov data

my $mov;

for (0 .. 1)
{

$mov =

atom ('ftyp',
    "qt\x{20}\x{20}\x{20}\x{05}\x{03}\x{00}".
    "qt\x{20}\x{20}\x{00}\x{00}\x{00}\x{00}".
    "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}"
).

atom ('moov',

#[Subrecursing 'moov' atom]

     atom ('mvhd',
         "\x{00}\x{00}\x{00}\x{00}".
         $date.$date.
         "\x{00}\x{00}\x{0e}\x{10}".
         "\x{00}\x{00}\x{0e}\x{10}\x{00}\x{01}\x{00}\x{00}".
         "\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{40}\x{00}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{0e}\x{10}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
         "\x{00}\x{00}\x{00}\x{04}"
     ).

     atom ('trak',

#    [Subrecursing 'trak' atom]

         atom ('tkhd',
             "\x{00}\x{00}\x{00}\x{0f}".
             $date.$date.
             "\x{00}\x{00}\x{00}\x{01}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{0e}\x{10}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}".
             "\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{40}\x{00}".
             $width.$height.
             "\x{00}\x{00}"
         ).

         atom ('edts',

             atom ('elst',
                 "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                 "\x{00}\x{00}\x{0e}\x{10}\x{00}\x{00}\x{00}\x{00}".
                 "\x{00}\x{01}\x{00}\x{00}"
             )
         ).

         atom ('tref',

             atom ('pano',
                 "\x{00}\x{00}\x{00}\x{02}"
             )
         ).  

         atom ('mdia',

#        [Subrecursing 'mdia' atom]

             atom ('mdhd',
                 "\x{00}\x{00}\x{00}\x{00}".
                 $date.$date.
                 "\x{00}\x{00}\x{0e}\x{10}".
                 "\x{00}\x{00}\x{0e}\x{10}\x{00}\x{00}\x{00}\x{00}"
             ).

             atom ('hdlr',
                 "\x{00}\x{00}\x{00}\x{00}".
                 "mhlrqtvrappl".
                 "\x{80}\x{00}\x{00}\x{01}\x{00}\x{01}\x{02}\x{a1}\x{12}".
                 "QTVR Media Handler"
             ).

             atom ('minf',

#            [Subrecursing 'minf' atom]

                 atom ('gmhd',

                     atom ('gmin',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{40}\x{80}\x{00}".
                         "\x{80}\x{00}\x{80}\x{00}\x{00}\x{00}\x{00}\x{00}"
                     )
                 ).

                 atom ('hdlr',
                     "\x{00}\x{00}\x{00}\x{00}dhlr".
                     "alisappl".
                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{01}\x{00}\x{2d}\x{18}".
                     "Apple Alias Data Handler"
                 ).

                 atom ('dinf',

#                [Subrecursing 'dinf' atom]

                     atom ('dref',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".

                         atom ('alis',
                             "\x{00}\x{00}\x{00}\x{01}"
                         )
                     )

#                [End subrecurse 'dinf' atom]

                 ).

                 atom ('stbl',

#                [Subrecursing 'stbl' atom]

                     atom ('stsd',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".

                         atom ('qtvr',
                             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                             "\x{00}\x{00}\x{00}\x{00}".

                             atom ('sean',
                                 "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{03}".
                                 "\x{00}\x{00}\x{00}\x{00}".

                                 atom ('vrsc',
                                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}".
                                     "\x{00}\x{00}\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}".
                                     "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                                     "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                     "\x{00}\x{00}\x{00}\x{00}"
                                 ).

                                 atom ('imgp',
                                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{02}".
                                     "\x{00}\x{00}\x{00}\x{00}".

                                     atom ('impn',
                                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}\x{00}\x{03}".
                                         "\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}"
                                     ).

                                     atom ('impn',
                                         "\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{03}".
                                         "\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}\x{03}\x{ff}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                         "\x{00}\x{00}\x{00}\x{00}"
                                     )
                                 ).

                                 atom ('vrnp',
                                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
                                     "\x{00}\x{00}\x{00}\x{00}".

                                     atom ('vrni',
                                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
                                         "\x{00}\x{00}\x{00}\x{00}".

                                         atom ('nloc',
                                             "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}".
                                             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}".
                                             "pano\x{00}\x{00}\x{00}\x{00}".
                                             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                                             "\x{00}\x{00}\x{00}\x{00}"
                                         )
                                     )
                                 )
                             )
                         )
                     ).

                     atom ('stts',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{0e}\x{10}"
                     ).

                     atom ('stsc',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}"
                     ).

                     atom ('stsz',
                         # ver  # flags
                         "\x{00}\x{00}\x{00}\x{00}".
                         # standard size
                         $lengthA.
                         # number of samples
                         "\x{00}\x{00}\x{00}\x{01}"
                     ).

                     atom ('stco',
                         # ver  # flags
                         "\x{00}\x{00}\x{00}\x{00}".
                         # number entries
                         "\x{00}\x{00}\x{00}\x{01}".
                         $offsetA
                     )
                 )

#                [End subrecurse 'stbl' atom]

             )

#            [End subrecurse 'minf' atom]

         )

#        [End subrecurse 'mdia' atom]

     ).

#    [End subrecurse 'trak' atom]

     atom ('trak',

#    [Subrecursing 'trak' atom]

         atom ('tkhd',

             "\x{00}\x{00}\x{00}\x{0f}".
             $date.$date.
             "\x{00}\x{00}\x{00}\x{02}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{0e}\x{10}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{40}\x{00}".
             $width.$height.
             "\x{00}\x{00}"
         ).

         atom ('load',
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}\x{00}"
         ).

         atom ('edts',

             atom ('elst',
                 "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                 "\x{00}\x{00}\x{0e}\x{10}\x{00}\x{00}\x{00}\x{00}".
                 "\x{00}\x{01}\x{00}\x{00}"
             )

         ).

         atom ('tref',

             atom ('imgt',
                 "\x{00}\x{00}\x{00}\x{03}"
             )

         ).

         atom ('mdia',

#        [Subrecursing 'mdia' atom]

             atom ('mdhd',
                 "\x{00}\x{00}\x{00}\x{00}".
                 $date.$date. 
                 "\x{00}\x{00}\x{0e}\x{10}".
                 "\x{00}\x{00}\x{0e}\x{10}".
                 "\x{00}\x{00}\x{00}\x{00}"
             ).

             atom ('hdlr',
                 "\x{00}\x{00}\x{00}\x{00}mhlrpanoappl".
                 "\x{00}\x{00}\x{00}\x{01}\x{00}\x{01}\x{02}\x{99}\x{1b}".
                 "QTVR Panorama Media Handler"
             ).

#            ReadAtom_HDLR:  We found the 'pano' media!

             atom ('minf',

#            [Subrecursing 'minf' atom]

                 atom ('gmhd',

                     atom ('gmin',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{40}\x{80}\x{00}".
                         "\x{80}\x{00}\x{80}\x{00}\x{00}\x{00}\x{00}\x{00}"
                     )
                 ).

                 atom ('hdlr',
                     "\x{00}\x{00}\x{00}\x{00}dhlralisappl".
                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{01}\x{00}\x{2d}\x{18}".
                     "Apple Alias Data Handler"
                 ).

                 atom ('dinf',

#                [Subrecursing 'dinf' atom]

                     atom ('dref',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".

                         atom ('alis',
                             "\x{00}\x{00}\x{00}\x{01}"
                         )
                     )
                 ).

#                [End subrecurse 'dinf' atom]

                 atom ('stbl',

#                [Subrecursing 'stbl' atom]

                     atom ('stsd',

                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{10}\x{00}\x{00}\x{00}\x{00}".
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}"
                     ).

                     atom ('stts',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{0e}\x{10}"
                     ).

                     atom ('stsc',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}"
                     ).

                     atom ('stsz',
                         # ver  flags
                         "\x{00}\x{00}\x{00}\x{00}".
                         # standard size
                         $lengthB.
                         # number of items
                         "\x{00}\x{00}\x{00}\x{01}"
                     ).

#                    'pano' sample size = : 192

                     atom ('stco',
                         # ver  # flags
                         "\x{00}\x{00}\x{00}\x{00}".
                         # number samples
                         "\x{00}\x{00}\x{00}\x{01}".
                         $offsetB

#                    Chunk offset to 'pano' is : 2322

#                    [Subrecursing pano 'stco' atom]
#
#                        QTAtom 0x0000091E  (0x000000B4)  sean child count: 2
#
#                        [Subrecursing 'sean' qt atom]
#
#                            QTAtom 0x00000932  (0x00000068)  pdat child count: 0
#                            QTAtom 0x0000099A  (0x00000038)  cuvw child count: 0
#
#                        [End subrecursing 'sean' qt atom]

                     )

#                    [End subrecurse pano 'stco' atom]

                 )

#                [End subrecurse 'stbl' atom]

             )

#            [End subrecurse 'minf' atom]

         )

#        [End subrecurse 'mdia' atom]

     ).

#    [End subrecurse 'trak' atom]

     atom ('trak',

#    [Subrecursing 'trak' atom]

         atom ('tkhd',
             "\x{00}\x{00}\x{00}\x{0e}".
             $date.$date.
             "\x{00}\x{00}\x{00}\x{03}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{0e}\x{10}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
             "\x{40}\x{00}".
             pack4B ($width_image).
             pack4B ($width_image).
             "\x{00}\x{00}"
         ).

         atom ('edts',

             atom ('elst',
                 "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                 "\x{00}\x{00}\x{0e}\x{10}\x{00}\x{00}\x{00}\x{00}".
                 "\x{00}\x{01}\x{00}\x{00}"
             )
         ).

         atom ('mdia',

#        [Subrecursing 'mdia' atom]

             atom ('mdhd',
                 "\x{00}\x{00}\x{00}\x{00}".
                 $date.$date.
                 "\x{00}\x{00}\x{0e}\x{10}".
                 "\x{00}\x{00}\x{0e}\x{10}\x{00}\x{00}\x{00}\x{00}"
             ).

             atom ('hdlr',
                 "\x{00}\x{00}\x{00}\x{00}mhlrvideappl".
                 "\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}\x{00}\x{23}\x{19}".
                 "Apple Video Media Handler"
             ).

#            ReadAtom_HDLR:  We found a 'vide' media!

             atom ('minf',

#            [Subrecursing 'minf' atom]

                 atom ('vmhd',
                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{40}\x{80}\x{00}".
                     "\x{80}\x{00}\x{80}\x{00}"
                 ).

                 atom ('hdlr',
                     "\x{00}\x{00}\x{00}\x{00}dhlralisappl".
                     "\x{00}\x{00}\x{00}\x{01}\x{00}\x{01}\x{00}\x{2d}\x{18}".
                     "Apple Alias Data Handler"
                 ).

                 atom ('dinf',

#                [Subrecursing 'dinf' atom]

                     atom ('dref',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".

                         atom ('alis',
                             "\x{00}\x{00}\x{00}\x{01}"
                         )
                     )
                 ).

#                [End subrecurse 'dinf' atom]

                 atom ('stbl',

#                [Subrecursing 'stbl' atom]

                     atom ('stsd',

                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{56}jpeg".
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{00}appl".
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{02}\x{00}".

                         pack2B ($width_image).
                         pack2B ($width_image).

                         "\x{00}\x{48}\x{00}\x{00}".
                         "\x{00}\x{48}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                         "\x{00}\x{01}\x{0c}".
                         "Photo - JPEG".
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
                         "\x{00}\x{00}\x{00}\x{00}\x{18}\x{ff}\x{ff}"
                     ).

                     atom ('stts',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{06}\x{00}\x{00}\x{02}\x{58}"
                     ).

                     atom ('stsc',
                         "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
                         "\x{00}\x{00}\x{00}\x{01}"
                     ).

                     atom ('stsz',
                         # ver  # flags
                         "\x{00}\x{00}\x{00}\x{00}".
                         # standard size
                         "\x{00}\x{00}\x{00}\x{00}".
                         # number of items
                         "\x{00}\x{00}\x{00}\x{06}".
                         pack4B (length $image0).
                         pack4B (length $image1).
                         pack4B (length $image2).
                         pack4B (length $image3).
                         pack4B (length $image4).
                         pack4B (length $image5)
                     ).

                     atom ('stco',
                         # ver  # flags 
                         "\x{00}\x{00}\x{00}\x{00}".
                         # number entries (6)
                         "\x{00}\x{00}\x{00}\x{06}".
                         $offset0.
                         $offset1.
                         $offset2.
                         $offset3.
                         $offset4.
                         $offset5
                     )

#                [End subrecurse 'stbl' atom]

                 )

#            [End subrecurse 'minf' atom]

             )

#        [End subrecurse 'mdia' atom]

         )

#    [End subrecurse 'trak' atom]

     ).

     atom ('udta',
         atom ('ctyp',
             # "none" for no controls
             "qtvr"
         ).
         atom ("\x{a9}nam",
             meta ("\x{00}\x{00}",
                 $name
             )
         ).
         atom ("\x{a9}swr",
             meta ("\x{00}\x{00}",
                 "jpeg2qtvr $VERSION"
             )
         ).
         "\x{00}\x{00}\x{00}\x{00}"
     )

#[End subrecurse 'moov' atom]

).

# end of instructions

atom ('free',
    "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
    "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}"
);

$mov .=

# follows is the actual movie data

# this atom is optional, but useful to define the end of the file
#Atom 0x000008AA  (0x001E29FC)  mdat

##atom ('wide', '').
## 
##"\x{00}\x{1e}\x{29}\x{fc}".  "mdat".
##
##atom ('wide', '').

"\x{00}\x{00}\x{00}\x{00}".  "mdat";

my $A =

"\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".

atom ('sean',
    "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
    "\x{00}\x{00}\x{00}\x{00}".

    atom ('ndhd',
        "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}\x{00}\x{02}\x{00}\x{00}".
        "pano\x{00}\x{00}\x{00}\x{01}".
        "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}"
    )
);

my $B = 

"\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".

atom ('sean',
    "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{02}".
    "\x{00}\x{00}\x{00}\x{00}".

    atom ('pdat',
        "\x{00}\x{00}\x{00}\x{01}".
        "\x{00}\x{00}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}".

        # TODO these are floats, need to figure-out which format
        # minPan
        "\x{00}\x{02}\x{00}\x{00}".
        # maxPan
        "\x{00}\x{00}\x{00}\x{01}".
        # minTilt
        "\x{00}\x{00}\x{00}\x{00}".
        # maxTilt
        "\x{00}\x{00}\x{00}\x{00}".
        # minFieldOfView
        "\x{43}\x{b4}\x{00}\x{00}".
        # maxFieldOfView
        "\x{c2}\x{34}\x{00}\x{00}".
        # defaultPan
        "\x{42}\x{34}\x{00}\x{00}".
        # defaultTilt
        "\x{42}\x{48}\x{00}\x{00}".
        # defaultFieldOfView
        "\x{42}\x{b4}\x{00}\x{00}".

        "\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}".
        "\x{42}\x{b4}\x{00}\x{00}\x{00}\x{00}\x{1e}\x{00}".
        "\x{00}\x{00}\x{07}\x{80}". # image width?
        "\x{00}\x{04}\x{00}\x{01}".
        "\x{00}\x{00}\x{1e}\x{00}".
        "\x{00}\x{00}\x{07}\x{80}". # image width?
        "\x{00}\x{04}\x{00}\x{01}\x{00}\x{00}\x{00}\x{01}".
        "cube\x{00}\x{00}\x{00}\x{00}"
    ).

    atom ('cuvw',
        "\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}".
        # minFieldOfView
        "\x{43}\x{b4}\x{00}\x{00}".
        # maxFieldOfView
        "\x{c2}\x{b4}\x{00}\x{00}".
        # defaultFieldOfView
        "\x{42}\x{b4}\x{00}\x{00}".
        # defaultTilt
        "\x{42}\x{48}\x{00}\x{00}".
        # defaultPan ????
        "\x{42}\x{f0}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}".
        "\x{00}\x{00}\x{00}\x{00}".
        "\x{42}\x{b4}\x{00}\x{00}"
    )
);

$offsetA = pack4B (length $mov);

$mov .= $A;

$offsetB = pack4B (length $mov);

$mov .= $B;

$lengthA = pack4B (length $A);
$lengthB = pack4B (length $B);

$offset0 = pack4B (length $mov);
$mov .= $image0;

$offset1 = pack4B (length $mov);
$mov .= $image1;

$offset2 = pack4B (length $mov);
$mov .= $image2;

$offset3 = pack4B (length $mov);
$mov .= $image3;

$offset4 = pack4B (length $mov);
$mov .= $image4;

$offset5 = pack4B (length $mov);
$mov .= $image5;

}

print STDOUT $mov;

sub pack4B
{
    my $bytes = sprintf ('%.8x', shift);
    $bytes =~ /(..)(..)(..)(..)/;
    return chr(hex($1)). chr(hex($2)). chr(hex($3)). chr(hex($4));
}

sub pack2B
{
    my $bytes = sprintf ('%.4x', shift);
    $bytes =~ /(..)(..)/;
    return chr(hex($1)). chr(hex($2));
}

sub atom
{
    my ($type, $data) = @_;
    return pack4B ((length $data) + 8) . $type . $data;
}

sub meta
{
    my ($lang, $data) = @_;
    return pack2B (length $data) . $lang . $data;
}

sub JPEGsize
{
    my $image = shift;
    $image =~ /.*?\x{ff}\x{c0}...(.)(.)(.)(.)/;
    ((256 * ord ($3)) + ord ($4), (256 * ord ($1)) + ord ($2));
}
