diff options
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | PVideo.cpp | 1 | ||||
-rw-r--r-- | PVideo.h | 1 | ||||
-rw-r--r-- | PlexHTTPRequestHandler.cpp | 6 | ||||
-rw-r--r-- | Plexservice.cpp | 148 | ||||
-rw-r--r-- | Plexservice.h | 32 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | SubscriptionManager.cpp | 3 | ||||
-rw-r--r-- | SubscriptionManager.h | 2 | ||||
-rw-r--r-- | hlsPlayer.cpp | 326 | ||||
-rw-r--r-- | hlsPlayer.h | 90 | ||||
-rw-r--r-- | hlsPlayerControl.cpp | 24 | ||||
-rw-r--r-- | hlsPlayerControl.h | 27 | ||||
-rw-r--r-- | m3u8Parser.cpp | 121 | ||||
-rw-r--r-- | m3u8Parser.h | 37 | ||||
-rw-r--r-- | player.c | 984 | ||||
-rw-r--r-- | player.h | 160 | ||||
-rw-r--r-- | plex.cpp | 803 | ||||
-rw-r--r-- | plex.h | 5 | ||||
-rw-r--r-- | video.c | 775 | ||||
-rw-r--r-- | video.h | 62 |
21 files changed, 812 insertions, 2812 deletions
@@ -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): @@ -15,6 +15,7 @@ Video::Video(Poco::XML::Node* pNode) Poco::XML::AutoPtr<Poco::XML::NamedNodeMap> 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")); @@ -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 <memory> 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<SHA256> 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 <vector> #include <stdio.h> #include <stdlib.h> +#include <memory> +#include <time.h> #include <Poco/Base64Encoder.h> #include <Poco/Net/HTTPClientSession.h> @@ -21,6 +23,9 @@ #include <Poco/Exception.h> #include <Poco/Format.h> #include <Poco/URI.h> +#include <Poco/ScopedLock.h> +#include <Poco/Mutex.h> +#include <Poco/Format.h> #include <Poco/StreamCopier.h> @@ -28,8 +33,14 @@ #include "user.h" #include "MediaContainer.h" -#include <Poco/ScopedLock.h> -#include <Poco/Mutex.h> +#ifdef CRYPTOPP +#include <cryptopp/cryptlib.h> +#include <cryptopp/hmac.h> +#include <cryptopp/sha.h> +#include <cryptopp/base64.h> +#include <cryptopp/filters.h> +#include <cryptopp/hex.h> +#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 + }; } @@ -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 <mutex> #include <sstream> -#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 <Poco/Net/HTTPRequest.h> +#include <Poco/Net/HTTPResponse.h> +#include <Poco/StreamCopier.h> + +#include <pcrecpp.h> + +//--- 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<char*>(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<char*>(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 <string> +#include <iostream> +#include <sstream> + +#include <vdr/thread.h> +#include <vdr/player.h> +#include <vdr/ringbuffer.h> +#include <vdr/tools.h> + +#include <Poco/Net/HTTPClientSession.h> +#include <Poco/URI.h> + +#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 <vdr/player.h> +#include <vdr/tools.h> + +#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 <pcrecpp.h> + +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 <vector> +#include <string> +#include <iostream> + +class cM3u8Parser +{ +public: + struct playListItem { + int length; + int bandwidth; + int programId; + std::string file; + int size; + }; +private: + void Init(); + +public: + std::vector<playListItem> 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 <sys/types.h> -#include <sys/wait.h> - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include <poll.h> -#include <pthread.h> - -#include <libintl.h> -#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 @@ -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; } @@ -792,291 +354,9 @@ eOSState cPlayMenu::ProcessKey(eKeys key) } ////////////////////////////////////////////////////////////////////////////// -// 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! @@ -16,6 +16,7 @@ #include "plexgdm.h" #include "play_service.h" #include "cPlexOsdItem.h" +#include "hlsPlayerControl.h" #include <iostream> #include <string> @@ -23,7 +24,6 @@ #include <iterator> #include <algorithm> - /** ** 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 <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include <libintl.h> -#define _(str) gettext(str) ///< gettext shortcut -#define _N(str) str ///< gettext_noop shortcut - -#include "misc.h" -#include "video.h" - -////////////////////////////////////////////////////////////////////////////// -// X11 / XCB -////////////////////////////////////////////////////////////////////////////// - -#include <xcb/xcb.h> -#include <xcb/xcb_icccm.h> -#include <xcb/xcb_image.h> -#include <xcb/xcb_pixel.h> -#include <xcb/xcb_event.h> -#include <xcb/xcb_keysyms.h> -#include <X11/keysym.h> // keysym XK_ -#include <X11/XF86keysym.h> // XF86XK_ -#include <poll.h> - -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. - -/// @} |