From 5d502c65b83fae65de1f34f1f73def9bd0b72e8e Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Wed, 20 Sep 2006 22:51:24 +0000 Subject: [PATCH] New player, ffwd/fbwd, iframe navigation stuff --- demuxer.cc | 32 ++ demuxer.h | 5 +- demuxervdr.h | 4 +- player.cc | 842 ++++++++++++++++++++++++++++++--------------------- player.h | 89 ++++-- vdr.cc | 49 +++ vdr.h | 3 + video.h | 1 + videomvp.cc | 5 + videomvp.h | 1 + vvideorec.cc | 15 +- 11 files changed, 679 insertions(+), 367 deletions(-) diff --git a/demuxer.cc b/demuxer.cc index 90db814..b519960 100644 --- a/demuxer.cc +++ b/demuxer.cc @@ -314,3 +314,35 @@ UINT Demuxer::PESPacket::findPictureHeader() } return pos-3; } + + +// static function for stripping audio from a buffer containing an I Frame and its audio +UINT Demuxer::stripAudio(UCHAR* buf, UINT len) +{ + UINT read_pos = 0, write_pos = 0; + UINT pattern, packet_length; + if (len < 4) return 0; + pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); + while (read_pos + 7 <= len) + { + pattern = ((pattern << 8) & 0xFFFFFFFF) | buf[read_pos+3]; + if (pattern < 0x000001E0 || pattern > 0x000001EF) + read_pos++; + else + { + packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6; + if (read_pos + packet_length > len) + read_pos = len; + else + { + if (read_pos != write_pos) + memmove(buf+write_pos, buf+read_pos, packet_length); + read_pos += packet_length; + write_pos += packet_length; + pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8) + | (buf[read_pos+2]); + } + } + } + return write_pos; +} diff --git a/demuxer.h b/demuxer.h index c4c9392..ecfc011 100644 --- a/demuxer.h +++ b/demuxer.h @@ -57,7 +57,7 @@ protected: UINT submitted; virtual void parseDetails(); UINT findPictureHeader(); - + static const ULLONG PTS_INVALID; }; friend class PESPacket; @@ -95,6 +95,9 @@ protected: ASPECT_16_9 = 3 }; + // Strip audio packets from buffer, leaving video only *static function* + static UINT stripAudio(UCHAR* buf, UINT len); + protected: // General demuxer objects and status indicators static Demuxer* instance; diff --git a/demuxervdr.h b/demuxervdr.h index 65b0e8e..ff2d030 100644 --- a/demuxervdr.h +++ b/demuxervdr.h @@ -30,10 +30,10 @@ class DemuxerVDR : public Demuxer { class PESPacketVDR : public PESPacket { - void parseDetails(); + void parseDetails(); }; friend class PESPacketVDR; - + public: DemuxerVDR(); void reset(); diff --git a/player.cc b/player.cc index 7477586..6dfb7da 100644 --- a/player.cc +++ b/player.cc @@ -29,18 +29,12 @@ Player::Player(MessageQueue* messageQueue, bool tIsRecording, bool tIsRadio) audio = Audio::getInstance(); video = Video::getInstance(); logger = Log::getInstance(); + vdr = VDR::getInstance(); initted = false; - paused = false; - playing = false; - ffwd = false; - fbwd = false; lengthBytes = 0; lengthFrames = 0; - feedPosition = 0; - feedMode = MODE_NORMAL; - lastRescan = 0; -// startTS = 0; -// endTS = 0; + currentFrameNumber = 0; + state = S_STOP; videoStartup = false; preBuffering = false; @@ -101,26 +95,12 @@ int Player::init() int Player::shutdown() { if (!initted) return 0; + switchState(S_STOP); initted = false; - // copy of stop - if (playing) - { - playing = false; - threadStop(); - video->stop(); - video->blank(); - audio->stop(); - vfeed.stop(); - - afeed.stop(); - video->reset(); - demuxer->reset(); - feedPosition = 0; - } - delete demuxer; demuxer = NULL; + #ifdef WIN32 CloseHandle(mutex); #endif @@ -130,20 +110,11 @@ int Player::shutdown() void Player::setStartFrame(ULONG startFrame) { - startFrameNum = startFrame; - if (startFrameNum) - { - feedPosition = VDR::getInstance()->positionFromFrameNumber(startFrameNum); - } - else - { - feedPosition = 0; - } + currentFrameNumber = startFrame; } void Player::setLengthBytes(ULLONG length) { - lastRescan = time(NULL); lengthBytes = length; logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes); } @@ -167,297 +138,386 @@ ULONG Player::getCurrentFrameNum() // ----------------------------------- Externally called events -int Player::play() +void Player::play() { + if (!initted) return; + if (state == S_PLAY) return; lock(); bool doUnlock = false; - int result = playInt(&doUnlock); + if (state == S_PAUSE) doUnlock = true; + switchState(S_PLAY); if (doUnlock) unLock(); - return result; } void Player::stop() { + if (!initted) return; + if (state == S_STOP) return; lock(); logger->log("Player", Log::DEBUG, "Stop called lock"); - stopInt(); + switchState(S_STOP); unLock(); } void Player::togglePause() { + if (!initted) return; lock(); - togglePauseInt(); + if (state == S_PAUSE) switchState(S_PLAY); + else switchState(S_PAUSE); unLock(); } void Player::toggleFastForward() { + if (!initted) return; lock(); - toggleFastForwardInt(); + if (state == S_FFWD) switchState(S_PLAY); + else switchState(S_FFWD); unLock(); } void Player::toggleFastBackward() { + if (!initted) return; lock(); - toggleFastBackwardInt(); + if (state == S_FBWD) switchState(S_PLAY); + else switchState(S_FBWD); unLock(); } void Player::jumpToPercent(int percent) { lock(); - jumpToPercentInt(percent); + logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent); + ULONG newFrame = percent * lengthFrames / 100; + switchState(S_JUMP, newFrame); // unLock(); - let thread unlock this } void Player::skipForward(int seconds) { lock(); - skipForwardInt(seconds); + logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); + ULONG newFrame = getCurrentFrameNum() + (seconds * video->getFPS()); + if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); } + else switchState(S_JUMP, newFrame); // unLock(); - let thread unlock this } void Player::skipBackward(int seconds) { lock(); - skipBackwardInt(seconds); + logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); + long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS()); + if (newFrameNum < 0) newFrameNum = 0; + switchState(S_JUMP, newFrameNum); // unLock(); - let thread unlock this } // ----------------------------------- Implementations called events - -int Player::playInt(bool* doUnlock) -{ - if (!initted) return 0; - - // If we are just paused, unpause! - if (paused) - { - togglePauseInt(); - *doUnlock = true; - return 1; - } - - // If we are fast forwarding, set to normal - if (ffwd) - { - toggleFastForwardInt(); - return 1; - } - - // If we are fast backwarding, set to normal - if (fbwd) - { - toggleFastBackwardInt(); - return 1; - } - - // If we are already playing, ignore. no resyncing to do now! - if (playing) - { - *doUnlock = true; - return 1; - } - - // Standard play start - logger->log("Player", Log::DEBUG, "Standard play start"); - - startup = true; - - audio->reset(); - video->reset(); - demuxer->reset(); - if (isRecording) - { - if (startFrameNum > lengthFrames) startFrameNum = 0; - demuxer->setFrameNum(startFrameNum); - } - if (!isRadio) demuxer->seek(); - - videoStartup = true; - threadStart(); - - if (isRecording) - { - logger->log("Player", Log::DEBUG, "Immediate play"); - afeed.start(); - vfeed.start(); - video->sync(); - audio->sync(); - audio->play(); - video->pause(); - } - else // do prebuffering - { - logger->log("Player", Log::DEBUG, "Prebuffering..."); -// afeed.start(); -// vfeed.start(); - preBuffering = true; - } - - playing = true; - return 1; -} - -void Player::stopInt() -{ - if (!initted) return; - if (!playing) return; - - if (ffwd || fbwd) - { - ffwd = false; - fbwd = false; - afeed.enable(); - video->unFastForward(); - audio->systemMuteOff(); - feedMode = MODE_NORMAL; - } - - playing = false; - paused = false; - - vfeed.stop(); - afeed.stop(); - if (threadIsActive()) threadStop(); - video->stop(); - video->blank(); - audio->stop(); - audio->unPause(); - video->reset(); - demuxer->reset(); - - feedPosition = 0; -} - -void Player::togglePauseInt() -{ - if (!initted) return; - if (!playing) return; - - if (ffwd) toggleFastForwardInt(); - if (fbwd) toggleFastBackwardInt(); - - if (paused) - { - video->unPause(); - audio->unPause(); - paused = false; - } - else - { - video->pause(); - audio->pause(); - paused = true; - } -} - -void Player::toggleFastForwardInt() +void Player::switchState(UCHAR toState, ULONG jumpFrame) { if (!initted) return; - if (!playing) return; - if (paused) togglePauseInt(); - if (fbwd) toggleFastBackwardInt(); + logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState); - if (ffwd) + switch(state) // current state selector { - ffwd = false; - threadStop(); - vfeed.stop(); - afeed.stop(); - video->stop(); - audio->stop(); - video->reset(); - audio->reset(); - demuxer->flush(); - if (!isRadio) demuxer->seek(); - - videoStartup = true; - afeed.enable(); - afeed.start(); - vfeed.start(); - threadStart(); - audio->play(); - video->sync(); - audio->sync(); - audio->systemMuteOff(); - audio->doMuting(); - fbwd = false; - } - else - { - ffwd = true; -#ifndef WIN32 - afeed.disable(); -#endif - audio->systemMuteOn(); - video->fastForward(); - } -} - -void Player::toggleFastBackwardInt() -{ - if (!initted) return; - if (!playing) return; - - if (paused) togglePauseInt(); - if (ffwd) toggleFastForwardInt(); - - if (fbwd) - { - fbwd = false; - afeed.enable(); - audio->systemMuteOff(); - -// threadStop(); - feedMode = MODE_NORMAL; -// threadStart(); - } - else - { - fbwd = false; -#ifndef WIN32 - afeed.disable(); -#endif - audio->systemMuteOn(); - - threadStop(); - feedMode = MODE_BACKWARDS; - video->reset(); - video->play(); - demuxer->flush(); - if (!isRadio) demuxer->seek(); - - threadStart(); + case S_PLAY: // from S_PLAY ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + return; + } + case S_PAUSE: // to S_PAUSE + { + video->pause(); + audio->pause(); + state = S_PAUSE; + return; + } + case S_FFWD: // to S_FFWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + threadStop(); + vfeed.stop(); + afeed.stop(); + demuxer->flush(); + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + threadStop(); + vfeed.stop(); + afeed.stop(); + demuxer->flush(); + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + vfeed.stop(); + afeed.stop(); + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + audio->unPause(); + video->reset(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + restartAtFrame(jumpFrame); + return; + } + } + } + case S_PAUSE: // from S_PAUSE ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + if (threadIsActive()) + { + video->unPause(); + audio->unPause(); + state = S_PLAY; + } + else + { + state = S_PLAY; + restartAtFrame(currentFrameNumber); + } + + return; + } + case S_PAUSE: // to S_PAUSE + { + return; + } + case S_FFWD: // to S_FFWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + vfeed.stop(); + afeed.stop(); + if (threadIsActive()) threadStop(); + video->unPause(); + audio->unPause(); + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + vfeed.stop(); + afeed.stop(); + if (threadIsActive()) threadStop(); + video->unPause(); + audio->unPause(); + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + vfeed.stop(); + afeed.stop(); + if (threadIsActive()) threadStop(); + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + audio->unPause(); + demuxer->reset(); + audio->systemMuteOff(); + state = S_FFWD; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + audio->unPause(); + restartAtFrame(jumpFrame); + return; + } + } + } + case S_FFWD: // from S_FFWD ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + state = S_PLAY; + restartAtFrame(currentFrameNumber); + return; + } + case S_PAUSE: // to S_PAUSE + { + threadStop(); + state = S_PAUSE; + return; + } + case S_FFWD: // to S_FFWD + { + return; + } + case S_FBWD: // to S_FBWD + { + threadStop(); + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + restartAtFrame(jumpFrame); + return; + } + } + } + case S_FBWD: // from S_FBWD ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + state = S_PLAY; + restartAtFrame(currentFrameNumber); + return; + } + case S_PAUSE: // to S_PAUSE + { + threadStop(); + state = S_PAUSE; + return; + } + case S_FFWD: // to S_FFWD + { + threadStop(); + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + return; + } + case S_STOP: // to S_STOP + { + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + restartAtFrame(jumpFrame); + return; + } + } + } + case S_STOP: // from S_STOP ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + startup = true; + + audio->reset(); + audio->systemMuteOff(); + video->reset(); + demuxer->reset(); + if (isRecording) + { + // FIXME use restartAtFrame here? + if (currentFrameNumber > lengthFrames) currentFrameNumber = 0; + demuxer->setFrameNum(currentFrameNumber); + } + if (!isRadio) demuxer->seek(); + + videoStartup = true; + state = S_PLAY; + threadStart(); + + if (isRecording) + { + logger->log("Player", Log::DEBUG, "Immediate play"); + afeed.start(); + vfeed.start(); + video->sync(); + audio->sync(); + audio->play(); + video->pause(); + } + else // do prebuffering + { + logger->log("Player", Log::DEBUG, "Prebuffering..."); + preBuffering = true; + } + return; + } + case S_PAUSE: // to S_PAUSE + { + return; + } + case S_FFWD: // to S_FFWD + { + return; + } + case S_FBWD: // to S_FBWD + { + return; + } + case S_STOP: // to S_STOP + { + return; + } + case S_JUMP: // to S_JUMP + { + return; + } + } + } + // case S_JUMP cannot be selected as a start state because it auto flips to play } } -void Player::skipForwardInt(int seconds) -{ - logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); - restartAtFrame(getCurrentFrameNum() + (seconds * video->getFPS())); -} - -void Player::skipBackwardInt(int seconds) -{ - logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); - long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS()); - if (newFrameNum < 0) newFrameNum = 0; - restartAtFrame(newFrameNum); -} - -void Player::jumpToPercentInt(int percent) -{ - logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent); - ULONG newFrame = percent * lengthFrames / 100; - restartAtFrame(newFrame); -} - - // ----------------------------------- Internal functions void Player::lock() @@ -483,13 +543,6 @@ void Player::unLock() void Player::restartAtFrame(ULONG newFrame) { - if (paused) togglePauseInt(); - if (ffwd) toggleFastForwardInt(); - - ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame); - if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; } - logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition); - vfeed.stop(); afeed.stop(); threadStop(); @@ -498,7 +551,7 @@ void Player::restartAtFrame(ULONG newFrame) audio->reset(); demuxer->flush(); if (!isRadio) demuxer->seek(); - feedPosition = newPosition; + currentFrameNumber = newFrame; demuxer->setFrameNum(newFrame); videoStartup = true; afeed.start(); @@ -509,7 +562,6 @@ void Player::restartAtFrame(ULONG newFrame) audio->sync(); audio->systemMuteOff(); audio->doMuting(); - fbwd = false; } void Player::doConnectionLost() @@ -561,6 +613,7 @@ void Player::call(void* caller) video->sync(); vfeed.release(); unLock(); + logger->log("Player", Log::DEBUG, "BANG BANG BANG BANG BANG BANG BANG BANG BANG BANG BANG"); } threadSignalNoLock(); @@ -570,14 +623,57 @@ void Player::call(void* caller) // ----------------------------------- Feed thread void Player::threadMethod() +{ + // this method used to be simple, the only thing it does + // is farm out to threadFeed Live/Play/Scan + // All the guff is to support scan hitting one end + + if (isRecording) + { + if ((state == S_FFWD) || (state == S_FBWD)) + { + threadFeedScan(); + // if this returns then scan hit one end + if (state == S_FFWD) // scan hit the end. stop + { + threadCheckExit(); + Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex + m->message = Message::STOP_PLAYBACK; + logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue); + commandMessageQueue->postMessage(m); + logger->log("Player", Log::DEBUG, "Message posted..."); + return; + } + // if execution gets to here, threadFeedScan hit the start, go to play mode + state = S_PLAY; + audio->reset(); + demuxer->flush(); + if (!isRadio) demuxer->seek(); + demuxer->setFrameNum(currentFrameNumber); + videoStartup = true; + afeed.start(); + vfeed.start(); + audio->play(); + audio->sync(); + audio->systemMuteOff(); + audio->doMuting(); + } + + if (state == S_PLAY) threadFeedPlay(); + } + else + { + threadFeedLive(); + } +} + +void Player::threadFeedLive() { UINT thisRead; UINT writeLength; UINT thisWrite; UINT preBufferTotal = 0; - VDR* vdr = VDR::getInstance(); - UINT askFor; while(1) { @@ -587,44 +683,12 @@ void Player::threadMethod() threadCheckExit(); - // If we havn't rescanned for a while.. - if (isRecording && ((lastRescan + 60) < time(NULL))) - { - lengthBytes = vdr->rescanRecording(&lengthFrames); - if (!vdr->isConnected()) { doConnectionLost(); return; } - logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); - lastRescan = time(NULL); -// setEndTS(); - } - - if (lengthBytes) // is playing a recording - { - if (feedPosition >= lengthBytes) break; // finished playback - - if (startup) - { - if (startupBlockSize > lengthBytes) - askFor = lengthBytes; // is a very small recording! - else - askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams - } - else - { - if ((feedPosition + blockSize) > lengthBytes) // last block of recording - askFor = lengthBytes - feedPosition; - else // normal - askFor = blockSize; - } - } - else // is playing live - { - if (startup) - askFor = startupBlockSize; // find audio streams sized block - else - askFor = blockSize; // normal - } + if (startup) + askFor = startupBlockSize; // find audio streams sized block + else + askFor = blockSize; // normal - threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); + threadBuffer = vdr->getBlock(0, askFor, &thisRead); if (!vdr->isConnected()) { @@ -639,11 +703,6 @@ void Player::threadMethod() int a_stream = demuxer->scan(threadBuffer, thisRead); demuxer->setAudioStream(a_stream); logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); - -// setStartTS(thisRead); - -// if (isRecording) setEndTS(); - startup = false; } @@ -668,22 +727,109 @@ void Player::threadMethod() } } - if (feedMode == MODE_NORMAL) - { - feedPosition += thisRead; - } - else if (feedMode == MODE_BACKWARDS) + threadCheckExit(); + + while(writeLength < thisRead) { - if (feedPosition >= blockSize) + thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); + writeLength += thisWrite; + + if (!thisWrite) { - feedPosition -= blockSize; - if (!isRadio) demuxer->seek(); + // demuxer is full and can't take anymore + threadLock(); + threadWaitForSignal(); + threadUnlock(); } + + threadCheckExit(); + } + + free(threadBuffer); + threadBuffer = NULL; + + } + + logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted"); + + if (videoStartup) // oh woe. there never was a stream, I was conned! + { + videoStartup = false; + unLock(); + MILLISLEEP(500); // I think this will solve a race + } + + threadCheckExit(); + + Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex + m->message = Message::STREAM_END; + logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue); + commandMessageQueue->postMessage(m); + logger->log("Player", Log::DEBUG, "Message posted..."); +} + +void Player::threadFeedPlay() +{ + ULLONG feedPosition; + UINT thisRead, writeLength, thisWrite, askFor; + time_t lastRescan = time(NULL); + + feedPosition = vdr->positionFromFrameNumber(currentFrameNumber); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition); + + + while(1) + { + thisRead = 0; + writeLength = 0; + thisWrite = 0; + + threadCheckExit(); + + // If we havn't rescanned for a while.. + if ((lastRescan + 60) < time(NULL)) + { + lengthBytes = vdr->rescanRecording(&lengthFrames); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); + lastRescan = time(NULL); + } + + if (feedPosition >= lengthBytes) break; // finished playback + + if (startup) + { + if (startupBlockSize > lengthBytes) + askFor = lengthBytes; // is a very small recording! else - { - // got to the start of the recording.. revert to play mode? how? - feedPosition += thisRead; - } + askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams + } + else + { + if ((feedPosition + blockSize) > lengthBytes) // last block of recording + askFor = lengthBytes - feedPosition; + else // normal + askFor = blockSize; + } + + threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); + feedPosition += thisRead; + + if (!vdr->isConnected()) + { + doConnectionLost(); + return; + } + + if (!threadBuffer) break; + + if (startup) + { + int a_stream = demuxer->scan(threadBuffer, thisRead); + demuxer->setAudioStream(a_stream); + logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); + startup = false; } threadCheckExit(); @@ -692,16 +838,13 @@ void Player::threadMethod() { thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); writeLength += thisWrite; -// logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite); if (!thisWrite) { -// logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!"); // demuxer is full and can't take anymore threadLock(); threadWaitForSignal(); threadUnlock(); -// logger->log("Player", Log::DEBUG, "BACK FROM WAIT"); } threadCheckExit(); @@ -725,13 +868,38 @@ void Player::threadMethod() threadCheckExit(); Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex - if (lengthBytes) m->message = Message::STOP_PLAYBACK; // recording - else m->message = Message::STREAM_END; // live + m->message = Message::STOP_PLAYBACK; // recording logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue); commandMessageQueue->postMessage(m); logger->log("Player", Log::DEBUG, "Message posted..."); } +void Player::threadFeedScan() +{ + ULONG direction = 0; + ULONG iframeNumber = 0; + ULONG iframeLength = 0; + ULLONG filePos; + UINT amountReceived; + UINT videoLength; + + if (state == S_FFWD) direction = 1; // and 0 for backward + + while(1) + { + if (!vdr->getNextIFrame(currentFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) break; + threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived); + videoLength = demuxer->stripAudio(threadBuffer, amountReceived); + video->displayIFrame(threadBuffer, videoLength); + currentFrameNumber = iframeNumber; + free(threadBuffer); + threadBuffer = NULL; + threadCheckExit(); + MILLISLEEP(100); + } + // scan has hit one end +} + void Player::threadPostStopCleanup() { if (threadBuffer) diff --git a/player.h b/player.h index 59826bb..c8a1ed0 100644 --- a/player.h +++ b/player.h @@ -52,7 +52,7 @@ class Player : public Thread_TYPE, public Callback void setLengthBytes(ULLONG length); void setLengthFrames(ULONG length); - int play(); + void play(); void stop(); void togglePause(); void toggleFastForward(); @@ -61,14 +61,19 @@ class Player : public Thread_TYPE, public Callback void skipForward(int seconds); void skipBackward(int seconds); - bool isPaused() { return paused; } - bool isFfwd() { return ffwd; } - bool isFbwd() { return fbwd; } + UCHAR getState() { return state; } ULONG getCurrentFrameNum(); ULONG getLengthFrames(); void call(void*); // for callback interface + const static UCHAR S_PLAY = 1; + const static UCHAR S_PAUSE = 2; + const static UCHAR S_FFWD = 3; + const static UCHAR S_FBWD = 4; + const static UCHAR S_STOP = 5; + const static UCHAR S_JUMP = 6; + #ifdef DEV void test1(); void test2(); @@ -79,14 +84,11 @@ class Player : public Thread_TYPE, public Callback void threadPostStopCleanup(); private: - int playInt(bool* doUnlock); - void stopInt(); - void togglePauseInt(); - void toggleFastForwardInt(); - void toggleFastBackwardInt(); - void jumpToPercentInt(int percent); - void skipForwardInt(int seconds); - void skipBackwardInt(int seconds); + void switchState(UCHAR newState, ULONG jumpFrame=0); + + void threadFeedLive(); + void threadFeedPlay(); + void threadFeedScan(); void setEndTS(); void doConnectionLost(); @@ -97,6 +99,7 @@ class Player : public Thread_TYPE, public Callback Audio* audio; Video* video; Demuxer* demuxer; + VDR* vdr; VFeed vfeed; AFeed afeed; @@ -115,22 +118,62 @@ class Player : public Thread_TYPE, public Callback void lock(); void unLock(); - ULONG startFrameNum; ULLONG lengthBytes; ULONG lengthFrames; - ULLONG feedPosition; - UCHAR feedMode; - time_t lastRescan; + ULONG currentFrameNumber; UINT blockSize; UINT startupBlockSize; - const static UCHAR MODE_NORMAL = 1; - const static UCHAR MODE_BACKWARDS = 2; UCHAR* threadBuffer; - - bool playing; // As in not stopped, (playing && paused) can == TRUE - bool paused; // Must be in playing state as well - bool ffwd; // Must be in playing state as well - bool fbwd; // Must be in playing state as well + UCHAR state; }; #endif + + +/* + +Possible states: + +Play, Pause, FFwd, FBwd, (Stop), [Jump] + + Possible Working + +Play -> Pause * * + -> FFwd * * + -> FBwd * * + -> Stop * * + -> Jump * * + + From prev.play / prev.fast + +Pause -> Play * * * + -> FFwd * * * + -> FBwd * * * + -> Stop * * * + -> Jump * * * + +FFwd -> Play * * + -> Pause * * + -> FBwd * * + -> Stop * * + -> Jump * * + +FBwd -> Play * * + -> Pause * * + -> FFwd * * + -> Stop * * + -> Jump * * + +Stop -> Play * * + -> Pause + -> FFwd + -> FBwd + -> Jump + +Jump -> Play + -> Pause + -> FFwd + -> FBwd + -> Stop + +*/ diff --git a/vdr.cc b/vdr.cc index 5a8db29..26860d4 100644 --- a/vdr.cc +++ b/vdr.cc @@ -803,6 +803,55 @@ ULONG VDR::frameNumberFromPosition(ULLONG position) return framenumber; } +bool VDR::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength) +{ + unsigned long totalLength = 16; + UCHAR* buffer = new UCHAR[totalLength]; + + *(unsigned long*)&buffer[0] = htonl(totalLength - 4); + *(unsigned long*)&buffer[4] = htonl(VDR_GETNEXTIFRAME); + *(unsigned long*)&buffer[8] = htonl(frameNumber); + *(unsigned long*)&buffer[12] = htonl(direction); + + MUTEX_LOCK(&mutex); + if (!connected) { MUTEX_UNLOCK(&mutex); return false; } + + unsigned int a = tcp->sendPacket(buffer, totalLength); + delete []buffer; + + if (a != totalLength) + { + disconnect(); + MUTEX_UNLOCK(&mutex); + return false; + } + + if (!getPacket()) + { + MUTEX_UNLOCK(&mutex); + return false; + } + + if (serverError()) + { + Log::getInstance()->log("VDR", Log::DEBUG, "Detected getNextIFrame error"); + freePacket(); + MUTEX_UNLOCK(&mutex); + return false; + } + + *rfilePosition = extractULLONG(); + *rframeNumber = extractULONG(); + *rframeLength = extractULONG(); + + freePacket(); + MUTEX_UNLOCK(&mutex); + + Log::getInstance()->log("VDR", Log::DEBUG, "VDR GNIF said %llu %lu %lu", *rfilePosition, *rframeNumber, *rframeLength); + + return true; +} + EventList* VDR::getChannelSchedule(ULONG number) { time_t now; diff --git a/vdr.h b/vdr.h index de5a97c..c3d9f8a 100644 --- a/vdr.h +++ b/vdr.h @@ -121,6 +121,8 @@ class VDR ULLONG rescanRecording(ULONG* lengthFrames); ULLONG positionFromFrameNumber(ULONG frameNumber); ULONG frameNumberFromPosition(ULLONG position); + bool getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength); + // Direction: 0=backwards, 1=forwards ChannelList* getChannelsList(ULONG type); int streamChannel(ULONG number); @@ -177,6 +179,7 @@ class VDR const static ULONG VDR_POSFROMFRAME = 16; const static ULONG VDR_FRAMEFROMPOS = 17; const static ULONG VDR_MOVERECORDING = 18; + const static ULONG VDR_GETNEXTIFRAME = 19; int getPacket(); void freePacket(); diff --git a/video.h b/video.h index 026aee2..3842175 100644 --- a/video.h +++ b/video.h @@ -64,6 +64,7 @@ class Video: public DrainTarget virtual int attachFrameBuffer()=0; // What does this do? virtual ULONG timecodeToFrameNumber(ULLONG timecode)=0; virtual ULLONG getCurrentTimestamp()=0; + virtual void displayIFrame(const UCHAR* buffer, UINT length)=0; virtual void turnVideoOn(){}; virtual void turnVideoOff(){}; diff --git a/videomvp.cc b/videomvp.cc index 279a134..4e2397d 100644 --- a/videomvp.cc +++ b/videomvp.cc @@ -398,3 +398,8 @@ UINT VideoMVP::DeliverMediaSample(const UCHAR* buffer, UINT* samplepos) void VideoMVP::ResetTimeOffsets() { } + +void VideoMVP::displayIFrame(const UCHAR* buffer, UINT length) +{ + write(fdVideo, buffer, length); +} diff --git a/videomvp.h b/videomvp.h index b8db9c6..bbcdb49 100644 --- a/videomvp.h +++ b/videomvp.h @@ -70,6 +70,7 @@ class VideoMVP : public Video int attachFrameBuffer(); // What does this do? ULONG timecodeToFrameNumber(ULLONG timecode); ULLONG getCurrentTimestamp(); + void displayIFrame(const UCHAR* buffer, UINT length); // Writing Data to Videodevice virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos); diff --git a/vvideorec.cc b/vvideorec.cc index 4b7b271..2b86724 100644 --- a/vvideorec.cc +++ b/vvideorec.cc @@ -187,6 +187,12 @@ int VVideoRec::handleCommand(int command) doBar(0); return 2; } + case Remote::REVERSE: + { + player->toggleFastBackward(); + doBar(0); + return 2; + } case Remote::YELLOW: { doBar(2); @@ -335,10 +341,11 @@ void VVideoRec::doBar(int action) } else { - if (player->isPaused()) w.nextSymbol = WSymbol::PAUSE; - else if (player->isFfwd()) w.nextSymbol = WSymbol::FFWD; - else if (player->isFbwd()) w.nextSymbol = WSymbol::FBWD; - else w.nextSymbol = WSymbol::PLAY; + UCHAR playerState = player->getState(); + if (playerState == Player::S_PAUSE) w.nextSymbol = WSymbol::PAUSE; + else if (playerState == Player::S_FFWD) w.nextSymbol = WSymbol::FFWD; + else if (playerState == Player::S_FBWD) w.nextSymbol = WSymbol::FBWD; + else w.nextSymbol = WSymbol::PLAY; } w.draw(); -- 2.39.2