0

我想通过appsrc将CV::mat保存为MP4文件或通过RTMP转发。我实现了一个简单的代码,可以成功运行,但是得到的MP4文件不能播放。有人可以告诉我问题是什么吗?我想可能是PTS设置错误,但我不知道如何解决。提前谢谢你。

#include <gst/gst.h>
#include <glib.h>
#include <gst/app/gstappsrc.h>
#include <opencv2/opencv.hpp>
#include <unistd.h>

typedef struct {
    GstPipeline *pipeline = nullptr;
    GstAppSrc  *app_src = nullptr;
    GstElement *video_convert = nullptr;
    GstElement *encoder = nullptr;
    GstElement *h264_parser = nullptr;
    GstElement *qt_mux = nullptr;
    GstElement *file_sink = nullptr;
}CustomData;



int main(int argc,char * argv[]){
    CustomData data;
    GstBus *bus = nullptr;
    GstMessage *msg = nullptr;
    GstStateChangeReturn ret;
    gboolean terminate = false;
    GstClockTime timestamp = 0;
    gst_init(&argc, &argv); 

    data.pipeline = (GstPipeline*)gst_pipeline_new("m_pipeline");
    data.app_src = (GstAppSrc*)gst_element_factory_make("appsrc","m_app_src");
    data.video_convert = gst_element_factory_make("videoconvert","m_video_convert");
    data.encoder = gst_element_factory_make("x264enc","m_x264enc");
    data.h264_parser = gst_element_factory_make("h264parse","m_h264_parser");
    data.qt_mux = gst_element_factory_make("qtmux","qt_mux");
    data.file_sink = gst_element_factory_make("filesink","file_sink");


    if (!data.app_src || !data.video_convert || !data.encoder || !data.h264_parser || !data.qt_mux || !data.file_sink || !data.pipeline){
        g_printerr("failed to create all elements\n");
        return -1;
    }

    gst_bin_add_many(GST_BIN(data.pipeline), (GstElement*)data.app_src, data.video_convert, data.encoder, data.h264_parser, data.qt_mux, data.file_sink, NULL);

    g_assert(gst_element_link_many((GstElement*)data.app_src, data.video_convert, data.encoder, data.h264_parser, data.qt_mux, data.file_sink,NULL));

    GstCaps *caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR",
                                        "width",G_TYPE_INT,1280,
                                        "height",G_TYPE_INT,720,
                                        "framerate",GST_TYPE_FRACTION,25,1,
                                        NULL);

    gst_app_src_set_caps(GST_APP_SRC(data.app_src), caps);
    g_object_set(data.app_src,"is_live",true,NULL);
    g_object_set(data.app_src,"format",GST_FORMAT_TIME,NULL);

    std::string mp4_url = "des.mp4";

    g_object_set(data.file_sink,"location",mp4_url.c_str(),NULL);

    ret = gst_element_set_state((GstElement*)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;
    }

    cv::VideoCapture cap;
    cap.open("src.mp4");
    
    if(!cap.isOpened())
        return -2;
    cv::Mat frame;
    while(true){
        cap.read(frame);
        if(frame.empty()){
            break;
        }
        GstBuffer *buffer;
        buffer = gst_buffer_new_wrapped(frame.data, frame.size().width * frame.size().height * frame.channels());

        GST_BUFFER_PTS (buffer) = timestamp;
        GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_int (1, GST_SECOND, 25);
        timestamp += GST_BUFFER_DURATION (buffer);
        GstFlowReturn ret;

        g_signal_emit_by_name(data.app_src, "push-buffer", buffer, &ret);
        usleep(1000000/25);
    }

    gst_element_set_state((GstElement*)data.pipeline, GST_STATE_NULL);
    gst_object_unref(data.pipeline);
    return 0;
}


4

1 回答 1

1

您不能简单地将管道状态设置为NULL. 相反,您需要将 EOS 事件发送到管道并等待 EOS 信号在管道总线上报告回。如果您不这样做,则 MP4 文件将不会写入某些要求标头,并且文件无法播放。

有关如何检查总线上的 EOS 消息,请参阅https://gstreamer.freedesktop.org/documentation/application-development/basics/helloworld.html?gi-language=c 。

检查https://gstreamer.freedesktop.org/documentation/applib/gstappsrc.html#gst_app_src_end_of_stream如何告诉您appsrc最后一个数据缓冲区已被推送。

于 2021-03-08T10:58:25.223 回答