最近公司codec要用到gstreamer编解码,所以编写了一个gstremer的案例:
这是一个gstreamer有关appsrc和appsink解码的案例。
1、当 push_data(CustomData *data) 返回false后,就再也不会调用它了,why?
2、gstomx hacks配置中,如果没有 “no-disable-outport”,那么默认是关闭的。

#include <gst/app/gstappsrc.h>
#include <stdio.h>

#include <iostream>

#include "gst/gst.h"
#include "gst/video/video.h"

typedef struct _CustomData {
  GstElement *pipeline;
  GstElement *source;
  GstElement *h264parse;
  GstElement *omx264dec;
  GstElement *convert;
  GstElement *sink;
  GMainLoop *main_loop;
  guint sourceid; /* To control the GSource */
  FILE *pFile;
  guint size;
  FILE *outFile;
} CustomData;

#define W 176
#define H 144

#define MAKE_CAPS(alignment)             \
  "video/x-h264, "                       \
  "alignment=(string) " alignment        \
  ", "                                   \
  "stream-format=(string) byte-stream, " \
  "width=(int) 176"                      \
  ", "                                   \
  "height=(int) 144"

/* The Zynq supports decoding subframes, though we want "au" to be the
 * default, so we keep it prepended. This is the only way that it works with
 * rtph264depay. */

#define SRC_CAPS MAKE_CAPS("au")

#define SINK_CAPS \
  "video/x-raw,format=(string) I420,width=(int) 176, height=(int) 144, pixel-aspect-ratio=1/1"

#define CHUNK_SIZE 2048

/* This method is called by the idle GSource in the mainloop, to feed CHUNK_SIZE bytes into appsrc.
 * The idle handler is added to the mainloop when appsrc requests us to start sending data
 * (need-data signal) and is removed when appsrc has enough data (enough-data signal).
 */
static gboolean push_data(CustomData *data) {
  GstBuffer *buffer;
  GstFlowReturn ret;
  GstMapInfo map;
  guint8 *raw;

  if (data->size == 0) {
    /* We got eof, send eos */
    buffer = gst_buffer_new_and_alloc(0);
    gst_buffer_map(buffer, &map, GST_MAP_WRITE);
    map.data = NULL;
    map.size = 0;
    g_signal_emit_by_name(data->source, "push-buffer", buffer, &ret);
    if (ret != GST_FLOW_OK) {
      /* We got some error, stop sending data */
      return FALSE;
    }
    gst_buffer_unref(buffer);

    g_print("last NUll data\n");
    ret = gst_app_src_end_of_stream(GST_APP_SRC(data->source));
    g_print("eos returned %d at %d\n", ret, __LINE__);
    return FALSE;
  }

  /* Create a new empty buffer */
  buffer = gst_buffer_new_and_alloc(data->size);

  gst_buffer_map(buffer, &map, GST_MAP_WRITE);
  raw = (guint8 *)map.data;
  gint lSize = 0;
  data->size > CHUNK_SIZE ? lSize = CHUNK_SIZE : lSize = data->size;
  gint result = fread(raw, 1, data->size, data->pFile);
  data->size -= result;
  g_print("read size=%d\n", result);
  gst_buffer_unmap(buffer, &map);

  /* Push the buffer into the appsrc */
  g_signal_emit_by_name(data->source, "push-buffer", buffer, &ret);
  if (ret != GST_FLOW_OK) {
    /* We got some error, stop sending data */
    return FALSE;
  }
  /* Free the buffer now that we are done with it */
  gst_buffer_unref(buffer);

  return TRUE;
}

/* This signal callback triggers when appsrc needs data. Here, we add an idle handler
 * to the mainloop to start pushing data into the appsrc */
static void start_feed(GstElement *source, guint size, CustomData *data) {
  if (data->sourceid == 0) {
    g_print("Start feeding\n");
    data->sourceid = g_idle_add((GSourceFunc)push_data, data);
  }
}

/* This callback triggers when appsrc has enough data and we can stop sending.
 * We remove the idle handler from the mainloop */
static void stop_feed(GstElement *source, CustomData *data) {
  if (data->sourceid != 0) {
    g_print("Stop feeding\n");
    g_source_remove(data->sourceid);
    data->sourceid = 0;
  }
}

static void eos(GstElement *source, CustomData *data) { g_print("this is appsrc eos\n"); }

/* The appsink has received a buffer */
static GstFlowReturn new_sample(GstElement *sink, CustomData *data) {
  GstSample *sample;

  /* Retrieve the buffer */
  g_signal_emit_by_name(sink, "pull-sample", &sample);
  if (sample) {
    /* The only thing we do in this example is print a * to indicate a received buffer */

    GstBuffer *buf = gst_sample_get_buffer(sample);
    GstMapInfo map;
    guint8 *raw;
    gst_buffer_map(buf, &map, GST_MAP_READ);
    // g_print("buf size=%d\n", map.size);
    raw = (guint8 *)map.data;
    fwrite(raw, 1, map.size, data->outFile);
    fflush(data->outFile);
    gst_sample_unref(sample);
    return GST_FLOW_OK;
  }

  return GST_FLOW_ERROR;
}

/* This function is called when an error message is posted on the bus */
static void error_cb(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);
      break;
    case GST_MESSAGE_EOS:
      g_print("End-Of-Stream reached.\n");
      break;
    default:
      /* We should not reach here because we only asked for ERRORs and EOS
       */
      g_printerr("Unexpected message received.\n");
      break;
  }
  // gst_message_unref(msg);

  g_main_loop_quit(data->main_loop);
}

int main(int argc, char *argv[]) {
  CustomData data;
  memset(&data, 0, sizeof(data));

  GstBus *bus;
  GstStateChangeReturn ret;
  char const *infile = "./305.264";
  data.pFile = fopen(infile, "rb+");
  if (data.pFile == NULL) {
    perror("Error opening file");
    return 1;
  }

  fseek(data.pFile, 0, SEEK_END);
  data.size = ftell(data.pFile);
  rewind(data.pFile);
  g_print("file [%s]size:%d\n", infile, data.size);

  data.outFile = fopen("./176-144.yuv", "wb+");
  if (data.outFile == NULL) {
    perror("Error opening file");
    return 1;
  }

  /* Initialize GStreamer */
  gst_init(&argc, &argv);

  /* Create the elements */
  data.source = gst_element_factory_make("appsrc", "app-source");
  data.h264parse = gst_element_factory_make("h264parse", "h264-parse");
  data.omx264dec = gst_element_factory_make("omxh264dec", "decodecr-omx64");
  data.convert = gst_element_factory_make("videoconvert", "color-convert");
  data.sink = gst_element_factory_make("appsink", "app-sink");

  /* Create the empty pipeline */
  data.pipeline = gst_pipeline_new("test-pipeline");

  if (!data.pipeline || !data.source || !data.sink || !data.h264parse || !data.omx264dec ||
      !data.convert) {
    g_printerr("Not all elements could be created.\n");
    return -1;
  }

  /* Configure appsrc */
  GstCaps *src_caps = NULL;
  src_caps = gst_caps_from_string(SRC_CAPS);
  g_object_set(data.source, "caps", src_caps, NULL);
  g_object_set(data.source, "stream-type", GST_APP_STREAM_TYPE_STREAM, NULL);
  g_signal_connect(data.source, "need-data", G_CALLBACK(start_feed), &data);
  g_signal_connect(data.source, "enough-data", G_CALLBACK(stop_feed), &data);
  g_signal_connect(data.source, "end-of-stream", G_CALLBACK(eos), &data);
  gst_caps_unref(src_caps);

  /* Configure appsink */
  GstCaps *sink_caps = NULL;
  sink_caps = gst_caps_from_string(SINK_CAPS);
  g_object_set(data.sink, "emit-signals", TRUE, "caps", sink_caps, NULL);
  g_signal_connect(data.sink, "new-sample", G_CALLBACK(new_sample), &data);
  gst_caps_unref(sink_caps);

  /* Build the pipeline */
  gst_bin_add_many(GST_BIN(data.pipeline), data.source, data.h264parse, data.omx264dec,
                   data.convert, data.sink, nullptr);

  if (gst_element_link_many(data.source, data.h264parse, data.omx264dec, data.convert, data.sink,
                            nullptr) != TRUE) {
    g_printerr("Elements could not be linked.\n");
    gst_object_unref(data.pipeline);
    return -1;
  }

  bus = gst_element_get_bus(data.pipeline);
  gst_bus_add_signal_watch(bus);
  g_signal_connect(G_OBJECT(bus), "message::error", (GCallback)error_cb, &data);
  g_signal_connect(G_OBJECT(bus), "message::eos", (GCallback)error_cb, &data);

  /* Start playing */
  ret = gst_element_set_state(data.pipeline, 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.pipeline);
    return -1;
  }
  /* Create a GLib Main Loop and set it to run */
  data.main_loop = g_main_loop_new(NULL, FALSE);
  g_main_loop_run(data.main_loop);

  /* Free resources */
  gst_element_set_state(data.pipeline, GST_STATE_NULL);
  gst_object_unref(bus);
  gst_object_unref(data.pipeline);
  fclose(data.pFile);
  fclose(data.outFile);
  return 0;
}


下面是cmake文件:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 3.5)

# 项目信息
project (Gstreamer-Demo1)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -fpermissive")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -fpermissive")

set(root_path /workspace/depends)


#dependencies
set(ENV{PKG_CONFIG_PATH} "${root_path}/libgstreamer/lib/x86_64-linux-gnu/pkgconfig")

find_package(PkgConfig)

pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
message(STATUS "=== GSTREAMER_LIBRARIES: ${GSTREAMER_LIBRARIES}")
message(STATUS "=== GSTREAMER_INCLUDE_DIRS: ${GSTREAMER_INCLUDE_DIRS}")
message(STATUS "=== GSTREAMER_LIBRARY_DIRS: ${GSTREAMER_LIBRARY_DIRS}")



include_directories(${GSTREAMER_INCLUDE_DIRS} )
link_directories(${GSTREAMER_LIBRARY_DIRS})

set(SOURCES main.cpp)

# 指定生成目标
add_executable(Demo ${SOURCES})
target_link_libraries(Demo  ${GSTREAMER_LIBRARIES} gstvideo-1.0 gstapp-1.0)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐