GStreamer 1.40断开音频后EOS时MP4未写入头文件求助
GStreamer 1.40 mp4mux无音频时无法写入文件头的修复方案
因遗留系统限制,必须使用GStreamer 1.40版本,该版本已在代码库中大量使用无法更换。尝试通过断开音频源或使用gst_bin_remove_many移除整个音频管线的方式切断音频,但从appsrc发送EOS后,断开音频时MP4文件的头文件无法写入,仅在音频连接时能正常写入。
问题场景伪代码
//INIT FUNC... GstElementFactory *appsrcfactory = gst_element_factory_find ( "appsrc" ); GstElement *appSrc = gst_element_factory_create ( appsrcfactory, "Video App Src" ); //stored in a global manager GstElement *appAudioSrc = gst_element_factory_create ( appsrcfactory, "Audio App Src" );//stored in a global manager //other elements created here ... GstElementFactory *muxfactory = gst_element_factory_find ( "mp4mux" ); GstElement *mux = gst_element_factory_create ( muxfactory, "mux" ); //stored in a global manager GstPad *videopad = gst_element_get_request_pad( mux, "video_%u" ); //stored in a global manager GstPad *audiopad = gst_element_get_request_pad( mux, "audio_%u" ); //stored in a global manager //pipeline bin add and linking and probe setup done here GstPad *audioOutPad = gst_element_get_static_pad ( audioOutputElement, "src" ); GstPad *videoOutPad = gst_element_get_static_pad ( videoOutputElement, "src" ); if ( gst_pad_link ( videoOutPad, videopad ) != GST_PAD_LINK_OK || gst_pad_link ( audioOutPad, audiopad ) != GST_PAD_LINK_OK ) { gst_object_unref ( bus ); gst_object_unref ( pipeline ); pipeline = nullptr; bus = nullptr; return; } gst_object_unref ( audioOutPad ); gst_object_unref ( videoOutPad ); //START RECORDING FUNC... if( AudioPresent && audiopad == nullptr ) { audiopad = gst_element_get_request_pad( mux, "audio_%u" ); GstPad *audioOutPad = gst_element_get_static_pad ( audioOutputElement, "src" ); if ( gst_pad_link ( audioOutPad, audiopad ) != GST_PAD_LINK_OK ) { gst_object_unref ( bus ); gst_object_unref ( pipeline ); pipeline = nullptr; bus = nullptr; return; } gst_object_unref ( audioOutPad ); } else if( !AudioPresent && audiopad != nullptr) { GstPad *audioOutPad = gst_pad_get_peer( audiopad ); if ( gst_pad_unlink ( audioOutPad, audiopad ) != TRUE ) { gst_object_unref ( bus ); gst_object_unref ( pipeline ); pipeline = nullptr; bus = nullptr; return; } gst_object_unref ( audioOutPad ); gst_element_release_request_pad ( mux, audiopad ); gst_object_unref ( audiopad ); audiopad = nullptr; } gst_element_set_state ( m_pGSTRecorder, GST_STATE_PLAYING ); //Data is pushed to the appsrc's from our render pipeline //STOP RECORDING FUNC... gst_app_src_end_of_stream ( GST_APP_SRC ( appSrc ) ); if ( AudioPresent ) { gst_app_src_end_of_stream ( GST_APP_SRC( appAudioSrc ) ); } gst_bus_timed_pop_filtered( bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS); gst_element_set_state ( m_pGSTRecorder, GST_STATE_NULL );
核心原因
GStreamer 1.40版本的mp4mux在音频pad被提前释放/断开的情况下,无法正确触发文件头的写入逻辑。MP4容器的元数据(包括头信息)需要所有流的关键初始化信息收集完成后才会写入,若音频流被中途移除,mp4mux会认为流的状态未完成,导致最终文件缺少必要的头信息。
修复方案
方案1:调整EOS发送与pad释放顺序
不要在停止录制前提前断开音频pad,而是确保所有流的EOS都被mp4mux接收处理后,再释放音频pad。修改停止录制逻辑如下:
//STOP RECORDING FUNC... // 给所有流发送EOS,无论是否有音频,确保mp4mux收到完整的结束信号 gst_app_src_end_of_stream ( GST_APP_SRC ( appSrc ) ); gst_app_src_end_of_stream ( GST_APP_SRC( appAudioSrc ) ); // 等待EOS消息确认,确保mp4mux处理完所有流的结束逻辑 gst_bus_timed_pop_filtered( bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS); // 此时再处理音频pad的释放 if (audiopad != nullptr) { GstPad *audioOutPad = gst_pad_get_peer( audiopad ); if (audioOutPad) { gst_pad_unlink ( audioOutPad, audiopad ); gst_object_unref ( audioOutPad ); } gst_element_release_request_pad ( mux, audiopad ); gst_object_unref ( audiopad ); audiopad = nullptr; } gst_element_set_state ( m_pGSTRecorder, GST_STATE_NULL );
方案2:强制mp4mux写入元数据
在发送EOS并收到确认后,给mp4mux发送FLUSH_STOP事件,强制其将缓冲的元数据写入文件:
// 在收到EOS消息后添加 GstEvent *flush_stop = gst_event_new_flush_stop(FALSE); gst_element_send_event(mux, flush_stop);
该方法在GStreamer 1.40中对mp4mux有效,可确保所有未写入的元数据被持久化到文件。
方案3:保留音频pad仅断开链接
若不需要音频时,不释放音频pad,仅断开与音频输出元素的链接。这样mp4mux会认为音频流存在但无数据,不会影响头文件的正常写入:
//START RECORDING FUNC... if( AudioPresent && audiopad != nullptr ) { GstPad *audioOutPad = gst_element_get_static_pad ( audioOutputElement, "src" ); if ( gst_pad_link ( audioOutPad, audiopad ) != GST_PAD_LINK_OK ) { // 错误处理逻辑 gst_object_unref ( audioOutPad ); return; } gst_object_unref ( audioOutPad ); } else if( !AudioPresent && audiopad != nullptr) { GstPad *audioOutPad = gst_pad_get_peer( audiopad ); if (audioOutPad) { gst_pad_unlink ( audioOutPad, audiopad ); gst_object_unref ( audioOutPad ); } // 不释放audiopad,仅保持断开状态 }
内容的提问来源于stack exchange,提问作者yosmo78
相关产品推荐
相关产品推荐

