📄 file_mp4_recorder.cpp
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com * Bill May wmay@cisco.com */#include "mp4live.h"#include "file_mp4_recorder.h"#include "video_encoder.h"#include "audio_encoder.h"#include "text_encoder.h"#include "mpeg4ip_byteswap.h"#include "mp4av_h264.h"#include "video_v4l_source.h"//#define DEBUG_H264 1int CMp4Recorder::ThreadMain(void) { CMsg *pMsg; bool stop = false; while (stop == false && SDL_SemWait(m_myMsgQueueSemaphore) == 0) { pMsg = m_myMsgQueue.get_message(); if (pMsg != NULL) { switch (pMsg->get_value()) { case MSG_NODE_STOP_THREAD: DoStopRecord(); stop = true; break; case MSG_NODE_START: DoStartRecord(); break; case MSG_NODE_STOP: DoStopRecord(); break; case MSG_SINK_FRAME: uint32_t dontcare; DoWriteFrame((CMediaFrame*)pMsg->get_message(dontcare)); break; } delete pMsg; } } while ((pMsg = m_myMsgQueue.get_message()) != NULL) { error_message("recorder - had msg after stop"); if (pMsg->get_value() == MSG_SINK_FRAME) { uint32_t dontcare; CMediaFrame *mf = (CMediaFrame*)pMsg->get_message(dontcare); if (mf->RemoveReference()) { delete mf; } } delete pMsg; } CHECK_AND_FREE(m_videoTempBuffer); m_videoTempBufferSize = 0; return 0;}void CMp4Recorder::DoStartRecord(){ // already recording if (m_sink) { return; } const char *filename; m_makeIod = true; m_makeIsmaCompliant = true; m_audioFrameType = UNDEFINEDFRAME; m_videoFrameType = UNDEFINEDFRAME; m_textFrameType = UNDEFINEDFRAME; if (m_stream != NULL) { // recording normal file m_video_profile = m_stream->GetVideoProfile(); m_audio_profile = m_stream->GetAudioProfile(); m_text_profile = m_stream->GetTextProfile(); m_recordVideo = m_stream->GetBoolValue(STREAM_VIDEO_ENABLED); m_recordAudio = m_stream->GetBoolValue(STREAM_AUDIO_ENABLED); m_recordText = m_stream->GetBoolValue(STREAM_TEXT_ENABLED); if (m_recordAudio) { m_audioTimeScale = m_audio_profile->GetIntegerValue(CFG_AUDIO_SAMPLE_RATE); } filename = m_stream->GetStringValue(STREAM_RECORD_MP4_FILE_NAME); } else { // recording raw file m_recordVideo = m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE) && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_VIDEO); m_recordAudio = m_pConfig->GetBoolValue(CONFIG_AUDIO_ENABLE) && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_AUDIO); m_recordText = false; m_audioTimeScale = m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE); filename = m_pConfig->GetStringValue(CONFIG_RECORD_RAW_MP4_FILE_NAME); } if (m_recordAudio && m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_VIDEO_TIMESCALE_USES_AUDIO)) m_videoTimeScale = m_audioTimeScale; m_prevVideoFrame = NULL; m_prevAudioFrame = NULL; m_prevTextFrame = NULL; m_videoTrackId = MP4_INVALID_TRACK_ID; m_audioTrackId = MP4_INVALID_TRACK_ID; m_textTrackId = MP4_INVALID_TRACK_ID; // are we recording any video? if (m_recordVideo || m_recordText) { m_movieTimeScale = m_videoTimeScale; } else { // just audio m_movieTimeScale = m_audioTimeScale; } // get the mp4 file setup // enable huge file mode in mp4 // if duration is very long or if estimated size goes over 1 GB u_int64_t duration = m_pConfig->GetIntegerValue(CONFIG_APP_DURATION) * m_pConfig->GetIntegerValue(CONFIG_APP_DURATION_UNITS) * m_movieTimeScale; bool hugeFile = (duration > 0xFFFFFFFF) || (m_pConfig->m_recordEstFileSize > (TO_U64(1000000000))); uint32_t createFlags = 0; if (hugeFile) { createFlags |= MP4_CREATE_64BIT_DATA; } debug_message("Creating huge file - %s", hugeFile ? "yes" : "no"); u_int32_t verbosity = MP4_DETAILS_ERROR /*DEBUG | MP4_DETAILS_WRITE_ALL */; bool create = false; switch (m_pConfig->GetIntegerValue(CONFIG_RECORD_MP4_FILE_STATUS)) { case FILE_MP4_APPEND: m_mp4File = MP4Modify(filename, verbosity); break; case FILE_MP4_CREATE_NEW: { struct stat stats; const char *fname = filename; if (stat(fname, &stats) == 0) { // file already exists - create new one size_t len = strlen(fname); if (strncasecmp(fname + len - 4, ".mp4", 4) == 0) { len -= 4; } struct tm timeval; int ret; char *buffer = (char *)malloc(len + 22); do { time_t val = time(NULL); localtime_r(&val, &timeval); memcpy(buffer, fname, len); sprintf(buffer + len, "_%04u%02u%02u_%02u%02u%02u.mp4", 1900 + timeval.tm_year, timeval.tm_mon + 1, timeval.tm_mday, timeval.tm_hour, timeval.tm_min, timeval.tm_sec); error_message("trying file %s", buffer); ret = stat(buffer, &stats); if (ret == 0) { SDL_Delay(100); } } while (ret == 0); m_mp4FileName = strdup(buffer); create = true; break; } } // else fall through case FILE_MP4_OVERWRITE: m_mp4FileName = strdup(filename); create = true; break; } if (create) { if (m_stream && (m_recordAudio == false || strcasecmp(m_audio_profile->GetStringValue(CFG_AUDIO_ENCODING), AUDIO_ENCODING_AMR) == 0) && (m_recordVideo == false || strcasecmp(m_video_profile->GetStringValue(CFG_VIDEO_ENCODING), VIDEO_ENCODING_H263) == 0)) { static char* p3gppSupportedBrands[2] = {"3gp5", "3gp4"}; m_mp4File = MP4CreateEx(m_mp4FileName, verbosity, createFlags, 1, 0, p3gppSupportedBrands[0], 0x0001, p3gppSupportedBrands, NUM_ELEMENTS_IN_ARRAY(p3gppSupportedBrands)); } else { m_mp4File = MP4Create(m_mp4FileName, verbosity, createFlags); } } if (!m_mp4File) { return; } MP4SetTimeScale(m_mp4File, m_movieTimeScale); char buffer[80]; sprintf(buffer, "mp4live version %s %s", MPEG4IP_VERSION, get_linux_video_type()); MP4SetMetadataTool(m_mp4File, buffer); if (m_recordVideo) { m_videoFrameNumber = 1; if (m_stream == NULL) { m_videoTrackId = MP4AddVideoTrack(m_mp4File, m_videoTimeScale, MP4_INVALID_DURATION, m_pConfig->m_videoWidth, m_pConfig->m_videoHeight, MP4_YUV12_VIDEO_TYPE); if (m_videoTrackId == MP4_INVALID_TRACK_ID) { error_message("can't create raw video track"); goto start_failure; } m_videoFrameType = YUVVIDEOFRAME; MP4SetVideoProfileLevel(m_mp4File, 0xFF); } else { bool vIod, vIsma; uint8_t videoProfile; uint8_t *videoConfig; uint32_t videoConfigLen; uint8_t videoType; m_videoFrameType = get_video_mp4_fileinfo(m_video_profile, &vIod, &vIsma, &videoProfile, &videoConfig, &videoConfigLen, &videoType); if (m_videoFrameType == H263VIDEOFRAME) { m_videoTrackId = MP4AddH263VideoTrack(m_mp4File, m_videoTimeScale, 0, m_video_profile->m_videoWidth, m_video_profile->m_videoHeight, 10, 0, 0, 0); uint32_t bitrate = m_video_profile->GetIntegerValue(CFG_VIDEO_BIT_RATE) * 1000; // may need to do this at the end MP4SetH263Bitrates(m_mp4File, m_videoTrackId, bitrate, bitrate); } else if (m_videoFrameType == H264VIDEOFRAME) { uint8_t avcprofile, profile_compat, avclevel; avcprofile = (m_video_profile->m_videoMpeg4ProfileId >> 16) & 0xff; profile_compat = (m_video_profile->m_videoMpeg4ProfileId >> 8) & 0xff; avclevel = (m_video_profile->m_videoMpeg4ProfileId) & 0xff; debug_message("h264 track %x %x %x", avcprofile, profile_compat, avclevel); m_videoTrackId = MP4AddH264VideoTrack(m_mp4File, m_videoTimeScale, MP4_INVALID_DURATION, m_video_profile->m_videoWidth, m_video_profile->m_videoHeight, avcprofile, profile_compat, avclevel, 3); MP4SetVideoProfileLevel(m_mp4File, 0x7f); m_makeIod = false; m_makeIsmaCompliant = false; } else if (m_videoFrameType == H261VIDEOFRAME) { error_message("H.261 recording is not supported"); } else { m_videoTrackId = MP4AddVideoTrack(m_mp4File, m_videoTimeScale, MP4_INVALID_DURATION, m_video_profile->m_videoWidth, m_video_profile->m_videoHeight, videoType); if (vIod == false) m_makeIod = false; if (vIsma == false) m_makeIsmaCompliant = false; if (m_videoTrackId == MP4_INVALID_TRACK_ID) { error_message("can't create encoded video track"); goto start_failure; } MP4SetVideoProfileLevel(m_mp4File, videoProfile); if (videoConfigLen > 0) { MP4SetTrackESConfiguration( m_mp4File, m_videoTrackId, videoConfig, videoConfigLen); } } } } m_audioFrameNumber = 1; m_canRecordVideo = true; if (m_recordAudio) { if (m_stream == NULL) { // raw audio m_canRecordVideo = false; m_audioTrackId = MP4AddAudioTrack(m_mp4File, m_audioTimeScale, 0, MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE); if (m_audioTrackId == MP4_INVALID_TRACK_ID) { error_message("can't create raw audio track"); goto start_failure; } MP4SetAudioProfileLevel(m_mp4File, 0xFF); m_audioFrameType = PCMAUDIOFRAME; } else { u_int8_t audioType; bool createIod = false; bool isma_compliant = false; uint8_t audioProfile; uint8_t *pAudioConfig; uint32_t audioConfigLen; m_canRecordVideo = false; m_audioFrameType = get_audio_mp4_fileinfo(m_audio_profile, &createIod, &isma_compliant, &audioProfile, &pAudioConfig, &audioConfigLen, &audioType); if (m_audioFrameType == AMRNBAUDIOFRAME || m_audioFrameType == AMRWBAUDIOFRAME) { m_audioTrackId = MP4AddAmrAudioTrack(m_mp4File, m_audioFrameType == AMRNBAUDIOFRAME ? 8000 : 16000, 0, 0, 1, m_audioFrameType == AMRWBAUDIOFRAME); } else { if (createIod == false) m_makeIod = false; if (isma_compliant == false) m_makeIsmaCompliant = false; MP4SetAudioProfileLevel(m_mp4File, audioProfile); m_audioTrackId = MP4AddAudioTrack( m_mp4File, m_audioTimeScale, MP4_INVALID_DURATION, audioType); } if (m_audioTrackId == MP4_INVALID_TRACK_ID) { error_message("can't create encoded audio track"); goto start_failure; } if (pAudioConfig) { MP4SetTrackESConfiguration( m_mp4File, m_audioTrackId, pAudioConfig, audioConfigLen); free(pAudioConfig); } } } debug_message("recording text %u", m_recordText); if (m_recordText) { m_textFrameNumber = 1; if (m_stream == NULL) { m_recordText = false; } else { const char *url; m_textFrameType = get_text_mp4_fileinfo(m_text_profile, &url); debug_message("text type %u", m_textFrameType); if (m_textFrameType == HREFTEXTFRAME) { m_textTrackId = MP4AddHrefTrack(m_mp4File, m_textTimeScale, MP4_INVALID_DURATION, url); debug_message("Added text track %u", m_textTrackId); } else { m_recordText = false; } } } m_sink = true; return; start_failure: MP4Close(m_mp4File); m_mp4File = NULL; return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -