Permalink
Cannot retrieve contributors at this time
| /* | |
| * Copyright (C) 2005-2008 Team XBMC | |
| * http://www.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, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * http://www.gnu.org/copyleft/gpl.html | |
| * | |
| */ | |
| #if (defined HAVE_CONFIG_H) && (!defined WIN32) | |
| #include "config.h" | |
| #elif defined(_WIN32) | |
| #include "system.h" | |
| #endif | |
| #include "OMXReader.h" | |
| #include "OMXClock.h" | |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include "linux/XMemUtils.h" | |
| #define MAX_DATA_SIZE_VIDEO 8 * 1024 * 1024 | |
| #define MAX_DATA_SIZE_AUDIO 2 * 1024 * 1024 | |
| #define MAX_DATA_SIZE 10 * 1024 * 1024 | |
| static bool g_abort = false; | |
| static int64_t timeout_start; | |
| static int64_t timeout_default_duration; | |
| static int64_t timeout_duration; | |
| static int64_t CurrentHostCounter(void) | |
| { | |
| struct timespec now; | |
| clock_gettime(CLOCK_MONOTONIC, &now); | |
| return( ((int64_t)now.tv_sec * 1000000000LL) + now.tv_nsec ); | |
| } | |
| #define RESET_TIMEOUT(x) do { \ | |
| timeout_start = CurrentHostCounter(); \ | |
| timeout_duration = (x) * timeout_default_duration; \ | |
| } while (0) | |
| OMXReader::OMXReader() | |
| { | |
| m_open = false; | |
| m_filename = ""; | |
| m_bMatroska = false; | |
| m_bAVI = false; | |
| g_abort = false; | |
| m_pFile = NULL; | |
| m_ioContext = NULL; | |
| m_pFormatContext = NULL; | |
| m_eof = false; | |
| m_chapter_count = 0; | |
| m_iCurrentPts = DVD_NOPTS_VALUE; | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| m_streams[i].extradata = NULL; | |
| ClearStreams(); | |
| pthread_mutex_init(&m_lock, NULL); | |
| } | |
| OMXReader::~OMXReader() | |
| { | |
| Close(); | |
| pthread_mutex_destroy(&m_lock); | |
| } | |
| void OMXReader::Lock() | |
| { | |
| pthread_mutex_lock(&m_lock); | |
| } | |
| void OMXReader::UnLock() | |
| { | |
| pthread_mutex_unlock(&m_lock); | |
| } | |
| static int interrupt_cb(void *unused) | |
| { | |
| int ret = 0; | |
| if (g_abort) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::interrupt_cb - Told to abort"); | |
| ret = 1; | |
| } | |
| else if (timeout_duration && CurrentHostCounter() - timeout_start > timeout_duration) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::interrupt_cb - Timed out"); | |
| ret = 1; | |
| } | |
| return ret; | |
| } | |
| static int dvd_file_read(void *h, uint8_t* buf, int size) | |
| { | |
| RESET_TIMEOUT(1); | |
| if(interrupt_cb(NULL)) | |
| return -1; | |
| XFILE::CFile *pFile = (XFILE::CFile *)h; | |
| int ret = pFile->Read(buf, size); | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58,12,100) | |
| if (ret == 0 && pFile->IsEOF()) | |
| ret = AVERROR_EOF; | |
| #endif | |
| return ret; | |
| } | |
| static offset_t dvd_file_seek(void *h, offset_t pos, int whence) | |
| { | |
| RESET_TIMEOUT(1); | |
| if(interrupt_cb(NULL)) | |
| return -1; | |
| XFILE::CFile *pFile = (XFILE::CFile *)h; | |
| if(whence == AVSEEK_SIZE) | |
| return pFile->GetLength(); | |
| else | |
| return pFile->Seek(pos, whence & ~AVSEEK_FORCE); | |
| } | |
| bool OMXReader::Open(std::string filename, bool dump_format, bool live /* =false */, float timeout /* = 0.0f */, std::string cookie /* = "" */, std::string user_agent /* = "" */, std::string lavfdopts /* = "" */, std::string avdict /* = "" */) | |
| { | |
| if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load()) | |
| return false; | |
| timeout_default_duration = (int64_t) (timeout * 1e9); | |
| m_iCurrentPts = DVD_NOPTS_VALUE; | |
| m_filename = filename; | |
| m_speed = DVD_PLAYSPEED_NORMAL; | |
| m_program = UINT_MAX; | |
| const AVIOInterruptCB int_cb = { interrupt_cb, NULL }; | |
| RESET_TIMEOUT(3); | |
| ClearStreams(); | |
| m_dllAvFormat.av_register_all(); | |
| m_dllAvFormat.avformat_network_init(); | |
| m_dllAvUtil.av_log_set_level(dump_format ? AV_LOG_INFO:AV_LOG_QUIET); | |
| int result = -1; | |
| AVInputFormat *iformat = NULL; | |
| unsigned char *buffer = NULL; | |
| unsigned int flags = READ_TRUNCATED | READ_BITRATE | READ_CHUNKED; | |
| m_pFormatContext = m_dllAvFormat.avformat_alloc_context(); | |
| result = m_dllAvFormat.av_set_options_string(m_pFormatContext, lavfdopts.c_str(), ":", ","); | |
| if (result < 0) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::OpenFile - invalid lavfdopts %s ", lavfdopts.c_str()); | |
| Close(); | |
| return false; | |
| } | |
| AVDictionary *d = NULL; | |
| result = m_dllAvUtil.av_dict_parse_string(&d, avdict.c_str(), ":", ",", 0); | |
| if (result < 0) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::OpenFile - invalid avdict %s ", avdict.c_str()); | |
| Close(); | |
| return false; | |
| } | |
| // set the interrupt callback, appeared in libavformat 53.15.0 | |
| m_pFormatContext->interrupt_callback = int_cb; | |
| // if format can be nonblocking, let's use that | |
| m_pFormatContext->flags |= AVFMT_FLAG_NONBLOCK; | |
| // strip off file:// | |
| if(m_filename.substr(0, 7) == "file://" ) | |
| m_filename.replace(0, 7, ""); | |
| if(m_filename.substr(0, 8) == "shout://" ) | |
| m_filename.replace(0, 8, "http://"); | |
| if(m_filename.substr(0,6) == "mms://" || m_filename.substr(0,7) == "mmsh://" || m_filename.substr(0,7) == "mmst://" || m_filename.substr(0,7) == "mmsu://" || | |
| m_filename.substr(0,7) == "http://" || m_filename.substr(0,8) == "https://" || | |
| m_filename.substr(0,7) == "rtmp://" || m_filename.substr(0,6) == "udp://" || | |
| m_filename.substr(0,7) == "rtsp://" || m_filename.substr(0,6) == "rtp://" || | |
| m_filename.substr(0,6) == "ftp://" || m_filename.substr(0,7) == "sftp://" || | |
| m_filename.substr(0,6) == "tcp://" || m_filename.substr(0,7) == "unix://" || | |
| m_filename.substr(0,6) == "smb://") | |
| { | |
| // ffmpeg dislikes the useragent from AirPlay urls | |
| //int idx = m_filename.Find("|User-Agent=AppleCoreMedia"); | |
| size_t idx = m_filename.find("|"); | |
| if(idx != string::npos) | |
| m_filename = m_filename.substr(0, idx); | |
| // Enable seeking if http, ftp | |
| if(m_filename.substr(0,7) == "http://" || m_filename.substr(0,6) == "ftp://" || | |
| m_filename.substr(0,7) == "sftp://" || m_filename.substr(0,6) == "smb://") | |
| { | |
| if(!live) | |
| { | |
| av_dict_set(&d, "seekable", "1", 0); | |
| } | |
| if(!cookie.empty()) | |
| { | |
| av_dict_set(&d, "cookies", cookie.c_str(), 0); | |
| } | |
| if(!user_agent.empty()) | |
| { | |
| av_dict_set(&d, "user_agent", user_agent.c_str(), 0); | |
| } | |
| } | |
| CLog::Log(LOGDEBUG, "COMXPlayer::OpenFile - avformat_open_input %s ", m_filename.c_str()); | |
| result = m_dllAvFormat.avformat_open_input(&m_pFormatContext, m_filename.c_str(), iformat, &d); | |
| if(av_dict_count(d) == 0) | |
| { | |
| CLog::Log(LOGDEBUG, "COMXPlayer::OpenFile - avformat_open_input enabled SEEKING "); | |
| if(m_filename.substr(0,7) == "http://") | |
| m_pFormatContext->pb->seekable = AVIO_SEEKABLE_NORMAL; | |
| } | |
| av_dict_free(&d); | |
| if(result < 0) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::OpenFile - avformat_open_input %s ", m_filename.c_str()); | |
| Close(); | |
| return false; | |
| } | |
| } | |
| else | |
| { | |
| m_pFile = new CFile(); | |
| if (!m_pFile->Open(m_filename, flags)) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::OpenFile - %s ", m_filename.c_str()); | |
| Close(); | |
| return false; | |
| } | |
| buffer = (unsigned char*)m_dllAvUtil.av_malloc(FFMPEG_FILE_BUFFER_SIZE); | |
| m_ioContext = m_dllAvFormat.avio_alloc_context(buffer, FFMPEG_FILE_BUFFER_SIZE, 0, m_pFile, dvd_file_read, NULL, dvd_file_seek); | |
| m_ioContext->max_packet_size = 6144; | |
| if(m_ioContext->max_packet_size) | |
| m_ioContext->max_packet_size *= FFMPEG_FILE_BUFFER_SIZE / m_ioContext->max_packet_size; | |
| if(m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL) == 0) | |
| m_ioContext->seekable = 0; | |
| m_dllAvFormat.av_probe_input_buffer(m_ioContext, &iformat, m_filename.c_str(), NULL, 0, 0); | |
| if(!iformat) | |
| { | |
| CLog::Log(LOGERROR, "COMXPlayer::OpenFile - av_probe_input_buffer %s ", m_filename.c_str()); | |
| Close(); | |
| return false; | |
| } | |
| m_pFormatContext->pb = m_ioContext; | |
| result = m_dllAvFormat.avformat_open_input(&m_pFormatContext, m_filename.c_str(), iformat, &d); | |
| av_dict_free(&d); | |
| if(result < 0) | |
| { | |
| Close(); | |
| return false; | |
| } | |
| } | |
| m_bMatroska = strncmp(m_pFormatContext->iformat->name, "matroska", 8) == 0; // for "matroska.webm" | |
| m_bAVI = strcmp(m_pFormatContext->iformat->name, "avi") == 0; | |
| // analyse very short to speed up mjpeg playback start | |
| if (iformat && (strcmp(iformat->name, "mjpeg") == 0) && m_ioContext->seekable == 0) | |
| m_pFormatContext->max_analyze_duration = 500000; | |
| if(/*m_bAVI || */m_bMatroska) | |
| m_pFormatContext->max_analyze_duration = 0; | |
| if (live) | |
| m_pFormatContext->flags |= AVFMT_FLAG_NOBUFFER; | |
| result = m_dllAvFormat.avformat_find_stream_info(m_pFormatContext, NULL); | |
| if(result < 0) | |
| { | |
| Close(); | |
| return false; | |
| } | |
| if(!GetStreams()) | |
| { | |
| Close(); | |
| return false; | |
| } | |
| if(m_pFile) | |
| { | |
| int64_t len = m_pFile->GetLength(); | |
| int64_t tim = GetStreamLength(); | |
| if(len > 0 && tim > 0) | |
| { | |
| unsigned rate = len * 1000 / tim; | |
| unsigned maxrate = rate + 1024 * 1024 / 8; | |
| if(m_pFile->IoControl(IOCTRL_CACHE_SETRATE, &maxrate) >= 0) | |
| CLog::Log(LOGDEBUG, "COMXPlayer::OpenFile - set cache throttle rate to %u bytes per second", maxrate); | |
| } | |
| } | |
| m_speed = DVD_PLAYSPEED_NORMAL; | |
| if(dump_format) | |
| m_dllAvFormat.av_dump_format(m_pFormatContext, 0, m_filename.c_str(), 0); | |
| UpdateCurrentPTS(); | |
| m_open = true; | |
| return true; | |
| } | |
| void OMXReader::ClearStreams() | |
| { | |
| m_audio_index = -1; | |
| m_video_index = -1; | |
| m_subtitle_index = -1; | |
| m_audio_count = 0; | |
| m_video_count = 0; | |
| m_subtitle_count = 0; | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].extradata) | |
| free(m_streams[i].extradata); | |
| memset(m_streams[i].language, 0, sizeof(m_streams[i].language)); | |
| m_streams[i].codec_name = ""; | |
| m_streams[i].name = ""; | |
| m_streams[i].type = OMXSTREAM_NONE; | |
| m_streams[i].stream = NULL; | |
| m_streams[i].extradata = NULL; | |
| m_streams[i].extrasize = 0; | |
| m_streams[i].index = 0; | |
| m_streams[i].id = 0; | |
| } | |
| m_program = UINT_MAX; | |
| } | |
| bool OMXReader::Close() | |
| { | |
| 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; | |
| } | |
| m_dllAvFormat.avformat_close_input(&m_pFormatContext); | |
| } | |
| if(m_ioContext) | |
| { | |
| m_dllAvUtil.av_free(m_ioContext->buffer); | |
| m_dllAvUtil.av_free(m_ioContext); | |
| } | |
| m_ioContext = NULL; | |
| m_pFormatContext = NULL; | |
| if(m_pFile) | |
| { | |
| m_pFile->Close(); | |
| delete m_pFile; | |
| m_pFile = NULL; | |
| } | |
| m_dllAvFormat.avformat_network_deinit(); | |
| m_dllAvUtil.Unload(); | |
| m_dllAvCodec.Unload(); | |
| m_dllAvFormat.Unload(); | |
| m_open = false; | |
| m_filename = ""; | |
| m_bMatroska = false; | |
| m_bAVI = false; | |
| m_video_count = 0; | |
| m_audio_count = 0; | |
| m_subtitle_count = 0; | |
| m_audio_index = -1; | |
| m_video_index = -1; | |
| m_subtitle_index = -1; | |
| m_eof = false; | |
| m_chapter_count = 0; | |
| m_iCurrentPts = DVD_NOPTS_VALUE; | |
| m_speed = DVD_PLAYSPEED_NORMAL; | |
| ClearStreams(); | |
| return true; | |
| } | |
| /*void OMXReader::FlushRead() | |
| { | |
| m_iCurrentPts = DVD_NOPTS_VALUE; | |
| if(!m_pFormatContext) | |
| return; | |
| ff_read_frame_flush(m_pFormatContext); | |
| }*/ | |
| bool OMXReader::SeekTime(int time, bool backwords, double *startpts) | |
| { | |
| if(time < 0) | |
| time = 0; | |
| if(!m_pFormatContext) | |
| return false; | |
| if(m_pFile && !m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL)) | |
| { | |
| CLog::Log(LOGDEBUG, "%s - input stream reports it is not seekable", __FUNCTION__); | |
| return false; | |
| } | |
| Lock(); | |
| //FlushRead(); | |
| if(m_ioContext) | |
| m_ioContext->buf_ptr = m_ioContext->buf_end; | |
| int64_t seek_pts = (int64_t)time * (AV_TIME_BASE / 1000); | |
| if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) | |
| seek_pts += m_pFormatContext->start_time; | |
| RESET_TIMEOUT(1); | |
| int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); | |
| if(ret >= 0) | |
| UpdateCurrentPTS(); | |
| // in this case the start time is requested time | |
| if(startpts) | |
| *startpts = DVD_MSEC_TO_TIME(time); | |
| // demuxer will return failure, if you seek to eof | |
| m_eof = false; | |
| if (ret < 0) | |
| { | |
| m_eof = true; | |
| ret = 0; | |
| } | |
| CLog::Log(LOGDEBUG, "OMXReader::SeekTime(%d) - seek ended up on time %d",time,(int)(m_iCurrentPts / DVD_TIME_BASE * 1000)); | |
| UnLock(); | |
| return (ret >= 0); | |
| } | |
| AVMediaType OMXReader::PacketType(OMXPacket *pkt) | |
| { | |
| if(!m_pFormatContext || !pkt) | |
| return AVMEDIA_TYPE_UNKNOWN; | |
| return m_pFormatContext->streams[pkt->stream_index]->codec->codec_type; | |
| } | |
| OMXPacket *OMXReader::Read() | |
| { | |
| AVPacket pkt; | |
| OMXPacket *m_omx_pkt = NULL; | |
| int result = -1; | |
| if(!m_pFormatContext || m_eof) | |
| return NULL; | |
| Lock(); | |
| // assume we are not eof | |
| if(m_pFormatContext->pb) | |
| m_pFormatContext->pb->eof_reached = 0; | |
| // keep track if ffmpeg doesn't always set these | |
| pkt.size = 0; | |
| pkt.data = NULL; | |
| pkt.stream_index = MAX_OMX_STREAMS; | |
| RESET_TIMEOUT(1); | |
| result = m_dllAvFormat.av_read_frame(m_pFormatContext, &pkt); | |
| if (result < 0) | |
| { | |
| m_eof = true; | |
| //FlushRead(); | |
| //m_dllAvCodec.av_free_packet(&pkt); | |
| UnLock(); | |
| return NULL; | |
| } | |
| if (pkt.size < 0 || pkt.stream_index >= MAX_OMX_STREAMS || interrupt_cb(NULL)) | |
| { | |
| // XXX, in some cases ffmpeg returns a negative packet size | |
| if(m_pFormatContext->pb && !m_pFormatContext->pb->eof_reached) | |
| { | |
| CLog::Log(LOGERROR, "OMXReader::Read no valid packet"); | |
| //FlushRead(); | |
| } | |
| m_dllAvCodec.av_free_packet(&pkt); | |
| m_eof = true; | |
| UnLock(); | |
| return NULL; | |
| } | |
| AVStream *pStream = m_pFormatContext->streams[pkt.stream_index]; | |
| /* only read packets for active streams */ | |
| /* | |
| if(!IsActive(pkt.stream_index)) | |
| { | |
| m_dllAvCodec.av_free_packet(&pkt); | |
| UnLock(); | |
| return NULL; | |
| } | |
| */ | |
| // 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 0 | |
| if(pkt.dts == 0) | |
| pkt.dts = AV_NOPTS_VALUE; | |
| if(pkt.pts == 0) | |
| pkt.pts = AV_NOPTS_VALUE; | |
| #endif | |
| if(m_bMatroska && pStream->codec && pStream->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(pStream->codec->codec_tag == 0) | |
| pkt.dts = AV_NOPTS_VALUE; | |
| else | |
| pkt.pts = AV_NOPTS_VALUE; | |
| } | |
| // we need to get duration slightly different for matroska embedded text subtitels | |
| if(m_bMatroska && pStream->codec->codec_id == AV_CODEC_ID_SUBRIP && pkt.convergence_duration != 0) | |
| pkt.duration = pkt.convergence_duration; | |
| if(m_bAVI && pStream->codec && pStream->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 | |
| pkt.pts = AV_NOPTS_VALUE; | |
| } | |
| m_omx_pkt = AllocPacket(pkt.size); | |
| /* oom error allocation av packet */ | |
| if(!m_omx_pkt) | |
| { | |
| m_eof = true; | |
| m_dllAvCodec.av_free_packet(&pkt); | |
| UnLock(); | |
| return NULL; | |
| } | |
| m_omx_pkt->codec_type = pStream->codec->codec_type; | |
| /* copy content into our own packet */ | |
| m_omx_pkt->size = pkt.size; | |
| if (pkt.data) | |
| memcpy(m_omx_pkt->data, pkt.data, m_omx_pkt->size); | |
| m_omx_pkt->stream_index = pkt.stream_index; | |
| GetHints(pStream, &m_omx_pkt->hints); | |
| m_omx_pkt->dts = ConvertTimestamp(pkt.dts, pStream->time_base.den, pStream->time_base.num); | |
| m_omx_pkt->pts = ConvertTimestamp(pkt.pts, pStream->time_base.den, pStream->time_base.num); | |
| m_omx_pkt->duration = DVD_SEC_TO_TIME((double)pkt.duration * pStream->time_base.num / pStream->time_base.den); | |
| // used to guess streamlength | |
| if (m_omx_pkt->dts != DVD_NOPTS_VALUE && (m_omx_pkt->dts > m_iCurrentPts || m_iCurrentPts == DVD_NOPTS_VALUE)) | |
| m_iCurrentPts = m_omx_pkt->dts; | |
| // check if stream has passed full duration, needed for live streams | |
| if(pkt.dts != (int64_t)AV_NOPTS_VALUE) | |
| { | |
| int64_t duration; | |
| duration = pkt.dts; | |
| if(pStream->start_time != (int64_t)AV_NOPTS_VALUE) | |
| duration -= pStream->start_time; | |
| if(duration > pStream->duration) | |
| { | |
| pStream->duration = duration; | |
| duration = m_dllAvUtil.av_rescale_rnd(pStream->duration, (int64_t)pStream->time_base.num * AV_TIME_BASE, | |
| pStream->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; | |
| } | |
| } | |
| m_dllAvCodec.av_free_packet(&pkt); | |
| UnLock(); | |
| return m_omx_pkt; | |
| } | |
| bool OMXReader::GetStreams() | |
| { | |
| if(!m_pFormatContext) | |
| return false; | |
| unsigned int m_program = UINT_MAX; | |
| ClearStreams(); | |
| if (m_pFormatContext->nb_programs) | |
| { | |
| // 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]); | |
| } | |
| } | |
| // 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); | |
| } | |
| if(m_video_count) | |
| SetActiveStreamInternal(OMXSTREAM_VIDEO, 0); | |
| if(m_audio_count) | |
| SetActiveStreamInternal(OMXSTREAM_AUDIO, 0); | |
| if(m_subtitle_count) | |
| SetActiveStreamInternal(OMXSTREAM_SUBTITLE, 0); | |
| int i = 0; | |
| for(i = 0; i < MAX_OMX_CHAPTERS; i++) | |
| { | |
| m_chapters[i].name = ""; | |
| m_chapters[i].seekto_ms = 0; | |
| m_chapters[i].ts = 0; | |
| } | |
| m_chapter_count = 0; | |
| if(m_video_index != -1) | |
| { | |
| //m_current_chapter = 0; | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,14,0) | |
| m_chapter_count = (m_pFormatContext->nb_chapters > MAX_OMX_CHAPTERS) ? MAX_OMX_CHAPTERS : m_pFormatContext->nb_chapters; | |
| for(i = 0; i < m_chapter_count; i++) | |
| { | |
| if(i > MAX_OMX_CHAPTERS) | |
| break; | |
| AVChapter *chapter = m_pFormatContext->chapters[i]; | |
| if(!chapter) | |
| continue; | |
| m_chapters[i].seekto_ms = ConvertTimestamp(chapter->start, chapter->time_base.den, chapter->time_base.num) / 1000; | |
| m_chapters[i].ts = m_chapters[i].seekto_ms / 1000; | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,83,0) | |
| AVDictionaryEntry *titleTag = m_dllAvUtil.av_dict_get(m_pFormatContext->chapters[i]->metadata,"title", NULL, 0); | |
| if (titleTag) | |
| m_chapters[i].name = titleTag->value; | |
| #else | |
| if(m_pFormatContext->chapters[i]->title) | |
| m_chapters[i].name = m_pFormatContext->chapters[i]->title; | |
| #endif | |
| printf("Chapter : \t%d \t%s \t%8.2f\n", i, m_chapters[i].name.c_str(), m_chapters[i].ts); | |
| } | |
| } | |
| #endif | |
| return true; | |
| } | |
| void OMXReader::AddStream(int id) | |
| { | |
| if(id > MAX_STREAMS || !m_pFormatContext) | |
| return; | |
| AVStream *pStream = m_pFormatContext->streams[id]; | |
| // discard if it's a picture attachment (e.g. album art embedded in MP3 or AAC) | |
| if(pStream->codec->codec_type == AVMEDIA_TYPE_VIDEO && | |
| (pStream->disposition & AV_DISPOSITION_ATTACHED_PIC)) | |
| return; | |
| switch (pStream->codec->codec_type) | |
| { | |
| case AVMEDIA_TYPE_AUDIO: | |
| m_streams[id].stream = pStream; | |
| m_streams[id].type = OMXSTREAM_AUDIO; | |
| m_streams[id].index = m_audio_count; | |
| m_streams[id].codec_name = GetStreamCodecName(pStream); | |
| m_streams[id].id = id; | |
| m_audio_count++; | |
| GetHints(pStream, &m_streams[id].hints); | |
| break; | |
| case AVMEDIA_TYPE_VIDEO: | |
| m_streams[id].stream = pStream; | |
| m_streams[id].type = OMXSTREAM_VIDEO; | |
| m_streams[id].index = m_video_count; | |
| m_streams[id].codec_name = GetStreamCodecName(pStream); | |
| m_streams[id].id = id; | |
| m_video_count++; | |
| GetHints(pStream, &m_streams[id].hints); | |
| break; | |
| case AVMEDIA_TYPE_SUBTITLE: | |
| m_streams[id].stream = pStream; | |
| m_streams[id].type = OMXSTREAM_SUBTITLE; | |
| m_streams[id].index = m_subtitle_count; | |
| m_streams[id].codec_name = GetStreamCodecName(pStream); | |
| m_streams[id].id = id; | |
| m_subtitle_count++; | |
| GetHints(pStream, &m_streams[id].hints); | |
| break; | |
| default: | |
| return; | |
| } | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,83,0) | |
| AVDictionaryEntry *langTag = m_dllAvUtil.av_dict_get(pStream->metadata, "language", NULL, 0); | |
| if (langTag) | |
| strncpy(m_streams[id].language, langTag->value, 3); | |
| #else | |
| strcpy( m_streams[id].language, pStream->language ); | |
| #endif | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,83,0) | |
| AVDictionaryEntry *titleTag = m_dllAvUtil.av_dict_get(pStream->metadata,"title", NULL, 0); | |
| if (titleTag) | |
| m_streams[id].name = titleTag->value; | |
| #else | |
| m_streams[id].name = pStream->title; | |
| #endif | |
| if( pStream->codec->extradata && pStream->codec->extradata_size > 0 ) | |
| { | |
| m_streams[id].extrasize = pStream->codec->extradata_size; | |
| m_streams[id].extradata = malloc(pStream->codec->extradata_size); | |
| memcpy(m_streams[id].extradata, pStream->codec->extradata, pStream->codec->extradata_size); | |
| } | |
| } | |
| bool OMXReader::SetActiveStreamInternal(OMXStreamType type, unsigned int index) | |
| { | |
| bool ret = false; | |
| switch(type) | |
| { | |
| case OMXSTREAM_AUDIO: | |
| if((int)index > (m_audio_count - 1)) | |
| index = (m_audio_count - 1); | |
| break; | |
| case OMXSTREAM_VIDEO: | |
| if((int)index > (m_video_count - 1)) | |
| index = (m_video_count - 1); | |
| break; | |
| case OMXSTREAM_SUBTITLE: | |
| if((int)index > (m_subtitle_count - 1)) | |
| index = (m_subtitle_count - 1); | |
| break; | |
| default: | |
| break; | |
| } | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].type == type && m_streams[i].index == index) | |
| { | |
| switch(m_streams[i].type) | |
| { | |
| case OMXSTREAM_AUDIO: | |
| m_audio_index = i; | |
| ret = true; | |
| break; | |
| case OMXSTREAM_VIDEO: | |
| m_video_index = i; | |
| ret = true; | |
| break; | |
| case OMXSTREAM_SUBTITLE: | |
| m_subtitle_index = i; | |
| ret = true; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| if(!ret) | |
| { | |
| switch(type) | |
| { | |
| case OMXSTREAM_AUDIO: | |
| m_audio_index = -1; | |
| break; | |
| case OMXSTREAM_VIDEO: | |
| m_video_index = -1; | |
| break; | |
| case OMXSTREAM_SUBTITLE: | |
| m_subtitle_index = -1; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| bool OMXReader::IsActive(int stream_index) | |
| { | |
| if((m_audio_index != -1) && m_streams[m_audio_index].id == stream_index) | |
| return true; | |
| if((m_video_index != -1) && m_streams[m_video_index].id == stream_index) | |
| return true; | |
| if((m_subtitle_index != -1) && m_streams[m_subtitle_index].id == stream_index) | |
| return true; | |
| return false; | |
| } | |
| bool OMXReader::IsActive(OMXStreamType type, int stream_index) | |
| { | |
| if((m_audio_index != -1) && m_streams[m_audio_index].id == stream_index && m_streams[m_audio_index].type == type) | |
| return true; | |
| if((m_video_index != -1) && m_streams[m_video_index].id == stream_index && m_streams[m_video_index].type == type) | |
| return true; | |
| if((m_subtitle_index != -1) && m_streams[m_subtitle_index].id == stream_index && m_streams[m_subtitle_index].type == type) | |
| return true; | |
| return false; | |
| } | |
| double OMXReader::SelectAspect(AVStream* st, bool& forced) | |
| { | |
| // trust matroshka container | |
| if (m_bMatroska && st->sample_aspect_ratio.num != 0) | |
| { | |
| forced = true; | |
| return av_q2d(st->sample_aspect_ratio); | |
| } | |
| 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; | |
| } | |
| bool OMXReader::GetHints(AVStream *stream, COMXStreamInfo *hints) | |
| { | |
| if(!hints || !stream) | |
| return false; | |
| hints->codec = stream->codec->codec_id; | |
| hints->extradata = stream->codec->extradata; | |
| hints->extrasize = stream->codec->extradata_size; | |
| hints->channels = stream->codec->channels; | |
| hints->samplerate = stream->codec->sample_rate; | |
| hints->blockalign = stream->codec->block_align; | |
| hints->bitrate = stream->codec->bit_rate; | |
| hints->bitspersample = stream->codec->bits_per_coded_sample; | |
| if(hints->bitspersample == 0) | |
| hints->bitspersample = 16; | |
| hints->width = stream->codec->width; | |
| hints->height = stream->codec->height; | |
| hints->profile = stream->codec->profile; | |
| hints->orientation = 0; | |
| if(stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) | |
| { | |
| hints->fpsrate = stream->r_frame_rate.num; | |
| hints->fpsscale = stream->r_frame_rate.den; | |
| if(m_bMatroska && stream->avg_frame_rate.den && stream->avg_frame_rate.num) | |
| { | |
| hints->fpsrate = stream->avg_frame_rate.num; | |
| hints->fpsscale = stream->avg_frame_rate.den; | |
| } | |
| else if(stream->r_frame_rate.num && stream->r_frame_rate.den) | |
| { | |
| hints->fpsrate = stream->r_frame_rate.num; | |
| hints->fpsscale = stream->r_frame_rate.den; | |
| } | |
| else | |
| { | |
| hints->fpsscale = 0; | |
| hints->fpsrate = 0; | |
| } | |
| hints->aspect = SelectAspect(stream, hints->forced_aspect) * stream->codec->width / stream->codec->height; | |
| if (m_bAVI && stream->codec->codec_id == AV_CODEC_ID_H264) | |
| hints->ptsinvalid = true; | |
| AVDictionaryEntry *rtag = m_dllAvUtil.av_dict_get(stream->metadata, "rotate", NULL, 0); | |
| if (rtag) | |
| hints->orientation = atoi(rtag->value); | |
| m_aspect = hints->aspect; | |
| m_width = hints->width; | |
| m_height = hints->height; | |
| } | |
| return true; | |
| } | |
| bool OMXReader::GetHints(OMXStreamType type, unsigned int index, COMXStreamInfo &hints) | |
| { | |
| for(unsigned int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].type == type && m_streams[i].index == i) | |
| { | |
| hints = m_streams[i].hints; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool OMXReader::GetHints(OMXStreamType type, COMXStreamInfo &hints) | |
| { | |
| bool ret = false; | |
| switch (type) | |
| { | |
| case OMXSTREAM_AUDIO: | |
| if(m_audio_index != -1) | |
| { | |
| ret = true; | |
| hints = m_streams[m_audio_index].hints; | |
| } | |
| break; | |
| case OMXSTREAM_VIDEO: | |
| if(m_video_index != -1) | |
| { | |
| ret = true; | |
| hints = m_streams[m_video_index].hints; | |
| } | |
| break; | |
| case OMXSTREAM_SUBTITLE: | |
| if(m_subtitle_index != -1) | |
| { | |
| ret = true; | |
| hints = m_streams[m_subtitle_index].hints; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return ret; | |
| } | |
| bool OMXReader::IsEof() | |
| { | |
| return m_eof; | |
| } | |
| void OMXReader::FreePacket(OMXPacket *pkt) | |
| { | |
| if(pkt) | |
| { | |
| if(pkt->data) | |
| free(pkt->data); | |
| free(pkt); | |
| } | |
| } | |
| OMXPacket *OMXReader::AllocPacket(int size) | |
| { | |
| OMXPacket *pkt = (OMXPacket *)malloc(sizeof(OMXPacket)); | |
| if(pkt) | |
| { | |
| memset(pkt, 0, sizeof(OMXPacket)); | |
| pkt->data = (uint8_t*) malloc(size + AV_INPUT_BUFFER_PADDING_SIZE); | |
| if(!pkt->data) | |
| { | |
| free(pkt); | |
| pkt = NULL; | |
| } | |
| else | |
| { | |
| memset(pkt->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); | |
| pkt->size = size; | |
| pkt->dts = DVD_NOPTS_VALUE; | |
| pkt->pts = DVD_NOPTS_VALUE; | |
| pkt->now = DVD_NOPTS_VALUE; | |
| pkt->duration = DVD_NOPTS_VALUE; | |
| } | |
| } | |
| return pkt; | |
| } | |
| bool OMXReader::SetActiveStream(OMXStreamType type, unsigned int index) | |
| { | |
| bool ret = false; | |
| Lock(); | |
| ret = SetActiveStreamInternal(type, index); | |
| UnLock(); | |
| return ret; | |
| } | |
| bool OMXReader::SeekChapter(int chapter, double* startpts) | |
| { | |
| if(chapter < 1) | |
| chapter = 1; | |
| if(m_pFormatContext == NULL) | |
| return false; | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,14,0) | |
| 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), 0, startpts); | |
| #else | |
| return false; | |
| #endif | |
| } | |
| double OMXReader::ConvertTimestamp(int64_t pts, int den, int num) | |
| { | |
| if(m_pFormatContext == NULL) | |
| return DVD_NOPTS_VALUE; | |
| 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; | |
| if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) | |
| starttime = (double)m_pFormatContext->start_time / AV_TIME_BASE; | |
| if(timestamp > starttime) | |
| timestamp -= starttime; | |
| else if( timestamp + 0.1f > starttime ) | |
| timestamp = 0; | |
| return timestamp*DVD_TIME_BASE; | |
| } | |
| int OMXReader::GetChapter() | |
| { | |
| if(m_pFormatContext == NULL | |
| || m_iCurrentPts == DVD_NOPTS_VALUE) | |
| return 0; | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,14,0) | |
| for(unsigned i = 0; i < m_pFormatContext->nb_chapters; i++) | |
| { | |
| AVChapter *chapter = m_pFormatContext->chapters[i]; | |
| if(m_iCurrentPts >= ConvertTimestamp(chapter->start, chapter->time_base.den, chapter->time_base.num) | |
| && m_iCurrentPts < ConvertTimestamp(chapter->end, chapter->time_base.den, chapter->time_base.num)) | |
| return i + 1; | |
| } | |
| #endif | |
| return 0; | |
| } | |
| void OMXReader::GetChapterName(std::string& strChapterName) | |
| { | |
| strChapterName = ""; | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,14,0) | |
| int chapterIdx = GetChapter(); | |
| if(chapterIdx <= 0) | |
| return; | |
| #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,83,0) | |
| // API added on: 2010-10-15 | |
| // (Note that while the function was available earlier, the generic | |
| // metadata tags were not populated by default) | |
| AVDictionaryEntry *titleTag = m_dllAvUtil.av_dict_get(m_pFormatContext->chapters[chapterIdx-1]->metadata, | |
| "title", NULL, 0); | |
| if (titleTag) | |
| strChapterName = titleTag->value; | |
| #else | |
| if (m_pFormatContext->chapters[chapterIdx-1]->title) | |
| strChapterName = m_pFormatContext->chapters[chapterIdx-1]->title; | |
| #endif | |
| #endif | |
| } | |
| void OMXReader::UpdateCurrentPTS() | |
| { | |
| m_iCurrentPts = DVD_NOPTS_VALUE; | |
| for(unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) | |
| { | |
| AVStream *stream = m_pFormatContext->streams[i]; | |
| 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_iCurrentPts == DVD_NOPTS_VALUE || m_iCurrentPts > ts ) | |
| m_iCurrentPts = ts; | |
| } | |
| } | |
| } | |
| void OMXReader::SetSpeed(int iSpeed) | |
| { | |
| if(!m_pFormatContext) | |
| return; | |
| if(m_speed != DVD_PLAYSPEED_PAUSE && iSpeed == DVD_PLAYSPEED_PAUSE) | |
| { | |
| m_dllAvFormat.av_read_pause(m_pFormatContext); | |
| } | |
| else if(m_speed == DVD_PLAYSPEED_PAUSE && iSpeed != DVD_PLAYSPEED_PAUSE) | |
| { | |
| m_dllAvFormat.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; | |
| } | |
| } | |
| } | |
| int OMXReader::GetStreamLength() | |
| { | |
| if (!m_pFormatContext) | |
| return 0; | |
| return (int)(m_pFormatContext->duration / (AV_TIME_BASE / 1000)); | |
| } | |
| double OMXReader::NormalizeFrameduration(double frameduration) | |
| { | |
| //if the duration is within 20 microseconds of a common duration, use that | |
| const double durations[] = {DVD_TIME_BASE * 1.001 / 24.0, DVD_TIME_BASE / 24.0, DVD_TIME_BASE / 25.0, | |
| DVD_TIME_BASE * 1.001 / 30.0, DVD_TIME_BASE / 30.0, DVD_TIME_BASE / 50.0, | |
| DVD_TIME_BASE * 1.001 / 60.0, DVD_TIME_BASE / 60.0}; | |
| double lowestdiff = DVD_TIME_BASE; | |
| int selected = -1; | |
| for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); i++) | |
| { | |
| double diff = fabs(frameduration - durations[i]); | |
| if (diff < DVD_MSEC_TO_TIME(0.02) && diff < lowestdiff) | |
| { | |
| selected = i; | |
| lowestdiff = diff; | |
| } | |
| } | |
| if (selected != -1) | |
| return durations[selected]; | |
| else | |
| return frameduration; | |
| } | |
| std::string OMXReader::GetStreamCodecName(AVStream *stream) | |
| { | |
| std::string strStreamName = ""; | |
| if(!stream) | |
| return strStreamName; | |
| unsigned int in = stream->codec->codec_tag; | |
| // 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->codec->codec_type == AVMEDIA_TYPE_VIDEO && in != 0) | |
| { | |
| char fourcc[5]; | |
| memcpy(fourcc, &in, 4); | |
| fourcc[4] = 0; | |
| // fourccs have to be 4 characters | |
| if (strlen(fourcc) == 4) | |
| { | |
| strStreamName = fourcc; | |
| return strStreamName; | |
| } | |
| } | |
| #ifdef FF_PROFILE_DTS_HD_MA | |
| /* use profile to determine the DTS type */ | |
| if (stream->codec->codec_id == AV_CODEC_ID_DTS) | |
| { | |
| if (stream->codec->profile == FF_PROFILE_DTS_HD_MA) | |
| strStreamName = "dtshd_ma"; | |
| else if (stream->codec->profile == FF_PROFILE_DTS_HD_HRA) | |
| strStreamName = "dtshd_hra"; | |
| else | |
| strStreamName = "dca"; | |
| return strStreamName; | |
| } | |
| #endif | |
| AVCodec *codec = m_dllAvCodec.avcodec_find_decoder(stream->codec->codec_id); | |
| if (codec) | |
| strStreamName = codec->name; | |
| return strStreamName; | |
| } | |
| std::string OMXReader::GetCodecName(OMXStreamType type) | |
| { | |
| std::string strStreamName; | |
| Lock(); | |
| switch (type) | |
| { | |
| case OMXSTREAM_AUDIO: | |
| if(m_audio_index != -1) | |
| strStreamName = m_streams[m_audio_index].codec_name; | |
| break; | |
| case OMXSTREAM_VIDEO: | |
| if(m_video_index != -1) | |
| strStreamName = m_streams[m_video_index].codec_name; | |
| break; | |
| case OMXSTREAM_SUBTITLE: | |
| if(m_subtitle_index != -1) | |
| strStreamName = m_streams[m_subtitle_index].codec_name; | |
| break; | |
| default: | |
| break; | |
| } | |
| UnLock(); | |
| return strStreamName; | |
| } | |
| std::string OMXReader::GetCodecName(OMXStreamType type, unsigned int index) | |
| { | |
| std::string strStreamName = ""; | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].type == type && m_streams[i].index == index) | |
| { | |
| strStreamName = m_streams[i].codec_name; | |
| break; | |
| } | |
| } | |
| return strStreamName; | |
| } | |
| std::string OMXReader::GetStreamLanguage(OMXStreamType type, unsigned int index) | |
| { | |
| std::string language = ""; | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].type == type && m_streams[i].index == index) | |
| { | |
| language = m_streams[i].language; | |
| break; | |
| } | |
| } | |
| return language; | |
| } | |
| std::string OMXReader::GetStreamName(OMXStreamType type, unsigned int index) | |
| { | |
| std::string name = ""; | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].type == type && m_streams[i].index == index) | |
| { | |
| name = m_streams[i].name; | |
| break; | |
| } | |
| } | |
| return name; | |
| } | |
| std::string OMXReader::GetStreamType(OMXStreamType type, unsigned int index) | |
| { | |
| std::string strInfo; | |
| char sInfo[64]; | |
| for(int i = 0; i < MAX_STREAMS; i++) | |
| { | |
| if(m_streams[i].type == type && m_streams[i].index == index) | |
| { | |
| if (m_streams[i].hints.codec == AV_CODEC_ID_AC3) strcpy(sInfo, "AC3 "); | |
| else if (m_streams[i].hints.codec == AV_CODEC_ID_DTS) | |
| { | |
| #ifdef FF_PROFILE_DTS_HD_MA | |
| if (m_streams[i].hints.profile == FF_PROFILE_DTS_HD_MA) | |
| strcpy(sInfo, "DTS-HD MA "); | |
| else if (m_streams[i].hints.profile == FF_PROFILE_DTS_HD_HRA) | |
| strcpy(sInfo, "DTS-HD HRA "); | |
| else | |
| #endif | |
| strcpy(sInfo, "DTS "); | |
| } | |
| else if (m_streams[i].hints.codec == AV_CODEC_ID_MP2) strcpy(sInfo, "MP2 "); | |
| else strcpy(sInfo, ""); | |
| if (m_streams[i].hints.channels == 1) strcat(sInfo, "Mono"); | |
| else if (m_streams[i].hints.channels == 2) strcat(sInfo, "Stereo"); | |
| else if (m_streams[i].hints.channels == 6) strcat(sInfo, "5.1"); | |
| else if (m_streams[i].hints.channels != 0) | |
| { | |
| char temp[32]; | |
| sprintf(temp, " %d %s", m_streams[i].hints.channels, "Channels"); | |
| strcat(sInfo, temp); | |
| } | |
| break; | |
| } | |
| } | |
| strInfo = sInfo; | |
| return strInfo; | |
| } | |
| bool OMXReader::CanSeek() | |
| { | |
| if(m_ioContext) | |
| return m_ioContext->seekable; | |
| if(!m_pFormatContext || !m_pFormatContext->pb) | |
| return false; | |
| if(m_pFormatContext->pb->seekable == AVIO_SEEKABLE_NORMAL) | |
| return true; | |
| return false; | |
| } |