From ffca21f2743a7b367fa212799c6e2fea6190dd5d Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 3 Mar 2015 16:53:59 +0100 Subject: initial commit for kodi master --- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp | 184 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h | 359 ++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp | 193 +++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h | 85 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp | 404 +++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h | 68 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp | 197 +++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h | 60 + .../cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp | 1767 ++++++++++++++++++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h | 172 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp | 391 +++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h | 68 + .../dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp | 493 ++++++ .../dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h | 118 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h | 36 + .../dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp | 199 +++ .../dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h | 60 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp | 89 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h | 31 + .../cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp | 247 +++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h | 99 ++ .../dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp | 153 ++ .../dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h | 30 + xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in | 19 + 24 files changed, 5522 insertions(+) create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in (limited to 'xbmc/cores/dvdplayer/DVDDemuxers') diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp new file mode 100644 index 0000000..2f833c5 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include "DVDCodecs/DVDCodecs.h" + +void CDemuxStreamTeletext::GetStreamInfo(std::string& strInfo) +{ + strInfo = "Teletext Data Stream"; +} + +void CDemuxStreamAudio::GetStreamType(std::string& strInfo) +{ + char sInfo[64]; + + if (codec == AV_CODEC_ID_AC3) strcpy(sInfo, "AC3 "); + else if (codec == AV_CODEC_ID_DTS) + { +#ifdef FF_PROFILE_DTS_HD_MA + if (profile == FF_PROFILE_DTS_HD_MA) + strcpy(sInfo, "DTS-HD MA "); + else if (profile == FF_PROFILE_DTS_HD_HRA) + strcpy(sInfo, "DTS-HD HRA "); + else +#endif + strcpy(sInfo, "DTS "); + } + else if (codec == AV_CODEC_ID_MP2) strcpy(sInfo, "MP2 "); + else if (codec == AV_CODEC_ID_TRUEHD) strcpy(sInfo, "Dolby TrueHD "); + else strcpy(sInfo, ""); + + if (iChannels == 1) strcat(sInfo, "Mono"); + else if (iChannels == 2) strcat(sInfo, "Stereo"); + else if (iChannels == 6) strcat(sInfo, "5.1"); + else if (iChannels == 8) strcat(sInfo, "7.1"); + else if (iChannels != 0) + { + char temp[32]; + sprintf(temp, " %d%s", iChannels, "-chs"); + strcat(sInfo, temp); + } + strInfo = sInfo; +} + +int CDVDDemux::GetNrOfAudioStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_AUDIO) iCounter++; + } + + return iCounter; +} + +int CDVDDemux::GetNrOfVideoStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_VIDEO) iCounter++; + } + + return iCounter; +} + +int CDVDDemux::GetNrOfSubtitleStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_SUBTITLE) iCounter++; + } + + return iCounter; +} + +int CDVDDemux::GetNrOfTeletextStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_TELETEXT) iCounter++; + } + + return iCounter; +} + +CDemuxStreamAudio* CDVDDemux::GetStreamFromAudioId(int iAudioIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_AUDIO) counter++; + if (iAudioIndex == counter) + return (CDemuxStreamAudio*)pStream; + } + return NULL; +} + +CDemuxStreamVideo* CDVDDemux::GetStreamFromVideoId(int iVideoIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_VIDEO) counter++; + if (iVideoIndex == counter) + return (CDemuxStreamVideo*)pStream; + } + return NULL; +} + +CDemuxStreamSubtitle* CDVDDemux::GetStreamFromSubtitleId(int iSubtitleIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_SUBTITLE) counter++; + if (iSubtitleIndex == counter) + return (CDemuxStreamSubtitle*)pStream; + } + return NULL; +} + +CDemuxStreamTeletext* CDVDDemux::GetStreamFromTeletextId(int iTeletextIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_TELETEXT) counter++; + if (iTeletextIndex == counter) + return (CDemuxStreamTeletext*)pStream; + } + return NULL; +} + +void CDemuxStream::GetStreamName( std::string& strInfo ) +{ + strInfo = ""; +} + +AVDiscard CDemuxStream::GetDiscard() +{ + return AVDISCARD_NONE; +} + +void CDemuxStream::SetDiscard(AVDiscard discard) +{ + return; +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h new file mode 100644 index 0000000..fca164d --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h @@ -0,0 +1,359 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include "system.h" +#include "DVDDemuxPacket.h" + +class CDVDInputStream; + +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS) + #include "config.h" +#endif + +extern "C" { +#include "libavcodec/avcodec.h" +} + +#ifndef __GNUC__ +#pragma warning(pop) +#endif + +enum AVDiscard; + +enum StreamType +{ + STREAM_NONE = 0,// if unknown + STREAM_AUDIO, // audio stream + STREAM_VIDEO, // video stream + STREAM_DATA, // data stream + STREAM_SUBTITLE,// subtitle stream + STREAM_TELETEXT // Teletext data stream +}; + +enum StreamSource { + STREAM_SOURCE_NONE = 0x000, + STREAM_SOURCE_DEMUX = 0x100, + STREAM_SOURCE_NAV = 0x200, + STREAM_SOURCE_DEMUX_SUB = 0x300, + STREAM_SOURCE_TEXT = 0x400, + STREAM_SOURCE_VIDEOMUX = 0x500 +}; + +#define STREAM_SOURCE_MASK(a) ((a) & 0xf00) + +/* + * CDemuxStream + * Base class for all demuxer streams + */ +class CDemuxStream +{ +public: + CDemuxStream() + { + iId = 0; + iPhysicalId = 0; + codec = (AVCodecID)0; // AV_CODEC_ID_NONE + codec_fourcc = 0; + profile = FF_PROFILE_UNKNOWN; + level = FF_LEVEL_UNKNOWN; + type = STREAM_NONE; + source = STREAM_SOURCE_NONE; + iDuration = 0; + pPrivate = NULL; + ExtraData = NULL; + ExtraSize = 0; + memset(language, 0, sizeof(language)); + disabled = false; + changes = 0; + flags = FLAG_NONE; + orig_type = 0; + } + + virtual ~CDemuxStream() + { + delete [] ExtraData; + } + + virtual void GetStreamInfo(std::string& strInfo) + { + strInfo = ""; + } + + virtual void GetStreamName(std::string& strInfo); + + virtual void SetDiscard(AVDiscard discard); + virtual AVDiscard GetDiscard(); + + int iId; // most of the time starting from 0 + int iPhysicalId; // id + AVCodecID codec; + unsigned int codec_fourcc; // if available + int profile; // encoder profile of the stream reported by the decoder. used to qualify hw decoders. + int level; // encoder level of the stream reported by the decoder. used to qualify hw decoders. + StreamType type; + int source; + + int iDuration; // in mseconds + void* pPrivate; // private pointer for the demuxer + uint8_t* ExtraData; // extra data for codec to use + unsigned int ExtraSize; // size of extra data + + char language[4]; // ISO 639 3-letter language code (empty string if undefined) + bool disabled; // set when stream is disabled. (when no decoder exists) + + int changes; // increment on change which player may need to know about + + int orig_type; // type of original source + + enum EFlags + { FLAG_NONE = 0x0000 + , FLAG_DEFAULT = 0x0001 + , FLAG_DUB = 0x0002 + , FLAG_ORIGINAL = 0x0004 + , FLAG_COMMENT = 0x0008 + , FLAG_LYRICS = 0x0010 + , FLAG_KARAOKE = 0x0020 + , FLAG_FORCED = 0x0040 + , FLAG_HEARING_IMPAIRED = 0x0080 + , FLAG_VISUAL_IMPAIRED = 0x0100 + } flags; +}; + +class CDemuxStreamVideo : public CDemuxStream +{ +public: + CDemuxStreamVideo() : CDemuxStream() + { + iFpsScale = 0; + iFpsRate = 0; + irFpsScale = 0; + irFpsRate = 0; + iHeight = 0; + iWidth = 0; + fAspect = 0.0; + bVFR = false; + bPTSInvalid = false; + bForcedAspect = false; + type = STREAM_VIDEO; + iOrientation = 0; + iBitsPerPixel = 0; + } + + virtual ~CDemuxStreamVideo() {} + int iFpsScale; // scale of 1000 and a rate of 29970 will result in 29.97 fps + int iFpsRate; + int irFpsScale; + int irFpsRate; + int iHeight; // height of the stream reported by the demuxer + int iWidth; // width of the stream reported by the demuxer + float fAspect; // display aspect of stream + bool bVFR; // variable framerate + bool bPTSInvalid; // pts cannot be trusted (avi's). + bool bForcedAspect; // aspect is forced from container + int iOrientation; // orientation of the video in degress counter clockwise + int iBitsPerPixel; + std::string stereo_mode; // expected stereo mode +}; + +class CDemuxStreamAudio : public CDemuxStream +{ +public: + CDemuxStreamAudio() : CDemuxStream() + { + iChannels = 0; + iSampleRate = 0; + iBlockAlign = 0; + iBitRate = 0; + iBitsPerSample = 0; + type = STREAM_AUDIO; + } + + virtual ~CDemuxStreamAudio() {} + + void GetStreamType(std::string& strInfo); + + int iChannels; + int iSampleRate; + int iBlockAlign; + int iBitRate; + int iBitsPerSample; +}; + +class CDemuxStreamSubtitle : public CDemuxStream +{ +public: + CDemuxStreamSubtitle() : CDemuxStream() + { + type = STREAM_SUBTITLE; + } +}; + +class CDemuxStreamTeletext : public CDemuxStream +{ +public: + CDemuxStreamTeletext() : CDemuxStream() + { + type = STREAM_TELETEXT; + } + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDVDDemux +{ +public: + + CDVDDemux() {} + virtual ~CDVDDemux() {} + + + /* + * Reset the entire demuxer (same result as closing and opening it) + */ + virtual void Reset() = 0; + + /* + * Aborts any internal reading that might be stalling main thread + * NOTICE - this can be called from another thread + */ + virtual void Abort() = 0; + + /* + * Flush the demuxer, if any data is kept in buffers, this should be freed now + */ + virtual void Flush() = 0; + + /* + * Read a packet, returns NULL on error + * + */ + virtual DemuxPacket* Read() = 0; + + /* + * Seek, time in msec calculated from stream start + */ + virtual bool SeekTime(int time, bool backwords = false, double* startpts = NULL) = 0; + + /* + * Seek to a specified chapter. + * startpts can be updated to the point where display should start + */ + virtual bool SeekChapter(int chapter, double* startpts = NULL) { return false; } + + /* + * Get the number of chapters available + */ + virtual int GetChapterCount() { return 0; } + + /* + * Get current chapter + */ + virtual int GetChapter() { return 0; } + + /* + * Get the name of a chapter + * \param strChapterName[out] Name of chapter + * \param chapterIdx -1 for current chapter, else a chapter index + */ + virtual void GetChapterName(std::string& strChapterName, int chapterIdx=-1) {} + + /* + * Get the position of a chapter + * \param chapterIdx -1 for current chapter, else a chapter index + */ + virtual int64_t GetChapterPos(int chapterIdx=-1) { return 0; } + + /* + * Set the playspeed, if demuxer can handle different + * speeds of playback + */ + virtual void SetSpeed(int iSpeed) = 0; + + /* + * returns the total time in msec + */ + virtual int GetStreamLength() = 0; + + /* + * returns the stream or NULL on error, starting from 0 + */ + virtual CDemuxStream* GetStream(int iStreamId) = 0; + + /* + * return nr of streams, 0 if none + */ + virtual int GetNrOfStreams() = 0; + + /* + * returns opened filename + */ + virtual std::string GetFileName() = 0; + /* + * return nr of audio streams, 0 if none + */ + int GetNrOfAudioStreams(); + + /* + * return nr of video streams, 0 if none + */ + int GetNrOfVideoStreams(); + + /* + * return nr of subtitle streams, 0 if none + */ + int GetNrOfSubtitleStreams(); + + /* + * return nr of teletext streams, 0 if none + */ + int GetNrOfTeletextStreams(); + + /* + * return the audio stream, or NULL if it does not exist + */ + CDemuxStreamAudio* GetStreamFromAudioId(int iAudioIndex); + + /* + * return the video stream, or NULL if it does not exist + */ + CDemuxStreamVideo* GetStreamFromVideoId(int iVideoIndex); + + /* + * return the subtitle stream, or NULL if it does not exist + */ + CDemuxStreamSubtitle* GetStreamFromSubtitleId(int iSubtitleIndex); + + /* + * return the teletext stream, or NULL if it does not exist + */ + CDemuxStreamTeletext* GetStreamFromTeletextId(int iTeletextIndex); + + /* + * return a user-presentable codec name of the given stream + */ + virtual void GetStreamCodecName(int iStreamId, std::string &strName) {}; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp new file mode 100644 index 0000000..9786983 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxBXA.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "../DVDClock.h" + +// AirTunes audio Demuxer. + +using namespace std; + +class CDemuxStreamAudioBXA + : public CDemuxStreamAudio +{ + CDVDDemuxBXA *m_parent; + string m_codec; +public: + CDemuxStreamAudioBXA(CDVDDemuxBXA *parent, const string& codec) + : m_parent(parent) + , m_codec(codec) + + {} + void GetStreamInfo(string& strInfo) + { + strInfo = StringUtils::Format("%s", m_codec.c_str()); + } +}; + +CDVDDemuxBXA::CDVDDemuxBXA() : CDVDDemux() +{ + m_pInput = NULL; + m_stream = NULL; + m_bytes = 0; + memset(&m_header, 0x0, sizeof(Demux_BXA_FmtHeader)); +} + +CDVDDemuxBXA::~CDVDDemuxBXA() +{ + Dispose(); +} + +bool CDVDDemuxBXA::Open(CDVDInputStream* pInput) +{ + Abort(); + + Dispose(); + + if(!pInput || !pInput->IsStreamType(DVDSTREAM_TYPE_FILE)) + return false; + + if(pInput->Read((uint8_t *)&m_header, sizeof(Demux_BXA_FmtHeader)) < 1) + return false; + + // file valid? + if (strncmp(m_header.fourcc, "BXA ", 4) != 0 || m_header.type != BXA_PACKET_TYPE_FMT_DEMUX) + { + pInput->Seek(0, SEEK_SET); + return false; + } + + m_pInput = pInput; + + m_stream = new CDemuxStreamAudioBXA(this, "BXA"); + + if(!m_stream) + return false; + + m_stream->iSampleRate = m_header.sampleRate; + m_stream->iBitsPerSample = m_header.bitsPerSample; + m_stream->iBitRate = m_header.sampleRate * m_header.channels * m_header.bitsPerSample; + m_stream->iChannels = m_header.channels; + m_stream->type = STREAM_AUDIO; + m_stream->codec = AV_CODEC_ID_PCM_S16LE; + + return true; +} + +void CDVDDemuxBXA::Dispose() +{ + delete m_stream; + m_stream = NULL; + + m_pInput = NULL; + m_bytes = 0; + + memset(&m_header, 0x0, sizeof(Demux_BXA_FmtHeader)); +} + +void CDVDDemuxBXA::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxBXA::Abort() +{ + if(m_pInput) + return m_pInput->Abort(); +} + +void CDVDDemuxBXA::Flush() +{ +} + +#define BXA_READ_SIZE 4096 +DemuxPacket* CDVDDemuxBXA::Read() +{ + if(!m_pInput) + return NULL; + + DemuxPacket* pPacket = CDVDDemuxUtils::AllocateDemuxPacket(BXA_READ_SIZE); + + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + pPacket->iSize = m_pInput->Read(pPacket->pData, BXA_READ_SIZE); + pPacket->iStreamId = 0; + + if(pPacket->iSize < 1) + { + delete pPacket; + pPacket = NULL; + } + else + { + int n = (m_header.channels * m_header.bitsPerSample * m_header.sampleRate)>>3; + if (n > 0) + { + m_bytes += pPacket->iSize; + pPacket->dts = (double)m_bytes * DVD_TIME_BASE / n; + pPacket->pts = pPacket->dts; + } + else + { + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + } + } + + return pPacket; +} + +CDemuxStream* CDVDDemuxBXA::GetStream(int iStreamId) +{ + if(iStreamId != 0) + return NULL; + + return m_stream; +} + +int CDVDDemuxBXA::GetNrOfStreams() +{ + return (m_stream == NULL ? 0 : 1); +} + +std::string CDVDDemuxBXA::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxBXA::GetStreamCodecName(int iStreamId, std::string &strName) +{ + if (m_stream && iStreamId == 0) + strName = "BXA"; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h new file mode 100644 index 0000000..bdb65b4 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h @@ -0,0 +1,85 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" + +#ifdef TARGET_WINDOWS +#define __attribute__(dummy_val) +#else +#include +#endif + +#ifdef TARGET_WINDOWS +#pragma pack(push) +#pragma pack(1) +#endif + +typedef struct +{ + char fourcc[4]; + uint32_t type; + uint32_t channels; + uint32_t sampleRate; + uint32_t bitsPerSample; + uint64_t durationMs; +} __attribute__((__packed__)) Demux_BXA_FmtHeader; + +#ifdef TARGET_WINDOWS +#pragma pack(pop) +#endif + +#include + +#define BXA_PACKET_TYPE_FMT_DEMUX 1 + +class CDemuxStreamAudioBXA; + +class CDVDDemuxBXA : public CDVDDemux +{ +public: + + CDVDDemuxBXA(); + ~CDVDDemuxBXA(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; } + void SetSpeed(int iSpeed) {}; + int GetStreamLength() { return (int)m_header.durationMs; } + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + +protected: + friend class CDemuxStreamAudioBXA; + CDVDInputStream* m_pInput; + int64_t m_bytes; + + CDemuxStreamAudioBXA *m_stream; + + Demux_BXA_FmtHeader m_header; +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp new file mode 100644 index 0000000..24d56da --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxUtils.h" +#include "DVDClock.h" +#include "DVDDemuxCC.h" +#include "cores/dvdplayer/DVDCodecs/Overlay/contrib/cc_decoder708.h" +#include "utils/log.h" + +#include + +class CBitstream +{ +public: + CBitstream(uint8_t *data, int bits) + { + m_data = data; + m_offset = 0; + m_len = bits; + m_error = false; + } + unsigned int readBits(int num) + { + int r = 0; + while (num > 0) + { + if (m_offset >= m_len) + { + m_error = true; + return 0; + } + num--; + if (m_data[m_offset / 8] & (1 << (7 - (m_offset & 7)))) + r |= 1 << num; + m_offset++; + } + return r; + } + unsigned int readGolombUE(int maxbits = 32) + { + int lzb = -1; + int bits = 0; + for (int b = 0; !b; lzb++, bits++) + { + if (bits > maxbits) + return 0; + b = readBits(1); + } + return (1 << lzb) - 1 + readBits(lzb); + } + +private: + uint8_t *m_data; + int m_offset; + int m_len; + bool m_error; +}; + +class CCaptionBlock +{ +public: + CCaptionBlock(int size) + { + m_data = (uint8_t*)malloc(size); + m_size = size; + } + virtual ~CCaptionBlock() + { + free(m_data); + } + double m_pts; + uint8_t *m_data; + int m_size; +}; + +bool reorder_sort (CCaptionBlock *lhs, CCaptionBlock *rhs) +{ + return (lhs->m_pts > rhs->m_pts); +} + +CDVDDemuxCC::CDVDDemuxCC(AVCodecID codec) +{ + m_hasData = false; + m_curPts = 0; + m_ccDecoder = NULL; + m_codec = codec; +} + +CDVDDemuxCC::~CDVDDemuxCC() +{ + Dispose(); +} + +CDemuxStream* CDVDDemuxCC::GetStream(int iStreamId) +{ + return &m_streams[iStreamId]; +} + +int CDVDDemuxCC::GetNrOfStreams() +{ + return m_streams.size(); +} + +DemuxPacket* CDVDDemuxCC::Read(DemuxPacket *pSrcPacket) +{ + DemuxPacket *pPacket = NULL; + uint32_t startcode = 0xffffffff; + int picType = 0; + int p = 0; + int len; + + if (!pSrcPacket) + { + pPacket = Decode(); + return pPacket; + } + if (pSrcPacket->pts == DVD_NOPTS_VALUE) + { + return pPacket; + } + + while (!m_ccTempBuffer.empty()) + { + m_ccReorderBuffer.push_back(m_ccTempBuffer.back()); + m_ccTempBuffer.pop_back(); + } + + while ((len = pSrcPacket->iSize - p) > 3) + { + if ((startcode & 0xffffff00) == 0x00000100) + { + if (m_codec == AV_CODEC_ID_MPEG2VIDEO) + { + int scode = startcode & 0xFF; + if (scode == 0x00) + { + if (len > 4) + { + uint8_t *buf = pSrcPacket->pData + p; + picType = (buf[1] & 0x38) >> 3; + } + } + else if (scode == 0xb2) // user data + { + uint8_t *buf = pSrcPacket->pData + p; + if (len >= 6 && + buf[0] == 'G' && buf[1] == 'A' && buf[2] == '9' && buf[3] == '4' && + buf[4] == 3 && (buf[5] & 0x40)) + { + int cc_count = buf[5] & 0x1f; + if (cc_count > 0 && len >= 7 + cc_count * 3) + { + CCaptionBlock *cc = new CCaptionBlock(cc_count * 3); + memcpy(cc->m_data, buf + 7, cc_count * 3); + cc->m_pts = pSrcPacket->pts; + if (picType == 1 || picType == 2) + m_ccTempBuffer.push_back(cc); + else + m_ccReorderBuffer.push_back(cc); + } + } + else if (len >= 6 && + buf[0] == 'C' && buf[1] == 'C' && buf[2] == 1) + { + int oddidx = (buf[4] & 0x80) ? 0 : 1; + int cc_count = (buf[4] & 0x3e) >> 1; + int extrafield = buf[4] & 0x01; + if (extrafield) + cc_count++; + + if (cc_count > 0 && len >= 5 + cc_count * 3 * 2) + { + CCaptionBlock *cc = new CCaptionBlock(cc_count * 3); + uint8_t *src = buf + 5; + uint8_t *dst = cc->m_data; + + for (int i = 0; i < cc_count; i++) + { + for (int j = 0; j < 2; j++) + { + if (i == cc_count - 1 && extrafield && j == 1) + break; + + if ((oddidx == j) && (src[0] == 0xFF)) + { + dst[0] = 0x04; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + } + src += 3; + } + } + cc->m_pts = pSrcPacket->pts; + m_ccReorderBuffer.push_back(cc); + picType = 1; + } + } + } + } + else if (m_codec == AV_CODEC_ID_H264) + { + int scode = startcode & 0x9F; + // slice data comes after SEI + if (scode >= 1 && scode <= 5) + { + uint8_t *buf = pSrcPacket->pData + p; + CBitstream bs(buf, len * 8); + bs.readGolombUE(); + int sliceType = bs.readGolombUE(); + if (sliceType == 2 || sliceType == 7) // I slice + picType = 1; + else if (sliceType == 0 || sliceType == 5) // P slice + picType = 2; + if (picType == 0) + { + while (!m_ccTempBuffer.empty()) + { + m_ccReorderBuffer.push_back(m_ccTempBuffer.back()); + m_ccTempBuffer.pop_back(); + } + } + } + if (scode == 0x06) // SEI + { + uint8_t *buf = pSrcPacket->pData + p; + if (len >= 12 && + buf[3] == 0 && buf[4] == 49 && + buf[5] == 'G' && buf[6] == 'A' && buf[7] == '9' && buf[8] == '4' && buf[9] == 3) + { + uint8_t *userdata = buf + 10; + int cc_count = userdata[0] & 0x1f; + if (len >= cc_count * 3 + 10) + { + CCaptionBlock *cc = new CCaptionBlock(cc_count * 3); + memcpy(cc->m_data, userdata + 2, cc_count * 3); + cc->m_pts = pSrcPacket->pts; + m_ccTempBuffer.push_back(cc); + } + } + } + } + } + startcode = startcode << 8 | pSrcPacket->pData[p++]; + } + + if ((picType == 1 || picType == 2) && !m_ccReorderBuffer.empty()) + { + if (!m_ccDecoder) + { + if (!OpenDecoder()) + return NULL; + } + std::sort(m_ccReorderBuffer.begin(), m_ccReorderBuffer.end(), reorder_sort); + pPacket = Decode(); + } + return pPacket; +} + +void CDVDDemuxCC::Handler(int service, void *userdata) +{ + CDVDDemuxCC *ctx = (CDVDDemuxCC*)userdata; + + unsigned int idx; + + // switch back from 608 fallback if we got 708 + if (ctx->m_ccDecoder->m_seen608 && ctx->m_ccDecoder->m_seen708) + { + for (idx = 0; idx < ctx->m_streamdata.size(); idx++) + { + if (ctx->m_streamdata[idx].service == 0) + break; + } + if (idx < ctx->m_streamdata.size()) + { + ctx->m_streamdata.erase(ctx->m_streamdata.begin() + idx); + ctx->m_ccDecoder->m_seen608 = false; + } + if (service == 0) + return; + } + + for (idx = 0; idx < ctx->m_streamdata.size(); idx++) + { + if (ctx->m_streamdata[idx].service == service) + break; + } + if (idx >= ctx->m_streamdata.size()) + { + CDemuxStreamSubtitle stream; + strcpy(stream.language, "cc"); + stream.codec = AV_CODEC_ID_TEXT; + stream.iPhysicalId = service; + stream.iId = idx; + ctx->m_streams.push_back(stream); + + streamdata data; + data.streamIdx = idx; + data.service = service; + ctx->m_streamdata.push_back(data); + + if (service == 0) + ctx->m_ccDecoder->m_seen608 = true; + else + ctx->m_ccDecoder->m_seen708 = true; + } + + ctx->m_streamdata[idx].pts = ctx->m_curPts; + ctx->m_streamdata[idx].hasData = true; + ctx->m_hasData = true; +} + +bool CDVDDemuxCC::OpenDecoder() +{ + m_ccDecoder = new CDecoderCC708(); + m_ccDecoder->Init(Handler, this); + return true; +} + +void CDVDDemuxCC::Dispose() +{ + m_streams.clear(); + m_streamdata.clear(); + delete m_ccDecoder; + m_ccDecoder = NULL; + + while (!m_ccReorderBuffer.empty()) + { + delete m_ccReorderBuffer.back(); + m_ccReorderBuffer.pop_back(); + } + while (!m_ccTempBuffer.empty()) + { + delete m_ccTempBuffer.back(); + m_ccTempBuffer.pop_back(); + } +} + +DemuxPacket* CDVDDemuxCC::Decode() +{ + DemuxPacket *pPacket = NULL; + + while(!m_hasData && !m_ccReorderBuffer.empty()) + { + CCaptionBlock *cc = m_ccReorderBuffer.back(); + m_ccReorderBuffer.pop_back(); + m_curPts = cc->m_pts; + m_ccDecoder->Decode(cc->m_data, cc->m_size); + delete cc; + } + + if (m_hasData) + { + for (unsigned int i=0; im_cc608decoder->text; + len = m_ccDecoder->m_cc608decoder->textlen; + } + else + { + data = m_ccDecoder->m_cc708decoders[service].text; + len = m_ccDecoder->m_cc708decoders[service].textlen; + } + + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(len); + pPacket->iSize = len; + memcpy(pPacket->pData, data, pPacket->iSize); + + pPacket->iStreamId = i; + pPacket->pts = m_streamdata[i].pts; + pPacket->duration = 0; + m_streamdata[i].hasData = false; + break; + } + m_hasData = false; + } + } + return pPacket; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h new file mode 100644 index 0000000..ae78298 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once +#include "DVDDemux.h" +#include + +class CCaptionBlock; +class CDecoderCC708; + +class CDVDDemuxCC : public CDVDDemux +{ +public: + CDVDDemuxCC(AVCodecID codec); + virtual ~CDVDDemuxCC(); + + virtual void Reset() {}; + virtual void Abort() {}; + virtual void Flush() {}; + virtual DemuxPacket* Read() { return NULL; }; + virtual bool SeekTime(int time, bool backwords = false, double* startpts = NULL) {return true;}; + virtual void SetSpeed(int iSpeed) {}; + virtual int GetStreamLength() {return 0;}; + virtual CDemuxStream* GetStream(int iStreamId); + virtual int GetNrOfStreams(); + virtual std::string GetFileName() {return "";}; + + DemuxPacket* Read(DemuxPacket *packet); + static void Handler(int service, void *userdata); + +protected: + bool OpenDecoder(); + void Dispose(); + DemuxPacket* Decode(); + + struct streamdata + { + int streamIdx; + int service; + bool hasData ; + double pts; + }; + std::vector m_streamdata; + std::vector m_streams; + bool m_hasData; + double m_curPts; + std::vector m_ccReorderBuffer; + std::vector m_ccTempBuffer; + CDecoderCC708 *m_ccDecoder; + AVCodecID m_codec; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp new file mode 100644 index 0000000..72067da --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxCDDA.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "../DVDClock.h" + +// CDDA audio demuxer based on AirTunes audio Demuxer. + +using namespace std; + +class CDemuxStreamAudioCDDA + : public CDemuxStreamAudio +{ +public: + void GetStreamInfo(string& strInfo) + { + strInfo = "pcm"; + } +}; + +CDVDDemuxCDDA::CDVDDemuxCDDA() : CDVDDemux() +{ + m_pInput = NULL; + m_stream = NULL; + m_bytes = 0; +} + +CDVDDemuxCDDA::~CDVDDemuxCDDA() +{ + Dispose(); +} + +bool CDVDDemuxCDDA::Open(CDVDInputStream* pInput) +{ + Abort(); + + Dispose(); + + if(!pInput || !pInput->IsStreamType(DVDSTREAM_TYPE_FILE)) + return false; + + m_pInput = pInput; + + m_stream = new CDemuxStreamAudioCDDA(); + + if(!m_stream) + return false; + + m_stream->iSampleRate = 44100; + m_stream->iBitsPerSample = 16; + m_stream->iBitRate = 44100 * 2 * 16; + m_stream->iChannels = 2; + m_stream->type = STREAM_AUDIO; + m_stream->codec = AV_CODEC_ID_PCM_S16LE; + + return true; +} + +void CDVDDemuxCDDA::Dispose() +{ + delete m_stream; + m_stream = NULL; + + m_pInput = NULL; + m_bytes = 0; +} + +void CDVDDemuxCDDA::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxCDDA::Abort() +{ + if(m_pInput) + return m_pInput->Abort(); +} + +void CDVDDemuxCDDA::Flush() +{ +} + +#define CDDA_READ_SIZE 4096 +DemuxPacket* CDVDDemuxCDDA::Read() +{ + if(!m_pInput) + return NULL; + + DemuxPacket* pPacket = CDVDDemuxUtils::AllocateDemuxPacket(CDDA_READ_SIZE); + + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + pPacket->iSize = m_pInput->Read(pPacket->pData, CDDA_READ_SIZE); + pPacket->iStreamId = 0; + + if(pPacket->iSize < 1) + { + delete pPacket; + pPacket = NULL; + } + else + { + int n = m_stream->iBitRate>>3; + if (n > 0) + { + m_bytes += pPacket->iSize; + pPacket->dts = (double)m_bytes * DVD_TIME_BASE / n; + pPacket->pts = pPacket->dts; + } + else + { + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + } + } + + return pPacket; +} + +bool CDVDDemuxCDDA::SeekTime(int time, bool backwords, double* startpts) +{ + int bytes_per_second = m_stream->iBitRate>>3; + // clamp seeks to bytes per full sample + int clamp_bytes = (m_stream->iBitsPerSample>>3) * m_stream->iChannels; + + // time is in milliseconds + int64_t seekPos = m_pInput->Seek((((int64_t)time * bytes_per_second / 1000) / clamp_bytes ) * clamp_bytes, SEEK_SET) > 0; + if (seekPos > 0) + m_bytes = seekPos; + + if (startpts) + *startpts = (double)m_bytes * DVD_TIME_BASE / bytes_per_second; + + return seekPos > 0; +}; + +int CDVDDemuxCDDA::GetStreamLength() +{ + int64_t num_track_bytes = m_pInput->GetLength(); + int bytes_per_second = (m_stream->iBitRate>>3); + int64_t track_mseconds = num_track_bytes*1000 / bytes_per_second; + return (int)track_mseconds; +} + +CDemuxStream* CDVDDemuxCDDA::GetStream(int iStreamId) +{ + if(iStreamId != 0) + return NULL; + + return m_stream; +} + +int CDVDDemuxCDDA::GetNrOfStreams() +{ + return (m_stream == NULL ? 0 : 1); +} + +std::string CDVDDemuxCDDA::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxCDDA::GetStreamCodecName(int iStreamId, std::string &strName) +{ + if (m_stream && iStreamId == 0) + strName = "pcm"; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h new file mode 100644 index 0000000..6a3c50a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h @@ -0,0 +1,60 @@ +#pragma once +/* + * Copyright (C) 2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include "utils/log.h" + +#ifdef TARGET_WINDOWS +#define __attribute__(dummy_val) +#else +#include +#endif + +class CDemuxStreamAudioCDDA; + +class CDVDDemuxCDDA : public CDVDDemux +{ +public: + + CDVDDemuxCDDA(); + ~CDVDDemuxCDDA(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + void SetSpeed(int iSpeed) {}; + int GetStreamLength() ; + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + +protected: + friend class CDemuxStreamAudioCDDA; + CDVDInputStream* m_pInput; + int64_t m_bytes; + + CDemuxStreamAudioCDDA *m_stream; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp new file mode 100644 index 0000000..f58472f --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp @@ -0,0 +1,1767 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "system.h" +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifdef TARGET_POSIX +#include "stdint.h" +#endif +#include "DVDDemuxFFmpeg.h" +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDInputStreams/DVDInputStreamNavigator.h" +#ifdef HAVE_LIBBLURAY +#include "DVDInputStreams/DVDInputStreamBluray.h" +#endif +#include "DVDInputStreams/DVDInputStreamPVRManager.h" +#include "DVDInputStreams/DVDInputStreamFFmpeg.h" +#include "DVDDemuxUtils.h" +#include "DVDClock.h" // for DVD_TIME_BASE +#include "commons/Exception.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "filesystem/File.h" +#include "filesystem/CurlFile.h" +#include "filesystem/Directory.h" +#include "utils/log.h" +#include "threads/Thread.h" +#include "threads/SystemClock.h" +#include "utils/TimeUtils.h" +#include "utils/StringUtils.h" +#include "URL.h" +#include "cores/FFmpeg.h" + +extern "C" { +#include "libavutil/opt.h" +} + +struct StereoModeConversionMap +{ + const char* name; + const char* mode; +}; + +// we internally use the matroska string representation of stereoscopic modes. +// This struct is a conversion map to convert stereoscopic mode values +// from asf/wmv to the internally used matroska ones +static const struct StereoModeConversionMap WmvToInternalStereoModeMap[] = +{ + { "SideBySideRF", "right_left" }, + { "SideBySideLF", "left_right" }, + { "OverUnderRT", "bottom_top" }, + { "OverUnderLT", "top_bottom" }, + {} +}; + +#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE) + +void CDemuxStreamAudioFFmpeg::GetStreamInfo(std::string& strInfo) +{ + if(!m_stream) return; + char temp[128]; + avcodec_string(temp, 128, m_stream->codec, 0); + strInfo = temp; +} + +void CDemuxStreamAudioFFmpeg::GetStreamName(std::string& strInfo) +{ + if(!m_stream) return; + if(!m_description.empty()) + strInfo = m_description; + else + CDemuxStream::GetStreamName(strInfo); +} + +void CDemuxStreamSubtitleFFmpeg::GetStreamName(std::string& strInfo) +{ + if(!m_stream) return; + if(!m_description.empty()) + strInfo = m_description; + else + CDemuxStream::GetStreamName(strInfo); +} + +void CDemuxStreamVideoFFmpeg::GetStreamInfo(std::string& strInfo) +{ + if(!m_stream) return; + char temp[128]; + avcodec_string(temp, 128, m_stream->codec, 0); + strInfo = temp; +} + +void CDemuxStreamSubtitleFFmpeg::GetStreamInfo(std::string& strInfo) +{ + if(!m_stream) return; + char temp[128]; + avcodec_string(temp, 128, m_stream->codec, 0); + strInfo = temp; +} + +static int interrupt_cb(void* ctx) +{ + CDVDDemuxFFmpeg* demuxer = static_cast(ctx); + if(demuxer && demuxer->Aborted()) + return 1; + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +/* +static int dvd_file_open(URLContext *h, const char *filename, int flags) +{ + return -1; +} +*/ + +static int dvd_file_read(void *h, uint8_t* buf, int size) +{ + if(interrupt_cb(h)) + return AVERROR_EXIT; + + CDVDInputStream* pInputStream = static_cast(h)->m_pInput; + return pInputStream->Read(buf, size); +} +/* +static int dvd_file_write(URLContext *h, uint8_t* buf, int size) +{ + return -1; +} +*/ +static int64_t dvd_file_seek(void *h, int64_t pos, int whence) +{ + if(interrupt_cb(h)) + return AVERROR_EXIT; + + CDVDInputStream* pInputStream = static_cast(h)->m_pInput; + if(whence == AVSEEK_SIZE) + return pInputStream->GetLength(); + else + return pInputStream->Seek(pos, whence & ~AVSEEK_FORCE); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// + +CDVDDemuxFFmpeg::CDVDDemuxFFmpeg() : CDVDDemux() +{ + m_pFormatContext = NULL; + m_pInput = NULL; + m_ioContext = NULL; + m_currentPts = DVD_NOPTS_VALUE; + m_bMatroska = false; + m_bAVI = false; + m_speed = DVD_PLAYSPEED_NORMAL; + m_program = UINT_MAX; + m_pkt.result = -1; + memset(&m_pkt.pkt, 0, sizeof(AVPacket)); + m_streaminfo = true; /* set to true if we want to look for streams before playback */ + m_checkvideo = false; +} + +CDVDDemuxFFmpeg::~CDVDDemuxFFmpeg() +{ + Dispose(); + ff_flush_avutil_log_buffers(); +} + +bool CDVDDemuxFFmpeg::Aborted() +{ + if(m_timeout.IsTimePast()) + return true; + + CDVDInputStreamFFmpeg * input = dynamic_cast(m_pInput); + if(input && input->Aborted()) + return true; + + return false; +} + +bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput, bool streaminfo, bool fileinfo) +{ + AVInputFormat* iformat = NULL; + std::string strFile; + m_streaminfo = streaminfo; + m_currentPts = DVD_NOPTS_VALUE; + m_speed = DVD_PLAYSPEED_NORMAL; + m_program = UINT_MAX; + const AVIOInterruptCB int_cb = { interrupt_cb, this }; + + if (!pInput) return false; + + m_pInput = pInput; + strFile = m_pInput->GetFileName(); + + if( m_pInput->GetContent().length() > 0 ) + { + std::string content = m_pInput->GetContent(); + StringUtils::ToLower(content); + + /* check if we can get a hint from content */ + if ( content.compare("video/x-vobsub") == 0 ) + iformat = av_find_input_format("mpeg"); + else if( content.compare("video/x-dvd-mpeg") == 0 ) + iformat = av_find_input_format("mpeg"); + else if( content.compare("video/mp2t") == 0 ) + iformat = av_find_input_format("mpegts"); + else if( content.compare("multipart/x-mixed-replace") == 0 ) + iformat = av_find_input_format("mjpeg"); + } + + // open the demuxer + m_pFormatContext = avformat_alloc_context(); + m_pFormatContext->interrupt_callback = int_cb; + + // try to abort after 30 seconds + m_timeout.Set(30000); + + if( m_pInput->IsStreamType(DVDSTREAM_TYPE_FFMPEG) ) + { + // special stream type that makes avformat handle file opening + // allows internal ffmpeg protocols to be used + CURL url = m_pInput->GetURL(); + + AVDictionary *options = GetFFMpegOptionsFromURL(url); + + int result=-1; + if (url.IsProtocol("mms")) + { + // try mmsh, then mmst + url.SetProtocol("mmsh"); + url.SetProtocolOptions(""); + result = avformat_open_input(&m_pFormatContext, url.Get().c_str(), iformat, &options); + if (result < 0) + { + url.SetProtocol("mmst"); + strFile = url.Get(); + } + } + if (result < 0 && avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0 ) + { + CLog::Log(LOGDEBUG, "Error, could not open file %s", CURL::GetRedacted(strFile).c_str()); + Dispose(); + av_dict_free(&options); + return false; + } + av_dict_free(&options); + } + else + { + unsigned char* buffer = (unsigned char*)av_malloc(FFMPEG_FILE_BUFFER_SIZE); + m_ioContext = avio_alloc_context(buffer, FFMPEG_FILE_BUFFER_SIZE, 0, this, dvd_file_read, NULL, dvd_file_seek); + m_ioContext->max_packet_size = m_pInput->GetBlockSize(); + if(m_ioContext->max_packet_size) + m_ioContext->max_packet_size *= FFMPEG_FILE_BUFFER_SIZE / m_ioContext->max_packet_size; + + if(m_pInput->Seek(0, SEEK_POSSIBLE) == 0) + m_ioContext->seekable = 0; + + std::string content = m_pInput->GetContent(); + StringUtils::ToLower(content); + if (StringUtils::StartsWith(content, "audio/l16")) + iformat = av_find_input_format("s16be"); + + if( iformat == NULL ) + { + // let ffmpeg decide which demuxer we have to open + + bool trySPDIFonly = (m_pInput->GetContent() == "audio/x-spdif-compressed"); + + if (!trySPDIFonly) + av_probe_input_buffer(m_ioContext, &iformat, strFile.c_str(), NULL, 0, 0); + + // Use the more low-level code in case we have been built against an old + // FFmpeg without the above av_probe_input_buffer(), or in case we only + // want to probe for spdif (DTS or IEC 61937) compressed audio + // specifically, or in case the file is a wav which may contain DTS or + // IEC 61937 (e.g. ac3-in-wav) and we want to check for those formats. + if (trySPDIFonly || (iformat && strcmp(iformat->name, "wav") == 0)) + { + AVProbeData pd; + uint8_t probe_buffer[FFMPEG_FILE_BUFFER_SIZE + AVPROBE_PADDING_SIZE]; + + // init probe data + pd.buf = probe_buffer; + pd.filename = strFile.c_str(); + + // av_probe_input_buffer might have changed the buffer_size beyond our allocated amount + int buffer_size = std::min((int) FFMPEG_FILE_BUFFER_SIZE, m_ioContext->buffer_size); + // read data using avformat's buffers + pd.buf_size = avio_read(m_ioContext, pd.buf, m_ioContext->max_packet_size ? m_ioContext->max_packet_size : buffer_size); + if (pd.buf_size <= 0) + { + CLog::Log(LOGERROR, "%s - error reading from input stream, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str()); + return false; + } + memset(pd.buf+pd.buf_size, 0, AVPROBE_PADDING_SIZE); + + // restore position again + avio_seek(m_ioContext , 0, SEEK_SET); + + // the advancedsetting is for allowing the user to force outputting the + // 44.1 kHz DTS wav file as PCM, so that an A/V receiver can decode + // it (this is temporary until we handle 44.1 kHz passthrough properly) + if (trySPDIFonly || (iformat && strcmp(iformat->name, "wav") == 0 && !g_advancedSettings.m_dvdplayerIgnoreDTSinWAV)) + { + // check for spdif and dts + // This is used with wav files and audio CDs that may contain + // a DTS or AC3 track padded for S/PDIF playback. If neither of those + // is present, we assume it is PCM audio. + // AC3 is always wrapped in iec61937 (ffmpeg "spdif"), while DTS + // may be just padded. + AVInputFormat *iformat2; + iformat2 = av_find_input_format("spdif"); + + if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4) + { + iformat = iformat2; + } + else + { + // not spdif or no spdif demuxer, try dts + iformat2 = av_find_input_format("dts"); + + if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4) + { + iformat = iformat2; + } + else if (trySPDIFonly) + { + // not dts either, return false in case we were explicitely + // requested to only check for S/PDIF padded compressed audio + CLog::Log(LOGDEBUG, "%s - not spdif or dts file, fallbacking", __FUNCTION__); + return false; + } + } + } + } + + if(!iformat) + { + std::string content = m_pInput->GetContent(); + + /* check if we can get a hint from content */ + if( content.compare("audio/aacp") == 0 ) + iformat = av_find_input_format("aac"); + else if( content.compare("audio/aac") == 0 ) + iformat = av_find_input_format("aac"); + else if( content.compare("video/flv") == 0 ) + iformat = av_find_input_format("flv"); + else if( content.compare("video/x-flv") == 0 ) + iformat = av_find_input_format("flv"); + } + + if (!iformat) + { + CLog::Log(LOGERROR, "%s - error probing input format, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str()); + return false; + } + else + { + if (iformat->name) + CLog::Log(LOGDEBUG, "%s - probing detected format [%s]", __FUNCTION__, iformat->name); + else + CLog::Log(LOGDEBUG, "%s - probing detected unnamed format", __FUNCTION__); + } + } + + + m_pFormatContext->pb = m_ioContext; + + AVDictionary *options = NULL; + if (iformat->name && (strcmp(iformat->name, "mp3") == 0 || strcmp(iformat->name, "mp2") == 0)) + { + CLog::Log(LOGDEBUG, "%s - setting usetoc to 0 for accurate VBR MP3 seek", __FUNCTION__); + av_dict_set(&options, "usetoc", "0", 0); + } + + if (StringUtils::StartsWith(content, "audio/l16")) + { + int channels = 2; + int samplerate = 44100; + GetL16Parameters(channels, samplerate); + av_dict_set_int(&options, "channels", channels, 0); + av_dict_set_int(&options, "sample_rate", samplerate, 0); + } + + if (avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0) + { + CLog::Log(LOGERROR, "%s - Error, could not open file %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str()); + Dispose(); + av_dict_free(&options); + return false; + } + av_dict_free(&options); + } + + // Avoid detecting framerate if advancedsettings.xml says so + if (g_advancedSettings.m_videoFpsDetect == 0) + m_pFormatContext->fps_probe_size = 0; + + // analyse very short to speed up mjpeg playback start + if (iformat && (strcmp(iformat->name, "mjpeg") == 0) && m_ioContext->seekable == 0) + av_opt_set_int(m_pFormatContext, "analyzeduration", 500000, 0); + + bool skipCreateStreams = false; + bool isBluray = pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY); + if (iformat && (strcmp(iformat->name, "mpegts") == 0) && !fileinfo && !isBluray) + { + av_opt_set_int(m_pFormatContext, "analyzeduration", 500000, 0); + m_checkvideo = true; + skipCreateStreams = true; + } + else if (!iformat || (strcmp(iformat->name, "mpegts") != 0)) + { + m_streaminfo = true; + } + + // we need to know if this is matroska or avi later + m_bMatroska = strncmp(m_pFormatContext->iformat->name, "matroska", 8) == 0; // for "matroska.webm" + m_bAVI = strcmp(m_pFormatContext->iformat->name, "avi") == 0; + + if (m_streaminfo) + { + /* to speed up dvd switches, only analyse very short */ + if(m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)) + av_opt_set_int(m_pFormatContext, "analyzeduration", 500000, 0); + + CLog::Log(LOGDEBUG, "%s - avformat_find_stream_info starting", __FUNCTION__); + int iErr = avformat_find_stream_info(m_pFormatContext, NULL); + if (iErr < 0) + { + CLog::Log(LOGWARNING,"could not find codec parameters for %s", CURL::GetRedacted(strFile).c_str()); + if (m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD) + || m_pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY) + || (m_pFormatContext->nb_streams == 1 && m_pFormatContext->streams[0]->codec->codec_id == AV_CODEC_ID_AC3)) + { + // special case, our codecs can still handle it. + } + else + { + Dispose(); + return false; + } + } + CLog::Log(LOGDEBUG, "%s - av_find_stream_info finished", __FUNCTION__); + + if (m_checkvideo) + { + // make sure we start video with an i-frame + ResetVideoStreams(); + } + } + else + { + m_program = 0; + m_checkvideo = true; + skipCreateStreams = true; + } + + // reset any timeout + m_timeout.SetInfinite(); + + // if format can be nonblocking, let's use that + m_pFormatContext->flags |= AVFMT_FLAG_NONBLOCK; + + // print some extra information + av_dump_format(m_pFormatContext, 0, strFile.c_str(), 0); + + UpdateCurrentPTS(); + + // in case of mpegts and we have not seen pat/pmt, defer creation of streams + if (!skipCreateStreams || m_pFormatContext->nb_programs > 0) + CreateStreams(); + + // allow IsProgramChange to return true + if (skipCreateStreams && GetNrOfStreams() == 0) + m_program = 0; + + return true; +} + +void CDVDDemuxFFmpeg::Dispose() +{ + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + + if (m_pFormatContext) + { + if (m_ioContext && m_pFormatContext->pb && m_pFormatContext->pb != m_ioContext) + { + CLog::Log(LOGWARNING, "CDVDDemuxFFmpeg::Dispose - demuxer changed our byte context behind our back, possible memleak"); + m_ioContext = m_pFormatContext->pb; + } + avformat_close_input(&m_pFormatContext); + } + + if(m_ioContext) + { + av_free(m_ioContext->buffer); + av_free(m_ioContext); + } + + m_ioContext = NULL; + m_pFormatContext = NULL; + m_speed = DVD_PLAYSPEED_NORMAL; + + DisposeStreams(); + + m_pInput = NULL; +} + +void CDVDDemuxFFmpeg::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream, m_streaminfo); +} + +void CDVDDemuxFFmpeg::Flush() +{ + // naughty usage of an internal ffmpeg function + if (m_pFormatContext) + av_read_frame_flush(m_pFormatContext); + + m_currentPts = DVD_NOPTS_VALUE; + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); +} + +void CDVDDemuxFFmpeg::Abort() +{ + m_timeout.SetExpired(); +} + +void CDVDDemuxFFmpeg::SetSpeed(int iSpeed) +{ + if(!m_pFormatContext) + return; + + if(m_speed != DVD_PLAYSPEED_PAUSE && iSpeed == DVD_PLAYSPEED_PAUSE) + { + m_pInput->Pause(m_currentPts); + av_read_pause(m_pFormatContext); + } + else if(m_speed == DVD_PLAYSPEED_PAUSE && iSpeed != DVD_PLAYSPEED_PAUSE) + { + m_pInput->Pause(m_currentPts); + av_read_play(m_pFormatContext); + } + m_speed = iSpeed; + + AVDiscard discard = AVDISCARD_NONE; + if(m_speed > 4*DVD_PLAYSPEED_NORMAL) + discard = AVDISCARD_NONKEY; + else if(m_speed > 2*DVD_PLAYSPEED_NORMAL) + discard = AVDISCARD_BIDIR; + else if(m_speed < DVD_PLAYSPEED_PAUSE) + discard = AVDISCARD_NONKEY; + + + for(unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + if(m_pFormatContext->streams[i]) + { + if(m_pFormatContext->streams[i]->discard != AVDISCARD_ALL) + m_pFormatContext->streams[i]->discard = discard; + } + } +} + +AVDictionary *CDVDDemuxFFmpeg::GetFFMpegOptionsFromURL(const CURL &url) +{ + + AVDictionary *options = NULL; + + if (url.IsProtocol("http") || url.IsProtocol("https")) + { + std::map protocolOptions; + url.GetProtocolOptions(protocolOptions); + std::string headers; + bool hasUserAgent = false; + for(std::map::const_iterator it = protocolOptions.begin(); it != protocolOptions.end(); ++it) + { + std::string name = it->first; StringUtils::ToLower(name); + const std::string &value = it->second; + + if (name == "seekable") + av_dict_set(&options, "seekable", value.c_str(), 0); + else if (name == "user-agent") + { + av_dict_set(&options, "user-agent", value.c_str(), 0); + hasUserAgent = true; + } + else if (name != "auth" && name != "encoding") + // all other protocol options can be added as http header. + headers.append(it->first).append(": ").append(value).append("\r\n"); + } + if (!hasUserAgent) + // set default xbmc user-agent. + av_dict_set(&options, "user-agent", g_advancedSettings.m_userAgent.c_str(), 0); + + if (!headers.empty()) + av_dict_set(&options, "headers", headers.c_str(), 0); + + std::string cookies; + if (XFILE::CCurlFile::GetCookies(url, cookies)) + av_dict_set(&options, "cookies", cookies.c_str(), 0); + + } + return options; +} + +double CDVDDemuxFFmpeg::ConvertTimestamp(int64_t pts, int den, int num) +{ + if (pts == (int64_t)AV_NOPTS_VALUE) + return DVD_NOPTS_VALUE; + + // do calculations in floats as they can easily overflow otherwise + // we don't care for having a completly exact timestamp anyway + double timestamp = (double)pts * num / den; + double starttime = 0.0f; + + // for dvd's we need the original time + if(CDVDInputStream::IMenus* menu = dynamic_cast(m_pInput)) + starttime = menu->GetTimeStampCorrection() / DVD_TIME_BASE; + else if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) + starttime = (double)m_pFormatContext->start_time / AV_TIME_BASE; + + if(timestamp > starttime) + timestamp -= starttime; + // allow for largest possible difference in pts and dts for a single packet + else if( timestamp + 0.5f > starttime ) + timestamp = 0; + + return timestamp*DVD_TIME_BASE; +} + +DemuxPacket* CDVDDemuxFFmpeg::Read() +{ + DemuxPacket* pPacket = NULL; + // on some cases where the received packet is invalid we will need to return an empty packet (0 length) otherwise the main loop (in CDVDPlayer) + // would consider this the end of stream and stop. + bool bReturnEmpty = false; + { CSingleLock lock(m_critSection); // open lock scope + if (m_pFormatContext) + { + // assume we are not eof + if(m_pFormatContext->pb) + m_pFormatContext->pb->eof_reached = 0; + + // check for saved packet after a program change + if (m_pkt.result < 0) + { + // keep track if ffmpeg doesn't always set these + m_pkt.pkt.size = 0; + m_pkt.pkt.data = NULL; + + // timeout reads after 100ms + m_timeout.Set(20000); + m_pkt.result = av_read_frame(m_pFormatContext, &m_pkt.pkt); + m_timeout.SetInfinite(); + } + + if (m_pkt.result == AVERROR(EINTR) || m_pkt.result == AVERROR(EAGAIN)) + { + // timeout, probably no real error, return empty packet + bReturnEmpty = true; + } + else if (m_pkt.result < 0) + { + Flush(); + } + else if (IsProgramChange()) + { + // update streams + CreateStreams(m_program); + + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(0); + pPacket->iStreamId = DMX_SPECIALID_STREAMCHANGE; + + return pPacket; + } + // check size and stream index for being in a valid range + else if (m_pkt.pkt.size < 0 || + m_pkt.pkt.stream_index < 0 || + m_pkt.pkt.stream_index >= (int)m_pFormatContext->nb_streams) + { + // XXX, in some cases ffmpeg returns a negative packet size + if(m_pFormatContext->pb && !m_pFormatContext->pb->eof_reached) + { + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::Read() no valid packet"); + bReturnEmpty = true; + Flush(); + } + else + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::Read() returned invalid packet and eof reached"); + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + } + else + { + ParsePacket(&m_pkt.pkt); + + AVStream *stream = m_pFormatContext->streams[m_pkt.pkt.stream_index]; + + if (IsVideoReady()) + { + if (m_program != UINT_MAX) + { + /* check so packet belongs to selected program */ + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + if(m_pkt.pkt.stream_index == (int)m_pFormatContext->programs[m_program]->stream_index[i]) + { + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size); + break; + } + } + + if (!pPacket) + bReturnEmpty = true; + } + else + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size); + } + else + bReturnEmpty = true; + + if (pPacket) + { + // lavf sometimes bugs out and gives 0 dts/pts instead of no dts/pts + // since this could only happens on initial frame under normal + // circomstances, let's assume it is wrong all the time + if(m_pkt.pkt.dts == 0) + m_pkt.pkt.dts = AV_NOPTS_VALUE; + if(m_pkt.pkt.pts == 0) + m_pkt.pkt.pts = AV_NOPTS_VALUE; + + if(m_bMatroska && stream->codec && stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { // matroska can store different timestamps + // for different formats, for native stored + // stuff it is pts, but for ms compatibility + // tracks, it is really dts. sadly ffmpeg + // sets these two timestamps equal all the + // time, so we select it here instead + if(stream->codec->codec_tag == 0) + m_pkt.pkt.dts = AV_NOPTS_VALUE; + else + m_pkt.pkt.pts = AV_NOPTS_VALUE; + } + + // we need to get duration slightly different for matroska embedded text subtitels + if(m_bMatroska && stream->codec && stream->codec->codec_id == AV_CODEC_ID_TEXT && m_pkt.pkt.convergence_duration != 0) + m_pkt.pkt.duration = m_pkt.pkt.convergence_duration; + + if(m_bAVI && stream->codec && stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + // AVI's always have borked pts, specially if m_pFormatContext->flags includes + // AVFMT_FLAG_GENPTS so always use dts + m_pkt.pkt.pts = AV_NOPTS_VALUE; + } + + // copy contents into our own packet + pPacket->iSize = m_pkt.pkt.size; + + // maybe we can avoid a memcpy here by detecting where pkt.destruct is pointing too? + if (m_pkt.pkt.data) + memcpy(pPacket->pData, m_pkt.pkt.data, pPacket->iSize); + + pPacket->pts = ConvertTimestamp(m_pkt.pkt.pts, stream->time_base.den, stream->time_base.num); + pPacket->dts = ConvertTimestamp(m_pkt.pkt.dts, stream->time_base.den, stream->time_base.num); + pPacket->duration = DVD_SEC_TO_TIME((double)m_pkt.pkt.duration * stream->time_base.num / stream->time_base.den); + + // used to guess streamlength + if (pPacket->dts != DVD_NOPTS_VALUE && (pPacket->dts > m_currentPts || m_currentPts == DVD_NOPTS_VALUE)) + m_currentPts = pPacket->dts; + + + // check if stream has passed full duration, needed for live streams + bool bAllowDurationExt = (stream->codec && (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO || stream->codec->codec_type == AVMEDIA_TYPE_AUDIO)); + if(bAllowDurationExt && m_pkt.pkt.dts != (int64_t)AV_NOPTS_VALUE) + { + int64_t duration; + duration = m_pkt.pkt.dts; + if(stream->start_time != (int64_t)AV_NOPTS_VALUE) + duration -= stream->start_time; + + if(duration > stream->duration) + { + stream->duration = duration; + duration = av_rescale_rnd(stream->duration, (int64_t)stream->time_base.num * AV_TIME_BASE, stream->time_base.den, AV_ROUND_NEAR_INF); + if ((m_pFormatContext->duration == (int64_t)AV_NOPTS_VALUE) + || (m_pFormatContext->duration != (int64_t)AV_NOPTS_VALUE && duration > m_pFormatContext->duration)) + m_pFormatContext->duration = duration; + } + } + + // store internal id until we know the continuous id presented to player + // the stream might not have been created yet + pPacket->iStreamId = m_pkt.pkt.stream_index; + } + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + } + } + } // end of lock scope + if (bReturnEmpty && !pPacket) + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(0); + + if (!pPacket) return NULL; + + // check streams, can we make this a bit more simple? + if (pPacket && pPacket->iStreamId >= 0) + { + CDemuxStream *stream = GetStreamInternal(pPacket->iStreamId); + if (!stream || + stream->pPrivate != m_pFormatContext->streams[pPacket->iStreamId] || + stream->codec != m_pFormatContext->streams[pPacket->iStreamId]->codec->codec_id) + { + // content has changed, or stream did not yet exist + stream = AddStream(pPacket->iStreamId); + } + // we already check for a valid m_streams[pPacket->iStreamId] above + else if (stream->type == STREAM_AUDIO) + { + if (((CDemuxStreamAudio*)stream)->iChannels != m_pFormatContext->streams[pPacket->iStreamId]->codec->channels || + ((CDemuxStreamAudio*)stream)->iSampleRate != m_pFormatContext->streams[pPacket->iStreamId]->codec->sample_rate) + { + // content has changed + stream = AddStream(pPacket->iStreamId); + } + } + else if (stream->type == STREAM_VIDEO) + { + if (((CDemuxStreamVideo*)stream)->iWidth != m_pFormatContext->streams[pPacket->iStreamId]->codec->width || + ((CDemuxStreamVideo*)stream)->iHeight != m_pFormatContext->streams[pPacket->iStreamId]->codec->height) + { + // content has changed + stream = AddStream(pPacket->iStreamId); + } + } + if (!stream) + { + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::AddStream - internal error, stream is null"); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + return NULL; + } + // set continuous stream id for player + pPacket->iStreamId = stream->iId; + } + return pPacket; +} + +bool CDVDDemuxFFmpeg::SeekTime(int time, bool backwords, double *startpts) +{ + if (!m_pInput) + return false; + + if(time < 0) + time = 0; + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + + CDVDInputStream::ISeekTime* ist = dynamic_cast(m_pInput); + if (ist) + { + if (!ist->SeekTime(time)) + return false; + + if(startpts) + *startpts = DVD_NOPTS_VALUE; + + Flush(); + + // also empty the internal ffmpeg buffer + m_ioContext->buf_ptr = m_ioContext->buf_end; + + return true; + } + + if(!m_pInput->Seek(0, SEEK_POSSIBLE) + && !m_pInput->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + CLog::Log(LOGDEBUG, "%s - input stream reports it is not seekable", __FUNCTION__); + return false; + } + + int64_t seek_pts = (int64_t)time * (AV_TIME_BASE / 1000); + bool ismp3 = m_pFormatContext->iformat && (strcmp(m_pFormatContext->iformat->name, "mp3") == 0); + if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE && !ismp3) + seek_pts += m_pFormatContext->start_time; + + int ret; + { + CSingleLock lock(m_critSection); + ret = av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); + + // demuxer will return failure, if you seek behind eof + if (ret < 0 && m_pFormatContext->duration && seek_pts >= (m_pFormatContext->duration + m_pFormatContext->start_time)) + ret = 0; + else if (ret < 0 && m_pInput->IsEOF()) + ret = 0; + + if(ret >= 0) + UpdateCurrentPTS(); + } + + if(m_currentPts == DVD_NOPTS_VALUE) + CLog::Log(LOGDEBUG, "%s - unknown position after seek", __FUNCTION__); + else + CLog::Log(LOGDEBUG, "%s - seek ended up on time %d", __FUNCTION__, (int)(m_currentPts / DVD_TIME_BASE * 1000)); + + // in this case the start time is requested time + if(startpts) + *startpts = DVD_MSEC_TO_TIME(time); + + return (ret >= 0); +} + +bool CDVDDemuxFFmpeg::SeekByte(int64_t pos) +{ + CSingleLock lock(m_critSection); + int ret = av_seek_frame(m_pFormatContext, -1, pos, AVSEEK_FLAG_BYTE); + + if(ret >= 0) + UpdateCurrentPTS(); + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + + return (ret >= 0); +} + +void CDVDDemuxFFmpeg::UpdateCurrentPTS() +{ + m_currentPts = DVD_NOPTS_VALUE; + + int idx = av_find_default_stream_index(m_pFormatContext); + if (idx >= 0) + { + AVStream *stream = m_pFormatContext->streams[idx]; + if(stream && stream->cur_dts != (int64_t)AV_NOPTS_VALUE) + { + double ts = ConvertTimestamp(stream->cur_dts, stream->time_base.den, stream->time_base.num); + if(m_currentPts == DVD_NOPTS_VALUE || m_currentPts > ts ) + m_currentPts = ts; + } + } +} + +int CDVDDemuxFFmpeg::GetStreamLength() +{ + if (!m_pFormatContext) + return 0; + + if (m_pFormatContext->duration < 0) + return 0; + + return (int)(m_pFormatContext->duration / (AV_TIME_BASE / 1000)); +} + +/** + * @brief Finds stream based on demuxer index + */ +CDemuxStream* CDVDDemuxFFmpeg::GetStream(int iStreamId) +{ + if(iStreamId >= 0 && (size_t)iStreamId < m_stream_index.size()) + return m_stream_index[iStreamId]->second; + else + return NULL; +} + +/** + * @brief Finds stream based on ffmpeg index + */ +CDemuxStream* CDVDDemuxFFmpeg::GetStreamInternal(int iId) +{ + std::map::iterator it = m_streams.find(iId); + if (it == m_streams.end()) + return NULL; + else + return it->second; +} + +int CDVDDemuxFFmpeg::GetNrOfStreams() +{ + return m_stream_index.size(); +} + +static double SelectAspect(AVStream* st, bool* forced) +{ + *forced = false; + /* if stream aspect is 1:1 or 0:0 use codec aspect */ + if((st->sample_aspect_ratio.den == 1 || st->sample_aspect_ratio.den == 0) + && (st->sample_aspect_ratio.num == 1 || st->sample_aspect_ratio.num == 0) + && st->codec->sample_aspect_ratio.num != 0) + return av_q2d(st->codec->sample_aspect_ratio); + + *forced = true; + if(st->sample_aspect_ratio.num != 0) + return av_q2d(st->sample_aspect_ratio); + + return 0.0; +} + +void CDVDDemuxFFmpeg::CreateStreams(unsigned int program) +{ + DisposeStreams(); + + // add the ffmpeg streams to our own stream map + if (m_pFormatContext->nb_programs) + { + // check if desired program is available + if (program < m_pFormatContext->nb_programs && m_pFormatContext->programs[program]->nb_stream_indexes > 0) + { + m_program = program; + } + else + m_program = UINT_MAX; + + // look for first non empty stream and discard nonselected programs + for (unsigned int i = 0; i < m_pFormatContext->nb_programs; i++) + { + if(m_program == UINT_MAX && m_pFormatContext->programs[i]->nb_stream_indexes > 0) + { + m_program = i; + } + + if(i != m_program) + m_pFormatContext->programs[i]->discard = AVDISCARD_ALL; + } + if(m_program != UINT_MAX) + { + // add streams from selected program + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + AddStream(m_pFormatContext->programs[m_program]->stream_index[i]); + } + } + else + m_program = UINT_MAX; + + // if there were no programs or they were all empty, add all streams + if (m_program == UINT_MAX) + { + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + AddStream(i); + } +} + +void CDVDDemuxFFmpeg::DisposeStreams() +{ + std::map::iterator it; + for(it = m_streams.begin(); it != m_streams.end(); ++it) + delete it->second; + m_streams.clear(); + m_stream_index.clear(); +} + +CDemuxStream* CDVDDemuxFFmpeg::AddStream(int iId) +{ + AVStream* pStream = m_pFormatContext->streams[iId]; + if (pStream) + { + CDemuxStream* stream = NULL; + + switch (pStream->codec->codec_type) + { + case AVMEDIA_TYPE_AUDIO: + { + CDemuxStreamAudioFFmpeg* st = new CDemuxStreamAudioFFmpeg(this, pStream); + stream = st; + st->iChannels = pStream->codec->channels; + st->iSampleRate = pStream->codec->sample_rate; + st->iBlockAlign = pStream->codec->block_align; + st->iBitRate = pStream->codec->bit_rate; + st->iBitsPerSample = pStream->codec->bits_per_raw_sample; + if (st->iBitsPerSample == 0) + st->iBitsPerSample = pStream->codec->bits_per_coded_sample; + + if(av_dict_get(pStream->metadata, "title", NULL, 0)) + st->m_description = av_dict_get(pStream->metadata, "title", NULL, 0)->value; + + break; + } + case AVMEDIA_TYPE_VIDEO: + { + CDemuxStreamVideoFFmpeg* st = new CDemuxStreamVideoFFmpeg(this, pStream); + stream = st; + if(strcmp(m_pFormatContext->iformat->name, "flv") == 0) + st->bVFR = true; + else + st->bVFR = false; + + // never trust pts in avi files with h264. + if (m_bAVI && pStream->codec->codec_id == AV_CODEC_ID_H264) + st->bPTSInvalid = true; + +#if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE) + AVRational r_frame_rate = av_stream_get_r_frame_rate(pStream); +#else + AVRational r_frame_rate = pStream->r_frame_rate; +#endif + + //average fps is more accurate for mkv files + if (m_bMatroska && pStream->avg_frame_rate.den && pStream->avg_frame_rate.num) + { + st->iFpsRate = pStream->avg_frame_rate.num; + st->iFpsScale = pStream->avg_frame_rate.den; + } + else if(r_frame_rate.den && r_frame_rate.num) + { + st->iFpsRate = r_frame_rate.num; + st->iFpsScale = r_frame_rate.den; + } + else + { + st->iFpsRate = 0; + st->iFpsScale = 0; + } + + // added for aml hw decoder, mkv frame-rate can be wrong. + if (r_frame_rate.den && r_frame_rate.num) + { + st->irFpsRate = r_frame_rate.num; + st->irFpsScale = r_frame_rate.den; + } + else + { + st->irFpsRate = 0; + st->irFpsScale = 0; + } + + if (pStream->codec_info_nb_frames > 0 + && pStream->codec_info_nb_frames <= 2 + && m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CLog::Log(LOGDEBUG, "%s - fps may be unreliable since ffmpeg decoded only %d frame(s)", __FUNCTION__, pStream->codec_info_nb_frames); + st->iFpsRate = 0; + st->iFpsScale = 0; + } + + st->iWidth = pStream->codec->width; + st->iHeight = pStream->codec->height; + st->fAspect = SelectAspect(pStream, &st->bForcedAspect) * pStream->codec->width / pStream->codec->height; + st->iOrientation = 0; + st->iBitsPerPixel = pStream->codec->bits_per_coded_sample; + + AVDictionaryEntry *rtag = av_dict_get(pStream->metadata, "rotate", NULL, 0); + if (rtag) + st->iOrientation = atoi(rtag->value); + + // detect stereoscopic mode + std::string stereoMode = GetStereoModeFromMetadata(pStream->metadata); + // check for metadata in file if detection in stream failed + if (stereoMode.empty()) + stereoMode = GetStereoModeFromMetadata(m_pFormatContext->metadata); + if (!stereoMode.empty()) + st->stereo_mode = stereoMode; + + + if ( m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD) ) + { + if (pStream->codec->codec_id == AV_CODEC_ID_PROBE) + { + // fix MPEG-1/MPEG-2 video stream probe returning AV_CODEC_ID_PROBE for still frames. + // ffmpeg issue 1871, regression from ffmpeg r22831. + if ((pStream->id & 0xF0) == 0xE0) + { + pStream->codec->codec_id = AV_CODEC_ID_MPEG2VIDEO; + pStream->codec->codec_tag = MKTAG('M','P','2','V'); + CLog::Log(LOGERROR, "%s - AV_CODEC_ID_PROBE detected, forcing AV_CODEC_ID_MPEG2VIDEO", __FUNCTION__); + } + } + } + break; + } + case AVMEDIA_TYPE_DATA: + { + stream = new CDemuxStream(); + stream->type = STREAM_DATA; + break; + } + case AVMEDIA_TYPE_SUBTITLE: + { + if (pStream->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT && CSettings::Get().GetBool("videoplayer.teletextenabled")) + { + CDemuxStreamTeletext* st = new CDemuxStreamTeletext(); + stream = st; + stream->type = STREAM_TELETEXT; + break; + } + else + { + CDemuxStreamSubtitleFFmpeg* st = new CDemuxStreamSubtitleFFmpeg(this, pStream); + stream = st; + + if(av_dict_get(pStream->metadata, "title", NULL, 0)) + st->m_description = av_dict_get(pStream->metadata, "title", NULL, 0)->value; + + break; + } + } + case AVMEDIA_TYPE_ATTACHMENT: + { //mkv attachments. Only bothering with fonts for now. + if(pStream->codec->codec_id == AV_CODEC_ID_TTF + || pStream->codec->codec_id == AV_CODEC_ID_OTF + ) + { + std::string fileName = "special://temp/fonts/"; + XFILE::CDirectory::Create(fileName); + AVDictionaryEntry *nameTag = av_dict_get(pStream->metadata, "filename", NULL, 0); + if (!nameTag) + { + CLog::Log(LOGERROR, "%s: TTF attachment has no name", __FUNCTION__); + } + else + { + fileName += nameTag->value; + XFILE::CFile file; + if(pStream->codec->extradata && file.OpenForWrite(fileName)) + { + if (file.Write(pStream->codec->extradata, pStream->codec->extradata_size) != pStream->codec->extradata_size) + { + file.Close(); + XFILE::CFile::Delete(fileName); + CLog::Log(LOGDEBUG, "%s: Error saving font file \"%s\"", __FUNCTION__, fileName.c_str()); + } + } + } + } + stream = new CDemuxStream(); + stream->type = STREAM_NONE; + break; + } + default: + { + stream = new CDemuxStream(); + stream->type = STREAM_NONE; + break; + } + } + + // set ffmpeg type + stream->orig_type = pStream->codec->codec_type; + + // generic stuff + if (pStream->duration != (int64_t)AV_NOPTS_VALUE) + stream->iDuration = (int)((pStream->duration / AV_TIME_BASE) & 0xFFFFFFFF); + + stream->codec = pStream->codec->codec_id; + stream->codec_fourcc = pStream->codec->codec_tag; + stream->profile = pStream->codec->profile; + stream->level = pStream->codec->level; + + stream->source = STREAM_SOURCE_DEMUX; + stream->pPrivate = pStream; + stream->flags = (CDemuxStream::EFlags)pStream->disposition; + + AVDictionaryEntry *langTag = av_dict_get(pStream->metadata, "language", NULL, 0); + if (langTag) + strncpy(stream->language, langTag->value, 3); + + if( pStream->codec->extradata && pStream->codec->extradata_size > 0 ) + { + stream->ExtraSize = pStream->codec->extradata_size; + stream->ExtraData = new uint8_t[pStream->codec->extradata_size]; + memcpy(stream->ExtraData, pStream->codec->extradata, pStream->codec->extradata_size); + } + +#ifdef HAVE_LIBBLURAY + if( m_pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) + static_cast(m_pInput)->GetStreamInfo(pStream->id, stream->language); +#endif + if( m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD) ) + { + // this stuff is really only valid for dvd's. + // this is so that the physicalid matches the + // id's reported from libdvdnav + switch(stream->codec) + { + case AV_CODEC_ID_AC3: + stream->iPhysicalId = pStream->id - 128; + break; + case AV_CODEC_ID_DTS: + stream->iPhysicalId = pStream->id - 136; + break; + case AV_CODEC_ID_MP2: + stream->iPhysicalId = pStream->id - 448; + break; + case AV_CODEC_ID_PCM_S16BE: + stream->iPhysicalId = pStream->id - 160; + break; + case AV_CODEC_ID_DVD_SUBTITLE: + stream->iPhysicalId = pStream->id - 0x20; + break; + default: + stream->iPhysicalId = pStream->id & 0x1f; + break; + } + } + else + stream->iPhysicalId = pStream->id; + + AddStream(iId, stream); + return stream; + } + else + return NULL; +} + +/** + * @brief Adds or updates a demux stream based in ffmpeg id + */ +void CDVDDemuxFFmpeg::AddStream(int iId, CDemuxStream* stream) +{ + std::pair::iterator, bool> res; + + res = m_streams.insert(std::make_pair(iId, stream)); + if(res.second) + { + /* was new stream */ + stream->iId = m_stream_index.size(); + m_stream_index.push_back(res.first); + } + else + { + /* replace old stream, keeping old index */ + stream->iId = res.first->second->iId; + + delete res.first->second; + res.first->second = stream; + } + if(g_advancedSettings.m_logLevel > LOG_LEVEL_NORMAL) + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::AddStream(%d, ...) -> %d", iId, stream->iId); +} + + +std::string CDVDDemuxFFmpeg::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +int CDVDDemuxFFmpeg::GetChapterCount() +{ + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + return ich->GetChapterCount(); + + if(m_pFormatContext == NULL) + return 0; + + return m_pFormatContext->nb_chapters; +} + +int CDVDDemuxFFmpeg::GetChapter() +{ + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + return ich->GetChapter(); + + if(m_pFormatContext == NULL + || m_currentPts == DVD_NOPTS_VALUE) + return 0; + + for(unsigned i = 0; i < m_pFormatContext->nb_chapters; i++) + { + AVChapter *chapter = m_pFormatContext->chapters[i]; + if(m_currentPts >= ConvertTimestamp(chapter->start, chapter->time_base.den, chapter->time_base.num) + && m_currentPts < ConvertTimestamp(chapter->end, chapter->time_base.den, chapter->time_base.num)) + return i + 1; + } + + return 0; +} + +void CDVDDemuxFFmpeg::GetChapterName(std::string& strChapterName, int chapterIdx) +{ + if (chapterIdx <= 0 || chapterIdx > GetChapterCount()) + chapterIdx = GetChapter(); + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + ich->GetChapterName(strChapterName, chapterIdx); + else + { + if(chapterIdx <= 0) + return; + + AVDictionaryEntry *titleTag = av_dict_get(m_pFormatContext->chapters[chapterIdx-1]->metadata, + "title", NULL, 0); + if (titleTag) + strChapterName = titleTag->value; + } +} + +int64_t CDVDDemuxFFmpeg::GetChapterPos(int chapterIdx) +{ + if (chapterIdx <= 0 || chapterIdx > GetChapterCount()) + chapterIdx = GetChapter(); + if(chapterIdx <= 0) + return 0; + + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + return ich->GetChapterPos(chapterIdx); + + return m_pFormatContext->chapters[chapterIdx-1]->start*av_q2d(m_pFormatContext->chapters[chapterIdx-1]->time_base); +} + +bool CDVDDemuxFFmpeg::SeekChapter(int chapter, double* startpts) +{ + if(chapter < 1) + chapter = 1; + + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + { + CLog::Log(LOGDEBUG, "%s - chapter seeking using input stream", __FUNCTION__); + if(!ich->SeekChapter(chapter)) + return false; + + if(startpts) + { + *startpts = DVD_SEC_TO_TIME(ich->GetChapterPos(chapter)); + } + + Flush(); + return true; + } + + if(m_pFormatContext == NULL) + return false; + + if(chapter < 1 || chapter > (int)m_pFormatContext->nb_chapters) + return false; + + AVChapter *ch = m_pFormatContext->chapters[chapter-1]; + double dts = ConvertTimestamp(ch->start, ch->time_base.den, ch->time_base.num); + return SeekTime(DVD_TIME_TO_MSEC(dts), true, startpts); +} + +void CDVDDemuxFFmpeg::GetStreamCodecName(int iStreamId, std::string &strName) +{ + CDemuxStream *stream = GetStream(iStreamId); + if (stream) + { + unsigned int in = stream->codec_fourcc; + // FourCC codes are only valid on video streams, audio codecs in AVI/WAV + // are 2 bytes and audio codecs in transport streams have subtle variation + // e.g AC-3 instead of ac3 + if (stream->type == STREAM_VIDEO && in != 0) + { + char fourcc[5]; +#if defined(__powerpc__) + fourcc[0] = in & 0xff; + fourcc[1] = (in >> 8) & 0xff; + fourcc[2] = (in >> 16) & 0xff; + fourcc[3] = (in >> 24) & 0xff; +#else + memcpy(fourcc, &in, 4); +#endif + fourcc[4] = 0; + // fourccs have to be 4 characters + if (strlen(fourcc) == 4) + { + strName = fourcc; + StringUtils::ToLower(strName); + return; + } + } + +#ifdef FF_PROFILE_DTS_HD_MA + /* use profile to determine the DTS type */ + if (stream->codec == AV_CODEC_ID_DTS) + { + if (stream->profile == FF_PROFILE_DTS_HD_MA) + strName = "dtshd_ma"; + else if (stream->profile == FF_PROFILE_DTS_HD_HRA) + strName = "dtshd_hra"; + else + strName = "dca"; + return; + } +#endif + + AVCodec *codec = avcodec_find_decoder(stream->codec); + if (codec) + strName = codec->name; + } +} + +bool CDVDDemuxFFmpeg::IsProgramChange() +{ + if (m_program == UINT_MAX) + return false; + + if (m_program == 0 && !m_pFormatContext->nb_programs) + return false; + + if(m_pFormatContext->programs[m_program]->nb_stream_indexes != m_streams.size()) + return true; + + if (m_program >= m_pFormatContext->nb_programs) + return true; + + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + int idx = m_pFormatContext->programs[m_program]->stream_index[i]; + CDemuxStream *stream = GetStreamInternal(idx); + if(!stream) + return true; + if(m_pFormatContext->streams[idx]->codec->codec_type != stream->orig_type) + return true; + } + return false; +} + +std::string CDVDDemuxFFmpeg::GetStereoModeFromMetadata(AVDictionary *pMetadata) +{ + std::string stereoMode; + AVDictionaryEntry *tag = NULL; + + // matroska + tag = av_dict_get(pMetadata, "stereo_mode", NULL, 0); + if (tag && tag->value) + stereoMode = tag->value; + + // asf / wmv + if (stereoMode.empty()) + { + tag = av_dict_get(pMetadata, "Stereoscopic", NULL, 0); + if (tag && tag->value) + { + tag = av_dict_get(pMetadata, "StereoscopicLayout", NULL, 0); + if (tag && tag->value) + stereoMode = ConvertCodecToInternalStereoMode(tag->value, WmvToInternalStereoModeMap); + } + } + + return stereoMode; +} + +std::string CDVDDemuxFFmpeg::ConvertCodecToInternalStereoMode(const std::string &mode, const StereoModeConversionMap *conversionMap) +{ + size_t i = 0; + while (conversionMap[i].name) + { + if (mode == conversionMap[i].name) + return conversionMap[i].mode; + i++; + } + return ""; +} + +void CDVDDemuxFFmpeg::ParsePacket(AVPacket *pkt) +{ + AVStream *st = m_pFormatContext->streams[pkt->stream_index]; + CDemuxStream *stream = GetStreamInternal(pkt->stream_index); + + // if the stream is new, tell ffmpeg to parse the stream + if (!stream && !st->parser) + { + st->need_parsing = AVSTREAM_PARSE_FULL; + } + + // split extradata + if(st->parser && st->parser->parser->split && !st->codec->extradata) + { + int i = st->parser->parser->split(st->codec, pkt->data, pkt->size); + if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) + { + // Found extradata, fill it in. This will cause + // a new stream to be created and used. + st->codec->extradata_size = i; + st->codec->extradata = (uint8_t*)av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) + { + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::Read() fetching extradata, extradata_size(%d)", st->codec->extradata_size); + memcpy(st->codec->extradata, pkt->data, st->codec->extradata_size); + memset(st->codec->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE); + } + else + { + st->codec->extradata_size = 0; + } + } + } + + // for video we need a decoder to get desired information into codec context + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->codec->extradata && + (!st->codec->width || st->codec->pix_fmt == PIX_FMT_NONE)) + { + // open a decoder, it will be cleared down by ffmpeg on closing the stream + if (!st->codec->codec) + { + const AVCodec* codec; + AVDictionary *thread_opt = NULL; + codec = avcodec_find_decoder(st->codec->codec_id); + // Force thread count to 1 since the h264 decoder will not extract + // SPS and PPS to extradata during multi-threaded decoding + av_dict_set(&thread_opt, "threads", "1", 0); + int res = avcodec_open2(st->codec, codec, &thread_opt); + if(res < 0) + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::ParsePacket() unable to open codec %d", res); + av_dict_free(&thread_opt); + } + + // We don't need to actually decode here + // we just want to transport SPS data into codec context + st->codec->skip_idct = AVDISCARD_ALL; + // extradata is not decoded if skip_frame >= AVDISCARD_NONREF +// st->codec->skip_frame = AVDISCARD_ALL; + st->codec->skip_loop_filter = AVDISCARD_ALL; + + // We are looking for an IDR frame + AVFrame picture; + memset(&picture, 0, sizeof(AVFrame)); + picture.pts = picture.pkt_dts = picture.pkt_pts = picture.best_effort_timestamp = AV_NOPTS_VALUE; + picture.pkt_pos = -1; + picture.key_frame = 1; + picture.format = -1; + + int got_picture = 0; + avcodec_decode_video2(st->codec, &picture, &got_picture, pkt); + } +} + +bool CDVDDemuxFFmpeg::IsVideoReady() +{ + AVStream *st; + bool hasVideo = false; + + if(!m_checkvideo) + return true; + + if (m_program == 0 && !m_pFormatContext->nb_programs) + return false; + + if(m_program != UINT_MAX) + { + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + int idx = m_pFormatContext->programs[m_program]->stream_index[i]; + st = m_pFormatContext->streams[idx]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + if (st->codec->width && st->codec->pix_fmt != PIX_FMT_NONE) + return true; + hasVideo = true; + } + } + } + else + { + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + st = m_pFormatContext->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + if (st->codec->width && st->codec->pix_fmt != PIX_FMT_NONE) + return true; + hasVideo = true; + } + } + } + return !hasVideo; +} + +void CDVDDemuxFFmpeg::ResetVideoStreams() +{ + AVStream *st; + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + st = m_pFormatContext->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + if (st->codec->extradata) + av_free(st->codec->extradata); + st->codec->extradata = NULL; + st->codec->width = 0; + } + } +} + +void CDVDDemuxFFmpeg::GetL16Parameters(int &channels, int &samplerate) +{ + std::string content; + if (XFILE::CCurlFile::GetContentType(m_pInput->GetURL(), content)) + { + StringUtils::ToLower(content); + const size_t len = content.length(); + size_t pos = content.find(';'); + while (pos < len) + { + // move to the next non-whitespace character + pos = content.find_first_not_of(" \t", pos + 1); + + if (pos != std::string::npos) + { + if (content.compare(pos, 9, "channels=", 9) == 0) + { + pos += 9; // move position to char after 'channels=' + size_t len = content.find(';', pos); + if (len != std::string::npos) + len -= pos; + std::string no_channels(content, pos, len); + // as we don't support any charset with ';' in name + StringUtils::Trim(no_channels, " \t"); + if (!no_channels.empty()) + { + int val = strtol(no_channels.c_str(), NULL, 0); + if (val > 0) + channels = val; + else + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::%s - no parameter for channels", __FUNCTION__); + } + } + else if (content.compare(pos, 5, "rate=", 5) == 0) + { + pos += 5; // move position to char after 'rate=' + size_t len = content.find(';', pos); + if (len != std::string::npos) + len -= pos; + std::string rate(content, pos, len); + // as we don't support any charset with ';' in name + StringUtils::Trim(rate, " \t"); + if (!rate.empty()) + { + int val = strtol(rate.c_str(), NULL, 0); + if (val > 0) + samplerate = val; + else + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::%s - no parameter for samplerate", __FUNCTION__); + } + } + pos = content.find(';', pos); // find next parameter + } + } + } +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h new file mode 100644 index 0000000..d180e40 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h @@ -0,0 +1,172 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include "threads/CriticalSection.h" +#include "threads/SystemClock.h" +#include +#include + +extern "C" { +#include "libavformat/avformat.h" +} + +class CDVDDemuxFFmpeg; +class CURL; + +class CDemuxStreamVideoFFmpeg + : public CDemuxStreamVideo +{ + CDVDDemuxFFmpeg *m_parent; + AVStream* m_stream; +public: + CDemuxStreamVideoFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream) + : m_parent(parent) + , m_stream(stream) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + + +class CDemuxStreamAudioFFmpeg + : public CDemuxStreamAudio +{ + CDVDDemuxFFmpeg *m_parent; + AVStream* m_stream; +public: + CDemuxStreamAudioFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream) + : m_parent(parent) + , m_stream(stream) + {} + std::string m_description; + + virtual void GetStreamInfo(std::string& strInfo); + virtual void GetStreamName(std::string& strInfo); +}; + +class CDemuxStreamSubtitleFFmpeg + : public CDemuxStreamSubtitle +{ + CDVDDemuxFFmpeg *m_parent; + AVStream* m_stream; +public: + CDemuxStreamSubtitleFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream) + : m_parent(parent) + , m_stream(stream) + {} + std::string m_description; + + virtual void GetStreamInfo(std::string& strInfo); + virtual void GetStreamName(std::string& strInfo); + +}; + +#define FFMPEG_FILE_BUFFER_SIZE 32768 // default reading size for ffmpeg +#define FFMPEG_DVDNAV_BUFFER_SIZE 2048 // for dvd's + +struct StereoModeConversionMap; + +class CDVDDemuxFFmpeg : public CDVDDemux +{ +public: + CDVDDemuxFFmpeg(); + virtual ~CDVDDemuxFFmpeg(); + + bool Open(CDVDInputStream* pInput, bool streaminfo = true, bool fileinfo = false); + void Dispose(); + void Reset(); + void Flush(); + void Abort(); + void SetSpeed(int iSpeed); + virtual std::string GetFileName(); + + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + bool SeekByte(int64_t pos); + int GetStreamLength(); + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + + bool SeekChapter(int chapter, double* startpts = NULL); + int GetChapterCount(); + int GetChapter(); + void GetChapterName(std::string& strChapterName, int chapterIdx=-1); + int64_t GetChapterPos(int chapterIdx=-1); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + + bool Aborted(); + + AVFormatContext* m_pFormatContext; + CDVDInputStream* m_pInput; + +protected: + friend class CDemuxStreamAudioFFmpeg; + friend class CDemuxStreamVideoFFmpeg; + friend class CDemuxStreamSubtitleFFmpeg; + + int ReadFrame(AVPacket *packet); + CDemuxStream* AddStream(int iId); + void AddStream(int iId, CDemuxStream* stream); + CDemuxStream* GetStreamInternal(int iStreamId); + void CreateStreams(unsigned int program = UINT_MAX); + void DisposeStreams(); + void ParsePacket(AVPacket *pkt); + bool IsVideoReady(); + void ResetVideoStreams(); + + AVDictionary *GetFFMpegOptionsFromURL(const CURL &url); + double ConvertTimestamp(int64_t pts, int den, int num); + void UpdateCurrentPTS(); + bool IsProgramChange(); + + std::string GetStereoModeFromMetadata(AVDictionary *pMetadata); + std::string ConvertCodecToInternalStereoMode(const std::string &mode, const StereoModeConversionMap *conversionMap); + + void GetL16Parameters(int &channels, int &samplerate); + + CCriticalSection m_critSection; + std::map m_streams; + std::vector::iterator> m_stream_index; + + AVIOContext* m_ioContext; + + double m_currentPts; // used for stream length estimation + bool m_bMatroska; + bool m_bAVI; + int m_speed; + unsigned m_program; + XbmcThreads::EndTime m_timeout; + + // Due to limitations of ffmpeg, we only can detect a program change + // with a packet. This struct saves the packet for the next read and + // signals STREAMCHANGE to player + struct + { + AVPacket pkt; // packet ffmpeg returned + int result; // result from av_read_packet + }m_pkt; + + bool m_streaminfo; + bool m_checkvideo; +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp new file mode 100644 index 0000000..3674416 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + + +#include "DVDCodecs/DVDCodecs.h" +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDInputStreams/DVDInputStreamHTSP.h" +#include "DVDDemuxHTSP.h" +#include "DVDDemuxUtils.h" +#include "DVDClock.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include + +extern "C" { +#include "lib/libhts/net.h" +#include "lib/libhts/htsmsg.h" +#include "lib/libhts/htsmsg_binary.h" +} + +using namespace std; +using namespace HTSP; + +class CDemuxStreamVideoHTSP + : public CDemuxStreamVideo +{ + CDVDDemuxHTSP *m_parent; + string m_codec; +public: + CDemuxStreamVideoHTSP(CDVDDemuxHTSP *parent, const string& codec) + : m_parent(parent) + , m_codec(codec) + {} + void GetStreamInfo(std::string& strInfo) + { + strInfo = StringUtils::Format("%s, delay: %u, drops: %ub %up %ui" + , m_codec.c_str() + , m_parent->m_QueueStatus.delay + , m_parent->m_QueueStatus.bdrops + , m_parent->m_QueueStatus.pdrops + , m_parent->m_QueueStatus.idrops); + } +}; + +class CDemuxStreamAudioHTSP + : public CDemuxStreamAudio +{ + CDVDDemuxHTSP *m_parent; + string m_codec; +public: + CDemuxStreamAudioHTSP(CDVDDemuxHTSP *parent, const string& codec) + : m_parent(parent) + , m_codec(codec) + + {} + void GetStreamInfo(string& strInfo) + { + strInfo = StringUtils::Format("%s", m_codec.c_str()); + } +}; + +CDVDDemuxHTSP::CDVDDemuxHTSP() + : CDVDDemux() + , m_Input(NULL) + , m_StatusCount(0) +{ +} + +CDVDDemuxHTSP::~CDVDDemuxHTSP() +{ + Dispose(); +} + +bool CDVDDemuxHTSP::Open(CDVDInputStream* input) +{ + Dispose(); + + if(!input->IsStreamType(DVDSTREAM_TYPE_HTSP)) + return false; + + m_Input = (CDVDInputStreamHTSP*)input; + m_StatusCount = 0; + + while(m_Streams.empty() && m_StatusCount == 0) + { + DemuxPacket* pkg = Read(); + if(!pkg) + return false; + CDVDDemuxUtils::FreeDemuxPacket(pkg); + } + + return true; +} + +void CDVDDemuxHTSP::Dispose() +{ +} + +void CDVDDemuxHTSP::Reset() +{ +} + + +void CDVDDemuxHTSP::Flush() +{ +} + +bool CDVDDemuxHTSP::ReadStream(uint8_t* buf, int len) +{ + while(len > 0) + { + int ret = m_Input->Read(buf, len); + if(ret <= 0) + return false; + len -= ret; + buf += ret; + } + return true; +} + +htsmsg_t* CDVDDemuxHTSP::ReadStream() +{ + if(m_Input->IsStreamType(DVDSTREAM_TYPE_HTSP)) + return ((CDVDInputStreamHTSP*)m_Input)->ReadStream(); + + uint32_t l; + if(!ReadStream((uint8_t*)&l, 4)) + return NULL; + + l = ntohl(l); + if(l == 0) + return htsmsg_create_map(); + + uint8_t* buf = (uint8_t*)malloc(l); + if(!buf) + return NULL; + + if(!ReadStream(buf, l)) + return NULL; + + return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */ +} + +DemuxPacket* CDVDDemuxHTSP::Read() +{ + htsmsg_t * msg; + while((msg = ReadStream())) + { + const char* method = htsmsg_get_str(msg, "method"); + if(method == NULL) + break; + + if (strcmp("subscriptionStart", method) == 0) + SubscriptionStart(msg); + else if(strcmp("subscriptionStop", method) == 0) + SubscriptionStop (msg); + else if(strcmp("subscriptionStatus", method) == 0) + SubscriptionStatus(msg); + else if(strcmp("queueStatus" , method) == 0) + CHTSPSession::ParseQueueStatus(msg, m_QueueStatus); + else if(strcmp("muxpkt" , method) == 0) + { + uint32_t index, duration; + const void* bin; + size_t binlen; + int64_t ts; + + if(htsmsg_get_u32(msg, "stream" , &index) || + htsmsg_get_bin(msg, "payload", &bin, &binlen)) + break; + + DemuxPacket* pkt = CDVDDemuxUtils::AllocateDemuxPacket(binlen); + + memcpy(pkt->pData, bin, binlen); + pkt->iSize = binlen; + + if(!htsmsg_get_u32(msg, "duration", &duration)) + pkt->duration = (double)duration * DVD_TIME_BASE / 1000000; + + if(!htsmsg_get_s64(msg, "dts", &ts)) + pkt->dts = (double)ts * DVD_TIME_BASE / 1000000; + else + pkt->dts = DVD_NOPTS_VALUE; + + if(!htsmsg_get_s64(msg, "pts", &ts)) + pkt->pts = (double)ts * DVD_TIME_BASE / 1000000; + else + pkt->pts = DVD_NOPTS_VALUE; + + pkt->iStreamId = -1; + for(int i = 0; i < (int)m_Streams.size(); i++) + { + if(m_Streams[i]->iPhysicalId == (int)index) + { + pkt->iStreamId = i; + break; + } + } + + htsmsg_destroy(msg); + return pkt; + } + + break; + } + + if(msg) + { + htsmsg_destroy(msg); + return CDVDDemuxUtils::AllocateDemuxPacket(0); + } + return NULL; +} + +void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m) +{ + htsmsg_t *streams; + htsmsg_t *info; + htsmsg_field_t *f; + + if((info = htsmsg_get_map(m, "sourceinfo"))) + { + HTSMSG_FOREACH(f, info) + { + if(f->hmf_type != HMF_STR) + continue; + CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - %s: %s", f->hmf_name, htsmsg_field_get_string(f)); + } + } + + if((streams = htsmsg_get_list(m, "streams")) == NULL) + { + CLog::Log(LOGERROR, "CDVDDemuxHTSP::SubscriptionStart - malformed message"); + return; + } + + for(int i = 0; i < (int)m_Streams.size(); i++) + delete m_Streams[i]; + m_Streams.clear(); + + HTSMSG_FOREACH(f, streams) + { + uint32_t index; + const char* type; + const char* lang; + htsmsg_t* sub; + + if(f->hmf_type != HMF_MAP) + continue; + sub = &f->hmf_msg; + + if((type = htsmsg_get_str(sub, "type")) == NULL) + continue; + + if(htsmsg_get_u32(sub, "index", &index)) + continue; + + union { + CDemuxStream* g; + CDemuxStreamAudio* a; + CDemuxStreamVideo* v; + CDemuxStreamSubtitle* s; + CDemuxStreamTeletext* t; + } st; + + CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - id: %d, type: %s", index, type); + + if(!strcmp(type, "AC3")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_AC3; + } else if(!strcmp(type, "EAC3")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_EAC3; + } else if(!strcmp(type, "MPEG2AUDIO")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_MP2; + } else if(!strcmp(type, "AAC")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_AAC; + } else if(!strcmp(type, "MPEG2VIDEO")) { + st.v = new CDemuxStreamVideoHTSP(this, type); + st.v->codec = AV_CODEC_ID_MPEG2VIDEO; + st.v->iWidth = htsmsg_get_u32_or_default(sub, "width" , 0); + st.v->iHeight = htsmsg_get_u32_or_default(sub, "height", 0); + } else if(!strcmp(type, "H264")) { + st.v = new CDemuxStreamVideoHTSP(this, type); + st.v->codec = AV_CODEC_ID_H264; + st.v->iWidth = htsmsg_get_u32_or_default(sub, "width" , 0); + st.v->iHeight = htsmsg_get_u32_or_default(sub, "height", 0); + } else if(!strcmp(type, "DVBSUB")) { + st.s = new CDemuxStreamSubtitle(); + st.s->codec = AV_CODEC_ID_DVB_SUBTITLE; + uint32_t composition_id = 0, ancillary_id = 0; + htsmsg_get_u32(sub, "composition_id", &composition_id); + htsmsg_get_u32(sub, "ancillary_id" , &ancillary_id); + if(composition_id || ancillary_id) + { + st.s->ExtraData = new uint8_t[4]; + st.s->ExtraSize = 4; + st.s->ExtraData[0] = (composition_id >> 8) & 0xff; + st.s->ExtraData[1] = (composition_id >> 0) & 0xff; + st.s->ExtraData[2] = (ancillary_id >> 8) & 0xff; + st.s->ExtraData[3] = (ancillary_id >> 0) & 0xff; + } + } else if(!strcmp(type, "TEXTSUB")) { + st.s = new CDemuxStreamSubtitle(); + st.s->codec = AV_CODEC_ID_TEXT; + } else if(!strcmp(type, "TELETEXT")) { + st.t = new CDemuxStreamTeletext(); + st.t->codec = AV_CODEC_ID_DVB_TELETEXT; + } else { + continue; + } + + if((lang = htsmsg_get_str(sub, "language"))) + { + strncpy(st.g->language, lang, sizeof(st.g->language)); + st.g->language[sizeof(st.g->language) - 1] = '\0'; + } + + st.g->iId = m_Streams.size(); + st.g->iPhysicalId = index; + m_Streams.push_back(st.g); + } +} +void CDVDDemuxHTSP::SubscriptionStop (htsmsg_t *m) +{ + for(int i = 0; i < (int)m_Streams.size(); i++) + delete m_Streams[i]; + m_Streams.clear(); +} + +void CDVDDemuxHTSP::SubscriptionStatus(htsmsg_t *m) +{ + const char* status; + status = htsmsg_get_str(m, "status"); + if(status == NULL) + m_Status = ""; + else + { + m_StatusCount++; + m_Status = status; + CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStatus - %s", status); + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, "TVHeadend Status", status, TOAST_DISPLAY_TIME, false); + } +} + +CDemuxStream* CDVDDemuxHTSP::GetStream(int iStreamId) +{ + if(iStreamId >= 0 && iStreamId < (int)m_Streams.size()) + return m_Streams[iStreamId]; + + return NULL; +} + +int CDVDDemuxHTSP::GetNrOfStreams() +{ + return m_Streams.size(); +} + +std::string CDVDDemuxHTSP::GetFileName() +{ + if(m_Input) + return m_Input->GetFileName(); + else + return ""; +} + +void CDVDDemuxHTSP::Abort() +{ + if(m_Input) + return m_Input->Abort(); +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h new file mode 100644 index 0000000..6d73a9d --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once +#include "DVDDemux.h" +#include "filesystem/HTSPSession.h" + +class CDVDInputStreamHTSP; +typedef struct htsmsg htsmsg_t; + +class CDVDDemuxHTSP : public CDVDDemux +{ +public: + CDVDDemuxHTSP(); + virtual ~CDVDDemuxHTSP(); + + bool Open(CDVDInputStream* input); + void Dispose(); + void Reset(); + void Flush(); + void Abort(); + void SetSpeed(int iSpeed){}; + + std::string GetFileName(); + + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; } + int GetStreamLength() { return 0; } + + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + +protected: + friend class CDemuxStreamVideoHTSP; + + void SubscriptionStart (htsmsg_t *m); + void SubscriptionStop (htsmsg_t *m); + void SubscriptionStatus(htsmsg_t *m); + + htsmsg_t* ReadStream(); + bool ReadStream(uint8_t* buf, int len); + + typedef std::vector TStreams; + + CDVDInputStream* m_Input; + TStreams m_Streams; + std::string m_Status; + int m_StatusCount; + HTSP::SQueueStatus m_QueueStatus; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp new file mode 100644 index 0000000..4ed2d5c --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxPVRClient.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "../DVDClock.h" + +#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE) + +using namespace PVR; + +CDemuxStreamPVRInternal::CDemuxStreamPVRInternal(CDVDDemuxPVRClient *parent) + : m_parent(parent) + , m_parser(NULL) + , m_context(NULL) + , m_parser_split(false) +{ +} + +CDemuxStreamPVRInternal::~CDemuxStreamPVRInternal() +{ + DisposeParser(); +} + +void CDemuxStreamPVRInternal::DisposeParser() +{ + if (m_parser) + { + av_parser_close(m_parser); + m_parser = NULL; + } + if (m_context) + { + avcodec_close(m_context); + m_context = NULL; + } +} + +void CDemuxStreamVideoPVRClient::GetStreamInfo(std::string& strInfo) +{ + switch (codec) + { + case AV_CODEC_ID_MPEG2VIDEO: + strInfo = "mpeg2video"; + break; + case AV_CODEC_ID_H264: + strInfo = "h264"; + break; + default: + break; + } +} + +void CDemuxStreamAudioPVRClient::GetStreamInfo(std::string& strInfo) +{ + switch (codec) + { + case AV_CODEC_ID_AC3: + strInfo = "ac3"; + break; + case AV_CODEC_ID_EAC3: + strInfo = "eac3"; + break; + case AV_CODEC_ID_MP2: + strInfo = "mpeg2audio"; + break; + case AV_CODEC_ID_AAC: + strInfo = "aac"; + break; + case AV_CODEC_ID_DTS: + strInfo = "dts"; + break; + default: + break; + } +} + +void CDemuxStreamSubtitlePVRClient::GetStreamInfo(std::string& strInfo) +{ +} + +CDVDDemuxPVRClient::CDVDDemuxPVRClient() : CDVDDemux() +{ + m_pInput = NULL; + for (int i = 0; i < MAX_STREAMS; i++) m_streams[i] = NULL; +} + +CDVDDemuxPVRClient::~CDVDDemuxPVRClient() +{ + Dispose(); +} + +bool CDVDDemuxPVRClient::Open(CDVDInputStream* pInput) +{ + Abort(); + + m_pInput = pInput; + if (!g_PVRClients->GetPlayingClient(m_pvrClient)) + return false; + + return true; +} + +void CDVDDemuxPVRClient::Dispose() +{ + for (int i = 0; i < MAX_STREAMS; i++) + { + delete m_streams[i]; + m_streams[i] = NULL; + } + + m_pInput = NULL; +} + +void CDVDDemuxPVRClient::DisposeStream(int iStreamId) +{ + if (iStreamId < 0 || iStreamId >= MAX_STREAMS) + return; + delete m_streams[iStreamId]; + m_streams[iStreamId] = NULL; +} + +void CDVDDemuxPVRClient::Reset() +{ + if(m_pInput && g_PVRManager.IsStarted()) + m_pvrClient->DemuxReset(); + + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxPVRClient::Abort() +{ + if(m_pInput) + m_pvrClient->DemuxAbort(); +} + +void CDVDDemuxPVRClient::Flush() +{ + if(m_pInput && g_PVRManager.IsStarted()) + m_pvrClient->DemuxFlush(); +} + +void CDVDDemuxPVRClient::ParsePacket(DemuxPacket* pkt) +{ + CDemuxStream* st = m_streams[pkt->iStreamId]; + if (st == NULL) + return; + + if (st->ExtraSize) + return; + + CDemuxStreamPVRInternal* pvr = dynamic_cast(st); + + if(pvr == NULL + || pvr->m_parser == NULL) + return; + + if(pvr->m_context == NULL) + { + AVCodec *codec = avcodec_find_decoder(st->codec); + if (codec == NULL) + { + CLog::Log(LOGERROR, "%s - can't find decoder", __FUNCTION__); + pvr->DisposeParser(); + return; + } + + pvr->m_context = avcodec_alloc_context3(codec); + if(pvr->m_context == NULL) + { + CLog::Log(LOGERROR, "%s - can't allocate context", __FUNCTION__); + pvr->DisposeParser(); + return; + } + pvr->m_context->time_base.num = 1; + pvr->m_context->time_base.den = DVD_TIME_BASE; + } + + if(pvr->m_parser_split && pvr->m_parser->parser->split) + { + int len = pvr->m_parser->parser->split(pvr->m_context, pkt->pData, pkt->iSize); + if (len > 0 && len < FF_MAX_EXTRADATA_SIZE) + { + if (st->ExtraData) + delete[] (uint8_t*)st->ExtraData; + st->changes++; + st->disabled = false; + st->ExtraSize = len; + st->ExtraData = new uint8_t[len+FF_INPUT_BUFFER_PADDING_SIZE]; + memcpy(st->ExtraData, pkt->pData, len); + memset((uint8_t*)st->ExtraData + len, 0 , FF_INPUT_BUFFER_PADDING_SIZE); + pvr->m_parser_split = false; + } + } + + + uint8_t *outbuf = NULL; + int outbuf_size = 0; + int len = av_parser_parse2(pvr->m_parser + , pvr->m_context, &outbuf, &outbuf_size + , pkt->pData, pkt->iSize + , (int64_t)(pkt->pts * DVD_TIME_BASE) + , (int64_t)(pkt->dts * DVD_TIME_BASE) + , 0); + /* our parse is setup to parse complete frames, so we don't care about outbufs */ + if(len >= 0) + { +#define CHECK_UPDATE(st, trg, src, invalid) do { \ + if(src != invalid \ + && src != st->trg) { \ + CLog::Log(LOGDEBUG, "%s - {%d} " #trg " changed from %d to %d", __FUNCTION__, st->iId, st->trg, src); \ + st->trg = src; \ + st->changes++; \ + st->disabled = false; \ + } \ + } while(0) + + + CHECK_UPDATE(st, profile, pvr->m_context->profile , FF_PROFILE_UNKNOWN); + CHECK_UPDATE(st, level , pvr->m_context->level , FF_LEVEL_UNKNOWN); + + switch (st->type) + { + case STREAM_AUDIO: { + CDemuxStreamAudioPVRClient* sta = static_cast(st); + CHECK_UPDATE(sta, iChannels , pvr->m_context->channels , 0); + CHECK_UPDATE(sta, iSampleRate , pvr->m_context->sample_rate, 0); + break; + } + case STREAM_VIDEO: { + CDemuxStreamVideoPVRClient* stv = static_cast(st); + CHECK_UPDATE(stv, iWidth , pvr->m_context->width , 0); + CHECK_UPDATE(stv, iHeight , pvr->m_context->height, 0); + break; + } + + default: + break; + } + +#undef CHECK_UPDATE + } + else + CLog::Log(LOGDEBUG, "%s - parser returned error %d", __FUNCTION__, len); + + return; +} + +DemuxPacket* CDVDDemuxPVRClient::Read() +{ + if (!g_PVRManager.IsStarted()) + return CDVDDemuxUtils::AllocateDemuxPacket(0); + + DemuxPacket* pPacket = m_pvrClient->DemuxRead(); + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + if (pPacket->iStreamId == DMX_SPECIALID_STREAMINFO) + { + RequestStreams(); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + return CDVDDemuxUtils::AllocateDemuxPacket(0); + } + else if (pPacket->iStreamId == DMX_SPECIALID_STREAMCHANGE) + { + RequestStreams(); + } + else if (pPacket->iStreamId >= 0 + && pPacket->iStreamId < MAX_STREAMS + && m_streams[pPacket->iStreamId]) + { + ParsePacket(pPacket); + } + + return pPacket; +} + +CDemuxStream* CDVDDemuxPVRClient::GetStream(int iStreamId) +{ + if (iStreamId < 0 || iStreamId >= MAX_STREAMS) return NULL; + return m_streams[iStreamId]; +} + +void CDVDDemuxPVRClient::RequestStreams() +{ + if (!g_PVRManager.IsStarted()) + return; + + PVR_STREAM_PROPERTIES props = {}; + m_pvrClient->GetStreamProperties(&props); + unsigned int i; + + for (i = 0; i < props.iStreamCount; ++i) + { + CDemuxStream *stm = m_streams[i]; + + if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_AUDIO) + { + CDemuxStreamAudioPVRClient* st = NULL; + if (stm) + { + st = dynamic_cast(stm); + if (!st || (st->codec != (AVCodecID)props.stream[i].iCodecId)) + DisposeStream(i); + } + if (!m_streams[i]) + { + st = new CDemuxStreamAudioPVRClient(this); + st->m_parser = av_parser_init(props.stream[i].iCodecId); + if(st->m_parser) + st->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } + st->iChannels = props.stream[i].iChannels; + st->iSampleRate = props.stream[i].iSampleRate; + st->iBlockAlign = props.stream[i].iBlockAlign; + st->iBitRate = props.stream[i].iBitRate; + st->iBitsPerSample = props.stream[i].iBitsPerSample; + m_streams[i] = st; + st->m_parser_split = true; + st->changes++; + } + else if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_VIDEO) + { + CDemuxStreamVideoPVRClient* st = NULL; + if (stm) + { + st = dynamic_cast(stm); + if (!st + || (st->codec != (AVCodecID)props.stream[i].iCodecId) + || (st->iWidth != props.stream[i].iWidth) + || (st->iHeight != props.stream[i].iHeight)) + DisposeStream(i); + } + if (!m_streams[i]) + { + st = new CDemuxStreamVideoPVRClient(this); + st->m_parser = av_parser_init(props.stream[i].iCodecId); + if(st->m_parser) + st->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } + st->iFpsScale = props.stream[i].iFPSScale; + st->iFpsRate = props.stream[i].iFPSRate; + st->iHeight = props.stream[i].iHeight; + st->iWidth = props.stream[i].iWidth; + st->fAspect = props.stream[i].fAspect; + st->stereo_mode = "mono"; + m_streams[i] = st; + st->m_parser_split = true; + } + else if (props.stream[i].iCodecId == AV_CODEC_ID_DVB_TELETEXT) + { + if (stm) + { + if (stm->codec != (AVCodecID)props.stream[i].iCodecId) + DisposeStream(i); + } + if (!m_streams[i]) + m_streams[i] = new CDemuxStreamTeletext(); + } + else if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_SUBTITLE) + { + CDemuxStreamSubtitlePVRClient* st = NULL; + if (stm) + { + st = dynamic_cast(stm); + if (!st || (st->codec != (AVCodecID)props.stream[i].iCodecId)) + DisposeStream(i); + } + if (!m_streams[i]) + { + st = new CDemuxStreamSubtitlePVRClient(this); + } + if(props.stream[i].iIdentifier) + { + st->ExtraData = new uint8_t[4]; + st->ExtraSize = 4; + st->ExtraData[0] = (props.stream[i].iIdentifier >> 8) & 0xff; + st->ExtraData[1] = (props.stream[i].iIdentifier >> 0) & 0xff; + st->ExtraData[2] = (props.stream[i].iIdentifier >> 24) & 0xff; + st->ExtraData[3] = (props.stream[i].iIdentifier >> 16) & 0xff; + } + m_streams[i] = st; + } + else + { + if (m_streams[i]) + DisposeStream(i); + m_streams[i] = new CDemuxStream(); + } + + m_streams[i]->codec = (AVCodecID)props.stream[i].iCodecId; + m_streams[i]->iId = i; + m_streams[i]->iPhysicalId = props.stream[i].iPhysicalId; + m_streams[i]->language[0] = props.stream[i].strLanguage[0]; + m_streams[i]->language[1] = props.stream[i].strLanguage[1]; + m_streams[i]->language[2] = props.stream[i].strLanguage[2]; + m_streams[i]->language[3] = props.stream[i].strLanguage[3]; + + CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added/updated stream %d:%d with codec_id %d", + m_streams[i]->iId, + m_streams[i]->iPhysicalId, + m_streams[i]->codec); + } + // check if we need to dispose any streams no longer in props + for (unsigned int j = i; j < MAX_STREAMS; j++) + { + if (m_streams[j]) + { + CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): disposed stream %d:%d with codec_id %d", + m_streams[j]->iId, + m_streams[j]->iPhysicalId, + m_streams[j]->codec); + DisposeStream(j); + } + } +} + +int CDVDDemuxPVRClient::GetNrOfStreams() +{ + int i = 0; + while (i < MAX_STREAMS && m_streams[i]) i++; + return i; +} + +std::string CDVDDemuxPVRClient::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, std::string &strName) +{ + CDemuxStream *stream = GetStream(iStreamId); + if (stream) + { + if (stream->codec == AV_CODEC_ID_AC3) + strName = "ac3"; + else if (stream->codec == AV_CODEC_ID_MP2) + strName = "mp2"; + else if (stream->codec == AV_CODEC_ID_AAC) + strName = "aac"; + else if (stream->codec == AV_CODEC_ID_DTS) + strName = "dca"; + else if (stream->codec == AV_CODEC_ID_MPEG2VIDEO) + strName = "mpeg2video"; + else if (stream->codec == AV_CODEC_ID_H264) + strName = "h264"; + else if (stream->codec == AV_CODEC_ID_EAC3) + strName = "eac3"; + } +} + +bool CDVDDemuxPVRClient::SeekTime(int timems, bool backwards, double *startpts) +{ + if (m_pInput) + return m_pvrClient->SeekTime(timems, backwards, startpts); + return false; +} + +void CDVDDemuxPVRClient::SetSpeed ( int speed ) +{ + if (m_pInput) + m_pvrClient->SetSpeed(speed); +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h new file mode 100644 index 0000000..2fc3c63 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h @@ -0,0 +1,118 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include +#include "pvr/addons/PVRClient.h" + +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +} + +class CDVDDemuxPVRClient; +struct PVR_STREAM_PROPERTIES; + +class CDemuxStreamPVRInternal +{ +public: + CDemuxStreamPVRInternal(CDVDDemuxPVRClient *parent); + ~CDemuxStreamPVRInternal(); + + void DisposeParser(); + + CDVDDemuxPVRClient * m_parent; + AVCodecParserContext* m_parser; + AVCodecContext * m_context; + bool m_parser_split; +}; + +class CDemuxStreamVideoPVRClient + : public CDemuxStreamVideo + , public CDemuxStreamPVRInternal +{ +public: + CDemuxStreamVideoPVRClient(CDVDDemuxPVRClient *parent) + : CDemuxStreamPVRInternal(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDemuxStreamAudioPVRClient + : public CDemuxStreamAudio + , public CDemuxStreamPVRInternal +{ +public: + CDemuxStreamAudioPVRClient(CDVDDemuxPVRClient *parent) + : CDemuxStreamPVRInternal(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDemuxStreamSubtitlePVRClient + : public CDemuxStreamSubtitle + , public CDemuxStreamPVRInternal +{ +public: + CDemuxStreamSubtitlePVRClient(CDVDDemuxPVRClient *parent) + : CDemuxStreamPVRInternal(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + + +class CDVDDemuxPVRClient : public CDVDDemux +{ + friend class CDemuxStreamPVRInternal; + +public: + + CDVDDemuxPVRClient(); + ~CDVDDemuxPVRClient(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + void SetSpeed(int iSpeed); + int GetStreamLength() { return 0; } + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + +protected: + CDVDInputStream* m_pInput; +#ifndef MAX_STREAMS + #define MAX_STREAMS 100 +#endif + CDemuxStream* m_streams[MAX_STREAMS]; // maximum number of streams that ffmpeg can handle + std::shared_ptr m_pvrClient; + +private: + void RequestStreams(); + void ParsePacket(DemuxPacket* pPacket); + void DisposeStream(int iStreamId); +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h new file mode 100644 index 0000000..d64fbb3 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h @@ -0,0 +1,36 @@ +#pragma once + +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#define DMX_SPECIALID_STREAMINFO -10 +#define DMX_SPECIALID_STREAMCHANGE -11 + + typedef struct DemuxPacket +{ + unsigned char* pData; // data + int iSize; // data size + int iStreamId; // integer representing the stream index + int iGroupId; // the group this data belongs to, used to group data from different streams together + + double pts; // pts in DVD_TIME_BASE + double dts; // dts in DVD_TIME_BASE + double duration; // duration in DVD_TIME_BASE if available +} DemuxPacket; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp new file mode 100644 index 0000000..a69342a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDCodecs/DVDCodecs.h" +#include "DVDInputStreams/DVDInputStreamHttp.h" +#include "DVDDemuxShoutcast.h" +#include "DVDDemuxUtils.h" +#include "DVDClock.h" // for DVD_TIME_BASE +#include "../../../utils/HttpHeader.h" + +#define ICY_NOTICE1 "icy-notice1" // string +#define ICY_NOTICE2 "icy-notice2" // string +#define ICY_NAME "icy-name" // string +#define ICY_GENRE "icy-genre" // string +#define ICY_URL "icy-url" // string +#define ICY_PUBLIC "icy-pub" // int (1 / 0) +#define ICY_BITRATE "icy-br" // int (bitrate = val * 1000 ?) +#define ICY_METAINTERVAL "icy-metaint" // int + +#define CONTENT_TYPE_MP3 "audio/mpeg" +#define CONTENT_TYPE_AAC "audio/aac" +#define CONTENT_TYPE_AACPLUS "audio/aacp" + +// class CDemuxStreamVideoFFmpeg +void CDemuxStreamAudioShoutcast::GetStreamInfo(std::string& strInfo) +{ + strInfo = "Shoutcast"; +} + +CDVDDemuxShoutcast::CDVDDemuxShoutcast() : CDVDDemux() +{ + m_pInput = NULL; + m_pDemuxStream = NULL; + m_iMetaStreamInterval = 0; +} + +CDVDDemuxShoutcast::~CDVDDemuxShoutcast() +{ + Dispose(); +} + +bool CDVDDemuxShoutcast::Open(CDVDInputStream* pInput) +{ + Dispose(); + + m_pInput = pInput; + + // the input stream should be a http stream + if (!pInput->IsStreamType(DVDSTREAM_TYPE_HTTP)) return false; + CDVDInputStreamHttp* pInputStreamHttp = (CDVDInputStreamHttp*)pInput; + + CHttpHeader* pHeader = pInputStreamHttp->GetHttpHeader(); + + std::string strMetaInt = pHeader->GetValue(ICY_METAINTERVAL); + std::string strMimeType = pHeader->GetMimeType(); + + // create new demuxer stream + m_pDemuxStream = new CDemuxStreamAudioShoutcast(); + m_pDemuxStream->iId = 0; + m_pDemuxStream->iPhysicalId = 0; + m_pDemuxStream->iDuration = 0; + m_pDemuxStream->iChannels = 2; + m_pDemuxStream->iSampleRate = 0; + + // set meta interval + m_iMetaStreamInterval = atoi(strMetaInt.c_str()); + + if (stricmp(strMimeType.c_str(), CONTENT_TYPE_AAC) == 0 || + stricmp(strMimeType.c_str(), CONTENT_TYPE_AACPLUS) == 0) + { + // need an aac decoder first + m_pDemuxStream->codec = AV_CODEC_ID_AAC; + } + else // (stricmp(strMimeType, CONTENT_TYPE_MP3) == 0) + { + // default to mp3 + m_pDemuxStream->codec = AV_CODEC_ID_MP3; + } + + return true; +} + +void CDVDDemuxShoutcast::Dispose() +{ + if (m_pDemuxStream) delete m_pDemuxStream; + m_pDemuxStream = NULL; + + m_pInput = NULL; +} + +void CDVDDemuxShoutcast::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxShoutcast::Flush() +{ +} + +DemuxPacket* CDVDDemuxShoutcast::Read() +{ + // XXX + // if meta interval is greater than FileCurl's max read size (currently 64k) + // it will simply fail becuse the meta-interval will get incorrect + + int iDataToRead = SHOUTCAST_BUFFER_SIZE; + if (m_iMetaStreamInterval > 0) iDataToRead = m_iMetaStreamInterval; + + DemuxPacket* pPacket; + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(iDataToRead); + if (pPacket) + { + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + pPacket->iStreamId = 0; + + // read the data + int iRead = m_pInput->Read(pPacket->pData, iDataToRead); + + pPacket->iSize = iRead; + + if (iRead <= 0) + { + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + pPacket = NULL; + } + } + + if (m_iMetaStreamInterval > 0) + { + // we already have read m_iMetaStreamInterval bytes of streaming data + // metadata follows + uint8_t l; + int iRead = m_pInput->Read(&l, 1); + if (iRead > 0) + { + int iMetaLength = l * 16; + + if (iMetaLength > 0) + { + // iMetaLength cannot be larger then 16 * 255 + uint8_t buffer[16 * 255]; + + // skip meta data for now + m_pInput->Read(buffer, iMetaLength); + } + } + } + + return pPacket; +} + +bool CDVDDemuxShoutcast::SeekTime(int time, bool backwords, double* startpts) +{ + return false; +} + +int CDVDDemuxShoutcast::GetStreamLength() +{ + return 0; +} + +CDemuxStream* CDVDDemuxShoutcast::GetStream(int iStreamId) +{ + return m_pDemuxStream; +} + +int CDVDDemuxShoutcast::GetNrOfStreams() +{ + return 1; +} + +std::string CDVDDemuxShoutcast::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h new file mode 100644 index 0000000..a802167 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h @@ -0,0 +1,60 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" + +class CDemuxStreamAudioShoutcast : public CDemuxStreamAudio +{ +public: + virtual void GetStreamInfo(std::string& strInfo); +}; + +#define SHOUTCAST_BUFFER_SIZE 1024 * 32 + +class CDVDDemuxShoutcast : public CDVDDemux +{ +public: + CDVDDemuxShoutcast(); + virtual ~CDVDDemuxShoutcast(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Flush(); + void Abort(){} + void SetSpeed(int iSpeed){}; + virtual std::string GetFileName(); + + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + int GetStreamLength(); + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + +protected: + + CDemuxStreamAudioShoutcast* m_pDemuxStream; + + int m_iMetaStreamInterval; + CDVDInputStream* m_pInput; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp new file mode 100644 index 0000000..ab298b2 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS) + #include "config.h" +#endif +#include "DVDDemuxUtils.h" +#include "DVDClock.h" +#include "utils/log.h" + +extern "C" { +#include "libavcodec/avcodec.h" +} + +void CDVDDemuxUtils::FreeDemuxPacket(DemuxPacket* pPacket) +{ + if (pPacket) + { + try { + if (pPacket->pData) _aligned_free(pPacket->pData); + delete pPacket; + } + catch(...) { + CLog::Log(LOGERROR, "%s - Exception thrown while freeing packet", __FUNCTION__); + } + } +} + +DemuxPacket* CDVDDemuxUtils::AllocateDemuxPacket(int iDataSize) +{ + DemuxPacket* pPacket = new DemuxPacket; + if (!pPacket) return NULL; + + try + { + memset(pPacket, 0, sizeof(DemuxPacket)); + + if (iDataSize > 0) + { + // need to allocate a few bytes more. + // From avcodec.h (ffmpeg) + /** + * Required number of additionally allocated bytes at the end of the input bitstream for decoding. + * this is mainly needed because some optimized bitstream readers read + * 32 or 64 bit at once and could read over the end
+ * Note, if the first 23 bits of the additional bytes are not 0 then damaged + * MPEG bitstreams could cause overread and segfault + */ + pPacket->pData =(uint8_t*)_aligned_malloc(iDataSize + FF_INPUT_BUFFER_PADDING_SIZE, 16); + if (!pPacket->pData) + { + FreeDemuxPacket(pPacket); + return NULL; + } + + // reset the last 8 bytes to 0; + memset(pPacket->pData + iDataSize, 0, FF_INPUT_BUFFER_PADDING_SIZE); + } + + // setup defaults + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + pPacket->iStreamId = -1; + } + catch(...) + { + CLog::Log(LOGERROR, "%s - Exception thrown", __FUNCTION__); + FreeDemuxPacket(pPacket); + pPacket = NULL; + } + return pPacket; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h new file mode 100644 index 0000000..2c12df3 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h @@ -0,0 +1,31 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxPacket.h" + +class CDVDDemuxUtils +{ +public: + static void FreeDemuxPacket(DemuxPacket* pPacket); + static DemuxPacket* AllocateDemuxPacket(int iDataSize = 0); +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp new file mode 100644 index 0000000..9625a19 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxVobsub.h" +#include "DVDInputStreams/DVDFactoryInputStream.h" +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDStreamInfo.h" +#include "DVDCodecs/DVDCodecs.h" +#include "DVDDemuxers/DVDDemuxFFmpeg.h" +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "DVDClock.h" +#include "DVDSubtitles/DVDSubtitleStream.h" + +#include + +using namespace std; + +CDVDDemuxVobsub::CDVDDemuxVobsub() +{ +} + +CDVDDemuxVobsub::~CDVDDemuxVobsub() +{ + for(unsigned i=0;i pStream(new CDVDSubtitleStream()); + if(!pStream->Open(filename)) + return false; + + string vobsub = subfilename; + if ( vobsub == "") + { + vobsub = filename; + vobsub.erase(vobsub.rfind('.'), vobsub.size()); + vobsub += ".sub"; + } + + m_Input.reset(CDVDFactoryInputStream::CreateInputStream(NULL, vobsub, "")); + if(!m_Input.get() || !m_Input->Open(vobsub.c_str(), "video/x-vobsub")) + return false; + + m_Demuxer.reset(new CDVDDemuxFFmpeg()); + if(!m_Demuxer->Open(m_Input.get())) + return false; + + CDVDStreamInfo hints; + CDVDCodecOptions options; + hints.codec = AV_CODEC_ID_DVD_SUBTITLE; + + char line[2048]; + DECLARE_UNUSED(bool,res) + + SState state; + state.delay = 0; + state.id = -1; + + while( pStream->ReadLine(line, sizeof(line)) ) + { + if (*line == 0 || *line == '\r' || *line == '\n' || *line == '#') + continue; + else if (strncmp("langidx:", line, 8) == 0) + res = ParseLangIdx(state, line + 8); + else if (strncmp("delay:", line, 6) == 0) + res = ParseDelay(state, line + 6); + else if (strncmp("id:", line, 3) == 0) + res = ParseId(state, line + 3); + else if (strncmp("timestamp:", line, 10) == 0) + res = ParseTimestamp(state, line + 10); + else if (strncmp("palette:", line, 8) == 0 + || strncmp("size:", line, 5) == 0 + || strncmp("org:", line, 4) == 0 + || strncmp("custom colors:", line, 14) == 0 + || strncmp("scale:", line, 6) == 0 + || strncmp("alpha:", line, 6) == 0 + || strncmp("fadein/out:", line, 11) == 0 + || strncmp("forced subs:", line, 12) == 0) + res = ParseExtra(state, line); + else + continue; + } + + struct sorter s; + sort(m_Timestamps.begin(), m_Timestamps.end(), s); + m_Timestamp = m_Timestamps.begin(); + + for(unsigned i=0;iExtraSize = state.extra.length()+1; + m_Streams[i]->ExtraData = new uint8_t[m_Streams[i]->ExtraSize]; + strcpy((char*)m_Streams[i]->ExtraData, state.extra.c_str()); + } + + return true; +} + +void CDVDDemuxVobsub::Reset() +{ + Flush(); +} + +void CDVDDemuxVobsub::Flush() +{ + m_Demuxer->Flush(); +} + +bool CDVDDemuxVobsub::SeekTime(int time, bool backwords, double* startpts) +{ + double pts = DVD_MSEC_TO_TIME(time); + m_Timestamp = m_Timestamps.begin(); + for(;m_Timestamp != m_Timestamps.end();++m_Timestamp) + { + if(m_Timestamp->pts > pts) + break; + } + for(unsigned i=0;i::iterator current; + do { + if(m_Timestamp == m_Timestamps.end()) + return NULL; + + current = m_Timestamp++; + } while(m_Streams[current->id]->m_discard == AVDISCARD_ALL); + + if(!m_Demuxer->SeekByte(current->pos)) + return NULL; + + DemuxPacket *packet = m_Demuxer->Read(); + if(!packet) + return NULL; + + packet->iStreamId = current->id; + packet->pts = current->pts; + packet->dts = current->pts; + + return packet; +} + +bool CDVDDemuxVobsub::ParseLangIdx(SState& state, char* line) +{ + return true; +} + +bool CDVDDemuxVobsub::ParseDelay(SState& state, char* line) +{ + int h,m,s,ms; + bool negative = false; + + while(*line == ' ') line++; + if(*line == '-') + { + line++; + negative = true; + } + if(sscanf(line, "%d:%d:%d:%d", &h, &m, &s, &ms) != 4) + return false; + state.delay = h*3600.0 + m*60.0 + s + ms*0.001; + if(negative) + state.delay *= -1; + return true; +} + +bool CDVDDemuxVobsub::ParseId(SState& state, char* line) +{ + unique_ptr stream(new CStream(this)); + + while(*line == ' ') line++; + strncpy(stream->language, line, 2); + stream->language[2] = '\0'; + line+=2; + + while(*line == ' ' || *line == ',') line++; + if (strncmp("index:", line, 6) == 0) + { + line+=6; + while(*line == ' ') line++; + stream->iPhysicalId = atoi(line); + } + else + stream->iPhysicalId = -1; + + stream->codec = AV_CODEC_ID_DVD_SUBTITLE; + stream->iId = m_Streams.size(); + stream->source = STREAM_SOURCE_DEMUX_SUB; + + state.id = stream->iId; + m_Streams.push_back(stream.release()); + return true; +} + +bool CDVDDemuxVobsub::ParseExtra(SState& state, char* line) +{ + state.extra += line; + state.extra += '\n'; + return true; +} + +bool CDVDDemuxVobsub::ParseTimestamp(SState& state, char* line) +{ + if(state.id < 0) + return false; + + int h,m,s,ms; + STimestamp timestamp; + + while(*line == ' ') line++; + if(sscanf(line, "%d:%d:%d:%d, filepos:%" PRIx64, &h, &m, &s, &ms, ×tamp.pos) != 5) + return false; + + timestamp.id = state.id; + timestamp.pts = DVD_SEC_TO_TIME(state.delay + h*3600.0 + m*60.0 + s + ms*0.001); + m_Timestamps.push_back(timestamp); + return true; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h new file mode 100644 index 0000000..0c75c4a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h @@ -0,0 +1,99 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" + +#include +#include + +class CDVDOverlayCodecFFmpeg; +class CDVDInputStream; +class CDVDDemuxFFmpeg; + +class CDVDDemuxVobsub : public CDVDDemux +{ +public: + CDVDDemuxVobsub(); + virtual ~CDVDDemuxVobsub(); + + virtual bool Open(const std::string& filename, const std::string& subfilename = ""); + virtual void Reset(); + virtual void Abort() {}; + virtual void Flush(); + virtual DemuxPacket* Read(); + virtual bool SeekTime(int time, bool backwords, double* startpts = NULL); + virtual void SetSpeed(int speed) {} + virtual CDemuxStream* GetStream(int index) { return m_Streams[index]; } + virtual int GetNrOfStreams() { return m_Streams.size(); } + virtual int GetStreamLength() { return 0; } + virtual std::string GetFileName() { return m_Filename; } + +private: + class CStream + : public CDemuxStreamSubtitle + { + public: + CStream(CDVDDemuxVobsub* parent) + : m_discard(AVDISCARD_NONE), m_parent(parent) + {} + virtual void SetDiscard(AVDiscard discard) { m_discard = discard; } + virtual AVDiscard GetDiscard() { return m_discard; } + + AVDiscard m_discard; + CDVDDemuxVobsub* m_parent; + }; + + typedef struct STimestamp + { + int64_t pos; + double pts; + int id; + } STimestamp; + + std::string m_Filename; + std::unique_ptr m_Input; + std::unique_ptr m_Demuxer; + std::vector m_Timestamps; + std::vector::iterator m_Timestamp; + std::vector m_Streams; + + typedef struct SState + { + int id; + double delay; + std::string extra; + } SState; + + struct sorter + { + bool operator()(const STimestamp &p1, const STimestamp &p2) + { + return p1.pts < p2.pts || (p1.pts == p2.pts && p1.id < p2.id); + } + }; + + bool ParseLangIdx(SState& state, char* line); + bool ParseDelay(SState& state, char* line); + bool ParseId(SState& state, char* line); + bool ParseExtra(SState& state, char* line); + bool ParseTimestamp(SState& state, char* line); +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp new file mode 100644 index 0000000..f909c32 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "system.h" +#include "DVDFactoryDemuxer.h" + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDInputStreams/DVDInputStreamHttp.h" +#include "DVDInputStreams/DVDInputStreamPVRManager.h" + +#include "DVDDemuxFFmpeg.h" +#include "DVDDemuxShoutcast.h" +#ifdef HAS_FILESYSTEM_HTSP +#include "DVDDemuxHTSP.h" +#endif +#include "DVDDemuxBXA.h" +#include "DVDDemuxCDDA.h" +#include "DVDDemuxPVRClient.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" + +using namespace std; +using namespace PVR; + +CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream, bool fileinfo) +{ + if (!pInputStream) + return NULL; + + // Try to open the AirTunes demuxer + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FILE) && pInputStream->GetContent().compare("audio/x-xbmc-pcm") == 0 ) + { + // audio/x-xbmc-pcm this is the used codec for AirTunes + // (apples audio only streaming) + unique_ptr demuxer(new CDVDDemuxBXA()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + + // Try to open CDDA demuxer + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FILE) && pInputStream->GetContent().compare("application/octet-stream") == 0) + { + std::string filename = pInputStream->GetFileName(); + if (filename.substr(0, 7) == "cdda://") + { + CLog::Log(LOGDEBUG, "DVDFactoryDemuxer: Stream is probably CD audio. Creating CDDA demuxer."); + + unique_ptr demuxer(new CDVDDemuxCDDA()); + if (demuxer->Open(pInputStream)) + { + return demuxer.release(); + } + } + } + + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_HTTP)) + { + CDVDInputStreamHttp* pHttpStream = (CDVDInputStreamHttp*)pInputStream; + CHttpHeader *header = pHttpStream->GetHttpHeader(); + + /* check so we got the meta information as requested in our http header */ + if( header->GetValue("icy-metaint").length() > 0 ) + { + unique_ptr demuxer(new CDVDDemuxShoutcast()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + } + +#ifdef HAS_FILESYSTEM_HTSP + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)) + { + unique_ptr demuxer(new CDVDDemuxHTSP()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } +#endif + + bool streaminfo = true; /* Look for streams before playback */ + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + CDVDInputStreamPVRManager* pInputStreamPVR = (CDVDInputStreamPVRManager*)pInputStream; + CDVDInputStream* pOtherStream = pInputStreamPVR->GetOtherStream(); + + /* Don't parse the streaminfo for some cases of streams to reduce the channel switch time */ + bool useFastswitch = URIUtils::IsUsingFastSwitch(pInputStream->GetFileName()); + streaminfo = !useFastswitch; + + if(pOtherStream) + { + /* Used for MediaPortal PVR addon (uses PVR otherstream for playback of rtsp streams) */ + if (pOtherStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + unique_ptr demuxer(new CDVDDemuxFFmpeg()); + if(demuxer->Open(pOtherStream, streaminfo)) + return demuxer.release(); + else + return NULL; + } + } + + /* Use PVR demuxer only for live streams */ + if (URIUtils::IsPVRChannel(pInputStream->GetFileName())) + { + std::shared_ptr client; + if (g_PVRClients->GetPlayingClient(client) && + client->HandlesDemuxing()) + { + unique_ptr demuxer(new CDVDDemuxPVRClient()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + } + } + + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + bool useFastswitch = URIUtils::IsUsingFastSwitch(pInputStream->GetFileName()); + streaminfo = !useFastswitch; + } + + unique_ptr demuxer(new CDVDDemuxFFmpeg()); + if(demuxer->Open(pInputStream, streaminfo, fileinfo)) + return demuxer.release(); + else + return NULL; +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h new file mode 100644 index 0000000..8281d28 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h @@ -0,0 +1,30 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +class CDVDDemux; +class CDVDInputStream; + +class CDVDFactoryDemuxer +{ +public: + static CDVDDemux* CreateDemuxer(CDVDInputStream* pInputStream, bool fileinfo = false); +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in new file mode 100644 index 0000000..98493fe --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in @@ -0,0 +1,19 @@ +INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/dvdplayer + +SRCS = DVDDemux.cpp +SRCS += DVDDemuxBXA.cpp +SRCS += DVDDemuxCDDA.cpp +SRCS += DVDDemuxFFmpeg.cpp +SRCS += DVDDemuxHTSP.cpp +SRCS += DVDDemuxPVRClient.cpp +SRCS += DVDDemuxShoutcast.cpp +SRCS += DVDDemuxUtils.cpp +SRCS += DVDDemuxVobsub.cpp +SRCS += DVDDemuxCC.cpp +SRCS += DVDFactoryDemuxer.cpp + +LIB = DVDDemuxers.a + +include @abs_top_srcdir@/Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + -- cgit v1.2.3