Ticket #10019: 0008-MythPlayer-Add-support-for-InteractiveTV-streams.patch
File 0008-MythPlayer-Add-support-for-InteractiveTV-streams.patch, 17.8 KB (added by , 13 years ago) |
---|
-
new file mythtv/libs/libmythtv/icringbuffer.cpp
From de6bdfee21eff3516bbb1eb857ca0cbbad106106 Mon Sep 17 00:00:00 2001 From: Lawrence Rust <lvr@softsystem.co.uk> Date: Tue, 19 Jul 2011 14:49:12 +0200 Subject: [PATCH 8/9] MythPlayer: Add support for InteractiveTV streams This patch adds functionality to MythPlayer to enable interactive TV content to select altenative streamed media for display. NB this patch needs to be applied together with that for InteractionChannel streaming. Signed-off-by: Lawrence Rust <lvr@softsystem.co.uk> --- mythtv/libs/libmythtv/icringbuffer.cpp | 107 ++++++++++++++++++++ mythtv/libs/libmythtv/icringbuffer.h | 40 ++++++++ mythtv/libs/libmythtv/interactivetv.cpp | 18 ++-- mythtv/libs/libmythtv/interactivetv.h | 2 + mythtv/libs/libmythtv/libmythtv.pro | 6 +- mythtv/libs/libmythtv/mythplayer.cpp | 165 ++++++++++++++++++++++++++++--- mythtv/libs/libmythtv/mythplayer.h | 7 ++ mythtv/libs/libmythtv/ringbuffer.h | 4 + 8 files changed, 325 insertions(+), 24 deletions(-) create mode 100644 mythtv/libs/libmythtv/icringbuffer.cpp create mode 100644 mythtv/libs/libmythtv/icringbuffer.h diff --git a/mythtv/libs/libmythtv/icringbuffer.cpp b/mythtv/libs/libmythtv/icringbuffer.cpp new file mode 100644 index 0000000..f099de2
- + 1 #include "icringbuffer.h" 2 3 #include <stdio.h> // SEEK_SET 4 5 #include <QScopedPointer> 6 #include <QWriteLocker> 7 8 #include "netstream.h" 9 #include "mythlogging.h" 10 11 12 #define LOC QString("ICRingBuf ") 13 14 15 ICRingBuffer::ICRingBuffer(const QString &url, RingBuffer *parent) 16 : RingBuffer(kRingBufferType), m_stream(0), m_parent(parent) 17 { 18 startreadahead = true; 19 OpenFile(url); 20 } 21 22 ICRingBuffer::~ICRingBuffer() 23 { 24 delete m_stream; 25 delete m_parent; 26 } 27 28 bool ICRingBuffer::IsOpen(void) const 29 { 30 return m_stream ? m_stream->IsOpen() : false; 31 } 32 33 bool ICRingBuffer::OpenFile(const QString &url, uint retry_ms) 34 { 35 if (!NetStream::IsSupported(url)) 36 { 37 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported URL %1").arg(url) ); 38 return false; 39 } 40 41 QScopedPointer<NetStream> stream(new NetStream(url)); 42 if (!stream || !stream->IsOpen()) 43 { 44 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open %1").arg(url) ); 45 return false; 46 } 47 48 if (!stream->WaitTillReady(30000)) 49 { 50 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Stream not ready%1").arg(url) ); 51 return false; 52 } 53 54 if (m_parent) 55 m_parent->Pause(); 56 57 QWriteLocker locker(&rwlock); 58 59 safefilename = url; 60 filename = url; 61 62 delete m_stream; 63 m_stream = stream.take(); 64 65 // The initial bitrate needs to be set with consideration for low bit rate 66 // streams (e.g. radio @ 64Kbps) such that fill_min bytes are received 67 // in a reasonable time period to enable decoders to peek the first few KB 68 // to determine type & settings. 69 rawbitrate = 128; // remotefile 70 CalcReadAheadThresh(); 71 72 locker.unlock(); 73 Reset(true, false, true); 74 75 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened %1").arg(url)); 76 return true; 77 } 78 79 long long ICRingBuffer::GetReadPosition(void) const 80 { 81 return m_stream ? m_stream->GetReadPosition() : 0; 82 } 83 84 long long ICRingBuffer::Seek(long long pos, int whence, bool has_lock) 85 { 86 return m_stream ? (whence == SEEK_SET ? m_stream->Seek(pos) : -1) : -1; 87 } 88 89 int ICRingBuffer::safe_read(void *data, uint sz) 90 { 91 return m_stream ? m_stream->safe_read(data, sz, 10) : (ateof = true, 0); 92 } 93 94 long long ICRingBuffer::GetRealFileSize(void) const 95 { 96 return m_stream ? m_stream->GetSize() : -1; 97 } 98 99 // Take ownership of parent RingBuffer 100 RingBuffer *ICRingBuffer::Take() 101 { 102 RingBuffer *parent = m_parent; 103 m_parent = 0; 104 return parent; 105 } 106 107 // End of file -
new file mythtv/libs/libmythtv/icringbuffer.h
diff --git a/mythtv/libs/libmythtv/icringbuffer.h b/mythtv/libs/libmythtv/icringbuffer.h new file mode 100644 index 0000000..15d4b1c
- + 1 #ifndef ICRINGBUFFER_H 2 #define ICRINGBUFFER_H 3 4 #include "ringbuffer.h" 5 6 class NetStream; 7 8 class ICRingBuffer : public RingBuffer 9 { 10 public: 11 static enum RingBufferType const kRingBufferType = kRingBuffer_MHEG; 12 13 ICRingBuffer(const QString &url, RingBuffer *parent = 0); 14 virtual ~ICRingBuffer(); 15 16 // RingBuffer implementation 17 virtual bool IsOpen(void) const; 18 virtual long long GetReadPosition(void) const; 19 virtual bool OpenFile(const QString &url, 20 uint retry_ms = kDefaultOpenTimeout); 21 virtual long long Seek(long long pos, int whence, bool has_lock); 22 virtual long long GetRealFileSize(void) const; 23 virtual bool IsStreamed(void) { return true; } 24 virtual bool IsSeekingAllowed(void) { return false; } 25 virtual bool IsBookmarkAllowed(void) { return false; } 26 27 protected: 28 virtual int safe_read(void *data, uint sz); 29 30 // Operations 31 public: 32 // Take ownership of parent RingBuffer 33 RingBuffer *Take(); 34 35 private: 36 NetStream *m_stream; 37 RingBuffer *m_parent; // parent RingBuffer 38 }; 39 40 #endif // ICRINGBUFFER_H -
mythtv/libs/libmythtv/interactivetv.cpp
diff --git a/mythtv/libs/libmythtv/interactivetv.cpp b/mythtv/libs/libmythtv/interactivetv.cpp index 2cdbea0..d1efa87 100644
a b InteractiveTV::InteractiveTV(MythPlayer *nvp) 17 17 { 18 18 Restart(0, 0, false); 19 19 20 if (VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_ANY)) 21 { 22 MHSetLogging(stdout, MHLogAll); 23 } 24 else 25 { 26 MHSetLogging(stdout, MHLogError); 27 } 20 MHSetLogging(stdout, 21 VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_DEBUG) ? MHLogAll : 22 VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_ANY) ? 23 MHLogError | MHLogWarning | MHLogNotifications /*| MHLogLinks | MHLogActions | MHLogDetail*/ : 24 MHLogError | MHLogWarning ); 28 25 } 29 26 30 27 InteractiveTV::~InteractiveTV() … … void InteractiveTV::SetNetBootInfo(const unsigned char *data, uint length) 79 76 { 80 77 m_context->SetNetBootInfo(data, length); 81 78 } 79 80 bool InteractiveTV::StreamStarted(bool bStarted) 81 { 82 return m_context->StreamStarted(bStarted); 83 } -
mythtv/libs/libmythtv/interactivetv.h
diff --git a/mythtv/libs/libmythtv/interactivetv.h b/mythtv/libs/libmythtv/interactivetv.h index c8e00e6..ba426de 100644
a b class InteractiveTV 39 39 40 40 // Get the initial component tags. 41 41 void GetInitialStreams(int &audioTag, int &videoTag); 42 // Called when a stream starts or stops. Returns true if event is handled 43 bool StreamStarted(bool bStarted = true); 42 44 43 45 MythPlayer *GetNVP(void) { return m_nvp; } 44 46 -
mythtv/libs/libmythtv/libmythtv.pro
diff --git a/mythtv/libs/libmythtv/libmythtv.pro b/mythtv/libs/libmythtv/libmythtv.pro index a67539b..8e40e03 100644
a b HEADERS += mythsystemevent.h 164 164 HEADERS += avfringbuffer.h ThreadedFileWriter.h 165 165 HEADERS += ringbuffer.h fileringbuffer.h 166 166 HEADERS += dvdringbuffer.h bdringbuffer.h 167 HEADERS += streamingringbuffer.h metadataimagehelper.h 167 HEADERS += streamingringbuffer.h icringbuffer.h 168 HEADERS += metadataimagehelper.h 168 169 169 170 SOURCES += recordinginfo.cpp 170 171 SOURCES += dbcheck.cpp … … SOURCES += mythsystemevent.cpp 192 193 SOURCES += avfringbuffer.cpp ThreadedFileWriter.cpp 193 194 SOURCES += ringbuffer.cpp fileringBuffer.cpp 194 195 SOURCES += dvdringbuffer.cpp bdringbuffer.cpp 195 SOURCES += streamingringbuffer.cpp metadataimagehelper.cpp 196 SOURCES += streamingringbuffer.cpp icringbuffer.cpp 197 SOURCES += metadataimagehelper.cpp 196 198 197 199 # DiSEqC 198 200 HEADERS += diseqc.h diseqcsettings.h -
mythtv/libs/libmythtv/mythplayer.cpp
diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp index 3e2966c..0523d77 100644
a b using namespace std; 26 26 #include <QCoreApplication> 27 27 #include <QKeyEvent> 28 28 #include <QDir> 29 #include <QScopedPointer> 29 30 30 31 // MythTV headers 31 32 #include "mthread.h" … … using namespace std; 59 60 #include "mythimage.h" 60 61 #include "mythuiimage.h" 61 62 #include "mythlogging.h" 63 #include "icringbuffer.h" 62 64 63 65 extern "C" { 64 66 #include "vbitext/vbi.h" … … int MythPlayer::OpenFile(uint retries, bool allow_libmpeg2) 916 918 MythTimer peekTimer; peekTimer.start(); 917 919 while (player_ctx->buffer->Peek(testbuf, testreadsize) != testreadsize) 918 920 { 919 if (peekTimer.elapsed() > 1000 || bigTimer.elapsed() > timeout) 921 // NB need to allow for streams encountering network congestion 922 if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout) 920 923 { 921 924 LOG(VB_GENERAL, LOG_ERR, LOC + 922 925 QString("OpenFile(): Could not read first %1 bytes of '%2'") … … int MythPlayer::OpenFile(uint retries, bool allow_libmpeg2) 926 929 return -1; 927 930 } 928 931 LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data"); 929 usleep( 50 * 1000);932 usleep(150 * 1000); 930 933 } 931 934 932 935 player_ctx->LockPlayingInfo(__FILE__, __LINE__); … … void MythPlayer::DisplayPauseFrame(void) 1937 1940 SetBuffering(false); 1938 1941 1939 1942 RefreshPauseFrame(); 1943 PreProcessNormalFrame(); // Allow interactiveTV to draw on pause frame 1940 1944 1941 1945 osdLock.lock(); 1942 1946 videofiltersLock.lock(); … … bool MythPlayer::PrebufferEnoughFrames(int min_buffers) 2001 2005 // to recover from serious problems if frames get leaked. 2002 2006 DiscardVideoFrames(true); 2003 2007 } 2004 if (waited_for > 20000) // 20 seconds2008 if (waited_for > 30000) // 30 seconds for internet streamed media 2005 2009 { 2006 2010 LOG(VB_GENERAL, LOG_ERR, LOC + 2007 2011 "Waited too long for decoder to fill video buffers. Exiting.."); … … void MythPlayer::SwitchToProgram(void) 2330 2334 return; 2331 2335 } 2332 2336 2337 if (player_ctx->buffer->GetType() == ICRingBuffer::kRingBufferType) 2338 { 2339 // Restore original ringbuffer 2340 ICRingBuffer *ic = dynamic_cast< ICRingBuffer* >(player_ctx->buffer); 2341 player_ctx->buffer = ic->Take(); 2342 delete ic; 2343 } 2344 2333 2345 player_ctx->buffer->OpenFile( 2334 2346 pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout); 2335 2347 … … void MythPlayer::JumpToProgram(void) 2437 2449 2438 2450 SendMythSystemPlayEvent("PLAY_CHANGED", pginfo); 2439 2451 2452 if (player_ctx->buffer->GetType() == ICRingBuffer::kRingBufferType) 2453 { 2454 // Restore original ringbuffer 2455 ICRingBuffer *ic = dynamic_cast< ICRingBuffer* >(player_ctx->buffer); 2456 player_ctx->buffer = ic->Take(); 2457 delete ic; 2458 } 2459 2440 2460 player_ctx->buffer->OpenFile( 2441 2461 pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout); 2442 2462 … … void MythPlayer::EventLoop(void) 2623 2643 JumpToProgram(); 2624 2644 } 2625 2645 2646 // Change interactive stream if requested 2647 { QMutexLocker locker(&itvLock); 2648 if (!m_newStream.isEmpty()) 2649 { 2650 QString stream = m_newStream; 2651 m_newStream.clear(); 2652 locker.unlock(); 2653 JumpToStream(stream); 2654 }} 2655 2626 2656 // Disable fastforward if we are too close to the end of the buffer 2627 2657 if (ffrew_skip > 1 && (CalcMaxFFTime(100, false) < 100)) 2628 2658 { … … void MythPlayer::EventLoop(void) 2659 2689 } 2660 2690 2661 2691 // Handle end of file 2662 if (GetEof() )2692 if (GetEof() && !allpaused) 2663 2693 { 2664 if ( player_ctx->tvchain)2694 if (interactiveTV && interactiveTV->StreamStarted(false)) 2665 2695 { 2666 if (!allpaused && player_ctx->tvchain->HasNext()) 2667 { 2668 LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1"); 2669 player_ctx->tvchain->JumpToNext(true, 1); 2670 return; 2671 } 2696 Pause(); 2697 return; 2672 2698 } 2673 else if (!allpaused) 2699 2700 if (player_ctx->tvchain && player_ctx->tvchain->HasNext()) 2674 2701 { 2675 SetPlaying(false); 2702 LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1"); 2703 player_ctx->tvchain->JumpToNext(true, 1); 2676 2704 return; 2677 2705 } 2706 2707 SetPlaying(false); 2708 return; 2678 2709 } 2679 2710 2680 2711 // Handle rewind … … void MythPlayer::UnpauseDecoder(void) 2805 2836 2806 2837 int tries = 0; 2807 2838 unpauseDecoder = true; 2808 while (decoder Thread && !killdecoder && (tries++ < 100) &&2839 while (decoderPaused && decoderThread && !killdecoder && (tries++ < 10) && 2809 2840 !decoderThreadUnpause.wait(&decoderPauseLock, 100)) 2810 2841 { 2811 2842 LOG(VB_GENERAL, LOG_WARNING, LOC + … … bool MythPlayer::SetVideoByComponentTag(int tag) 4641 4672 return false; 4642 4673 } 4643 4674 4675 // Called from MHIContext::Begin/End/Stream on the MHIContext::StartMHEGEngine thread 4676 bool MythPlayer::SetStream(const QString &stream) 4677 { 4678 // The stream name is empty if the stream is closing 4679 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStream '%1'").arg(stream)); 4680 4681 QMutexLocker locker(&itvLock); 4682 m_newStream = stream; 4683 m_newStream.detach(); 4684 // Stream will be changed by JumpToStream called from EventLoop 4685 // If successful will call interactiveTV->StreamStarted(); 4686 return !stream.isEmpty(); 4687 } 4688 4689 // Called from EventLoop pn the main application thread 4690 void MythPlayer::JumpToStream(const QString &stream) 4691 { 4692 LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - begin"); 4693 4694 if (stream.isEmpty()) 4695 return; // Shouldn't happen 4696 4697 Pause(); 4698 ResetCaptions(); 4699 4700 if (player_ctx->buffer->GetType() != ICRingBuffer::kRingBufferType) 4701 player_ctx->buffer = new ICRingBuffer(stream, player_ctx->buffer); 4702 else 4703 player_ctx->buffer->OpenFile(stream); 4704 4705 if (!player_ctx->buffer->IsOpen()) 4706 { 4707 LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream buffer OpenFile failed"); 4708 SetEof(true); 4709 SetErrored(QObject::tr("Error opening remote stream buffer")); 4710 return; 4711 } 4712 4713 if (OpenFile(120) < 0) // 120 retries ~= 60 seconds 4714 { 4715 LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream OpenFile failed."); 4716 SetEof(true); 4717 SetErrored(QObject::tr("Error opening remote stream")); 4718 return; 4719 } 4720 4721 SetEof(false); 4722 4723 if (player_ctx->tvchain) CheckTVChain(); 4724 //player_ctx->buffer->IgnoreLiveEOF(false); 4725 4726 Play(); 4727 ChangeSpeed(); 4728 4729 player_ctx->SetPlayerChangingBuffers(false); 4730 if (interactiveTV) interactiveTV->StreamStarted(); 4731 4732 LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - end"); 4733 } 4734 4735 static inline int SafeFPS(DecoderBase *decoder) 4736 { 4737 if (!decoder) 4738 return 25; 4739 double fps = decoder->GetFPS(); 4740 return fps > 0 ? (int)(fps + 0.5) : 25; 4741 } 4742 4743 long MythPlayer::GetStreamPos() 4744 { 4745 return (1000L * GetFramesPlayed()) / SafeFPS(decoder); 4746 } 4747 4748 long MythPlayer::GetStreamMaxPos() 4749 { 4750 uint kbps = decoder ? decoder->GetRawBitrate() : 0; 4751 long pos = GetStreamPos(), maxpos = 0; 4752 long long len = player_ctx->buffer->GetRealFileSize(); 4753 if (len > 0) 4754 { 4755 const uint kMin = 64; 4756 maxpos = (long)((8 * len) / (kbps < kMin ? kMin : kbps)); 4757 } 4758 4759 if (maxpos < pos) 4760 maxpos = pos; 4761 LOG(VB_PLAYBACK, LOG_INFO, LOC + 4762 QString("GetStreamMaxPos => %1 mS (%2 KB @ %3 KBPS)") 4763 .arg(maxpos).arg(len > 0 ? len/1024 : len).arg((kbps+4)/8) ); 4764 return maxpos; 4765 } 4766 4767 long MythPlayer::SetStreamPos(long ms) 4768 { 4769 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStreamPos %1").arg(ms)); 4770 return JumpToFrame((uint64_t)((ms * SafeFPS(decoder)) / 1000)); 4771 } 4772 4773 void MythPlayer::StreamPlay(bool play) 4774 { 4775 if (play) 4776 Play(); 4777 else 4778 Pause(); 4779 } 4780 4644 4781 /** \fn MythPlayer::SetDecoder(DecoderBase*) 4645 4782 * \brief Sets the stream decoder, deleting any existing recorder. 4646 4783 */ -
mythtv/libs/libmythtv/mythplayer.h
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h index daeb4dc..e4eae0b 100644
a b class MTV_PUBLIC MythPlayer 259 259 // Public MHEG/MHI stream selection 260 260 bool SetAudioByComponentTag(int tag); 261 261 bool SetVideoByComponentTag(int tag); 262 bool SetStream(const QString &); 263 long GetStreamPos(); // mS 264 long GetStreamMaxPos(); // mS 265 long SetStreamPos(long); // mS 266 void StreamPlay(bool play = true); 262 267 263 268 // LiveTV public stuff 264 269 void CheckTVChain(); … … class MTV_PUBLIC MythPlayer 524 529 // Private LiveTV stuff 525 530 void SwitchToProgram(void); 526 531 void JumpToProgram(void); 532 void JumpToStream(const QString&); 527 533 528 534 void calcSliderPosPriv(osdInfo &info, bool paddedFields, 529 535 int playbackLen, float secsplayed, bool islive); … … class MTV_PUBLIC MythPlayer 660 666 InteractiveTV *interactiveTV; 661 667 bool itvEnabled; 662 668 QMutex itvLock; 669 QString m_newStream; // Guarded by itvLock 663 670 664 671 // OSD stuff 665 672 OSD *osd; -
mythtv/libs/libmythtv/ringbuffer.h
diff --git a/mythtv/libs/libmythtv/ringbuffer.h b/mythtv/libs/libmythtv/ringbuffer.h index 8dc633d..08a007c 100644
a b enum RingBufferType 38 38 kRingBuffer_DVD, 39 39 kRingBuffer_BD, 40 40 kRingBuffer_HTTP, 41 kRingBuffer_MHEG 41 42 }; 42 43 43 44 class MTV_PUBLIC RingBuffer : protected MThread 44 45 { 46 friend class ICRingBuffer; 47 45 48 public: 46 49 static RingBuffer *Create(const QString &lfilename, bool write, 47 50 bool usereadahead = true, … … class MTV_PUBLIC RingBuffer : protected MThread 84 87 virtual bool IsBookmarkAllowed(void) { return true; } 85 88 virtual int BestBufferSize(void) { return 32768; } 86 89 static QString BitrateToString(uint64_t rate); 90 RingBufferType GetType() const { return type; } 87 91 88 92 // DVD and bluray methods 89 93 bool IsDisc(void) const { return IsDVD() || IsBD(); }