⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 file_mp4_recorder.cpp

📁 MPEG-4编解码的实现(包括MPEG4视音频编解码)
💻 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_v4l_source.h"


int CMp4Recorder::ThreadMain(void) 
{
	while (SDL_SemWait(m_myMsgQueueSemaphore) == 0) {
		CMsg* pMsg = m_myMsgQueue.get_message();
		
		if (pMsg != NULL) {
			switch (pMsg->get_value()) {
			case MSG_NODE_STOP_THREAD:
				DoStopRecord();
				delete pMsg;
				return 0;
			case MSG_NODE_START:
				DoStartRecord();
				break;
			case MSG_NODE_STOP:
				DoStopRecord();
				break;
			case MSG_SINK_FRAME:
				size_t dontcare;
				DoWriteFrame((CMediaFrame*)pMsg->get_message(dontcare));
				break;
			}

			delete pMsg;
		}
	}

	return -1;
}

void CMp4Recorder::DoStartRecord()
{
	if (m_sink) {
		return;
	}

	// enable huge file mode in mp4 if estimated size goes over 1 GB
	bool hugeFile = 
		m_pConfig->m_recordEstFileSize > 1000000000;
	u_int32_t verbosity =
		MP4_DETAILS_ERROR /* | MP4_DETAILS_WRITE_ALL */;

	if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_OVERWRITE)) {
		m_mp4File = MP4Create(
			m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME),
			verbosity, hugeFile);
	} else {
		m_mp4File = MP4Modify(
			m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME),
			verbosity);
	}

	if (!m_mp4File) {
		return;
	}

	m_rawVideoTrackId = MP4_INVALID_TRACK_ID;
	m_encodedVideoTrackId = MP4_INVALID_TRACK_ID;
	m_rawAudioTrackId = MP4_INVALID_TRACK_ID;
	m_encodedAudioTrackId = MP4_INVALID_TRACK_ID;

	m_canRecordAudio = true;

	m_rawAudioTimeScale = m_encodedAudioTimeScale = 
		m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE);

	if (m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE)
	  && (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_VIDEO)
	    || m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO))) {
		m_movieTimeScale = m_videoTimeScale;

	} else if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {
		m_movieTimeScale = m_encodedAudioTimeScale;

	} else {
		m_movieTimeScale = m_rawAudioTimeScale;
	}
	MP4SetTimeScale(m_mp4File, m_movieTimeScale);

	if (m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE)) {

		if (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_VIDEO)) {
			m_rawVideoFrameNum = 1;
			m_canRecordAudio = false;

			m_rawVideoTrackId = MP4AddVideoTrack(
				m_mp4File,
				m_videoTimeScale,
				MP4_INVALID_DURATION,
				m_pConfig->m_videoWidth, 
				m_pConfig->m_videoHeight,
				MP4_YUV12_VIDEO_TYPE);

			if (m_rawVideoTrackId == MP4_INVALID_TRACK_ID) {
				error_message("can't create raw video track");
				goto start_failure;
			}

			MP4SetVideoProfileLevel(m_mp4File, 0xFF);
		}

		if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)) {
			m_encodedVideoFrameNum = 1;
			m_canRecordAudio = false;

			m_encodedVideoTrackId = MP4AddVideoTrack(
				m_mp4File,
				m_videoTimeScale,
				MP4_INVALID_DURATION,
				m_pConfig->m_videoWidth, 
				m_pConfig->m_videoHeight,
				MP4_MPEG4_VIDEO_TYPE);

			if (m_encodedVideoTrackId == MP4_INVALID_TRACK_ID) {
				error_message("can't create encoded video track");
				goto start_failure;
			}

			MP4SetVideoProfileLevel(m_mp4File, 
				m_pConfig->GetIntegerValue(CONFIG_VIDEO_PROFILE_ID));

			MP4SetTrackESConfiguration(m_mp4File, m_encodedVideoTrackId,
				m_pConfig->m_videoMpeg4Config, 
				m_pConfig->m_videoMpeg4ConfigLength); 

		}
	}

	if (m_pConfig->GetBoolValue(CONFIG_AUDIO_ENABLE)) {

		if (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_AUDIO)) {
			m_rawAudioFrameNum = 1;
			m_rawAudioDuration = 0;

			m_rawAudioTrackId = MP4AddAudioTrack(
				m_mp4File, 
				m_rawAudioTimeScale, 
				0,
				MP4_PCM16_AUDIO_TYPE);

			if (m_rawAudioTrackId == MP4_INVALID_TRACK_ID) {
				error_message("can't create raw audio track");
				goto start_failure;
			}

			MP4SetAudioProfileLevel(m_mp4File, 0xFF);
		}

		if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {
			m_encodedAudioFrameNum = 1;
			m_encodedAudioDuration = 0;

			u_int8_t audioType;

			if (!strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
			  AUDIO_ENCODING_AAC)) {
				audioType = MP4_MPEG4_AUDIO_TYPE;
				MP4SetAudioProfileLevel(m_mp4File, 0x0F);
			} else {
				audioType = MP4_MP3_AUDIO_TYPE;
				MP4SetAudioProfileLevel(m_mp4File, 0xFE);
			}

			m_encodedAudioTrackId = MP4AddAudioTrack(
				m_mp4File, 
				m_encodedAudioTimeScale, 
				MP4_INVALID_DURATION,
				audioType);

			if (m_encodedAudioTrackId == MP4_INVALID_TRACK_ID) {
				error_message("can't create encoded audio track");
				goto start_failure;
			}

			if (!strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
			  AUDIO_ENCODING_AAC)) {
				u_int8_t* pConfig = NULL;
				u_int32_t configLength = 0;

				MP4AV_AacGetConfiguration(
					&pConfig,
					&configLength,
					MP4AV_AAC_LC_PROFILE,
					m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE),
					m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS));

				MP4SetTrackESConfiguration(
					m_mp4File, 
					m_encodedAudioTrackId,
					pConfig, 
					configLength);
			}
		}
	}

	m_sink = true;
	return;

start_failure:
	MP4Close(m_mp4File);
	m_mp4File = NULL;
	return;
}

void CMp4Recorder::DoWriteFrame(CMediaFrame* pFrame)
{
	if (pFrame == NULL) {
		return;
	}
	if (!m_sink) {
		delete pFrame;
		return;
	}

	if (pFrame->GetType() == CMediaFrame::PcmAudioFrame
	  && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_AUDIO)) {

		if (m_canRecordAudio) {
			if (m_rawAudioFrameNum == 1) {
				m_rawAudioStartTimestamp = pFrame->GetTimestamp();
				m_rawAudioDuration = 0;
			}

			// check for audio continuity
			Duration skew =
				MP4ConvertToTrackDuration(
					m_mp4File, 
					m_rawAudioTrackId, 
					(pFrame->GetTimestamp() - m_rawAudioStartTimestamp)
						- m_rawAudioDuration,
					TimestampTicks);

			if (skew > 128) {
				// record audio gap
				MP4WriteSample(
					m_mp4File,
					m_rawAudioTrackId,
					NULL,
					0,
					skew);
			}

			MP4WriteSample(
				m_mp4File,
				m_rawAudioTrackId,
				(u_int8_t*)pFrame->GetData(), 
				pFrame->GetDataLength(),
				pFrame->GetDataLength() 
					/ (2 * m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS)));

			m_rawAudioFrameNum++;

			m_rawAudioDuration += 
				pFrame->ConvertDuration(TimestampTicks);
		}

	} else if ((pFrame->GetType() == CMediaFrame::Mp3AudioFrame
	    || pFrame->GetType() == CMediaFrame::AacAudioFrame)
	  && m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {

		if (m_canRecordAudio) {
			if (m_encodedAudioFrameNum == 1) {
				m_encodedAudioStartTimestamp = pFrame->GetTimestamp();
				m_encodedAudioDuration = 0;
			}

			// check for audio continuity
			Duration skew =
				MP4ConvertToTrackDuration(
					m_mp4File, 
					m_encodedAudioTrackId, 
					(pFrame->GetTimestamp() - m_encodedAudioStartTimestamp)
						- m_encodedAudioDuration,
					TimestampTicks);

			if (skew > 128) {
				// record audio gap
				MP4WriteSample(
					m_mp4File,
					m_encodedAudioTrackId,
					NULL,
					0,
					skew);
			}

			MP4WriteSample(
				m_mp4File,
				m_encodedAudioTrackId,
				(u_int8_t*)pFrame->GetData(), 
				pFrame->GetDataLength(),
				pFrame->ConvertDuration(m_encodedAudioTimeScale));

			m_encodedAudioFrameNum++;

			m_encodedAudioDuration += 
				pFrame->ConvertDuration(TimestampTicks);
		}

	} else if (pFrame->GetType() == CMediaFrame::YuvVideoFrame
	  && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_VIDEO)) {

		// let audio record if raw is the only video being recorded
		if (!m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)) {
			m_canRecordAudio = true;
		}

		MP4WriteSample(
			m_mp4File,
			m_rawVideoTrackId,
			(u_int8_t*)pFrame->GetData(), 
			pFrame->GetDataLength(),
			pFrame->ConvertDuration(m_videoTimeScale));

		m_rawVideoFrameNum++;

	} else if (pFrame->GetType() == CMediaFrame::Mpeg4VideoFrame
	  && m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)) {

		u_int8_t* pSample = NULL;
		u_int32_t sampleLength = 0;
		Duration sampleDuration = 
			pFrame->ConvertDuration(m_videoTimeScale);
		bool isIFrame = (MP4AV_Mpeg4GetVopType(
			(u_int8_t*)pFrame->GetData(), pFrame->GetDataLength()) == 'I');

		if (m_encodedVideoFrameNum > 1 || isIFrame) {
			pSample = (u_int8_t*)pFrame->GetData();
			sampleLength = pFrame->GetDataLength();

			m_canRecordAudio = true;

		} // else waiting for I frame at start of recording

		if (pSample != NULL) {
			MP4WriteSample(
				m_mp4File,
				m_encodedVideoTrackId,
				pSample, 
				sampleLength,
				sampleDuration,
				0,
				isIFrame);
		
			m_encodedVideoFrameNum++;
		}
	}

	delete pFrame;
}

void CMp4Recorder::DoStopRecord()
{
	if (!m_sink) {
		return;
	}

	bool optimize = false;

	// create hint tracks
	if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_HINT_TRACKS)) {

		if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_OPTIMIZE)) {
			optimize = true;
		}

		if (MP4_IS_VALID_TRACK_ID(m_encodedVideoTrackId)) {
			MP4AV_Rfc3016Hinter(
				m_mp4File, 
				m_encodedVideoTrackId,
				m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));

			// LATER H.26L hinter when we have a real-time H.26L encoder
		}

		if (MP4_IS_VALID_TRACK_ID(m_encodedAudioTrackId)) {
			const char *encoding = 
				m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING);

			if (!strcasecmp(encoding, AUDIO_ENCODING_MP3)) {
				MP4AV_Rfc2250Hinter(
					m_mp4File, 
					m_encodedAudioTrackId, 
					false, 
					m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));

			} else if (!strcasecmp(encoding, AUDIO_ENCODING_AAC)) {
				MP4AV_RfcIsmaHinter(
					m_mp4File, 
					m_encodedAudioTrackId, 
					false, 
					m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE));
			}
		}
	}

	// close the mp4 file
	MP4Close(m_mp4File);
	m_mp4File = NULL;

	// add ISMA style OD and Scene tracks
	if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_VIDEO)
	  || m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)) {

		bool useIsmaTag = false;

		// if AAC track is present, can tag this as ISMA compliant content
	  	if (m_pConfig->GetBoolValue(CONFIG_RECORD_ENCODED_AUDIO)
		  && !strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
		    AUDIO_ENCODING_AAC)) {

			useIsmaTag = true;
		}

		MP4MakeIsmaCompliant(
			m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME),
			0,
			useIsmaTag);
	}

	if (optimize) {
		MP4Optimize(m_pConfig->GetStringValue(CONFIG_RECORD_MP4_FILE_NAME));
	}

	m_sink = false;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -