void CButtonBar::AddButton(std::string n, std::string d)
{
    if (!m_pCurBox)
        PushBox();
    
    n = GetTranslation(n);
    d = GetTranslation(d);
    
    std::string text = n + ": " + d;
    int width = SafeConvert<int>(MBWidth(text));

    if (m_iMaxWidth < width)
        throw Exceptions::CExFrontend("Not enough space for buttonbar entry");
    else if ((m_pCurBox->RequestWidth() + width + m_iBoxSpacing) > m_iMaxWidth)
        PushBox();
    
    PushLabel(n, d);
    RequestUpdate();
}
Example #2
0
    ~MP4FileStream()
    {
        if(!bStreamOpened)
            return;

        App->EnableSceneSwitching(false);

        //---------------------------------------------------

        //HWND hwndProgressDialog = CreateDialog(hinstMain, MAKEINTRESOURCE(IDD_BUILDINGMP4), hwndMain, (DLGPROC)MP4ProgressDialogProc);
        //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETRANGE32, 0, 100);

        mdatStop = fileOut.GetPos();

        BufferOutputSerializer output(endBuffer);

        //set a reasonable initial buffer size
        endBuffer.SetSize((videoFrames.Num() + audioFrames.Num()) * 20 + 131072);

        UINT64 audioFrameSize = App->GetAudioEncoder()->GetFrameSize();

        DWORD macTime = fastHtonl(DWORD(GetMacTime()));
        UINT videoDuration = fastHtonl(lastVideoTimestamp + App->GetFrameTime());
        UINT audioDuration = fastHtonl(lastVideoTimestamp + DWORD(double(audioFrameSize)*1000.0/double(App->GetSampleRateHz())));
        UINT width, height;
        App->GetOutputSize(width, height);

        LPCSTR lpVideoTrack = "Video Media Handler";
        LPCSTR lpAudioTrack = "Sound Media Handler";

        const char videoCompressionName[31] = "AVC Coding";

        //-------------------------------------------
        // get video headers
        DataPacket videoHeaders;
        App->GetVideoHeaders(videoHeaders);
        List<BYTE> SPS, PPS;

        LPBYTE lpHeaderData = videoHeaders.lpPacket+11;
        SPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData));

        lpHeaderData += SPS.Num()+3;
        PPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData));

        //-------------------------------------------
        // get AAC headers if using AAC
        List<BYTE> AACHeader;
        if(!bMP3)
        {
            DataPacket data;
            App->GetAudioHeaders(data);
            AACHeader.CopyArray(data.lpPacket+2, data.size-2);
        }

        //-------------------------------------------

        EndChunkInfo(videoChunks, videoSampleToChunk, curVideoChunkOffset, numVideoSamples);
        EndChunkInfo(audioChunks, audioSampleToChunk, curAudioChunkOffset, numAudioSamples);

        if (numVideoSamples > 1)
            GetVideoDecodeTime(videoFrames.Last(), true);

        if (numAudioSamples > 1)
            GetAudioDecodeTime(audioFrames.Last(), true);

        UINT audioUnitDuration = fastHtonl(UINT(lastAudioTimeVal));

        //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 25, 0);

        //-------------------------------------------
        // sound descriptor thingy.  this part made me die a little inside admittedly.
        UINT maxBitRate = fastHtonl(App->GetAudioEncoder()->GetBitRate()*1000);

        List<BYTE> esDecoderDescriptor;
        BufferOutputSerializer esDecoderOut(esDecoderDescriptor);
        esDecoderOut.OutputByte(bMP3 ? 107 : 64);
        esDecoderOut.OutputByte(0x15); //stream/type flags.  always 0x15 for my purposes.
        esDecoderOut.OutputByte(0); //buffer size, just set it to 1536 for both mp3 and aac
        esDecoderOut.OutputWord(WORD_BE(0x600)); 
        esDecoderOut.OutputDword(maxBitRate); //max bit rate (cue bill 'o reily meme for these two)
        esDecoderOut.OutputDword(maxBitRate); //avg bit rate

        if(!bMP3) //if AAC, put in headers
        {
            esDecoderOut.OutputByte(0x5);  //decoder specific descriptor type
            /*esDecoderOut.OutputByte(0x80); //some stuff that no one should probably care about
            esDecoderOut.OutputByte(0x80);
            esDecoderOut.OutputByte(0x80);*/
            esDecoderOut.OutputByte(AACHeader.Num());
            esDecoderOut.Serialize((LPVOID)AACHeader.Array(), AACHeader.Num());
        }


        List<BYTE> esDescriptor;
        BufferOutputSerializer esOut(esDescriptor);
        esOut.OutputWord(0); //es id
        esOut.OutputByte(0); //stream priority
        esOut.OutputByte(4); //descriptor type
        /*esOut.OutputByte(0x80); //some stuff that no one should probably care about
        esOut.OutputByte(0x80);
        esOut.OutputByte(0x80);*/
        esOut.OutputByte(esDecoderDescriptor.Num());
        esOut.Serialize((LPVOID)esDecoderDescriptor.Array(), esDecoderDescriptor.Num());
        esOut.OutputByte(0x6);  //config descriptor type
        /*esOut.OutputByte(0x80); //some stuff that no one should probably care about
        esOut.OutputByte(0x80);
        esOut.OutputByte(0x80);*/
        esOut.OutputByte(1); //len
        esOut.OutputByte(2); //SL value(? always 2)

        //-------------------------------------------

        PushBox(output, DWORD_BE('moov'));

          //------------------------------------------------------
          // header
          PushBox(output, DWORD_BE('mvhd'));
            output.OutputDword(0); //version and flags (none)
            output.OutputDword(macTime); //creation time
            output.OutputDword(macTime); //modified time
            output.OutputDword(DWORD_BE(1000)); //time base (milliseconds, so 1000)
            output.OutputDword(videoDuration); //duration (in time base units)
            output.OutputDword(DWORD_BE(0x00010000)); //fixed point playback speed 1.0
            output.OutputWord(WORD_BE(0x0100)); //fixed point vol 1.0
            output.OutputQword(0); //reserved (10 bytes)
            output.OutputWord(0);
            output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
            output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
            output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
            output.OutputDword(0); //prevew start time (time base units)
            output.OutputDword(0); //prevew duration (time base units)
            output.OutputDword(0); //still poster frame (timestamp of frame)
            output.OutputDword(0); //selection(?) start time (time base units)
            output.OutputDword(0); //selection(?) duration (time base units)
            output.OutputDword(0); //current time (0, time base units)
            output.OutputDword(DWORD_BE(3)); //next free track id (1-based rather than 0-based)
          PopBox(output); //mvhd

          //------------------------------------------------------
          // audio track
          PushBox(output, DWORD_BE('trak'));
            PushBox(output, DWORD_BE('tkhd')); //track header
              output.OutputDword(DWORD_BE(0x00000007)); //version (0) and flags (0xF)
              output.OutputDword(macTime); //creation time
              output.OutputDword(macTime); //modified time
              output.OutputDword(DWORD_BE(1)); //track ID
              output.OutputDword(0); //reserved
              output.OutputDword(audioDuration); //duration (in time base units)
              output.OutputQword(0); //reserved
              output.OutputWord(0); //video layer (0)
              output.OutputWord(WORD_BE(0)); //quicktime alternate track id
              output.OutputWord(WORD_BE(0x0100)); //volume
              output.OutputWord(0); //reserved
              output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
              output.OutputDword(0); //width (fixed point)
              output.OutputDword(0); //height (fixed point)
            PopBox(output); //tkhd
            /*PushBox(output, DWORD_BE('edts'));
              PushBox(output, DWORD_BE('elst'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(DWORD_BE(1)); //count
                output.OutputDword(audioDuration); //duration
                output.OutputDword(0); //start time
                output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0)
              PopBox(); //elst
            PopBox(); //tdst*/
            PushBox(output, DWORD_BE('mdia'));
              PushBox(output, DWORD_BE('mdhd'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(macTime); //creation time
                output.OutputDword(macTime); //modified time
                output.OutputDword(DWORD_BE(App->GetSampleRateHz())); //time scale
                output.OutputDword(audioUnitDuration);
                output.OutputDword(bMP3 ? DWORD_BE(0x55c40000) : DWORD_BE(0x15c70000));
              PopBox(output); //mdhd
              PushBox(output, DWORD_BE('hdlr'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(0); //quicktime type (none)
                output.OutputDword(DWORD_BE('soun')); //media type
                output.OutputDword(0); //manufacturer reserved
                output.OutputDword(0); //quicktime component reserved flags
                output.OutputDword(0); //quicktime component reserved mask
                output.Serialize((LPVOID)lpAudioTrack, (DWORD)strlen(lpAudioTrack)+1); //track name
              PopBox(output); //hdlr
              PushBox(output, DWORD_BE('minf'));
                PushBox(output, DWORD_BE('smhd'));
                  output.OutputDword(0); //version and flags (none)
                  output.OutputDword(0); //balance (fixed point)
                PopBox(output); //vdhd
                PushBox(output, DWORD_BE('dinf'));
                  PushBox(output, DWORD_BE('dref'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('url '));
                      output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
                    PopBox(output); //url
                  PopBox(output); //dref
                PopBox(output); //dinf
                PushBox(output, DWORD_BE('stbl'));
                  PushBox(output, DWORD_BE('stsd'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('mp4a'));
                      output.OutputDword(0); //reserved (6 bytes)
                      output.OutputWord(0);
                      output.OutputWord(WORD_BE(1)); //dref index
                      output.OutputWord(0); //quicktime encoding version
                      output.OutputWord(0); //quicktime encoding revision
                      output.OutputDword(0); //quicktime audio encoding vendor
                      output.OutputWord(0); //channels (ignored)
                      output.OutputWord(WORD_BE(16)); //sample size
                      output.OutputWord(0); //quicktime audio compression id
                      output.OutputWord(0); //quicktime audio packet size
                      output.OutputDword(DWORD_BE(App->GetSampleRateHz()<<16)); //sample rate (fixed point)
                      PushBox(output, DWORD_BE('esds'));
                        output.OutputDword(0); //version and flags (none)
                        output.OutputByte(3); //ES descriptor type
                        /*output.OutputByte(0x80);
                        output.OutputByte(0x80);
                        output.OutputByte(0x80);*/
                        output.OutputByte(esDescriptor.Num());
                        output.Serialize((LPVOID)esDescriptor.Array(), esDescriptor.Num());
                      PopBox(output);
                    PopBox(output);
                  PopBox(output); //stsd
                  PushBox(output, DWORD_BE('stts')); //list of keyframe (i-frame) IDs
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(audioDecodeTimes.Num()));
                    for(UINT i=0; i<audioDecodeTimes.Num(); i++)
                    {
                        output.OutputDword(fastHtonl(audioDecodeTimes[i].count));
                        output.OutputDword(fastHtonl(audioDecodeTimes[i].val));
                    }
                  PopBox(output); //stss
                  PushBox(output, DWORD_BE('stsc')); //sample to chunk list
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(audioSampleToChunk.Num()));
                    for(UINT i=0; i<audioSampleToChunk.Num(); i++)
                    {
                        SampleToChunk &stc  = audioSampleToChunk[i];
                        output.OutputDword(fastHtonl(stc.firstChunkID));
                        output.OutputDword(fastHtonl(stc.samplesPerChunk));
                        output.OutputDword(DWORD_BE(1));
                    }
                  PopBox(output); //stsc

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 30, 0);
                  //ProcessEvents();

                  PushBox(output, DWORD_BE('stsz')); //sample sizes
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(0); //block size for all (0 if differing sizes)
                    output.OutputDword(fastHtonl(audioFrames.Num()));
                    for(UINT i=0; i<audioFrames.Num(); i++)
                        output.OutputDword(fastHtonl(audioFrames[i].size));
                  PopBox(output);

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 40, 0);
                  //ProcessEvents();

                  if(audioChunks.Num() && audioChunks.Last() > 0xFFFFFFFFLL)
                  {
                      PushBox(output, DWORD_BE('co64')); //chunk offsets
                      output.OutputDword(0); //version and flags (none)
                      output.OutputDword(fastHtonl(audioChunks.Num()));
                      for(UINT i=0; i<audioChunks.Num(); i++)
                          output.OutputQword(fastHtonll(audioChunks[i]));
                      PopBox(output); //co64
                  }
                  else
                  {
                      PushBox(output, DWORD_BE('stco')); //chunk offsets
                        output.OutputDword(0); //version and flags (none)
                        output.OutputDword(fastHtonl(audioChunks.Num()));
                        for(UINT i=0; i<audioChunks.Num(); i++)
                            output.OutputDword(fastHtonl((DWORD)audioChunks[i]));
                      PopBox(output); //stco
                  }
                PopBox(output); //stbl
              PopBox(output); //minf
            PopBox(output); //mdia
          PopBox(output); //trak

          //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 50, 0);
          //ProcessEvents();

          //------------------------------------------------------
          // video track
          PushBox(output, DWORD_BE('trak'));
            PushBox(output, DWORD_BE('tkhd')); //track header
              output.OutputDword(DWORD_BE(0x00000007)); //version (0) and flags (0x7)
              output.OutputDword(macTime); //creation time
              output.OutputDword(macTime); //modified time
              output.OutputDword(DWORD_BE(2)); //track ID
              output.OutputDword(0); //reserved
              output.OutputDword(videoDuration); //duration (in time base units)
              output.OutputQword(0); //reserved
              output.OutputWord(0); //video layer (0)
              output.OutputWord(0); //quicktime alternate track id (0)
              output.OutputWord(0); //track audio volume (this is video, so 0)
              output.OutputWord(0); //reserved
              output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
              output.OutputDword(fastHtonl(width<<16));  //width (fixed point)
              output.OutputDword(fastHtonl(height<<16)); //height (fixed point)
            PopBox(output); //tkhd
            /*PushBox(output, DWORD_BE('edts'));
              PushBox(output, DWORD_BE('elst'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(DWORD_BE(1)); //count
                output.OutputDword(videoDuration); //duration
                output.OutputDword(0); //start time
                output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0)
              PopBox(); //elst
            PopBox(); //tdst*/
            PushBox(output, DWORD_BE('mdia'));
              PushBox(output, DWORD_BE('mdhd'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(macTime); //creation time
                output.OutputDword(macTime); //modified time
                output.OutputDword(DWORD_BE(1000)); //time scale
                output.OutputDword(videoDuration);
                output.OutputDword(DWORD_BE(0x55c40000));
              PopBox(output); //mdhd
              PushBox(output, DWORD_BE('hdlr'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(0); //quicktime type (none)
                output.OutputDword(DWORD_BE('vide')); //media type
                output.OutputDword(0); //manufacturer reserved
                output.OutputDword(0); //quicktime component reserved flags
                output.OutputDword(0); //quicktime component reserved mask
                output.Serialize((LPVOID)lpVideoTrack, (DWORD)strlen(lpVideoTrack)+1); //track name
              PopBox(output); //hdlr
              PushBox(output, DWORD_BE('minf'));
                PushBox(output, DWORD_BE('vmhd'));
                  output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
                  output.OutputWord(0); //quickdraw graphic mode (copy = 0)
                  output.OutputWord(0); //quickdraw red value
                  output.OutputWord(0); //quickdraw green value
                  output.OutputWord(0); //quickdraw blue value
                PopBox(output); //vdhd
                PushBox(output, DWORD_BE('dinf'));
                  PushBox(output, DWORD_BE('dref'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('url '));
                      output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
                    PopBox(output); //url
                  PopBox(output); //dref
                PopBox(output); //dinf
                PushBox(output, DWORD_BE('stbl'));
                  PushBox(output, DWORD_BE('stsd'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('avc1'));
                      output.OutputDword(0); //reserved 6 bytes
                      output.OutputWord(0);
                      output.OutputWord(WORD_BE(1)); //index
                      output.OutputWord(0); //encoding version
                      output.OutputWord(0); //encoding revision level
                      output.OutputDword(0); //encoding vendor
                      output.OutputDword(0); //temporal quality
                      output.OutputDword(0); //spatial quality
                      output.OutputWord(fastHtons(width)); //width
                      output.OutputWord(fastHtons(height)); //height
                      output.OutputDword(DWORD_BE(0x00480000)); //fixed point width pixel resolution (72.0)
                      output.OutputDword(DWORD_BE(0x00480000)); //fixed point height pixel resolution (72.0)
                      output.OutputDword(0); //quicktime video data size 
                      output.OutputWord(WORD_BE(1)); //frame count(?)
                      output.OutputByte((BYTE)strlen(videoCompressionName)); //compression name length
                      output.Serialize(videoCompressionName, 31); //31 bytes for the name
                      output.OutputWord(WORD_BE(24)); //bit depth
                      output.OutputWord(0xFFFF); //quicktime video color table id (none = -1)
                      PushBox(output, DWORD_BE('avcC'));
                        output.OutputByte(1); //version
                        output.OutputByte(100); //h264 profile ID
                        output.OutputByte(0); //h264 compatible profiles
                        output.OutputByte(0x1f); //h264 level
                        output.OutputByte(0xff); //reserved
                        output.OutputByte(0xe1); //first half-byte = no clue. second half = sps count
                        output.OutputWord(fastHtons(SPS.Num())); //sps size
                        output.Serialize(SPS.Array(), SPS.Num()); //sps data
                        output.OutputByte(1); //pps count
                        output.OutputWord(fastHtons(PPS.Num())); //pps size
                        output.Serialize(PPS.Array(), PPS.Num()); //pps data
                      PopBox(output); //avcC
                    PopBox(output); //avc1
                  PopBox(output); //stsd
                  PushBox(output, DWORD_BE('stts')); //frame times
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(videoDecodeTimes.Num()));
                    for(UINT i=0; i<videoDecodeTimes.Num(); i++)
                    {
                        output.OutputDword(fastHtonl(videoDecodeTimes[i].count));
                        output.OutputDword(fastHtonl(videoDecodeTimes[i].val));
                    }
                  PopBox(output); //stts

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 60, 0);
                  //ProcessEvents();

                  if (IFrameIDs.Num())
                  {
                      PushBox(output, DWORD_BE('stss')); //list of keyframe (i-frame) IDs
                        output.OutputDword(0); //version and flags (none)
                        output.OutputDword(fastHtonl(IFrameIDs.Num()));
                        output.Serialize(IFrameIDs.Array(), IFrameIDs.Num()*sizeof(UINT));
                      PopBox(output); //stss
                  }
                  PushBox(output, DWORD_BE('ctts')); //list of composition time offsets
                    output.OutputDword(0); //version (0) and flags (none)
                    //output.OutputDword(DWORD_BE(0x01000000)); //version (1) and flags (none)

                    output.OutputDword(fastHtonl(compositionOffsets.Num()));
                    for(UINT i=0; i<compositionOffsets.Num(); i++)
                    {
                        output.OutputDword(fastHtonl(compositionOffsets[i].count));
                        output.OutputDword(fastHtonl(compositionOffsets[i].val));
                    }
                  PopBox(output); //ctts

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 70, 0);
                  //ProcessEvents();

                  PushBox(output, DWORD_BE('stsc')); //sample to chunk list
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(videoSampleToChunk.Num()));
                    for(UINT i=0; i<videoSampleToChunk.Num(); i++)
                    {
                        SampleToChunk &stc  = videoSampleToChunk[i];
                        output.OutputDword(fastHtonl(stc.firstChunkID));
                        output.OutputDword(fastHtonl(stc.samplesPerChunk));
                        output.OutputDword(DWORD_BE(1));
                    }
                  PopBox(output); //stsc
                  PushBox(output, DWORD_BE('stsz')); //sample sizes
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(0); //block size for all (0 if differing sizes)
                    output.OutputDword(fastHtonl(videoFrames.Num()));
                    for(UINT i=0; i<videoFrames.Num(); i++)
                        output.OutputDword(fastHtonl(videoFrames[i].size));
                  PopBox(output);

                  if(videoChunks.Num() && videoChunks.Last() > 0xFFFFFFFFLL)
                  {
                      PushBox(output, DWORD_BE('co64')); //chunk offsets
                      output.OutputDword(0); //version and flags (none)
                      output.OutputDword(fastHtonl(videoChunks.Num()));
                      for(UINT i=0; i<videoChunks.Num(); i++)
                          output.OutputQword(fastHtonll(videoChunks[i]));
                      PopBox(output); //co64
                  }
                  else
                  {
                      PushBox(output, DWORD_BE('stco')); //chunk offsets
                        output.OutputDword(0); //version and flags (none)
                        output.OutputDword(fastHtonl(videoChunks.Num()));
                        for(UINT i=0; i<videoChunks.Num(); i++)
                            output.OutputDword(fastHtonl((DWORD)videoChunks[i]));
                      PopBox(output); //stco
                  }
                PopBox(output); //stbl
              PopBox(output); //minf
            PopBox(output); //mdia
          PopBox(output); //trak

          //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 80, 0);
          //ProcessEvents();

          //------------------------------------------------------
          // info thingy
          PushBox(output, DWORD_BE('udta'));
            PushBox(output, DWORD_BE('meta'));
              output.OutputDword(0); //version and flags (none)
              PushBox(output, DWORD_BE('hdlr'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(0); //quicktime type
                output.OutputDword(DWORD_BE('mdir')); //metadata type
                output.OutputDword(DWORD_BE('appl')); //quicktime manufacturer reserved thingy
                output.OutputDword(0); //quicktime component reserved flag
                output.OutputDword(0); //quicktime component reserved flag mask
                output.OutputByte(0); //null string
              PopBox(output); //hdlr
              PushBox(output, DWORD_BE('ilst'));
                PushBox(output, DWORD_BE('\xa9too'));
                  PushBox(output, DWORD_BE('data'));
                    output.OutputDword(DWORD_BE(1)); //version (1) + flags (0)
                    output.OutputDword(0); //reserved
                    LPSTR lpVersion = OBS_VERSION_STRING_ANSI;
                    output.Serialize(lpVersion, (DWORD)strlen(lpVersion));
                  PopBox(output); //data
                PopBox(output); //@too
              PopBox(output); //ilst
            PopBox(output); //meta
          PopBox(output); //udta

        PopBox(output); //moov

        fileOut.Serialize(endBuffer.Array(), (DWORD)output.GetPos());
        fileOut.Close();

        XFile file;
        if(file.Open(strFile, XFILE_WRITE, XFILE_OPENEXISTING))
        {
#ifdef USE_64BIT_MP4
            file.SetPos((INT64)mdatStart+8, XFILE_BEGIN);

            UINT64 size = fastHtonll(mdatStop-mdatStart);
            file.Write(&size, 8);
#else
            file.SetPos((INT64)mdatStart, XFILE_BEGIN);
            UINT size = fastHtonl((DWORD)(mdatStop-mdatStart));
            file.Write(&size, 4);
#endif
            file.Close();
        }

        App->EnableSceneSwitching(true);

        //DestroyWindow(hwndProgressDialog);
    }