2012-05-01 123 views
9

Tôi đang cố gắng thực hiện những việc sau: nhận luồng video bằng cách sử dụng gstreamer và xử lý bằng opencv. Tôi đã tìm thấy vài giải pháp, và một trong số đó là viết video vào (từ gstreamer) fifo và sau đó đọc nó bằng cách sử dụng opencv. (OPTION3 tại đây MJPEG streaming and decoding). Vấn đề là tôi không thể mở ống. cvCreateFileCapture không bao giờ trả về. Đây là một phần mã tôi đã viết:Thêm quá trình xử lý opencv vào ứng dụng gstreamer

if(mkfifo("fifo.avi", S_IRUSR| S_IWUSR) == -1) 
{ 
    cout<<"Cant create fifo"<<endl; 
    cout<<errno<<endl; 
} 

loop = g_main_loop_new(NULL, false); 

fsink = gst_element_factory_make("filesink", "fsink"); 
g_object_set(G_OBJECT(fsink), "location", "fifo.avi", NULL); 

playbin = gst_element_factory_make("playbin2", "play");  
g_object_set(G_OBJECT(playbin), "uri", uri.c_str(), NULL); 
g_object_set(G_OBJECT(playbin), "video-sink", fsink, NULL); 

bus = gst_pipeline_get_bus(GST_PIPELINE(playbin)); 
gst_bus_add_signal_watch(bus); 

g_signal_connect(bus, "message::buffering", G_CALLBACK(&set_playbin_state), playbin); 
gst_object_unref(bus); 

cvNamedWindow("output", CV_WINDOW_AUTOSIZE); 
capture = cvCreateFileCapture("fifo.avi"); 

Ngăn xếp chương trình ở dòng cuối cùng. PS: Tôi đang sử dụng opencv 2.3.1.

Trả lời

11

So. Sau khi tìm kiếm một lúc, tôi đã tìm thấy một giải pháp, trong đó bao gồm việc lấy dữ liệu từ bộ đệm. Vì vậy, ý tưởng là tạo playbin và đặt appink là "video-sink". Đây là mẫu mã:

cout<<"Creating appsink"<<endl; 
appsink = gst_element_factory_make("appsink", "asink"); 
gst_app_sink_set_emit_signals((GstAppSink*)appsink, true); 
gst_app_sink_set_drop((GstAppSink*)appsink, true); 
gst_app_sink_set_max_buffers((GstAppSink*)appsink, 1); 

//creating and initialising pipeline 

g_object_set(G_OBJECT(playbin), "video-sink", appsink, NULL); 

g_signal_connect(appsink, "new-buffer", G_CALLBACK(DisplayFrame), (gpointer) mark); 

//callback function looks like this 

gboolean Core::DisplayFrame(GstAppSink *fks, gpointer mark) 
{ 
static bool init = false; 
static IplImage *frame; 
GstBuffer* buf; 
Mark* mk = (Mark*) mark; 

if(!init) 
{ 
    init = true; 
    frame = cvCreateImage(cvSize(mk->frame_w, mk->frame_h), IPL_DEPTH_8U, 1); 
} 
buf = gst_app_sink_pull_buffer(fks); 
frame->imageData = (char*)GST_BUFFER_DATA(buf); 

ProcessFrame(frame); 
gst_buffer_unref(buf); 
return true; 
} 

công trình này. PS. Có rất nhiều thông tin về phương pháp này, nhưng tôi đã dành rất nhiều thời gian tìm kiếm nó. Vì vậy, tôi quyết định đăng nó ở đây để cung cấp ít nhất một số từ khóa để tìm kiếm.

CẬP NHẬT. Và thêm một chút thông tin về kết nối gstreamer và opencv. Đó là về chuyển đổi bộ đệm để iplimage bây giờ. Trước hết, chúng ta cần nhận bộ đệm rgb, để thực hiện chuyển đổi dễ dàng nhất có thể. Để thực hiện việc này, chúng tôi sẽ thay thế appsinks bằng appsink, được kết nối với ffmpegcolorspace

cout<<"Creating appsink"<<endl; 
appsink = gst_element_factory_make("appsink", "asink"); 
gst_app_sink_set_emit_signals((GstAppSink*)appsink, true); 
gst_app_sink_set_drop((GstAppSink*)appsink, true); 
gst_app_sink_set_max_buffers((GstAppSink*)appsink, 1); 
csp = gst_element_factory_make("ffmpegcolorspace", "csp"); 
sinkpipe = gst_pipeline_new("sinkp"); 
gst_bin_add_many(GST_BIN(sinkpipe), csp, appsink, NULL); 
gst_element_link_filtered(csp, appsink, gst_caps_new_simple("video/x-raw-rgb", NULL)); 
pad = gst_element_get_static_pad(csp, "sink"); 
gst_element_add_pad(sinkpipe, gst_ghost_pad_new("ghost", pad)); 
g_object_unref(pad); 

//... 

g_object_set(G_OBJECT(playbin), "video-sink", sinkpipe, NULL); 

//... 

g_signal_connect(appsink, "new-buffer", G_CALLBACK(GetFrame), (gpointer) mark); 

//... 

//caps_struct can be retrieved via writing data probe 
//search for it in streamer manual 

cout<<"Getting frame resolution"<<endl; 
gst_structure_get_int(caps_struct, "width", &(mark->frame_w)); 
gst_structure_get_int(caps_struct, "height", &(mark->frame_h)); 
gst_structure_get_int(caps_struct, "depth", &depth); 

mark->GeneratePoints(); 
frame = cvCreateImage(cvSize(mark->frame_w, mark->frame_h), depth/3, 3); 


//callback function 

gboolean Core::GetFrame(GstAppSink *fks, gpointer frame) 
{ 

IplImage* frame_temp = frame 
IplImage* frame_temp_two = cvCloneImage(frame_temp); 

GstBuffer* buf; 
buf = gst_app_sink_pull_buffer(fks); 
frame_temp_two->imageData = (char*) GST_BUFFER_DATA(buf); 
cvConvertImage(frame_temp_two, frame_temp, CV_CVTIMG_SWAP_RB); 
ProcessFrame(frame_temp); 
gst_buffer_unref(buf); 
return true; 
} 

Tôi hy vọng điều này sẽ giúp ai đó.

7

Đây là giải pháp mã nguồn hoàn chỉnh của tôi cho Gstreamer 1.4.0 và OpenCV 2.4.9.

Sử dụng gst_parse_launch() để phân tích cú pháp dòng lệnh thông thường mà bạn sẽ cung cấp cho gst-launch. Đường dẫn Gstreamer chuyển đổi khung thành định dạng RGB888 trước khi cho chúng vào OpenCV để chuyển đổi dễ dàng nhất có thể.

xử lý khung OpenCV không được thực hiện trong new_sample() gọi lại nhưng nó chỉ lấy khung từ GStreamer và đẩy nó phải xếp hàng sau đó sẽ được tiêu thụ trong chủ đề chính. Bằng cách này, chúng tôi gọi mat, ví dụ: imshow() từ OpenCV để thực sự hiển thị hình ảnh lên màn hình.

~ 150 dòng ... bằng cách xóa các bản in gỡ lỗi, v.v. có thể được giảm xuống < 100 dòng mã.

Có lẽ ta nên thêm có đồng bộ hóa thread xung quanh deque đọc/viết

#include <gst/gst.h> 
#include <gst/app/gstappsink.h> 
#include <stdlib.h> 

#include "opencv2/opencv.hpp" 
using namespace cv; 

// TODO: use synchronized deque 
std::deque<Mat> frameQueue; 

GstFlowReturn 
new_preroll(GstAppSink *appsink, gpointer data) { 
    g_print ("Got preroll!\n"); 
    return GST_FLOW_OK; 
} 

GstFlowReturn 
new_sample(GstAppSink *appsink, gpointer data) { 
    static int framecount = 0; 
    framecount++; 

    GstSample *sample = gst_app_sink_pull_sample(appsink); 
    GstCaps *caps = gst_sample_get_caps(sample); 
    GstBuffer *buffer = gst_sample_get_buffer(sample); 
    const GstStructure *info = gst_sample_get_info(sample); 

    // ---- Read frame and convert to opencv format --------------- 

    GstMapInfo map; 
    gst_buffer_map (buffer, &map, GST_MAP_READ); 

    // convert gstreamer data to OpenCV Mat, you could actually 
    // resolve height/width from caps... 
    Mat frame(Size(320, 240), CV_8UC3, (char*)map.data, Mat::AUTO_STEP); 
    int frameSize = map.size; 

    // TODO: synchronize this.... 
    frameQueue.push_back(frame); 

    gst_buffer_unmap(buffer, &map); 

    // ------------------------------------------------------------ 

    // print dot every 30 frames 
    if (framecount%30 == 0) { 
    g_print ("."); 
    } 

    // show caps on first frame 
    if (framecount == 1) { 
    g_print ("%s\n", gst_caps_to_string(caps)); 
    } 

    gst_sample_unref (sample); 
    return GST_FLOW_OK; 
} 

static gboolean 
my_bus_callback (GstBus *bus, GstMessage *message, gpointer data) { 
    g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message)); 
    switch (GST_MESSAGE_TYPE (message)) { 
    case GST_MESSAGE_ERROR: { 
     GError *err; 
     gchar *debug; 

     gst_message_parse_error (message, &err, &debug); 
     g_print ("Error: %s\n", err->message); 
     g_error_free (err); 
     g_free (debug);  
     break; 
    } 
    case GST_MESSAGE_EOS: 
     /* end-of-stream */ 
     break; 
    default: 
     /* unhandled message */ 
     break; 
    } 
    /* we want to be notified again the next time there is a message 
    * on the bus, so returning TRUE (FALSE means we want to stop watching 
    * for messages on the bus and our callback should not be called again) 
    */ 
    return TRUE; 
} 

int 
main (int argc, char *argv[]) 
{ 
    GError *error = NULL; 

    gst_init (&argc, &argv); 

    gchar *descr = g_strdup(
    "videotestsrc pattern=ball ! " 
    "video/x-raw,format=RGB ! " 
    "videoconvert ! " 
    "appsink name=sink sync=true" 
); 
    GstElement *pipeline = gst_parse_launch (descr, &error); 

    if (error != NULL) { 
    g_print ("could not construct pipeline: %s\n", error->message); 
    g_error_free (error); 
    exit (-1); 
    } 

    /* get sink */ 
    GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink"); 

    gst_app_sink_set_emit_signals((GstAppSink*)sink, true); 
    gst_app_sink_set_drop((GstAppSink*)sink, true); 
    gst_app_sink_set_max_buffers((GstAppSink*)sink, 1); 
    GstAppSinkCallbacks callbacks = { NULL, new_preroll, new_sample }; 
    gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, NULL, NULL); 

    GstBus *bus; 
    guint bus_watch_id; 
    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); 
    bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL); 
    gst_object_unref (bus); 

    gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); 

    namedWindow("edges",1); 
    while(1) { 
    g_main_iteration(false); 

     // TODO: synchronize... 
    if (frameQueue.size() > 0) { 
     // this lags pretty badly even when grabbing frames from webcam 
     Mat frame = frameQueue.front(); 
     Mat edges; 
     cvtColor(frame, edges, CV_RGB2GRAY); 
     GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5); 
     Canny(edges, edges, 0, 30, 3); 
     imshow("edges", edges); 
     cv::waitKey(30); 
     frameQueue.clear(); 
    } 
    } 

    gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); 
    gst_object_unref (GST_OBJECT (pipeline)); 

    return 0; 
} 

Để biên dịch trên OSX/Linux làm cho tập tin như thế này nên làm:

GST_FLAGS=$(shell pkg-config --cflags --libs gstreamer-gl-1.0 gstreamer-tag-1.0 gstreamer-net-1.0 gstreamer-sdp-1.0 \ 
    gstreamer-1.0 gstreamer-allocators-1.0 gstreamer-insertbin-1.0 gstreamer-plugins-base-1.0 \ 
    gstreamer-codecparsers-1.0 gstreamer-base-1.0 gstreamer-app-1.0 gstreamer-check-1.0 \ 
    gstreamer-controller-1.0 gstreamer-video-1.0 gstreamer-fft-1.0 gstreamer-mpegts-1.0 \ 
    gstreamer-pbutils-1.0 gstreamer-rtp-1.0 gstreamer-rtsp-1.0 \ 
    gstreamer-riff-1.0 gstreamer-audio-1.0 gstreamer-plugins-bad-1.0 opencv) 

OPENCV_FLAGS=$(shell pkg-config --cflags --libs opencv) 

all: gst_opencv 

gst_opencv: gst_opencv 
    g++ $(GST_FLAGS) $(OPENCV_FLAGS) gst_opencv -o gst_opencv 

clean: 
    rm -f gst_opencv