file_mp4_recorder.cpp
上传用户:sun1608
上传日期:2007-02-02
资源大小:6116k
文件大小:11k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

Visual C++

  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. #include "mp4live.h"
  23. #include "file_mp4_recorder.h"
  24. #include "video_v4l_source.h"
  25. int CMp4Recorder::ThreadMain(void) 
  26. {
  27. while (SDL_SemWait(m_myMsgQueueSemaphore) == 0) {
  28. CMsg* pMsg = m_myMsgQueue.get_message();
  29. if (pMsg != NULL) {
  30. switch (pMsg->get_value()) {
  31. case MSG_NODE_STOP_THREAD:
  32. DoStopRecord();
  33. delete pMsg;
  34. return 0;
  35. case MSG_NODE_START:
  36. DoStartRecord();
  37. break;
  38. case MSG_NODE_STOP:
  39. DoStopRecord();
  40. break;
  41. case MSG_SINK_FRAME:
  42. size_t dontcare;
  43. DoWriteFrame((CMediaFrame*)pMsg->get_message(dontcare));
  44. break;
  45. }
  46. delete pMsg;
  47. }
  48. }
  49. return -1;
  50. }
  51. void CMp4Recorder::DoStartRecord()
  52. {
  53. if (m_sink) {
  54. return;
  55. }
  56. // enable huge file mode in mp4 if estimated size goes over 1 GB
  57. bool hugeFile = 
  58. m_pConfig->m_recordEstFileSize > 1000000000;
  59. u_int32_t verbosity =
  60. MP4_DETAILS_ERROR /* | MP4_DETAILS_WRITE_ALL */;
  61. if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_OVERWRITE)) {
  62. m_mp4File = MP4Create(
  63. m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME),
  64. verbosity, hugeFile);
  65. } else {
  66. m_mp4File = MP4Modify(
  67. m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME),
  68. verbosity);
  69. }
  70. if (!m_mp4File) {
  71. return;
  72. }
  73. m_rawVideoTrackId = MP4_INVALID_TRACK_ID;
  74. m_encodedVideoTrackId = MP4_INVALID_TRACK_ID;
  75. m_rawAudioTrackId = MP4_INVALID_TRACK_ID;
  76. m_encodedAudioTrackId = MP4_INVALID_TRACK_ID;
  77. m_canRecordAudio = true;
  78. m_rawAudioTimeScale = m_encodedAudioTimeScale = 
  79. m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE);
  80. if (m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE)
  81.   && (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_VIDEO)
  82.     || m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO))) {
  83. m_movieTimeScale = m_videoTimeScale;
  84. } else if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {
  85. m_movieTimeScale = m_encodedAudioTimeScale;
  86. } else {
  87. m_movieTimeScale = m_rawAudioTimeScale;
  88. }
  89. MP4SetTimeScale(m_mp4File, m_movieTimeScale);
  90. if (m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE)) {
  91. if (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_VIDEO)) {
  92. m_rawVideoFrameNum = 1;
  93. m_canRecordAudio = false;
  94. m_rawVideoTrackId = MP4AddVideoTrack(
  95. m_mp4File,
  96. m_videoTimeScale,
  97. MP4_INVALID_DURATION,
  98. m_pConfig->m_videoWidth, 
  99. m_pConfig->m_videoHeight,
  100. MP4_YUV12_VIDEO_TYPE);
  101. if (m_rawVideoTrackId == MP4_INVALID_TRACK_ID) {
  102. error_message("can't create raw video track");
  103. goto start_failure;
  104. }
  105. MP4SetVideoProfileLevel(m_mp4File, 0xFF);
  106. }
  107. if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)) {
  108. m_encodedVideoFrameNum = 1;
  109. m_canRecordAudio = false;
  110. m_encodedVideoTrackId = MP4AddVideoTrack(
  111. m_mp4File,
  112. m_videoTimeScale,
  113. MP4_INVALID_DURATION,
  114. m_pConfig->m_videoWidth, 
  115. m_pConfig->m_videoHeight,
  116. MP4_MPEG4_VIDEO_TYPE);
  117. if (m_encodedVideoTrackId == MP4_INVALID_TRACK_ID) {
  118. error_message("can't create encoded video track");
  119. goto start_failure;
  120. }
  121. MP4SetVideoProfileLevel(m_mp4File, 
  122. m_pConfig->GetIntegerValue(CONFIG_VIDEO_PROFILE_ID));
  123. MP4SetTrackESConfiguration(m_mp4File, m_encodedVideoTrackId,
  124. m_pConfig->m_videoMpeg4Config, 
  125. m_pConfig->m_videoMpeg4ConfigLength); 
  126. }
  127. }
  128. if (m_pConfig->GetBoolValue(CONFIG_AUDIO_ENABLE)) {
  129. if (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_AUDIO)) {
  130. m_rawAudioFrameNum = 1;
  131. m_rawAudioDuration = 0;
  132. m_rawAudioTrackId = MP4AddAudioTrack(
  133. m_mp4File, 
  134. m_rawAudioTimeScale, 
  135. 0,
  136. MP4_PCM16_AUDIO_TYPE);
  137. if (m_rawAudioTrackId == MP4_INVALID_TRACK_ID) {
  138. error_message("can't create raw audio track");
  139. goto start_failure;
  140. }
  141. MP4SetAudioProfileLevel(m_mp4File, 0xFF);
  142. }
  143. if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {
  144. m_encodedAudioFrameNum = 1;
  145. m_encodedAudioDuration = 0;
  146. u_int8_t audioType;
  147. if (!strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
  148.   AUDIO_ENCODING_AAC)) {
  149. audioType = MP4_MPEG4_AUDIO_TYPE;
  150. MP4SetAudioProfileLevel(m_mp4File, 0x0F);
  151. } else {
  152. audioType = MP4_MP3_AUDIO_TYPE;
  153. MP4SetAudioProfileLevel(m_mp4File, 0xFE);
  154. }
  155. m_encodedAudioTrackId = MP4AddAudioTrack(
  156. m_mp4File, 
  157. m_encodedAudioTimeScale, 
  158. MP4_INVALID_DURATION,
  159. audioType);
  160. if (m_encodedAudioTrackId == MP4_INVALID_TRACK_ID) {
  161. error_message("can't create encoded audio track");
  162. goto start_failure;
  163. }
  164. if (!strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
  165.   AUDIO_ENCODING_AAC)) {
  166. u_int8_t* pConfig = NULL;
  167. u_int32_t configLength = 0;
  168. MP4AV_AacGetConfiguration(
  169. &pConfig,
  170. &configLength,
  171. MP4AV_AAC_LC_PROFILE,
  172. m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE),
  173. m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS));
  174. MP4SetTrackESConfiguration(
  175. m_mp4File, 
  176. m_encodedAudioTrackId,
  177. pConfig, 
  178. configLength);
  179. }
  180. }
  181. }
  182. m_sink = true;
  183. return;
  184. start_failure:
  185. MP4Close(m_mp4File);
  186. m_mp4File = NULL;
  187. return;
  188. }
  189. void CMp4Recorder::DoWriteFrame(CMediaFrame* pFrame)
  190. {
  191. if (pFrame == NULL) {
  192. return;
  193. }
  194. if (!m_sink) {
  195. delete pFrame;
  196. return;
  197. }
  198. if (pFrame->GetType() == CMediaFrame::PcmAudioFrame
  199.   && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_AUDIO)) {
  200. if (m_canRecordAudio) {
  201. if (m_rawAudioFrameNum == 1) {
  202. m_rawAudioStartTimestamp = pFrame->GetTimestamp();
  203. m_rawAudioDuration = 0;
  204. }
  205. // check for audio continuity
  206. Duration skew =
  207. MP4ConvertToTrackDuration(
  208. m_mp4File, 
  209. m_rawAudioTrackId, 
  210. (pFrame->GetTimestamp() - m_rawAudioStartTimestamp)
  211. - m_rawAudioDuration,
  212. TimestampTicks);
  213. if (skew > 128) {
  214. // record audio gap
  215. MP4WriteSample(
  216. m_mp4File,
  217. m_rawAudioTrackId,
  218. NULL,
  219. 0,
  220. skew);
  221. }
  222. MP4WriteSample(
  223. m_mp4File,
  224. m_rawAudioTrackId,
  225. (u_int8_t*)pFrame->GetData(), 
  226. pFrame->GetDataLength(),
  227. pFrame->GetDataLength() 
  228. / (2 * m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS)));
  229. m_rawAudioFrameNum++;
  230. m_rawAudioDuration += 
  231. pFrame->ConvertDuration(TimestampTicks);
  232. }
  233. } else if ((pFrame->GetType() == CMediaFrame::Mp3AudioFrame
  234.     || pFrame->GetType() == CMediaFrame::AacAudioFrame)
  235.   && m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {
  236. if (m_canRecordAudio) {
  237. if (m_encodedAudioFrameNum == 1) {
  238. m_encodedAudioStartTimestamp = pFrame->GetTimestamp();
  239. m_encodedAudioDuration = 0;
  240. }
  241. // check for audio continuity
  242. Duration skew =
  243. MP4ConvertToTrackDuration(
  244. m_mp4File, 
  245. m_encodedAudioTrackId, 
  246. (pFrame->GetTimestamp() - m_encodedAudioStartTimestamp)
  247. - m_encodedAudioDuration,
  248. TimestampTicks);
  249. if (skew > 128) {
  250. // record audio gap
  251. MP4WriteSample(
  252. m_mp4File,
  253. m_encodedAudioTrackId,
  254. NULL,
  255. 0,
  256. skew);
  257. }
  258. MP4WriteSample(
  259. m_mp4File,
  260. m_encodedAudioTrackId,
  261. (u_int8_t*)pFrame->GetData(), 
  262. pFrame->GetDataLength(),
  263. pFrame->ConvertDuration(m_encodedAudioTimeScale));
  264. m_encodedAudioFrameNum++;
  265. m_encodedAudioDuration += 
  266. pFrame->ConvertDuration(TimestampTicks);
  267. }
  268. } else if (pFrame->GetType() == CMediaFrame::YuvVideoFrame
  269.   && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_VIDEO)) {
  270. // let audio record if raw is the only video being recorded
  271. if (!m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)) {
  272. m_canRecordAudio = true;
  273. }
  274. MP4WriteSample(
  275. m_mp4File,
  276. m_rawVideoTrackId,
  277. (u_int8_t*)pFrame->GetData(), 
  278. pFrame->GetDataLength(),
  279. pFrame->ConvertDuration(m_videoTimeScale));
  280. m_rawVideoFrameNum++;
  281. } else if (pFrame->GetType() == CMediaFrame::Mpeg4VideoFrame
  282.   && m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)) {
  283. u_int8_t* pSample = NULL;
  284. u_int32_t sampleLength = 0;
  285. Duration sampleDuration = 
  286. pFrame->ConvertDuration(m_videoTimeScale);
  287. bool isIFrame = (MP4AV_Mpeg4GetVopType(
  288. (u_int8_t*)pFrame->GetData(), pFrame->GetDataLength()) == 'I');
  289. if (m_encodedVideoFrameNum > 1 || isIFrame) {
  290. pSample = (u_int8_t*)pFrame->GetData();
  291. sampleLength = pFrame->GetDataLength();
  292. m_canRecordAudio = true;
  293. } // else waiting for I frame at start of recording
  294. if (pSample != NULL) {
  295. MP4WriteSample(
  296. m_mp4File,
  297. m_encodedVideoTrackId,
  298. pSample, 
  299. sampleLength,
  300. sampleDuration,
  301. 0,
  302. isIFrame);
  303. m_encodedVideoFrameNum++;
  304. }
  305. }
  306. delete pFrame;
  307. }
  308. void CMp4Recorder::DoStopRecord()
  309. {
  310. if (!m_sink) {
  311. return;
  312. }
  313. bool optimize = false;
  314. // create hint tracks
  315. if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_HINT_TRACKS)) {
  316. if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_OPTIMIZE)) {
  317. optimize = true;
  318. }
  319. if (MP4_IS_VALID_TRACK_ID(m_encodedVideoTrackId)) {
  320. MP4AV_Rfc3016Hinter(
  321. m_mp4File, 
  322. m_encodedVideoTrackId,
  323. m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
  324. // LATER H.26L hinter when we have a real-time H.26L encoder
  325. }
  326. if (MP4_IS_VALID_TRACK_ID(m_encodedAudioTrackId)) {
  327. const char *encoding = 
  328. m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING);
  329. if (!strcasecmp(encoding, AUDIO_ENCODING_MP3)) {
  330. MP4AV_Rfc2250Hinter(
  331. m_mp4File, 
  332. m_encodedAudioTrackId, 
  333. false, 
  334. m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
  335. } else if (!strcasecmp(encoding, AUDIO_ENCODING_AAC)) {
  336. MP4AV_RfcIsmaHinter(
  337. m_mp4File, 
  338. m_encodedAudioTrackId, 
  339. false, 
  340. m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
  341. }
  342. }
  343. }
  344. // close the mp4 file
  345. MP4Close(m_mp4File);
  346. m_mp4File = NULL;
  347. // add ISMA style OD and Scene tracks
  348. if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)
  349.   || m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {
  350. bool useIsmaTag = false;
  351. // if AAC track is present, can tag this as ISMA compliant content
  352.    if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)
  353.   && !strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
  354.     AUDIO_ENCODING_AAC)) {
  355. useIsmaTag = true;
  356. }
  357. MP4MakeIsmaCompliant(
  358. m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME),
  359. 0,
  360. useIsmaTag);
  361. }
  362. if (optimize) {
  363. MP4Optimize(m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME));
  364. }
  365. m_sink = false;
  366. }