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

GStreamer创建视频片段报错gst_segment_do_seek断言失败,求正确方案

GStreamer 创建视频片段的正确方法

问题背景

用FFmpeg仅靠-ss和-t参数就能轻松截取视频片段,但GStreamer中gst-launch-1.0没有直接对应参数,编写代码调用gst_element_seek时易触发错误。比如测试场景中,播放模式(启用PLAY宏)可正常播放12-20秒的视频片段,但转码导出模式(默认逻辑)会抛出断言错误:

gst_segment_do_seek: assertion 'segment->format == format' failed

测试环境:Windows + GStreamer 1.24.11(最新稳定版)

错误原因

转码管道中的mp4mux封装器初始化后,会将流的segment格式锁定为字节位置(GST_FORMAT_BYTES),但代码中调用gst_element_seek时使用的是时间格式(GST_FORMAT_TIME),两者格式不匹配触发了断言失败。而播放模式中的视频渲染sink不会修改segment格式,因此能正常工作。

正确实现方式

方式1:用gst-launch-1.0命令行实现(无需编写代码)

gst-launch-1.0支持通过--seek参数直接触发截取逻辑,无需手动写代码:

# 截取12秒至20秒的视频片段,导出为MP4(包含音视频流)
gst-launch-1.0 filesrc location=sintel_trailer-480p.mp4 ! qtdemux name=demux \
demux.video_0 ! h264parse ! nvh264dec ! nvh264enc ! h264parse ! mp4mux.video_0 \
demux.audio_0 ! aacparse ! faac ! aacparse ! mp4mux.audio_0 \
mp4mux ! filesink location=clip.mp4 \
--seek 1.0 -1 0 12000000000 1 0 20000000000

参数说明:

  • --seek <速率> <格式> <标志> <起始类型> <起始时间> <结束类型> <结束时间>
  • 这里format=-1表示自动适配格式,12000000000对应12秒(单位:纳秒),20000000000对应20秒

方式2:修复C代码逻辑

要让转码管道支持时间格式的seek,需先重置mp4mux的状态,避免其锁定字节格式,同时补充音频流的处理(原代码仅处理视频流,会导致封装器工作异常)。修改后的代码如下:

#include <gst/gst.h>

GMainLoop* loop = NULL;
GstElement* pipeline = NULL;
GstElement* mux = NULL;

gboolean gst_bus_handler(GstBus* bus, GstMessage* message, gpointer data)
{
    GstMessageType type = message->type;

    if (type == GST_MESSAGE_EOS) {
        g_main_loop_quit(loop);
    }
    else if (type == GST_MESSAGE_STATE_CHANGED) {
        GstState old_state, new_state;
        gst_message_parse_state_changed(message, &old_state, &new_state, NULL);

        if (GST_MESSAGE_SRC(message) == GST_OBJECT_CAST(pipeline)) {
            if (old_state != GST_STATE_PAUSED && new_state == GST_STATE_PAUSED) {
                // 重置muxer状态,解除字节格式锁定
                gst_element_set_state(mux, GST_STATE_READY);
                gst_element_set_state(mux, GST_STATE_PAUSED);
                
                // 执行时间格式的seek操作
                gst_element_seek(
                    pipeline, 1.0, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
                    GST_SEEK_TYPE_SET, 12 * GST_SECOND,
                    GST_SEEK_TYPE_SET, 20 * GST_SECOND);
            }
        }
    }
    else if (type == GST_MESSAGE_ASYNC_DONE) {
        gst_element_set_state(pipeline, GST_STATE_PLAYING);
    }
    else if (type == GST_MESSAGE_ERROR) {
        GError *err;
        gchar *debug_info;
        gst_message_parse_error(message, &err, &debug_info);
        g_printerr("Error from %s: %s\n", GST_OBJECT_NAME(message->src), err->message);
        g_printerr("Debug info: %s\n", debug_info ? debug_info : "none");
        g_clear_error(&err);
        g_free(debug_info);
        g_main_loop_quit(loop);
    }

    return TRUE;
}

int main(int argc, char* argv[])
{
    gst_init(NULL, NULL);

    // 创建包含音视频处理的完整管道
    pipeline = gst_parse_launch(
        "filesrc location=sintel_trailer-480p.mp4 name=src ! qtdemux name=demux"
        " demux.video_0 ! nvh264dec name=dec ! nvh264enc name=enc ! h264parse name=parse ! mux.video_0"
        " demux.audio_0 ! aacparse ! faac ! aacparse ! mux.audio_0"
        " mp4mux name=mux ! filesink name=sink location=clip.mp4", NULL);
    if (!pipeline) exit(-1);

    // 获取mp4mux元素用于状态重置
    mux = gst_bin_get_by_name(GST_BIN(pipeline), "mux");
    if (!mux) {
        g_printerr("Failed to get mux element\n");
        exit(-1);
    }

    loop = g_main_loop_new(NULL, FALSE);

    GstBus* bus = gst_element_get_bus(pipeline);
    guint watch_id = gst_bus_add_watch(bus, gst_bus_handler, NULL);
    gst_object_unref(bus);

    GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PAUSED);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr("Unable to set pipeline to PAUSED state\n");
    }
    else {
        g_main_loop_run(loop);
    }

    g_printerr("Setting pipeline state to NULL\n");
    gst_element_set_state(pipeline, GST_STATE_NULL);
    g_source_remove(watch_id);
    g_main_loop_unref(loop);
    gst_object_unref(mux);
    gst_object_unref(pipeline);
    gst_deinit();
    return 0;
}

方式3:使用GStreamer编辑服务(GES)

如果需要更复杂的视频剪辑操作,官方推荐使用GES框架,它封装了seek、剪辑、拼接等逻辑,代码更简洁:

#include <ges/ges.h>

int main(int argc, char* argv[]) {
    ges_init();

    // 创建导出项目
    GESProject *project = ges_project_new(GES_URI_FORMAT_FILE, "clip.mp4");
    GESTimeline *timeline = ges_project_get_timeline(project);

    // 添加视频源并设置截取范围(12秒开始,持续8秒)
    GESClip *clip = ges_uri_clip_new("sintel_trailer-480p.mp4");
    ges_clip_set_inpoint(clip, 12 * GST_SECOND);
    ges_clip_set_duration(clip, 8 * GST_SECOND);
    
    // 将片段添加到时间线
    GESLayer *layer = ges_layer_new();
    ges_timeline_add_layer(timeline, layer);
    ges_layer_add_clip(layer, clip);

    // 渲染导出
    ges_project_render(project, NULL);

    // 释放资源
    g_object_unref(project);
    ges_deinit();
    return 0;
}

内容的提问来源于stack exchange,提问作者F32

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 18:14:59