📄 file_mp4_recorder.cpp
字号:
if (m_prevTextFrame->RemoveReference()) { delete m_prevTextFrame; } } m_prevTextFrame = pFrame; if (m_canRecordVideo == false) return; if (m_recordAudio) { // we can record. Set the timestamp of this frame to the audio start timestamp m_prevTextFrame->SetTimestamp(m_audioStartTimestamp); m_textStartTimestamp = m_audioStartTimestamp; } else { // need to work with video here, as well... m_textStartTimestamp = m_prevTextFrame->GetTimestamp(); } m_textFrameNumber++; m_textDurationTimescale = 0; return; } Duration textDurationInTicks; textDurationInTicks = pFrame->GetTimestamp() - m_textStartTimestamp; Duration textDurationInTimescaleTotal; textDurationInTimescaleTotal = GetTimescaleFromTicks(textDurationInTicks, m_textTimeScale); Duration textDurationInTimescaleFrame; textDurationInTimescaleFrame = textDurationInTimescaleTotal - m_textDurationTimescale; m_textDurationTimescale += textDurationInTimescaleFrame; MP4WriteSample(m_mp4File, m_textTrackId, (uint8_t *)m_prevTextFrame->GetData(), m_prevTextFrame->GetDataLength(), textDurationInTimescaleFrame); debug_message("wrote text frame %u", m_textFrameNumber); m_textFrameNumber++; if (m_prevTextFrame->RemoveReference()) { delete m_prevTextFrame; } m_prevTextFrame = pFrame;}void CMp4Recorder::DoWriteFrame(CMediaFrame* pFrame){ // dispose of degenerate cases if (pFrame == NULL) return; if (!m_sink) { if (pFrame->RemoveReference()) { delete pFrame; } return; } // RAW AUDIO if (m_recordAudio) { if ((m_stream == NULL && pFrame->GetType() == PCMAUDIOFRAME) || (m_stream != NULL && pFrame->GetType() == NETPCMAUDIOFRAME)) { if (m_audioFrameNumber == 1) { debug_message("First raw audio frame at "U64, pFrame->GetTimestamp()); m_audioStartTimestamp = pFrame->GetTimestamp(); m_canRecordVideo = true; m_prevAudioFrame = pFrame; m_audioFrameNumber++; m_audioSamples = 0; return; // wait until the next audio frame }#if 0 Duration audioFrameSamples = m_prevAudioFrame->GetDatalength() / (m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS) * sizeof(int16_t)); m_audioSamples += ======#endif Duration audioDurationInTicks = pFrame->GetTimestamp() - m_prevAudioFrame->GetTimestamp(); MP4Duration audioDurationInSamples = MP4ConvertToTrackDuration(m_mp4File, m_audioTrackId, audioDurationInTicks, TimestampTicks);#if 0 debug_message("prev "U64" this "U64" diff samples"U64, m_prevAudioFrame->GetTimestamp(), pFrame->GetTimestamp(), audioDurationInSamples);#endif m_prevAudioFrame->SetDuration(audioDurationInSamples); void *pcm;#ifdef WORDS_BIGENDIAN pcm = m_prevAudioFrame->GetData();#else if (pFrame->GetType() != NETPCMAUDIOFRAME) { uint32_t convert_size = m_prevAudioFrame->GetDataLength(); uint16_t *pdata = (uint16_t *)m_prevAudioFrame->GetData(); if (m_convert_pcm_size < convert_size) { m_convert_pcm = (uint16_t *)realloc(m_convert_pcm, convert_size); m_convert_pcm_size = convert_size; } convert_size /= sizeof(uint16_t); for (uint32_t ix = 0; ix < convert_size; ix++) { uint16_t swap = *pdata++; m_convert_pcm[ix] = B2N_16(swap); } pcm = m_convert_pcm; } else { pcm = m_prevAudioFrame->GetData(); }#endif MP4WriteSample( m_mp4File, m_audioTrackId, (u_int8_t*)pcm, m_prevAudioFrame->GetDataLength(), audioDurationInSamples); // m_prevAudioFrame->ConvertDuration(m_audioTimeScale)); m_audioFrameNumber++; if (m_prevAudioFrame->RemoveReference()) { delete m_prevAudioFrame; } m_prevAudioFrame = pFrame; return; // ENCODED AUDIO } else if (pFrame->GetType() == m_audioFrameType) { ProcessEncodedAudioFrame(pFrame); return; // RAW VIDEO } } if (m_recordVideo) { if (m_stream == NULL && pFrame->GetType() == YUVVIDEOFRAME) { // we drop raw video frames until we get the first raw audio frame // after that: // if we are also recording encoded video, we wait until the first I frame // else // we wait until the next raw video frame // in both cases, the duration of the first raw video frame is stretched // to the start of the first raw audio frame if (m_videoFrameNumber == 1) { // wait until the first raw audio frame is received if (!m_canRecordVideo) { if (pFrame->RemoveReference()) delete pFrame; return; } // make sure this frame was captured after the first audio frame if (m_recordAudio && pFrame->GetTimestamp() < m_audioStartTimestamp) { if (pFrame->RemoveReference()) delete pFrame; return; } debug_message("First raw video frame at "U64, pFrame->GetTimestamp()); m_videoStartTimestamp = pFrame->GetTimestamp(); m_prevVideoFrame = pFrame; m_videoFrameNumber++; return; // wait until the next video frame } Duration videoDurationInTicks; // the first raw video frame is stretched to the begining // of the first raw audio frame if (m_videoFrameNumber == 2 && m_recordAudio) { videoDurationInTicks = pFrame->GetTimestamp() - m_audioStartTimestamp; } else { videoDurationInTicks = pFrame->GetTimestamp() - m_prevVideoFrame->GetTimestamp(); } m_prevVideoFrame->SetDuration(videoDurationInTicks); yuv_media_frame_t *pYUV = (yuv_media_frame_t *)m_prevVideoFrame->GetData(); if (pYUV->y + m_pConfig->m_ySize == pYUV->u) { MP4WriteSample( m_mp4File, m_videoTrackId, pYUV->y, m_pConfig->m_yuvSize, m_prevVideoFrame->ConvertDuration(m_videoTimeScale)); } else { if (m_rawYUV == NULL) { debug_message("Mallocing %u", m_pConfig->m_yuvSize); m_rawYUV = (uint8_t *)malloc(m_pConfig->m_yuvSize); } CopyYuv(pYUV->y, pYUV->u, pYUV->v, pYUV->y_stride, pYUV->uv_stride, pYUV->uv_stride, m_rawYUV, m_rawYUV + m_pConfig->m_ySize, m_rawYUV + m_pConfig->m_ySize + m_pConfig->m_uvSize, m_pConfig->m_videoWidth, m_pConfig->m_videoWidth / 2, m_pConfig->m_videoWidth / 2, m_pConfig->m_videoWidth, m_pConfig->m_videoHeight); MP4WriteSample(m_mp4File, m_videoTrackId, m_rawYUV, m_pConfig->m_yuvSize, m_prevVideoFrame->ConvertDuration(m_videoTimeScale)); } m_videoFrameNumber++; if (m_prevVideoFrame->RemoveReference()) { delete m_prevVideoFrame; } m_prevVideoFrame = pFrame; return; // ENCODED VIDEO } else if (pFrame->GetType() == m_videoFrameType) { ProcessEncodedVideoFrame(pFrame); return; } } if (pFrame->GetType() == m_textFrameType && m_recordText) { ProcessEncodedTextFrame(pFrame); } else { // degenerate case if (pFrame->RemoveReference()) delete pFrame; }}void CMp4Recorder::DoStopRecord(){ if (!m_sink) return; Duration totalAudioDuration = 0; // write last audio frame if (m_prevAudioFrame) { MP4WriteSample( m_mp4File, m_audioTrackId, (u_int8_t*)m_prevAudioFrame->GetData(), m_prevAudioFrame->GetDataLength(), m_prevAudioFrame->ConvertDuration(m_audioTimeScale)); m_audioSamples += m_prevAudioFrame->GetDuration(); totalAudioDuration = m_audioSamples; totalAudioDuration *= TimestampTicks; totalAudioDuration /= m_audioTimeScale; if (m_prevAudioFrame->RemoveReference()) { delete m_prevAudioFrame; } if (m_audioFrameType == AMRNBAUDIOFRAME || m_audioFrameType == AMRWBAUDIOFRAME) { MP4SetAmrModeSet(m_mp4File, m_audioTrackId, m_amrMode); } } // write last video frame if (m_prevVideoFrame) { if (m_stream == NULL) {#if 1 error_message("write last raw video frame");#else MP4WriteSample( m_mp4File, m_videoTrackId, (u_int8_t*)m_prevVideoFrame->GetData(), m_prevVideoFrame->GetDataLength(), m_prevVideoFrame->ConvertDuration(m_videoTimeScale));#endif if (m_prevVideoFrame->RemoveReference()) { delete m_prevVideoFrame; } CHECK_AND_FREE(m_rawYUV); } else { bool isIFrame; Duration rend_offset = 0; if (m_prevVideoFrame->GetType() == H264VIDEOFRAME) { WriteH264Frame(m_prevVideoFrame, m_prevVideoFrame->ConvertDuration(m_videoTimeScale)); } else { if (m_prevVideoFrame->GetType() == MPEG4VIDEOFRAME || m_prevVideoFrame->GetType() == H263VIDEOFRAME) { isIFrame = (MP4AV_Mpeg4GetVopType( (u_int8_t*) m_prevVideoFrame->GetData(), m_prevVideoFrame->GetDataLength()) == VOP_TYPE_I); } else { int ret, ftype; ret = MP4AV_Mpeg3FindPictHdr((u_int8_t *)m_prevVideoFrame->GetData(), m_prevVideoFrame->GetDataLength(), &ftype); isIFrame = false; if (ret >= 0) { if (ftype == 1) isIFrame = true; if (ftype != 3) { rend_offset = m_prevVideoFrame->GetPtsTimestamp() - m_prevVideoFrame->GetTimestamp(); rend_offset = GetTimescaleFromTicks(rend_offset, m_movieTimeScale); } } } MP4WriteSample( m_mp4File, m_videoTrackId, (u_int8_t*) m_prevVideoFrame->GetData(), m_prevVideoFrame->GetDataLength(), m_prevVideoFrame->ConvertDuration(m_videoTimeScale), rend_offset, isIFrame); } if (m_prevVideoFrame->RemoveReference()) { delete m_prevVideoFrame; } } } if (m_prevTextFrame) { Duration lastDuration = TimestampTicks; if (totalAudioDuration != 0) { Duration totalSoFar = m_prevTextFrame->GetTimestamp() - m_textStartTimestamp; if (totalAudioDuration > totalSoFar) { lastDuration = totalAudioDuration - totalSoFar; } } debug_message("last duration "U64" timescale "U64, lastDuration, GetTimescaleFromTicks(lastDuration, m_textTimeScale)); MP4WriteSample(m_mp4File, m_textTrackId, (uint8_t *)m_prevTextFrame->GetData(), m_prevTextFrame->GetDataLength(), GetTimescaleFromTicks(lastDuration, m_textTimeScale)); if (m_prevTextFrame->RemoveReference()) { delete m_prevTextFrame; } m_prevTextFrame = NULL; } CHECK_AND_FREE(m_videoH264Seq); m_videoH264SeqSize = 0; CHECK_AND_FREE(m_videoH264Pic); m_videoH264PicSize = 0; // close the mp4 file MP4Close(m_mp4File); m_mp4File = NULL; debug_message("done with writing last frame"); bool optimize = false; // create hint tracks if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_HINT_TRACKS)) { m_mp4File = MP4Modify(m_mp4FileName, MP4_DETAILS_ERROR); if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_OPTIMIZE)) { optimize = true; } if (m_stream != NULL) { if (MP4_IS_VALID_TRACK_ID(m_videoTrackId) && m_stream) { create_mp4_video_hint_track(m_video_profile, m_mp4File, m_videoTrackId, m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)); } if (MP4_IS_VALID_TRACK_ID(m_audioTrackId)) { create_mp4_audio_hint_track(m_audio_profile, m_mp4File, m_audioTrackId, m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)); } if (MP4_IS_VALID_TRACK_ID(m_textTrackId)) { create_mp4_text_hint_track(m_text_profile, m_mp4File, m_textTrackId, m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)); } } else { if (MP4_IS_VALID_TRACK_ID(m_audioTrackId)) { L16Hinter(m_mp4File, m_audioTrackId, m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)); } } MP4Close(m_mp4File); m_mp4File = NULL; } debug_message("done with hint"); // add ISMA style OD and Scene tracks if (m_stream != NULL) { if (m_pConfig->GetBoolValue(CONFIG_RECORD_MP4_ISMA_COMPLIANT)) { bool useIsmaTag = false; // if AAC track is present, can tag this as ISMA compliant content useIsmaTag = m_makeIsmaCompliant; if (m_makeIod) { MP4MakeIsmaCompliant( m_mp4FileName, 0, useIsmaTag); } } } if (optimize) { MP4Optimize(m_mp4FileName); } m_sink = false;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -