Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ffmpeg demuxer: faster channel change for PVR addons ... #3590

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions lib/DllAvCodec.h
Expand Up @@ -56,7 +56,7 @@ class DllAvCodecInterface
virtual ~DllAvCodecInterface() {}
virtual void avcodec_register_all(void)=0;
virtual void avcodec_flush_buffers(AVCodecContext *avctx)=0;
virtual int avcodec_open2_dont_call(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)=0;
virtual int avcodec_open2_dont_call(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)=0;
virtual AVCodec *avcodec_find_decoder(enum AVCodecID id)=0;
virtual AVCodec *avcodec_find_encoder(enum AVCodecID id)=0;
virtual int avcodec_close_dont_call(AVCodecContext *avctx)=0;
Expand Down Expand Up @@ -109,12 +109,12 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
::avcodec_register_all();
}
virtual void avcodec_flush_buffers(AVCodecContext *avctx) { ::avcodec_flush_buffers(avctx); }
virtual int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)
virtual int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
CSingleLock lock(DllAvCodec::m_critSection);
return ::avcodec_open2(avctx, codec, options);
}
virtual int avcodec_open2_dont_call(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options) { *(volatile int *)0x0 = 0; return 0; }
virtual int avcodec_open2_dont_call(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options) { *(volatile int *)0x0 = 0; return 0; }
virtual int avcodec_close_dont_call(AVCodecContext *avctx) { *(volatile int *)0x0 = 0; return 0; }
virtual AVCodec *avcodec_find_decoder(enum AVCodecID id) { return ::avcodec_find_decoder(id); }
virtual AVCodec *avcodec_find_encoder(enum AVCodecID id) { return ::avcodec_find_encoder(id); }
Expand Down Expand Up @@ -178,7 +178,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
{
DECLARE_DLL_WRAPPER(DllAvCodec, DLL_PATH_LIBAVCODEC)
DEFINE_FUNC_ALIGNED1(void, __cdecl, avcodec_flush_buffers, AVCodecContext*)
DEFINE_FUNC_ALIGNED3(int, __cdecl, avcodec_open2_dont_call, AVCodecContext*, AVCodec *, AVDictionary **)
DEFINE_FUNC_ALIGNED3(int, __cdecl, avcodec_open2_dont_call, AVCodecContext*, const AVCodec *, AVDictionary **)
DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_video2, AVCodecContext*, AVFrame*, int*, AVPacket*)
DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_audio4, AVCodecContext*, AVFrame*, int*, AVPacket*)
DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_subtitle2, AVCodecContext*, AVSubtitle*, int*, AVPacket*)
Expand Down Expand Up @@ -255,7 +255,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface

public:
static CCriticalSection m_critSection;
int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
CSingleLock lock(DllAvCodec::m_critSection);
return avcodec_open2_dont_call(avctx,codec, options);
Expand Down
172 changes: 155 additions & 17 deletions xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
Expand Up @@ -51,6 +51,8 @@
#include "utils/StringUtils.h"
#include "URL.h"

#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE)

void CDemuxStreamAudioFFmpeg::GetStreamInfo(std::string& strInfo)
{
if(!m_stream) return;
Expand Down Expand Up @@ -217,6 +219,7 @@ CDVDDemuxFFmpeg::CDVDDemuxFFmpeg() : CDVDDemux()
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 */
}

CDVDDemuxFFmpeg::~CDVDDemuxFFmpeg()
Expand All @@ -237,10 +240,11 @@ bool CDVDDemuxFFmpeg::Aborted()
return false;
}

bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput)
bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput, bool streaminfo)
{
AVInputFormat* iformat = NULL;
std::string strFile;
m_streaminfo = streaminfo;
m_iCurrentPts = DVD_NOPTS_VALUE;
m_speed = DVD_PLAYSPEED_NORMAL;
m_program = UINT_MAX;
Expand All @@ -259,8 +263,6 @@ bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput)
m_pInput = pInput;
strFile = m_pInput->GetFileName();

bool streaminfo = true; /* set to true if we want to look for streams before playback*/

if( m_pInput->GetContent().length() > 0 )
{
std::string content = m_pInput->GetContent();
Expand Down Expand Up @@ -446,17 +448,23 @@ bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput)
if (iformat && (strcmp(iformat->name, "mjpeg") == 0) && m_ioContext->seekable == 0)
m_pFormatContext->max_analyze_duration = 500000;

bool short_analyze = false;
if (iformat && (strcmp(iformat->name, "mpegts") == 0))
{
m_pFormatContext->max_analyze_duration = 500000;
short_analyze = 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 (streaminfo)
if (m_streaminfo)
{
/* too speed up dvd switches, only analyse very short */
/* to speed up dvd switches, only analyse very short */
if(m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD))
m_pFormatContext->max_analyze_duration = 500000;


CLog::Log(LOGDEBUG, "%s - avformat_find_stream_info starting", __FUNCTION__);
int iErr = m_dllAvFormat.avformat_find_stream_info(m_pFormatContext, NULL);
if (iErr < 0)
Expand All @@ -475,7 +483,16 @@ bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput)
}
}
CLog::Log(LOGDEBUG, "%s - av_find_stream_info finished", __FUNCTION__);

if (short_analyze)
{
// make sure we start video with an i-frame
ResetVideoStreams();
}
}
else
m_program = 0;

// reset any timeout
m_timeout.SetInfinite();

Expand Down Expand Up @@ -530,7 +547,7 @@ void CDVDDemuxFFmpeg::Reset()
{
CDVDInputStream* pInputStream = m_pInput;
Dispose();
Open(pInputStream);
Open(pInputStream, m_streaminfo);
}

void CDVDDemuxFFmpeg::Flush()
Expand Down Expand Up @@ -719,25 +736,32 @@ DemuxPacket* CDVDDemuxFFmpeg::Read()
}
else
{
ParsePacket(&m_pkt.pkt);

AVStream *stream = m_pFormatContext->streams[m_pkt.pkt.stream_index];

if (m_program != UINT_MAX)
if (IsVideoReady())
{
/* check so packet belongs to selected program */
for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++)
if (m_program != UINT_MAX)
{
if(m_pkt.pkt.stream_index == (int)m_pFormatContext->programs[m_program]->stream_index[i])
/* check so packet belongs to selected program */
for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++)
{
pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size);
break;
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;
if (!pPacket)
bReturnEmpty = true;
}
else
pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size);
}
else
pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size);
bReturnEmpty = true;

if (pPacket)
{
Expand Down Expand Up @@ -1488,3 +1512,117 @@ bool CDVDDemuxFFmpeg::IsProgramChange()
}
return false;
}

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*)m_dllAvUtil.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 = m_dllAvCodec.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
m_dllAvUtil.av_dict_set(&thread_opt, "threads", "1", 0);
m_dllAvCodec.avcodec_open2(st->codec, codec, &thread_opt);

m_dllAvUtil.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;
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;
m_dllAvCodec.avcodec_decode_video2(st->codec, &picture, &got_picture, pkt);
}
}

bool CDVDDemuxFFmpeg::IsVideoReady()
{
AVStream *st;
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 &&
(!st->codec->width || st->codec->pix_fmt == PIX_FMT_NONE))
return false;
}
}
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 &&
(!st->codec->width || st->codec->pix_fmt == PIX_FMT_NONE))
return false;
}
}
return true;
}

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)
m_dllAvUtil.av_free(st->codec->extradata);
st->codec->extradata = NULL;
st->codec->width = 0;
}
}
}
7 changes: 6 additions & 1 deletion xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h
Expand Up @@ -89,7 +89,7 @@ class CDVDDemuxFFmpeg : public CDVDDemux
CDVDDemuxFFmpeg();
virtual ~CDVDDemuxFFmpeg();

bool Open(CDVDInputStream* pInput);
bool Open(CDVDInputStream* pInput, bool streaminfo = true);
void Dispose();
void Reset();
void Flush();
Expand Down Expand Up @@ -127,6 +127,9 @@ class CDVDDemuxFFmpeg : public CDVDDemux
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);
Expand Down Expand Up @@ -158,5 +161,7 @@ class CDVDDemuxFFmpeg : public CDVDDemux
AVPacket pkt; // packet ffmpeg returned
int result; // result from av_read_packet
}m_pkt;

bool m_streaminfo;
};

19 changes: 15 additions & 4 deletions xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp
Expand Up @@ -99,26 +99,31 @@ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream)
}
#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))
{
auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg());
if(demuxer->Open(pOtherStream))
if(demuxer->Open(pOtherStream, streaminfo))
return demuxer.release();
else
return NULL;
}
}

std::string filename = pInputStream->GetFileName();
/* Use PVR demuxer only for live streams */
if (filename.substr(0, 14) == "pvr://channels")
if (URIUtils::IsPVRChannel(pInputStream->GetFileName()))
{
boost::shared_ptr<CPVRClient> client;
if (g_PVRClients->GetPlayingClient(client) &&
Expand All @@ -133,8 +138,14 @@ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream)
}
}

if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG))
{
bool useFastswitch = URIUtils::IsUsingFastSwitch(pInputStream->GetFileName());
streaminfo = !useFastswitch;
}

auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg());
if(demuxer->Open(pInputStream))
if(demuxer->Open(pInputStream, streaminfo))
return demuxer.release();
else
return NULL;
Expand Down