#include "gstvideoloader.h" 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); } ofGstVideoPlayer::ofGstVideoPlayer(){ nFrames = 0; //internalPixelFormat = OF_PIXELS_RGB; bIsStream = false; bIsAllocated = false; threadAppSink = false; videoUtils.setSinkListener(this); } ofGstVideoPlayer::~ofGstVideoPlayer(){ close(); } bool ofGstVideoPlayer::loadMovie(string name){ close(); /* if( name.find( "file://",0 ) != string::npos){ bIsStream = false; }else if( name.find( "://",0 ) == string::npos){ name = "file://"+ofToDataPath(name,true); bIsStream = false; }else{ bIsStream = true; } */ cerr<<"loading "< 0.5f) framePosInInt = framePosInInt + 1; //frame = (int)ceil((getTotalNumFrames() * getPosition())); frame = framePosInInt; return frame; } int ofGstVideoPlayer::getTotalNumFrames(){ return nFrames; } void ofGstVideoPlayer::firstFrame(){ setFrame(0); } void ofGstVideoPlayer::nextFrame(){ gint64 currentFrame = getCurrentFrame(); if(currentFrame!=-1) setFrame(currentFrame + 1); } void ofGstVideoPlayer::previousFrame(){ gint64 currentFrame = getCurrentFrame(); if(currentFrame!=-1) setFrame(currentFrame - 1); } void ofGstVideoPlayer::setFrame(int frame){ // frame 0 = first frame... float pct = (float)frame / (float)nFrames; setPosition(pct); } bool ofGstVideoPlayer::isStream(){ return bIsStream; } void ofGstVideoPlayer::update(){ videoUtils.update(); } void ofGstVideoPlayer::play(){ videoUtils.play(); } void ofGstVideoPlayer::stop(){ videoUtils.stop(); } void ofGstVideoPlayer::setPaused(bool bPause){ videoUtils.setPaused(bPause); } bool ofGstVideoPlayer::isPaused(){ return videoUtils.isPaused(); } bool ofGstVideoPlayer::isLoaded(){ return videoUtils.isLoaded(); } bool ofGstVideoPlayer::isPlaying(){ return videoUtils.isPlaying(); } float ofGstVideoPlayer::getPosition(){ return videoUtils.getPosition(); } float ofGstVideoPlayer::getSpeed(){ return videoUtils.getSpeed(); } float ofGstVideoPlayer::getDuration(){ return videoUtils.getDuration(); } bool ofGstVideoPlayer::getIsMovieDone(){ return videoUtils.getIsMovieDone(); } void ofGstVideoPlayer::setPosition(float pct){ videoUtils.setPosition(pct); } void ofGstVideoPlayer::setVolume(float volume){ videoUtils.setVolume(volume); } void ofGstVideoPlayer::setLoopState(ofLoopType state){ videoUtils.setLoopState(state); } ofLoopType ofGstVideoPlayer::getLoopState(){ return videoUtils.getLoopState(); } void ofGstVideoPlayer::setSpeed(float speed){ videoUtils.setSpeed(speed); } void ofGstVideoPlayer::close(){ bIsAllocated = false; videoUtils.close(); } bool ofGstVideoPlayer::isFrameNew(){ return videoUtils.isFrameNew(); } unsigned char * ofGstVideoPlayer::getPixels(){ return videoUtils.getPixels(); } /* ofPixelsRef ofGstVideoPlayer::getPixelsRef(){ return videoUtils.getPixelsRef(); } */ float ofGstVideoPlayer::getHeight(){ return videoUtils.getHeight(); } float ofGstVideoPlayer::getWidth(){ return videoUtils.getWidth(); } ofGstVideoUtils * ofGstVideoPlayer::getGstVideoUtils(){ return &videoUtils; } void ofGstVideoPlayer::setFrameByFrame(bool frameByFrame){ videoUtils.setFrameByFrame(frameByFrame); }