//-------------------------------------------------------------- bool ofxThreadedVideo::loadMovie(string fileName){ // check if we're using a queue or only allowing one file to load at a time if (!bUseQueue && pathsToLoad.size() > 0){ ofLogWarning() << "Ignoring loadMovie(" << fileName << ") as we're not using a queue and a movie is already loading. Returning false. You can change this behaviour with setUseQueue(true)"; // send event notification ofxThreadedVideoEvent videoEvent = ofxThreadedVideoEvent(loadPath, VIDEO_EVENT_LOAD_BLOCKED, this); ofNotifyEvent(threadedVideoEvent, videoEvent, this); return false; } // put the movie path in a queue pathsToLoad.push(ofToDataPath(fileName)); return true; }
//-------------------------------------------------------------- void ofxThreadedVideo::threadedFunction(){ while (isThreadRunning()){ lock(); ofxThreadedVideoGlobalMutex.lock(); if(!ofxThreadedVideoGlobalCritical && !bCriticalSection){ ofxThreadedVideoGlobalCritical = true; bCriticalSection = true; int videoID = currentVideoID; ofxThreadedVideoCommand c = getCommand(); bool bCanLoad = !bLoaded; bool bPopCommand = false; unlock(); ofxThreadedVideoGlobalMutex.unlock(); if(c.getInstance() == instanceID){ if(c.getCommand() == "play"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); video[videoID].play(); lock(); bIsPlaying = true; bIsPaused = false; unlock(); bPopCommand = true; } if(c.getCommand() == "setPosition"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); lock(); position = c.getArgument<float>(0); unlock(); video[videoID].setPosition(position); bPopCommand = true; } if(c.getCommand() == "setVolume"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); lock(); volume = c.getArgument<float>(0); unlock(); video[videoID].setVolume(volume); bPopCommand = true; } #ifdef USE_QUICKTIME_7 if(c.getCommand() == "setPan"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); lock(); pan = c.getArgument<float>(0); unlock(); video[videoID].setPan(pan); bPopCommand = true; } #endif if(c.getCommand() == "setLoopState"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); lock(); loopState = (ofLoopType)c.getArgument<int>(0); unlock(); video[videoID].setLoopState(loopState); bPopCommand = true; } // if(c.getCommand() == "setSpeed"){ // if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); // lock(); // speed = c.getArgument<float>(0); // unlock(); // video[videoID].setSpeed(speed); // bPopCommand = true; // } // if(c.getCommand() == "setFrame"){ // if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); // int frameTarget = c.getArgument<int>(0); // CLAMP(frameTarget, 0, frameTotal); // video[videoID].setFrame(frameTarget); // bForceFrameNew = true; // bPopCommand = true; // } // if(c.getCommand() == "setFrame"){ // if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); // lock(); // int frameTarget = c.getArgument<int>(0); // bForceFrameNew = true; // frameTarget = CLAMP(frameTarget, 0, frameTotal); // cout << "setframe A: " << frameTarget << " " << videoID << " " << bCriticalSection << endl; // video[videoID].setFrame(frameTarget); // cout << "setframe B: " << frameTarget << " " << videoID << " " << bCriticalSection << endl; // unlock(); // bPopCommand = true; // } if(c.getCommand() == "setPaused"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); lock(); bIsPaused = c.getArgument<bool>(0); unlock(); video[videoID].setPaused(bIsPaused); bPopCommand = true; } if(c.getCommand() == "setAnchorPercent"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); video[videoID].setAnchorPercent(c.getArgument<float>(0), c.getArgument<float>(0)); bPopCommand = true; } if(c.getCommand() == "setAnchorPoint"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); video[videoID].setAnchorPercent(c.getArgument<float>(0), c.getArgument<float>(0)); bPopCommand = true; } if(c.getCommand() == "resetAnchor"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); video[videoID].resetAnchor(); bPopCommand = true; } if(c.getCommand() == "setFade"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); int frameEnd; int frameStart = c.getArgument<int>(0); int durationMillis = c.getArgument<int>(1); float fadeTarget = c.getArgument<float>(2); bool fadeSound = c.getArgument<bool>(3); bool fadeVideo = c.getArgument<bool>(4); bool fadeOnce = c.getArgument<bool>(5); CLAMP(fadeTarget, 0.0f, 1.0f); if(frameStart == -1){ // fade is durationMillis from the end frameEnd = frameTotal; frameStart = frameTotal - ((float)durationMillis / 1000.0) * 25.0; }else{ frameEnd = frameStart + ((float)durationMillis / 1000.0) * 25.0; } if(frameStart == frameEnd){ _fade = fadeTarget; if(fadeVideo) fade = _fade; lock(); if(fadeSound) video[videoID].setVolume(_fade); unlock(); }else{ frameEnd -= 1; // assert(frameStart >= 0); // assert(frameEnd >= frameStart); // assert(frameEnd <= frameTotal); fades.push_back(ofxThreadedVideoFade(frameStart, frameEnd, fadeTarget, fadeSound, fadeVideo, fadeOnce)); } bPopCommand = true; } #ifdef USE_JACK_AUDIO if(c.getCommand() == "setAudioTrackToChannel"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); video[videoID].setAudioTrackToChannel(c.getArgument<int>(0), c.getArgument<int>(1), c.getArgument<int>(2)); bPopCommand = true; } if(c.getCommand() == "setAudioDevice"){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); video[videoID].setAudioDevice(c.getArgument<string>(0)); bPopCommand = true; } #endif if(c.getCommand() == "loadMovie" && bCanLoad){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString(); if(video[videoID].loadMovie(c.getArgument<string>(0))){ if(bVerbose) ofLogVerbose() << instanceID << " = " << c.getCommandAsString() << " executed in thread";; // lock(); fades.clear(); width = video[videoID].getWidth(); height = video[videoID].getHeight(); speed = video[videoID].getSpeed(); duration = video[videoID].getDuration(); position = video[videoID].getPosition(); frameCurrent = video[videoID].getCurrentFrame(); frameTotal = video[videoID].getTotalNumFrames(); #ifdef USE_QUICKTIME_7 volume = video[videoID].getVolume(); // we should implement for QT6 pan = video[videoID].getPan(); #endif loopState = video[videoID].getLoopState(); moviePath = c.getArgument<string>(0); #ifdef TARGET_OSX vector<string> pathParts = ofSplitString(moviePath, "/"); #else vector<string> pathParts = ofSplitString(moviePath, "\\"); #endif movieName = pathParts[pathParts.size() - 1]; bIsPaused = true; bIsPlaying = false; bIsTextureReady = false; bIsLoading = false; bIsMovieDone = false; bLoaded = true; pixels = &video[videoID].getPixelsRef(); unlock(); bPopCommand = true; ofxThreadedVideoEvent e = ofxThreadedVideoEvent(moviePath, VIDEO_EVENT_LOAD_OK, this); ofNotifyEvent(threadedVideoEvent, e, this); ofxThreadedVideoLoadOk++; }else{ ofLogError() << "Could not load: " << instanceID << " + " << c.getCommandAsString(); ofxThreadedVideoEvent e = ofxThreadedVideoEvent(moviePath, VIDEO_EVENT_LOAD_FAIL, this); ofNotifyEvent(threadedVideoEvent, e, this); ofxThreadedVideoLoadFail++; } } } if(bPopCommand){ video[videoID].update(); } lock(); if(bIsFrameNew){ for(unsigned int i = 0; i < fades.size(); i++){ ofxThreadedVideoFade& currentFade = fades.at(i); if(currentFade.getIsFading(frameCurrent)){ _fade = currentFade.getFade(_fade, frameCurrent); if(currentFade.fadeVideo){ if(fade != _fade) fade = _fade; } #ifdef USE_QUICKTIME_7 if(currentFade.fadeSound){ // we should implement for QT6 if(video[videoID].getVolume() != _fade) video[videoID].setVolume(_fade); } #endif } if(currentFade.fadeOnce && currentFade.getFadeDone(frameCurrent)){ fades.erase(fades.begin() + i); i--; } } } ofxThreadedVideoGlobalMutex.lock(); if(bPopCommand) popCommand(); ofxThreadedVideoGlobalCritical = false; bCriticalSection = false; ofxThreadedVideoGlobalMutex.unlock(); unlock(); }else{ ofxThreadedVideoGlobalMutex.unlock(); unlock(); } ofSleepMillis(1); } }
//-------------------------------------------------------------- void ofxThreadedVideo::threadedFunction(){ while (isThreadRunning()){ if(lock()){ // if there's a movie to load... if(loadPath != ""){ loadVideoID = getNextLoadID(); paths[loadVideoID] = loadPath; loadPath = ""; #ifdef TARGET_OSX vector<string> pathParts = ofSplitString(paths[loadVideoID], "/"); #else vector<string> pathParts = ofSplitString(paths[loadVideoID], "\\"); #endif names[loadVideoID] = pathParts[pathParts.size() - 1]; // using a static mutex blocks all threads (including the main app) until we've loaded ofxThreadedVideoMutex.lock(); // load that movie! if(videos[loadVideoID].loadMovie(paths[loadVideoID])){ ofLogVerbose() << "Loaded" << names[loadVideoID] << " " << loadVideoID; // start rolling if AutoPlay is true if (bUseAutoPlay) videos[loadVideoID].play(); // set pixel refs pixels[loadVideoID] = &videos[loadVideoID].getPixelsRef(); }else{ ofLogVerbose() << "Could not load video"; loadVideoID = VIDEO_NONE; // send event notification ofxThreadedVideoEvent videoEvent = ofxThreadedVideoEvent(paths[loadVideoID], VIDEO_EVENT_LOAD_FAIL, this); ofNotifyEvent(threadedVideoEvent, videoEvent, this); } ofxThreadedVideoMutex.unlock(); } // do threaded update of videos updateVideo(currentVideoID); updateVideo(loadVideoID); unlock(); // sleep a bit so we don't thrash the cores!! ofSleepMillis(1000/30); // TODO: implement target frame rate? US might need 30 here? } } }
//-------------------------------------------------------------- void ofxThreadedVideo::update(){ if(lock()){ // check if we're loading a video if(loadVideoID != VIDEO_NONE){ videos[loadVideoID].update(); float w = videos[loadVideoID].getWidth(); float h = videos[loadVideoID].getHeight(); // allocate a texture if the one we have is a different size if(bUseTexture && (textures[loadVideoID].getWidth() != w || textures[loadVideoID].getHeight() != h)){ ofLogVerbose() << "Allocating texture" << loadVideoID << w << h; textures[loadVideoID].allocate(w, h, ofGetGLTypeFromPixelFormat(internalPixelFormat)); } // check for a new frame before loading video if(bFrameNew[loadVideoID]){ // switch the current movie ID to the one we just loaded int lastVideoID = currentVideoID; currentVideoID = loadVideoID; loadVideoID = VIDEO_NONE; // close the last movie - we do this here because // ofQuicktimeVideo chokes if you try to close in a thread if(lastVideoID != VIDEO_NONE){ ofLogVerbose() << "Closing last video" << lastVideoID; paths[lastVideoID] = names[lastVideoID] = ""; videos[lastVideoID].stop(); // reset properties to defaults newPosition[lastVideoID] = -1.0f; newFrame[lastVideoID] = -1; newSpeed[lastVideoID] = 1.0f; newLoopType[lastVideoID] = -1; frame[lastVideoID] = 0; bFrameNew[lastVideoID] = false; bPaused[lastVideoID] = false; volume[lastVideoID] = 255; } // send event notification ofxThreadedVideoEvent videoEvent = ofxThreadedVideoEvent(paths[currentVideoID], VIDEO_EVENT_LOAD_OK, this); ofNotifyEvent(threadedVideoEvent, videoEvent, this); } } // check for a new frame for current video updateTexture(currentVideoID); // if there's a movie in the queue if(pathsToLoad.size() > 0 && loadPath == "" && loadVideoID == VIDEO_NONE){ // ...let's start trying to load it! loadPath = pathsToLoad.front(); pathsToLoad.pop(); }; // calculate frameRate -> taken from ofAppRunner prevMillis = ofGetElapsedTimeMillis(); timeNow = ofGetElapsedTimef(); double diff = timeNow-timeThen; if( diff > 0.00001 ){ fps = 1.0 / diff; frameRate *= 0.9f; frameRate += 0.1f*fps; } lastFrameTime = diff; timeThen = timeNow; unlock(); } }