如何从 GMemoryOutputStream 访问数据

How to access data from GMemoryOutputStream

我想解码音频文件并将 PCM/int 值存储到数组中。为此,我使用 gstreamer 和 giostreamsink,它为我提供了一个 GMemoryOutputStream。到目前为止一切顺利,但我现在如何访问或循环访问 GMemoryOutputStream?

我为获得 gpointer 做了什么:

gpointer out_data = g_memory_output_stream_get_data(G_MEMORY_OUTPUT_STREAM(stream));

但是我现在可以用那个 gpointer 做什么?如何访问流数据?

我目前拥有的完整代码:

#include <string>
#include <stdio.h>
#include <gst/gst.h>
#include <gio/gio.h>
#include <boost/thread.hpp>

static void on_pad_added(GstElement *decodebin,
                         GstPad *pad,
                         gpointer data) {
    GstElement *convert = (GstElement *) data;

    GstCaps *caps;
    GstStructure *str;
    GstPad *audiopad;

    audiopad = gst_element_get_static_pad(convert, "sink");
    if (GST_PAD_IS_LINKED(audiopad)) {
        g_object_unref(audiopad);
        return;
    }

    caps = gst_pad_get_caps(pad);
    str = gst_caps_get_structure(caps, 0);
    printf("here %s\n",gst_structure_get_name(str));
    if (!g_strrstr(gst_structure_get_name(str), "audio")) {
        gst_caps_unref(caps);
        gst_object_unref(audiopad);
        return;
    }
    gst_caps_unref(caps);
    gst_pad_link(pad, audiopad);
    g_object_unref(audiopad);
}

static gboolean bus_call(GstBus *bus,
                         GstMessage *msg,
                         gpointer data) {
    GMainLoop *loop = (GMainLoop*)data;

    switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_EOS:
            g_print ("End of stream\n");
            g_main_loop_quit(loop);
            break;
        case GST_MESSAGE_ERROR: {
            gchar  *debug;
            GError *error;

            gst_message_parse_error(msg, &error, &debug);
            g_free (debug);

            g_printerr("Error: %s\n", error->message);
            g_error_free(error);

            g_main_loop_quit(loop);
            break;
        }
        default:
            break;
    }
    return true;
}

int main (int argc, char **argv) {
    gst_init(&argc, &argv);

    GstElement *pipeline, *source, *decode, *sink, *convert;
    int rate = 44100;
    int channels = 1;
    int depth = 16;
    bool output_signed = true;
    GMainLoop *loop;
    GstBus *bus;
    guint bus_watch_id;
    GMemoryOutputStream *stream;
    gpointer out_data;

    // loop
    loop = g_main_loop_new(NULL, false);
    // pipeline
    pipeline = gst_pipeline_new("test_pipeline");
    // sink
    stream = G_MEMORY_OUTPUT_STREAM(g_memory_output_stream_new(NULL, 0, (GReallocFunc)g_realloc, (GDestroyNotify)g_free));
    sink = gst_element_factory_make ("giostreamsink", "sink");
    g_object_set(G_OBJECT(sink), "stream", stream, NULL);
    // source
    source = gst_element_factory_make("filesrc", "source");
    g_object_set(G_OBJECT(source), "location", "/home/sam/Desktop/audio/audio.wav", NULL);
    // convert
    convert = gst_element_factory_make("audioconvert", "convert");
    // decode
    decode = gst_element_factory_make("decodebin", "decoder");
    // link decode to convert
    g_signal_connect(decode, "pad-added", G_CALLBACK(on_pad_added), convert);

    // bus
    bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
    bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
    gst_object_unref(bus);

    // add elements into pipeline
    gst_bin_add_many(GST_BIN(pipeline), source, decode, convert, sink, NULL);
    // link source to decode
    gst_element_link(source, decode);
    // caps
    GstCaps *caps;
    caps = gst_caps_new_simple("audio/x-raw-int",
                               "rate", G_TYPE_INT, rate,
                               "channels", G_TYPE_INT, channels,
                               "width", G_TYPE_INT, depth,
                               "depth", G_TYPE_INT, depth,
                               "signed", G_TYPE_BOOLEAN, output_signed,
                               NULL);
    // link convert to sink
    gst_element_link_filtered(convert, sink, caps);
    gst_caps_unref(caps);
    // start playing
    gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);

    // iterate
    g_print("Running...\n");
    g_main_loop_run(loop);

    // out of the main loop, clean up nicely
    g_print("Returned, stopping playback\n");
    gst_element_set_state(pipeline, GST_STATE_NULL);

    g_print("Deleting pipeline\n");
    gst_object_unref(GST_OBJECT(pipeline));
    g_source_remove (bus_watch_id);
    g_main_loop_unref(loop);

    // get data
    g_print("get data\n");
    out_data = g_memory_output_stream_get_data(G_MEMORY_OUTPUT_STREAM(stream));

    unsigned long size = g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(stream));
    unsigned long sizeData = g_memory_output_stream_get_data_size(G_MEMORY_OUTPUT_STREAM(stream));
    std::cout << "stream size: " << size << std::endl;
    std::cout << "stream data size: " << sizeData << std::endl;

    for (int i = 0; i < 5; ++i) {
//        std::cout << out_data[i] << std::endl; // not working
    }
    return 0;
}

我解决了问题,我不得不将 gpointer 转换为 gint16*:

std::vector<int16_t> data;
for (unsigned long i = 0; i < sizeData/2; ++i) {
    data.push_back(((gint16*)out_data)[i]);
}

铸件必须改变,这取决于你使用的深度,例如深度 8 和无符号 guint8* 并更改 for loop 限制。

感兴趣者的完整代码:

#include <string>
#include <stdio.h>
#include <gst/gst.h>
#include <gio/gio.h>
#include <boost/thread.hpp>

static void on_pad_added(GstElement *decodebin,
                         GstPad *pad,
                         gpointer data) {
    GstElement *convert = (GstElement *) data;

    GstCaps *caps;
    GstStructure *str;
    GstPad *audiopad;

    audiopad = gst_element_get_static_pad(convert, "sink");
    if (GST_PAD_IS_LINKED(audiopad)) {
        g_object_unref(audiopad);
        return;
    }

    caps = gst_pad_get_caps(pad);
    str = gst_caps_get_structure(caps, 0);
    printf("here %s\n",gst_structure_get_name(str));
    if (!g_strrstr(gst_structure_get_name(str), "audio")) {
        gst_caps_unref(caps);
        gst_object_unref(audiopad);
        return;
    }
    gst_caps_unref(caps);
    gst_pad_link(pad, audiopad);
    g_object_unref(audiopad);
}

static gboolean bus_call(GstBus *bus,
                         GstMessage *msg,
                         gpointer data) {
    GMainLoop *loop = (GMainLoop*)data;

    switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_EOS:
            g_print ("End of stream\n");
            g_main_loop_quit(loop);
            break;
        case GST_MESSAGE_ERROR: {
            gchar  *debug;
            GError *error;

            gst_message_parse_error(msg, &error, &debug);
            g_free (debug);

            g_printerr("Error: %s\n", error->message);
            g_error_free(error);

            g_main_loop_quit(loop);
            break;
        }
        default:
            break;
    }
    return true;
}

int main (int argc, char **argv) {
    gst_init(&argc, &argv);

    GstElement *pipeline, *source, *decode, *sink, *convert;
    int rate = 44100;
    int channels = 1;
    int depth = 16;
    bool output_signed = true;
    GMainLoop *loop;
    GstBus *bus;
    guint bus_watch_id;
    GMemoryOutputStream *stream;
    gpointer out_data;

    // loop
    loop = g_main_loop_new(NULL, false);
    // pipeline
    pipeline = gst_pipeline_new("test_pipeline");
    // sink
    stream = G_MEMORY_OUTPUT_STREAM(g_memory_output_stream_new(NULL, 0, (GReallocFunc)g_realloc, (GDestroyNotify)g_free));
    sink = gst_element_factory_make ("giostreamsink", "sink");
    g_object_set(G_OBJECT(sink), "stream", stream, NULL);
    // source
    source = gst_element_factory_make("filesrc", "source");
    g_object_set(G_OBJECT(source), "location", "/home/sam/Desktop/audio/audio.wav", NULL);
    // convert
    convert = gst_element_factory_make("audioconvert", "convert");
    // decode
    decode = gst_element_factory_make("decodebin", "decoder");
    // link decode to convert
    g_signal_connect(decode, "pad-added", G_CALLBACK(on_pad_added), convert);

    // bus
    bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
    bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
    gst_object_unref(bus);

    // add elements into pipeline
    gst_bin_add_many(GST_BIN(pipeline), source, decode, convert, sink, NULL);
    // link source to decode
    gst_element_link(source, decode);
    // caps
    GstCaps *caps;
    caps = gst_caps_new_simple("audio/x-raw-int",
                               "rate", G_TYPE_INT, rate,
                               "channels", G_TYPE_INT, channels,
                               "width", G_TYPE_INT, depth,
                               "depth", G_TYPE_INT, depth,
                               "signed", G_TYPE_BOOLEAN, output_signed,
                               NULL);
    // link convert to sink
    gst_element_link_filtered(convert, sink, caps);
    gst_caps_unref(caps);
    // start playing
    gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);

    // iterate
    g_print("Running...\n");
    g_main_loop_run(loop);

    // out of the main loop, clean up nicely
    g_print("Returned, stopping playback\n");
    gst_element_set_state(pipeline, GST_STATE_NULL);

    g_print("Deleting pipeline\n");
    gst_object_unref(GST_OBJECT(pipeline));
    g_source_remove (bus_watch_id);
    g_main_loop_unref(loop);

    // get data
    g_print("get data\n");
    out_data = g_memory_output_stream_get_data(G_MEMORY_OUTPUT_STREAM(stream));

    unsigned long size = g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(stream));
    unsigned long sizeData = g_memory_output_stream_get_data_size(G_MEMORY_OUTPUT_STREAM(stream));
    std::cout << "stream size: " << size << std::endl;
    std::cout << "stream data size: " << sizeData << std::endl;

    // access data and store in vector
    std::vector<int16_t> data;
    for (unsigned long i = 0; i < sizeData/2; ++i) {
        data.push_back(((gint16*)out_data)[i]);
    }
    return 0;
}