You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:20:43