#include "gstvideoloader.h" #include #include #include #include #include #include using namespace std; void ofGstUtils::startGstMainLoop(){ static bool initialized = false; if(!initialized){ g_main_loop_new (NULL, FALSE); initialized=true; } } //------------------------------------------------- //----------------------------------------- gstUtils //------------------------------------------------- static bool plugin_registered = false; static bool gst_inited = false; // called when the appsink notifies us that there is a new buffer ready for // processing static GstFlowReturn on_new_buffer_from_source (GstAppSink * elt, void * data){ GstBuffer *buffer = gst_app_sink_pull_buffer (GST_APP_SINK (elt)); return ((ofGstUtils*)data)->buffer_cb(buffer); } static GstFlowReturn on_new_preroll_from_source (GstAppSink * elt, void * data){ GstBuffer *buffer = gst_app_sink_pull_preroll(GST_APP_SINK (elt)); return ((ofGstUtils*)data)->preroll_cb(buffer); } static void on_eos_from_source (GstAppSink * elt, void * data){ ((ofGstUtils*)data)->eos_cb(); } static gboolean appsink_plugin_init (GstPlugin * plugin) { gst_element_register (plugin, "appsink", GST_RANK_NONE, GST_TYPE_APP_SINK); return TRUE; } ofGstUtils::ofGstUtils() { bLoaded = false; speed = 1; bPaused = false; bIsMovieDone = false; bPlaying = false; loopMode = OF_LOOP_NONE; bFrameByFrame = false; gstPipeline = NULL; gstSink = NULL; durationNanos = 0; isAppSink = false; isStream = false; appsink = NULL; if(!g_thread_supported()){ g_thread_init(NULL); } if(!gst_inited){ gst_init (NULL, NULL); gst_inited=true; cerr <<"ofGstUtils: gstreamer inited"<on_preroll(buffer); else return GST_FLOW_OK; } GstFlowReturn ofGstUtils::buffer_cb(GstBuffer * buffer){ bIsMovieDone = false; if(appsink) return appsink->on_buffer(buffer); else return GST_FLOW_OK; } void ofGstUtils::eos_cb(){ bIsMovieDone = true; if(appsink) appsink->on_eos(); } bool ofGstUtils::setPipelineWithSink(string pipeline, string sinkname, bool isStream){ ofGstUtils::startGstMainLoop(); gchar* pipeline_string = g_strdup((pipeline).c_str()); GError * error = NULL; gstPipeline = gst_parse_launch (pipeline_string, &error); cerr << "gstreamer pipeline: " <message)<0){ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1)) { cerr<<"GStreamer: unable to seek\n"; } }else{ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos)) { cerr<<"GStreamer: unable to seek\n"; } } } void ofGstUtils::setVolume(float volume){ gdouble gvolume = volume; g_object_set(G_OBJECT(gstPipeline), "volume", gvolume, (void*)NULL); } void ofGstUtils::setLoopState(ofLoopType state){ loopMode = state; } void ofGstUtils::setSpeed(float _speed){ GstFormat format = GST_FORMAT_TIME; GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_SKIP | GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH); gint64 pos; if(_speed==0){ gst_element_set_state (gstPipeline, GST_STATE_PAUSED); return; } if(!gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos) || pos<0){ //ofLog(OF_LOG_ERROR,"GStreamer: cannot query position"); return; } speed = _speed; //pos = (float)gstData.lastFrame * (float)fps_d / (float)fps_n * GST_SECOND; if(!bPaused) gst_element_set_state (gstPipeline, GST_STATE_PLAYING); if(speed>0){ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1)) { cerr<<"GStreamer: unable to change speed\n"; } }else{ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos)) { cerr<<"GStreamer: unable to change speed\n"; } } cerr<<"Gstreamer: speed change to"<< speed<on_message(msg)) continue; cerr << "GStreamer: Got " << GST_MESSAGE_TYPE_NAME(msg) << " message from " << GST_MESSAGE_SRC_NAME(msg)<message<on_eos(); switch(loopMode){ case OF_LOOP_NORMAL:{ GstFormat format = GST_FORMAT_TIME; GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH |GST_SEEK_FLAG_KEY_UNIT); gint64 pos; gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos); if(!gst_element_seek(GST_ELEMENT(gstPipeline), speed, format, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, durationNanos)) { cerr<<"GStreamer: unable to seek\n"; } }break; case OF_LOOP_PALINDROME:{ GstFormat format = GST_FORMAT_TIME; GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH |GST_SEEK_FLAG_KEY_UNIT); gint64 pos; gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos); float loopSpeed; if(pos>0) loopSpeed=-speed; else loopSpeed=speed; if(!gst_element_seek(GST_ELEMENT(gstPipeline), loopSpeed, GST_FORMAT_UNDEFINED, flags, GST_SEEK_TYPE_NONE, 0, GST_SEEK_TYPE_NONE, 0)) { cerr<<"GStreamer: unable to seek\n"; } }break; default: break; } break; default: cerr << "GStreamer: unhandled message from " << GST_MESSAGE_SRC_NAME(msg)< lock(mutex); pixels.clear(); backPixels.clear(); bIsFrameNew = false; bHavePixelsChanged = false; bBackPixelsChanged = false; if(prevBuffer) gst_buffer_unref (prevBuffer); if(buffer) gst_buffer_unref (buffer); prevBuffer = 0; buffer = 0; } bool ofGstVideoUtils::isFrameNew(){ return bIsFrameNew; } unsigned char * ofGstVideoUtils::getPixels(){ return pixels.getPixels(); } /* ofPixelsRef ofGstVideoUtils::getPixelsRef(){ return pixels; } */ void ofGstVideoUtils::update(){ if (isLoaded()){ if(!isFrameByFrame()){ //mutex.lock(); bHavePixelsChanged = bBackPixelsChanged; if (bHavePixelsChanged){ bBackPixelsChanged=false; pixels.swap(backPixels); if(prevBuffer) gst_buffer_unref (prevBuffer); prevBuffer = buffer; } //mutex.unlock(); }else{ GstBuffer *buffer; //get the buffer from appsink if(isPaused()) buffer = gst_app_sink_pull_preroll (GST_APP_SINK (getSink())); else buffer = gst_app_sink_pull_buffer (GST_APP_SINK (getSink())); if(buffer){ if(pixels.isAllocated()){ if(prevBuffer) gst_buffer_unref (prevBuffer); //memcpy (pixels.getPixels(), GST_BUFFER_DATA (buffer), size); pixels.setFromExternalPixels(GST_BUFFER_DATA (buffer),pixels.getWidth(),pixels.getHeight(),pixels.getNumChannels()); prevBuffer = buffer; bHavePixelsChanged=true; } } } }else{ cerr<<"ofGstVideoUtils not loaded\n"; } bIsFrameNew = bHavePixelsChanged; bHavePixelsChanged = false; } float ofGstVideoUtils::getHeight(){ return pixels.getHeight(); } float ofGstVideoUtils::getWidth(){ return pixels.getWidth(); } bool ofGstVideoUtils::setPipeline(string pipeline, int bpp, bool isStream, int w, int h){ string caps; if(bpp==8) caps="video/x-raw-gray, depth=8, bpp=8"; else if(bpp==32) caps="video/x-raw-rgb, depth=24, bpp=32, endianness=4321, red_mask=0xff0000, green_mask=0x00ff00, blue_mask=0x0000ff, alpha_mask=0x000000ff"; else caps="video/x-raw-rgb, depth=24, bpp=24, endianness=4321, red_mask=0xff0000, green_mask=0x00ff00, blue_mask=0x0000ff, alpha_mask=0x000000ff"; if(w!=-1 && h!=-1){ caps+=", width=" + ofToString(w) + ", height=" + ofToString(h); } gchar* pipeline_string = g_strdup((pipeline + " ! appsink name=ofappsink caps=\"" + caps + "\"").c_str()); // caps=video/x-raw-rgb if((isStream && (w==-1 || h==-1)) || allocate(w,h,bpp)){ return setPipelineWithSink(pipeline_string,"ofappsink",isStream); }else{ return false; } } bool ofGstVideoUtils::allocate(int w, int h, int _bpp){ pixels.allocate(w,h,_bpp/8); backPixels.allocate(w,h,_bpp/8); prevBuffer = 0; pixels.set(0); bHavePixelsChanged = pixels.isAllocated(); return pixels.isAllocated(); } GstFlowReturn ofGstVideoUtils::preroll_cb(GstBuffer * _buffer){ guint size; size = GST_BUFFER_SIZE (_buffer); if(pixels.isAllocated() && pixels.getWidth()*pixels.getHeight()*pixels.getBytesPerPixel()!=(int)size){ cerr << "on_preproll: error preroll buffer size: " << size<< "!= init size: " << (pixels.getWidth()*pixels.getHeight()*pixels.getBytesPerPixel()) << endl; gst_buffer_unref (_buffer); return GST_FLOW_ERROR; } //mutex.lock(); if(bBackPixelsChanged && buffer) gst_buffer_unref (buffer); if(pixels.isAllocated()){ buffer = _buffer; backPixels.setFromExternalPixels(GST_BUFFER_DATA (buffer),pixels.getWidth(),pixels.getHeight(),pixels.getNumChannels()); //eventPixels.setFromExternalPixels(GST_BUFFER_DATA (buffer),pixels.getWidth(),pixels.getHeight(),pixels.getNumChannels()); bBackPixelsChanged=true; //mutex.unlock(); //ofNotifyEvent(prerollEvent,eventPixels); }else{ if(isStream && appsink){ appsink->on_stream_prepared(); }else{ cerr << "received a preroll without allocation\n"; } //mutex.unlock(); } return ofGstUtils::preroll_cb(_buffer); } GstFlowReturn ofGstVideoUtils::buffer_cb(GstBuffer * _buffer){ guint size; size = GST_BUFFER_SIZE (_buffer); if(pixels.isAllocated() && pixels.getWidth()*pixels.getHeight()*pixels.getBytesPerPixel()!=(int)size){ cerr<<"on_preproll: error on new buffer, buffer size: " <on_stream_prepared(); }else{ cerr<<"received a preroll without allocation\n"; } //mutex.unlock(); } return ofGstUtils::buffer_cb(buffer); } void ofGstVideoUtils::eos_cb(){ ofGstUtils::eos_cb(); //ofEventArgs args; //ofNotifyEvent(eosEvent,args); }