2017-12-07 29 views
0

다음 작업 파이프 라인이 있습니다. 명령 줄 도구 gst-launch-1.0과 기능 gst_parse_launch()을 사용하여 테스트되었으며 두 경우 모두에서 작동합니다.C 코드에서 GStreamer tee를 구현하는 방법

videotestsrc  ! video/x-raw,width=640,height=480 ! videocrop left=80 right=80 ! tee name=t ! queue ! glupload ! glimagesink t. ! queue ! jpegenc ! avimux ! filesink location=output.avi 

나는 코드를 수동으로 설정하려고했지만, 지금은 다음과 같은 오류에 붙어있어 (응용 프로그램이 열립니다 있지만 비디오가 표시되지 않습니다) :

오류로부터받은 요소 videotestsrc0 : 내부 데이터 흐름 오류입니다. gstbasesrc.c (2948) : gst_base_src_loop() : /GstPipeline : pipeline0/GstVideoTestSrc가 : videotestsrc0 : 난 작업 이 일시 정지 스트리밍, 이유는 협상하지 (-4)

디버깅 정보

Qt 애플리케이션에서 GStreamer를 사용하고 glimagesink는 비디오를 QML 유형에 연결합니다. GStreamer와 관련된 모든 코드는 GStreamer라는 GStreamer 클래스에 있습니다. 문제가 어딘가에있을 경우에 대비하여 전체 cpp 파일이 아래에 게시됩니다. 관련이없는 코드에 대해 사과드립니다.

static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data); 

GStreamer::GStreamer(QQuickItem *parent) : QQuickItem(parent) 
{ 
    qDebug() << "Constructed GSteamer";   
}  

void GStreamer::createPipeline() 
{ 
    qDebug() << "Creating pipeline";  

    if(m_source.isEmpty()){ 
     qDebug() << "Error: Missing source property for GStreamer component"; 
     return; 
    } 

    if(m_videoItem.isEmpty()){ 
     qDebug() << "Error: Missing videoItem property for GStreamer component"; 
     return; 
    } 

    m_pipeline = gst_pipeline_new(NULL); 
    m_sink = NULL; 

    QByteArray ba = m_source.toLatin1(); 
    m_src = gst_element_factory_make(ba.data(), NULL); 
    g_assert(m_src); 


    m_filter = gst_element_factory_make("capsfilter", "filter"); 
    g_assert(m_filter); 

    g_object_set(G_OBJECT (m_filter), "caps", gst_caps_new_simple("video/x-raw", 
     "width", G_TYPE_INT, 640, 
     "height", G_TYPE_INT, 480, 
     NULL), 
    NULL); 

    m_convert = gst_element_factory_make("videoconvert", NULL); 
    g_assert(m_convert); 

    m_crop = gst_element_factory_make("videocrop", "crop"); 
    g_assert(m_crop); 

    g_object_set(G_OBJECT (m_crop), "left", 80, "right", 80, NULL); 

    // Tee 
    m_tee = gst_element_factory_make("tee", "videotee"); 
    g_assert(m_tee); 

    // Display queue 
    m_displayQueue = gst_element_factory_make("queue", "displayQueue"); 
    g_assert(m_displayQueue);  

    m_upload = gst_element_factory_make("glupload", NULL); 
    g_assert(m_upload);  

    m_sink = gst_element_factory_make("qmlglsink", NULL); 
    g_assert(m_sink); 

    // Record queue 
    m_recordQueue = gst_element_factory_make("queue", "recordQueue"); 
    g_assert(m_recordQueue); 

    m_encode = gst_element_factory_make("jpegenc", NULL); 
    g_assert(m_encode); 

    m_mux = gst_element_factory_make("avimux", NULL); 
    g_assert(m_mux); 

    m_filesink = gst_element_factory_make("filesink", NULL); 
    g_assert(m_filesink); 

    g_object_set(G_OBJECT(m_filesink), "location", "output.avi", NULL);  

    gst_bin_add_many(GST_BIN (m_pipeline), m_src, m_filter, m_convert, m_crop, m_upload, m_sink, NULL); 
    gst_bin_add_many(GST_BIN(m_pipeline), m_tee, m_displayQueue, m_recordQueue, m_encode, m_mux, m_filesink, NULL); 

    // If I only link this simple pipeline, it works fine 
    /* 
    if(!gst_element_link_many(m_src, m_filter, m_convert, m_crop, m_upload, m_sink, NULL)){ 
     qDebug() << "Unable to link source"; 
    } 
    */ 

    if(!gst_element_link_many(m_src, m_filter, m_convert, m_crop, m_tee, NULL)){ 
     qDebug() << "Unable to link source"; 
    } 
    if(!gst_element_link_many(m_displayQueue, m_upload, m_sink, NULL)){ 
     qDebug() << "Unable to link display queue"; 
    } 
    if(!gst_element_link_many(m_recordQueue, m_encode, m_mux, m_filesink, NULL)){ 
     qDebug() << "Unable to link record queue"; 
    }  

    GstPad *teeDisplayPad = gst_element_get_request_pad(m_tee, "src_%u"); 
    GstPad *queueDisplayPad = gst_element_get_static_pad(m_displayQueue, "sink"); 

    GstPad *teeRecordPad = gst_element_get_request_pad(m_tee, "src_%u"); 
    GstPad *queueRecordPad = gst_element_get_static_pad(m_recordQueue, "sink"); 

    if(gst_pad_link(teeDisplayPad, queueDisplayPad) != GST_PAD_LINK_OK){ 
     qDebug() << "Unable to link display tee"; 
    } 

    if(gst_pad_link(teeRecordPad, queueRecordPad) != GST_PAD_LINK_OK){ 
     qDebug() << "Unable to link record tee"; 
    } 

    //gst_object_unref(teeDisplayPad); 
    gst_object_unref(queueDisplayPad); 
    //gst_object_unref(teeRecordPad); 
    gst_object_unref(queueRecordPad);  

    QQuickItem *videoItem = window()->findChild<QQuickItem *> (m_videoItem); 
    g_object_set(m_sink, "widget", videoItem, NULL); 

    // This will call gst_element_set_state(m_pipeline, GST_STATE_PLAYING) when the window is ready 
    window()->scheduleRenderJob (new SetPlaying (m_pipeline), QQuickWindow::BeforeSynchronizingStage);  

    m_bus = gst_element_get_bus(m_pipeline); 

    gst_bus_add_watch(m_bus, busCallback, m_loop); 
    gst_object_unref(m_bus); 

    m_loop = g_main_loop_new(NULL, false); 
    g_main_loop_run(m_loop);  
} 

static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data){ 
    qDebug() << "Callback function reached"; 
    switch(GST_MESSAGE_TYPE(message)){ 
     case GST_MESSAGE_ERROR: 
      GError *error; 
      gchar *debugInfo; 
      gst_message_parse_error(message, &error, &debugInfo); 
      qDebug() << "Error received from element" << GST_OBJECT_NAME(message->src) << ":" << error->message; 
      qDebug() << "Debugging information:" << (debugInfo ? debugInfo : "none"); 
      //g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (m_message->src), error->message); 
      //g_printerr ("Debugging information: %s\n", debugInfo ? debugInfo : "none"); 
      g_clear_error (&error); 
      g_free (debugInfo); 

      g_main_loop_quit(static_cast<GMainLoop *>(data)); 
      break; 
     case GST_MESSAGE_EOS: 
      qDebug() << "End-Of-Stream reached."; 
      g_main_loop_quit(static_cast<GMainLoop *>(data)); 
      break; 
     default: 
      qDebug() << "Unexpected message received."; 
      break; 
     } 
    return true;  
} 

/** 
The rest of the code is probably not relevant. It contains 
only destructor and some getters and setters. 
**/ 

GStreamer::~GStreamer() 
{ 
    gst_object_unref(m_bus); 
    gst_element_set_state(m_pipeline, GST_STATE_NULL); 
    gst_object_unref(m_pipeline); 
} 

QString GStreamer::source() const 
{ 
    return m_source; 
} 

void GStreamer::setSource(const QString &source) 
{ 
    if(source != m_source){ 
     m_source = source; 
    } 
} 

QString GStreamer::videoItem() const 
{ 
    return m_videoItem; 
} 

void GStreamer::setVideoItem(const QString &videoItem) 
{ 
    if(videoItem != m_videoItem){ 
     m_videoItem = videoItem; 
    } 
} 

모든 멤버 변수는 .h 파일에 정의되어 있습니다.

티 요소를 bin에 추가하지 않고 파이프 라인에 연결하면 예상대로 비디오가 화면에 나타납니다. 그래서 나는 티 요소의 패드를 엉망으로 만들고 있다고 생각합니다.

나는 GStreamers 문서의 자습서를 따라 왔기 때문에 왜 작동하지 않는지 이해할 수 없습니다.

누군가가 도움을 줄 수 있기를 바랍니다.

답변

1

좋아요, 제공되는 gst-launch 줄과 응용 프로그램 코드의 차이점은 glimagesink 대신 qmlglsink 요소를 사용한다는 것입니다.

문제는 qmlglsink는 RGBA 포맷 비디오 그러나 버퍼 RGBA 포맷 비디오 버퍼를 허용하지 않는 tee의 다른 지점에서 jpegenc을 허용한다는 것입니다. 이 경우 tee의 두 가지 지점에서 지원하는 공통 형식이 없으므로 협상 문제가 발생합니다.

수정은 tee 두 가지 같은 비디오 포맷을 협상 할 수 있도록 qmlglsink 전에 jpegencglcolorconvert 요소 또는 전 videoconvert 요소를 추가하는 것이다.

사이드 노트 : glimagesink은 내부적으로 glupload ! glcolorconvert ! actual-sink을 포함하므로 이미 비디오 포맷을 변환 중입니다.

+0

감사합니다 !!!! 나는 이것을 며칠 동안 고심하고 있었고, 이것으로 해결되었습니다. – KMK