#include "ofVideoPlayer.h" #include "ofUtils.h" #ifdef TARGET_LINUX #include #endif //-------------------------------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------------------------------- bool createMovieFromPath(char * path, Movie &movie); bool createMovieFromPath(char * path, Movie &movie){ Boolean isdir = false; OSErr result = 0; FSSpec theFSSpec; short actualResId = DoTheRightThing; #ifdef TARGET_WIN32 result = NativePathNameToFSSpec (path, &theFSSpec, 0); if (result != noErr) { ofLog(OF_LOG_ERROR,"NativePathNameToFSSpec failed %d", result); ofLog(OF_LOG_ERROR,"Error loading movie"); return false; } #endif #ifdef TARGET_OSX FSRef fsref; result = FSPathMakeRef((const UInt8*)path, &fsref, &isdir); if (result) { ofLog(OF_LOG_ERROR,"FSPathMakeRef failed %d", result); ofLog(OF_LOG_ERROR,"Error loading movie"); return false; } result = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL); if (result) { ofLog(OF_LOG_ERROR,"FSGetCatalogInfo failed %d", result); ofLog(OF_LOG_ERROR,"Error loading movie"); return false; } #endif short movieResFile; result = OpenMovieFile (&theFSSpec, &movieResFile, fsRdPerm); if (result == noErr) { short movieResID = 0; result = NewMovieFromFile(&movie, movieResFile, &movieResID, (unsigned char *) 0, newMovieActive, (Boolean *) 0); if (result == noErr){ CloseMovieFile (movieResFile); } else { ofLog(OF_LOG_ERROR,"NewMovieFromFile failed %d", result); return false; } } else { ofLog(OF_LOG_ERROR,"OpenMovieFile failed %d", result); return false; } return true; } //-------------------------------------------------------------- bool createMovieFromURL(string urlIn, Movie &movie){ char * url = (char *)urlIn.c_str(); Handle urlDataRef; OSErr err; urlDataRef = NewHandle(strlen(url) + 1); if ( ( err = MemError()) != noErr){ ofLog(OF_LOG_ERROR,"createMovieFromURL: error creating url handle"); return false;} BlockMoveData(url, *urlDataRef, strlen(url) + 1); err = NewMovieFromDataRef(&movie, newMovieActive,nil, urlDataRef, URLDataHandlerSubType); DisposeHandle(urlDataRef); if(err != noErr){ ofLog(OF_LOG_ERROR,"createMovieFromURL: error loading url"); return false; }else{ return true; } return false; } //-------------------------------------------------------------- OSErr DrawCompleteProc(Movie theMovie, long refCon); OSErr DrawCompleteProc(Movie theMovie, long refCon){ ofVideoPlayer * ofvp = (ofVideoPlayer *)refCon; #if defined(TARGET_OSX) && defined(BIG_ENDIAN) convertPixels(ofvp->offscreenGWorldPixels, ofvp->pixels, ofvp->width, ofvp->height); #endif ofvp->bHavePixelsChanged = true; if (ofvp->bUseTexture == true){ ofvp->tex.loadData(ofvp->pixels, ofvp->width, ofvp->height, GL_RGB); } return noErr; } //-------------------------------------------------------------- #else //-------------------------------------------------------------- #include "gst/app/gstappsink.h" static bool plugin_registered = false; static bool gst_inited = false; //------------------------------------ void ofGstDataLock(ofGstVideoData * data){ pthread_mutex_lock( &(data->buffer_mutex) ); } //------------------------------------ void ofGstDataUnlock(ofGstVideoData * data){ pthread_mutex_unlock( &(data->buffer_mutex) ); } // called when the appsink notifies us that there is a new buffer ready for // processing static void on_new_buffer_from_source (GstElement * elt, ofGstVideoData * data) { guint size; GstBuffer *buffer; //get the buffer from appsink buffer = gst_app_sink_pull_buffer (GST_APP_SINK (elt)); size = GST_BUFFER_SIZE (buffer); //ofLog(OF_LOG_VERBOSE,"new buffer of size %d", size); ofGstDataLock(data); if(data->pixels){ memcpy (data->pixels, GST_BUFFER_DATA (buffer), size); data->bHasPixelsChanged=true; } ofGstDataUnlock(data); if( !data->nFrames && data->pipelineState==GST_STATE_PLAYING && data->speed==1 ){ data->nFrames=data->durationNanos/buffer->duration; } /// we don't need the appsink buffer anymore gst_buffer_unref (buffer); } static gboolean appsink_plugin_init (GstPlugin * plugin) { gst_element_register (plugin, "appsink", GST_RANK_NONE, GST_TYPE_APP_SINK); return TRUE; } //-------------------------------------------------------------- #endif //-------------------------------------------------------------- //--------------------------------------------------------------------------- ofVideoPlayer::ofVideoPlayer (){ bLoaded = false; width = 0; height = 0; speed = 1; bUseTexture = true; bStarted = false; pixels = NULL; nFrames = 0; bPaused = false; //-------------------------------------------------------------- #ifndef TARGET_LINUX // !linux = quicktime... //-------------------------------------------------------------- moviePtr = NULL; allocated = false; offscreenGWorld = NULL; //-------------------------------------------------------------- #else //-------------------------------------------------------------- gstPipeline = NULL; bIsFrameNew = false; loopMode = OF_LOOP_NONE; durationNanos = 0; bIsMovieDone = false; posChangingPaused = 0; isStream = false; gstData.durationNanos = 0; gstData.nFrames = 0; gstData.speed = speed; pthread_mutex_init(&(gstData.buffer_mutex),NULL); pthread_mutex_init(&seek_mutex,NULL); if(!g_thread_supported()){ g_thread_init(NULL); } if(!gst_inited){ gst_init (NULL, NULL); gst_inited=true; } if(!plugin_registered){ gst_plugin_register_static(GST_VERSION_MAJOR, GST_VERSION_MINOR, "appsink", "Element application sink", appsink_plugin_init, "0.1", "LGPL", "ofVideoPlayer", "openFrameworks", "http://openframeworks.cc/"); plugin_registered=true; } //-------------------------------------------------------------- #endif //-------------------------------------------------------------- } //--------------------------------------------------------------------------- unsigned char * ofVideoPlayer::getPixels(){ return pixels; } //------------------------------------ //for getting a reference to the texture ofTexture & ofVideoPlayer::getTextureReference(){ if(!tex.bAllocated() ){ ofLog(OF_LOG_WARNING, "ofVideoPlayer - getTextureReference - texture is not allocated"); } return tex; } //--------------------------------------------------------------------------- bool ofVideoPlayer::isFrameNew(){ return bIsFrameNew; } //-------------------------------------------------------------------- void ofVideoPlayer::update(){ idleMovie(); } //--------------------------------------------------------------------------- void ofVideoPlayer::idleMovie(){ //-------------------------------------------------------------- #ifndef TARGET_LINUX // !linux = quicktime... //-------------------------------------------------------------- if (bLoaded == true){ #if defined(TARGET_WIN32) || defined(QT_USE_MOVIETASK) MoviesTask(moviePtr,0); #endif //-------------------------------------------------------------- #else // linux. //-------------------------------------------------------------- gstHandleMessage(); if (bLoaded == true){ ofGstDataLock(&gstData); bHavePixelsChanged = gstData.bHasPixelsChanged; if (bHavePixelsChanged){ gstData.bHasPixelsChanged=false; bIsMovieDone = false; if(bUseTexture) tex.loadData(pixels, width, height, GL_RGB); } ofGstDataUnlock(&gstData); //-------------------------------------------------------------- #endif //-------------------------------------------------------------- // --------------------------------------------------- // on all platforms, // do "new"ness ever time we idle... // before "isFrameNew" was clearning, // people had issues with that... // and it was badly named so now, newness happens // per-idle not per isNew call // --------------------------------------------------- bIsFrameNew = bHavePixelsChanged; if (bHavePixelsChanged == true) { bHavePixelsChanged = false; } } } //--------------------------------------------------------------------------- void ofVideoPlayer::closeMovie(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- if (bLoaded == true){ DisposeMovie (moviePtr); #ifdef TARGET_WIN32 DisposeMovieDrawingCompleteUPP (myDrawCompleteProc); #endif moviePtr = NULL; } //-------------------------------------- #else //-------------------------------------- if(bLoaded){ gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_NULL); //gst_object_unref(gstSink); gst_object_unref(gstPipeline); } //-------------------------------------- #endif //-------------------------------------- bLoaded = false; } //--------------------------------------------------------------------------- void ofVideoPlayer::close(){ closeMovie(); } //--------------------------------------------------------------------------- ofVideoPlayer::~ofVideoPlayer(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- closeMovie(); if(allocated) delete[] pixels; if(allocated) delete[] offscreenGWorldPixels; if ((offscreenGWorld)) DisposeGWorld((offscreenGWorld)); //-------------------------------------- #else //-------------------------------------- closeMovie(); if (pixels != NULL){ delete[] pixels; } //-------------------------------------- #endif //-------------------------------------- tex.clear(); } //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- void ofVideoPlayer::createImgMemAndGWorld(){ Rect movieRect; movieRect.top = 0; movieRect.left = 0; movieRect.bottom = height; movieRect.right = width; offscreenGWorldPixels = new unsigned char[4 * width * height + 32]; pixels = new unsigned char[width*height*3]; #if defined(TARGET_OSX) && defined(BIG_ENDIAN) QTNewGWorldFromPtr (&(offscreenGWorld), k32ARGBPixelFormat, &(movieRect), NULL, NULL, 0, (offscreenGWorldPixels), 4 * width); #else QTNewGWorldFromPtr (&(offscreenGWorld), k24RGBPixelFormat, &(movieRect), NULL, NULL, 0, (pixels), 3 * width); #endif LockPixels(GetGWorldPixMap(offscreenGWorld)); SetGWorld (offscreenGWorld, NULL); SetMovieGWorld (moviePtr, offscreenGWorld, nil); //------------------------------------ texture stuff: if (bUseTexture){ // create the texture, set the pixels to black and // upload them to the texture (so at least we see nothing black the callback) tex.allocate(width,height,GL_RGB); memset(pixels, 0, width*height*3); tex.loadData(pixels, width, height, GL_RGB); allocated = true; } } //-------------------------------------- #endif //-------------------------------------- //--------------------------------------------------------------------------- bool ofVideoPlayer::loadMovie(string name){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- initializeQuicktime(); // init quicktime closeMovie(); // if we have a movie open, close it bLoaded = false; // try to load now if( name.substr(0, 7) == "http://" || name.substr(0,7) == "rtsp://" ){ if(! createMovieFromURL(name, moviePtr) ) return false; }else{ name = ofToDataPath(name); if( !createMovieFromPath((char *)name.c_str(), moviePtr) ) return false; } bool bDoWeAlreadyHaveAGworld = false; if (width != 0 && height != 0){ bDoWeAlreadyHaveAGworld = true; } Rect movieRect; GetMovieBox(moviePtr, &(movieRect)); if (bDoWeAlreadyHaveAGworld){ // is the gworld the same size, then lets *not* de-allocate and reallocate: if (width == movieRect.right && height == movieRect.bottom){ SetMovieGWorld (moviePtr, offscreenGWorld, nil); } else { width = movieRect.right; height = movieRect.bottom; delete(pixels); delete(offscreenGWorldPixels); if ((offscreenGWorld)) DisposeGWorld((offscreenGWorld)); createImgMemAndGWorld(); } } else { width = movieRect.right; height = movieRect.bottom; createImgMemAndGWorld(); } if (moviePtr == NULL){ return false; } //----------------- callback method MovieDrawingCompleteUPP myDrawCompleteProc; myDrawCompleteProc = NewMovieDrawingCompleteUPP (DrawCompleteProc); SetMovieDrawingCompleteProc (moviePtr, movieDrawingCallWhenChanged, myDrawCompleteProc, (long)this); // ------------- get the total # of frames: nFrames = 0; TimeValue curMovieTime; curMovieTime = 0; TimeValue duration; //OSType whichMediaType = VIDEO_TYPE; // mingw chokes on this OSType whichMediaType = FOUR_CHAR_CODE('vide'); short flags = nextTimeMediaSample + nextTimeEdgeOK; while( curMovieTime >= 0 ) { nFrames++; GetMovieNextInterestingTime(moviePtr,flags,1,&whichMediaType,curMovieTime,0,&curMovieTime,&duration); flags = nextTimeMediaSample; } nFrames--; // there's an extra time step at the end of themovie // ------------- get some pixels in there ------ GoToBeginningOfMovie(moviePtr); SetMovieActiveSegment(moviePtr, -1,-1); MoviesTask(moviePtr,0); #if defined(TARGET_OSX) && defined(BIG_ENDIAN) convertPixels(offscreenGWorldPixels, pixels, width, height); #endif if (bUseTexture == true){ tex.loadData(pixels, width, height, GL_RGB); } bStarted = false; bLoaded = true; bPlaying = false; bHavePixelsChanged = false; speed = 1; return true; //-------------------------------------- #else //-------------------------------------- bLoaded = false; bPaused = true; speed = 1.0f; bHavePixelsChanged = false; if( name.find( "://",0 ) == string::npos){ name = "file://"+ofToDataPath(name,true); isStream = false; }else{ isStream = true; } ofLog(OF_LOG_VERBOSE,"loading "+name); gstData.loop = g_main_loop_new (NULL, FALSE); gstPipeline = gst_element_factory_make("playbin","player"); g_object_set(G_OBJECT(gstPipeline), "uri", name.c_str(), NULL); // create the oF appsink for video rgb without sync to clock gstSink = gst_element_factory_make("appsink", NULL); GstCaps *caps = gst_caps_new_simple("video/x-raw-rgb", NULL); gst_app_sink_set_caps(GST_APP_SINK(gstSink), caps); gst_caps_unref(caps); gst_base_sink_set_sync(GST_BASE_SINK(gstSink), false); g_object_set (G_OBJECT(gstPipeline),"video-sink",gstSink,NULL); GstElement *audioSink = gst_element_factory_make("gconfaudiosink", NULL); g_object_set (G_OBJECT(gstPipeline),"audio-sink",audioSink,NULL); // pause the pipeline if(gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { ofLog(OF_LOG_ERROR, "GStreamer: unable to set pipeline to paused\n"); gst_object_unref(gstPipeline); return false; } gstData.pipeline=gstPipeline; // set the appsink to emit signals to get eos and errors g_object_set (G_OBJECT (gstSink), "emit-signals", TRUE, "sync", TRUE, NULL); g_signal_connect (gstSink, "new-buffer", G_CALLBACK (on_new_buffer_from_source), &gstData); if(!isStream){ return allocate(); } // unreference all elements so they get deleted on close gst_object_unref(audioSink); return true; //-------------------------------------- #endif //-------------------------------------- } //-------------------------------------------------------- void ofVideoPlayer::start(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- if (bLoaded == true && bStarted == false){ SetMovieActive(moviePtr, true); //------------------ set the movie rate to default //------------------ and preroll, so the first frames come correct TimeValue timeNow = GetMovieTime(moviePtr, 0); Fixed playRate = GetMoviePreferredRate(moviePtr); //Not being used! PrerollMovie(moviePtr, timeNow, X2Fix(speed)); SetMovieRate(moviePtr, X2Fix(speed)); setLoopState(OF_LOOP_NORMAL); // get some pixels in there right away: MoviesTask(moviePtr,0); #if defined(TARGET_OSX) && defined(BIG_ENDIAN) convertPixels(offscreenGWorldPixels, pixels, width, height); #endif bHavePixelsChanged = true; if (bUseTexture == true){ tex.loadData(pixels, width, height, GL_RGB); } bStarted = true; bPlaying = true; } //-------------------------------------- #else //-------------------------------------- bHavePixelsChanged = true; bStarted = true; bPlaying = true; setPaused(false); //-------------------------------------- #endif //-------------------------------------- } //-------------------------------------------------------- void ofVideoPlayer::play(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- if (!bStarted){ start(); }else { // ------------ lower level "startMovie" // ------------ use desired speed & time (-1,1,etc) to Preroll... bPlaying = true; TimeValue timeNow; timeNow = GetMovieTime(moviePtr, nil); PrerollMovie(moviePtr, timeNow, X2Fix(speed)); SetMovieRate(moviePtr, X2Fix(speed)); MoviesTask(moviePtr, 0); } //-------------------------------------- #else //-------------------------------------- if (!bStarted){ start(); }else { bPlaying = true; setPaused(false); } //-------------------------------------- #endif //-------------------------------------- } //-------------------------------------------------------- void ofVideoPlayer::stop(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- StopMovie (moviePtr); SetMovieActive (moviePtr, false); bStarted = false; //-------------------------------------- #else //-------------------------------------- setPaused(true); //-------------------------------------- #endif //-------------------------------------- } //-------------------------------------------------------- void ofVideoPlayer::setVolume(int volume){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- SetMovieVolume(moviePtr, volume); //-------------------------------------- #else //-------------------------------------- gdouble gvolume = CLAMP(volume,0,10); g_object_set(G_OBJECT(gstPipeline), "volume", gvolume, NULL); //-------------------------------------- #endif //-------------------------------------- } //-------------------------------------------------------- void ofVideoPlayer::setLoopState(int state){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- TimeBase myTimeBase; long myFlags = 0L; myTimeBase = GetMovieTimeBase(moviePtr); myFlags = GetTimeBaseFlags(myTimeBase); switch (state) { case OF_LOOP_NORMAL: myFlags |= loopTimeBase; myFlags &= ~palindromeLoopTimeBase; SetMoviePlayHints(moviePtr, hintsLoop, hintsLoop); SetMoviePlayHints(moviePtr, 0L, hintsPalindrome); break; case OF_LOOP_PALINDROME: myFlags |= loopTimeBase; myFlags |= palindromeLoopTimeBase; SetMoviePlayHints(moviePtr, hintsLoop, hintsLoop); SetMoviePlayHints(moviePtr, hintsPalindrome, hintsPalindrome); break; case OF_LOOP_NONE: default: myFlags &= ~loopTimeBase; myFlags &= ~palindromeLoopTimeBase; SetMoviePlayHints(moviePtr, 0L, hintsLoop | hintsPalindrome); break; } SetTimeBaseFlags(myTimeBase, myFlags); //-------------------------------------- #else //-------------------------------------- loopMode = state; //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- void ofVideoPlayer::setPosition(float pct){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- TimeRecord tr; tr.base = GetMovieTimeBase(moviePtr); long total = GetMovieDuration(moviePtr ); long newPos = (long)((float)total * pct); SetMovieTimeValue(moviePtr, newPos); MoviesTask(moviePtr,0); //-------------------------------------- #else //-------------------------------------- //pct = CLAMP(pct, 0,1);// check between 0 and 1; GstFormat format = GST_FORMAT_TIME; GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); gint64 pos = (guint64)((double)pct*(double)durationNanos); if(bPaused){ seek_lock(); gst_element_set_state (gstPipeline, GST_STATE_PLAYING); posChangingPaused=true; seek_unlock(); } if(speed>0){ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1)) { ofLog(OF_LOG_WARNING,"GStreamer: unable to change speed"); } }else{ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos)) { ofLog(OF_LOG_WARNING,"GStreamer: unable to change speed"); } } //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- void ofVideoPlayer::setFrame(int frame){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- // frame 0 = first frame... // this is the simple way... //float durationPerFrame = getDuration() / getTotalNumFrames(); // seems that freezing, doing this and unfreezing seems to work alot // better then just SetMovieTimeValue() ; if (!bPaused) SetMovieRate(moviePtr, X2Fix(0)); // this is better with mpeg, etc: double frameRate = 0; double movieTimeScale = 0; MovieGetStaticFrameRate(moviePtr, &frameRate); movieTimeScale = GetMovieTimeScale(moviePtr); if (frameRate > 0){ double frameDuration = 1 / frameRate; TimeValue t = (TimeValue)(frame * frameDuration * movieTimeScale); SetMovieTimeValue(moviePtr, t); MoviesTask(moviePtr, 0); } if (!bPaused) SetMovieRate(moviePtr, X2Fix(speed)); //-------------------------------------- #else //-------------------------------------- float pct = (float)frame / (float)gstData.nFrames; setPosition(pct); //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- float ofVideoPlayer::getDuration(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- return (float) (GetMovieDuration (moviePtr) / (double) GetMovieTimeScale (moviePtr)); //-------------------------------------- #else //-------------------------------------- return (float)durationNanos/(float)GST_SECOND; //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- float ofVideoPlayer::getPosition(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- long total = GetMovieDuration(moviePtr); long current = GetMovieTime(moviePtr, nil); float pct = ((float)current/(float)total); return pct; //-------------------------------------- #else //-------------------------------------- gint64 pos=0; GstFormat format=GST_FORMAT_TIME; if(!gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos)) ofLog(OF_LOG_ERROR,"GStreamer: cannot query position"); return (float)pos/(float)durationNanos; //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- int ofVideoPlayer::getCurrentFrame(){ int frame = 0; // zach I think this may fail on variable length frames... float pos = getPosition(); float framePosInFloat = ((float)getTotalNumFrames() * pos); int framePosInInt = (int)framePosInFloat; float floatRemainder = (framePosInFloat - framePosInInt); if (floatRemainder > 0.5f) framePosInInt = framePosInInt + 1; //frame = (int)ceil((getTotalNumFrames() * getPosition())); frame = framePosInInt; return frame; } //--------------------------------------------------------------------------- bool ofVideoPlayer::getIsMovieDone(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- bool bIsMovieDone = (bool)IsMovieDone(moviePtr); return bIsMovieDone; //-------------------------------------- #else //-------------------------------------- return bIsMovieDone; //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- void ofVideoPlayer::firstFrame(){ setFrame(0); } //--------------------------------------------------------------------------- void ofVideoPlayer::nextFrame(){ setFrame(getCurrentFrame() + 1); } //--------------------------------------------------------------------------- void ofVideoPlayer::previousFrame(){ setFrame(getCurrentFrame() - 1); } //--------------------------------------------------------------------------- void ofVideoPlayer::setSpeed(float _speed){ speed = _speed; //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- if (bPlaying == true){ //setMovieRate actually plays, so let's call it only when we are playing SetMovieRate(moviePtr, X2Fix(speed)); } //-------------------------------------- #else //-------------------------------------- GstFormat format = GST_FORMAT_TIME; GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH |GST_SEEK_FLAG_ACCURATE); gint64 pos; if(speed==0){ gst_element_set_state (gstPipeline, GST_STATE_PAUSED); return; } if(!gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos)) ofLog(OF_LOG_ERROR,"GStreamer: cannot query position"); 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)) { ofLog(OF_LOG_WARNING,"GStreamer: unable to change speed"); } }else{ if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos)) { ofLog(OF_LOG_WARNING,"GStreamer: unable to change speed"); } } ofLog(OF_LOG_VERBOSE,"Gstreamer: speed change to %f", speed); //-------------------------------------- #endif //-------------------------------------- } //--------------------------------------------------------------------------- float ofVideoPlayer::getSpeed(){ return speed; } //--------------------------------------------------------------------------- void ofVideoPlayer::setPaused(bool _bPause){ bPaused = _bPause; //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- // there might be a more "quicktime-ish" way (or smarter way) // to do this for now, to pause, just set the movie's speed to zero, // on un-pause, set the movie's speed to "speed" // (and hope that speed != 0...) if (bPlaying == true){ if (bPaused == true) SetMovieRate(moviePtr, X2Fix(0)); else SetMovieRate(moviePtr, X2Fix(speed)); } //-------------------------------------- #else //-------------------------------------- //timeLastIdle = ofGetElapsedTimeMillis(); if(bLoaded){ if(bPaused) gst_element_set_state (gstPipeline, GST_STATE_PAUSED); else gst_element_set_state (gstPipeline, GST_STATE_PLAYING); } //-------------------------------------- #endif //-------------------------------------- } //------------------------------------ void ofVideoPlayer::setUseTexture(bool bUse){ bUseTexture = bUse; } //we could cap these values - but it might be more useful //to be able to set anchor points outside the image //---------------------------------------------------------- void ofVideoPlayer::setAnchorPercent(float xPct, float yPct){ if (bUseTexture)tex.setAnchorPercent(xPct, yPct); } //---------------------------------------------------------- void ofVideoPlayer::setAnchorPoint(int x, int y){ if (bUseTexture)tex.setAnchorPoint(x, y); } //---------------------------------------------------------- void ofVideoPlayer::resetAnchor(){ if (bUseTexture)tex.resetAnchor(); } //------------------------------------ void ofVideoPlayer::draw(float _x, float _y, float _w, float _h){ if (bUseTexture){ tex.draw(_x, _y, _w, _h); } } //------------------------------------ void ofVideoPlayer::draw(float _x, float _y){ draw(_x, _y, (float)width, (float)height); } //------------------------------------ int ofVideoPlayer::getTotalNumFrames(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- return nFrames; //-------------------------------------- #else //-------------------------------------- return gstData.nFrames; //-------------------------------------- #endif //-------------------------------------- } //---------------------------------------------------------- float ofVideoPlayer::getHeight(){ return (float)height; } //---------------------------------------------------------- float ofVideoPlayer::getWidth(){ return (float)width; } //-------------------------------------- #ifdef OF_VIDEO_PLAYER_GSTREAMER //-------------------------------------- //---------------------------------------------------------- bool ofVideoPlayer::allocate(){ // wait for paused state to query the duration GstState state = GST_STATE_PAUSED; gst_element_get_state(gstPipeline,&state,NULL,2*GST_SECOND); GstFormat format=GST_FORMAT_TIME; if(!gst_element_query_duration(gstPipeline,&format,&durationNanos)) ofLog(OF_LOG_WARNING,"GStreamer: cannot query time duration"); gstData.durationNanos = durationNanos; gstData.nFrames = 0; // query width, height, fps and do data allocation if (GstPad* pad = gst_element_get_static_pad(gstSink, "sink")) { if(gst_video_get_size(GST_PAD(pad), &width, &height) && bUseTexture){ pixels=new unsigned char[width*height*3]; gstData.pixels=pixels; memset(pixels,0,width*height*3); tex.allocate(width,height,GL_RGB); tex.loadData(pixels,width,height,GL_RGB); allocated = true; }else{ ofLog(OF_LOG_ERROR,"GStreamer: cannot query width and height"); return false; } /*GstCaps * caps = gst_pad_get_caps(pad); if(caps){ int fps_n; int fps_d; if(gst_video_parse_caps_framerate (caps,&fps_n,&fps_d)) ofLog(OF_LOG_VERBOSE,"fps_n:%d fps_d:%d",fps_n,fps_d); else ofLog(OF_LOG_WARNING,"Gstreamer: cannot get framerate, frame seek won't work"); }else{ ofLog(OF_LOG_WARNING,"Gstreamer: cannot get pad caps, frame seek won't work"); }*/ gst_object_unref(GST_OBJECT(pad)); }else{ ofLog(OF_LOG_ERROR,"GStreamer: cannot get sink pad"); return false; } bLoaded = true; return bLoaded; } //---------------------------------------------------------- void ofVideoPlayer::gstHandleMessage() { GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(gstPipeline)); while(gst_bus_have_pending(bus)) { GstMessage* msg = gst_bus_pop(bus); ofLog(OF_LOG_VERBOSE,"GStreamer: Got %s message", GST_MESSAGE_TYPE_NAME(msg)); switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_BUFFERING: gint pctBuffered; gst_message_parse_buffering(msg,&pctBuffered); ofLog(OF_LOG_VERBOSE,"GStreamer: buffering %i\%", pctBuffered); if(isStream && !bLoaded){ allocate(); } if(pctBuffered<100){ gst_element_set_state (gstPipeline, GST_STATE_PAUSED); }else if(!bPaused){ gst_element_set_state (gstPipeline, GST_STATE_PLAYING); } break; case GST_MESSAGE_DURATION:{ GstFormat format=GST_FORMAT_TIME; if(!gst_element_query_duration(gstPipeline,&format,&durationNanos)) ofLog(OF_LOG_WARNING,"GStreamer: cannot query duration"); }break; case GST_MESSAGE_STATE_CHANGED: GstState oldstate, newstate, pendstate; gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate); gstData.pipelineState=newstate; seek_lock(); if(posChangingPaused && newstate==GST_STATE_PLAYING){ gst_element_set_state (gstPipeline, GST_STATE_PAUSED); posChangingPaused=false; } seek_unlock(); ofLog(OF_LOG_VERBOSE,"GStreamer: state changed from %d to %d (%d)", oldstate, newstate, pendstate); break; case GST_MESSAGE_ASYNC_DONE: gstData.speed=speed; ofLog(OF_LOG_VERBOSE,"GStreamer: async done"); break; case GST_MESSAGE_ERROR: { GError *err; gchar *debug; gst_message_parse_error(msg, &err, &debug); ofLog(OF_LOG_ERROR, "GStreamer Plugin: Embedded video playback halted; module %s reported: %s", gst_element_get_name(GST_MESSAGE_SRC (msg)), err->message); g_error_free(err); g_free(debug); gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_NULL); }break; case GST_MESSAGE_EOS: ofLog(OF_LOG_VERBOSE,"GStreamer: end of the stream."); bIsMovieDone = true; 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); float loopSpeed; if(pos>0) loopSpeed=-speed; else loopSpeed=speed; if(!gst_element_seek(GST_ELEMENT(gstPipeline), speed, format, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, durationNanos)) { ofLog(OF_LOG_WARNING,"GStreamer: unable to seek"); } }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)) { ofLog(OF_LOG_WARNING,"GStreamer: unable to seek"); } }break; } break; default: ofLog(OF_LOG_VERBOSE,"GStreamer: unhandled message"); break; } gst_message_unref(msg); } gst_object_unref(GST_OBJECT(bus)); } void ofVideoPlayer::seek_lock(){ pthread_mutex_lock( &seek_mutex ); } void ofVideoPlayer::seek_unlock(){ pthread_mutex_unlock( &seek_mutex ); } //-------------------------------------- #endif //--------------------------------------