~ubuntu-branches/ubuntu/karmic/mpeg4ip/karmic

« back to all changes in this revision

Viewing changes to server/mp4live/file_mp4_recorder.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mario Limonciello
  • Date: 2008-01-12 15:59:56 UTC
  • Revision ID: james.westby@ubuntu.com-20080112155956-1vznw5njidvrh649
Tags: upstream-1.6dfsg
ImportĀ upstreamĀ versionĀ 1.6dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * The contents of this file are subject to the Mozilla Public
 
3
 * License Version 1.1 (the "License"); you may not use this file
 
4
 * except in compliance with the License. You may obtain a copy of
 
5
 * the License at http://www.mozilla.org/MPL/
 
6
 * 
 
7
 * Software distributed under the License is distributed on an "AS
 
8
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
9
 * implied. See the License for the specific language governing
 
10
 * rights and limitations under the License.
 
11
 * 
 
12
 * The Original Code is MPEG4IP.
 
13
 * 
 
14
 * The Initial Developer of the Original Code is Cisco Systems Inc.
 
15
 * Portions created by Cisco Systems Inc. are
 
16
 * Copyright (C) Cisco Systems Inc. 2000, 2001.  All Rights Reserved.
 
17
 * 
 
18
 * Contributor(s): 
 
19
 *              Dave Mackie             dmackie@cisco.com
 
20
 *              Bill May                wmay@cisco.com
 
21
 */
 
22
 
 
23
#include "mp4live.h"
 
24
#include "file_mp4_recorder.h"
 
25
#include "video_encoder.h"
 
26
#include "audio_encoder.h"
 
27
#include "text_encoder.h"
 
28
#include "mpeg4ip_byteswap.h"
 
29
#include "mp4av_h264.h"
 
30
#include "video_v4l_source.h"
 
31
//#define DEBUG_H264 1
 
32
 
 
33
int CMp4Recorder::ThreadMain(void) 
 
34
{
 
35
  CMsg *pMsg;
 
36
  bool stop = false;
 
37
 
 
38
  while (stop == false && SDL_SemWait(m_myMsgQueueSemaphore) == 0) {
 
39
    pMsg = m_myMsgQueue.get_message();
 
40
                
 
41
    if (pMsg != NULL) {
 
42
      switch (pMsg->get_value()) {
 
43
      case MSG_NODE_STOP_THREAD:
 
44
        DoStopRecord();
 
45
        stop = true;
 
46
        break;
 
47
      case MSG_NODE_START:
 
48
        DoStartRecord();
 
49
        break;
 
50
      case MSG_NODE_STOP:
 
51
        DoStopRecord();
 
52
        break;
 
53
      case MSG_SINK_FRAME:
 
54
        uint32_t dontcare;
 
55
        DoWriteFrame((CMediaFrame*)pMsg->get_message(dontcare));
 
56
        break;
 
57
      }
 
58
 
 
59
      delete pMsg;
 
60
    }
 
61
  }
 
62
 
 
63
  while ((pMsg = m_myMsgQueue.get_message()) != NULL) {
 
64
    error_message("recorder - had msg after stop");
 
65
    if (pMsg->get_value() == MSG_SINK_FRAME) {
 
66
      uint32_t dontcare;
 
67
      CMediaFrame *mf = (CMediaFrame*)pMsg->get_message(dontcare);
 
68
      if (mf->RemoveReference()) {
 
69
        delete mf;
 
70
      }
 
71
    }
 
72
    delete pMsg;
 
73
  }
 
74
  CHECK_AND_FREE(m_videoTempBuffer);
 
75
  m_videoTempBufferSize = 0;
 
76
  return 0;
 
77
}
 
78
 
 
79
void CMp4Recorder::DoStartRecord()
 
80
{
 
81
  // already recording
 
82
  if (m_sink) {
 
83
    return;
 
84
  }
 
85
  const char *filename;
 
86
 
 
87
  m_makeIod = true;
 
88
  m_makeIsmaCompliant = true;
 
89
 
 
90
  m_audioFrameType = UNDEFINEDFRAME;
 
91
  m_videoFrameType = UNDEFINEDFRAME;
 
92
  m_textFrameType = UNDEFINEDFRAME;
 
93
 
 
94
  if (m_stream != NULL) {
 
95
    // recording normal file
 
96
    m_video_profile = m_stream->GetVideoProfile();
 
97
    m_audio_profile = m_stream->GetAudioProfile();
 
98
    m_text_profile = m_stream->GetTextProfile();
 
99
    m_recordVideo = m_stream->GetBoolValue(STREAM_VIDEO_ENABLED);
 
100
    m_recordAudio = m_stream->GetBoolValue(STREAM_AUDIO_ENABLED);
 
101
    m_recordText = m_stream->GetBoolValue(STREAM_TEXT_ENABLED);
 
102
    if (m_recordAudio) {
 
103
      m_audioTimeScale = 
 
104
        m_audio_profile->GetIntegerValue(CFG_AUDIO_SAMPLE_RATE);
 
105
    }
 
106
    filename = m_stream->GetStringValue(STREAM_RECORD_MP4_FILE_NAME);
 
107
  } else {
 
108
    // recording raw file
 
109
    m_recordVideo = m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE) &&
 
110
      m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_VIDEO);
 
111
    m_recordAudio = m_pConfig->GetBoolValue(CONFIG_AUDIO_ENABLE) &&
 
112
      m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_AUDIO);
 
113
    m_recordText = false;
 
114
    m_audioTimeScale = m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE);
 
115
    filename = m_pConfig->GetStringValue(CONFIG_RECORD_RAW_MP4_FILE_NAME);
 
116
  }
 
117
 
 
118
  if (m_recordAudio &&
 
119
      m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_VIDEO_TIMESCALE_USES_AUDIO))
 
120
    m_videoTimeScale = m_audioTimeScale;
 
121
 
 
122
  m_prevVideoFrame = NULL;
 
123
  m_prevAudioFrame = NULL;
 
124
  m_prevTextFrame = NULL;
 
125
 
 
126
  m_videoTrackId = MP4_INVALID_TRACK_ID;
 
127
  m_audioTrackId = MP4_INVALID_TRACK_ID;
 
128
  m_textTrackId = MP4_INVALID_TRACK_ID;
 
129
 
 
130
  // are we recording any video?
 
131
  if (m_recordVideo || m_recordText) {
 
132
    m_movieTimeScale = m_videoTimeScale;
 
133
  } else { // just audio
 
134
    m_movieTimeScale = m_audioTimeScale;
 
135
  }
 
136
 
 
137
  // get the mp4 file setup
 
138
 
 
139
  // enable huge file mode in mp4 
 
140
  // if duration is very long or if estimated size goes over 1 GB
 
141
  u_int64_t duration = m_pConfig->GetIntegerValue(CONFIG_APP_DURATION) 
 
142
    * m_pConfig->GetIntegerValue(CONFIG_APP_DURATION_UNITS)
 
143
    * m_movieTimeScale;
 
144
  bool hugeFile = 
 
145
    (duration > 0xFFFFFFFF) 
 
146
    || (m_pConfig->m_recordEstFileSize > (TO_U64(1000000000)));
 
147
  uint32_t createFlags = 0;
 
148
  if (hugeFile) {
 
149
    createFlags |= MP4_CREATE_64BIT_DATA;
 
150
  }
 
151
  debug_message("Creating huge file - %s", hugeFile ? "yes" : "no");
 
152
  u_int32_t verbosity =
 
153
    MP4_DETAILS_ERROR  /*DEBUG  | MP4_DETAILS_WRITE_ALL */;
 
154
 
 
155
  bool create = false;
 
156
  switch (m_pConfig->GetIntegerValue(CONFIG_RECORD_MP4_FILE_STATUS)) {
 
157
  case FILE_MP4_APPEND:
 
158
    m_mp4File = MP4Modify(filename,
 
159
                          verbosity);
 
160
    break;
 
161
  case FILE_MP4_CREATE_NEW: {
 
162
    struct stat stats;
 
163
    const char *fname = filename;
 
164
    if (stat(fname, &stats) == 0) {
 
165
      // file already exists - create new one
 
166
      size_t len = strlen(fname);
 
167
      if (strncasecmp(fname + len - 4, ".mp4", 4) == 0) {
 
168
        len -= 4;
 
169
      }
 
170
      struct tm timeval;
 
171
      int ret;
 
172
      char *buffer = (char *)malloc(len + 22);
 
173
      do {
 
174
        time_t val = time(NULL);
 
175
        localtime_r(&val, &timeval);
 
176
        memcpy(buffer, fname, len);
 
177
        sprintf(buffer + len, "_%04u%02u%02u_%02u%02u%02u.mp4",
 
178
                1900 + timeval.tm_year, timeval.tm_mon + 1, timeval.tm_mday,
 
179
                timeval.tm_hour, timeval.tm_min, timeval.tm_sec);
 
180
        error_message("trying file %s", buffer);
 
181
        ret = stat(buffer, &stats);
 
182
        if (ret == 0) {
 
183
          SDL_Delay(100);
 
184
        }
 
185
      } while (ret == 0);
 
186
      m_mp4FileName = strdup(buffer);
 
187
      create = true;
 
188
      break;
 
189
    }
 
190
  }
 
191
    // else fall through
 
192
    
 
193
  case FILE_MP4_OVERWRITE:
 
194
    m_mp4FileName = strdup(filename);
 
195
    create = true;
 
196
    break;
 
197
  }
 
198
  if (create) {
 
199
    if (m_stream &&
 
200
        (m_recordAudio == false ||
 
201
         strcasecmp(m_audio_profile->GetStringValue(CFG_AUDIO_ENCODING), 
 
202
                    AUDIO_ENCODING_AMR) == 0) &&
 
203
        (m_recordVideo == false ||
 
204
         strcasecmp(m_video_profile->GetStringValue(CFG_VIDEO_ENCODING), 
 
205
                    VIDEO_ENCODING_H263) == 0)) {
 
206
      static char* p3gppSupportedBrands[2] = {"3gp5", "3gp4"};
 
207
 
 
208
      m_mp4File = MP4CreateEx(m_mp4FileName,
 
209
                              verbosity,
 
210
                              createFlags,
 
211
                              1,
 
212
                              0,
 
213
                              p3gppSupportedBrands[0],
 
214
                              0x0001,
 
215
                              p3gppSupportedBrands,
 
216
                              NUM_ELEMENTS_IN_ARRAY(p3gppSupportedBrands));
 
217
    } else {
 
218
      m_mp4File = MP4Create(m_mp4FileName,
 
219
                            verbosity, createFlags);
 
220
    }
 
221
  }
 
222
    
 
223
  if (!m_mp4File) {
 
224
    return;
 
225
  }
 
226
  MP4SetTimeScale(m_mp4File, m_movieTimeScale);
 
227
 
 
228
  char buffer[80];
 
229
  sprintf(buffer, "mp4live version %s %s", MPEG4IP_VERSION, 
 
230
          get_linux_video_type());
 
231
  MP4SetMetadataTool(m_mp4File, buffer);
 
232
 
 
233
  if (m_recordVideo) {
 
234
    m_videoFrameNumber = 1;
 
235
    if (m_stream == NULL) {
 
236
      m_videoTrackId = MP4AddVideoTrack(m_mp4File,
 
237
                                        m_videoTimeScale,
 
238
                                        MP4_INVALID_DURATION,
 
239
                                        m_pConfig->m_videoWidth, 
 
240
                                        m_pConfig->m_videoHeight,
 
241
                                        MP4_YUV12_VIDEO_TYPE);
 
242
 
 
243
      if (m_videoTrackId == MP4_INVALID_TRACK_ID) {
 
244
        error_message("can't create raw video track");
 
245
        goto start_failure;
 
246
      }
 
247
      m_videoFrameType = YUVVIDEOFRAME;
 
248
      MP4SetVideoProfileLevel(m_mp4File, 0xFF);
 
249
    } else {
 
250
      bool vIod, vIsma;
 
251
      uint8_t videoProfile;
 
252
      uint8_t *videoConfig;
 
253
      uint32_t videoConfigLen;
 
254
      uint8_t videoType;
 
255
 
 
256
      m_videoFrameType = 
 
257
        get_video_mp4_fileinfo(m_video_profile,
 
258
                               &vIod,
 
259
                               &vIsma,
 
260
                               &videoProfile,
 
261
                               &videoConfig,
 
262
                               &videoConfigLen,
 
263
                               &videoType);
 
264
 
 
265
      if (m_videoFrameType == H263VIDEOFRAME) {
 
266
        m_videoTrackId = MP4AddH263VideoTrack(m_mp4File,
 
267
                                              m_videoTimeScale,
 
268
                                              0,
 
269
                                              m_video_profile->m_videoWidth,
 
270
                                              m_video_profile->m_videoHeight,
 
271
                                              10,
 
272
                                              0,
 
273
                                              0, 
 
274
                                              0);
 
275
        uint32_t bitrate = 
 
276
          m_video_profile->GetIntegerValue(CFG_VIDEO_BIT_RATE) * 1000;
 
277
        // may need to do this at the end
 
278
        MP4SetH263Bitrates(m_mp4File,
 
279
                           m_videoTrackId,
 
280
                           bitrate,
 
281
                           bitrate);
 
282
      } else if (m_videoFrameType == H264VIDEOFRAME) {
 
283
        uint8_t avcprofile, profile_compat, avclevel;
 
284
        avcprofile = (m_video_profile->m_videoMpeg4ProfileId >> 16) & 0xff;
 
285
        profile_compat = (m_video_profile->m_videoMpeg4ProfileId >> 8) & 0xff;
 
286
        avclevel = (m_video_profile->m_videoMpeg4ProfileId) & 0xff;
 
287
        debug_message("h264 track %x %x %x", 
 
288
                      avcprofile, profile_compat, avclevel);
 
289
        m_videoTrackId = MP4AddH264VideoTrack(m_mp4File, 
 
290
                                              m_videoTimeScale,
 
291
                                              MP4_INVALID_DURATION,
 
292
                                              m_video_profile->m_videoWidth,
 
293
                                              m_video_profile->m_videoHeight,
 
294
                                              avcprofile, 
 
295
                                              profile_compat, 
 
296
                                              avclevel, 
 
297
                                              3);
 
298
 
 
299
        MP4SetVideoProfileLevel(m_mp4File, 0x7f);
 
300
        m_makeIod = false;
 
301
        m_makeIsmaCompliant = false;
 
302
      } else if (m_videoFrameType == H261VIDEOFRAME) {
 
303
        error_message("H.261 recording is not supported");
 
304
      } else {
 
305
        m_videoTrackId = MP4AddVideoTrack(m_mp4File,
 
306
                                          m_videoTimeScale,
 
307
                                          MP4_INVALID_DURATION,
 
308
                                          m_video_profile->m_videoWidth, 
 
309
                                          m_video_profile->m_videoHeight,
 
310
                                          videoType);
 
311
        
 
312
        if (vIod == false) m_makeIod = false;
 
313
        if (vIsma == false) m_makeIsmaCompliant = false;
 
314
        
 
315
        if (m_videoTrackId == MP4_INVALID_TRACK_ID) {
 
316
          error_message("can't create encoded video track");
 
317
          goto start_failure;
 
318
        }
 
319
        
 
320
        MP4SetVideoProfileLevel(m_mp4File, 
 
321
                                videoProfile);
 
322
 
 
323
        if (videoConfigLen > 0) {
 
324
          MP4SetTrackESConfiguration(
 
325
                                     m_mp4File, 
 
326
                                     m_videoTrackId,
 
327
                                     videoConfig,
 
328
                                     videoConfigLen);
 
329
        }
 
330
      }
 
331
    }
 
332
  }
 
333
 
 
334
  m_audioFrameNumber = 1;
 
335
  m_canRecordVideo = true;
 
336
  if (m_recordAudio) {
 
337
    if (m_stream == NULL) {
 
338
      // raw audio
 
339
      m_canRecordVideo = false;
 
340
      m_audioTrackId = MP4AddAudioTrack(m_mp4File, 
 
341
                                        m_audioTimeScale, 
 
342
                                        0,
 
343
                                        MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE);
 
344
 
 
345
      if (m_audioTrackId == MP4_INVALID_TRACK_ID) {
 
346
        error_message("can't create raw audio track");
 
347
        goto start_failure;
 
348
      }
 
349
 
 
350
      MP4SetAudioProfileLevel(m_mp4File, 0xFF);
 
351
      m_audioFrameType = PCMAUDIOFRAME;
 
352
    } else {
 
353
      u_int8_t audioType;
 
354
      bool createIod = false;
 
355
      bool isma_compliant = false;
 
356
      uint8_t audioProfile;
 
357
      uint8_t *pAudioConfig;
 
358
      uint32_t audioConfigLen;
 
359
      m_canRecordVideo = false;
 
360
      m_audioFrameType = 
 
361
        get_audio_mp4_fileinfo(m_audio_profile, 
 
362
                               &createIod,
 
363
                               &isma_compliant,
 
364
                               &audioProfile,
 
365
                               &pAudioConfig,
 
366
                               &audioConfigLen,
 
367
                               &audioType);
 
368
                                               
 
369
      if (m_audioFrameType == AMRNBAUDIOFRAME ||
 
370
          m_audioFrameType == AMRWBAUDIOFRAME) {
 
371
        m_audioTrackId = 
 
372
          MP4AddAmrAudioTrack(m_mp4File,
 
373
                              m_audioFrameType == AMRNBAUDIOFRAME ? 
 
374
                              8000 : 16000,
 
375
                              0, 
 
376
                              0, 
 
377
                              1, 
 
378
                              m_audioFrameType == AMRWBAUDIOFRAME);
 
379
      } else {
 
380
        if (createIod == false) m_makeIod = false;
 
381
        if (isma_compliant == false) 
 
382
          m_makeIsmaCompliant = false;
 
383
        MP4SetAudioProfileLevel(m_mp4File, audioProfile);
 
384
        m_audioTrackId = MP4AddAudioTrack(
 
385
                                                 m_mp4File, 
 
386
                                                 m_audioTimeScale, 
 
387
                                                 MP4_INVALID_DURATION,
 
388
                                                 audioType);
 
389
      }
 
390
 
 
391
      if (m_audioTrackId == MP4_INVALID_TRACK_ID) {
 
392
        error_message("can't create encoded audio track");
 
393
        goto start_failure;
 
394
      }
 
395
 
 
396
      if (pAudioConfig) {
 
397
        MP4SetTrackESConfiguration(
 
398
                                   m_mp4File, 
 
399
                                   m_audioTrackId,
 
400
                                   pAudioConfig, 
 
401
                                   audioConfigLen);
 
402
        free(pAudioConfig);
 
403
      }
 
404
    }
 
405
  }
 
406
  
 
407
  debug_message("recording text %u", m_recordText);
 
408
  if (m_recordText) {
 
409
    m_textFrameNumber = 1;
 
410
  if (m_stream == NULL) {
 
411
      m_recordText = false;
 
412
    } else {
 
413
      const char *url;
 
414
      m_textFrameType = get_text_mp4_fileinfo(m_text_profile, &url);
 
415
      debug_message("text type %u", m_textFrameType);
 
416
      if (m_textFrameType == HREFTEXTFRAME) {
 
417
        m_textTrackId = MP4AddHrefTrack(m_mp4File, m_textTimeScale, MP4_INVALID_DURATION, url);
 
418
        debug_message("Added text track %u", m_textTrackId);
 
419
      } else {
 
420
        m_recordText = false;
 
421
      }
 
422
    }
 
423
  }
 
424
      
 
425
  m_sink = true;
 
426
  return;
 
427
 
 
428
 start_failure:
 
429
  MP4Close(m_mp4File);
 
430
  m_mp4File = NULL;
 
431
  return;
 
432
}
 
433
 
 
434
void CMp4Recorder::ProcessEncodedAudioFrame (CMediaFrame *pFrame)
 
435
{
 
436
  if (m_audioFrameType == AMRNBAUDIOFRAME ||
 
437
      m_audioFrameType == AMRWBAUDIOFRAME) {
 
438
    uint8_t decMode = *(uint8_t *)pFrame->GetData();
 
439
    m_amrMode |= 1 << ((decMode >> 3) & 0xf);
 
440
  }
 
441
 
 
442
    if (m_audioFrameNumber == 1) {
 
443
      m_audioStartTimestamp = pFrame->GetTimestamp();
 
444
      debug_message("record audio start "U64, m_audioStartTimestamp);
 
445
      m_canRecordVideo = true;
 
446
      m_prevAudioFrame = pFrame;
 
447
      m_audioSamples = 0;
 
448
      m_audioDiffTicks = 0;
 
449
      m_audioDiffTicksTotal = 0;
 
450
      m_audioFrameNumber++;
 
451
      return; // wait until the next audio frame
 
452
    }
 
453
 
 
454
    Duration thisFrameDurationInTicks = 
 
455
      pFrame->GetTimestamp() - m_prevAudioFrame->GetTimestamp();
 
456
 
 
457
    Duration thisFrameDurationInSamples =
 
458
      GetTimescaleFromTicks(thisFrameDurationInTicks, m_audioTimeScale);
 
459
 
 
460
    Duration elapsedTimeFromTimestamp;
 
461
    elapsedTimeFromTimestamp = pFrame->GetTimestamp() - m_audioStartTimestamp;
 
462
 
 
463
    if (thisFrameDurationInSamples > pFrame->GetDuration()) {
 
464
      // we have a gap frame
 
465
      // add the number of extra samples
 
466
      m_audioSamples += 
 
467
        (thisFrameDurationInSamples - pFrame->GetDuration());
 
468
      error_message("adding encoded "D64" samples", 
 
469
                    thisFrameDurationInSamples - pFrame->GetDuration());
 
470
    }
 
471
    // we have a consecutive frame
 
472
    // Timestamp of pFrame should reflect encoded audio samples
 
473
    // take difference
 
474
    m_audioSamples += pFrame->GetDuration();
 
475
    /*
 
476
     * we don't convert the audio duration any more
 
477
     *  m_prevEncodedAudioFrame->SetDuration(audioDurationInSamples);
 
478
     * Instead, we'll convert the video duration
 
479
     */
 
480
    MP4WriteSample(
 
481
                   m_mp4File,
 
482
                   m_audioTrackId,
 
483
                   (u_int8_t*)m_prevAudioFrame->GetData(), 
 
484
                   m_prevAudioFrame->GetDataLength(),
 
485
                   m_prevAudioFrame->ConvertDuration(m_audioTimeScale));
 
486
 
 
487
    m_audioFrameNumber++;
 
488
    if (m_prevAudioFrame->RemoveReference()) {
 
489
      delete m_prevAudioFrame;
 
490
    }
 
491
    m_prevAudioFrame = pFrame;
 
492
}
 
493
 
 
494
/******************************************************************************
 
495
 * Process encoded video frame
 
496
 ******************************************************************************/
 
497
static uint32_t write_sei (uint8_t *to, const uint8_t *from, uint32_t len)
 
498
{
 
499
  uint8_t *nal_write = to + 4;
 
500
  uint32_t written_bytes = 1;
 
501
#ifdef DEBUG_H264
 
502
  debug_message("sei start %u", len);
 
503
#endif
 
504
  *nal_write++ = *from++;
 
505
  len--;
 
506
  
 
507
  uint32_t payload_type, payload_size, typesize, sizesize;
 
508
  while (len >= 2 && *from != 0x80) {
 
509
    payload_type = h264_read_sei_value(from, &typesize);
 
510
    payload_size = h264_read_sei_value(from + typesize, &sizesize);
 
511
#ifdef DEBUG_H264
 
512
    debug_message("type %u sizes %u", payload_type, payload_size);
 
513
#endif
 
514
    uint32_t size = typesize + sizesize + payload_size;
 
515
    if (size <= len) {
 
516
      switch (payload_type) {
 
517
      case 3:
 
518
      case 10:
 
519
      case 11:
 
520
      case 12:
 
521
        // need to skip these
 
522
        break;
 
523
      default:
 
524
        memmove(nal_write, from, size);
 
525
        nal_write += size;
 
526
        written_bytes += size;
 
527
      }
 
528
      from += size;
 
529
      len -= size;
 
530
    } else {
 
531
      len = 0;
 
532
    }
 
533
  }
 
534
  if (written_bytes <= 1) {
 
535
    return 0;
 
536
  } 
 
537
  to[0] = (written_bytes >> 24) & 0xff;
 
538
  to[1] = (written_bytes >> 16) & 0xff;
 
539
  to[2] = (written_bytes >> 8) & 0xff;
 
540
  to[3] = written_bytes & 0xff;
 
541
#ifdef DEBUG_H264
 
542
  debug_message("sei - %u bytes", written_bytes + 4);
 
543
#endif
 
544
  return written_bytes + 4;
 
545
}
 
546
 
 
547
void CMp4Recorder::WriteH264Frame (CMediaFrame *pFrame,
 
548
                                   Duration dur)
 
549
{
 
550
  bool isIFrame = false;
 
551
  Duration rend_offset = 0;
 
552
  rend_offset = pFrame->GetPtsTimestamp() - 
 
553
    pFrame->GetTimestamp();
 
554
  rend_offset = GetTimescaleFromTicks(rend_offset, m_movieTimeScale);
 
555
 
 
556
  h264_media_frame_t *mf = (h264_media_frame_t *)pFrame->GetData();
 
557
  
 
558
  uint32_t size = mf->buffer_len + (4 * mf->nal_number);
 
559
  if (size > m_videoTempBufferSize) {
 
560
    m_videoTempBuffer = (uint8_t *)realloc(m_videoTempBuffer, size);
 
561
    m_videoTempBufferSize = size;
 
562
  }
 
563
  uint32_t len_written = 0;
 
564
  for (uint32_t ix = 0; ix < mf->nal_number; ix++) {
 
565
    bool write_it = false;
 
566
    switch (mf->nal_bufs[ix].nal_type) {
 
567
    case H264_NAL_TYPE_SEI:
 
568
      len_written += write_sei(m_videoTempBuffer + len_written,
 
569
                               mf->buffer + mf->nal_bufs[ix].nal_offset,
 
570
                               mf->nal_bufs[ix].nal_length);
 
571
      break;
 
572
    case H264_NAL_TYPE_SEQ_PARAM: 
 
573
      if (mf->nal_bufs[ix].nal_length != m_videoH264SeqSize ||
 
574
          (m_videoH264Seq != NULL &&
 
575
           memcmp(m_videoH264Seq, 
 
576
                  mf->buffer + mf->nal_bufs[ix].nal_offset,
 
577
                  m_videoH264SeqSize) != 0)) {
 
578
        m_videoH264SeqSize = mf->nal_bufs[ix].nal_length;
 
579
        m_videoH264Seq = 
 
580
          (uint8_t *)realloc(m_videoH264Seq, m_videoH264SeqSize);
 
581
        memcpy(m_videoH264Seq, 
 
582
               mf->buffer + mf->nal_bufs[ix].nal_offset,
 
583
               m_videoH264SeqSize);
 
584
        MP4AddH264SequenceParameterSet(m_mp4File,
 
585
                                       m_videoTrackId,
 
586
                                       m_videoH264Seq,
 
587
                                       m_videoH264SeqSize);
 
588
        debug_message("writing seq parameter %u",mf->nal_bufs[ix].nal_length);
 
589
      }
 
590
      break;
 
591
    case H264_NAL_TYPE_PIC_PARAM:
 
592
      if (mf->nal_bufs[ix].nal_length != m_videoH264PicSize ||
 
593
          (m_videoH264Pic != NULL &&
 
594
           memcmp(m_videoH264Pic, 
 
595
                  mf->buffer + mf->nal_bufs[ix].nal_offset,
 
596
                  m_videoH264PicSize) != 0)) {
 
597
        m_videoH264PicSize = mf->nal_bufs[ix].nal_length;
 
598
        m_videoH264Pic = 
 
599
          (uint8_t *)realloc(m_videoH264Pic, m_videoH264PicSize);
 
600
        memcpy(m_videoH264Pic, 
 
601
               mf->buffer + mf->nal_bufs[ix].nal_offset,
 
602
               m_videoH264PicSize);
 
603
        MP4AddH264PictureParameterSet(m_mp4File,
 
604
                                      m_videoTrackId, 
 
605
                                      m_videoH264Pic,
 
606
                                      m_videoH264PicSize);
 
607
        debug_message("writing pic parameter %u", mf->nal_bufs[ix].nal_length);
 
608
      }
 
609
      break;
 
610
    case H264_NAL_TYPE_IDR_SLICE:
 
611
      isIFrame = true;
 
612
      // fall through
 
613
    case H264_NAL_TYPE_NON_IDR_SLICE:
 
614
    case H264_NAL_TYPE_DP_A_SLICE:
 
615
    case H264_NAL_TYPE_DP_B_SLICE:
 
616
    case H264_NAL_TYPE_DP_C_SLICE:
 
617
#if 0
 
618
      if (m_videoFrameNumber % 7 == 0) {
 
619
        debug_message("drop frame");
 
620
        return;
 
621
      }
 
622
#endif
 
623
      write_it = true;
 
624
      break;
 
625
    case H264_NAL_TYPE_FILLER_DATA:
 
626
      write_it = false;
 
627
    default:
 
628
      write_it = true;
 
629
      break;
 
630
    }
 
631
#ifdef DEBUG_H264
 
632
    debug_message("%u h264 nal %d type %d %u write %d", 
 
633
                  m_videoFrameNumber, ix, mf->nal_bufs[ix].nal_type, 
 
634
                  mf->nal_bufs[ix].nal_length,
 
635
                  write_it);
 
636
#endif
 
637
    if (write_it) {
 
638
      // write length.
 
639
      uint32_t to_write;
 
640
      to_write = mf->nal_bufs[ix].nal_length;
 
641
      m_videoTempBuffer[len_written] = (to_write >> 24) & 0xff;
 
642
      m_videoTempBuffer[len_written + 1] = (to_write >> 16) & 0xff;
 
643
      m_videoTempBuffer[len_written + 2] = (to_write >> 8) & 0xff;
 
644
      m_videoTempBuffer[len_written + 3] = to_write & 0xff;
 
645
      len_written += 4;
 
646
      memcpy(m_videoTempBuffer + len_written,
 
647
             mf->buffer + mf->nal_bufs[ix].nal_offset,
 
648
             to_write);
 
649
      len_written += to_write;
 
650
    }
 
651
  }
 
652
#ifdef DEBUG_H264
 
653
  debug_message("%u h264 write %u", m_videoFrameNumber, len_written);
 
654
#endif
 
655
  MP4WriteSample(m_mp4File, 
 
656
                 m_videoTrackId,
 
657
                 m_videoTempBuffer, 
 
658
                 len_written,
 
659
                 dur, 
 
660
                 rend_offset, 
 
661
                 isIFrame);
 
662
}
 
663
 
 
664
void CMp4Recorder::ProcessEncodedVideoFrame (CMediaFrame *pFrame)
 
665
{
 
666
    // we drop encoded video frames until we get the first encoded audio frame
 
667
    // after that we drop encoded video frames until we get the first I frame.
 
668
    // we then stretch this I frame to the start of the first encoded audio
 
669
    // frame and write it to the encoded video track
 
670
    bool isIFrame = false;
 
671
    uint8_t *pData, *pDataStart;
 
672
    uint32_t dataLen;
 
673
 
 
674
    if (m_videoFrameNumber == 1) {
 
675
      // wait until first audio frame before looking for the next I frame
 
676
      if (!m_canRecordVideo) {
 
677
        if (pFrame->RemoveReference()) delete pFrame;
 
678
        return;
 
679
      }
 
680
 
 
681
      // make sure this frame was captured after the first audio frame
 
682
      if (m_recordAudio &&
 
683
          pFrame->GetTimestamp() < m_audioStartTimestamp) {
 
684
        if (pFrame->RemoveReference()) delete pFrame;
 
685
        return;
 
686
      }
 
687
 
 
688
      dataLen = pFrame->GetDataLength();
 
689
      pDataStart = (uint8_t *)pFrame->GetData();
 
690
      if (pFrame->GetType() == MPEG4VIDEOFRAME) {
 
691
        pData = MP4AV_Mpeg4FindVop(pDataStart, dataLen);
 
692
        if (pData == NULL) {
 
693
          error_message("Couldn't find vop header");
 
694
          if (pFrame->RemoveReference()) delete pFrame;
 
695
          return;
 
696
        }
 
697
        int voptype =
 
698
            MP4AV_Mpeg4GetVopType(pData,
 
699
                                  dataLen - (pData - pDataStart));
 
700
        if (voptype != VOP_TYPE_I) {
 
701
          debug_message(U64" wrong vop type %d %02x %02x %02x %02x %02x", 
 
702
                        pFrame->GetTimestamp(),
 
703
                        voptype,
 
704
                        pData[0],
 
705
                        pData[1],
 
706
                        pData[2],
 
707
                        pData[3],
 
708
                        pData[4]);
 
709
          if (pFrame->RemoveReference()) delete pFrame;
 
710
          return;
 
711
        }
 
712
      } else if (pFrame->GetType() == H263VIDEOFRAME) {
 
713
        // wait for an i frame
 
714
        if ((pDataStart[4] & 0x02) != 0) {
 
715
          if (pFrame->RemoveReference()) delete pFrame;
 
716
          return;
 
717
        }
 
718
      } else if (pFrame->GetType() == H264VIDEOFRAME) {
 
719
        h264_media_frame_t *mf = (h264_media_frame_t *)pFrame->GetData();
 
720
        bool found_idr = false;
 
721
        for (uint32_t ix = 0;
 
722
             found_idr == false && ix < mf->nal_number;
 
723
             ix++) {
 
724
          found_idr = mf->nal_bufs[ix].nal_type == H264_NAL_TYPE_IDR_SLICE;
 
725
        }
 
726
#ifdef DEBUG_H264
 
727
        debug_message("h264 nals %d found %d", 
 
728
                      mf->nal_number, found_idr);
 
729
#endif
 
730
        if (found_idr == false) {
 
731
          if (pFrame->RemoveReference()) delete pFrame;
 
732
          return;
 
733
        }
 
734
      } else {
 
735
        // MPEG2 video
 
736
        int ret, ftype;
 
737
        ret = MP4AV_Mpeg3FindPictHdr(pDataStart, dataLen, &ftype);
 
738
        if (ret < 0 || ftype != 1) {
 
739
          if (pFrame->RemoveReference()) delete pFrame;
 
740
          return;
 
741
        }
 
742
      }
 
743
 
 
744
      debug_message("Video start ts "U64, pFrame->GetTimestamp());
 
745
        if (m_recordAudio) {
 
746
        // reset this timestamp to video's beginning
 
747
          pFrame->SetTimestamp(m_audioStartTimestamp);
 
748
      } 
 
749
      m_videoStartTimestamp = pFrame->GetTimestamp();
 
750
      m_prevVideoFrame = pFrame;
 
751
      m_videoFrameNumber++;
 
752
      m_videoDurationTimescale = 0;
 
753
      return; // wait until the next video frame
 
754
    }
 
755
 
 
756
    Duration videoDurationInTicks;
 
757
 
 
758
    videoDurationInTicks = 
 
759
      pFrame->GetTimestamp() - m_videoStartTimestamp;
 
760
 
 
761
    // at this point, we'll probably want to add in the audio drift values, 
 
762
    // if there are any
 
763
    Duration videoDurationInTimescaleTotal;
 
764
    videoDurationInTimescaleTotal = 
 
765
      GetTimescaleFromTicks(videoDurationInTicks, m_videoTimeScale);
 
766
 
 
767
    Duration videoDurationInTimescaleFrame;
 
768
    videoDurationInTimescaleFrame = 
 
769
      videoDurationInTimescaleTotal - m_videoDurationTimescale;
 
770
 
 
771
    m_videoDurationTimescale += videoDurationInTimescaleFrame;
 
772
#if 0
 
773
    debug_message("vdit "D64" vdits "D64" frame "D64" total "D64" "D64,
 
774
                  videoDurationInTicks, videoDurationInTimescaleTotal,
 
775
                  videoDurationInTimescaleFrame, m_videoDurationTimescale,
 
776
                  GetTicksFromTimescale(m_videoDurationTimescale, 0, 0, m_videoTimeScale));
 
777
#endif
 
778
    dataLen = m_prevVideoFrame->GetDataLength();
 
779
 
 
780
    
 
781
    if (pFrame->GetType() == H264VIDEOFRAME) {
 
782
      WriteH264Frame(m_prevVideoFrame, videoDurationInTimescaleFrame);
 
783
    } else {
 
784
      Duration rend_offset = 0;
 
785
      if (pFrame->GetType() == MPEG4VIDEOFRAME) {
 
786
        pData = MP4AV_Mpeg4FindVop((uint8_t *)m_prevVideoFrame->GetData(),
 
787
                                   dataLen);
 
788
        if (pData) {
 
789
          dataLen -= (pData - (uint8_t *)m_prevVideoFrame->GetData());
 
790
          int vop_type = MP4AV_Mpeg4GetVopType(pData,dataLen);
 
791
          isIFrame = (vop_type == VOP_TYPE_I);
 
792
          rend_offset = m_prevVideoFrame->GetPtsTimestamp() - 
 
793
            m_prevVideoFrame->GetTimestamp();
 
794
          if (rend_offset != 0 && vop_type != VOP_TYPE_B) {
 
795
            rend_offset = GetTimescaleFromTicks(rend_offset, m_movieTimeScale);
 
796
          }
 
797
#if 0
 
798
          debug_message("record type %d %02x %02x %02x %02x",
 
799
                        MP4AV_Mpeg4GetVopType(pData, dataLen),
 
800
                        pData[0],
 
801
                        pData[1],
 
802
                        pData[2],
 
803
                        pData[3]);
 
804
#endif
 
805
        } else {
 
806
          pData = (uint8_t *)m_prevVideoFrame->GetData();
 
807
        }
 
808
      } else if (pFrame->GetType() == H263VIDEOFRAME) {
 
809
        pData = (uint8_t *)m_prevVideoFrame->GetData();
 
810
        isIFrame = ((pData[4] & 0x02) == 0);
 
811
#if 0
 
812
        isIFrame =
 
813
          (MP4AV_Mpeg4GetVopType(pData,dataLen) == VOP_TYPE_I);
 
814
        debug_message("frame %02x %02x %02x %d", pData[2], pData[3], pData[4],
 
815
                      isIFrame);
 
816
#endif
 
817
      } else {
 
818
        // mpeg2
 
819
        int ret, ftype;
 
820
        pData = (uint8_t *)m_prevVideoFrame->GetData();
 
821
        ret = MP4AV_Mpeg3FindPictHdr(pData, dataLen, &ftype);
 
822
        isIFrame = false;
 
823
        if (ret >= 0) {
 
824
          if (ftype == 1) isIFrame = true;
 
825
          if (ftype != 3) {
 
826
#if 1
 
827
            rend_offset = m_prevVideoFrame->GetPtsTimestamp() - 
 
828
              m_prevVideoFrame->GetTimestamp();
 
829
            rend_offset = GetTimescaleFromTicks(rend_offset, m_movieTimeScale);
 
830
#else
 
831
            rend_offset = 
 
832
              GetTimescaleFromTicks(m_prevVideoFrame->GetPtsTimestamp(),
 
833
                                    m_movieTimeScale);
 
834
            rend_offset -= 
 
835
              GetTimescaleFromTicks(m_prevVideoFrame->GetTimestamp(),
 
836
                                    m_movieTimeScale);
 
837
#endif
 
838
          }
 
839
        }
 
840
      }
 
841
 
 
842
      MP4WriteSample(
 
843
                     m_mp4File,
 
844
                     m_videoTrackId,
 
845
                     pData,
 
846
                     dataLen,
 
847
                     videoDurationInTimescaleFrame,
 
848
                     rend_offset,
 
849
                     isIFrame);
 
850
    }
 
851
                
 
852
    m_videoFrameNumber++;
 
853
    if (m_prevVideoFrame->RemoveReference()) {
 
854
      delete m_prevVideoFrame;
 
855
    }
 
856
    m_prevVideoFrame = pFrame;
 
857
}
 
858
 
 
859
void CMp4Recorder::ProcessEncodedTextFrame (CMediaFrame *pFrame)
 
860
{
 
861
  if (m_textFrameNumber == 1) {
 
862
    if (m_prevTextFrame != NULL) {
 
863
      if (m_prevTextFrame->RemoveReference()) {
 
864
        delete m_prevTextFrame;
 
865
      }
 
866
    }
 
867
    m_prevTextFrame = pFrame;
 
868
 
 
869
    if (m_canRecordVideo == false) return;
 
870
 
 
871
    if (m_recordAudio) {
 
872
    // we can record.  Set the timestamp of this frame to the audio start timestamp
 
873
      m_prevTextFrame->SetTimestamp(m_audioStartTimestamp);
 
874
      m_textStartTimestamp = m_audioStartTimestamp;
 
875
    } else {
 
876
      // need to work with video here, as well...
 
877
      m_textStartTimestamp = m_prevTextFrame->GetTimestamp();
 
878
    }
 
879
    m_textFrameNumber++;
 
880
    m_textDurationTimescale = 0;
 
881
    return;
 
882
  }
 
883
 
 
884
  Duration textDurationInTicks;
 
885
  textDurationInTicks = pFrame->GetTimestamp() - m_textStartTimestamp;
 
886
  
 
887
  Duration textDurationInTimescaleTotal;
 
888
  textDurationInTimescaleTotal = 
 
889
    GetTimescaleFromTicks(textDurationInTicks, m_textTimeScale);
 
890
 
 
891
  Duration textDurationInTimescaleFrame;
 
892
  textDurationInTimescaleFrame = 
 
893
    textDurationInTimescaleTotal - m_textDurationTimescale;
 
894
 
 
895
  m_textDurationTimescale += textDurationInTimescaleFrame;
 
896
 
 
897
  MP4WriteSample(m_mp4File, m_textTrackId,
 
898
                 (uint8_t *)m_prevTextFrame->GetData(),
 
899
                 m_prevTextFrame->GetDataLength(),
 
900
                 textDurationInTimescaleFrame);
 
901
  debug_message("wrote text frame %u", m_textFrameNumber);
 
902
 
 
903
  m_textFrameNumber++;
 
904
  if (m_prevTextFrame->RemoveReference()) {
 
905
    delete m_prevTextFrame;
 
906
  }
 
907
  m_prevTextFrame = pFrame;
 
908
}
 
909
 
 
910
 
 
911
void CMp4Recorder::DoWriteFrame(CMediaFrame* pFrame)
 
912
{
 
913
  // dispose of degenerate cases
 
914
  if (pFrame == NULL) return;
 
915
 
 
916
  if (!m_sink) {
 
917
    if (pFrame->RemoveReference()) {
 
918
      delete pFrame;
 
919
    }
 
920
    return;
 
921
  }
 
922
  // RAW AUDIO
 
923
  if (m_recordAudio) {
 
924
    if ((m_stream == NULL && pFrame->GetType() == PCMAUDIOFRAME) ||
 
925
        (m_stream != NULL && pFrame->GetType() == NETPCMAUDIOFRAME)) {
 
926
      if (m_audioFrameNumber == 1) {
 
927
 
 
928
        debug_message("First raw audio frame at "U64, pFrame->GetTimestamp());
 
929
 
 
930
        m_audioStartTimestamp = pFrame->GetTimestamp();
 
931
        m_canRecordVideo = true;
 
932
        m_prevAudioFrame = pFrame;
 
933
        m_audioFrameNumber++;
 
934
        m_audioSamples = 0;
 
935
        return; // wait until the next audio frame
 
936
      }
 
937
 
 
938
#if 0
 
939
      Duration audioFrameSamples = 
 
940
        m_prevAudioFrame->GetDatalength() /
 
941
        (m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS) * sizeof(int16_t));
 
942
 
 
943
      m_audioSamples += ======
 
944
#endif
 
945
 
 
946
 
 
947
 
 
948
        Duration audioDurationInTicks = 
 
949
        pFrame->GetTimestamp() - m_prevAudioFrame->GetTimestamp();
 
950
 
 
951
      MP4Duration audioDurationInSamples =
 
952
        MP4ConvertToTrackDuration(m_mp4File, m_audioTrackId,
 
953
                                  audioDurationInTicks, TimestampTicks);
 
954
#if 0
 
955
      debug_message("prev "U64" this "U64" diff samples"U64,
 
956
                    m_prevAudioFrame->GetTimestamp(),
 
957
                    pFrame->GetTimestamp(),
 
958
                    audioDurationInSamples);
 
959
#endif
 
960
                  
 
961
      m_prevAudioFrame->SetDuration(audioDurationInSamples);
 
962
      void *pcm;
 
963
#ifdef WORDS_BIGENDIAN
 
964
      pcm = m_prevAudioFrame->GetData();
 
965
#else
 
966
      if (pFrame->GetType() != NETPCMAUDIOFRAME) {
 
967
        uint32_t convert_size = m_prevAudioFrame->GetDataLength();
 
968
        uint16_t *pdata = (uint16_t *)m_prevAudioFrame->GetData();
 
969
        
 
970
        if (m_convert_pcm_size < convert_size) {
 
971
          m_convert_pcm = (uint16_t *)realloc(m_convert_pcm, convert_size);
 
972
          m_convert_pcm_size = convert_size;
 
973
        }
 
974
        convert_size /= sizeof(uint16_t);
 
975
        for (uint32_t ix = 0; ix < convert_size; ix++) {
 
976
        uint16_t swap = *pdata++;
 
977
        m_convert_pcm[ix] = B2N_16(swap);
 
978
        }
 
979
        pcm = m_convert_pcm;
 
980
      } else {
 
981
        pcm = m_prevAudioFrame->GetData();
 
982
      }
 
983
#endif
 
984
      MP4WriteSample(
 
985
                     m_mp4File,
 
986
                     m_audioTrackId,
 
987
                     (u_int8_t*)pcm,
 
988
                     m_prevAudioFrame->GetDataLength(),
 
989
                     audioDurationInSamples);
 
990
      //                   m_prevAudioFrame->ConvertDuration(m_audioTimeScale));
 
991
 
 
992
      m_audioFrameNumber++;
 
993
      if (m_prevAudioFrame->RemoveReference()) {
 
994
        delete m_prevAudioFrame;
 
995
      }
 
996
      m_prevAudioFrame = pFrame;
 
997
      return;
 
998
      // ENCODED AUDIO
 
999
    } else if (pFrame->GetType() == m_audioFrameType) {
 
1000
 
 
1001
      ProcessEncodedAudioFrame(pFrame);
 
1002
      return;
 
1003
      // RAW VIDEO
 
1004
    } 
 
1005
  }
 
1006
 
 
1007
  if (m_recordVideo) {
 
1008
    if (m_stream == NULL && pFrame->GetType() == YUVVIDEOFRAME) {
 
1009
      // we drop raw video frames until we get the first raw audio frame
 
1010
      // after that:
 
1011
      // if we are also recording encoded video, we wait until the first I frame
 
1012
      // else
 
1013
      // we wait until the next raw video frame
 
1014
      // in both cases, the duration of the first raw video frame is stretched
 
1015
      // to the start of the first raw audio frame
 
1016
    
 
1017
      if (m_videoFrameNumber == 1) {
 
1018
        // wait until the first raw audio frame is received
 
1019
        if (!m_canRecordVideo) {
 
1020
          if (pFrame->RemoveReference()) delete pFrame;
 
1021
          return;
 
1022
        }
 
1023
 
 
1024
        // make sure this frame was captured after the first audio frame
 
1025
        if (m_recordAudio &&
 
1026
            pFrame->GetTimestamp() < m_audioStartTimestamp) {
 
1027
          if (pFrame->RemoveReference()) delete pFrame;
 
1028
          return;
 
1029
        }
 
1030
 
 
1031
        debug_message("First raw video frame at "U64, pFrame->GetTimestamp());
 
1032
 
 
1033
        m_videoStartTimestamp = pFrame->GetTimestamp();
 
1034
        m_prevVideoFrame = pFrame;
 
1035
        m_videoFrameNumber++;
 
1036
        return; // wait until the next video frame
 
1037
      }
 
1038
 
 
1039
      Duration videoDurationInTicks;
 
1040
    
 
1041
      // the first raw video frame is stretched to the begining
 
1042
      // of the first raw audio frame
 
1043
      if (m_videoFrameNumber == 2 && m_recordAudio) {
 
1044
        videoDurationInTicks =
 
1045
          pFrame->GetTimestamp() - m_audioStartTimestamp;
 
1046
      } else {
 
1047
        videoDurationInTicks =
 
1048
          pFrame->GetTimestamp() - m_prevVideoFrame->GetTimestamp();
 
1049
      }
 
1050
 
 
1051
      m_prevVideoFrame->SetDuration(videoDurationInTicks);
 
1052
      yuv_media_frame_t *pYUV = (yuv_media_frame_t *)m_prevVideoFrame->GetData();
 
1053
      if (pYUV->y + m_pConfig->m_ySize == pYUV->u) {
 
1054
        MP4WriteSample(
 
1055
                       m_mp4File,
 
1056
                       m_videoTrackId,
 
1057
                       pYUV->y,
 
1058
                       m_pConfig->m_yuvSize,
 
1059
                       m_prevVideoFrame->ConvertDuration(m_videoTimeScale));
 
1060
      } else {
 
1061
        if (m_rawYUV == NULL) {
 
1062
          debug_message("Mallocing %u", m_pConfig->m_yuvSize);
 
1063
          m_rawYUV = (uint8_t *)malloc(m_pConfig->m_yuvSize);
 
1064
        }
 
1065
        CopyYuv(pYUV->y, pYUV->u, pYUV->v,
 
1066
                pYUV->y_stride, pYUV->uv_stride, pYUV->uv_stride,
 
1067
                m_rawYUV, 
 
1068
                m_rawYUV + m_pConfig->m_ySize, 
 
1069
                m_rawYUV + m_pConfig->m_ySize + m_pConfig->m_uvSize,
 
1070
                m_pConfig->m_videoWidth, 
 
1071
                m_pConfig->m_videoWidth / 2, 
 
1072
                m_pConfig->m_videoWidth / 2,
 
1073
                m_pConfig->m_videoWidth, m_pConfig->m_videoHeight);
 
1074
        MP4WriteSample(m_mp4File, 
 
1075
                       m_videoTrackId, 
 
1076
                       m_rawYUV,
 
1077
                       m_pConfig->m_yuvSize,
 
1078
                       m_prevVideoFrame->ConvertDuration(m_videoTimeScale));
 
1079
      }
 
1080
              
 
1081
 
 
1082
      m_videoFrameNumber++;
 
1083
      if (m_prevVideoFrame->RemoveReference()) {
 
1084
        delete m_prevVideoFrame;
 
1085
      }
 
1086
      m_prevVideoFrame = pFrame;
 
1087
      return;
 
1088
      // ENCODED VIDEO
 
1089
    } else if (pFrame->GetType() == m_videoFrameType) {
 
1090
 
 
1091
      ProcessEncodedVideoFrame(pFrame);
 
1092
      return;
 
1093
    }
 
1094
  }
 
1095
  if (pFrame->GetType() == m_textFrameType && m_recordText) {
 
1096
    ProcessEncodedTextFrame(pFrame);
 
1097
  } else {    // degenerate case
 
1098
    if (pFrame->RemoveReference()) delete pFrame;
 
1099
  }
 
1100
}
 
1101
 
 
1102
void CMp4Recorder::DoStopRecord()
 
1103
{
 
1104
  if (!m_sink) return;
 
1105
 
 
1106
  Duration totalAudioDuration = 0;
 
1107
 
 
1108
  // write last audio frame
 
1109
  if (m_prevAudioFrame) {
 
1110
    MP4WriteSample(
 
1111
                   m_mp4File,
 
1112
                   m_audioTrackId,
 
1113
                   (u_int8_t*)m_prevAudioFrame->GetData(), 
 
1114
                   m_prevAudioFrame->GetDataLength(),
 
1115
                   m_prevAudioFrame->ConvertDuration(m_audioTimeScale));
 
1116
    m_audioSamples += m_prevAudioFrame->GetDuration();
 
1117
 
 
1118
    totalAudioDuration = m_audioSamples;
 
1119
    totalAudioDuration *= TimestampTicks;
 
1120
    totalAudioDuration /= m_audioTimeScale;
 
1121
 
 
1122
    if (m_prevAudioFrame->RemoveReference()) {
 
1123
      delete m_prevAudioFrame;
 
1124
    }
 
1125
    if (m_audioFrameType == AMRNBAUDIOFRAME ||
 
1126
        m_audioFrameType == AMRWBAUDIOFRAME) {
 
1127
      MP4SetAmrModeSet(m_mp4File, m_audioTrackId, m_amrMode);
 
1128
    }
 
1129
  }
 
1130
 
 
1131
  // write last video frame
 
1132
  if (m_prevVideoFrame) {
 
1133
    if (m_stream == NULL) {
 
1134
#if 1
 
1135
      error_message("write last raw video frame");
 
1136
#else
 
1137
      MP4WriteSample(
 
1138
                     m_mp4File,
 
1139
                     m_videoTrackId,
 
1140
                     (u_int8_t*)m_prevVideoFrame->GetData(), 
 
1141
                     m_prevVideoFrame->GetDataLength(),
 
1142
                     m_prevVideoFrame->ConvertDuration(m_videoTimeScale));
 
1143
#endif
 
1144
      if (m_prevVideoFrame->RemoveReference()) {
 
1145
        delete m_prevVideoFrame;
 
1146
      }
 
1147
      CHECK_AND_FREE(m_rawYUV);
 
1148
    } else {
 
1149
      bool isIFrame;
 
1150
      Duration rend_offset = 0;
 
1151
 
 
1152
      if (m_prevVideoFrame->GetType() == H264VIDEOFRAME) {
 
1153
        WriteH264Frame(m_prevVideoFrame, 
 
1154
                       m_prevVideoFrame->ConvertDuration(m_videoTimeScale));
 
1155
      } else {
 
1156
        if (m_prevVideoFrame->GetType() == MPEG4VIDEOFRAME ||
 
1157
            m_prevVideoFrame->GetType() == H263VIDEOFRAME) {
 
1158
          isIFrame = 
 
1159
            (MP4AV_Mpeg4GetVopType(
 
1160
                                   (u_int8_t*) m_prevVideoFrame->GetData(),
 
1161
                                   m_prevVideoFrame->GetDataLength()) == VOP_TYPE_I);
 
1162
        } else {
 
1163
          int ret, ftype;
 
1164
          ret = 
 
1165
            MP4AV_Mpeg3FindPictHdr((u_int8_t *)m_prevVideoFrame->GetData(), 
 
1166
                                   m_prevVideoFrame->GetDataLength(),
 
1167
                                   &ftype);
 
1168
          isIFrame = false;
 
1169
          if (ret >= 0) {
 
1170
            if (ftype == 1) isIFrame = true;
 
1171
            if (ftype != 3) {
 
1172
              rend_offset = m_prevVideoFrame->GetPtsTimestamp() - 
 
1173
                m_prevVideoFrame->GetTimestamp();
 
1174
              rend_offset = GetTimescaleFromTicks(rend_offset, m_movieTimeScale);
 
1175
            }
 
1176
          }
 
1177
        }
 
1178
 
 
1179
        MP4WriteSample(
 
1180
                       m_mp4File,
 
1181
                       m_videoTrackId,
 
1182
                       (u_int8_t*) m_prevVideoFrame->GetData(),
 
1183
                       m_prevVideoFrame->GetDataLength(),
 
1184
                       m_prevVideoFrame->ConvertDuration(m_videoTimeScale),
 
1185
                       rend_offset,
 
1186
                       isIFrame);
 
1187
      }
 
1188
      if (m_prevVideoFrame->RemoveReference()) {
 
1189
        delete m_prevVideoFrame;
 
1190
      }
 
1191
    }
 
1192
  }
 
1193
 
 
1194
  if (m_prevTextFrame) {
 
1195
    Duration lastDuration = TimestampTicks;
 
1196
 
 
1197
    if (totalAudioDuration != 0) {
 
1198
      Duration totalSoFar = m_prevTextFrame->GetTimestamp() - m_textStartTimestamp;
 
1199
      if (totalAudioDuration > totalSoFar) {
 
1200
        lastDuration = totalAudioDuration - totalSoFar;
 
1201
      }
 
1202
    }
 
1203
    debug_message("last duration "U64" timescale "U64,
 
1204
                  lastDuration, GetTimescaleFromTicks(lastDuration, m_textTimeScale));
 
1205
    MP4WriteSample(m_mp4File, 
 
1206
                   m_textTrackId, 
 
1207
                   (uint8_t *)m_prevTextFrame->GetData(),
 
1208
                   m_prevTextFrame->GetDataLength(),
 
1209
                   GetTimescaleFromTicks(lastDuration, m_textTimeScale));
 
1210
    if (m_prevTextFrame->RemoveReference()) {
 
1211
      delete m_prevTextFrame;
 
1212
    }
 
1213
    m_prevTextFrame = NULL;
 
1214
  }
 
1215
    
 
1216
  CHECK_AND_FREE(m_videoH264Seq);
 
1217
  m_videoH264SeqSize = 0;
 
1218
  CHECK_AND_FREE(m_videoH264Pic);
 
1219
  m_videoH264PicSize = 0;
 
1220
 
 
1221
  // close the mp4 file
 
1222
  MP4Close(m_mp4File);
 
1223
  m_mp4File = NULL;
 
1224
    
 
1225
  debug_message("done with writing last frame");
 
1226
  bool optimize = false;
 
1227
 
 
1228
  // create hint tracks
 
1229
  if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_HINT_TRACKS)) {
 
1230
 
 
1231
    m_mp4File = MP4Modify(m_mp4FileName, MP4_DETAILS_ERROR);
 
1232
 
 
1233
    if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_OPTIMIZE)) {
 
1234
      optimize = true;
 
1235
    }
 
1236
 
 
1237
    if (m_stream != NULL) {
 
1238
      if (MP4_IS_VALID_TRACK_ID(m_videoTrackId) && m_stream) {
 
1239
        create_mp4_video_hint_track(m_video_profile,
 
1240
                                    m_mp4File, 
 
1241
                                    m_videoTrackId,
 
1242
                                    m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
 
1243
      }
 
1244
 
 
1245
      if (MP4_IS_VALID_TRACK_ID(m_audioTrackId)) {
 
1246
        create_mp4_audio_hint_track(m_audio_profile, 
 
1247
                                    m_mp4File, 
 
1248
                                    m_audioTrackId,
 
1249
                                    m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
 
1250
      }
 
1251
      if (MP4_IS_VALID_TRACK_ID(m_textTrackId)) {
 
1252
        create_mp4_text_hint_track(m_text_profile, 
 
1253
                                   m_mp4File, 
 
1254
                                   m_textTrackId,
 
1255
                                   m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
 
1256
      }
 
1257
    } else {
 
1258
      if (MP4_IS_VALID_TRACK_ID(m_audioTrackId)) {
 
1259
        L16Hinter(m_mp4File, 
 
1260
                  m_audioTrackId,
 
1261
                  m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
 
1262
      }
 
1263
    }
 
1264
    MP4Close(m_mp4File);
 
1265
    m_mp4File = NULL;
 
1266
  }
 
1267
  debug_message("done with hint");
 
1268
 
 
1269
  // add ISMA style OD and Scene tracks
 
1270
  if (m_stream != NULL) {
 
1271
    if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_ISMA_COMPLIANT)) {
 
1272
      bool useIsmaTag = false;
 
1273
      
 
1274
    // if AAC track is present, can tag this as ISMA compliant content
 
1275
      useIsmaTag = m_makeIsmaCompliant;
 
1276
      if (m_makeIod) {
 
1277
        MP4MakeIsmaCompliant(
 
1278
                             m_mp4FileName,
 
1279
                             0,
 
1280
                             useIsmaTag);
 
1281
      }
 
1282
    }
 
1283
  }
 
1284
 
 
1285
  if (optimize) {
 
1286
    MP4Optimize(m_mp4FileName);
 
1287
  }
 
1288
 
 
1289
  m_sink = false;
 
1290
}