From 9d11b08ad61b1f0d6d7023ce403285d8662efaed Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 4 Mar 2015 00:23:39 +0100 Subject: sync with upstream --- .../cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp | 1767 -------------------- 1 file changed, 1767 deletions(-) delete mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp (limited to 'xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp') diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp deleted file mode 100644 index f58472f..0000000 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp +++ /dev/null @@ -1,1767 +0,0 @@ -/* - * 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 - } - } - } -} -- cgit v1.2.3