From 508ba3458094f0c30b278cc05abc626b6f3940e0 Mon Sep 17 00:00:00 2001 From: chriszero Date: Sun, 11 Jan 2015 15:14:03 +0100 Subject: Removed Mplayer support. Added HLS Streaming Player for directly play in VDR --- Makefile | 10 +- PVideo.cpp | 1 + PVideo.h | 1 + PlexHTTPRequestHandler.cpp | 6 +- Plexservice.cpp | 148 ++++++- Plexservice.h | 32 +- README.md | 7 + SubscriptionManager.cpp | 3 +- SubscriptionManager.h | 2 - hlsPlayer.cpp | 326 +++++++++++++++ hlsPlayer.h | 90 +++++ hlsPlayerControl.cpp | 24 ++ hlsPlayerControl.h | 27 ++ m3u8Parser.cpp | 121 ++++++ m3u8Parser.h | 37 ++ player.c | 984 --------------------------------------------- player.h | 160 -------- plex.cpp | 803 +----------------------------------- plex.h | 5 +- video.c | 775 ----------------------------------- video.h | 62 --- 21 files changed, 812 insertions(+), 2812 deletions(-) create mode 100644 hlsPlayer.cpp create mode 100644 hlsPlayer.h create mode 100644 hlsPlayerControl.cpp create mode 100644 hlsPlayerControl.h create mode 100644 m3u8Parser.cpp create mode 100644 m3u8Parser.h delete mode 100644 player.c delete mode 100644 player.h delete mode 100644 video.c delete mode 100644 video.h diff --git a/Makefile b/Makefile index 551c4c7..30abf93 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ PLUGIN = plex -LIBS += -lPocoUtil -lPocoNet -lPocoNetSSL -lPocoXML -lPocoFoundation +LIBS += -lPocoUtil -lPocoNet -lPocoNetSSL -lPocoXML -lPocoFoundation -lpcrecpp ### Configuration (edit this for your needs) @@ -17,11 +17,11 @@ LIBS += -lPocoUtil -lPocoNet -lPocoNetSSL -lPocoXML -lPocoFoundation # FIXME: AVFS isn't working, corrupts memory #AVFS ?= $(shell test -x /usr/bin/avfs-config && echo 1) # use ffmpeg libswscale -SWSCALE ?= $(shell pkg-config --exists libswscale && echo 1) +#SWSCALE ?= $(shell pkg-config --exists libswscale && echo 1) # support png images -PNG ?= $(shell pkg-config --exists libpng && echo 1) +#PNG ?= $(shell pkg-config --exists libpng && echo 1) # support jpg images -JPG ?= $(shell test -r /usr/include/jpeglib.h && echo 1) +#JPG ?= $(shell test -r /usr/include/jpeglib.h && echo 1) CONFIG := #-DDEBUG # uncomment to build DEBUG @@ -49,7 +49,7 @@ endif _CFLAGS += $(shell pkg-config --cflags xcb xcb-image xcb-keysyms xcb-icccm) -LIBS += -lrt $(shell pkg-config --libs xcb xcb-image xcb-keysyms xcb-icccm) +#LIBS += -lrt $(shell pkg-config --libs xcb xcb-image xcb-keysyms xcb-icccm) ### The version number of this plugin (taken from the main source file): diff --git a/PVideo.cpp b/PVideo.cpp index b22bb66..21a7de6 100644 --- a/PVideo.cpp +++ b/PVideo.cpp @@ -15,6 +15,7 @@ Video::Video(Poco::XML::Node* pNode) Poco::XML::AutoPtr pAttribs = pNode->attributes(); m_iRatingKey = GetNodeValueAsInt(pAttribs->getNamedItem("ratingKey")); + m_sKey = GetNodeValue(pAttribs->getNamedItem("key")); m_sStudio = GetNodeValue(pAttribs->getNamedItem("studio")); m_tType = GetNodeValueAsMediaType(pAttribs->getNamedItem("type")); m_sTitle = GetNodeValue(pAttribs->getNamedItem("title")); diff --git a/PVideo.h b/PVideo.h index ae61da4..3d69bc4 100644 --- a/PVideo.h +++ b/PVideo.h @@ -37,6 +37,7 @@ public: public: int m_iRatingKey; + std::string m_sKey; std::string m_sStudio; MediaType m_tType; std::string m_sTitle; diff --git a/PlexHTTPRequestHandler.cpp b/PlexHTTPRequestHandler.cpp index f18001c..034a739 100644 --- a/PlexHTTPRequestHandler.cpp +++ b/PlexHTTPRequestHandler.cpp @@ -128,15 +128,15 @@ void PlayerRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, } else if(request.getURI().find("/playback/playMedia")!= std::string::npos) { - std::cout << "playMedia_1" << std::endl; + //std::cout << "playMedia_1" << std::endl; AddHeaders(response, request); - std::cout << "playMedia_2" << std::endl; + //std::cout << "playMedia_2" << std::endl; std::string protocol = query["protocol"]; std::string address = query["address"]; std::string port = query["port"]; std::string key = query["key"]; - std::cout << "playMedia_3" << std::endl; + //std::cout << "playMedia_3" << std::endl; std::string fullUrl = protocol + "://" + address + ":" + port + key; // Metainfo std::cout << "FullUrl: " << fullUrl << std::endl; diff --git a/Plexservice.cpp b/Plexservice.cpp index 03775f2..f6d2db9 100644 --- a/Plexservice.cpp +++ b/Plexservice.cpp @@ -1,5 +1,4 @@ #include "Plexservice.h" -#include namespace plexclient { @@ -27,8 +26,7 @@ Poco::Net::HTTPClientSession* Plexservice::GetHttpSession(bool createNew) m_pPlexSession = new Poco::Net::HTTPClientSession(pServer->GetIpAdress(), pServer->GetPort()); m_pPlexSession->setKeepAlive(true); } - } - catch(Poco::Exception &exc) { + } catch(Poco::Exception &exc) { esyslog("[plex]Exception in %s s%", __func__, exc.displayText().c_str() ); m_pPlexSession = 0; } @@ -39,7 +37,7 @@ std::string Plexservice::GetMyPlexToken() { // Syncronize Poco::Mutex::ScopedLock lock(m_mutex); - + //todo: cache token in file or db if(&m_sToken != 0 || m_sToken.empty()) { std::stringstream ss; @@ -98,8 +96,7 @@ void Plexservice::Authenticate() // TODO: process response //Poco::StreamCopier::copyStream(rs, std::cout); delete pRequest; - } - else { + } else { esyslog("[plex] %s HttpSession is NULL", __func__); } } catch (Poco::Exception &exc) { @@ -119,7 +116,8 @@ void Plexservice::DiscoverAllServers() } */ -PlexServer* Plexservice::GetServer() { +PlexServer* Plexservice::GetServer() +{ return pServer; } @@ -147,14 +145,13 @@ MediaContainer* Plexservice::GetSection(std::string section) std::istream &rs = m_pPlexSession->receiveResponse(response); dsyslog("[plex] URI: %s[s%]", m_pPlexSession->getHost().c_str(), pRequest->getURI().c_str()); - + MediaContainer* pAllsections = new MediaContainer(&rs); //Poco::StreamCopier::copyStream(rs, std::cout); delete pRequest; return pAllsections; - - } - else { + + } else { esyslog("[plex] %s HttpSession is NULL", __func__); return 0; } @@ -181,10 +178,11 @@ Poco::Net::HTTPRequest* Plexservice::CreateRequest(std::string path) return pRequest; } -MediaContainer* Plexservice::GetMediaContainer(std::string fullUrl) { - +MediaContainer* Plexservice::GetMediaContainer(std::string fullUrl) +{ + Poco::URI fileuri(fullUrl); - + Poco::Net::HTTPRequest* pRequest = new Poco::Net::HTTPRequest(Poco::Net::HTTPRequest::HTTP_GET, fileuri.getPath(), Poco::Net::HTTPMessage::HTTP_1_1); @@ -200,22 +198,136 @@ MediaContainer* Plexservice::GetMediaContainer(std::string fullUrl) { pRequest->add("X-Plex-Product", "plex for vdr"); pRequest->add("X-Plex-Provides", "player"); pRequest->add("X-Plex-Version", "0.0.1a"); - + Poco::Net::HTTPClientSession* session = new Poco::Net::HTTPClientSession(fileuri.getHost(), fileuri.getPort()); - + session->sendRequest(*pRequest); Poco::Net::HTTPResponse response; std::istream &rs = session->receiveResponse(response); std::cout << "URI: " << session->getHost() << "[" << pRequest->getURI() << "]" << std::endl; - + delete pRequest; delete session; - + MediaContainer* pAllsections = new MediaContainer(&rs); //Poco::StreamCopier::copyStream(rs, std::cout); return pAllsections; } +#ifdef CRYPTOPP +std::string Plexservice::computeHMAC(std::string message) +{ + using CryptoPP::Exception; + using CryptoPP::HMAC; + using CryptoPP::SHA256; + using CryptoPP::Base64Encoder; + using CryptoPP::Base64Decoder; + using CryptoPP::StringSink; + using CryptoPP::ArraySink; + using CryptoPP::StringSource; + using CryptoPP::HashFilter; + + const std::string transcode_private = "k3U6GLkZOoNIoSgjDshPErvqMIFdE0xMTx8kgsrhnC0="; + + std::string encoded; + byte key[32]; + + StringSource(transcode_private, true, + new Base64Decoder( + new ArraySink(key, 32) + ) // Base64Encoder + ); // StringSource + + try { + HMAC hmac((byte*)key, sizeof(key)); + + StringSource(message, true, + new HashFilter(hmac, + new Base64Encoder( + new StringSink(encoded), + false + ) // Base64Encoder + ) // HashFilter + ); // StringSource + } catch(const CryptoPP::Exception& e) { + std::cerr << e.what() << std::endl; + } + return encoded; +} + +std::string Plexservice::GetTranscodeUrl(Video* video) +{ + const std::string transcodeURL = "/video/:/transcode/segmented/start.m3u8?"; + const std::string transcode_public = "KQMIY6GATPC63AIMC4R2"; + + std::stringstream params; + params << "identifier=com.plexapp.plugins.library"; + params << "&url=" << encode(pServer->GetUri() + video->m_pMedia->m_sPartKey); + params << "&quality=8"; + params << "&ratingKey=" << video->m_iRatingKey; + params << "&3g=0"; + params << "&offset=0"; + params << "&directStream=1"; + params << "&maxVideoBitrate=30000"; + + int time = std::time(0); + std::string message = Poco::format("%s@%d", transcodeURL + params.str(), time); + + std::string b64hmacMessage = computeHMAC(message); + + std::stringstream plexAccess; + plexAccess << "&X-Plex-Access-Key=" << encode(transcode_public); + plexAccess << "&X-Plex-Access-Time=" << Poco::format("%d", time); + plexAccess << "&X-Plex-Access-Code=" << encode(b64hmacMessage); + plexAccess << "&X-Plex-Client-Capabilities="; + plexAccess << encode("protocols=http-live-streaming,http-streaming-video-720p,http-streaming-video-1080p;"); + plexAccess << encode("videoDecoders=h264{profile:high&resolution:1080&level:51};"); + plexAccess << encode("audioDecoders=aac,ac3{channels:6}"); + + std::string fullQuery = params.str() + plexAccess.str(); + return pServer->GetUri() + transcodeURL + fullQuery; +} +#endif + +std::string Plexservice::encode(std::string message) +{ + std::string temp; + Poco::URI::encode(message, " !\"#$%&'()*+,/:;=?@[]", temp); + return temp; +} + + +std::string Plexservice::GetUniversalTranscodeUrl(Video* video) +{ + std::stringstream params; + params << "/video/:/transcode/universal/start.m3u8?"; + params << "path=" << encode(pServer->GetUri() + video->m_sKey); + params << "&mediaIndex=0"; + params << "&partIndex=0"; + params << "&protocol=hls"; + params << "&offset=0"; + params << "&fastSeek=1"; + params << "&directPlay=0"; + params << "&directStream=1"; + params << "&maxVideoBitrate=20000"; + //params << "&subtitleSize=90"; + params << "&skipSubtitles=1"; + //params << "&audioBoost=100"; + params << "&videoResolution=1920x1080"; + params << "&videoQuality=100"; + params << "&session=" << encode(Config::GetInstance().GetUUID()); // TODO: generate Random SessionID + + params << "&X-Plex-Client-Identifier=" << encode(Config::GetInstance().GetUUID()); + params << "&X-Plex-Product=Plex%20Home%20Theater"; + params << "&X-Plex-Device=PC"; + params << "&X-Plex-Platform=Plex%20Home%20Theater"; + params << "&X-Plex-Model=Linux"; + //params << "&X-Plex-Platform-Version=7"; + //params << "&X-Plex-Version=1.2.12"; + //params << "&X-Plex-Device-Name=" << "Plex%2FWeb%20(Chrome)"; + + return pServer->GetUri() + params.str(); } +} diff --git a/Plexservice.h b/Plexservice.h index 456341b..ba16ccf 100644 --- a/Plexservice.h +++ b/Plexservice.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -21,6 +23,9 @@ #include #include #include +#include +#include +#include #include @@ -28,8 +33,14 @@ #include "user.h" #include "MediaContainer.h" -#include -#include +#ifdef CRYPTOPP +#include +#include +#include +#include +#include +#include +#endif namespace plexclient { @@ -48,14 +59,18 @@ public: void Authenticate(); //void DiscoverFirstServer(); PlexServer* GetServer(); - + std::string GetUniversalTranscodeUrl(Video* video); + static MediaContainer* GetMediaContainer(std::string fullUrl); - private: +protected: + std::string encode(std::string message); + +private: Poco::Mutex m_mutex; // Never Access m_sToken directly! => possible race condition std::string m_sToken; - + std::string USERAGENT; Poco::Net::HTTPClientSession *m_pPlexSession; @@ -64,6 +79,13 @@ public: Poco::Net::HTTPClientSession* GetHttpSession(bool createNew = false); Poco::Net::HTTPRequest* CreateRequest(std::string path); +#ifdef CRYPTOPP +protected: + std::string computeHMAC(std::string message); +public: + std::string GetTranscodeUrl(Video* video); +#endif + }; } diff --git a/README.md b/README.md index 99e49ec..650b786 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ vdr-plugin-plex =============== +ALPHA!! May not work + A Plex plugin for Linux VDR +Plays files via the Plexmediaserver transcoder directly in VDR + +deps: +libpoco +libpcrepp diff --git a/SubscriptionManager.cpp b/SubscriptionManager.cpp index c3e97b6..c4589ef 100644 --- a/SubscriptionManager.cpp +++ b/SubscriptionManager.cpp @@ -45,8 +45,7 @@ void SubscriptionManager::RemoveSubscriber(std::string uuid) { } std::string SubscriptionManager::GetMsg(std::string commandId) { - PlayerGetCurrentPosition(); - int time = PlayerCurrent; + int time = 0;//PlayerGetCurrentPosition(); //bool paused = PlayerPaused; diff --git a/SubscriptionManager.h b/SubscriptionManager.h index 44ec899..b3f7f33 100644 --- a/SubscriptionManager.h +++ b/SubscriptionManager.h @@ -8,8 +8,6 @@ //#include #include -#include "player.h" - namespace plexclient { diff --git a/hlsPlayer.cpp b/hlsPlayer.cpp new file mode 100644 index 0000000..5fa7b0e --- /dev/null +++ b/hlsPlayer.cpp @@ -0,0 +1,326 @@ +#include "hlsPlayer.h" + +#include +#include +#include + +#include + +//--- cHlsSegmentLoader + +cHlsSegmentLoader::cHlsSegmentLoader(std::string startm3u8) +{ + m_bufferFilled = false; + m_lastLoadedSegment = 0; + m_segmentsToBuffer = 3; + m_pBuffer = new uchar[8192]; + + // Initialize members + m_pClientSession = NULL; + + m_ringBufferSize = MEGABYTE(32); + m_pRingbuffer = NULL; + + m_startUri = Poco::URI(startm3u8); + m_startParser = cM3u8Parser(); + m_indexParser = cM3u8Parser(); +} + +cHlsSegmentLoader::~cHlsSegmentLoader() +{ + // Stop Thread + Cancel(0); + + delete m_pClientSession; + delete[] m_pBuffer; + delete m_pRingbuffer; +} + +void cHlsSegmentLoader::Action(void) +{ + LoadStartList(); + LoadIndexList(); + + int estSize = EstimateSegmentSize(); + m_ringBufferSize = MEGABYTE(estSize*3); + + std::cout << "Create Ringbuffer " << estSize*3 << "MB" << std::endl; + + m_pRingbuffer = new cRingBufferLinear(m_ringBufferSize, 2*TS_SIZE); + + while(Running()) { + DoLoad(); + cCondWait::SleepMs(3); + } +} + +void cHlsSegmentLoader::LoadIndexList(void) +{ + if(m_startParser.MasterPlaylist && m_startParser.vPlaylistItems.size() > 0) { + // Todo: make it universal, might only work for Plexmediaserver + ConnectToServer(); + + std::string startUri = m_startUri.toString(); + startUri = startUri.substr(0, startUri.find_last_of("/")+1); + + Poco::URI indexUri(startUri+m_startParser.vPlaylistItems[0].file); + + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, indexUri.getPath()); + // same server + m_pClientSession->sendRequest(request); + + Poco::Net::HTTPResponse responseStart; + std::istream& indexFile = m_pClientSession->receiveResponse(responseStart); + + if(responseStart.getStatus() != 200) { + // error + return; + } + + m_indexParser.Parse(indexFile); + + // Segment URI is relative to index.m3u8 + std::string path = indexUri.getPath(); + m_segmentUriPart = path.substr(0, path.find_last_of("/")+1); + } +} + +void cHlsSegmentLoader::LoadStartList(void) +{ + ConnectToServer(); + + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, m_startUri.getPathAndQuery()); + m_pClientSession->sendRequest(request); + + Poco::Net::HTTPResponse responseStart; + std::istream& startFile = m_pClientSession->receiveResponse(responseStart); + + if(responseStart.getStatus() != 200) { + // error + return; + } + + m_startParser.Parse(startFile); + + pcrecpp::RE re("([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})", pcrecpp::RE_Options(PCRE_CASELESS)); + string value; + re.PartialMatch(m_startParser.vPlaylistItems[0].file, &value); + m_sessionCookie = value; +} + +int cHlsSegmentLoader::EstimateSegmentSize() +{ + double bandw = m_startParser.vPlaylistItems[0].bandwidth / 8.0 / 1000.0 / 1000.0; + + int len = m_indexParser.TargetDuration; + double estSize = (bandw) * len; + estSize = max(estSize, 16.0); // default + return ceil(estSize); +} + +bool cHlsSegmentLoader::LoadSegment(std::string segmentUri) +{ + std::cout << "Loading Segment: " << segmentUri << "... "; + Poco::Net::HTTPRequest segmentRequest(Poco::Net::HTTPRequest::HTTP_GET, segmentUri); + m_pClientSession->sendRequest(segmentRequest); + + Poco::Net::HTTPResponse segmentResponse; + std::istream& segmentFile = m_pClientSession->receiveResponse(segmentResponse); + + if(segmentResponse.getStatus() != 200) { + // error + std::cout << "failed." << std::endl; + return false; + } + std::cout << "successfully." << std::endl; + + // copy response + + int m = 0; + segmentFile.read(reinterpret_cast(m_pBuffer), sizeof(m_pBuffer)); + std::streamsize n = segmentFile.gcount(); + while(n > 0) { + m = m_pRingbuffer->Put(m_pBuffer, n); + if(m < n) { + // oops, this should not happen. Data doesn't fitted completly into ringbuffer + break; + } else { + segmentFile.read(reinterpret_cast(m_pBuffer), sizeof(m_pBuffer)); + n = segmentFile.gcount(); + } + } + return true; +} + +int cHlsSegmentLoader::GetSegmentSize(int segmentIndex) +{ + if(m_indexParser.vPlaylistItems[segmentIndex].size > 0) { + return m_indexParser.vPlaylistItems[segmentIndex].size; + } + Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_HEAD, GetSegmentUri(segmentIndex)); + m_pClientSession->sendRequest(req); + Poco::Net::HTTPResponse reqResponse; + m_pClientSession->receiveResponse(reqResponse); + + return m_indexParser.vPlaylistItems[segmentIndex].size = reqResponse.getContentLength(); +} + +std::string cHlsSegmentLoader::GetSegmentUri(int segmentIndex) const +{ + return m_segmentUriPart + m_indexParser.vPlaylistItems[segmentIndex].file; +} + +void cHlsSegmentLoader::CloseConnection(void) +{ + if(m_pClientSession) + m_pClientSession->abort(); + + delete m_pClientSession; + m_pClientSession = NULL; +} + +bool cHlsSegmentLoader::ConnectToServer(void) +{ + if(!m_pClientSession) + m_pClientSession = new Poco::Net::HTTPClientSession(m_startUri.getHost(), m_startUri.getPort()); + + return m_pClientSession->connected(); +} + +bool cHlsSegmentLoader::DoLoad(void) +{ + bool result = true; + + int nextSegmentSize = GetSegmentSize(m_lastLoadedSegment + 1); + while(m_pRingbuffer->Free() > nextSegmentSize) { + + if(m_lastLoadedSegment + 1 < m_indexParser.vPlaylistItems.size()) { + std::string segmentUri = GetSegmentUri(m_lastLoadedSegment + 1); + result = LoadSegment(segmentUri); + m_lastLoadedSegment++; + } else { + // out of segments + } + nextSegmentSize = GetSegmentSize(m_lastLoadedSegment + 1); + } + return m_bufferFilled = result; +} + +bool cHlsSegmentLoader::BufferFilled(void) +{ + return m_bufferFilled; +} + +bool cHlsSegmentLoader::StopLoader(void) +{ + std::string stopUri = "/video/:/transcode/segmented/stop?session=" + m_sessionCookie; + Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, stopUri); + m_pClientSession->sendRequest(req); + Poco::Net::HTTPResponse reqResponse; + m_pClientSession->receiveResponse(reqResponse); + + Cancel(); + + return reqResponse.getStatus() == 200; +} + +//--- cHlsPlayer + +cHlsPlayer::cHlsPlayer(std::string startm3u8) +{ + m_pSegmentLoader = new cHlsSegmentLoader(startm3u8); +} + +cHlsPlayer::~cHlsPlayer() +{ + delete m_pSegmentLoader; +} + + +void cHlsPlayer::Action(void) +{ + // Start SegmentLoader + m_pSegmentLoader->Start(); + + while (Running()) { + if(m_pSegmentLoader->BufferFilled()) { + DoPlay(); + } else { + // Pause + cCondWait::SleepMs(3); + } + } +} + +bool cHlsPlayer::DoPlay(void) +{ + cPoller Poller; + if(DevicePoll(Poller, 10)) { + LOCK_THREAD; + +// Handle running out of packets. Buffer-> Play-> Pause-> Buffer-> Play + + for(int i = 0; i < 100; i++) { + // Get a pointer to start of the data and the number of avaliable bytes + int bytesAvaliable = 0; + uchar* toPlay = m_pSegmentLoader->m_pRingbuffer->Get(bytesAvaliable); + if(bytesAvaliable >= TS_SIZE) { + int playedBytes = PlayTs(toPlay, TS_SIZE, false); + m_pSegmentLoader->m_pRingbuffer->Del(playedBytes); + } else { + // Pause & Buffer + break; + } + } + } + return true; +} + +void cHlsPlayer::Activate(bool On) +{ + if(On) { + Start(); + } else { + Cancel(1); + } +} + +bool cHlsPlayer::GetIndex(int& Current, int& Total, bool SnapToIFrame) +{ + Total = 9999; + Current = -1; + return true; + +} + +bool cHlsPlayer::GetReplayMode(bool& Play, bool& Forward, int& Speed) +{ + Play = (playMode == pmPlay); + Forward = true; + Speed = -1; + return true; +} + +void cHlsPlayer::Pause(void) +{ + // from vdr-1.7.34 + if (playMode == pmPause) { + Play(); + } else { + LOCK_THREAD; + + DeviceFreeze(); + playMode = pmPause; + } +} + +void cHlsPlayer::Play(void) +{ + // from vdr-1.7.34 + if (playMode != pmPlay) { + LOCK_THREAD; + + DevicePlay(); + playMode = pmPlay; + } +} diff --git a/hlsPlayer.h b/hlsPlayer.h new file mode 100644 index 0000000..3df9c46 --- /dev/null +++ b/hlsPlayer.h @@ -0,0 +1,90 @@ +#ifndef HLSPLAYER_H +#define HLSPLAYER_H + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "m3u8Parser.h" + +class cHlsSegmentLoader : public cThread +{ +private: + int m_ringBufferSize; + int m_segmentsToBuffer; + unsigned int m_lastLoadedSegment; + bool m_bufferFilled; + + uchar* m_pBuffer; + + Poco::Net::HTTPClientSession* m_pClientSession; + Poco::URI m_startUri; + std::string m_sessionUriPart; + std::string m_segmentUriPart; + std::string m_sessionCookie; + + cM3u8Parser m_startParser; + cM3u8Parser m_indexParser; + + bool ConnectToServer(void); + void CloseConnection(void); + void LoadStartList(void); + void LoadIndexList(void); + std::string GetSegmentUri(int segmentIndex) const; + int GetSegmentSize(int segmentIndex); + bool LoadSegment(std::string uri); + int EstimateSegmentSize(); + +protected: + void Action(void); + bool DoLoad(void); + bool StopLoader(void); + +public: + cHlsSegmentLoader(std::string startm3u8); + ~cHlsSegmentLoader(); + + cRingBufferLinear* m_pRingbuffer; + bool BufferFilled(); +}; + +class cHlsPlayer : public cPlayer, cThread +{ +private: + cHlsSegmentLoader* m_pSegmentLoader; + + int m_videoLenght; + int m_actualSegment; + int m_actualTime; + + enum ePlayModes { pmPlay, pmPause }; + ePlayModes playMode; + + virtual void Activate(bool On); + + +protected: + void Action(void); + bool DoPlay(void); + + +public: + cHlsPlayer(std::string startm3u8); + ~cHlsPlayer(); + + virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); + virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed); + void Pause(void); + void Play(void); + +}; + +#endif // HLSPLAYER_H diff --git a/hlsPlayerControl.cpp b/hlsPlayerControl.cpp new file mode 100644 index 0000000..f572604 --- /dev/null +++ b/hlsPlayerControl.cpp @@ -0,0 +1,24 @@ +#include "hlsPlayerControl.h" + +cHlsPlayerControl::cHlsPlayerControl(cHlsPlayer* Player, std::string title) :cControl(Player) +{ + m_pPlayer = Player; + m_title = title; +} + +cHlsPlayerControl::~cHlsPlayerControl() +{ +} + +cString cHlsPlayerControl::GetHeader(void) +{ + return m_title.c_str(); +} + +void cHlsPlayerControl::Hide(void) +{ +} + +void cHlsPlayerControl::Show(void) +{ +} diff --git a/hlsPlayerControl.h b/hlsPlayerControl.h new file mode 100644 index 0000000..934feff --- /dev/null +++ b/hlsPlayerControl.h @@ -0,0 +1,27 @@ +#ifndef HLSPLAYERCONTROL_H +#define HLSPLAYERCONTROL_H + +#include +#include + +#include "hlsPlayer.h" + +class cHlsPlayerControl : public cControl +{ +private: + cHlsPlayer* m_pPlayer; + std::string m_title; + +public: + cHlsPlayerControl(cHlsPlayer* Player, std::string title); + virtual ~cHlsPlayerControl(); + + virtual void Show(void); + virtual void Hide(void); + + virtual cString GetHeader(void); + //virtual eOSState ProcessKey(eKeys Key); + +}; + +#endif // HLSPLAYERCONTROL_H diff --git a/m3u8Parser.cpp b/m3u8Parser.cpp new file mode 100644 index 0000000..8d7382b --- /dev/null +++ b/m3u8Parser.cpp @@ -0,0 +1,121 @@ +#include "m3u8Parser.h" +#include + +cM3u8Parser::cM3u8Parser() +{ + Init(); +} +cM3u8Parser::cM3u8Parser(std::istream& m3u8) +{ + Init(); + Parse(m3u8); +} +void cM3u8Parser::Init() +{ + TargetDuration = -1; + MediaSequence = -1; + MasterPlaylist = false; + AllowCache = true; +} + + +bool cM3u8Parser::Parse(std::istream& m3u8) +{ + bool ok = true; + bool nextLineIsMedia = false; + int lineNo = 0; + + // prepare regex + pcrecpp::RE re("#(EXT[^:\\n]+)(?::[^\\n]+)"); + pcrecpp::RE reVal("(?::([^\\n]+))"); + + std::string line; + playListItem pItem; + pItem.bandwidth = -1; + pItem.file = ""; + pItem.length = -1; + pItem.programId = -1; + + while (std::getline(m3u8, line)) { + if(lineNo == 0 && "#EXTM3U" == line ) { + // Invalid File + ok = false; + continue; + } + + if( re.FullMatch(line) ) { + string var; + //string value; + re.PartialMatch(line, &var); + if("EXT-X-TARGETDURATION" == var) { + int value; + reVal.PartialMatch(line, &value); + TargetDuration = value; + } else if ("EXT-X-ALLOW-CACHE" == var) { + string value; + reVal.PartialMatch(line, &value); + AllowCache = "YES" == value; + } else if ("EXT-X-MEDIA-SEQUENCE" == var) { + int value; + reVal.PartialMatch(line, &value); + MediaSequence = value; + } else if ("EXT-X-STREAM-INF" == var) { + MasterPlaylist = true; + nextLineIsMedia = true; + + int bandw; + pcrecpp::RE reBand("BANDWIDTH=(\\d+)"); + if(reBand.PartialMatch(line, &bandw)) { + pItem.bandwidth = bandw; + } + + int id; + pcrecpp::RE reId("PROGRAM-ID=(\\d+)"); + if(reId.PartialMatch(line, &id)) { + pItem.programId = id; + } + + } else if ("EXTINF" == var) { + MasterPlaylist = false; + int value; + pcrecpp::RE reInt("(?:#EXTINF:([\\d]+))"); + if( reInt.PartialMatch(line, &value)) { + nextLineIsMedia = true; + pItem.length = value; + } + } + } + // possible mediafile + else { + if(nextLineIsMedia) { + nextLineIsMedia = false; + pItem.file = line; + pItem.size = 0; + vPlaylistItems.push_back(pItem); + } + } + lineNo++; + } + return ok; +} + +/* +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=4000000 +session/731d4143-c05d-47ed-afa8-37913402630a/8/index.m3u8 +*/ + +/* +#EXTM3U +#EXT-X-TARGETDURATION:3 +#EXT-X-ALLOW-CACHE:NO +#EXT-X-MEDIA-SEQUENCE:0 +#EXTINF:3, nodesc +00000.ts +#EXTINF:3, nodesc +00001.ts +... +#EXTINF:2, nodesc +01935.ts +#EXT-X-ENDLIST +*/ diff --git a/m3u8Parser.h b/m3u8Parser.h new file mode 100644 index 0000000..0f52002 --- /dev/null +++ b/m3u8Parser.h @@ -0,0 +1,37 @@ +#ifndef M3U8PARSER_H +#define M3U8PARSER_H + +#include +#include +#include + +class cM3u8Parser +{ +public: + struct playListItem { + int length; + int bandwidth; + int programId; + std::string file; + int size; + }; +private: + void Init(); + +public: + std::vector vPlaylistItems; + int TargetDuration; + int MediaSequence; + bool MasterPlaylist; + bool AllowCache; + bool Parse(std::istream &m3u8); + +public: + cM3u8Parser(std::istream &m3u8); + cM3u8Parser(); + ~cM3u8Parser() {}; + +}; + + +#endif // M3U8PARSER_H diff --git a/player.c b/player.c deleted file mode 100644 index 56cb827..0000000 --- a/player.c +++ /dev/null @@ -1,984 +0,0 @@ -/// -/// @file player.c @brief A play plugin for VDR. -/// -/// Copyright (c) 2012, 2013 by Johns. All Rights Reserved. -/// -/// Contributor(s): Dennis Bendlin -/// -/// License: AGPLv3 -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU Affero General Public License as -/// published by the Free Software Foundation, either version 3 of the -/// License. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU Affero General Public License for more details. -/// -/// $Id: 23f35b1a9358694e2b022aed1eff081887bc3f16 $ -////////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#define _(str) gettext(str) ///< gettext shortcut -#define _N(str) str ///< gettext_noop shortcut - -#include "player.h" -#include "video.h" -#include "misc.h" - -////////////////////////////////////////////////////////////////////////////// - -const char *ConfigBrowserRoot = "/"; ///< browser starting point - -static char ConfigOsdOverlay; ///< show osd overlay -static char ConfigUseSlave; ///< external player use slave mode -static char ConfigFullscreen; ///< external player uses fullscreen -static char *ConfigVideoOut; ///< video out device -static char *ConfigAudioOut; ///< audio out device -static char *ConfigAudioMixer; ///< audio mixer device -static char *ConfigMixerChannel; ///< audio mixer channel -static const char *ConfigMplayer = "/usr/bin/mplayer"; ///< mplayer executable -static const char *ConfigMplayerArguments; ///< extra mplayer arguments -static const char *ConfigX11Display = ":0.0"; ///< x11 display - - /// DVD-Drive for mplayer -static const char *ConfigMplayerDevice = "/dev/dvd"; -static uint32_t ConfigColorKey = 0x00020507; ///< color key - -////////////////////////////////////////////////////////////////////////////// -// Osd -////////////////////////////////////////////////////////////////////////////// - -/** -** Open OSD. -*/ -void OsdOpen(void) -{ - Debug(3, "plex: %s\n", __FUNCTION__); - - VideoWindowShow(); -} - -/** -** Close OSD. -*/ -void OsdClose(void) -{ - Debug(3, "plex: %s\n", __FUNCTION__); - - VideoWindowHide(); - VideoWindowClear(); -} - -/** -** Clear OSD. -*/ -void OsdClear(void) -{ - Debug(3, "plex: %s\n", __FUNCTION__); - - VideoWindowClear(); -} - -/** -** Get OSD size and aspect. -** -** @param width[OUT] width of OSD -** @param height[OUT] height of OSD -** @param aspect[OUT] aspect ratio (4/3, 16/9, ...) of OSD -*/ -void GetOsdSize(int *width, int *height, double *aspect) -{ - VideoGetOsdSize(width, height); - *aspect = 16.0 / 9.0 / (double)*width * (double)*height; -} - -/** -** Draw osd pixmap -*/ -void OsdDrawARGB(int x, int y, int w, int h, const uint8_t * argb) -{ - Debug(3, "plex: %s %d,%d %d,%d\n", __FUNCTION__, x, y, w, h); - - VideoDrawARGB(x, y, w, h, argb); -} - -////////////////////////////////////////////////////////////////////////////// -// External player -////////////////////////////////////////////////////////////////////////////// - -static pid_t PlayerPid; ///< player pid -static pthread_t PlayerThread; ///< player decode thread - -static char PlayerPipeBuf[4096]; ///< pipe buffer -static int PlayerPipeCnt; ///< pipe buffer count -static int PlayerPipeIdx; ///< pipe buffer index -static int PlayerPipeOut[2]; ///< player write pipe -static int PlayerPipeIn[2]; ///< player read pipe - -static int PlayerVolume = -1; ///< volume 0 - 100 - -char PlayerDvdNav; ///< dvdnav active -char PlayerPaused; ///< player paused -char PlayerSpeed; ///< player playback speed -int PlayerCurrent; ///< current postion in seconds -int PlayerTotal; ///< total length in seconds -char PlayerTitle[256]; ///< title from meta data -char PlayerFilename[256]; ///< filename - -////////////////////////////////////////////////////////////////////////////// -// Slave -////////////////////////////////////////////////////////////////////////////// - -/** -** Parse player output. -** -** @param data line pointer (\0 terminated) -** @param size line length -*/ -static void PlayerParseLine(const char *data, int size) -{ - Debug(4, "play/parse: |%.*s|\n", size, data); - (void)size; - - // data is \0 terminated - if (!strncasecmp(data, "DVDNAV_TITLE_IS_MENU", 20)) { - PlayerDvdNav = 1; - } else if (!strncasecmp(data, "DVDNAV_TITLE_IS_MOVIE", 21)) { - PlayerDvdNav = 2; - } else if (!strncasecmp(data, "ID_DVD_VOLUME_ID=", 17)) { - Debug(3, "DVD_VOLUME = %s\n", data + 17); - } else if (!strncasecmp(data, "ID_AID_", 7)) { - char lang[64]; - int aid; - - if (sscanf(data, "ID_AID_%d_LANG=%s", &aid, lang) == 2) { - Debug(3, "AID(%d) = %s\n", aid, lang); - } - } else if (!strncasecmp(data, "ID_SID_", 7)) { - char lang[64]; - int sid; - - if (sscanf(data, "ID_SID_%d_LANG=%s", &sid, lang) == 2) { - Debug(3, "SID(%d) = %s\n", sid, lang); - } - } else if (!strncasecmp(data, "ANS_META_TITLE=", 14)) { - if (sscanf(data, "ANS_META_TITLE='%[^\t\n]", PlayerTitle) == 1) { - PlayerTitle[strlen(PlayerTitle) - 1] = 0; - Debug(3, "PlayerTitle= %s\n", PlayerTitle); - } - } else if (!strncasecmp(data, "ANS_FILENAME=", 12)) { - if (sscanf(data, "ANS_FILENAME='%[^\t\n]", PlayerFilename) == 1) { - PlayerFilename[strlen(PlayerFilename) - 1] = 0; - Debug(3, "PlayerFilename= %s\n", PlayerFilename); - } - } else if (!strncasecmp(data, "ANS_LENGTH=", 10)) { - if (sscanf(data, "ANS_LENGTH=%d", &PlayerTotal) == 1) { - Debug(3, "PlayerTotal=%d\n", PlayerTotal); - } - } else if (!strncasecmp(data, "ANS_TIME_POSITION=", 17)) { - if (sscanf(data, "ANS_TIME_POSITION=%d", &PlayerCurrent) == 1) { - Debug(3, "PlayerCurrent=%d\n", PlayerCurrent); - } - } -} - -/** -** Poll input pipe. -*/ -static void PlayerPollPipe(void) -{ - struct pollfd poll_fds[1]; - int n; - int i; - int l; - - // check if something to read - poll_fds[0].fd = PlayerPipeOut[0]; - poll_fds[0].events = POLLIN; - - switch (poll(poll_fds, 1, 0)) { - case 0: // timeout - return; - case -1: // error - Error(_("play/player: poll failed: %s\n"), strerror(errno)); - return; - default: // ready - break; - } - - // fill buffer - if ((n = read(PlayerPipeOut[0], PlayerPipeBuf + PlayerPipeCnt, - sizeof(PlayerPipeBuf) - PlayerPipeCnt)) < 0) { - Error(_("play/player: read failed: %s\n"), strerror(errno)); - return; - } - - PlayerPipeCnt += n; - l = 0; - for (i = PlayerPipeIdx; i < PlayerPipeCnt; ++i) { - if (PlayerPipeBuf[i] == '\n') { - PlayerPipeBuf[i] = '\0'; - PlayerParseLine(PlayerPipeBuf + PlayerPipeIdx, i - PlayerPipeIdx); - PlayerPipeIdx = i + 1; - l = PlayerPipeIdx; - } - } - - if (l) { // remove consumed bytes - if (PlayerPipeCnt - l) { - memmove(PlayerPipeBuf, PlayerPipeBuf + l, PlayerPipeCnt - l); - } - PlayerPipeCnt -= l; - PlayerPipeIdx -= l; - } else if (PlayerPipeCnt == sizeof(PlayerPipeBuf)) { - // no '\n' in buffer use it as single line - PlayerPipeBuf[sizeof(PlayerPipeBuf) - 1] = '\0'; - PlayerParseLine(PlayerPipeBuf + PlayerPipeIdx, PlayerPipeCnt); - PlayerPipeIdx = 0; - PlayerPipeCnt = 0; - } -} - -#define MPLAYER_MAX_ARGS 64 ///< number of arguments supported - -/** -** Execute external player. -** -** @param filename path and name of file to play -*/ -static void PlayerExec(const char *filename) -{ - const char *args[MPLAYER_MAX_ARGS]; - int argn; - char wid_buf[32]; - char volume_buf[32]; - int i; - - if (ConfigUseSlave) { // connect pipe to stdin/stdout - dup2(PlayerPipeIn[0], STDIN_FILENO); - close(PlayerPipeIn[0]); - close(PlayerPipeIn[1]); - close(PlayerPipeOut[0]); - dup2(PlayerPipeOut[1], STDOUT_FILENO); - dup2(PlayerPipeOut[1], STDERR_FILENO); - close(PlayerPipeOut[1]); - } - // close all file handles - for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) { - close(i); - } - - // export DISPLAY= - - args[0] = ConfigMplayer; - args[1] = "-quiet"; - args[2] = "-msglevel"; - // FIXME: play with the options -#ifdef DEBUG - args[3] = "all=6:global=4:cplayer=4:identify=4"; -#else - args[3] = "all=2:global=4:cplayer=2:identify=4"; -#endif - if (ConfigOsdOverlay) { - args[4] = "-noontop"; - } else { - args[4] = "-ontop"; - } - args[5] = "-noborder"; - if (ConfigDisableRemote) { - args[6] = "-lirc"; - } else { - args[6] = "-nolirc"; - } - args[7] = "-nojoystick"; // disable all unwanted inputs - args[8] = "-noar"; - args[9] = "-nomouseinput"; - args[10] = "-nograbpointer"; - args[11] = "-noconsolecontrols"; - args[12] = "-fixed-vo"; - args[13] = "-sid"; // subtitle selection - args[14] = "0"; - args[15] = "-slang"; - args[16] = "de,en"; // FIXME: use VDR config - args[17] = "-alang"; - args[18] = "de,en"; // FIXME: use VDR config - argn = 19; - if (ConfigMplayerDevice) { // dvd-device - args[argn++] = "-dvd-device"; - args[argn++] = ConfigMplayerDevice; - } - if (!strncasecmp(filename, "cdda://", 7)) { - args[argn++] = "-cache"; // cdrom needs cache - args[argn++] = "1000"; - } else { - args[argn++] = "-nocache"; // dvdnav needs nocache - } - if (ConfigUseSlave) { - args[argn++] = "-slave"; - //args[argn++] = "-idle"; - } - if (ConfigOsdOverlay) { // no mplayer osd with overlay - args[argn++] = "-osdlevel"; - args[argn++] = "0"; - } - if (ConfigFullscreen) { - args[argn++] = "-fs"; - args[argn++] = "-zoom"; - } else { - args[argn++] = "-nofs"; - } - if (VideoGetPlayWindow()) { - snprintf(wid_buf, sizeof(wid_buf), "%d", VideoGetPlayWindow()); - args[argn++] = "-wid"; - args[argn++] = wid_buf; - } - if (ConfigVideoOut) { - args[argn++] = "-vo"; - args[argn++] = ConfigVideoOut; - // add options based on selected video out - if (!strncmp(ConfigVideoOut, "vdpau", 5)) { - args[argn++] = "-vc"; - args[argn++] = - "ffmpeg12vdpau,ffwmv3vdpau,ffvc1vdpau,ffh264vdpau,ffodivxvdpau,"; - } else if (!strncmp(ConfigVideoOut, "vaapi", 5)) { - args[argn++] = "-va"; - args[argn++] = "vaapi"; - } - } - if (ConfigAudioOut) { - args[argn++] = "-ao"; - args[argn++] = ConfigAudioOut; - // FIXME: -ac hwac3,hwdts,hwmpa, - } - if (ConfigAudioMixer) { - args[argn++] = "-mixer"; - args[argn++] = ConfigAudioMixer; - } - if (ConfigMixerChannel) { - args[argn++] = "-mixer-channel"; - args[argn++] = ConfigMixerChannel; - } - if (ConfigX11Display) { - args[argn++] = "-display"; - args[argn++] = ConfigX11Display; - } - if (PlayerVolume != -1) { - // FIXME: here could be a problem with LANG - snprintf(volume_buf, sizeof(volume_buf), "%.2f", - (PlayerVolume * 100.0) / 255); - args[argn++] = "-volume"; - args[argn++] = volume_buf; - } - // split X server arguments string into words - if (ConfigMplayerArguments) { - const char *sval; - char *buf; - char *s; - - sval = ConfigMplayerArguments; -#ifndef __FreeBSD__ - s = buf = strdupa(sval); -#else - s = buf = alloca(strlen(sval) + 1); - strcpy(buf, sval); -#endif - while ((sval = strsep(&s, " \t"))) { - args[argn++] = sval; - - if (argn == MPLAYER_MAX_ARGS - 3) { // argument overflow - Error(_("play: too many arguments for mplayer\n")); - // argn = 1; - break; - } - } - } - args[argn++] = "--"; - args[argn++] = filename; - args[argn] = NULL; -#ifdef DEBUG - if (argn + 1 >= (int)(sizeof(args) / sizeof(*args))) { - Debug(3, "play: too many arguments %d\n", argn); - } - for (i = 0; i < argn; ++i) { - Debug(3, "%s", args[i]); - } -#endif - - execvp(args[0], (char *const *)args); - - // shouldn't be reached - Error(_("play: execvp of '%s' failed: %s\n"), args[0], strerror(errno)); - exit(-1); -} - -/** -** Execute external player. -** -** @param filename path and name of file to play -*/ -static void PlayerForkAndExec(const char *filename) -{ - pid_t pid; - - if (ConfigUseSlave) { - if (pipe(PlayerPipeIn)) { - Error(_("play: pipe failed: %s\n"), strerror(errno)); - return; - } - if (pipe(PlayerPipeOut)) { - Error(_("play: pipe failed: %s\n"), strerror(errno)); - return; - } - } - - if ((pid = fork()) == -1) { - Error(_("play: fork failed: %s\n"), strerror(errno)); - return; - } - if (!pid) { // child - PlayerExec(filename); - return; - } - PlayerPid = pid; // parent - setpgid(pid, 0); - - if (ConfigUseSlave) { - close(PlayerPipeIn[0]); - close(PlayerPipeOut[1]); - } - - Debug(3, "play: child pid=%d\n", pid); -} - -/** -** Close pipes. -*/ -static void PlayerClosePipes(void) -{ - if (ConfigUseSlave) { - if (PlayerPipeIn[0] != -1) { - close(PlayerPipeIn[0]); - } - if (PlayerPipeOut[1] != -1) { - close(PlayerPipeOut[1]); - } - } -} - -////////////////////////////////////////////////////////////////////////////// -// Thread -////////////////////////////////////////////////////////////////////////////// - -/** -** External player handler thread. -** -** @param dummy dummy pointer -*/ -static void *PlayerHandlerThread(void *dummy) -{ - Debug(3, "play: player thread started\n"); - - // Need: thread for video poll: while (PlayerIsRunning()) - for (;;) { - if (ConfigUseSlave && PlayerIsRunning()) { - PlayerPollPipe(); - // FIXME: wait only if pipe not ready - } - if (ConfigOsdOverlay) { - VideoPollEvents(10); - } else { - usleep(10 * 1000); - } - } - - Debug(3, "play: player thread stopped\n"); - PlayerThread = 0; - - return dummy; -} - -/** -** Initialize player threads. -*/ -static void PlayerThreadInit(void) -{ - pthread_create(&PlayerThread, NULL, PlayerHandlerThread, NULL); - //pthread_setname_np(PlayerThread, "play player"); -} - -/** -** Exit and cleanup player threads. -*/ -static void PlayerThreadExit(void) -{ - if (PlayerThread) { - void *retval; - - Debug(3, "play: player thread canceled\n"); - if (pthread_cancel(PlayerThread)) { - Error(_("play: can't queue cancel player thread\n")); - } - if (pthread_join(PlayerThread, &retval) || retval != PTHREAD_CANCELED) { - Error(_("play: can't cancel player thread\n")); - } - PlayerThread = 0; - } -} - -/** -** Send command to player. -** -** @param formst printf format string -*/ -static void SendCommand(const char *format, ...) -{ - va_list va; - char buf[256]; - int n; - - if (!PlayerPid) { - return; - } - if (PlayerPipeIn[1] == -1) { - Error(_("play: no pipe to send command available\n")); - return; - } - va_start(va, format); - n = vsnprintf(buf, sizeof(buf), format, va); - va_end(va); - - Debug(3, "play: send '%.*s'\n", n - 1, buf); - - // FIXME: poll pipe if ready - if (write(PlayerPipeIn[1], buf, n) != n) { - fprintf(stderr, "play: write failed\n"); - } -} - -/** -** Send player quit. -*/ -void PlayerSendQuit(void) -{ - if (ConfigUseSlave) { - SendCommand("quit\n"); - } -} - -/** -** Send player pause. -*/ -void PlayerSendPause(void) -{ - if (ConfigUseSlave) { - SendCommand("pause\n"); - } -} - -/** -** Send player speed_set. -** -** @param speed mplayer speed -*/ -void PlayerSendSetSpeed(int speed) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep speed_set %d\n", speed); - } -} - -/** -** Send player seek. -** -** @param seconds seek in seconds relative to current position -*/ -void PlayerSendSeek(int seconds) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep seek %+d 0\n", seconds); - } -} - -/** -** Send player volume. -*/ -void PlayerSendVolume(void) -{ - if (ConfigUseSlave) { - // FIXME: %.2f could have a problem with LANG - SendCommand("pausing_keep volume %.2f 1\n", - (PlayerVolume * 100.0) / 255); - } -} - -/** -** Send switch audio track. -*/ -void PlayerSendSwitchAudio(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep switch_audio\n"); - } -} - -/** -** Send select subtitle. -*/ -void PlayerSendSubSelect(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep sub_select\n"); - } -} - -/** -** Send up for dvdnav. -*/ -void PlayerSendDvdNavUp(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav up\n"); - } -} - -/** -** Send down for dvdnav. -*/ -void PlayerSendDvdNavDown(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav down\n"); - } -} - -/** -** Send left for dvdnav. -*/ -void PlayerSendDvdNavLeft(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav left\n"); - } -} - -/** -** Send right for dvdnav. -*/ -void PlayerSendDvdNavRight(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav right\n"); - } -} - -/** -** Send select for dvdnav. -*/ -void PlayerSendDvdNavSelect(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav select\n"); - } -} - -/** -** Send prev for dvdnav. -*/ -void PlayerSendDvdNavPrev(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav prev\n"); - } -} - -/** -** Send menu for dvdnav. -*/ -void PlayerSendDvdNavMenu(void) -{ - if (ConfigUseSlave) { - SendCommand("pausing_keep dvdnav menu\n"); - } -} - -/** -** Get length in seconds. -*/ -void PlayerGetLength(void) -{ - if (ConfigUseSlave) { - SendCommand("get_time_length\n"); - } -} - -/** -** Get current position in seconds. -*/ -void PlayerGetCurrentPosition(void) -{ - if (ConfigUseSlave) { - SendCommand("get_time_pos\n"); - } -} - -/** -** Get title from meta data. -*/ -void PlayerGetMetaTitle(void) -{ - if (ConfigUseSlave) { - SendCommand("get_meta_title\n"); - } -} - -/** -** Get filename. -*/ -void PlayerGetFilename(void) -{ - if (ConfigUseSlave) { - SendCommand("get_file_name\n"); - } -} - -/** -** Start external player. -** -** @param filename path and name of file to play -*/ -void PlayerStart(const char *filename) -{ - PlayerPipeCnt = 0; // reset to defaults - PlayerPipeIdx = 0; - - PlayerPipeIn[0] = -1; - PlayerPipeIn[1] = -1; - PlayerPipeOut[0] = -1; - PlayerPipeOut[1] = -1; - PlayerPid = 0; - - PlayerPaused = 0; - PlayerSpeed = 1; - - PlayerDvdNav = 0; - - if (ConfigOsdOverlay) { // overlay wanted - VideoSetColorKey(ConfigColorKey); - VideoInit(ConfigX11Display); - EnableDummyDevice(); - } - PlayerForkAndExec(filename); - - if (ConfigOsdOverlay || ConfigUseSlave) { - PlayerThreadInit(); - } -} - -/** -** Stop external player. -*/ -void PlayerStop(void) -{ - PlayerThreadExit(); - - // - // stop mplayer, if it is still running. - // - if (PlayerIsRunning()) { - int waittime; - int timeout; - - waittime = 0; - timeout = 500; // 0.5s - - kill(PlayerPid, SIGTERM); - // wait for player finishing, with timeout - while (PlayerIsRunning() && waittime++ < timeout) { - usleep(1 * 1000); - } - if (PlayerIsRunning()) { // still running - waittime = 0; - timeout = 500; // 0.5s - - kill(PlayerPid, SIGKILL); - // wait for player finishing, with timeout - while (PlayerIsRunning() && waittime++ < timeout) { - usleep(1 * 1000); - } - if (PlayerIsRunning()) { - Error(_("play: can't stop player\n")); - } - } - } - PlayerPid = 0; - PlayerClosePipes(); - - if (ConfigOsdOverlay) { - DisableDummyDevice(); - VideoExit(); - } -} - -/** -** Is external player still running? -*/ -int PlayerIsRunning(void) -{ - pid_t wpid; - int status; - - if (!PlayerPid) { // no player - return 0; - } - - wpid = waitpid(PlayerPid, &status, WNOHANG); - if (wpid <= 0) { - return 1; - } - if (WIFEXITED(status)) { - Debug(3, "play: player exited (%d)\n", WEXITSTATUS(status)); - } - if (WIFSIGNALED(status)) { - Debug(3, "play: player killed (%d)\n", WTERMSIG(status)); - } - PlayerPid = 0; - return 0; -} - -/** -** Set player volume. -** -** @param volume new volume (0..255) -*/ -void PlayerSetVolume(int volume) -{ - Debug(3, "player: set volume=%d\n", volume); - - if (PlayerVolume != volume) { - PlayerVolume = volume; - if (PlayerPid) { - PlayerSendVolume(); - } - } -} - -////////////////////////////////////////////////////////////////////////////// -// Device/Plugin C part -////////////////////////////////////////////////////////////////////////////// - -/** -** Return command line help string. -*/ -const char *CommandLineHelp(void) -{ - return " -% device\tmplayer dvd device\n" - " -/\t/dir\tbrowser root directory\n" - " -a audio\tmplayer -ao (alsa:device=hw=0.0) overwrites mplayer.conf\n" - " -d display\tX11 display (default :0.0) overwrites $DISPLAY\n" - " -f\t\tmplayer fullscreen playback\n" - " -g geometry\tx11 window geometry wxh+x+y\n" - " -k colorkey\tvideo color key (default=0x020507, mplayer2=0x76B901)\n" - " -m mplayer\tfilename of mplayer executable\n" - " -M args\targuments for mplayer\n" - " -o\t\tosd overlay experiments\n" " -s\t\tmplayer slave mode\n" - " -v video\tmplayer -vo (vdpau:deint=4:hqscaling=1) overwrites mplayer.conf\n"; -} - -/** -** Process the command line arguments. -** -** @param argc number of arguments -** @param argv arguments vector -*/ -int ProcessArgs(int argc, char *const argv[]) -{ - const char *s; - - // - // Parse arguments. - // -#ifdef __FreeBSD__ - if (!strcmp(*argv, "play")) { - ++argv; - --argc; - } -#endif - if ((s = getenv("DISPLAY"))) { - ConfigX11Display = s; - } - - for (;;) { - switch (getopt(argc, argv, "-%:/:a:b:d:fg:k:m:M:osv:")) { - case '%': // dvd-device - ConfigMplayerDevice = optarg; - continue; - case '/': // browser root - ConfigBrowserRoot = optarg; - continue; - case 'a': // audio out - ConfigAudioOut = optarg; - continue; - case 'd': // display x11 - ConfigX11Display = optarg; - continue; - case 'f': // fullscreen mode - ConfigFullscreen = 1; - continue; - case 'g': // geometry - VideoSetGeometry(optarg); - continue; - case 'k': // color key - ConfigColorKey = strtol(optarg, NULL, 0); - continue; - case 'm': // mplayer executable - ConfigMplayer = optarg; - continue; - case 'M': // mplayer extra arguments - ConfigMplayerArguments = optarg; - continue; - case 'o': // osd / overlay - ConfigOsdOverlay = 1; - continue; - case 's': // slave mode - ConfigUseSlave = 1; - continue; - case 'v': // video out - ConfigVideoOut = optarg; - continue; - case EOF: - break; - case '-': - fprintf(stderr, _("We need no long options\n")); - return 0; - case ':': - fprintf(stderr, _("Missing argument for option '%c'\n"), - optopt); - return 0; - default: - fprintf(stderr, _("Unkown option '%c'\n"), optopt); - return 0; - } - break; - } - while (optind < argc) { - fprintf(stderr, _("Unhandled argument '%s'\n"), argv[optind++]); - } - - return 1; -} diff --git a/player.h b/player.h deleted file mode 100644 index d4acedd..0000000 --- a/player.h +++ /dev/null @@ -1,160 +0,0 @@ -/// -/// @file player.h @brief A play plugin header file. -/// -/// Copyright (c) 2012, 2013 by Johns. All Rights Reserved. -/// -/// Contributor(s): Dennis Bendlin -/// -/// License: AGPLv3 -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU Affero General Public License as -/// published by the Free Software Foundation, either version 3 of the -/// License. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU Affero General Public License for more details. -/// -/// $Id: 0c86be2575387d12e692ffa0cc8ce515bbc6f5e5 $ -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" -{ -#endif - /// C callback feed key press - extern void FeedKeyPress(const char *, const char *, int, int); - - /// C callback enable dummy device - extern void EnableDummyDevice(void); - /// C callback disable dummy device - extern void DisableDummyDevice(void); - - /// C plugin get osd size and ascpect - extern void GetOsdSize(int *, int *, double *); - - /// C plugin open osd - extern void OsdOpen(void); - /// C plugin close osd - extern void OsdClose(void); - /// C plugin clear osd - extern void OsdClear(void); - /// C plugin draw osd pixmap - extern void OsdDrawARGB(int, int, int, int, const uint8_t *); - - /// C plugin play audio packet - extern int PlayAudio(const uint8_t *, int, uint8_t); - /// C plugin play TS audio packet - extern int PlayTsAudio(const uint8_t *, int); - /// C plugin set audio volume - extern void SetVolumeDevice(int); - - /// C plugin play video packet - extern int PlayVideo(const uint8_t *, int); - /// C plugin play TS video packet - extern void PlayTsVideo(const uint8_t *, int); - /// C plugin grab an image - extern uint8_t *GrabImage(int *, int, int, int, int); - - /// C plugin set play mode - extern int SetPlayMode(int); - /// C plugin get current system time counter - extern int64_t GetSTC(void); - /// C plugin get video stream size and aspect - extern void GetVideoSize(int *, int *, double *); - /// C plugin set trick speed - extern void TrickSpeed(int); - /// C plugin clears all video and audio data from the device - extern void Clear(void); - /// C plugin sets the device into play mode - extern void Play(void); - /// C plugin sets the device into "freeze frame" mode - extern void Freeze(void); - /// C plugin mute audio - extern void Mute(void); - /// C plugin display I-frame as a still picture. - extern void StillPicture(const uint8_t *, int); - /// C plugin poll if ready - extern int Poll(int); - /// C plugin flush output buffers - extern int Flush(int); - - /// C plugin command line help - extern const char *CommandLineHelp(void); - /// C plugin process the command line arguments - extern int ProcessArgs(int, char *const[]); - - /// C plugin exit + cleanup - extern void PlayExit(void); - /// C plugin start code - extern int Start(void); - /// C plugin stop code - extern void Stop(void); - /// C plugin house keeping - extern void Housekeeping(void); - /// C plugin main thread hook - extern void MainThreadHook(void); - - /// Browser root=start directory - extern const char *ConfigBrowserRoot; - ///< Disable remote during external play - extern char ConfigDisableRemote; - extern const char *X11DisplayName; ///< x11 display name - extern char PlayerDvdNav; ///< dvdnav active - extern char PlayerPaused; ///< player paused - extern char PlayerSpeed; ///< player playback speed - extern int PlayerCurrent; ///< current postion in seconds - extern int PlayerTotal; ///< total length in seconds - extern char PlayerTitle[256]; ///< title from meta data - extern char PlayerFilename[256]; ///< filename - - /// Start external player - extern void PlayerStart(const char *name); - /// Stop external player - extern void PlayerStop(void); - /// Is external player still running - extern int PlayerIsRunning(void); - - /// Set player volume - extern void PlayerSetVolume(int); - - /// Player send quit command - extern void PlayerSendQuit(void); - /// Player send toggle pause command - extern void PlayerSendPause(void); - /// Player send set play speed - extern void PlayerSendSetSpeed(int); - /// Player send seek - extern void PlayerSendSeek(int); - /// Player send switch audio track - extern void PlayerSendSwitchAudio(void); - /// Player send select subtitle - extern void PlayerSendSubSelect(void); - /// Player send dvd-nav up - extern void PlayerSendDvdNavUp(void); - /// Player send dvd-nav down - extern void PlayerSendDvdNavDown(void); - /// Player send dvd-nav left - extern void PlayerSendDvdNavLeft(void); - /// Player send dvd-nav right - extern void PlayerSendDvdNavRight(void); - /// Player send dvd-nav menu select - extern void PlayerSendDvdNavSelect(void); - /// Player send dvd-nav menu prev - extern void PlayerSendDvdNavPrev(void); - /// Player send dvd-nav prev - extern void PlayerSendDvdNavMenu(void); - /// Get length in seconds. - extern void PlayerGetLength(void); - /// Get current position in seconds. - extern void PlayerGetCurrentPosition(void); - /// Get title from meta data. - extern void PlayerGetMetaTitle(void); - /// Get filename. - extern void PlayerGetFilename(void); - -#ifdef __cplusplus -} -#endif diff --git a/plex.cpp b/plex.cpp index a5287ae..893d058 100644 --- a/plex.cpp +++ b/plex.cpp @@ -2,12 +2,6 @@ #include "SubscriptionManager.h" #include "plex.h" -extern "C" -{ -#include "video.h" -#include "player.h" -} - static const char *const PLUGINNAME = "plex"; ////////////////////////////////////////////////////////////////////////////// @@ -118,443 +112,6 @@ extern "C" void DrawText(int x, int y, const char *s, uint32_t fg, uint32_t bg, #endif -////////////////////////////////////////////////////////////////////////////// -// cPlayer -////////////////////////////////////////////////////////////////////////////// - - -/** -** Player constructor. -** -** @param filename path and name of file to play -*/ -cMyPlayer::cMyPlayer(const char *filename) -:cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED) -{ - dsyslog("[plex]%s: '%s'\n", __FUNCTION__, filename); - - PlayerSetVolume(cDevice::CurrentVolume()); - dsyslog("[plex]: initial volume %d\n", cDevice::CurrentVolume()); - - FileName = strdup(filename); - if (ConfigDisableRemote) { - RemoteDisable(); - } -} - -/** -** Player destructor. -*/ -cMyPlayer::~cMyPlayer() -{ - dsyslog("[plex]%s: end\n", __FUNCTION__); - - PlayerStop(); - free(FileName); - if (ConfigDisableRemote) { - RemoteEnable(); - } - // FIXME: wait until primary device is switched? - dsyslog("[plex]: device %d->%d\n", - cDevice::PrimaryDevice()->DeviceNumber(), DoMakePrimary); -} - -/** -** Player attached or detached. -** -** @param on flag turn player on or off -*/ -void cMyPlayer::Activate(bool on) -{ - dsyslog("[plex]%s: '%s' %d\n", __FUNCTION__, FileName, on); - - if (on) { - PlayerStart(FileName); - } else { - PlayerStop(); - } -} - -/** -** Get current replay mode. -*/ -bool cMyPlayer::GetReplayMode(bool & play, bool & forward, int &speed) -{ - play = !PlayerPaused; - forward = true; - if (PlayerSpeed == 1) { - speed = -1; - } else { - speed = PlayerSpeed; - } - return true; -} - -////////////////////////////////////////////////////////////////////////////// -// cStatus -////////////////////////////////////////////////////////////////////////////// - -cMyStatus *Status; ///< status monitor for volume - -/** -** Status constructor. -*/ -cMyStatus::cMyStatus(void) -{ - Volume = cDevice::CurrentVolume(); - - dsyslog("[plex]: status volume %d\n", Volume); -} - -/** -** Called if volume is set. -*/ -void cMyStatus::SetVolume(int volume, bool absolute) -{ - dsyslog("[plex]: volume %d %s\n", volume, absolute ? "abs" : "rel"); - - if (absolute) { - Volume = volume; - } else { - Volume += volume; - } - - PlayerSetVolume(Volume); -} - -////////////////////////////////////////////////////////////////////////////// -// cControl -////////////////////////////////////////////////////////////////////////////// - - - -/** -** Show replay mode. -*/ -void cMyControl::ShowReplayMode(void) -{ - dsyslog("[plex]%s: %d - %d\n", __FUNCTION__, Setup.ShowReplayMode, - cOsd::IsOpen()); - - // use vdr setup - if (Display || (Setup.ShowReplayMode && !cOsd::IsOpen())) { - bool play; - bool forward; - int speed; - - if (GetReplayMode(play, forward, speed)) { - if (!Display) { - // no need to show normal play - if (play && forward && speed == 1) { - return; - } - Display = Skins.Current()->DisplayReplay(true); - } - Display->SetMode(play, forward, speed); - } - } -} - -/** -** Show progress. -*/ -void cMyControl::ShowProgress(void) -{ - if (Display || (!cOsd::IsOpen())) { - bool play; - bool forward; - int speed; - - if (GetReplayMode(play, forward, speed)) { - if (!Display) { - Display = Skins.Current()->DisplayReplay(false); - } - - if (!infoVisible) { - infoVisible = true; - timeoutShow = time(0) + Setup.ChannelInfoTime; - PlayerGetLength(); - PlayerGetMetaTitle(); - PlayerGetFilename(); - } - - PlayerGetCurrentPosition(); - if (strcmp(PlayerTitle, "") != 0) { - Display->SetTitle(PlayerTitle); - } else { - Display->SetTitle(PlayerFilename); - } - Display->SetProgress(PlayerCurrent, PlayerTotal); - Display->SetMode(play, forward, speed); - Display->SetCurrent(IndexToHMSF(PlayerCurrent, false, 1)); - Display->SetTotal(IndexToHMSF(PlayerTotal, false, 1)); - } - SetNeedsFastResponse(true); - Skins.Flush(); - } -} - -/** -** Show control. -*/ -void cMyControl::Show(void) -{ - dsyslog("[plex]%s:\n", __FUNCTION__); - if (Setup.ShowReplayMode) - ShowReplayMode(); - else - ShowProgress(); -} - -/** -** Control constructor. -** -** @param filename pathname of file to play. -*/ -cMyControl::cMyControl(const char *filename) -:cControl(Player = new cMyPlayer(filename)) -{ - Display = NULL; - Status = new cMyStatus; // start monitoring volume - infoVisible = false; - - //LastSkipKey = kNone; - //LastSkipSeconds = REPLAYCONTROLSKIPSECONDS; - //LastSkipTimeout.Set(0); - cStatus::MsgReplaying(this, filename, filename, true); - - cDevice::PrimaryDevice()->ClrAvailableTracks(true); -} - -/** -** Control destructor. -*/ -cMyControl::~cMyControl() -{ - dsyslog("[plex]%s\n", __FUNCTION__); - - delete Player; - - //delete Display; - delete Status; - - Hide(); - cStatus::MsgReplaying(this, NULL, NULL, false); - //Stop(); -} - -/** -** Hide control. -*/ -void cMyControl::Hide(void) -{ - dsyslog("[plex]%s:\n", __FUNCTION__); - - if (Display) { - delete Display; - - Display = NULL; - SetNeedsFastResponse(false); - } -} - -/** -** Process keyboard input. -** -** @param key pressed or releaded key -*/ -eOSState cMyControl::ProcessKey(eKeys key) -{ - eOSState state; - - if (key != kNone) { - dsyslog("[plex]%s: key=%d\n", __FUNCTION__, key); - } - - if (!PlayerIsRunning()) { // check if player is still alive - dsyslog("[plex]: player died\n"); - Hide(); - //FIXME: Stop(); - cControl::Shutdown(); - return osEnd; - } - - if (infoVisible) { // if RecordingInfo visible then update - if (timeoutShow && time(0) > timeoutShow) { - Hide(); - timeoutShow = 0; - infoVisible = false; - } else - ShowProgress(); - } - //state=cOsdMenu::ProcessKey(key); - state = osContinue; - switch ((int)key) { // cast to shutup g++ warnings - case kUp: - if (PlayerDvdNav) { - PlayerSendDvdNavUp(); - break; - } - case kPlay: - Hide(); - if (PlayerSpeed != 1) { - PlayerSendSetSpeed(PlayerSpeed = 1); - } - if (PlayerPaused) { - PlayerSendPause(); - PlayerPaused ^= 1; - } - Show(); - break; - - case kDown: - if (PlayerDvdNav) { - PlayerSendDvdNavDown(); - break; - } - case kPause: - PlayerSendPause(); - PlayerPaused ^= 1; - Show(); - break; - - case kFastRew | k_Release: - case kLeft | k_Release: - if (Setup.MultiSpeedMode) { - break; - } - // FIXME: - break; - case kLeft: - if (PlayerDvdNav) { - PlayerSendDvdNavLeft(); - break; - } - case kFastRew: - if (PlayerSpeed > 1) { - PlayerSendSetSpeed(PlayerSpeed /= 2); - } else { - PlayerSendSeek(-10); - } - Show(); - break; - case kRight: - if (PlayerDvdNav) { - PlayerSendDvdNavRight(); - break; - } - case kFastFwd: - if (PlayerSpeed < 32) { - PlayerSendSetSpeed(PlayerSpeed *= 2); - } - Show(); - break; - - case kRed: - // FIXME: TimeSearch(); - break; - -#ifdef USE_JUMPINGSECONDS - case kGreen | k_Repeat: - PlayerSendSeek(-Setup.JumpSecondsRepeat); - break; - case kGreen: - PlayerSendSeek(-Setup.JumpSeconds); - break; - case k1 | k_Repeat: - case k1: - PlayerSendSeek(-Setup.JumpSecondsSlow); - break; - case k3 | k_Repeat: - case k3: - PlayerSendSeek(Setup.JumpSecondsSlow); - break; - case kYellow | k_Repeat: - PlayerSendSeek(Setup.JumpSecondsRepeat); - break; - case kYellow: - PlayerSendSeek(Setup.JumpSeconds); - break; -#else - case kGreen | k_Repeat: - case kGreen: - PlayerSendSeek(-60); - break; - case kYellow | k_Repeat: - case kYellow: - PlayerSendSeek(+60); - break; -#endif /* JUMPINGSECONDS */ -#ifdef USE_LIEMIKUUTIO -#ifndef USE_JUMPINGSECONDS - case k1 | k_Repeat: - case k1: - PlayerSendSeek(-20); - break; - case k3 | k_Repeat: - case k3: - PlayerSendSeek(+20); - break; -#endif /* JUMPINGSECONDS */ -#endif - - case kStop: - case kBlue: - dsyslog("[plex]: player stopped\n"); - Hide(); - // FIXME: Stop(); - cControl::Shutdown(); - return osEnd; - - case kOk: - if (PlayerDvdNav) { - PlayerSendDvdNavSelect(); - // FIXME: PlayerDvdNav = 0; - break; - } - if (infoVisible) { - Hide(); - infoVisible = false; - } else - Show(); - break; - - case kBack: - if (PlayerDvdNav > 1) { - PlayerSendDvdNavPrev(); - break; - } - PlayerSendQuit(); - // FIXME: need to select old directory and index - // FIXME: this shows a half drawn OSD - cRemote::CallPlugin(PLUGINNAME); - return osBack; - - case kMenu: // VDR: eats the keys - case k5: - if (PlayerDvdNav) { - PlayerSendDvdNavMenu(); - break; - } - break; - - case kAudio: // VDR: eats the keys - case k7: - // FIXME: audio menu - PlayerSendSwitchAudio(); - break; - case kSubtitles: // VDR: eats the keys - case k9: - // FIXME: subtitle menu - PlayerSendSubSelect(); - break; - - default: - break; - } - - return state; -} /** ** Play a file. @@ -564,7 +121,8 @@ eOSState cMyControl::ProcessKey(eKeys key) static void PlayFile(const char *filename) { dsyslog("[plex]: play file '%s'\n", filename); - cControl::Launch(new cMyControl(filename)); + //cControl::Launch(new cMyControl(filename)); + cControl::Launch(new cHlsPlayerControl(new cHlsPlayer(filename), "Test Title")); } ////////////////////////////////////////////////////////////////////////////// @@ -695,7 +253,11 @@ eOSState cPlexBrowser::ProcessSelected() { if(item->IsVideo()) { plexclient::Video* pVid = item->GetAttachedVideo(); fullUri = pService->GetServer()->GetUri() + pVid->m_pMedia->m_sPartKey; - PlayFile(fullUri.c_str()); + //PlayFile(fullUri.c_str()); + //std::cout << "TrancodeUri: " << pService->GetTranscodeUrl(pVid) << std::endl; + std::cout << "TrancodeUri: " << pService->GetUniversalTranscodeUrl(pVid) << std::endl; + + PlayFile(pService->GetUniversalTranscodeUrl(pVid).c_str()); return osEnd; } @@ -791,292 +353,10 @@ eOSState cPlayMenu::ProcessKey(eKeys key) return state; } -////////////////////////////////////////////////////////////////////////////// -// cOsd -////////////////////////////////////////////////////////////////////////////// - - - -volatile char cMyOsd::Dirty; ///< flag force redraw everything - -/** -** Sets this OSD to be the active one. -** -** @param on true on, false off -** -** @note only needed as workaround for text2skin plugin with -** undrawn areas. -*/ -void cMyOsd::SetActive(bool on) -{ - dsyslog("[plex]%s: %d\n", __FUNCTION__, on); - - if (Active() == on) { - return; // already active, no action - } - cOsd::SetActive(on); - - // ignore sub-title, if menu is open - if (OsdLevel >= OSD_LEVEL_SUBTITLES && IsOpen()) { - return; - } - - if (on) { - Dirty = 1; - // only flush here if there are already bitmaps - //if (GetBitmap(0)) { - // Flush(); - //} - OsdOpen(); - } else { - OsdClose(); - } -} - -/** -** Constructor OSD. -** -** Initializes the OSD with the given coordinates. -** -** @param left x-coordinate of osd on display -** @param top y-coordinate of osd on display -** @param level level of the osd (smallest is shown) -*/ -cMyOsd::cMyOsd(int left, int top, uint level) -:cOsd(left, top, level) -{ - /* FIXME: OsdWidth/OsdHeight not correct! - dsyslog("[plex]%s: %dx%d+%d+%d, %d\n", __FUNCTION__, OsdWidth(), - OsdHeight(), left, top, level); - */ - - OsdLevel = level; - SetActive(true); -} - -/** -** OSD Destructor. -** -** Shuts down the OSD. -*/ -cMyOsd::~cMyOsd(void) -{ - dsyslog("[plex]%s:\n", __FUNCTION__); - SetActive(false); - // done by SetActive: OsdClose(); -} - -/** -** Actually commits all data to the OSD hardware. -*/ -void cMyOsd::Flush(void) -{ - cPixmapMemory *pm; - - dsyslog("[plex]%s: level %d active %d\n", __FUNCTION__, OsdLevel, - Active()); - - if (!Active()) { // this osd is not active - return; - } - // don't draw sub-title if menu is active - if (OsdLevel >= OSD_LEVEL_SUBTITLES && IsOpen()) { - return; - } - // - // VDR draws subtitle without clearing the old - // - if (OsdLevel >= OSD_LEVEL_SUBTITLES) { - OsdClear(); - cMyOsd::Dirty = 1; - dsyslog("[plex]%s: subtitle clear\n", __FUNCTION__); - } - - if (!IsTrueColor()) { - cBitmap *bitmap; - int i; - - // draw all bitmaps - for (i = 0; (bitmap = GetBitmap(i)); ++i) { - uint8_t *argb; - int x; - int y; - int w; - int h; - int x1; - int y1; - int x2; - int y2; - - // get dirty bounding box - if (Dirty) { // forced complete update - x1 = 0; - y1 = 0; - x2 = bitmap->Width() - 1; - y2 = bitmap->Height() - 1; - } else if (!bitmap->Dirty(x1, y1, x2, y2)) { - continue; // nothing dirty continue - } - // convert and upload only dirty areas - w = x2 - x1 + 1; - h = y2 - y1 + 1; - if (1) { // just for the case it makes trouble - int width; - int height; - double video_aspect; - - ::GetOsdSize(&width, &height, &video_aspect); - if (w > width) { - w = width; - x2 = x1 + width - 1; - } - if (h > height) { - h = height; - y2 = y1 + height - 1; - } - } -#ifdef DEBUG - if (w > bitmap->Width() || h > bitmap->Height()) { - esyslog(tr("[plex]: dirty area too big\n")); - abort(); - } -#endif - argb = (uint8_t *) malloc(w * h * sizeof(uint32_t)); - for (y = y1; y <= y2; ++y) { - for (x = x1; x <= x2; ++x) { - ((uint32_t *) argb)[x - x1 + (y - y1) * w] = - bitmap->GetColor(x, y); - } - } - dsyslog("[plex]%s: draw %dx%d%+d%+d bm\n", __FUNCTION__, w, h, - Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1); - OsdDrawARGB(Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1, - w, h, argb); - - bitmap->Clean(); - // FIXME: reuse argb - free(argb); - } - cMyOsd::Dirty = 0; - return; - } - - LOCK_PIXMAPS; - while ((pm = RenderPixmaps())) { - int x; - int y; - int w; - int h; - - x = Left() + pm->ViewPort().X(); - y = Top() + pm->ViewPort().Y(); - w = pm->ViewPort().Width(); - h = pm->ViewPort().Height(); - - dsyslog("[plex]%s: draw %dx%d%+d%+d %p\n", __FUNCTION__, w, h, x, y, - pm->Data()); - OsdDrawARGB(x, y, w, h, pm->Data()); - - delete pm; - } - cMyOsd::Dirty = 0; -} - -////////////////////////////////////////////////////////////////////////////// -// cOsdProvider -////////////////////////////////////////////////////////////////////////////// - -cOsd *cMyOsdProvider::Osd; ///< single osd - -/** -** Create a new OSD. -** -** @param left x-coordinate of OSD -** @param top y-coordinate of OSD -** @param level layer level of OSD -*/ -cOsd *cMyOsdProvider::CreateOsd(int left, int top, uint level) -{ - dsyslog("[plex]%s: %d, %d, %d\n", __FUNCTION__, left, top, level); - - return Osd = new cMyOsd(left, top, level); -} - -/** -** Check if this OSD provider is able to handle a true color OSD. -** -** @returns true we are able to handle a true color OSD. -*/ -bool cMyOsdProvider::ProvidesTrueColor(void) -{ - return true; -} - -/** -** Create cOsdProvider class. -*/ -cMyOsdProvider::cMyOsdProvider(void) -: cOsdProvider() -{ - dsyslog("[plex]%s:\n", __FUNCTION__); -} - - -////////////////////////////////////////////////////////////////////////////// -// cDevice -////////////////////////////////////////////////////////////////////////////// - -/** -** Device constructor. -*/ -cMyDevice::cMyDevice(void) -{ - dsyslog("[plex]%s\n", __FUNCTION__); -} - -/** -** Device destructor. (never called!) -*/ -cMyDevice::~cMyDevice(void) -{ - dsyslog("[plex]%s:\n", __FUNCTION__); -} - -/** -** Informs a device that it will be the primary device. -** -** @param on flag if becoming or loosing primary -*/ -void cMyDevice::MakePrimaryDevice(bool on) -{ - dsyslog("[plex]%s: %d\n", __FUNCTION__, on); - - cDevice::MakePrimaryDevice(on); - if (on) { - new cMyOsdProvider(); - } -} - -/** -** Returns the width, height and pixel_aspect ratio the OSD. -** -** FIXME: Called every second, for nothing (no OSD displayed)? -*/ -void cMyDevice::GetOsdSize(int &width, int &height, double &pixel_aspect) -{ - if (!&width || !&height || !&pixel_aspect) { - esyslog(tr("[plex]: GetOsdSize invalid pointer(s)\n")); - return; - } - ::GetOsdSize(&width, &height, &pixel_aspect); -} - ////////////////////////////////////////////////////////////////////////////// // cPlugin ////////////////////////////////////////////////////////////////////////////// -static cMyDevice *MyDevice; ///< dummy device needed for osd - /** ** Initialize any member variables here. ** @@ -1125,7 +405,6 @@ const char *cMyPlugin::Description(void) */ const char *cMyPlugin::CommandLineHelp(void) { - return::CommandLineHelp(); } /** @@ -1133,7 +412,6 @@ const char *cMyPlugin::CommandLineHelp(void) */ bool cMyPlugin::ProcessArgs(int argc, char *argv[]) { - return::ProcessArgs(argc, argv); } /** @@ -1162,8 +440,6 @@ bool cMyPlugin::Initialize(void) perror("No Plexserver found"); std::cout << "No Plexmediaserver found" << std::endl; } - - MyDevice = new cMyDevice; return true; } @@ -1198,14 +474,6 @@ cOsdObject *cMyPlugin::MainMenuAction(void) */ bool cMyPlugin::Service(const char *id, void *data) { - if (strcmp(id, PLEX_OSD_3DMODE_SERVICE) == 0) { - VideoSetOsd3DMode(0); - Play_Osd3DModeService_v1_0_t *r = - (Play_Osd3DModeService_v1_0_t *) data; - VideoSetOsd3DMode(r->Mode); - return true; - } - return false; } /** @@ -1216,11 +484,7 @@ bool cMyPlugin::Service(const char *id, void *data) */ const char **cMyPlugin::SVDRPHelpPages(void) { - static const char *HelpPages[] = { - "3DOF\n" " TURN OFF 3D", "3DTB\n" " TURN ON 3D TB", - "3DSB\n" " TURN ON 3D SBS", NULL - }; - return HelpPages; + return NULL; } /** @@ -1230,22 +494,8 @@ const char **cMyPlugin::SVDRPHelpPages(void) ** @param option all command arguments ** @param reply_code reply code */ -cString cMyPlugin::SVDRPCommand(const char *command, - __attribute__ ((unused)) const char *option, - __attribute__ ((unused)) int &reply_code) +cString cMyPlugin::SVDRPCommand(const char *command, const char *option, int &reply_code) { - if (!strcasecmp(command, "3DOF")) { - VideoSetOsd3DMode(0); - return "3d off"; - } - if (!strcasecmp(command, "3DSB")) { - VideoSetOsd3DMode(1); - return "3d sbs"; - } - if (!strcasecmp(command, "3DTB")) { - VideoSetOsd3DMode(2); - return "3d tb"; - } return NULL; } @@ -1301,39 +551,4 @@ bool cMyPlugin::SetupParse(const char *name, const char *value) return true; } -////////////////////////////////////////////////////////////////////////////// - -int OldPrimaryDevice; ///< old primary device - -/** -** Enable dummy device. -*/ -extern "C" void EnableDummyDevice(void) -{ - OldPrimaryDevice = cDevice::PrimaryDevice()->DeviceNumber() + 1; - - dsyslog("[plex]: primary device %d to new %d\n", OldPrimaryDevice, - MyDevice->DeviceNumber() + 1); - - //if (!cDevice::SetPrimaryDevice(MyDevice->DeviceNumber() + 1)) { - DoMakePrimary = MyDevice->DeviceNumber() + 1; - cOsdProvider::Shutdown(); - //} -} - -/** -** Disable dummy device. -*/ -extern "C" void DisableDummyDevice(void) -{ - dsyslog("[plex]: primary device %d to old %d\n", - cDevice::PrimaryDevice()->DeviceNumber() + 1, OldPrimaryDevice); - - //if (!cDevice::SetPrimaryDevice(OldPrimaryDevice)) { - DoMakePrimary = OldPrimaryDevice; - OldPrimaryDevice = 0; - cOsdProvider::Shutdown(); - //} -} - VDRPLUGINCREATOR(cMyPlugin); // Don't touch this! diff --git a/plex.h b/plex.h index a81f08e..d5e6dc5 100644 --- a/plex.h +++ b/plex.h @@ -16,6 +16,7 @@ #include "plexgdm.h" #include "play_service.h" #include "cPlexOsdItem.h" +#include "hlsPlayerControl.h" #include #include @@ -23,7 +24,6 @@ #include #include - /** ** Device plugin remote class. */ @@ -214,7 +214,8 @@ class cMyPlugin:public cPlugin virtual const char **SVDRPHelpPages(void); virtual cString SVDRPCommand(const char *, const char *, int &); - private: +private: + Poco::Crypto::OpenSSLInitializer _openSSLInitializer; //Bug in Poco::Crypto, see https://github.com/pocoproject/poco/issues/527 }; #endif \ No newline at end of file diff --git a/video.c b/video.c deleted file mode 100644 index 7fe2fff..0000000 --- a/video.c +++ /dev/null @@ -1,775 +0,0 @@ -/// -/// @file video.c @brief Video module -/// -/// Copyright (c) 2012, 2013 by Johns. All Rights Reserved. -/// -/// Contributor(s): -/// -/// License: AGPLv3 -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU Affero General Public License as -/// published by the Free Software Foundation, either version 3 of the -/// License. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU Affero General Public License for more details. -/// -/// $Id: 54d117902655b23d2eff9ecaf59ce4eab5f46d42 $ -////////////////////////////////////////////////////////////////////////////// - -/// -/// @defgroup Video The video module. -/// -/// This module contains all video rendering functions. -/// - -#include -#include -#include -#include -#include -#include - -#include -#define _(str) gettext(str) ///< gettext shortcut -#define _N(str) str ///< gettext_noop shortcut - -#include "misc.h" -#include "video.h" - -////////////////////////////////////////////////////////////////////////////// -// X11 / XCB -////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include -#include -#include // keysym XK_ -#include // XF86XK_ -#include - -static xcb_connection_t *Connection; ///< xcb connection -static xcb_colormap_t VideoColormap; ///< video colormap -static xcb_window_t VideoOsdWindow; ///< video osd window -static xcb_window_t VideoPlayWindow; ///< video player window -static xcb_screen_t const *VideoScreen; ///< video screen -static uint32_t VideoBlankTick; ///< blank cursor timer -static xcb_pixmap_t VideoPixmap; ///< blank cursor pixmap -static xcb_cursor_t VideoBlankCursor; ///< empty invisible cursor - -static uint32_t VideoColorKey; ///< color key pixel value - -static int VideoWindowX; ///< video output window x coordinate -static int VideoWindowY; ///< video outout window y coordinate -static unsigned VideoWindowWidth; ///< video output window width -static unsigned VideoWindowHeight; ///< video output window height - -static char Osd3DMode; ///< 3D OSD mode - -/// -/// Create X11 window. -/// -/// @param parent parent of new window -/// @param visual visual of parent -/// @param depth depth of parent -/// -/// @returns created X11 window id -/// -static xcb_window_t VideoCreateWindow(xcb_window_t parent, - xcb_visualid_t visual, uint8_t depth) -{ - uint32_t values[5]; - xcb_window_t window; - - Debug(3, "video: visual %#0x depth %d\n", visual, depth); - - // - // create color map - // - if (VideoColormap == XCB_NONE) { - VideoColormap = xcb_generate_id(Connection); - xcb_create_colormap(Connection, XCB_COLORMAP_ALLOC_NONE, VideoColormap, - parent, visual); - } - // - // create blank cursor - // - if (VideoBlankCursor == XCB_NONE) { - VideoPixmap = xcb_generate_id(Connection); - xcb_create_pixmap(Connection, 1, VideoPixmap, parent, 1, 1); - VideoBlankCursor = xcb_generate_id(Connection); - xcb_create_cursor(Connection, VideoBlankCursor, VideoPixmap, - VideoPixmap, 0, 0, 0, 0, 0, 0, 1, 1); - VideoBlankTick = 0; - } - - values[0] = VideoColorKey; // ARGB - values[1] = VideoColorKey; - values[2] = - XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | - XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY; - values[3] = VideoColormap; - values[4] = VideoBlankCursor; - window = xcb_generate_id(Connection); - xcb_create_window(Connection, depth, window, parent, VideoWindowX, - VideoWindowY, VideoWindowWidth, VideoWindowHeight, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, visual, - XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | - XCB_CW_COLORMAP | XCB_CW_CURSOR, values); - - // define only available with xcb-utils-0.3.8 -#ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS - // FIXME: utf _NET_WM_NAME - xcb_icccm_set_wm_name(Connection, window, XCB_ATOM_STRING, 8, - sizeof("play control") - 1, "play control"); - xcb_icccm_set_wm_icon_name(Connection, window, XCB_ATOM_STRING, 8, - sizeof("play control") - 1, "play control"); -#endif - // define only available with xcb-utils-0.3.6 -#ifdef XCB_NUM_WM_HINTS_ELEMENTS - // FIXME: utf _NET_WM_NAME - xcb_set_wm_name(Connection, window, XCB_ATOM_STRING, - sizeof("play control") - 1, "play control"); - xcb_set_wm_icon_name(Connection, window, XCB_ATOM_STRING, - sizeof("play control") - 1, "play control"); -#endif - - // FIXME: size hints - - // window above parent - values[0] = parent; - values[1] = XCB_STACK_MODE_ABOVE; - xcb_configure_window(Connection, window, - XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); - - return window; -} - -/// -/// Enable OSD 3d mode. -/// -/// @param mode turn 3d mode on/off -/// -void VideoSetOsd3DMode(int mode) -{ - Osd3DMode = mode; -} - -/// -/// Draw a ARGB image. -/// -/// @param x x position of image in osd -/// @param y y position of image in osd -/// @param width width of image -/// @param height height of image -/// @param argb argb image -/// -void VideoDrawARGB(int x, int y, int width, int height, const uint8_t * argb) -{ - xcb_image_t *xcb_image; - xcb_gcontext_t gc; - int sx; - int sy; - int fs; - - if (!Connection) { - Debug(3, "play: FIXME: must restore osd provider\n"); - return; - } - - if (x + y < 1 && (unsigned)height == VideoWindowHeight - && (unsigned)width == VideoWindowWidth) { - fs = 1; - } else { - fs = 0; - } - - gc = xcb_generate_id(Connection); - xcb_create_gc(Connection, gc, VideoOsdWindow, 0, NULL); - - switch (Osd3DMode) { - case 1: // SBS - xcb_image = - xcb_image_create_native(Connection, width / 2, height, - XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L, - NULL); - break; - case 2: // TB - xcb_image = - xcb_image_create_native(Connection, width, height / 2, - XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L, - NULL); - break; - default: - xcb_image = - xcb_image_create_native(Connection, width, height, - XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L, - NULL); - } - - // fast 32it versions - if (xcb_image->bpp == 32) { - if (xcb_image->byte_order == XCB_IMAGE_ORDER_LSB_FIRST) { - for (sy = 0; sy < height; ++sy) { - for (sx = 0; sx < width; ++sx) { - uint32_t pixel; - - if (argb[(width * sy + sx) * 4 + 3] < 200) { - pixel = VideoColorKey; - } else { - pixel = argb[(width * sy + sx) * 4 + 0] << 0; - pixel |= argb[(width * sy + sx) * 4 + 1] << 8; - pixel |= argb[(width * sy + sx) * 4 + 2] << 16; - } - switch (Osd3DMode) { - case 1: // SBS - xcb_image_put_pixel_Z32L(xcb_image, sx / 2, sy, - pixel); - break; - case 2: // TB - xcb_image_put_pixel_Z32L(xcb_image, sx, sy / 2, - pixel); - break; - default: - xcb_image_put_pixel_Z32L(xcb_image, sx, sy, pixel); - } - } - } - } else { - Error(_("play: unsupported put_image\n")); - } - } else { - for (sy = 0; sy < height; ++sy) { - for (sx = 0; sx < width; ++sx) { - uint32_t pixel; - - if (argb[(width * sy + sx) * 4 + 3] < 200) { - pixel = argb[(width * sy + sx) * 4 + 3] << 0; - pixel |= argb[(width * sy + sx) * 4 + 3] << 8; - pixel |= argb[(width * sy + sx) * 4 + 3] << 16; - } else { - pixel = argb[(width * sy + sx) * 4 + 0] << 0; - pixel |= argb[(width * sy + sx) * 4 + 1] << 8; - pixel |= argb[(width * sy + sx) * 4 + 2] << 16; - } - switch (Osd3DMode) { - case 1: // SBS - xcb_image_put_pixel(xcb_image, sx / 2, sy, pixel); - break; - case 2: // TB - xcb_image_put_pixel(xcb_image, sx, sy / 2, pixel); - break; - default: - xcb_image_put_pixel(xcb_image, sx, sy, pixel); - } - } - } - } - - // render xcb_image to color data pixmap - switch (Osd3DMode) { - case 1: // SBS - if (fs) { - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y, - 0); - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, - x + VideoWindowWidth / 2, y, 0); - } else { - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x / 2, - y, 0); - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, - x / 2 + VideoWindowWidth / 2, y, 0); - } - break; - case 2: // TB - if (fs) { - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y, - 0); - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, - y + VideoWindowHeight / 2, 0); - } else { - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, - y / 2, 0); - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, - y / 2 + VideoWindowHeight / 2, 0); - } - break; - default: - xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y, 0); - } - // release xcb_image - xcb_image_destroy(xcb_image); - xcb_free_gc(Connection, gc); - xcb_flush(Connection); -} - -/// -/// Show window. -/// -void VideoWindowShow(void) -{ - if (!Connection) { - Debug(3, "play: FIXME: must restore osd provider\n"); - return; - } - xcb_map_window(Connection, VideoOsdWindow); -} - -/// -/// Hide window. -/// -void VideoWindowHide(void) -{ - if (!Connection) { - Debug(3, "play: FIXME: must restore osd provider\n"); - return; - } - xcb_unmap_window(Connection, VideoOsdWindow); -} - -/// -/// Clear window. -/// -void VideoWindowClear(void) -{ - if (!Connection) { - Debug(3, "play: FIXME: must restore osd provider\n"); - return; - } - xcb_clear_area(Connection, 0, VideoOsdWindow, 0, 0, VideoWindowWidth, - VideoWindowHeight); - xcb_flush(Connection); -} - -static xcb_key_symbols_t *XcbKeySymbols; ///< Keyboard symbols -static uint16_t NumLockMask; ///< mod mask for num-lock -static uint16_t ShiftLockMask; ///< mod mask for shift-lock -static uint16_t CapsLockMask; ///< mod mask for caps-lock -static uint16_t ModeSwitchMask; ///< mod mask for mode-switch - -/// -/// Handle key press event. -/// -static void VideoKeyPress(const xcb_key_press_event_t * event) -{ - char buf[2]; - xcb_keysym_t ks0; - xcb_keysym_t ks1; - xcb_keysym_t keysym; - xcb_keycode_t keycode; - unsigned modifier; - - if (!XcbKeySymbols) { - XcbKeySymbols = xcb_key_symbols_alloc(Connection); - if (!XcbKeySymbols) { - Error(_("play/event: can't read key symbols\n")); - return; - } - NumLockMask = ShiftLockMask = CapsLockMask = ModeSwitchMask = 0; - - // FIXME: lock and mode keys are not prepared! - } - - keycode = event->detail; - modifier = event->state; - // handle mode-switch - if (modifier & ModeSwitchMask) { - ks0 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 2); - ks1 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 3); - } else { - ks0 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 0); - ks1 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 1); - } - // use first keysym, if second keysym didn't exists - if (ks1 == XCB_NO_SYMBOL) { - ks1 = ks0; - } - // see xcb-util-0.3.6/keysyms/keysyms.c: - if (!(modifier & XCB_MOD_MASK_SHIFT) && !(modifier & XCB_MOD_MASK_LOCK)) { - keysym = ks0; - } else { - // FIXME: more cases - - keysym = ks0; - } - - // FIXME: use xcb_lookup_string - switch (keysym) { - case XK_space: - FeedKeyPress("XKeySym", "space", 0, 0); - break; - case XK_exclam ... XK_slash: - case XK_0 ... XK_9: - case XK_A ... XK_Z: - case XK_a ... XK_z: - buf[0] = keysym; - buf[1] = '\0'; - FeedKeyPress("XKeySym", buf, 0, 0); - break; - - case XK_BackSpace: - FeedKeyPress("XKeySym", "BackSpace", 0, 0); - break; - case XK_Tab: - FeedKeyPress("XKeySym", "Tab", 0, 0); - break; - case XK_Return: - FeedKeyPress("XKeySym", "Return", 0, 0); - break; - case XK_Escape: - FeedKeyPress("XKeySym", "Escape", 0, 0); - break; - case XK_Delete: - FeedKeyPress("XKeySym", "Delete", 0, 0); - break; - - case XK_Home: - FeedKeyPress("XKeySym", "Home", 0, 0); - break; - case XK_Left: - FeedKeyPress("XKeySym", "Left", 0, 0); - break; - case XK_Up: - FeedKeyPress("XKeySym", "Up", 0, 0); - break; - case XK_Right: - FeedKeyPress("XKeySym", "Right", 0, 0); - break; - case XK_Down: - FeedKeyPress("XKeySym", "Down", 0, 0); - break; - case XK_Page_Up: - FeedKeyPress("XKeySym", "Page_Up", 0, 0); - break; - case XK_Page_Down: - FeedKeyPress("XKeySym", "Page_Down", 0, 0); - break; - case XK_End: - FeedKeyPress("XKeySym", "End", 0, 0); - break; - case XK_Begin: - FeedKeyPress("XKeySym", "Begin", 0, 0); - break; - - case XK_F1: - FeedKeyPress("XKeySym", "F1", 0, 0); - break; - case XK_F2: - FeedKeyPress("XKeySym", "F2", 0, 0); - break; - case XK_F3: - FeedKeyPress("XKeySym", "F3", 0, 0); - break; - case XK_F4: - FeedKeyPress("XKeySym", "F4", 0, 0); - break; - case XK_F5: - FeedKeyPress("XKeySym", "F5", 0, 0); - break; - case XK_F6: - FeedKeyPress("XKeySym", "F6", 0, 0); - break; - case XK_F7: - FeedKeyPress("XKeySym", "F7", 0, 0); - break; - case XK_F8: - FeedKeyPress("XKeySym", "F8", 0, 0); - break; - case XK_F9: - FeedKeyPress("XKeySym", "F9", 0, 0); - break; - case XK_F10: - FeedKeyPress("XKeySym", "F10", 0, 0); - break; - case XK_F11: - FeedKeyPress("XKeySym", "F11", 0, 0); - break; - case XK_F12: - FeedKeyPress("XKeySym", "F12", 0, 0); - break; - - case XF86XK_Red: - FeedKeyPress("XKeySym", "Red", 0, 0); - break; - case XF86XK_Green: - FeedKeyPress("XKeySym", "Green", 0, 0); - break; - case XF86XK_Yellow: - FeedKeyPress("XKeySym", "Yellow", 0, 0); - break; - case XF86XK_Blue: - FeedKeyPress("XKeySym", "Blue", 0, 0); - break; - - case XF86XK_HomePage: - FeedKeyPress("XKeySym", "XF86HomePage", 0, 0); - break; - case XF86XK_AudioLowerVolume: - FeedKeyPress("XKeySym", "XF86AudioLowerVolume", 0, 0); - break; - case XF86XK_AudioMute: - FeedKeyPress("XKeySym", "XF86AudioMute", 0, 0); - break; - case XF86XK_AudioRaiseVolume: - FeedKeyPress("XKeySym", "XF86AudioRaiseVolume", 0, 0); - break; - case XF86XK_AudioPlay: - FeedKeyPress("XKeySym", "XF86AudioPlay", 0, 0); - break; - case XF86XK_AudioStop: - FeedKeyPress("XKeySym", "XF86AudioStop", 0, 0); - break; - case XF86XK_AudioPrev: - FeedKeyPress("XKeySym", "XF86AudioPrev", 0, 0); - break; - case XF86XK_AudioNext: - FeedKeyPress("XKeySym", "XF86AudioNext", 0, 0); - break; - - default: - Debug(3, "play/event: keycode %d\n", event->detail); - break; - - } -} - -/// -/// Poll video events. -/// -/// @param timeout timeout in milliseconds -/// -void VideoPollEvents(int timeout) -{ - struct pollfd fds[1]; - xcb_generic_event_t *event; - int n; - int delay; - - if (!Connection) { - Debug(3, "play: poll without connection\n"); - return; - } - - fds[0].fd = xcb_get_file_descriptor(Connection); - fds[0].events = POLLIN | POLLPRI; - - delay = timeout; - for (;;) { - xcb_flush(Connection); - - // wait for events or timeout - // FIXME: this can poll forever - if ((n = poll(fds, 1, delay)) <= 0) { - // error or timeout - if (n) { // error - Error(_("play/event: poll failed: %s\n"), strerror(errno)); - } - return; - } - if (fds[0].revents & (POLLIN | POLLPRI)) { - if ((event = xcb_poll_for_event(Connection))) { - - switch (XCB_EVENT_RESPONSE_TYPE(event)) { -#if 0 - // background pixmap no need to redraw - case XCB_EXPOSE: - // collapse multi expose - if (!((xcb_expose_event_t *) event)->count) { - xcb_clear_area(Connection, 0, Window, 0, 0, 64, - 64); - // flush the request - xcb_flush(Connection); - } - break; -#endif - case XCB_MAP_NOTIFY: - Debug(3, "video/event: MapNotify\n"); - // hide cursor after mapping - xcb_change_window_attributes(Connection, - VideoOsdWindow, XCB_CW_CURSOR, &VideoBlankCursor); - xcb_change_window_attributes(Connection, - VideoPlayWindow, XCB_CW_CURSOR, &VideoBlankCursor); - break; - case XCB_DESTROY_NOTIFY: - return; - case XCB_KEY_PRESS: - VideoKeyPress((xcb_key_press_event_t *) event); - break; - case XCB_KEY_RELEASE: - case XCB_BUTTON_PRESS: - case XCB_BUTTON_RELEASE: - break; - case XCB_MOTION_NOTIFY: - break; - - case 0: - // error_code - Debug(3, "play/event: error %x\n", - event->response_type); - break; - default: - // unknown event type, ignore it - Debug(3, "play/event: unknown %x\n", - event->response_type); - break; - } - - free(event); - } else { - // no event, can happen, but we must check for close - if (xcb_connection_has_error(Connection)) { - return; - } - } - } - } -} - -/// -/// Get OSD size. -/// -/// @param[out] width OSD width -/// @param[out] height OSD height -/// -void VideoGetOsdSize(int *width, int *height) -{ - *width = 1920; - *height = 1080; // unknown default - if (VideoWindowWidth && VideoWindowHeight) { - *width = VideoWindowWidth; - *height = VideoWindowHeight; - } -} - -/// -/// Get player video window id. -/// -int VideoGetPlayWindow(void) -{ - return VideoPlayWindow; -} - -/// -/// Set video geometry. -/// -/// @todo write/search the good version -/// -void VideoSetGeometry(const char *geometry) -{ - sscanf(geometry, "%dx%d%d%d", &VideoWindowWidth, &VideoWindowHeight, - &VideoWindowX, &VideoWindowY); -} - -/// -/// Set video color key. -/// -/// Should be called before VideoInit(). -/// -void VideoSetColorKey(uint32_t color_key) -{ - VideoColorKey = color_key; -} - -/// -/// Initialize video. -/// -int VideoInit(const char *display) -{ - const char *display_name; - xcb_connection_t *connection; - xcb_screen_iterator_t iter; - int screen_nr; - int i; - - display_name = display ? display : getenv("DISPLAY"); - - // Open the connection to the X server. - connection = xcb_connect(display_name, &screen_nr); - if (!connection || xcb_connection_has_error(connection)) { - fprintf(stderr, "play: can't connect to X11 server on %s\n", - display_name); - return -1; - } - Connection = connection; - - // Get the requested screen number - iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); - for (i = 0; i < screen_nr; ++i) { - xcb_screen_next(&iter); - } - VideoScreen = iter.data; - - // - // Default window size - // - if (!VideoWindowHeight) { - if (VideoWindowWidth) { - VideoWindowHeight = (VideoWindowWidth * 9) / 16; - } else { // default to fullscreen - VideoWindowHeight = VideoScreen->height_in_pixels; - VideoWindowWidth = VideoScreen->width_in_pixels; - } - } - if (!VideoWindowWidth) { - VideoWindowWidth = (VideoWindowHeight * 16) / 9; - } - - VideoPlayWindow = - VideoCreateWindow(VideoScreen->root, VideoScreen->root_visual, - VideoScreen->root_depth); - xcb_map_window(Connection, VideoPlayWindow); - VideoOsdWindow = - VideoCreateWindow(VideoPlayWindow, VideoScreen->root_visual, - VideoScreen->root_depth); - Debug(3, "play: osd %x, play %x\n", VideoOsdWindow, VideoPlayWindow); - - VideoWindowClear(); - // done by clear: xcb_flush(Connection); - - return 0; -} - -/// -/// Cleanup video. -/// -void VideoExit(void) -{ - if (VideoOsdWindow != XCB_NONE) { - xcb_destroy_window(Connection, VideoOsdWindow); - VideoOsdWindow = XCB_NONE; - } - if (VideoPlayWindow != XCB_NONE) { - xcb_destroy_window(Connection, VideoPlayWindow); - VideoPlayWindow = XCB_NONE; - } - if (VideoColormap != XCB_NONE) { - xcb_free_colormap(Connection, VideoColormap); - VideoColormap = XCB_NONE; - } - if (VideoBlankCursor != XCB_NONE) { - xcb_free_cursor(Connection, VideoBlankCursor); - VideoBlankCursor = XCB_NONE; - } - if (VideoPixmap != XCB_NONE) { - xcb_free_pixmap(Connection, VideoPixmap); - VideoPixmap = XCB_NONE; - } - if (XcbKeySymbols != XCB_NONE) { - xcb_key_symbols_free(XcbKeySymbols); - XcbKeySymbols = XCB_NONE; - } - - if (Connection) { - xcb_flush(Connection); - xcb_disconnect(Connection); - Connection = NULL; - } -} diff --git a/video.h b/video.h deleted file mode 100644 index 246dc87..0000000 --- a/video.h +++ /dev/null @@ -1,62 +0,0 @@ -/// -/// @file video.h @brief Video module header file -/// -/// Copyright (c) 2012 by Johns. All Rights Reserved. -/// -/// Contributor(s): -/// -/// License: AGPLv3 -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU Affero General Public License as -/// published by the Free Software Foundation, either version 3 of the -/// License. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU Affero General Public License for more details. -/// -/// $Id: bc7bb8f7e869de413ebadbe58e4af1b603b6531e $ -////////////////////////////////////////////////////////////////////////////// - -/// @addtogroup Video -/// @{ - - /// C callback feed key press -extern void FeedKeyPress(const char *, const char *, int, int); - - /// Show window. -extern void VideoWindowShow(void); - - /// Hide window. -extern void VideoWindowHide(void); - - /// Clear window. -extern void VideoWindowClear(void); - - /// Poll video events. -extern void VideoPollEvents(int); - - /// Get player window id. -extern int VideoGetPlayWindow(void); - - /// Set Osd 3D Mode -extern void VideoSetOsd3DMode(int); - - /// Draw an OSD ARGB image. -extern void VideoDrawARGB(int, int, int, int, const uint8_t *); - - /// Get OSD size. -extern void VideoGetOsdSize(int *, int *); - - /// Set video geometry. -extern void VideoSetGeometry(const char *); - - /// Set video color key. -extern void VideoSetColorKey(uint32_t); - -extern int VideoInit(const char *); ///< Setup video module. -extern void VideoExit(void); ///< Cleanup and exit video module. - -/// @} -- cgit v1.2.3