GStreamer —— 2.16、Windows下Qt加载GStreamer库后运行 - “播放教程 2:字幕管理“(附:完整源码)

发布于:2025-03-12 ⋅ 阅读:(18) ⋅ 点赞:(0)
运行效果在这里插入图片描述

介绍

     本教程与上一个教程非常相似,但不是 在不同的音频流之间切换,我们将使用字幕流。 这将使我们能够学习:

          • 如何选择字幕流

          • 如何添加外部字幕

          • 如何自定义用于字幕的字体

     我们已经知道(从前面的教程中)容器文件可以 保存多个音频和视频流,并且我们可以很容易地 通过更改 or 属性在它们中进行选择。切换字幕很容易。值得注意的是,就像音频和视频一样,要注意为字幕选择合适的解码器, 并且 GStreamer 的插件结构允许添加对新 格式与复制文件一样简单。除了容器中嵌入的字幕外,还提供 可以从外部 URI 添加额外的字幕流。本教程将打开一个已包含 5 个字幕流的文件, 并从另一个文件(希腊语)添加另一个 1 个文件。

GStreamer相关运行库
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/gstreamer-1.0/gst
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/gstreamer-1.0
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/glib-2.0
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/lib/glib-2.0/include

LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gstreamer-1.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/glib-2.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gobject-2.0.lib

源码
#include <stdio.h>
#include <gst/gst.h>

typedef struct _CustomData
{
    GstElement *playbin;    /* 唯一的播放元素 */

    gint n_video;          /* 视频路数 */
    gint n_audio;          /* 音频路数 */
    gint n_text;           /* 字幕路数 */

    gint current_video;    /* 当前播放的视频流 */
    gint current_audio;    /* 当前播放的音频流 */
    gint current_text;     /* 当前播放的字母流 */

    GMainLoop *main_loop;  /* GLib主循环 */
} CustomData;

/* playbin的flag */
typedef enum
{
    GST_PLAY_FLAG_VIDEO         = (1 << 0), /* 想要视频输出 */
    GST_PLAY_FLAG_AUDIO         = (1 << 1), /* 想要音频频输出 */
    GST_PLAY_FLAG_TEXT          = (1 << 2)  /* 想要字幕输出 */
} GstPlayFlags;

/* 消息和键盘处理功能的转发定义 */
static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data);
static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data);

int main(int argc, char *argv[])
{
    /* 初始化GStreamer */
    gst_init (&argc, &argv);

    /* 创建元素 */
    CustomData data;
    data.playbin = gst_element_factory_make ("playbin", "playbin");
    if (!data.playbin) { g_printerr ("Not all elements could be created.\n"); return -1; }

    /* 设置播放的uri */
    g_object_set (data.playbin, "uri", "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.ogv", NULL);

    /* 设置要播放的字幕URI和一些字体描述 */
    g_object_set (data.playbin, "suburi", "https://gstreamer.freedesktop.org/data/media/sintel_trailer_gr.srt", NULL);
    g_object_set (data.playbin, "subtitle-font-desc", "Sans, 18", NULL);

    /* 设置标志以显示音频、视频和字幕 */
    gint flags;
    g_object_get (data.playbin, "flags", &flags, NULL);
    flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
    g_object_set (data.playbin, "flags", flags, NULL);

    /* 添加一个bus监视,这样我们就可以在消息到达时收到通知 */
    GstBus *bus = gst_element_get_bus (data.playbin);
    gst_bus_add_watch (bus, (GstBusFunc)handle_message, &data);

    /* 添加键盘监视,以便我们收到按键通知 */
#ifdef G_OS_WIN32
    GIOChannel *io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
#else
    GIOChannel *io_stdin = g_io_channel_unix_new (fileno (stdin));
#endif
    g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);

    /* 开始播放 */
    GstStateChangeReturn ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE)
    {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (data.playbin);
        return -1;
    }

    /* 创建glib主循环 */
    data.main_loop = g_main_loop_new (NULL, FALSE);
    g_main_loop_run (data.main_loop);

    /* 是否资源 */
    g_main_loop_unref (data.main_loop);
    g_io_channel_unref (io_stdin);
    gst_object_unref (bus);
    gst_element_set_state (data.playbin, GST_STATE_NULL);
    gst_object_unref (data.playbin);
    return 0;
}

/* 从流中提取一些元数据并将其打印在屏幕上 */
static void analyze_streams (CustomData *data)
{
    GstTagList *tags;
    gchar *str;
    guint rate;

    /* 读取属性 */
    g_object_get (data->playbin, "n-video", &data->n_video, NULL);
    g_object_get (data->playbin, "n-audio", &data->n_audio, NULL);
    g_object_get (data->playbin, "n-text", &data->n_text, NULL);

    g_print ("%d video stream(s), %d audio stream(s), %d text stream(s)\n", data->n_video, data->n_audio, data->n_text);
    g_print ("\n");
    for (gint i = 0; i < data->n_video; i++)
    {
        tags = NULL;
        /* 检索流的视频标签 */
        g_signal_emit_by_name (data->playbin, "get-video-tags", i, &tags);
        if (tags)
        {
            g_print ("video stream %d:\n", i);
            gst_tag_list_get_string (tags, GST_TAG_VIDEO_CODEC, &str);
            g_print ("  codec: %s\n", str ? str : "unknown");
            g_free (str);
            gst_tag_list_free (tags);
        }
    }

    g_print ("\n");
    for (gint i = 0; i < data->n_audio; i++)
    {
        tags = NULL;
        /* 检索流的音频标签 */
        g_signal_emit_by_name (data->playbin, "get-audio-tags", i, &tags);
        if (tags) {
            g_print ("audio stream %d:\n", i);
            if (gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &str))
            {
                g_print ("  codec: %s\n", str);
                g_free (str);
            }
            if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str))
            {
                g_print ("  language: %s\n", str);
                g_free (str);
            }
            if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &rate))
            {
                g_print ("  bitrate: %d\n", rate);
            }
            gst_tag_list_free (tags);
        }
    }

    g_print ("\n");
    for (gint i = 0; i < data->n_text; i++)
    {
        tags = NULL;
        /* 检索流的字幕流标签 */
        g_print ("subtitle stream %d:\n", i);
        g_signal_emit_by_name (data->playbin, "get-text-tags", i, &tags);
        if (tags)
        {
            if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str))
            {
                g_print ("  language: %s\n", str);
                g_free (str);
            }
            gst_tag_list_free (tags);
        }
        else
        {
            g_print ("  no tags found\n");
        }
    }

    g_object_get (data->playbin, "current-video", &data->current_video, NULL);
    g_object_get (data->playbin, "current-audio", &data->current_audio, NULL);
    g_object_get (data->playbin, "current-text", &data->current_text, NULL);

    g_print ("\n");
    g_print ("Currently playing video stream %d, audio stream %d and subtitle stream %d\n", data->current_video, data->current_audio, data->current_text);
    g_print ("Type any number and hit ENTER to select a different subtitle stream\n");
}

/* 处理GStreamer消息 */
static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data)
{
    GError *err;
    gchar *debug_info;

    switch (GST_MESSAGE_TYPE (msg))
    {
    case GST_MESSAGE_ERROR:
        gst_message_parse_error (msg, &err, &debug_info);
        g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
        g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
        g_clear_error (&err);
        g_free (debug_info);
        g_main_loop_quit (data->main_loop);
        break;
    case GST_MESSAGE_EOS:
        g_print ("End-Of-Stream reached.\n");
        g_main_loop_quit (data->main_loop);
        break;
    case GST_MESSAGE_STATE_CHANGED:
    {
        GstState old_state, new_state, pending_state;
        gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
        if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin))
        {
            if (new_state == GST_STATE_PLAYING)
            {
                /* 分析流 */
                analyze_streams (data);
            }
        }
    } break;
    }

    /* 持续接收消息 */
    return TRUE;
}

/* 处理键盘消息 */
static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data)
{
    gchar *str = NULL;

    if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) == G_IO_STATUS_NORMAL)
    {
        int index = atoi (str);
        if (index < 0 || index >= data->n_text)
        {
            g_printerr ("Index out of bounds\n");
        }
        else
        {
            /* 如果输入是有效的字幕流索引,请设置当前字幕流 */
            g_print ("Setting current subtitle stream to %d\n", index);
            g_object_set (data->playbin, "current-text", index, NULL);
        }
    }
    g_free (str);
    return TRUE;
}

关注

笔者 - jxd