From 306e265019bd9145be4f301d64deb30a2cde8af0 Mon Sep 17 00:00:00 2001 From: huceke Date: Thu, 22 Mar 2012 07:31:01 +0100 Subject: [PATCH] Initial import --- BitstreamConverter.cpp | 911 ++++++++ BitstreamConverter.h | 172 ++ COPYING | 340 +++ DllAvCodec.h | 377 ++++ DllAvCore.h | 186 ++ DllAvFilter.h | 331 +++ DllAvFormat.h | 333 +++ DllAvUtil.h | 216 ++ DllBCM.h | 245 +++ DllOMX.h | 123 ++ DynamicDll.cpp | 95 + DynamicDll.h | 497 +++++ File.cpp | 142 ++ File.h | 83 + IAudioRenderer.h | 86 + Makefile | 65 + Makefile.ffmpeg | 70 + Makefile.include | 37 + OMXAudio.cpp | 1466 +++++++++++++ OMXAudio.h | 143 ++ OMXAudioCodecOMX.cpp | 351 ++++ OMXAudioCodecOMX.h | 74 + OMXClock.cpp | 688 +++++++ OMXClock.h | 156 ++ OMXCore.cpp | 1642 +++++++++++++++ OMXCore.h | 216 ++ OMXOverlay.h | 78 + OMXOverlayCodec.h | 88 + OMXOverlayCodecText.cpp | 152 ++ OMXOverlayCodecText.h | 43 + OMXOverlayText.h | 129 ++ OMXPlayerAudio.cpp | 721 +++++++ OMXPlayerAudio.h | 135 ++ OMXPlayerVideo.cpp | 630 ++++++ OMXPlayerVideo.h | 128 ++ OMXReader.cpp | 1429 +++++++++++++ OMXReader.h | 202 ++ OMXStreamInfo.cpp | 71 + OMXStreamInfo.h | 80 + OMXSubtitleTagSami.cpp | 261 +++ OMXSubtitleTagSami.h | 68 + OMXThread.cpp | 132 ++ OMXThread.h | 49 + OMXVideo.cpp | 1090 ++++++++++ OMXVideo.h | 106 + RBP.h | 65 + README | 45 + cores/IAudioCallback.h | 43 + guilib/Geometry.h | 173 ++ linux/ConvUtils.h | 50 + linux/PlatformDefs.h | 678 ++++++ linux/PlatformInclude.h | 36 + linux/RBP.cpp | 58 + linux/RBP.h | 61 + linux/XMemUtils.cpp | 52 + linux/XMemUtils.d | 37 + linux/XMemUtils.h | 31 + linux/XSyncUtils.h | 44 + linux/stat_utf8.h | 28 + linux/stdio_utf8.h | 32 + omxplayer | 34 + omxplayer.cpp | 730 +++++++ profiler.sh | 11 + system.h | 295 +++ utils/MathUtils.h | 209 ++ utils/PCMRemap.cpp | 795 +++++++ utils/PCMRemap.h | 147 ++ utils/RegExp.cpp | 266 +++ utils/RegExp.h | 87 + utils/StdString.h | 4344 +++++++++++++++++++++++++++++++++++++++ utils/log.cpp | 220 ++ utils/log.h | 63 + 72 files changed, 23271 insertions(+) create mode 100644 BitstreamConverter.cpp create mode 100644 BitstreamConverter.h create mode 100644 COPYING create mode 100644 DllAvCodec.h create mode 100644 DllAvCore.h create mode 100644 DllAvFilter.h create mode 100644 DllAvFormat.h create mode 100644 DllAvUtil.h create mode 100644 DllBCM.h create mode 100644 DllOMX.h create mode 100644 DynamicDll.cpp create mode 100644 DynamicDll.h create mode 100644 File.cpp create mode 100644 File.h create mode 100644 IAudioRenderer.h create mode 100644 Makefile create mode 100644 Makefile.ffmpeg create mode 100644 Makefile.include create mode 100644 OMXAudio.cpp create mode 100644 OMXAudio.h create mode 100644 OMXAudioCodecOMX.cpp create mode 100644 OMXAudioCodecOMX.h create mode 100644 OMXClock.cpp create mode 100644 OMXClock.h create mode 100644 OMXCore.cpp create mode 100644 OMXCore.h create mode 100644 OMXOverlay.h create mode 100644 OMXOverlayCodec.h create mode 100644 OMXOverlayCodecText.cpp create mode 100644 OMXOverlayCodecText.h create mode 100644 OMXOverlayText.h create mode 100644 OMXPlayerAudio.cpp create mode 100644 OMXPlayerAudio.h create mode 100644 OMXPlayerVideo.cpp create mode 100644 OMXPlayerVideo.h create mode 100644 OMXReader.cpp create mode 100644 OMXReader.h create mode 100644 OMXStreamInfo.cpp create mode 100644 OMXStreamInfo.h create mode 100644 OMXSubtitleTagSami.cpp create mode 100644 OMXSubtitleTagSami.h create mode 100644 OMXThread.cpp create mode 100644 OMXThread.h create mode 100644 OMXVideo.cpp create mode 100644 OMXVideo.h create mode 100644 RBP.h create mode 100644 README create mode 100644 cores/IAudioCallback.h create mode 100644 guilib/Geometry.h create mode 100644 linux/ConvUtils.h create mode 100644 linux/PlatformDefs.h create mode 100644 linux/PlatformInclude.h create mode 100644 linux/RBP.cpp create mode 100644 linux/RBP.h create mode 100644 linux/XMemUtils.cpp create mode 100644 linux/XMemUtils.d create mode 100644 linux/XMemUtils.h create mode 100644 linux/XSyncUtils.h create mode 100644 linux/stat_utf8.h create mode 100644 linux/stdio_utf8.h create mode 100755 omxplayer create mode 100644 omxplayer.cpp create mode 100644 profiler.sh create mode 100644 system.h create mode 100644 utils/MathUtils.h create mode 100644 utils/PCMRemap.cpp create mode 100644 utils/PCMRemap.h create mode 100644 utils/RegExp.cpp create mode 100644 utils/RegExp.h create mode 100644 utils/StdString.h create mode 100644 utils/log.cpp create mode 100644 utils/log.h diff --git a/BitstreamConverter.cpp b/BitstreamConverter.cpp new file mode 100644 index 00000000..037ff089 --- /dev/null +++ b/BitstreamConverter.cpp @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2010 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 + * + */ + +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif + +#include "BitstreamConverter.h" + +void CBitstreamConverter::bits_reader_set( bits_reader_t *br, uint8_t *buf, int len ) +{ + br->buffer = br->start = buf; + br->offbits = 0; + br->length = len; + br->oflow = 0; +} + +uint32_t CBitstreamConverter::read_bits( bits_reader_t *br, int nbits ) +{ + int i, nbytes; + uint32_t ret = 0; + uint8_t *buf; + + buf = br->buffer; + nbytes = (br->offbits + nbits)/8; + if ( ((br->offbits + nbits) %8 ) > 0 ) + nbytes++; + if ( (buf + nbytes) > (br->start + br->length) ) { + br->oflow = 1; + return 0; + } + for ( i=0; ioffbits; + ret = ((ret<>i)>>((nbytes*8)-nbits-br->offbits); + + br->offbits += nbits; + br->buffer += br->offbits / 8; + br->offbits %= 8; + + return ret; +} + +void CBitstreamConverter::skip_bits( bits_reader_t *br, int nbits ) +{ + br->offbits += nbits; + br->buffer += br->offbits / 8; + br->offbits %= 8; + if ( br->buffer > (br->start + br->length) ) { + br->oflow = 1; + } +} + +uint32_t CBitstreamConverter::get_bits( bits_reader_t *br, int nbits ) +{ + int i, nbytes; + uint32_t ret = 0; + uint8_t *buf; + + buf = br->buffer; + nbytes = (br->offbits + nbits)/8; + if ( ((br->offbits + nbits) %8 ) > 0 ) + nbytes++; + if ( (buf + nbytes) > (br->start + br->length) ) { + br->oflow = 1; + return 0; + } + for ( i=0; ioffbits; + ret = ((ret<>i)>>((nbytes*8)-nbits-br->offbits); + + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// +// GStreamer h264 parser +// Copyright (C) 2005 Michal Benes +// (C) 2008 Wim Taymans +// gsth264parse.c: +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +void CBitstreamConverter::nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size) +{ + bs->data = data; + bs->end = data + size; + bs->head = 0; + // fill with something other than 0 to detect + // emulation prevention bytes + bs->cache = 0xffffffff; +} + +uint32_t CBitstreamConverter::nal_bs_read(nal_bitstream *bs, int n) +{ + uint32_t res = 0; + int shift; + + if (n == 0) + return res; + + // fill up the cache if we need to + while (bs->head < n) + { + uint8_t a_byte; + bool check_three_byte; + + check_three_byte = true; +next_byte: + if (bs->data >= bs->end) + { + // we're at the end, can't produce more than head number of bits + n = bs->head; + break; + } + // get the byte, this can be an emulation_prevention_three_byte that we need + // to ignore. + a_byte = *bs->data++; + if (check_three_byte && a_byte == 0x03 && ((bs->cache & 0xffff) == 0)) + { + // next byte goes unconditionally to the cache, even if it's 0x03 + check_three_byte = false; + goto next_byte; + } + // shift bytes in cache, moving the head bits of the cache left + bs->cache = (bs->cache << 8) | a_byte; + bs->head += 8; + } + + // bring the required bits down and truncate + if ((shift = bs->head - n) > 0) + res = bs->cache >> shift; + else + res = bs->cache; + + // mask out required bits + if (n < 32) + res &= (1 << n) - 1; + bs->head = shift; + + return res; +} + +bool CBitstreamConverter::nal_bs_eos(nal_bitstream *bs) +{ + return (bs->data >= bs->end) && (bs->head == 0); +} + +// read unsigned Exp-Golomb code +int CBitstreamConverter::nal_bs_read_ue(nal_bitstream *bs) +{ + int i = 0; + + while (nal_bs_read(bs, 1) == 0 && !nal_bs_eos(bs) && i < 32) + i++; + + return ((1 << i) - 1 + nal_bs_read(bs, i)); +} + +void CBitstreamConverter::parseh264_sps(uint8_t *sps, uint32_t sps_size, bool *interlaced, int32_t *max_ref_frames) +{ + nal_bitstream bs; + sps_info_struct sps_info; + + nal_bs_init(&bs, sps, sps_size); + + sps_info.profile_idc = nal_bs_read(&bs, 8); + nal_bs_read(&bs, 1); // constraint_set0_flag + nal_bs_read(&bs, 1); // constraint_set1_flag + nal_bs_read(&bs, 1); // constraint_set2_flag + nal_bs_read(&bs, 1); // constraint_set3_flag + nal_bs_read(&bs, 4); // reserved + sps_info.level_idc = nal_bs_read(&bs, 8); + sps_info.sps_id = nal_bs_read_ue(&bs); + + if (sps_info.profile_idc == 100 || + sps_info.profile_idc == 110 || + sps_info.profile_idc == 122 || + sps_info.profile_idc == 244 || + sps_info.profile_idc == 44 || + sps_info.profile_idc == 83 || + sps_info.profile_idc == 86) + { + sps_info.chroma_format_idc = nal_bs_read_ue(&bs); + if (sps_info.chroma_format_idc == 3) + sps_info.separate_colour_plane_flag = nal_bs_read(&bs, 1); + sps_info.bit_depth_luma_minus8 = nal_bs_read_ue(&bs); + sps_info.bit_depth_chroma_minus8 = nal_bs_read_ue(&bs); + sps_info.qpprime_y_zero_transform_bypass_flag = nal_bs_read(&bs, 1); + + sps_info.seq_scaling_matrix_present_flag = nal_bs_read (&bs, 1); + if (sps_info.seq_scaling_matrix_present_flag) + { + /* TODO: unfinished */ + } + } + sps_info.log2_max_frame_num_minus4 = nal_bs_read_ue(&bs); + if (sps_info.log2_max_frame_num_minus4 > 12) + { // must be between 0 and 12 + return; + } + sps_info.pic_order_cnt_type = nal_bs_read_ue(&bs); + if (sps_info.pic_order_cnt_type == 0) + { + sps_info.log2_max_pic_order_cnt_lsb_minus4 = nal_bs_read_ue(&bs); + } + else if (sps_info.pic_order_cnt_type == 1) + { // TODO: unfinished + /* + delta_pic_order_always_zero_flag = gst_nal_bs_read (bs, 1); + offset_for_non_ref_pic = gst_nal_bs_read_se (bs); + offset_for_top_to_bottom_field = gst_nal_bs_read_se (bs); + + num_ref_frames_in_pic_order_cnt_cycle = gst_nal_bs_read_ue (bs); + for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) + offset_for_ref_frame[i] = gst_nal_bs_read_se (bs); + */ + } + + sps_info.max_num_ref_frames = nal_bs_read_ue(&bs); + sps_info.gaps_in_frame_num_value_allowed_flag = nal_bs_read(&bs, 1); + sps_info.pic_width_in_mbs_minus1 = nal_bs_read_ue(&bs); + sps_info.pic_height_in_map_units_minus1 = nal_bs_read_ue(&bs); + + sps_info.frame_mbs_only_flag = nal_bs_read(&bs, 1); + if (!sps_info.frame_mbs_only_flag) + sps_info.mb_adaptive_frame_field_flag = nal_bs_read(&bs, 1); + + sps_info.direct_8x8_inference_flag = nal_bs_read(&bs, 1); + + sps_info.frame_cropping_flag = nal_bs_read(&bs, 1); + if (sps_info.frame_cropping_flag) + { + sps_info.frame_crop_left_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_right_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_top_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_bottom_offset = nal_bs_read_ue(&bs); + } + + *interlaced = !sps_info.frame_mbs_only_flag; + *max_ref_frames = sps_info.max_num_ref_frames; +} + +const uint8_t *CBitstreamConverter::avc_find_startcode_internal(const uint8_t *p, const uint8_t *end) +{ + const uint8_t *a = p + 4 - ((intptr_t)p & 3); + + for (end -= 3; p < a && p < end; p++) + { + if (p[0] == 0 && p[1] == 0 && p[2] == 1) + return p; + } + + for (end -= 3; p < end; p += 4) + { + uint32_t x = *(const uint32_t*)p; + if ((x - 0x01010101) & (~x) & 0x80808080) // generic + { + if (p[1] == 0) + { + if (p[0] == 0 && p[2] == 1) + return p; + if (p[2] == 0 && p[3] == 1) + return p+1; + } + if (p[3] == 0) + { + if (p[2] == 0 && p[4] == 1) + return p+2; + if (p[4] == 0 && p[5] == 1) + return p+3; + } + } + } + + for (end += 3; p < end; p++) + { + if (p[0] == 0 && p[1] == 0 && p[2] == 1) + return p; + } + + return end + 3; +} + +const uint8_t *CBitstreamConverter::avc_find_startcode(const uint8_t *p, const uint8_t *end) +{ + const uint8_t *out= avc_find_startcode_internal(p, end); + if (pput_be32(pb, nal_end - nal_start); + m_dllAvFormat->put_buffer(pb, nal_start, nal_end - nal_start); + size += 4 + nal_end - nal_start; + nal_start = nal_end; + } + return size; +} + +const int CBitstreamConverter::avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size) +{ + ByteIOContext *pb; + int ret = m_dllAvFormat->url_open_dyn_buf(&pb); + if (ret < 0) + return ret; + + avc_parse_nal_units(pb, buf_in, *size); + + m_dllAvUtil->av_freep(buf); + *size = m_dllAvFormat->url_close_dyn_buf(pb, buf); + return 0; +} + +const int CBitstreamConverter::isom_write_avcc(ByteIOContext *pb, const uint8_t *data, int len) +{ + // extradata from bytestream h264, convert to avcC atom data for bitstream + if (len > 6) + { + /* check for h264 start code */ + if (OMX_RB32(data) == 0x00000001 || OMX_RB24(data) == 0x000001) + { + uint8_t *buf=NULL, *end, *start; + uint32_t sps_size=0, pps_size=0; + uint8_t *sps=0, *pps=0; + + int ret = avc_parse_nal_units_buf(data, &buf, &len); + if (ret < 0) + return ret; + start = buf; + end = buf + len; + + /* look for sps and pps */ + while (end - buf > 4) + { + uint32_t size; + uint8_t nal_type; + size = FFMIN(OMX_RB32(buf), end - buf - 4); + buf += 4; + nal_type = buf[0] & 0x1f; + if (nal_type == 7) /* SPS */ + { + sps = buf; + sps_size = size; + } + else if (nal_type == 8) /* PPS */ + { + pps = buf; + pps_size = size; + } + buf += size; + } + if (!sps || !pps || sps_size < 4 || sps_size > UINT16_MAX || pps_size > UINT16_MAX) + assert(0); + + m_dllAvFormat->put_byte(pb, 1); /* version */ + m_dllAvFormat->put_byte(pb, sps[1]); /* profile */ + m_dllAvFormat->put_byte(pb, sps[2]); /* profile compat */ + m_dllAvFormat->put_byte(pb, sps[3]); /* level */ + m_dllAvFormat->put_byte(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */ + m_dllAvFormat->put_byte(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ + + m_dllAvFormat->put_be16(pb, sps_size); + m_dllAvFormat->put_buffer(pb, sps, sps_size); + if (pps) + { + m_dllAvFormat->put_byte(pb, 1); /* number of pps */ + m_dllAvFormat->put_be16(pb, pps_size); + m_dllAvFormat->put_buffer(pb, pps, pps_size); + } + m_dllAvUtil->av_free(start); + } + else + { + m_dllAvFormat->put_buffer(pb, data, len); + } + } + return 0; +} + +CBitstreamConverter::CBitstreamConverter() +{ + m_convert_bitstream = false; + m_convertBuffer = NULL; + m_convertSize = 0; + m_inputBuffer = NULL; + m_inputSize = 0; + m_to_annexb = false; + m_extradata = NULL; + m_extrasize = 0; + m_convert_3byteTo4byteNALSize = false; + m_dllAvUtil = NULL; + m_dllAvFormat = NULL; + m_convert_bytestream = false; + m_convert_vc1 = false; +} + +CBitstreamConverter::~CBitstreamConverter() +{ + Close(); +} + +bool CBitstreamConverter::Open(enum CodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb) +{ + m_to_annexb = to_annexb; + m_convert_vc1 = false; + + m_codec = codec; + + switch(codec) + { + case CODEC_ID_VC1: + m_extradata = (uint8_t *)malloc(in_extrasize); + memcpy(m_extradata, in_extradata, in_extrasize); + m_extrasize = in_extrasize; + m_dllAvUtil = new DllAvUtil; + m_dllAvFormat = new DllAvFormat; + if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load()) + return false; + + return true; + break; + case CODEC_ID_H264: + if (in_extrasize < 7 || in_extradata == NULL) + { + CLog::Log(LOGERROR, "CBitstreamConverter::Open avcC data too small or missing\n"); + return false; + } + // valid avcC data (bitstream) always starts with the value 1 (version) + if(m_to_annexb) + { + if ( *(char*)in_extradata == 1 ) + { + CLog::Log(LOGINFO, "CBitstreamConverter::Open bitstream to annexb init\n"); + m_convert_bitstream = BitstreamConvertInit(in_extradata, in_extrasize); + return true; + } + } + else + { + // valid avcC atom data always starts with the value 1 (version) + if ( *in_extradata != 1 ) + { + if (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 0 && in_extradata[3] == 1) + { + CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init\n"); + // video content is from x264 or from bytestream h264 (AnnexB format) + // NAL reformating to bitstream format needed + m_dllAvUtil = new DllAvUtil; + m_dllAvFormat = new DllAvFormat; + if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load()) + return false; + + ByteIOContext *pb; + if (m_dllAvFormat->url_open_dyn_buf(&pb) < 0) + return false; + m_convert_bytestream = true; + // create a valid avcC atom data from ffmpeg's extradata + isom_write_avcc(pb, in_extradata, in_extrasize); + // unhook from ffmpeg's extradata + in_extradata = NULL; + // extract the avcC atom data into extradata then write it into avcCData for VDADecoder + in_extrasize = m_dllAvFormat->url_close_dyn_buf(pb, &in_extradata); + // make a copy of extradata contents + m_extradata = (uint8_t *)malloc(in_extrasize); + memcpy(m_extradata, in_extradata, in_extrasize); + m_extrasize = in_extrasize; + // done with the converted extradata, we MUST free using av_free + m_dllAvUtil->av_free(in_extradata); + return true; + } + else + { + CLog::Log(LOGNOTICE, "CBitstreamConverter::Open invalid avcC atom data"); + return false; + } + } + else + { + if (in_extradata[4] == 0xFE) + { + CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init 3 byte to 4 byte nal\n"); + // video content is from so silly encoder that think 3 byte NAL sizes + // are valid, setup to convert 3 byte NAL sizes to 4 byte. + m_dllAvUtil = new DllAvUtil; + m_dllAvFormat = new DllAvFormat; + if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load()) + return false; + + in_extradata[4] = 0xFF; + m_convert_3byteTo4byteNALSize = true; + + m_extradata = (uint8_t *)malloc(in_extrasize); + memcpy(m_extradata, in_extradata, in_extrasize); + m_extrasize = in_extrasize; + return true; + } + } + } + return false; + break; + default: + return false; + break; + } + return false; +} + +void CBitstreamConverter::Close(void) +{ + if (m_convert_bitstream) + { + if (m_sps_pps_context.sps_pps_data) + { + free(m_sps_pps_context.sps_pps_data); + m_sps_pps_context.sps_pps_data = NULL; + } + if(m_convertBuffer) + free(m_convertBuffer); + m_convertSize = 0; + } + + if (m_convert_bytestream || m_convert_vc1) + { + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + } + + if(m_extradata) + free(m_extradata); + m_extradata = NULL; + m_extrasize = 0; + + m_inputBuffer = NULL; + m_inputSize = 0; + m_convert_3byteTo4byteNALSize = false; + + m_convert_bitstream = false; + + if (m_dllAvUtil) + { + delete m_dllAvUtil; + m_dllAvUtil = NULL; + } + if (m_dllAvFormat) + { + delete m_dllAvFormat; + m_dllAvFormat = NULL; + } +} + +bool CBitstreamConverter::Convert(uint8_t *pData, int iSize) +{ + if(m_convertBuffer) + free(m_convertBuffer); + m_convertBuffer = NULL; + m_convertSize = 0; + m_inputBuffer = NULL; + m_inputSize = 0; + + if (pData) + { + if(m_codec == CODEC_ID_H264) + { + if(m_to_annexb) + { + int demuxer_bytes = iSize; + + uint8_t *demuxer_content = pData; + + if (m_convert_bitstream) + { + // convert demuxer packet from bitstream to bytestream (AnnexB) + int bytestream_size = 0; + uint8_t *bytestream_buff = NULL; + + BitstreamConvert(demuxer_content, demuxer_bytes, &bytestream_buff, &bytestream_size); + if (bytestream_buff && (bytestream_size > 0)) + { + m_convertSize = bytestream_size; + m_convertBuffer = bytestream_buff; + } + else + { + Close(); + m_inputBuffer = pData; + m_inputSize = iSize; + CLog::Log(LOGERROR, "CBitstreamConverter::Convert error converting. disable converter\n"); + } + } + else + { + m_inputBuffer = pData; + m_inputSize = iSize; + } + + return true; + } + else + { + m_inputBuffer = pData; + m_inputSize = iSize; + + if (m_convert_bytestream) + { + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + + // convert demuxer packet from bytestream (AnnexB) to bitstream + ByteIOContext *pb; + + if(m_dllAvFormat->url_open_dyn_buf(&pb) < 0) + { + return false; + } + m_convertSize = avc_parse_nal_units(pb, pData, iSize); + m_convertSize = m_dllAvFormat->url_close_dyn_buf(pb, &m_convertBuffer); + } + else if (m_convert_3byteTo4byteNALSize) + { + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + + // convert demuxer packet from 3 byte NAL sizes to 4 byte + ByteIOContext *pb; + if (m_dllAvFormat->url_open_dyn_buf(&pb) < 0) + return false; + + uint32_t nal_size; + uint8_t *end = pData + iSize; + uint8_t *nal_start = pData; + while (nal_start < end) + { + nal_size = OMX_RB24(nal_start); + m_dllAvFormat->put_be32(pb, nal_size); + nal_start += 3; + m_dllAvFormat->put_buffer(pb, nal_start, nal_size); + nal_start += nal_size; + } + + m_convertSize = m_dllAvFormat->url_close_dyn_buf(pb, &m_convertBuffer); + } + return true; + } + } + else if (m_codec == CODEC_ID_VC1) + { + if(!(iSize >= 3 && !pData[0] && !pData[1] && pData[2] == 1) && !m_convert_vc1) + m_convert_vc1 = true; + + if(m_convert_vc1) + { + + m_inputBuffer = pData; + m_inputSize = iSize; + + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + + ByteIOContext *pb; + if (m_dllAvFormat->url_open_dyn_buf(&pb) < 0) + return false; + + m_dllAvFormat->put_byte(pb, 0); + m_dllAvFormat->put_byte(pb, 0); + m_dllAvFormat->put_byte(pb, !m_convert_vc1 ? 0 : 1); + m_dllAvFormat->put_byte(pb, !m_convert_vc1 ? 0 : 0xd); + m_dllAvFormat->put_buffer(pb, pData, iSize); + m_convertSize = m_dllAvFormat->url_close_dyn_buf(pb, &m_convertBuffer); + return true; + } + else + { + m_inputBuffer = pData; + m_inputSize = iSize; + return true; + } + } + } + + return false; +} + + +uint8_t *CBitstreamConverter::GetConvertBuffer() +{ + if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize || m_convert_vc1) && m_convertBuffer != NULL) + return m_convertBuffer; + else + return m_inputBuffer; +} + +int CBitstreamConverter::GetConvertSize() +{ + if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize || m_convert_vc1) && m_convertBuffer != NULL) + return m_convertSize; + else + return m_inputSize; +} + +uint8_t *CBitstreamConverter::GetExtraData() +{ + return m_extradata; +} +int CBitstreamConverter::GetExtraSize() +{ + return m_extrasize; +} + +bool CBitstreamConverter::BitstreamConvertInit(void *in_extradata, int in_extrasize) +{ + // based on h264_mp4toannexb_bsf.c (ffmpeg) + // which is Copyright (c) 2007 Benoit Fouet + // and Licensed GPL 2.1 or greater + + m_sps_pps_size = 0; + m_sps_pps_context.sps_pps_data = NULL; + + // nothing to filter + if (!in_extradata || in_extrasize < 6) + return false; + + uint16_t unit_size; + uint32_t total_size = 0; + uint8_t *out = NULL, unit_nb, sps_done = 0; + const uint8_t *extradata = (uint8_t*)in_extradata + 4; + static const uint8_t nalu_header[4] = {0, 0, 0, 1}; + + // retrieve length coded size + m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1; + if (m_sps_pps_context.length_size == 3) + return false; + + // retrieve sps and pps unit(s) + unit_nb = *extradata++ & 0x1f; // number of sps unit(s) + if (!unit_nb) + { + unit_nb = *extradata++; // number of pps unit(s) + sps_done++; + } + while (unit_nb--) + { + unit_size = extradata[0] << 8 | extradata[1]; + total_size += unit_size + 4; + if ( (extradata + 2 + unit_size) > ((uint8_t*)in_extradata + in_extrasize) ) + { + free(out); + return false; + } + out = (uint8_t*)realloc(out, total_size); + if (!out) + return false; + + memcpy(out + total_size - unit_size - 4, nalu_header, 4); + memcpy(out + total_size - unit_size, extradata + 2, unit_size); + extradata += 2 + unit_size; + + if (!unit_nb && !sps_done++) + unit_nb = *extradata++; // number of pps unit(s) + } + + m_sps_pps_context.sps_pps_data = out; + m_sps_pps_context.size = total_size; + m_sps_pps_context.first_idr = 1; + + return true; +} + +bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size) +{ + // based on h264_mp4toannexb_bsf.c (ffmpeg) + // which is Copyright (c) 2007 Benoit Fouet + // and Licensed GPL 2.1 or greater + + + uint8_t *buf = pData; + uint32_t buf_size = iSize; + uint8_t unit_type; + int32_t nal_size; + uint32_t cumul_size = 0; + const uint8_t *buf_end = buf + buf_size; + + do + { + if (buf + m_sps_pps_context.length_size > buf_end) + goto fail; + + if (m_sps_pps_context.length_size == 1) + nal_size = buf[0]; + else if (m_sps_pps_context.length_size == 2) + nal_size = buf[0] << 8 | buf[1]; + else + nal_size = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + + buf += m_sps_pps_context.length_size; + unit_type = *buf & 0x1f; + + if (buf + nal_size > buf_end || nal_size < 0) + goto fail; + + // prepend only to the first type 5 NAL unit of an IDR picture + if (m_sps_pps_context.first_idr && unit_type == 5) + { + BitstreamAllocAndCopy(poutbuf, poutbuf_size, + m_sps_pps_context.sps_pps_data, m_sps_pps_context.size, buf, nal_size); + m_sps_pps_context.first_idr = 0; + } + else + { + BitstreamAllocAndCopy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size); + if (!m_sps_pps_context.first_idr && unit_type == 1) + m_sps_pps_context.first_idr = 1; + } + + buf += nal_size; + cumul_size += nal_size + m_sps_pps_context.length_size; + } while (cumul_size < buf_size); + + return true; + +fail: + free(*poutbuf); + *poutbuf = NULL; + *poutbuf_size = 0; + return false; +} + +void CBitstreamConverter::BitstreamAllocAndCopy( uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size) +{ + // based on h264_mp4toannexb_bsf.c (ffmpeg) + // which is Copyright (c) 2007 Benoit Fouet + // and Licensed GPL 2.1 or greater + + #define CHD_WB32(p, d) { \ + ((uint8_t*)(p))[3] = (d); \ + ((uint8_t*)(p))[2] = (d) >> 8; \ + ((uint8_t*)(p))[1] = (d) >> 16; \ + ((uint8_t*)(p))[0] = (d) >> 24; } + + uint32_t offset = *poutbuf_size; + uint8_t nal_header_size = offset ? 3 : 4; + + *poutbuf_size += sps_pps_size + in_size + nal_header_size; + *poutbuf = (uint8_t*)realloc(*poutbuf, *poutbuf_size); + if (sps_pps) + memcpy(*poutbuf + offset, sps_pps, sps_pps_size); + + memcpy(*poutbuf + sps_pps_size + nal_header_size + offset, in, in_size); + if (!offset) + { + CHD_WB32(*poutbuf + sps_pps_size, 1); + } + else + { + (*poutbuf + offset + sps_pps_size)[0] = 0; + (*poutbuf + offset + sps_pps_size)[1] = 0; + (*poutbuf + offset + sps_pps_size)[2] = 1; + } +} + + diff --git a/BitstreamConverter.h b/BitstreamConverter.h new file mode 100644 index 00000000..ba009c2f --- /dev/null +++ b/BitstreamConverter.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010 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 + * + */ + +#ifndef _BITSTREAMCONVERTER_H_ +#define _BITSTREAMCONVERTER_H_ + +#include +#include "DllAvUtil.h" +#include "DllAvFormat.h" +#include "DllAvFilter.h" +#include "DllAvCodec.h" +#include "DllAvCore.h" + +typedef struct { + uint8_t *buffer, *start; + int offbits, length, oflow; +} bits_reader_t; + +//////////////////////////////////////////////////////////////////////////////////////////// +// TODO: refactor this so as not to need these ffmpeg routines. +// These are not exposed in ffmpeg's API so we dupe them here. +// AVC helper functions for muxers, +// * Copyright (c) 2006 Baptiste Coudurier +// This is part of FFmpeg +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +#define OMX_RB16(x) \ + ((((const uint8_t*)(x))[0] << 8) | \ + ((const uint8_t*)(x)) [1]) + +#define OMX_RB24(x) \ + ((((const uint8_t*)(x))[0] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[2]) + +#define OMX_RB32(x) \ + ((((const uint8_t*)(x))[0] << 24) | \ + (((const uint8_t*)(x))[1] << 16) | \ + (((const uint8_t*)(x))[2] << 8) | \ + ((const uint8_t*)(x))[3]) + +#define OMX_WB32(p, d) { \ + ((uint8_t*)(p))[3] = (d); \ + ((uint8_t*)(p))[2] = (d) >> 8; \ + ((uint8_t*)(p))[1] = (d) >> 16; \ + ((uint8_t*)(p))[0] = (d) >> 24; } + +typedef struct +{ + const uint8_t *data; + const uint8_t *end; + int head; + uint64_t cache; +} nal_bitstream; + +typedef struct +{ + int profile_idc; + int level_idc; + int sps_id; + + int chroma_format_idc; + int separate_colour_plane_flag; + int bit_depth_luma_minus8; + int bit_depth_chroma_minus8; + int qpprime_y_zero_transform_bypass_flag; + int seq_scaling_matrix_present_flag; + + int log2_max_frame_num_minus4; + int pic_order_cnt_type; + int log2_max_pic_order_cnt_lsb_minus4; + + int max_num_ref_frames; + int gaps_in_frame_num_value_allowed_flag; + int pic_width_in_mbs_minus1; + int pic_height_in_map_units_minus1; + + int frame_mbs_only_flag; + int mb_adaptive_frame_field_flag; + + int direct_8x8_inference_flag; + + int frame_cropping_flag; + int frame_crop_left_offset; + int frame_crop_right_offset; + int frame_crop_top_offset; + int frame_crop_bottom_offset; +} sps_info_struct; + +class CBitstreamConverter +{ +public: + CBitstreamConverter(); + ~CBitstreamConverter(); + // Required overrides + static void bits_reader_set( bits_reader_t *br, uint8_t *buf, int len ); + static uint32_t read_bits( bits_reader_t *br, int nbits ); + static void skip_bits( bits_reader_t *br, int nbits ); + static uint32_t get_bits( bits_reader_t *br, int nbits ); + + bool Open(enum CodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb); + void Close(void); + bool NeedConvert(void) { return m_convert_bitstream; }; + bool Convert(uint8_t *pData, int iSize); + uint8_t *GetConvertBuffer(void); + int GetConvertSize(); + uint8_t *GetExtraData(void); + int GetExtraSize(); + void parseh264_sps(uint8_t *sps, uint32_t sps_size, bool *interlaced, int32_t *max_ref_frames); +protected: + // bytestream (Annex B) to bistream conversion support. + void nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size); + uint32_t nal_bs_read(nal_bitstream *bs, int n); + bool nal_bs_eos(nal_bitstream *bs); + int nal_bs_read_ue(nal_bitstream *bs); + const uint8_t *avc_find_startcode_internal(const uint8_t *p, const uint8_t *end); + const uint8_t *avc_find_startcode(const uint8_t *p, const uint8_t *end); + const int avc_parse_nal_units(ByteIOContext *pb, const uint8_t *buf_in, int size); + const int avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size); + const int isom_write_avcc(ByteIOContext *pb, const uint8_t *data, int len); + // bitstream to bytestream (Annex B) conversion support. + bool BitstreamConvertInit(void *in_extradata, int in_extrasize); + bool BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size); + void BitstreamAllocAndCopy( uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size); + + typedef struct omx_bitstream_ctx { + uint8_t length_size; + uint8_t first_idr; + uint8_t *sps_pps_data; + uint32_t size; + } omx_bitstream_ctx; + + uint8_t *m_convertBuffer; + int m_convertSize; + uint8_t *m_inputBuffer; + int m_inputSize; + + uint32_t m_sps_pps_size; + omx_bitstream_ctx m_sps_pps_context; + bool m_convert_bitstream; + bool m_to_annexb; + bool m_convert_vc1; + + uint8_t *m_extradata; + int m_extrasize; + bool m_convert_3byteTo4byteNALSize; + bool m_convert_bytestream; + DllAvUtil *m_dllAvUtil; + DllAvFormat *m_dllAvFormat; + CodecID m_codec; +}; + +#endif diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 of the License, 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/DllAvCodec.h b/DllAvCodec.h new file mode 100644 index 00000000..d3633004 --- /dev/null +++ b/DllAvCodec.h @@ -0,0 +1,377 @@ +#pragma once +/* + * Copyright (C) 2005-2010 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" +#endif +#include "DynamicDll.h" +#include "DllAvCore.h" +#include "utils/log.h" + +#ifndef AV_NOWARN_DEPRECATED +#define AV_NOWARN_DEPRECATED +#endif + +extern "C" { +#ifndef HAVE_MMX +#define HAVE_MMX +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +#ifndef __GNUC__ +#pragma warning(disable:4244) +#endif + +#if (defined USE_EXTERNAL_FFMPEG) + #if (defined HAVE_LIBAVCODEC_AVCODEC_H) + #include + #if (defined HAVE_LIBAVCODEC_OPT_H) + #include + #endif + #if (defined AVPACKET_IN_AVFORMAT) + #include + #endif + #elif (defined HAVE_FFMPEG_AVCODEC_H) + #include + #include + #if (defined AVPACKET_IN_AVFORMAT) + #include + #endif + #endif + + /* From non-public audioconvert.h */ + int64_t avcodec_guess_channel_layout(int nb_channels, enum CodecID codec_id, const char *fmt_name); + struct AVAudioConvert; + typedef struct AVAudioConvert AVAudioConvert; + AVAudioConvert *av_audio_convert_alloc(enum AVSampleFormat out_fmt, int out_channels, + enum AVSampleFormat in_fmt, int in_channels, + const float *matrix, int flags); + void av_audio_convert_free(AVAudioConvert *ctx); + int av_audio_convert(AVAudioConvert *ctx, + void * const out[6], const int out_stride[6], + const void * const in[6], const int in_stride[6], int len); +#else + #include "libavcodec/avcodec.h" + #include "libavcodec/audioconvert.h" +#endif +} + +/* Some convenience macros introduced at this particular revision of libavcodec. + */ +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52,25,0) +#define CH_LAYOUT_5POINT0_BACK (CH_LAYOUT_SURROUND|CH_BACK_LEFT|CH_BACK_RIGHT) +#define CH_LAYOUT_5POINT1_BACK (CH_LAYOUT_5POINT0_BACK|CH_LOW_FREQUENCY) +#undef CH_LAYOUT_7POINT1_WIDE +#define CH_LAYOUT_7POINT1_WIDE (CH_LAYOUT_5POINT1_BACK|\ + CH_FRONT_LEFT_OF_CENTER|CH_FRONT_RIGHT_OF_CENTER) +#endif + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52,64,0) +// API added on: 2010-03-31 +#define AVMediaType CodecType +#define AVMEDIA_TYPE_UNKNOWN CODEC_TYPE_UNKNOWN +#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO +#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO +#define AVMEDIA_TYPE_DATA CODEC_TYPE_DATA +#define AVMEDIA_TYPE_SUBTITLE CODEC_TYPE_SUBTITLE +#define AVMEDIA_TYPE_ATTACHMENT CODEC_TYPE_ATTACHMENT +#define AVMEDIA_TYPE_NB CODEC_TYPE_NB +#endif + +//#include "threads/SingleLock.h" + +class DllAvCodecInterface +{ +public: + virtual ~DllAvCodecInterface() {} + virtual void avcodec_register_all(void)=0; + virtual void avcodec_flush_buffers(AVCodecContext *avctx)=0; + virtual int avcodec_open_dont_call(AVCodecContext *avctx, AVCodec *codec)=0; + virtual AVCodec *avcodec_find_decoder(enum CodecID id)=0; + virtual AVCodec *avcodec_find_encoder(enum CodecID id)=0; + virtual int avcodec_close_dont_call(AVCodecContext *avctx)=0; + virtual AVFrame *avcodec_alloc_frame(void)=0; + virtual int avpicture_fill(AVPicture *picture, uint8_t *ptr, PixelFormat pix_fmt, int width, int height)=0; + virtual int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt)=0; + virtual int avcodec_decode_audio3(AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr, AVPacket *avpkt)=0; + virtual int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt)=0; + virtual int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size, const short *samples)=0; + virtual int avpicture_get_size(PixelFormat pix_fmt, int width, int height)=0; + virtual AVCodecContext *avcodec_alloc_context(void)=0; + virtual void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)=0; + virtual void avcodec_get_context_defaults(AVCodecContext *s)=0; + virtual AVCodecParserContext *av_parser_init(int codec_id)=0; + virtual int av_parser_parse2(AVCodecParserContext *s,AVCodecContext *avctx, uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, + int64_t pts, int64_t dts, int64_t pos)=0; + virtual void av_parser_close(AVCodecParserContext *s)=0; + virtual AVBitStreamFilterContext *av_bitstream_filter_init(const char *name)=0; + virtual int av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe) =0; + virtual void av_bitstream_filter_close(AVBitStreamFilterContext *bsfc) =0; + virtual void avpicture_free(AVPicture *picture)=0; + virtual void av_free_packet(AVPacket *pkt)=0; + virtual int avpicture_alloc(AVPicture *picture, PixelFormat pix_fmt, int width, int height)=0; + virtual enum PixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum PixelFormat *fmt)=0; + virtual int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic)=0; + virtual void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic)=0; + virtual int avcodec_thread_init(AVCodecContext *s, int thread_count)=0; + virtual AVCodec *av_codec_next(AVCodec *c)=0; + virtual AVAudioConvert *av_audio_convert_alloc(enum AVSampleFormat out_fmt, int out_channels, + enum AVSampleFormat in_fmt , int in_channels, + const float *matrix , int flags)=0; + virtual void av_audio_convert_free(AVAudioConvert *ctx)=0; + virtual int av_audio_convert(AVAudioConvert *ctx, + void * const out[6], const int out_stride[6], + const void * const in[6], const int in_stride[6], int len)=0; + virtual int av_dup_packet(AVPacket *pkt)=0; + virtual void av_init_packet(AVPacket *pkt)=0; + virtual int64_t avcodec_guess_channel_layout(int nb_channels, enum CodecID codec_id, const char *fmt_name)=0; +}; + +#if (defined USE_EXTERNAL_FFMPEG) + +// Use direct layer +class DllAvCodec : public DllDynamic, DllAvCodecInterface +{ +public: + virtual ~DllAvCodec() {} + virtual void avcodec_register_all() + { + ::avcodec_register_all(); + } + virtual void avcodec_flush_buffers(AVCodecContext *avctx) { ::avcodec_flush_buffers(avctx); } + virtual int avcodec_open(AVCodecContext *avctx, AVCodec *codec) + { + return ::avcodec_open(avctx, codec); + } + virtual int avcodec_open_dont_call(AVCodecContext *avctx, AVCodec *codec) { *(int *)0x0 = 0; return 0; } + virtual int avcodec_close_dont_call(AVCodecContext *avctx) { *(int *)0x0 = 0; return 0; } + virtual AVCodec *avcodec_find_decoder(enum CodecID id) { return ::avcodec_find_decoder(id); } + virtual AVCodec *avcodec_find_encoder(enum CodecID id) { return ::avcodec_find_encoder(id); } + virtual int avcodec_close(AVCodecContext *avctx) + { + return ::avcodec_close(avctx); + } + virtual AVFrame *avcodec_alloc_frame() { return ::avcodec_alloc_frame(); } + virtual int avpicture_fill(AVPicture *picture, uint8_t *ptr, PixelFormat pix_fmt, int width, int height) { return ::avpicture_fill(picture, ptr, pix_fmt, width, height); } +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,23,0) + // API added on: 2009-04-07 + virtual int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt) { return ::avcodec_decode_video2(avctx, picture, got_picture_ptr, avpkt); } + virtual int avcodec_decode_audio3(AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr, AVPacket *avpkt) { return ::avcodec_decode_audio3(avctx, samples, frame_size_ptr, avpkt); } + virtual int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt) { return ::avcodec_decode_subtitle2(avctx, sub, got_sub_ptr, avpkt); } +#else + virtual int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt) { return ::avcodec_decode_video(avctx, picture, got_picture_ptr, avpkt->data, avpkt->size); } + virtual int avcodec_decode_audio3(AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr, AVPacket *avpkt) { return ::avcodec_decode_audio2(avctx, samples, frame_size_ptr, avpkt->data, avpkt->size); } + virtual int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt) { return ::avcodec_decode_subtitle(avctx, sub, got_sub_ptr, avpkt->data, avpkt->size); } +#endif + virtual int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size, const short *samples) { return ::avcodec_encode_audio(avctx, buf, buf_size, samples); } + virtual int avpicture_get_size(PixelFormat pix_fmt, int width, int height) { return ::avpicture_get_size(pix_fmt, width, height); } + virtual AVCodecContext *avcodec_alloc_context() { return ::avcodec_alloc_context(); } + virtual void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode) { ::avcodec_string(buf, buf_size, enc, encode); } + virtual void avcodec_get_context_defaults(AVCodecContext *s) { ::avcodec_get_context_defaults(s); } + + virtual AVCodecParserContext *av_parser_init(int codec_id) { return ::av_parser_init(codec_id); } + virtual int av_parser_parse2(AVCodecParserContext *s,AVCodecContext *avctx, uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, + int64_t pts, int64_t dts, int64_t pos) + { +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,21,0) + // API added on : 2009-03-05 + return ::av_parser_parse2(s, avctx, poutbuf, poutbuf_size, buf, buf_size, pts, dts, pos); +#else + return ::av_parser_parse(s, avctx, poutbuf, poutbuf_size, buf, buf_size, pts, dts); +#endif + } + virtual void av_parser_close(AVCodecParserContext *s) { ::av_parser_close(s); } + + virtual AVBitStreamFilterContext *av_bitstream_filter_init(const char *name) { return ::av_bitstream_filter_init(name); } + virtual int av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe) { return ::av_bitstream_filter_filter(bsfc, avctx, args, poutbuf, poutbuf_size, buf, buf_size, keyframe); } + virtual void av_bitstream_filter_close(AVBitStreamFilterContext *bsfc) { ::av_bitstream_filter_close(bsfc); } + + virtual void avpicture_free(AVPicture *picture) { ::avpicture_free(picture); } + virtual void av_free_packet(AVPacket *pkt) { ::av_free_packet(pkt); } + virtual int avpicture_alloc(AVPicture *picture, PixelFormat pix_fmt, int width, int height) { return ::avpicture_alloc(picture, pix_fmt, width, height); } + virtual int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic) { return ::avcodec_default_get_buffer(s, pic); } + virtual void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic) { ::avcodec_default_release_buffer(s, pic); } + virtual enum PixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum PixelFormat *fmt) { return ::avcodec_default_get_format(s, fmt); } + virtual int avcodec_thread_init(AVCodecContext *s, int thread_count) { return ::avcodec_thread_init(s, thread_count); } + virtual AVCodec *av_codec_next(AVCodec *c) { return ::av_codec_next(c); } + virtual AVAudioConvert *av_audio_convert_alloc(enum AVSampleFormat out_fmt, int out_channels, + enum AVSampleFormat in_fmt , int in_channels, + const float *matrix , int flags) + { return ::av_audio_convert_alloc(out_fmt, out_channels, in_fmt, in_channels, matrix, flags); } + virtual void av_audio_convert_free(AVAudioConvert *ctx) + { ::av_audio_convert_free(ctx); } + + virtual int av_audio_convert(AVAudioConvert *ctx, + void * const out[6], const int out_stride[6], + const void * const in[6], const int in_stride[6], int len) + { return ::av_audio_convert(ctx, out, out_stride, in, in_stride, len); } + + virtual int av_dup_packet(AVPacket *pkt) { return ::av_dup_packet(pkt); } + virtual void av_init_packet(AVPacket *pkt) { return ::av_init_packet(pkt); } + virtual int64_t avcodec_guess_channel_layout(int nb_channels, enum CodecID codec_id, const char *fmt_name) { return ::avcodec_guess_channel_layout(nb_channels, codec_id, fmt_name); } + + // DLL faking. + virtual bool ResolveExports() { return true; } + virtual bool Load() { + CLog::Log(LOGDEBUG, "DllAvCodec: Using libavcodec system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllAvCodec : public DllDynamic, DllAvCodecInterface +{ + DECLARE_DLL_WRAPPER(DllAvCodec, DLL_PATH_LIBAVCODEC) +#ifndef _LINUX + DEFINE_FUNC_ALIGNED1(void, __cdecl, avcodec_flush_buffers, AVCodecContext*) + DEFINE_FUNC_ALIGNED2(int, __cdecl, avcodec_open_dont_call, AVCodecContext*, AVCodec *) + DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_video2, AVCodecContext*, AVFrame*, int*, AVPacket*) + DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_audio3, AVCodecContext*, int16_t*, int*, AVPacket*) + DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_subtitle2, AVCodecContext*, AVSubtitle*, int*, AVPacket*) + DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_encode_audio, AVCodecContext*, uint8_t*, int, const short*) + DEFINE_FUNC_ALIGNED0(AVCodecContext*, __cdecl, avcodec_alloc_context) + DEFINE_FUNC_ALIGNED1(AVCodecParserContext*, __cdecl, av_parser_init, int) + DEFINE_FUNC_ALIGNED9(int, __cdecl, av_parser_parse2, AVCodecParserContext*,AVCodecContext*, uint8_t**, int*, const uint8_t*, int, int64_t, int64_t, int64_t) +#else + DEFINE_METHOD1(void, avcodec_flush_buffers, (AVCodecContext* p1)) + DEFINE_METHOD2(int, avcodec_open_dont_call, (AVCodecContext* p1, AVCodec *p2)) + DEFINE_METHOD4(int, avcodec_decode_video2, (AVCodecContext* p1, AVFrame *p2, int *p3, AVPacket *p4)) + DEFINE_METHOD4(int, avcodec_decode_audio3, (AVCodecContext* p1, int16_t *p2, int *p3, AVPacket *p4)) + DEFINE_METHOD4(int, avcodec_decode_subtitle2, (AVCodecContext* p1, AVSubtitle *p2, int *p3, AVPacket *p4)) + DEFINE_METHOD4(int, avcodec_encode_audio, (AVCodecContext* p1, uint8_t *p2, int p3, const short *p4)) + DEFINE_METHOD0(AVCodecContext*, avcodec_alloc_context) + DEFINE_METHOD1(AVCodecParserContext*, av_parser_init, (int p1)) + DEFINE_METHOD9(int, av_parser_parse2, (AVCodecParserContext* p1, AVCodecContext* p2, uint8_t** p3, int* p4, const uint8_t* p5, int p6, int64_t p7, int64_t p8, int64_t p9)) +#endif + DEFINE_METHOD1(int, av_dup_packet, (AVPacket *p1)) + DEFINE_METHOD1(void, av_init_packet, (AVPacket *p1)) + DEFINE_METHOD3(int64_t, avcodec_guess_channel_layout, (int p1, enum CodecID p2, const char *p3)) + + LOAD_SYMBOLS(); + + DEFINE_METHOD0(void, avcodec_register_all_dont_call) + DEFINE_METHOD1(AVCodec*, avcodec_find_decoder, (enum CodecID p1)) + DEFINE_METHOD1(AVCodec*, avcodec_find_encoder, (enum CodecID p1)) + DEFINE_METHOD1(int, avcodec_close_dont_call, (AVCodecContext *p1)) + DEFINE_METHOD0(AVFrame*, avcodec_alloc_frame) + DEFINE_METHOD5(int, avpicture_fill, (AVPicture *p1, uint8_t *p2, PixelFormat p3, int p4, int p5)) + DEFINE_METHOD3(int, avpicture_get_size, (PixelFormat p1, int p2, int p3)) + DEFINE_METHOD4(void, avcodec_string, (char *p1, int p2, AVCodecContext *p3, int p4)) + DEFINE_METHOD1(void, avcodec_get_context_defaults, (AVCodecContext *p1)) + DEFINE_METHOD1(void, av_parser_close, (AVCodecParserContext *p1)) + DEFINE_METHOD1(void, avpicture_free, (AVPicture *p1)) + DEFINE_METHOD1(AVBitStreamFilterContext*, av_bitstream_filter_init, (const char *p1)) + DEFINE_METHOD8(int, av_bitstream_filter_filter, (AVBitStreamFilterContext* p1, AVCodecContext* p2, const char* p3, uint8_t** p4, int* p5, const uint8_t* p6, int p7, int p8)) + DEFINE_METHOD1(void, av_bitstream_filter_close, (AVBitStreamFilterContext *p1)) + DEFINE_METHOD1(void, av_free_packet, (AVPacket *p1)) + DEFINE_METHOD4(int, avpicture_alloc, (AVPicture *p1, PixelFormat p2, int p3, int p4)) + DEFINE_METHOD2(int, avcodec_default_get_buffer, (AVCodecContext *p1, AVFrame *p2)) + DEFINE_METHOD2(void, avcodec_default_release_buffer, (AVCodecContext *p1, AVFrame *p2)) + DEFINE_METHOD2(enum PixelFormat, avcodec_default_get_format, (struct AVCodecContext *p1, const enum PixelFormat *p2)) + + DEFINE_METHOD2(int, avcodec_thread_init, (AVCodecContext *p1, int p2)) + DEFINE_METHOD1(AVCodec*, av_codec_next, (AVCodec *p1)) + DEFINE_METHOD6(AVAudioConvert*, av_audio_convert_alloc, (enum AVSampleFormat p1, int p2, + enum AVSampleFormat p3, int p4, + const float *p5, int p6)) + DEFINE_METHOD1(void, av_audio_convert_free, (AVAudioConvert *p1)); + DEFINE_METHOD6(int, av_audio_convert, (AVAudioConvert *p1, + void * const p2[6], const int p3[6], + const void * const p4[6], const int p5[6], int p6)) + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(avcodec_flush_buffers) + RESOLVE_METHOD_RENAME(avcodec_open,avcodec_open_dont_call) + RESOLVE_METHOD_RENAME(avcodec_close,avcodec_close_dont_call) + RESOLVE_METHOD(avcodec_find_decoder) + RESOLVE_METHOD(avcodec_find_encoder) + RESOLVE_METHOD(avcodec_alloc_frame) + RESOLVE_METHOD_RENAME(avcodec_register_all, avcodec_register_all_dont_call) + RESOLVE_METHOD(avpicture_fill) + RESOLVE_METHOD(avcodec_decode_video2) + RESOLVE_METHOD(avcodec_decode_audio3) + RESOLVE_METHOD(avcodec_decode_subtitle2) + RESOLVE_METHOD(avcodec_encode_audio) + RESOLVE_METHOD(avpicture_get_size) + RESOLVE_METHOD(avcodec_alloc_context) + RESOLVE_METHOD(avcodec_string) + RESOLVE_METHOD(avcodec_get_context_defaults) + RESOLVE_METHOD(av_parser_init) + RESOLVE_METHOD(av_parser_parse2) + RESOLVE_METHOD(av_parser_close) + RESOLVE_METHOD(av_bitstream_filter_init) + RESOLVE_METHOD(av_bitstream_filter_filter) + RESOLVE_METHOD(av_bitstream_filter_close) + RESOLVE_METHOD(avpicture_free) + RESOLVE_METHOD(avpicture_alloc) + RESOLVE_METHOD(av_free_packet) + RESOLVE_METHOD(avcodec_default_get_buffer) + RESOLVE_METHOD(avcodec_default_release_buffer) + RESOLVE_METHOD(avcodec_default_get_format) + RESOLVE_METHOD(avcodec_thread_init) + RESOLVE_METHOD(av_codec_next) + RESOLVE_METHOD(av_audio_convert_alloc) + RESOLVE_METHOD(av_audio_convert_free) + RESOLVE_METHOD(av_audio_convert) + RESOLVE_METHOD(av_dup_packet) + RESOLVE_METHOD(av_init_packet) + RESOLVE_METHOD(avcodec_guess_channel_layout) + END_METHOD_RESOLVE() + + /* dependencies of libavcodec */ + DllAvCore m_dllAvCore; + // DllAvUtil loaded implicitely by m_dllAvCore + +public: + int avcodec_open(AVCodecContext *avctx, AVCodec *codec) + { + return avcodec_open_dont_call(avctx,codec); + } + int avcodec_close(AVCodecContext *avctx) + { + return avcodec_close_dont_call(avctx); + } + void avcodec_register_all() + { + avcodec_register_all_dont_call(); + } + virtual bool Load() + { + if (!m_dllAvCore.Load()) + return false; + return DllDynamic::Load(); + } +}; + +#endif diff --git a/DllAvCore.h b/DllAvCore.h new file mode 100644 index 00000000..59a23437 --- /dev/null +++ b/DllAvCore.h @@ -0,0 +1,186 @@ +#pragma once +/* + * Copyright (C) 2005-2010 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, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#include "DynamicDll.h" +#include "DllAvUtil.h" +#include "utils/log.h" + +#ifndef AV_NOWARN_DEPRECATED +#define AV_NOWARN_DEPRECATED +#endif + +extern "C" { +#ifdef USE_EXTERNAL_FFMPEG + #ifdef HAVE_LIBAVUTIL_SAMPLEFMT_H + // libavcore was merged to libavutil on 2010-02-15 + #include + #include + #endif + + #ifdef HAVE_LIBAVCORE_AVCORE_H + #include + #endif + #ifdef HAVE_LIBAVCORE_SAMPLEFMT_H + #include + #endif + + /* Needed for old FFmpeg versions as used below */ + #ifdef HAVE_LIBAVCODEC_AVCODEC_H + #include + #else + #include + #endif +#else + #include "libavcore/avcore.h" + #include "libavcore/samplefmt.h" +#endif +} + +/* Compatibility for old external FFmpeg versions. */ + +#ifdef USE_EXTERNAL_FFMPEG + +#ifndef LIBAVCORE_VERSION_INT +// API added on: 2010-07-21, removed on 2010-02-15 +#define LIBAVCORE_VERSION_INT 0 +#endif + +#ifndef AV_SAMPLE_FMT_NONE +// API added on: 2010-11-02 +#define AVSampleFormat SampleFormat +#define AV_SAMPLE_FMT_NONE SAMPLE_FMT_NONE +#define AV_SAMPLE_FMT_U8 SAMPLE_FMT_U8 +#define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16 +#define AV_SAMPLE_FMT_S32 SAMPLE_FMT_S32 +#define AV_SAMPLE_FMT_FLT SAMPLE_FMT_FLT +#define AV_SAMPLE_FMT_DBL SAMPLE_FMT_DBL +#endif + +#ifndef AV_CH_FRONT_LEFT +// API added on: 2010-11-21 +#define AV_CH_FRONT_LEFT CH_FRONT_LEFT +#define AV_CH_FRONT_RIGHT CH_FRONT_RIGHT +#define AV_CH_FRONT_CENTER CH_FRONT_CENTER +#define AV_CH_LOW_FREQUENCY CH_LOW_FREQUENCY +#define AV_CH_BACK_LEFT CH_BACK_LEFT +#define AV_CH_BACK_RIGHT CH_BACK_RIGHT +#define AV_CH_FRONT_LEFT_OF_CENTER CH_FRONT_LEFT_OF_CENTER +#define AV_CH_FRONT_RIGHT_OF_CENTER CH_FRONT_RIGHT_OF_CENTER +#define AV_CH_BACK_CENTER CH_BACK_CENTER +#define AV_CH_SIDE_LEFT CH_SIDE_LEFT +#define AV_CH_SIDE_RIGHT CH_SIDE_RIGHT +#define AV_CH_TOP_CENTER CH_TOP_CENTER +#define AV_CH_TOP_FRONT_LEFT CH_TOP_FRONT_LEFT +#define AV_CH_TOP_FRONT_CENTER CH_TOP_FRONT_CENTER +#define AV_CH_TOP_FRONT_RIGHT CH_TOP_FRONT_RIGHT +#define AV_CH_TOP_BACK_LEFT CH_TOP_BACK_LEFT +#define AV_CH_TOP_BACK_CENTER CH_TOP_BACK_CENTER +#define AV_CH_TOP_BACK_RIGHT CH_TOP_BACK_RIGHT +#define AV_CH_STEREO_LEFT CH_STEREO_LEFT +#define AV_CH_STEREO_RIGHT CH_STEREO_RIGHT + +#define AV_CH_LAYOUT_NATIVE CH_LAYOUT_NATIVE + +#define AV_CH_LAYOUT_MONO CH_LAYOUT_MONO +#define AV_CH_LAYOUT_STEREO CH_LAYOUT_STEREO +#define AV_CH_LAYOUT_2_1 CH_LAYOUT_2_1 +#define AV_CH_LAYOUT_SURROUND CH_LAYOUT_SURROUND +#define AV_CH_LAYOUT_4POINT0 CH_LAYOUT_4POINT0 +#define AV_CH_LAYOUT_2_2 CH_LAYOUT_2_2 +#define AV_CH_LAYOUT_QUAD CH_LAYOUT_QUAD +#define AV_CH_LAYOUT_5POINT0 CH_LAYOUT_5POINT0 +#define AV_CH_LAYOUT_5POINT1 CH_LAYOUT_5POINT1 +#define AV_CH_LAYOUT_5POINT0_BACK CH_LAYOUT_5POINT0_BACK +#define AV_CH_LAYOUT_5POINT1_BACK CH_LAYOUT_5POINT1_BACK +#define AV_CH_LAYOUT_7POINT0 CH_LAYOUT_7POINT0 +#define AV_CH_LAYOUT_7POINT1 CH_LAYOUT_7POINT1 +#define AV_CH_LAYOUT_7POINT1_WIDE CH_LAYOUT_7POINT1_WIDE +#define AV_CH_LAYOUT_STEREO_DOWNMIX CH_LAYOUT_STEREO_DOWNMIX +#endif + +#endif // USE_EXTERNAL_FFMPEG + +class DllAvCoreInterface +{ +public: + virtual ~DllAvCoreInterface() {} + virtual int av_get_bits_per_sample_fmt(enum AVSampleFormat sample_fmt) = 0; +}; + +#if (defined USE_EXTERNAL_FFMPEG) + +// Use direct layer +class DllAvCore : public DllDynamic, DllAvCoreInterface +{ +public: + virtual ~DllAvCore() {} +#if LIBAVCORE_VERSION_INT >= AV_VERSION_INT(0,12,0) || LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(50,38,0) + // API added on: 2010-11-02, moved to libavutil on 2010-02-15 + virtual int av_get_bits_per_sample_fmt(enum AVSampleFormat sample_fmt) { return ::av_get_bits_per_sample_fmt(sample_fmt); } +#else + // from avcodec.h + virtual int av_get_bits_per_sample_fmt(enum AVSampleFormat sample_fmt) { return ::av_get_bits_per_sample_format(sample_fmt); } +#endif + + // DLL faking. + virtual bool ResolveExports() { return true; } + virtual bool Load() { +#if LIBAVCORE_VERSION_INT > 0 + CLog::Log(LOGDEBUG, "DllAvCore: Using libavcore system library"); +#endif + return true; + } + virtual void Unload() {} +}; + +#else + +class DllAvCore : public DllDynamic, DllAvCoreInterface +{ + DECLARE_DLL_WRAPPER(DllAvCore, DLL_PATH_LIBAVCORE) + + LOAD_SYMBOLS() + + DEFINE_METHOD1(int, av_get_bits_per_sample_fmt, (enum AVSampleFormat p1)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(av_get_bits_per_sample_fmt) + END_METHOD_RESOLVE() + + /* dependency of libavcore */ + DllAvUtil m_dllAvUtil; + +public: + virtual bool Load() + { + if (!m_dllAvUtil.Load()) + return false; + return DllDynamic::Load(); + } +}; + +#endif + diff --git a/DllAvFilter.h b/DllAvFilter.h new file mode 100644 index 00000000..9cf91e4d --- /dev/null +++ b/DllAvFilter.h @@ -0,0 +1,331 @@ +#pragma once +/* + * Copyright (C) 2005-2011 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" +#endif +#include "DynamicDll.h" +#include "DllAvCore.h" +#include "DllAvCodec.h" +#include "utils/log.h" + +#ifndef AV_NOWARN_DEPRECATED +#define AV_NOWARN_DEPRECATED +#endif + +extern "C" { +#ifndef HAVE_MMX +#define HAVE_MMX +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#ifndef __GNUC__ +#pragma warning(disable:4244) +#endif + +#if (defined USE_EXTERNAL_FFMPEG) + #if (defined HAVE_LIBAVFILTER_AVFILTER_H) + #include + #elif (defined HAVE_FFMPEG_AVFILTER_H) + #include + #endif + /* for av_vsrc_buffer_add_frame */ + #if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,8,0) + #include + #elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,7,0) + int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, + AVFrame *frame); + #elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,3,0) + int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, + AVFrame *frame, int64_t pts); + #else + int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, + AVFrame *frame, int64_t pts, AVRational pixel_aspect); + #endif +#else + #include "libavfilter/avfiltergraph.h" +#endif +} + +//#include "threads/SingleLock.h" + +class DllAvFilterInterface +{ +public: + virtual ~DllAvFilterInterface() {} + virtual int avfilter_open(AVFilterContext **filter_ctx, AVFilter *filter, const char *inst_name)=0; + virtual void avfilter_free(AVFilterContext *filter)=0; + virtual void avfilter_graph_free(AVFilterGraph **graph)=0; + virtual int avfilter_graph_create_filter(AVFilterContext **filt_ctx, AVFilter *filt, const char *name, const char *args, void *opaque, AVFilterGraph *graph_ctx)=0; + virtual AVFilter *avfilter_get_by_name(const char *name)=0; + virtual AVFilterGraph *avfilter_graph_alloc(void)=0; + virtual AVFilterInOut *avfilter_inout_alloc()=0; + virtual void avfilter_inout_free(AVFilterInOut **inout)=0; + virtual int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs, AVFilterInOut **outputs, void *log_ctx)=0; + virtual int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx)=0; + virtual int avfilter_poll_frame(AVFilterLink *link)=0; + virtual int avfilter_request_frame(AVFilterLink *link)=0; +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,13,0) + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame, int flags)=0; +#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,7,0) + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame)=0; +#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,3,0) + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame, int64_t pts)=0; +#else + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame, int64_t pts, AVRational pixel_aspect)=0; +#endif + virtual AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link, int perms, int w, int h)=0; + virtual void avfilter_unref_buffer(AVFilterBufferRef *ref)=0; + virtual int avfilter_link(AVFilterContext *src, unsigned srcpad, AVFilterContext *dst, unsigned dstpad)=0; +}; + +#if (defined USE_EXTERNAL_FFMPEG) +// Use direct mapping +class DllAvFilter : public DllDynamic, DllAvFilterInterface +{ +public: + virtual ~DllAvFilter() {} + virtual int avfilter_open(AVFilterContext **filter_ctx, AVFilter *filter, const char *inst_name) + { + return ::avfilter_open(filter_ctx, filter, inst_name); + } + virtual void avfilter_free(AVFilterContext *filter) + { + ::avfilter_free(filter); + } + virtual void avfilter_graph_free(AVFilterGraph **graph) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(1,76,0) + ::avfilter_graph_free(graph); +#else + ::avfilter_graph_free(*graph); + *graph = NULL; +#endif + } + void avfilter_register_all() + { + ::avfilter_register_all(); + } + virtual int avfilter_graph_create_filter(AVFilterContext **filt_ctx, AVFilter *filt, const char *name, const char *args, void *opaque, AVFilterGraph *graph_ctx) { return ::avfilter_graph_create_filter(filt_ctx, filt, name, args, opaque, graph_ctx); } + virtual AVFilter *avfilter_get_by_name(const char *name) { return ::avfilter_get_by_name(name); } + virtual AVFilterGraph *avfilter_graph_alloc() { return ::avfilter_graph_alloc(); } + virtual AVFilterInOut *avfilter_inout_alloc() + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,17,0) + return ::avfilter_inout_alloc(); +#else + return (AVFilterInOut*)::av_mallocz(sizeof(AVFilterInOut)); +#endif + } + virtual void avfilter_inout_free(AVFilterInOut **inout) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,17,0) + ::avfilter_inout_free(inout); +#else + *inout = NULL; +#endif + } + virtual int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs, AVFilterInOut **outputs, void *log_ctx) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,16,0) + return ::avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx); +#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,15,1) + return ::avfilter_graph_parse(graph, filters, *inputs, *outputs, log_ctx); +#else + return ::avfilter_graph_parse(graph, filters, *inputs, *outputs, (AVClass*)log_ctx); +#endif + } + virtual int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,15,1) + return ::avfilter_graph_config(graphctx, log_ctx); +#else + return ::avfilter_graph_config(graphctx, (AVClass*)log_ctx); +#endif + } + virtual int avfilter_poll_frame(AVFilterLink *link) { return ::avfilter_poll_frame(link); } + virtual int avfilter_request_frame(AVFilterLink *link) { return ::avfilter_request_frame(link); } +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,13,0) + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame, int flags) { return ::av_vsrc_buffer_add_frame(buffer_filter, frame, flags); } +#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,7,0) + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame) { return ::av_vsrc_buffer_add_frame(buffer_filter, frame); } +#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,3,0) + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame, int64_t pts) { return ::av_vsrc_buffer_add_frame(buffer_filter, frame, pts); } +#else + virtual int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame, int64_t pts, AVRational pixel_aspect) { return ::av_vsrc_buffer_add_frame(buffer_filter, frame, pts, pixel_aspect); } +#endif + virtual AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link, int perms, int w, int h) { return ::avfilter_get_video_buffer(link, perms, w, h); } + virtual void avfilter_unref_buffer(AVFilterBufferRef *ref) { ::avfilter_unref_buffer(ref); } + virtual int avfilter_link(AVFilterContext *src, unsigned srcpad, AVFilterContext *dst, unsigned dstpad) { return ::avfilter_link(src, srcpad, dst, dstpad); } + // DLL faking. + virtual bool ResolveExports() { return true; } + virtual bool Load() { + CLog::Log(LOGDEBUG, "DllAvFilter: Using libavfilter system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllAvFilter : public DllDynamic, DllAvFilterInterface +{ + DECLARE_DLL_WRAPPER(DllAvFilter, DLL_PATH_LIBAVFILTER) + + LOAD_SYMBOLS() + + DEFINE_METHOD3(int, avfilter_open_dont_call, (AVFilterContext **p1, AVFilter *p2, const char *p3)) + DEFINE_METHOD1(void, avfilter_free_dont_call, (AVFilterContext *p1)) +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(1,76,0) + DEFINE_METHOD1(void, avfilter_graph_free_dont_call, (AVFilterGraph **p1)) +#else + DEFINE_METHOD1(void, avfilter_graph_free_dont_call, (AVFilterGraph *p1)) +#endif + DEFINE_METHOD0(void, avfilter_register_all_dont_call) + DEFINE_METHOD6(int, avfilter_graph_create_filter, (AVFilterContext **p1, AVFilter *p2, const char *p3, const char *p4, void *p5, AVFilterGraph *p6)) + DEFINE_METHOD1(AVFilter*, avfilter_get_by_name, (const char *p1)) + DEFINE_METHOD0(AVFilterGraph*, avfilter_graph_alloc) +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,17,0) + DEFINE_METHOD0(AVFilterInOut*, avfilter_inout_alloc_dont_call) + DEFINE_METHOD1(void, avfilter_inout_free_dont_call, (AVFilterInOut **p1)) +#endif +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,16,0) + DEFINE_METHOD5(int, avfilter_graph_parse_dont_call, (AVFilterGraph *p1, const char *p2, AVFilterInOut **p3, AVFilterInOut **p4, void *p5)) +#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,15,1) + DEFINE_METHOD5(int, avfilter_graph_parse_dont_call, (AVFilterGraph *p1, const char *p2, AVFilterInOut *p3, AVFilterInOut *p4, void *p5)) +#else + DEFINE_METHOD5(int, avfilter_graph_parse_dont_call, (AVFilterGraph *p1, const char *p2, AVFilterInOut *p3, AVFilterInOut *p4, AVClass *p5)) +#endif +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,15,1) + DEFINE_METHOD2(int, avfilter_graph_config_dont_call, (AVFilterGraph *p1, void *p2)) +#else + DEFINE_METHOD2(int, avfilter_graph_config_dont_call, (AVFilterGraph *p1, AVClass *p2)) +#endif +#ifdef _LINUX + DEFINE_METHOD1(int, avfilter_poll_frame, (AVFilterLink *p1)) + DEFINE_METHOD1(int, avfilter_request_frame, (AVFilterLink* p1)) +#else + DEFINE_FUNC_ALIGNED1(int, __cdecl, avfilter_poll_frame, AVFilterLink *) + DEFINE_FUNC_ALIGNED1(int, __cdecl, avfilter_request_frame, AVFilterLink*) +#endif +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,13,0) + DEFINE_METHOD3(int, av_vsrc_buffer_add_frame, (AVFilterContext *p1, AVFrame *p2, int p3)) +#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,7,0) + DEFINE_METHOD2(int, av_vsrc_buffer_add_frame, (AVFilterContext *p1, AVFrame *p2)) +#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,3,0) + DEFINE_METHOD3(int, av_vsrc_buffer_add_frame, (AVFilterContext *p1, AVFrame *p2, int64_t p3)) +#else + DEFINE_METHOD4(int, av_vsrc_buffer_add_frame, (AVFilterContext *p1, AVFrame *p2, int64_t p3, AVRational p4)) +#endif + DEFINE_METHOD4(AVFilterBufferRef*, avfilter_get_video_buffer, (AVFilterLink *p1, int p2, int p3, int p4)) + DEFINE_METHOD1(void, avfilter_unref_buffer, (AVFilterBufferRef *p1)) + DEFINE_METHOD4(int, avfilter_link, (AVFilterContext *p1, unsigned p2, AVFilterContext *p3, unsigned p4)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(avfilter_open, avfilter_open_dont_call) + RESOLVE_METHOD_RENAME(avfilter_free, avfilter_free_dont_call) + RESOLVE_METHOD_RENAME(avfilter_graph_free, avfilter_graph_free_dont_call) + RESOLVE_METHOD_RENAME(avfilter_register_all, avfilter_register_all_dont_call) + RESOLVE_METHOD(avfilter_graph_create_filter) + RESOLVE_METHOD(avfilter_get_by_name) + RESOLVE_METHOD(avfilter_graph_alloc) +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,17,0) + RESOLVE_METHOD_RENAME(avfilter_inout_alloc, avfilter_inout_alloc_dont_call) + RESOLVE_METHOD_RENAME(avfilter_inout_free, avfilter_inout_free_dont_call) +#endif + RESOLVE_METHOD_RENAME(avfilter_graph_parse, avfilter_graph_parse_dont_call) + RESOLVE_METHOD_RENAME(avfilter_graph_config, avfilter_graph_config_dont_call) + RESOLVE_METHOD(avfilter_poll_frame) + RESOLVE_METHOD(avfilter_request_frame) + RESOLVE_METHOD(av_vsrc_buffer_add_frame) + RESOLVE_METHOD(avfilter_get_video_buffer) + RESOLVE_METHOD(avfilter_unref_buffer) + RESOLVE_METHOD(avfilter_link) + END_METHOD_RESOLVE() + + /* dependencies of libavfilter */ + DllAvUtil m_dllAvUtil; + +public: + int avfilter_open(AVFilterContext **filter_ctx, AVFilter *filter, const char *inst_name) + { + return avfilter_open_dont_call(filter_ctx, filter, inst_name); + } + void avfilter_free(AVFilterContext *filter) + { + avfilter_free_dont_call(filter); + } + void avfilter_graph_free(AVFilterGraph **graph) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(1,76,0) + avfilter_graph_free_dont_call(graph); +#else + avfilter_graph_free_dont_call(*graph); + m_dllAvUtil.av_freep(graph); +#endif + } + void avfilter_register_all() + { + avfilter_register_all_dont_call(); + } + AVFilterInOut* avfilter_inout_alloc() + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,17,0) + return avfilter_inout_alloc_dont_call(); +#else + return (AVFilterInOut*)m_dllAvUtil.av_mallocz(sizeof(AVFilterInOut)); +#endif + } + int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs, AVFilterInOut **outputs, void *log_ctx) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,16,0) + return avfilter_graph_parse_dont_call(graph, filters, inputs, outputs, log_ctx); +#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,15,1) + return avfilter_graph_parse_dont_call(graph, filters, *inputs, *outputs, log_ctx); +#else + return avfilter_graph_parse_dont_call(graph, filters, *inputs, *outputs, (AVClass*)log_ctx); +#endif + } + void avfilter_inout_free(AVFilterInOut **inout) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,17,0) + avfilter_inout_free_dont_call(inout); +#else + *inout = NULL; +#endif + } + int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) + { +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,15,1) + return avfilter_graph_config_dont_call(graphctx, log_ctx); +#else + return avfilter_graph_config_dont_call(graphctx, (AVClass*)log_ctx); +#endif + } + virtual bool Load() + { + if (!m_dllAvUtil.Load()) + return false; + return DllDynamic::Load(); + } +}; +#endif diff --git a/DllAvFormat.h b/DllAvFormat.h new file mode 100644 index 00000000..8b4f46b2 --- /dev/null +++ b/DllAvFormat.h @@ -0,0 +1,333 @@ +#pragma once +/* + * Copyright (C) 2005-2010 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" +#endif +#include "DynamicDll.h" +#include "DllAvCodec.h" + +#ifndef AV_NOWARN_DEPRECATED +#define AV_NOWARN_DEPRECATED +#endif + +extern "C" { +#ifndef HAVE_MMX +#define HAVE_MMX +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#ifndef __GNUC__ +#pragma warning(disable:4244) +#endif +#if (defined USE_EXTERNAL_FFMPEG) + #if (defined HAVE_LIBAVFORMAT_AVFORMAT_H) + #include + #else + #include + #endif + /* libavformat/riff.h is not a public header, so include it here */ + //#include "libavformat/riff.h" +#else + #include "libavformat/avformat.h" + //#include "libavformat/riff.h" +#endif +} + +/* Flag introduced without a version bump */ +#ifndef AVSEEK_FORCE +#define AVSEEK_FORCE 0x20000 +#endif + +typedef int64_t offset_t; + +class DllAvFormatInterface +{ +public: + virtual ~DllAvFormatInterface() {} + virtual void av_register_all_dont_call(void)=0; + virtual AVInputFormat *av_find_input_format(const char *short_name)=0; + virtual int url_feof(ByteIOContext *s)=0; + virtual AVMetadataTag *av_metadata_get(AVMetadata *m, const char *key, const AVMetadataTag *prev, int flags)=0; + virtual void av_close_input_file(AVFormatContext *s)=0; + virtual void av_close_input_stream(AVFormatContext *s)=0; + virtual int av_read_frame(AVFormatContext *s, AVPacket *pkt)=0; + virtual int av_read_play(AVFormatContext *s)=0; + virtual int av_read_pause(AVFormatContext *s)=0; + virtual int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)=0; +#if (!defined USE_EXTERNAL_FFMPEG) + virtual int av_find_stream_info_dont_call(AVFormatContext *ic)=0; +#endif + virtual int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap)=0; + virtual void url_set_interrupt_cb(URLInterruptCB *interrupt_cb)=0; + virtual int av_open_input_stream(AVFormatContext **ic_ptr, ByteIOContext *pb, const char *filename, AVInputFormat *fmt, AVFormatParameters *ap)=0; + virtual AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)=0; + virtual AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)=0; + virtual int av_probe_input_buffer(ByteIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size)=0; + virtual void dump_format(AVFormatContext *ic, int index, const char *url, int is_output)=0; + virtual int url_fdopen(ByteIOContext **s, URLContext *h)=0; + virtual int url_fopen(ByteIOContext **s, const char *filename, int flags)=0; + virtual int url_fclose(ByteIOContext *s)=0; + virtual int url_open_dyn_buf(ByteIOContext **s)=0; + virtual int url_close_dyn_buf(ByteIOContext *s, uint8_t **pbuffer)=0; + virtual offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence)=0; + virtual int get_buffer(ByteIOContext *s, unsigned char *buf, int size)=0; + virtual int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size)=0; + virtual void put_byte(ByteIOContext *s, int b)=0; + virtual void put_buffer(ByteIOContext *s, const unsigned char *buf, int size)=0; + virtual void put_be24(ByteIOContext *s, unsigned int val)=0; + virtual void put_be32(ByteIOContext *s, unsigned int val)=0; + virtual void put_be16(ByteIOContext *s, unsigned int val)=0; + virtual AVFormatContext *avformat_alloc_context(void)=0; + virtual AVStream *av_new_stream(AVFormatContext *s, int id)=0; + virtual AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type)=0; + virtual int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap)=0; + virtual ByteIOContext *av_alloc_put_byte(unsigned char *buffer, int buffer_size, int write_flag, void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + offset_t (*seek)(void *opaque, offset_t offset, int whence))=0; + virtual int av_write_header (AVFormatContext *s)=0; + virtual int av_write_trailer(AVFormatContext *s)=0; + virtual int av_write_frame (AVFormatContext *s, AVPacket *pkt)=0; + virtual int av_metadata_set2(AVMetadata **pm, const char *key, const char *value, int flags)=0; + virtual int64_t av_gettime(void)=0; +}; + +#if (defined USE_EXTERNAL_FFMPEG) + +// Use direct mapping +class DllAvFormat : public DllDynamic, DllAvFormatInterface +{ +public: + virtual ~DllAvFormat() {} + virtual void av_register_all() + { + return ::av_register_all(); + } + virtual void av_register_all_dont_call() { *(int* )0x0 = 0; } + virtual AVInputFormat *av_find_input_format(const char *short_name) { return ::av_find_input_format(short_name); } + virtual int url_feof(ByteIOContext *s) { return ::url_feof(s); } +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,31,0) + // API added on: 2009-03-01 + virtual AVMetadataTag *av_metadata_get(AVMetadata *m, const char *key, const AVMetadataTag *prev, int flags){ return ::av_metadata_get(m, key, prev, flags); } +#else + virtual AVMetadataTag *av_metadata_get(AVMetadata *m, const char *key, const AVMetadataTag *prev, int flags){ return NULL; } +#endif + virtual void av_close_input_file(AVFormatContext *s) { ::av_close_input_file(s); } + virtual void av_close_input_stream(AVFormatContext *s) { ::av_close_input_stream(s); } + virtual int av_read_frame(AVFormatContext *s, AVPacket *pkt) { return ::av_read_frame(s, pkt); } + virtual int av_read_play(AVFormatContext *s) { return ::av_read_play(s); } + virtual int av_read_pause(AVFormatContext *s) { return ::av_read_pause(s); } + virtual int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { return ::av_seek_frame(s, stream_index, timestamp, flags); } + virtual int av_find_stream_info(AVFormatContext *ic) + { + return ::av_find_stream_info(ic); + } + virtual int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap) { return ::av_open_input_file(ic_ptr, filename, fmt, buf_size, ap); } + virtual void url_set_interrupt_cb(URLInterruptCB *interrupt_cb) { ::url_set_interrupt_cb(interrupt_cb); } + virtual int av_open_input_stream(AVFormatContext **ic_ptr, ByteIOContext *pb, const char *filename, AVInputFormat *fmt, AVFormatParameters *ap) { return ::av_open_input_stream(ic_ptr, pb, filename, fmt, ap); } + virtual AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened) {return ::av_probe_input_format(pd, is_opened); } + virtual AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max) {*score_max = 100; return ::av_probe_input_format(pd, is_opened); } // Use av_probe_input_format, this is not exported by ffmpeg's headers +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,98,0) + // API added on: 2010-02-08 + virtual int av_probe_input_buffer(ByteIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size) { return ::av_probe_input_buffer(pb, fmt, filename, logctx, offset, max_probe_size); } +#else + virtual int av_probe_input_buffer(ByteIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size) { return -1; } +#endif + virtual void dump_format(AVFormatContext *ic, int index, const char *url, int is_output) { ::dump_format(ic, index, url, is_output); } + virtual int url_fdopen(ByteIOContext **s, URLContext *h) { return ::url_fdopen(s, h); } + virtual int url_fopen(ByteIOContext **s, const char *filename, int flags) { return ::url_fopen(s, filename, flags); } + virtual int url_fclose(ByteIOContext *s) { return ::url_fclose(s); } + virtual int url_open_dyn_buf(ByteIOContext **s) { return ::url_open_dyn_buf(s); } + virtual int url_close_dyn_buf(ByteIOContext *s, uint8_t **pbuffer) { return ::url_close_dyn_buf(s, pbuffer); } + virtual offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence) { return ::url_fseek(s, offset, whence); } + virtual int get_buffer(ByteIOContext *s, unsigned char *buf, int size) { return ::get_buffer(s, buf, size); } + virtual int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size) { return ::get_partial_buffer(s, buf, size); } + virtual void put_byte(ByteIOContext *s, int b) { ::put_byte(s, b); } + virtual void put_buffer(ByteIOContext *s, const unsigned char *buf, int size) { ::put_buffer(s, buf, size); } + virtual void put_be24(ByteIOContext *s, unsigned int val) { ::put_be24(s, val); } + virtual void put_be32(ByteIOContext *s, unsigned int val) { ::put_be32(s, val); } + virtual void put_be16(ByteIOContext *s, unsigned int val) { ::put_be16(s, val); } + virtual AVFormatContext *avformat_alloc_context() { return ::avformat_alloc_context(); } + virtual AVStream *av_new_stream(AVFormatContext *s, int id) { return ::av_new_stream(s, id); } +#if LIBAVFORMAT_VERSION_INT < (52<<16 | 45<<8) + virtual AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type) { return ::guess_format(short_name, filename, mime_type); } +#else + virtual AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type) { return ::av_guess_format(short_name, filename, mime_type); } +#endif + virtual int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap) { return ::av_set_parameters(s, ap); } + virtual ByteIOContext *av_alloc_put_byte(unsigned char *buffer, int buffer_size, int write_flag, void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + offset_t (*seek)(void *opaque, offset_t offset, int whence)) { return ::av_alloc_put_byte(buffer, buffer_size, write_flag, opaque, read_packet, write_packet, seek); } + virtual int av_write_header (AVFormatContext *s) { return ::av_write_header (s); } + virtual int av_write_trailer(AVFormatContext *s) { return ::av_write_trailer(s); } + virtual int av_write_frame (AVFormatContext *s, AVPacket *pkt) { return ::av_write_frame(s, pkt); } +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,43,0) + // API added on: 2009-12-13 + virtual int av_metadata_set2(AVMetadata **pm, const char *key, const char *value, int flags) { return ::av_metadata_set2(pm, key, value, flags); } +#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,31,0) + // API added on: 2009-03-01 + virtual int av_metadata_set2(AVMetadata **pm, const char *key, const char *value, int flags) { return ::av_metadata_set(pm, key, value); } +#else + virtual int av_metadata_set2(AVMetadata **pm, const char *key, const char *value, int flags) { return -1; } +#endif + virtual int64_t av_gettime(void) { return ::av_gettime(); } + + // DLL faking. + virtual bool ResolveExports() { return true; } + virtual bool Load() { + CLog::Log(LOGDEBUG, "DllAvFormat: Using libavformat system library"); + return true; + } + virtual void Unload() {} +}; + +#else + +class DllAvFormat : public DllDynamic, DllAvFormatInterface +{ + DECLARE_DLL_WRAPPER(DllAvFormat, DLL_PATH_LIBAVFORMAT) + + LOAD_SYMBOLS() + + DEFINE_METHOD0(void, av_register_all_dont_call) + DEFINE_METHOD1(AVInputFormat*, av_find_input_format, (const char *p1)) + DEFINE_METHOD1(int, url_feof, (ByteIOContext *p1)) + DEFINE_METHOD4(AVMetadataTag*, av_metadata_get, (AVMetadata *p1, const char *p2, const AVMetadataTag *p3, int p4)) + DEFINE_METHOD1(void, av_close_input_file, (AVFormatContext *p1)) + DEFINE_METHOD1(void, av_close_input_stream, (AVFormatContext *p1)) + DEFINE_METHOD1(int, av_read_play, (AVFormatContext *p1)) + DEFINE_METHOD1(int, av_read_pause, (AVFormatContext *p1)) + DEFINE_FUNC_ALIGNED2(int, __cdecl, av_read_frame, AVFormatContext *, AVPacket *) + DEFINE_FUNC_ALIGNED4(int, __cdecl, av_seek_frame, AVFormatContext*, int, int64_t, int) + DEFINE_FUNC_ALIGNED1(int, __cdecl, av_find_stream_info_dont_call, AVFormatContext*) + DEFINE_FUNC_ALIGNED5(int, __cdecl, av_open_input_file, AVFormatContext**, const char *, AVInputFormat *, int, AVFormatParameters *) + DEFINE_FUNC_ALIGNED5(int,__cdecl, av_open_input_stream, AVFormatContext **, ByteIOContext *, const char *, AVInputFormat *, AVFormatParameters *) + DEFINE_FUNC_ALIGNED2(AVInputFormat*, __cdecl, av_probe_input_format, AVProbeData*, int) + DEFINE_FUNC_ALIGNED3(AVInputFormat*, __cdecl, av_probe_input_format2, AVProbeData*, int, int*) + DEFINE_FUNC_ALIGNED6(int, __cdecl, av_probe_input_buffer, ByteIOContext *, AVInputFormat **, const char *, void *, unsigned int, unsigned int) + DEFINE_FUNC_ALIGNED3(int, __cdecl, get_buffer, ByteIOContext*, unsigned char *, int) + DEFINE_FUNC_ALIGNED3(int, __cdecl, get_partial_buffer, ByteIOContext*, unsigned char *, int) + DEFINE_FUNC_ALIGNED2(void, __cdecl, put_byte, ByteIOContext*, int) + DEFINE_FUNC_ALIGNED3(void, __cdecl, put_buffer, ByteIOContext*, const unsigned char *, int) + DEFINE_FUNC_ALIGNED2(void, __cdecl, put_be24, ByteIOContext*, unsigned int) + DEFINE_FUNC_ALIGNED2(void, __cdecl, put_be32, ByteIOContext*, unsigned int) + DEFINE_FUNC_ALIGNED2(void, __cdecl, put_be16, ByteIOContext*, unsigned int) + DEFINE_METHOD1(void, url_set_interrupt_cb, (URLInterruptCB *p1)) + DEFINE_METHOD4(void, dump_format, (AVFormatContext *p1, int p2, const char *p3, int p4)) + DEFINE_METHOD2(int, url_fdopen, (ByteIOContext **p1, URLContext *p2)) + DEFINE_METHOD3(int, url_fopen, (ByteIOContext **p1, const char *p2, int p3)) + DEFINE_METHOD1(int, url_fclose, (ByteIOContext *p1)) + DEFINE_METHOD1(int, url_open_dyn_buf, (ByteIOContext **p1)) + DEFINE_METHOD2(int, url_close_dyn_buf, (ByteIOContext *p1, uint8_t **p2)) + DEFINE_METHOD3(offset_t, url_fseek, (ByteIOContext *p1, offset_t p2, int p3)) + DEFINE_METHOD0(AVFormatContext *, avformat_alloc_context) + DEFINE_METHOD2(AVStream *, av_new_stream, (AVFormatContext *p1, int p2)) +#if LIBAVFORMAT_VERSION_INT < (52<<16 | 45<<8) + DEFINE_METHOD3(AVOutputFormat *, guess_format, (const char *p1, const char *p2, const char *p3)) +#else + DEFINE_METHOD3(AVOutputFormat *, av_guess_format, (const char *p1, const char *p2, const char *p3)) +#endif + DEFINE_METHOD2(int, av_set_parameters, (AVFormatContext *p1, AVFormatParameters *p2)); + DEFINE_METHOD7(ByteIOContext *, av_alloc_put_byte, (unsigned char *p1, int p2, int p3, void *p4, + int(*p5)(void *opaque, uint8_t *buf, int buf_size), + int(*p6)(void *opaque, uint8_t *buf, int buf_size), + offset_t(*p7)(void *opaque, offset_t offset, int whence))) + DEFINE_METHOD1(int, av_write_header , (AVFormatContext *p1)) + DEFINE_METHOD1(int, av_write_trailer, (AVFormatContext *p1)) + DEFINE_METHOD2(int, av_write_frame , (AVFormatContext *p1, AVPacket *p2)) + DEFINE_METHOD4(int, av_metadata_set2, (AVMetadata **p1, const char *p2, const char *p3, int p4)) + DEFINE_METHOD0(int64_t, av_gettime); + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(av_register_all, av_register_all_dont_call) + RESOLVE_METHOD(av_find_input_format) + RESOLVE_METHOD(url_feof) + RESOLVE_METHOD(av_metadata_get) + RESOLVE_METHOD(av_close_input_file) + RESOLVE_METHOD(av_close_input_stream) + RESOLVE_METHOD(av_read_frame) + RESOLVE_METHOD(av_read_play) + RESOLVE_METHOD(av_read_pause) + RESOLVE_METHOD(av_seek_frame) + RESOLVE_METHOD_RENAME(av_find_stream_info, av_find_stream_info_dont_call) + RESOLVE_METHOD(av_open_input_file) + RESOLVE_METHOD(url_set_interrupt_cb) + RESOLVE_METHOD(av_open_input_stream) + RESOLVE_METHOD(av_probe_input_format) + RESOLVE_METHOD(av_probe_input_format2) + RESOLVE_METHOD(av_probe_input_buffer) + RESOLVE_METHOD(dump_format) + RESOLVE_METHOD(url_fdopen) + RESOLVE_METHOD(url_fopen) + RESOLVE_METHOD(url_fclose) + RESOLVE_METHOD(url_open_dyn_buf) + RESOLVE_METHOD(url_close_dyn_buf) + RESOLVE_METHOD(url_fseek) + RESOLVE_METHOD(get_buffer) + RESOLVE_METHOD(get_partial_buffer) + RESOLVE_METHOD(put_byte) + RESOLVE_METHOD(put_buffer) + RESOLVE_METHOD(put_be24) + RESOLVE_METHOD(put_be32) + RESOLVE_METHOD(put_be16) + RESOLVE_METHOD(avformat_alloc_context) + RESOLVE_METHOD(av_new_stream) +#if LIBAVFORMAT_VERSION_INT < (52<<16 | 45<<8) + RESOLVE_METHOD(guess_format) +#else + RESOLVE_METHOD(av_guess_format) +#endif + RESOLVE_METHOD(av_set_parameters) + RESOLVE_METHOD(av_alloc_put_byte) + RESOLVE_METHOD(av_write_header) + RESOLVE_METHOD(av_write_trailer) + RESOLVE_METHOD(av_write_frame) + RESOLVE_METHOD(av_metadata_set2) + RESOLVE_METHOD(av_gettime) + END_METHOD_RESOLVE() + + /* dependencies of libavformat */ + DllAvCodec m_dllAvCodec; + // DllAvCore loaded implicitely by m_dllAvCodec + // DllAvUtil loaded implicitely by m_dllAvCodec + +public: + void av_register_all() + { + av_register_all_dont_call(); + } + int av_find_stream_info(AVFormatContext *ic) + { + return(av_find_stream_info_dont_call(ic)); + } + + virtual bool Load() + { + if (!m_dllAvCodec.Load()) + return false; + return DllDynamic::Load(); + } +}; + +#endif diff --git a/DllAvUtil.h b/DllAvUtil.h new file mode 100644 index 00000000..b570b63a --- /dev/null +++ b/DllAvUtil.h @@ -0,0 +1,216 @@ +#pragma once +/* + * Copyright (C) 2005-2010 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, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#include "DynamicDll.h" +#include "utils/log.h" + +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +#ifndef AV_NOWARN_DEPRECATED +#define AV_NOWARN_DEPRECATED +#endif + +extern "C" { +#if (defined USE_EXTERNAL_FFMPEG) + #if (defined HAVE_LIBAVUTIL_AVUTIL_H) + #include + #include + #include + // for LIBAVCODEC_VERSION_INT: + #include + #elif (defined HAVE_FFMPEG_AVUTIL_H) + #include + #include + #include + // for LIBAVCODEC_VERSION_INT: + #include + #endif + #if defined(HAVE_LIBAVUTIL_OPT_H) + #include + #elif defined(HAVE_LIBAVCODEC_AVCODEC_H) + #include + #else + #include + #endif + #if defined(HAVE_LIBAVUTIL_MEM_H) + #include + #else + #include + #endif +#else + #include "libavutil/avutil.h" + #include "libavutil/crc.h" + #include "libavutil/opt.h" + #include "libavutil/mem.h" + #include "libavutil/fifo.h" +#endif +} + +#ifndef __GNUC__ +#pragma warning(pop) +#endif + +// calback used for logging +//void ff_avutil_log(void* ptr, int level, const char* format, va_list va); + +class DllAvUtilInterface +{ +public: + virtual ~DllAvUtilInterface() {} + virtual void av_log_set_callback(void (*)(void*, int, const char*, va_list))=0; + virtual void *av_malloc(unsigned int size)=0; + virtual void *av_mallocz(unsigned int size)=0; + virtual void *av_realloc(void *ptr, unsigned int size)=0; + virtual void av_free(void *ptr)=0; + virtual void av_freep(void *ptr)=0; + virtual int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding)=0; + virtual int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)=0; + virtual const AVCRC* av_crc_get_table(AVCRCId crc_id)=0; + virtual uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)=0; + virtual int av_set_string3(void *obj, const char *name, const char *val, int alloc, const AVOption **o_out)=0; + virtual AVFifoBuffer *av_fifo_alloc(unsigned int size) = 0; + virtual void av_fifo_free(AVFifoBuffer *f) = 0; + virtual void av_fifo_reset(AVFifoBuffer *f) = 0; + virtual int av_fifo_size(AVFifoBuffer *f) = 0; + virtual int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int)) = 0; + virtual int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int)) = 0; + virtual char *av_strdup(const char *s)=0; + virtual void av_log_set_level(int level) = 0; +}; + +#if (defined USE_EXTERNAL_FFMPEG) + +// Use direct layer +class DllAvUtilBase : public DllDynamic, DllAvUtilInterface +{ +public: + + virtual ~DllAvUtilBase() {} + virtual void av_log_set_callback(void (*foo)(void*, int, const char*, va_list)) { ::av_log_set_callback(foo); } + virtual void *av_malloc(unsigned int size) { return ::av_malloc(size); } + virtual void *av_mallocz(unsigned int size) { return ::av_mallocz(size); } + virtual void *av_realloc(void *ptr, unsigned int size) { return ::av_realloc(ptr, size); } + virtual void av_free(void *ptr) { ::av_free(ptr); } + virtual void av_freep(void *ptr) { ::av_freep(ptr); } + virtual int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding d) { return ::av_rescale_rnd(a, b, c, d); } + virtual int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) { return ::av_rescale_q(a, bq, cq); } + virtual const AVCRC* av_crc_get_table(AVCRCId crc_id) { return ::av_crc_get_table(crc_id); } + virtual uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length) { return ::av_crc(ctx, crc, buffer, length); } +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,7,0) + // API added on: 2008-12-16 + virtual int av_set_string3(void *obj, const char *name, const char *val, int alloc, const AVOption **o_out) { return ::av_set_string3(obj, name, val, alloc, o_out); } +#else + virtual int av_set_string3(void *obj, const char *name, const char *val, int alloc, const AVOption **o_out) { return AVERROR(ENOENT); } +#endif + virtual AVFifoBuffer *av_fifo_alloc(unsigned int size) {return ::av_fifo_alloc(size); } + virtual void av_fifo_free(AVFifoBuffer *f) { ::av_fifo_free(f); } + virtual void av_fifo_reset(AVFifoBuffer *f) { ::av_fifo_reset(f); } + virtual int av_fifo_size(AVFifoBuffer *f) { return ::av_fifo_size(f); } + virtual int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int)) + { return ::av_fifo_generic_read(f, dest, buf_size, func); } + virtual int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int)) + { return ::av_fifo_generic_write(f, src, size, func); } + virtual char *av_strdup(const char *s) { return ::av_strdup(s); } + virtual void av_log_set_level(int level) { ::av_log_set_level(level); }; + + // DLL faking. + virtual bool ResolveExports() { return true; } + virtual bool Load() { + CLog::Log(LOGDEBUG, "DllAvUtilBase: Using libavutil system library"); + return true; + } + virtual void Unload() {} +}; + +#else + +class DllAvUtilBase : public DllDynamic, DllAvUtilInterface +{ + DECLARE_DLL_WRAPPER(DllAvUtilBase, DLL_PATH_LIBAVUTIL) + + LOAD_SYMBOLS() + + DEFINE_METHOD1(void, av_log_set_callback, (void (*p1)(void*, int, const char*, va_list))) + DEFINE_METHOD1(void*, av_malloc, (unsigned int p1)) + DEFINE_METHOD1(void*, av_mallocz, (unsigned int p1)) + DEFINE_METHOD2(void*, av_realloc, (void *p1, unsigned int p2)) + DEFINE_METHOD1(void, av_free, (void *p1)) + DEFINE_METHOD1(void, av_freep, (void *p1)) + DEFINE_METHOD4(int64_t, av_rescale_rnd, (int64_t p1, int64_t p2, int64_t p3, enum AVRounding p4)); + DEFINE_METHOD3(int64_t, av_rescale_q, (int64_t p1, AVRational p2, AVRational p3)); + DEFINE_METHOD1(const AVCRC*, av_crc_get_table, (AVCRCId p1)) + DEFINE_METHOD4(uint32_t, av_crc, (const AVCRC *p1, uint32_t p2, const uint8_t *p3, size_t p4)); + DEFINE_METHOD5(int, av_set_string3, (void *p1, const char *p2, const char *p3, int p4, const AVOption **p5)); + DEFINE_METHOD1(AVFifoBuffer*, av_fifo_alloc, (unsigned int p1)) + DEFINE_METHOD1(void, av_fifo_free, (AVFifoBuffer *p1)) + DEFINE_METHOD1(void, av_fifo_reset, (AVFifoBuffer *p1)) + DEFINE_METHOD1(int, av_fifo_size, (AVFifoBuffer *p1)) + DEFINE_METHOD4(int, av_fifo_generic_read, (AVFifoBuffer *p1, void *p2, int p3, void (*p4)(void*, void*, int))) + DEFINE_METHOD4(int, av_fifo_generic_write, (AVFifoBuffer *p1, void *p2, int p3, int (*p4)(void*, void*, int))) + DEFINE_METHOD1(char*, av_strdup, (const char *p1)) + DEFINE_METHOD1(void, av_log_set_level, (int p1)) + + public: + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(av_log_set_callback) + RESOLVE_METHOD(av_malloc) + RESOLVE_METHOD(av_mallocz) + RESOLVE_METHOD(av_realloc) + RESOLVE_METHOD(av_free) + RESOLVE_METHOD(av_freep) + RESOLVE_METHOD(av_rescale_rnd) + RESOLVE_METHOD(av_rescale_q) + RESOLVE_METHOD(av_crc_get_table) + RESOLVE_METHOD(av_crc) + RESOLVE_METHOD(av_set_string3) + RESOLVE_METHOD(av_fifo_alloc) + RESOLVE_METHOD(av_fifo_free) + RESOLVE_METHOD(av_fifo_reset) + RESOLVE_METHOD(av_fifo_size) + RESOLVE_METHOD(av_fifo_generic_read) + RESOLVE_METHOD(av_fifo_generic_write) + RESOLVE_METHOD(av_strdup) + END_METHOD_RESOLVE() +}; + +#endif + +class DllAvUtil : public DllAvUtilBase +{ +public: + virtual bool Load() + { + if( DllAvUtilBase::Load() ) + { + //DllAvUtilBase::av_log_set_callback(ff_avutil_log); + return true; + } + return false; + } +}; diff --git a/DllBCM.h b/DllBCM.h new file mode 100644 index 00000000..8cc7cdbc --- /dev/null +++ b/DllBCM.h @@ -0,0 +1,245 @@ +#pragma once +/* + * Copyright (C) 2005-2011 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_PLATFORM_RASPBERRY_PI) + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +extern "C" { +#include +} + +#include "DynamicDll.h" +#include "utils/log.h" + +//////////////////////////////////////////////////////////////////////////////////////////// + +class DllBcmHostDisplayInterface +{ +public: + virtual ~DllBcmHostDisplayInterface() {} + + virtual DISPMANX_DISPLAY_HANDLE_T vc_dispmanx_display_open( uint32_t device ) = 0; + virtual DISPMANX_UPDATE_HANDLE_T vc_dispmanx_update_start( int32_t priority ) = 0; + virtual DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + int32_t layer, const VC_RECT_T *dest_rect, DISPMANX_RESOURCE_HANDLE_T src, + const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, + VC_DISPMANX_ALPHA_T *alpha, + DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform ) = 0; + virtual int vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) = 0; + virtual int vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ) = 0; + virtual int vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ) = 0; + virtual int vc_dispmanx_display_get_info( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_MODEINFO_T * pinfo ) = 0; + virtual int vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + uint8_t red, uint8_t green, uint8_t blue ) = 0; +}; + +#if (defined USE_EXTERNAL_LIBBCM_HOST) +class DllBcmHostDisplay : public DllDynamic, DllBcmHostDisplayInterface +{ +public: + virtual DISPMANX_DISPLAY_HANDLE_T vc_dispmanx_display_open( uint32_t device ) + { return ::vc_dispmanx_display_open(device); }; + virtual DISPMANX_UPDATE_HANDLE_T vc_dispmanx_update_start( int32_t priority ) + { return ::vc_dispmanx_update_start(priority); }; + virtual DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + int32_t layer, const VC_RECT_T *dest_rect, DISPMANX_RESOURCE_HANDLE_T src, + const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, + VC_DISPMANX_ALPHA_T *alpha, + DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform ) + { return ::vc_dispmanx_element_add(update, display, layer, dest_rect, src, src_rect, protection, alpha, clamp, transform); }; + virtual int vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) + { return ::vc_dispmanx_update_submit_sync(update); }; + virtual int vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ) + { return ::vc_dispmanx_element_remove(update, element); }; + virtual int vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ) + { return ::vc_dispmanx_display_close(display); }; + virtual int vc_dispmanx_display_get_info( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_MODEINFO_T *pinfo ) + { return ::vc_dispmanx_display_get_info(display, pinfo); }; + virtual int vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + uint8_t red, uint8_t green, uint8_t blue ) + { return ::vc_dispmanx_display_set_background(update, display, red, green, blue); }; + virtual bool ResolveExports() + { return true; } + virtual bool Load() + { + CLog::Log(LOGDEBUG, "DllBcm: Using omx system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllBcmHostDisplay : public DllDynamic, DllBcmHostDisplayInterface +{ + //DECLARE_DLL_WRAPPER(DllBcmHostDisplay, "/opt/vc/lib/libopenmaxil.so") + DECLARE_DLL_WRAPPER(DllBcmHostDisplay, "/opt/vc/lib/libEGL.so") + + DEFINE_METHOD1(DISPMANX_DISPLAY_HANDLE_T, vc_dispmanx_display_open, (uint32_t p1 )) + DEFINE_METHOD1(DISPMANX_UPDATE_HANDLE_T, vc_dispmanx_update_start, (int32_t p1 )) + DEFINE_METHOD10(DISPMANX_ELEMENT_HANDLE_T, vc_dispmanx_element_add, (DISPMANX_UPDATE_HANDLE_T p1, DISPMANX_DISPLAY_HANDLE_T p2, + int32_t p3, const VC_RECT_T *p4, DISPMANX_RESOURCE_HANDLE_T p5, + const VC_RECT_T *p6, DISPMANX_PROTECTION_T p7, + VC_DISPMANX_ALPHA_T *p8, + DISPMANX_CLAMP_T *p9, DISPMANX_TRANSFORM_T p10 )) + DEFINE_METHOD1(int, vc_dispmanx_update_submit_sync, (DISPMANX_UPDATE_HANDLE_T p1)) + DEFINE_METHOD2(int, vc_dispmanx_element_remove, (DISPMANX_UPDATE_HANDLE_T p1, DISPMANX_ELEMENT_HANDLE_T p2)) + DEFINE_METHOD1(int, vc_dispmanx_display_close, (DISPMANX_DISPLAY_HANDLE_T p1)) + DEFINE_METHOD2(int, vc_dispmanx_display_get_info, (DISPMANX_DISPLAY_HANDLE_T p1, DISPMANX_MODEINFO_T *p2)) + DEFINE_METHOD5(int, vc_dispmanx_display_set_background, ( DISPMANX_UPDATE_HANDLE_T p1, DISPMANX_DISPLAY_HANDLE_T p2, + uint8_t p3, uint8_t p4, uint8_t p5 )) + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(vc_dispmanx_display_open) + RESOLVE_METHOD(vc_dispmanx_update_start) + RESOLVE_METHOD(vc_dispmanx_element_add) + RESOLVE_METHOD(vc_dispmanx_update_submit_sync) + RESOLVE_METHOD(vc_dispmanx_element_remove) + RESOLVE_METHOD(vc_dispmanx_display_close) + RESOLVE_METHOD(vc_dispmanx_display_get_info) + RESOLVE_METHOD(vc_dispmanx_display_set_background) + END_METHOD_RESOLVE() + +public: + virtual bool Load() + { + return DllDynamic::Load(); + } +}; +#endif + +class DllBcmHostInterface +{ +public: + virtual ~DllBcmHostInterface() {} + + virtual void bcm_host_init() = 0; + virtual void bcm_host_deinit() = 0; + virtual int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) = 0; + virtual int vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) = 0; + virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) = 0; + + virtual int vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_T *supported_modes, + uint32_t max_supported_modes, HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode) = 0; + virtual int vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) = 0; + virtual int vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate) = 0; + virtual void vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) = 0; + virtual void vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback) = 0; + virtual void vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) = 0; + //virtual void vc_cec_unregister_callback(CECSERVICE_CALLBACK_T callback) = 0; +}; + +#if (defined USE_EXTERNAL_LIBBCM_HOST) +class DllBcmHost : public DllDynamic, DllBcmHostInterface +{ +public: + virtual void bcm_host_init() + { return ::bcm_host_init(); }; + virtual void bcm_host_deinit() + { return ::bcm_host_deinit(); }; + virtual int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) + { return ::graphics_get_display_size(display_number, width, height); }; + virtual int vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) + { return ::vc_tv_hdmi_power_on_best(width, height, frame_rate, scan_mode, match_flags); }; + virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) + { return ::vc_tv_hdmi_power_on_best_3d(width, height, frame_rate, scan_mode, match_flags); }; + virtual int vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_T *supported_modes, + uint32_t max_supported_modes, HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode) + { return ::vc_tv_hdmi_get_supported_modes(group, supported_modes, max_supported_modes, preferred_group, preferred_mode); }; + virtual int vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) + { return ::vc_tv_hdmi_power_on_explicit(mode, group, code); }; + virtual int vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate) + { return ::vc_tv_get_state(tvstate); }; + virtual void vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) + { ::vc_tv_register_callback(callback, callback_data); }; + virtual void vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback) + { ::vc_tv_unregister_callback(callback); }; + virtual void vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) + { ::vc_cec_register_callback(callback, callback_data); }; + //virtual void vc_cec_unregister_callback(CECSERVICE_CALLBACK_T callback) + // { ::vc_cec_unregister_callback(callback); }; + virtual bool ResolveExports() + { return true; } + virtual bool Load() + { + CLog::Log(LOGDEBUG, "DllBcm: Using omx system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllBcmHost : public DllDynamic, DllBcmHostInterface +{ + DECLARE_DLL_WRAPPER(DllBcmHost, "/opt/vc/lib/libbcm_host.so") + + DEFINE_METHOD0(void, bcm_host_init) + DEFINE_METHOD0(void, bcm_host_deinit) + DEFINE_METHOD3(int32_t, graphics_get_display_size, (const uint16_t p1, uint32_t *p2, uint32_t *p3)) + DEFINE_METHOD5(int, vc_tv_hdmi_power_on_best, (uint32_t p1, uint32_t p2, uint32_t p3, + HDMI_INTERLACED_T p4, EDID_MODE_MATCH_FLAG_T p5)) + DEFINE_METHOD5(int, vc_tv_hdmi_power_on_best_3d, (uint32_t p1, uint32_t p2, uint32_t p3, + HDMI_INTERLACED_T p4, EDID_MODE_MATCH_FLAG_T p5)) + DEFINE_METHOD5(int, vc_tv_hdmi_get_supported_modes, (HDMI_RES_GROUP_T p1, TV_SUPPORTED_MODE_T *p2, + uint32_t p3, HDMI_RES_GROUP_T *p4, uint32_t *p5)) + DEFINE_METHOD3(int, vc_tv_hdmi_power_on_explicit, (HDMI_MODE_T p1, HDMI_RES_GROUP_T p2, uint32_t p3)) + DEFINE_METHOD1(int, vc_tv_get_state, (TV_GET_STATE_RESP_T *p1)) + + DEFINE_METHOD2(void, vc_tv_register_callback, (TVSERVICE_CALLBACK_T p1, void *p2)) + DEFINE_METHOD1(void, vc_tv_unregister_callback, (TVSERVICE_CALLBACK_T p1)) + + DEFINE_METHOD2(void, vc_cec_register_callback, (CECSERVICE_CALLBACK_T p1, void *p2)) + //DEFINE_METHOD1(void, vc_cec_unregister_callback, (CECSERVICE_CALLBACK_T p1)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(bcm_host_init) + RESOLVE_METHOD(bcm_host_deinit) + RESOLVE_METHOD(graphics_get_display_size) + RESOLVE_METHOD(vc_tv_hdmi_power_on_best) + RESOLVE_METHOD(vc_tv_hdmi_power_on_best_3d) + RESOLVE_METHOD(vc_tv_hdmi_get_supported_modes) + RESOLVE_METHOD(vc_tv_hdmi_power_on_explicit) + RESOLVE_METHOD(vc_tv_get_state) + RESOLVE_METHOD(vc_tv_register_callback) + RESOLVE_METHOD(vc_tv_unregister_callback) + RESOLVE_METHOD(vc_cec_register_callback) + //RESOLVE_METHOD(vc_cec_unregister_callback) + END_METHOD_RESOLVE() + +public: + virtual bool Load() + { + return DllDynamic::Load(); + } +}; +#endif + +#endif diff --git a/DllOMX.h b/DllOMX.h new file mode 100644 index 00000000..4bad4604 --- /dev/null +++ b/DllOMX.h @@ -0,0 +1,123 @@ +#pragma once +/* + * Copyright (C) 2005-2010 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_OMXLIB) + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +#include "DynamicDll.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////// + +class DllOMXInterface +{ +public: + virtual ~DllOMXInterface() {} + + virtual OMX_ERRORTYPE OMX_Init(void) = 0; + virtual OMX_ERRORTYPE OMX_Deinit(void) = 0; + virtual OMX_ERRORTYPE OMX_GetHandle(OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) = 0; + virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) = 0; + virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) = 0; + virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) = 0; + virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) = 0; + virtual OMX_ERRORTYPE OMX_SetupTunnel(OMX_HANDLETYPE hOutput, OMX_U32 nPortOutput, OMX_HANDLETYPE hInput, OMX_U32 nPortInput) = 0; + +}; + +#if (defined USE_EXTERNAL_OMX) +class DllOMX : public DllDynamic, DllOMXInterface +{ +public: + virtual OMX_ERRORTYPE OMX_Init(void) + { return ::OMX_Init(); }; + virtual OMX_ERRORTYPE OMX_Deinit(void) + { return ::OMX_Deinit(); }; + virtual OMX_ERRORTYPE OMX_GetHandle(OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) + { return ::OMX_GetHandle(pHandle, cComponentName, pAppData, pCallBacks); }; + virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) + { return ::OMX_FreeHandle(hComponent); }; + virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) + { return ::OMX_GetComponentsOfRole(role, pNumComps, compNames); }; + virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) + { return ::OMX_GetRolesOfComponent(compName, pNumRoles, roles); }; + virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) + { return ::OMX_ComponentNameEnum(cComponentName, nNameLength, nIndex); }; + virtual OMX_ERRORTYPE OMX_SetupTunnel(OMX_HANDLETYPE hOutput, OMX_U32 nPortOutput, OMX_HANDLETYPE hInput, OMX_U32 nPortInput) + { return ::OMX_SetupTunnel(hOutput, nPortOutput, hInput, nPortInput); }; + virtual bool ResolveExports() + { return true; } + virtual bool Load() + { + CLog::Log(LOGDEBUG, "DllOMX: Using omx system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllOMX : public DllDynamic, DllOMXInterface +{ + //DECLARE_DLL_WRAPPER(DllLibOpenMax, "/usr/lib/libnvomx.so") + DECLARE_DLL_WRAPPER(DllOMX, "/opt/vc/lib/libopenmaxil.so") + + DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Init) + DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Deinit) + DEFINE_METHOD4(OMX_ERRORTYPE, OMX_GetHandle, (OMX_HANDLETYPE *p1, OMX_STRING p2, OMX_PTR p3, OMX_CALLBACKTYPE *p4)) + DEFINE_METHOD1(OMX_ERRORTYPE, OMX_FreeHandle, (OMX_HANDLETYPE p1)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetComponentsOfRole, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetRolesOfComponent, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_ComponentNameEnum, (OMX_STRING p1, OMX_U32 p2, OMX_U32 p3)) + DEFINE_METHOD4(OMX_ERRORTYPE, OMX_SetupTunnel, (OMX_HANDLETYPE p1, OMX_U32 p2, OMX_HANDLETYPE p3, OMX_U32 p4)); + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(OMX_Init) + RESOLVE_METHOD(OMX_Deinit) + RESOLVE_METHOD(OMX_GetHandle) + RESOLVE_METHOD(OMX_FreeHandle) + RESOLVE_METHOD(OMX_GetComponentsOfRole) + RESOLVE_METHOD(OMX_GetRolesOfComponent) + RESOLVE_METHOD(OMX_ComponentNameEnum) + RESOLVE_METHOD(OMX_SetupTunnel) + END_METHOD_RESOLVE() + +public: + virtual bool Load() + { + return DllDynamic::Load(); + } +}; +#endif + +#endif diff --git a/DynamicDll.cpp b/DynamicDll.cpp new file mode 100644 index 00000000..5c151302 --- /dev/null +++ b/DynamicDll.cpp @@ -0,0 +1,95 @@ +/* + * 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 + * + */ + +#include "DynamicDll.h" +#include "utils/log.h" + +DllDynamic::DllDynamic() +{ + m_dll=NULL; + m_DelayUnload=true; +} + +DllDynamic::DllDynamic(const CStdString& strDllName) +{ + m_strDllName=strDllName; + m_dll=NULL; + m_DelayUnload=true; +} + +DllDynamic::~DllDynamic() +{ + Unload(); +} + +bool DllDynamic::Load() +{ + if (m_dll) + return true; + + /* + if (!(m_dll=CSectionLoader::LoadDLL(m_strDllName, m_DelayUnload, LoadSymbols()))) + return false; + + if (!ResolveExports()) + { + CLog::Log(LOGERROR, "Unable to resolve exports from dll %s", m_strDllName.c_str()); + Unload(); + return false; + } + */ + + return true; +} + +void DllDynamic::Unload() +{ + /* + if(m_dll) + CSectionLoader::UnloadDLL(m_strDllName); + */ + m_dll=NULL; +} + +bool DllDynamic::CanLoad() +{ + return true; +} + +bool DllDynamic::EnableDelayedUnload(bool bOnOff) +{ + if (m_dll) + return false; + + m_DelayUnload=bOnOff; + + return true; +} + +bool DllDynamic::SetFile(const CStdString& strDllName) +{ + if (m_dll) + return false; + + m_strDllName=strDllName; + return true; +} + diff --git a/DynamicDll.h b/DynamicDll.h new file mode 100644 index 00000000..c2d29fde --- /dev/null +++ b/DynamicDll.h @@ -0,0 +1,497 @@ +#pragma once + +/* + * 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 + * + */ + +#include "utils/StdString.h" + +/////////////////////////////////////////////////////////// +// +// DECLARE_DLL_WRAPPER +// +// Declares the constructor of the wrapper class. +// This must be followed by one or more +// DEFINE_METHODX/DEFINE_METHOD_LINKAGEX and +// one BEGIN_METHOD_RESOLVE/END_METHOD_RESOLVE block. +// +// classname: name of the wrapper class to construct +// dllname: file including path of the dll to wrap + +#define DECLARE_DLL_WRAPPER(classname, dllname) \ +XDECLARE_DLL_WRAPPER(classname,dllname) + +#define XDECLARE_DLL_WRAPPER(classname, dllname) \ +public: \ + classname () : DllDynamic( dllname ) {} + +/////////////////////////////////////////////////////////// +// +// DECLARE_DLL_WRAPPER_TEMPLATE_BEGIN +// +// Declares the constructor of the wrapper class. +// The method SetFile(strDllName) can be used to set the +// dll of this wrapper. +// This must be followed by one or more +// DEFINE_METHODX/DEFINE_METHOD_LINKAGEX and +// one BEGIN_METHOD_RESOLVE/END_METHOD_RESOLVE block. +// +// classname: name of the wrapper class to construct +// +#define DECLARE_DLL_WRAPPER_TEMPLATE(classname) \ +public: \ + classname () {} \ + + +/////////////////////////////////////////////////////////// +// +// LOAD_SYMBOLS +// +// Tells the dllloader to load Debug symblos when possible +#define LOAD_SYMBOLS() \ + protected: \ + virtual bool LoadSymbols() { return true; } + +/////////////////////////////////////////////////////////// +// +// DEFINE_GLOBAL +// +// Defines a global for export from the dll as well as +// a function for accessing it (Get_name). +// +// type: The variables type. +// name: Name of the variable. +// + +#define DEFINE_GLOBAL_PTR(type, name) \ + protected: \ + union { \ + type* m_##name; \ + void* m_##name##_ptr; \ + }; \ + public: \ + virtual type* Get_##name (void) \ + { \ + return m_##name; \ + } + +#define DEFINE_GLOBAL(type, name) \ + protected: \ + union { \ + type* m_##name; \ + void* m_##name##_ptr; \ + }; \ + public: \ + virtual type Get_##name (void) \ + { \ + return *m_##name; \ + } + +/////////////////////////////////////////////////////////// +// +// DEFINE_METHOD_LINKAGE +// +// Defines a function for an export from a dll, if the +// calling convention is not __cdecl. +// Use DEFINE_METHOD_LINKAGE for each function to be resolved. +// +// result: Result of the function +// linkage: Calling convention of the function +// name: Name of the function +// args: Arguments of the function, enclosed in parentheses +// +#define DEFINE_METHOD_LINKAGE_FP(result, linkage, name, args) \ + protected: \ + typedef result (linkage * name##_METHOD) args; \ + public: \ + union { \ + name##_METHOD name; \ + void* name##_ptr; \ + }; + +#define DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, args2) \ + protected: \ + typedef result (linkage * name##_METHOD) args; \ + union { \ + name##_METHOD m_##name; \ + void* m_##name##_ptr; \ + }; \ + public: \ + virtual result name args \ + { \ + return m_##name args2; \ + } + +#define DEFINE_METHOD_LINKAGE0(result, linkage, name) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, () , ()) + +#define DEFINE_METHOD_LINKAGE1(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1)) + +#define DEFINE_METHOD_LINKAGE2(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2)) + +#define DEFINE_METHOD_LINKAGE3(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3)) + +#define DEFINE_METHOD_LINKAGE4(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4)) + +#define DEFINE_METHOD_LINKAGE5(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5)) + +#define DEFINE_METHOD_LINKAGE6(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5, p6)) + +#define DEFINE_METHOD_LINKAGE7(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5, p6, p7)) + +#define DEFINE_METHOD_LINKAGE8(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5, p6, p7, p8)) + +#define DEFINE_METHOD_LINKAGE9(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5, p6, p7, p8, p9)) + +#define DEFINE_METHOD_LINKAGE10(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)) + +#define DEFINE_METHOD_LINKAGE11(result, linkage, name, args) \ + DEFINE_METHOD_LINKAGE_BASE(result, linkage, name, args, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)) + +/////////////////////////////////////////////////////////// +// +// DEFINE_METHOD_FP +// +// Defines a function for an export from a dll as a fuction pointer. +// Use DEFINE_METHOD_FP for each function to be resolved. Functions +// defined like this are not listed by IntelliSence. +// +// result: Result of the function +// name: Name of the function +// args: Arguments of the function, enclosed in parentheses +// The parameter names can be anything +// +#define DEFINE_METHOD_FP(result, name, args) DEFINE_METHOD_LINKAGE_FP(result, __cdecl, name, args) + +/////////////////////////////////////////////////////////// +// +// DEFINE_METHODX +// +// Defines a function for an export from a dll. +// Use DEFINE_METHODX for each function to be resolved. +// where X is the number of parameter the function has. +// +// result: Result of the function +// name: Name of the function +// args: Arguments of the function, enclosed in parentheses +// The parameter names have to be renamed to px, where +// x is the number of the parameter +// +#define DEFINE_METHOD0(result, name) DEFINE_METHOD_LINKAGE0(result, __cdecl, name) +#define DEFINE_METHOD1(result, name, args) DEFINE_METHOD_LINKAGE1(result, __cdecl, name, args) +#define DEFINE_METHOD2(result, name, args) DEFINE_METHOD_LINKAGE2(result, __cdecl, name, args) +#define DEFINE_METHOD3(result, name, args) DEFINE_METHOD_LINKAGE3(result, __cdecl, name, args) +#define DEFINE_METHOD4(result, name, args) DEFINE_METHOD_LINKAGE4(result, __cdecl, name, args) +#define DEFINE_METHOD5(result, name, args) DEFINE_METHOD_LINKAGE5(result, __cdecl, name, args) +#define DEFINE_METHOD6(result, name, args) DEFINE_METHOD_LINKAGE6(result, __cdecl, name, args) +#define DEFINE_METHOD7(result, name, args) DEFINE_METHOD_LINKAGE7(result, __cdecl, name, args) +#define DEFINE_METHOD8(result, name, args) DEFINE_METHOD_LINKAGE8(result, __cdecl, name, args) +#define DEFINE_METHOD9(result, name, args) DEFINE_METHOD_LINKAGE9(result, __cdecl, name, args) +#define DEFINE_METHOD10(result, name, args) DEFINE_METHOD_LINKAGE10(result, __cdecl, name, args) +#define DEFINE_METHOD11(result, name, args) DEFINE_METHOD_LINKAGE11(result, __cdecl, name, args) + +/////////////////////////////////////////////////////////// +// +// DEFINE_FUNC_ALIGNED 0-X +// +// Defines a function for an export from a dll, wich +// require a aligned stack on function call +// Use DEFINE_FUNC_ALIGNED for each function to be resolved. +// +// result: Result of the function +// linkage: Calling convention of the function +// name: Name of the function +// args: Argument types of the function +// +// Actual function call will expand to something like this +// this will align the stack (esp) at the point of function +// entry as required by gcc compiled dlls, it is abit abfuscated +// to allow for different sized variables +// +// __int64 test(__int64 p1, char p2, char p3) +// { +// int o,s = ((sizeof(p1)+3)&~3)+((sizeof(p2)+3)&~3)+((sizeof(p3)+3)&~3); +// __asm mov [o],esp; +// __asm sub esp, [s]; +// __asm and esp, ~15; +// __asm add esp, [s] +// m_test(p1, p2, p3); //return value will still be correct aslong as we don't mess with it +// __asm mov esp,[o]; +// }; + +#define ALS(a) ((sizeof(a)+3)&~3) +#define DEFINE_FUNC_PART1(result, linkage, name, args) \ + private: \ + typedef result (linkage * name##_type)##args; \ + union { \ + name##_type m_##name; \ + void* m_##name##_ptr; \ + }; \ + public: \ + virtual result name##args + +#define DEFINE_FUNC_PART2(size) \ + { \ + int o,s = size; \ + __asm { \ + __asm mov [o], esp \ + __asm sub esp, [s] \ + __asm and esp, ~15 \ + __asm add esp, [s] \ + } + +#define DEFINE_FUNC_PART3(name,args) \ + m_##name##args; \ + __asm { \ + __asm mov esp,[o] \ + } \ + } + +#define DEFINE_FUNC_ALIGNED0(result, linkage, name) \ + DEFINE_FUNC_PART1(result, linkage, name, ()) \ + DEFINE_FUNC_PART2(0) \ + DEFINE_FUNC_PART3(name,()) + +#define DEFINE_FUNC_ALIGNED1(result, linkage, name, t1) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1)) \ + DEFINE_FUNC_PART2(ALS(p1)) \ + DEFINE_FUNC_PART3(name,(p1)) + +#define DEFINE_FUNC_ALIGNED2(result, linkage, name, t1, t2) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)) \ + DEFINE_FUNC_PART3(name,(p1, p2)) + +#define DEFINE_FUNC_ALIGNED3(result, linkage, name, t1, t2, t3) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3)) + +#define DEFINE_FUNC_ALIGNED4(result, linkage, name, t1, t2, t3, t4) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3, t4 p4)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)+ALS(p4)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3, p4)) + +#define DEFINE_FUNC_ALIGNED5(result, linkage, name, t1, t2, t3, t4, t5) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)+ALS(p4)+ALS(p5)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3, p4, p5)) + +#define DEFINE_FUNC_ALIGNED6(result, linkage, name, t1, t2, t3, t4, t5, t6) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)+ALS(p4)+ALS(p5)+ALS(p6)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3, p4, p5, p6)) + +#define DEFINE_FUNC_ALIGNED7(result, linkage, name, t1, t2, t3, t4, t5, t6, t7) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)+ALS(p4)+ALS(p5)+ALS(p6)+ALS(p7)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3, p4, p5, p6, p7)) + +#define DEFINE_FUNC_ALIGNED8(result, linkage, name, t1, t2, t3, t4, t5, t6, t7, t8) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)+ALS(p4)+ALS(p5)+ALS(p6)+ALS(p7)+ALS(p8)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3, p4, p5, p6, p7, p8)) + +#define DEFINE_FUNC_ALIGNED9(result, linkage, name, t1, t2, t3, t4, t5, t6, t7, t8, t9) \ + DEFINE_FUNC_PART1(result, linkage, name, (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9)) \ + DEFINE_FUNC_PART2(ALS(p1)+ALS(p2)+ALS(p3)+ALS(p4)+ALS(p5)+ALS(p6)+ALS(p7)+ALS(p8)+ALS(p9)) \ + DEFINE_FUNC_PART3(name,(p1, p2, p3, p4, p5, p6, p7, p8, p9)) + +/////////////////////////////////////////////////////////// +// +// BEGIN_METHOD_RESOLVE/END_METHOD_RESOLVE +// +// Defines a method that resolves the exported functions +// defined with DEFINE_METHOD or DEFINE_METHOD_LINKAGE. +// There must be a RESOLVE_METHOD or RESOLVE_METHOD_RENAME +// for each DEFINE_METHOD or DEFINE_METHOD_LINKAGE within this +// block. This block must be followed by an END_METHOD_RESOLVE. +// +#define BEGIN_METHOD_RESOLVE() \ + protected: \ + virtual bool ResolveExports() \ + { \ + return ( + +#define END_METHOD_RESOLVE() \ + 1 \ + ); \ + } + +/////////////////////////////////////////////////////////// +// +// RESOLVE_METHOD +// +// Resolves a method from a dll +// +// method: Name of the method defined with DEFINE_METHOD +// or DEFINE_METHOD_LINKAGE +// +#define RESOLVE_METHOD(method) \ + m_dll->ResolveExport( #method , & m_##method##_ptr ) && + +#define RESOLVE_METHOD_FP(method) \ + m_dll->ResolveExport( #method , & method##_ptr ) && + +/////////////////////////////////////////////////////////// +// +// RESOLVE_METHOD_RENAME +// +// Resolves a method from a dll +// +// dllmethod: Name of the function exported from the dll +// method: Name of the method defined with DEFINE_METHOD +// or DEFINE_METHOD_LINKAGE +// +#define RESOLVE_METHOD_RENAME(dllmethod, method) \ + m_dll->ResolveExport( #dllmethod , & m_##method##_ptr ) && + +#define RESOLVE_METHOD_RENAME_FP(dllmethod, method) \ + m_dll->ResolveExport( #dllmethod , & method##_ptr ) && + + +//////////////////////////////////////////////////////////////////// +// +// Example declaration of a dll wrapper class +// +// 1. Define a class with pure virtual functions with all functions +// exported from the dll. This is needed to use the IntelliSence +// feature of the Visual Studio Editor. +// +// class DllExampleInterface +// { +// public: +// virtual void foo (unsigned int type, char* szTest)=0; +// virtual void bar (char* szTest, unsigned int type)=0; +// }; +// +// 2. Define a class, derived from DllDynamic and the previously defined +// interface class. Define the constructor of the class using the +// DECLARE_DLL_WRAPPER macro. Use the DEFINE_METHODX/DEFINE_METHOD_LINKAGEX +// macros to define the functions from the interface above, where X is number of +// parameters the function has. The function parameters +// have to be enclosed in parentheses. The parameter names have to be changed to px +// where x is the number on which position the parameter appears. +// Use the RESOLVE_METHOD/RESOLVE_METHOD_RENAME to do the actually resolve the functions +// from the dll when it's loaded. The RESOLVE_METHOD/RESOLVE_METHOD_RENAME have to +// be between the BEGIN_METHOD_RESOLVE/END_METHOD_RESOLVE block. +// +// class DllExample : public DllDynamic, DllExampleInterface +// { +// DECLARE_DLL_WRAPPER(DllExample, special://xbmcbin/system/Example.dll) +// LOAD_SYMBOLS() // add this if you want to load debug symbols for the dll +// DEFINE_METHOD2(void, foo, (int p1, char* p2)) +// DEFINE_METHOD_LINKAGE2(void, __stdcall, bar, (char* p1, int p2)) +// DEFINE_METHOD_FP(void, foobar, (int type, char* szTest)) // No need to define this function in the +// // interface class, as it's a function pointer. +// // But its not recognised by IntelliSence +// BEGIN_METHOD_RESOLVE() +// RESOLVE_METHOD(foo) +// RESOLVE_METHOD_RENAME("_bar@8", bar) +// RESOLVE_METHOD_FP(foobar) +// END_METHOD_RESOLVE() +// }; +// +// The above macros will expand to a class that will look like this +// +// class DllExample : public DllDynamic, DllExampleInterface +// { +// public: +// DllExample() : DllDynamic( "special://xbmcbin/system/Example.dll" ) {} +// protected: +// virtual bool LoadSymbols() { return true; } +// protected: +// typedef void (* foo_METHOD) ( int p1, char* p2 ); +// foo_METHOD m_foo; +// public: +// virtual void foo( int p1, char* p2 ) +// { +// return m_foo(p1, p2); +// } +// protected: +// typedef void (__stdcall * bar_METHOD) ( char* p1, int p2 ); +// bar_METHOD m_bar; +// public: +// virtual void bar( char* p1, int p2 ) +// { +// return m_bar(p1, p2); +// } +// protected: +// typedef void (* foobar_METHOD) (int type, char* szTest); +// public: +// foobar_METHOD foobar; +// protected: +// virtual bool ResolveExports() +// { +// return ( +// m_dll->ResolveExport( "foo", (void**)& m_foo ) && +// m_dll->ResolveExport( "_bar@8", (void**)& m_bar ) && +// m_dll->ResolveExport( "foobar" , (void**)& foobar ) && +// 1 +// ); +// } +// }; +// +// Usage of the class +// +// DllExample dll; +// dll.Load(); +// if (dll.IsLoaded()) +// { +// dll.foo(1, "bar"); +// dll.Unload(); +// } +// + +/////////////////////////////////////////////////////////// +// +// Baseclass for a Dynamically loaded dll +// use the above macros to create a dll wrapper +// +class DllDynamic +{ +public: + DllDynamic(); + DllDynamic(const CStdString& strDllName); + virtual ~DllDynamic(); + virtual bool Load(); + virtual void Unload(); + virtual bool IsLoaded() { return m_dll!=NULL; } + bool CanLoad(); + bool EnableDelayedUnload(bool bOnOff); + bool SetFile(const CStdString& strDllName); + +protected: + virtual bool ResolveExports()=0; + virtual bool LoadSymbols() { return false; } + bool m_DelayUnload; + void *m_dll; + CStdString m_strDllName; +}; diff --git a/File.cpp b/File.cpp new file mode 100644 index 00000000..f71343ee --- /dev/null +++ b/File.cpp @@ -0,0 +1,142 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 Frodo +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* 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 of the License, 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 this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "linux/PlatformDefs.h" +#include +#include +#include "utils/StdString.h" + +#include "File.h" + +using namespace XFILE; +using namespace std; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +#ifndef __GNUC__ +#pragma warning (disable:4244) +#endif + +//********************************************************************************************* +CFile::CFile() +{ + m_pFile = NULL; + m_flags = 0; + m_iLength = 0; +} + +//********************************************************************************************* +CFile::~CFile() +{ + if (m_pFile) + fclose(m_pFile); +} + +//********************************************************************************************* +bool CFile::Open(const CStdString& strFileName, unsigned int flags) +{ + m_flags = flags; + + m_pFile = fopen64(strFileName.c_str(), "r"); + if(!m_pFile) + return false; + + fseeko64(m_pFile, 0, SEEK_END); + m_iLength = ftello64(m_pFile); + fseeko64(m_pFile, 0, SEEK_SET); + + return true; +} + +bool CFile::OpenForWrite(const CStdString& strFileName, bool bOverWrite) +{ + return false; +} + +bool CFile::Exists(const CStdString& strFileName, bool bUseCache /* = true */) +{ + FILE *fp = fopen64(strFileName.c_str(), "r"); + + if(!fp) + return false; + + fclose(fp); + + return true; +} + +unsigned int CFile::Read(void *lpBuf, int64_t uiBufSize) +{ + unsigned int ret = 0; + + if(!m_pFile) + return 0; + + ret = fread(lpBuf, 1, uiBufSize, m_pFile); + + return ret; +} + +//********************************************************************************************* +void CFile::Close() +{ + if(m_pFile) + fclose(m_pFile); + m_pFile = NULL; +} + +//********************************************************************************************* +int64_t CFile::Seek(int64_t iFilePosition, int iWhence) +{ + if (!m_pFile) + return -1; + + return fseeko64(m_pFile, iFilePosition, iWhence);; +} + +//********************************************************************************************* +int64_t CFile::GetLength() +{ + return m_iLength; +} + +//********************************************************************************************* +int64_t CFile::GetPosition() +{ + if (!m_pFile) + return -1; + + return ftello64(m_pFile); +} + +//********************************************************************************************* +int CFile::Write(const void* lpBuf, int64_t uiBufSize) +{ + return -1; +} + +int CFile::IoControl(EIoControl request, void* param) +{ + if(request == IOCTRL_SEEK_POSSIBLE) + return 1; + + return -1; +} diff --git a/File.h b/File.h new file mode 100644 index 00000000..f7bb5732 --- /dev/null +++ b/File.h @@ -0,0 +1,83 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 Frodo +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* 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 of the License, 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 this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +// File.h: interface for the CFile class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_FILE_H__A7ED6320_C362_49CB_8925_6C6C8CAE7B78__INCLUDED_) +#define AFX_FILE_H__A7ED6320_C362_49CB_8925_6C6C8CAE7B78__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define FFMPEG_FILE_BUFFER_SIZE 32768 + +namespace XFILE +{ + +/* indicate that caller can handle truncated reads, where function returns before entire buffer has been filled */ +#define READ_TRUNCATED 0x01 + +/* indicate that that caller support read in the minimum defined chunk size, this disables internal cache then */ +#define READ_CHUNKED 0x02 + +/* use cache to access this file */ +#define READ_CACHED 0x04 + +/* open without caching. regardless to file type. */ +#define READ_NO_CACHE 0x08 + +/* calcuate bitrate for file while reading */ +#define READ_BITRATE 0x10 + +typedef enum { + IOCTRL_NATIVE = 1, /**< SNativeIoControl structure, containing what should be passed to native ioctrl */ + IOCTRL_SEEK_POSSIBLE = 2, /**< return 0 if known not to work, 1 if it should work */ + IOCTRL_CACHE_STATUS = 3, /**< SCacheStatus structure */ + IOCTRL_CACHE_SETRATE = 4, /**< unsigned int with with speed limit for caching in bytes per second */ +} EIoControl; + +class CFile +{ +public: + CFile(); + ~CFile(); + + bool Open(const CStdString& strFileName, unsigned int flags = 0); + bool OpenForWrite(const CStdString& strFileName, bool bOverWrite); + unsigned int Read(void* lpBuf, int64_t uiBufSize); + int Write(const void* lpBuf, int64_t uiBufSize); + int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET); + int64_t GetPosition(); + int64_t GetLength(); + void Close(); + static bool Exists(const CStdString& strFileName, bool bUseCache = true); + int GetChunkSize() { return 6144 /*FFMPEG_FILE_BUFFER_SIZE*/; }; + int IoControl(EIoControl request, void* param); +private: + unsigned int m_flags; + FILE *m_pFile; + int64_t m_iLength; +}; + +}; +#endif // !defined(AFX_FILE_H__A7ED6320_C362_49CB_8925_6C6C8CAE7B78__INCLUDED_) diff --git a/IAudioRenderer.h b/IAudioRenderer.h new file mode 100644 index 00000000..e1342fd3 --- /dev/null +++ b/IAudioRenderer.h @@ -0,0 +1,86 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 d7o3g4q and RUNTiME +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* 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 of the License, 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 this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +// AsyncAudioRenderer.h: interface for the CAsyncDirectSound class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_IAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_) +#define AFX_IAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "utils/StdString.h" +#include "cores/IAudioCallback.h" +#include "utils/PCMRemap.h" +extern void RegisterAudioCallback(IAudioCallback* pCallback); +extern void UnRegisterAudioCallback(); + +typedef std::pair AudioSink; +typedef std::vector AudioSinkList; + +class IAudioRenderer +{ +public: + enum EEncoded { + ENCODED_NONE = 0, + ENCODED_IEC61937_AC3, + ENCODED_IEC61937_EAC3, + ENCODED_IEC61937_DTS, + ENCODED_IEC61937_MPEG, + ENCODED_IEC61937_UNKNOWN, + }; + + IAudioRenderer() {}; + virtual ~IAudioRenderer() {}; + virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = ENCODED_NONE) = 0; + virtual void UnRegisterAudioCallback() = 0; + virtual void RegisterAudioCallback(IAudioCallback* pCallback) = 0; + virtual float GetDelay() = 0; + virtual float GetCacheTime() = 0; + virtual float GetCacheTotal() { return 1.0f; } + + virtual unsigned int AddPackets(const void* data, unsigned int len) = 0; + virtual bool IsResampling() { return false;}; + virtual unsigned int GetSpace() = 0; + virtual bool Deinitialize() = 0; + virtual bool Pause() = 0; + virtual bool Stop() = 0; + virtual bool Resume() = 0; + virtual unsigned int GetChunkLen() = 0; + + virtual long GetCurrentVolume() const = 0; + virtual void Mute(bool bMute) = 0; + virtual bool SetCurrentVolume(long nVolume) = 0; + virtual void SetDynamicRangeCompression(long drc) {}; + virtual float GetCurrentAttenuation() { return m_remap.GetCurrentAttenuation(); } + virtual int SetPlaySpeed(int iSpeed) = 0; + virtual void WaitCompletion() = 0; + virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) = 0; + +protected: + CPCMRemap m_remap; + +private: +}; + +#endif // !defined(AFX_IAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_) diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..d33017a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +include Makefile.include + +CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CMAKE_CONFIG -D__VIDEOCORE4__ -U_FORTIFY_SOURCE -Wall -mfpu=vfp -mfloat-abi=softfp -mno-apcs-stack-check -DHAVE_OMXLIB -DUSE_EXTERNAL_FFMPEG -DHAVE_LIBAVCODEC_AVCODEC_H -DHAVE_LIBAVUTIL_MEM_H -DHAVE_LIBAVUTIL_AVUTIL_H -DHAVE_LIBAVFORMAT_AVFORMAT_H -DHAVE_LIBAVFILTER_AVFILTER_H -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_PLATFORM_RASPBERRY_PI -DUSE_EXTERNAL_LIBBCM_HOST -Wno-psabi -I$(SDKSTAGE)/opt/vc/include/ + +LDFLAGS+=-L./ -lc -lWFC -lGLESv2 -lEGL -lbcm_host -lopenmaxil -Lffmpeg_compiled/usr/local/lib/ +INCLUDES+=-I./ -Ilinux -Iffmpeg_compiled/usr/local/include/ + +SRC=linux/XMemUtils.cpp \ + utils/log.cpp \ + DynamicDll.cpp \ + utils/PCMRemap.cpp \ + utils/RegExp.cpp \ + OMXSubtitleTagSami.cpp \ + OMXOverlayCodecText.cpp \ + BitstreamConverter.cpp \ + linux/RBP.cpp \ + OMXThread.cpp \ + OMXReader.cpp \ + OMXStreamInfo.cpp \ + OMXAudioCodecOMX.cpp \ + OMXCore.cpp \ + OMXVideo.cpp \ + OMXAudio.cpp \ + OMXClock.cpp \ + File.cpp \ + OMXPlayerVideo.cpp \ + OMXPlayerAudio.cpp \ + omxplayer.cpp \ + +OBJS+=$(filter %.o,$(SRC:.cpp=.o)) + +all: omxplayer.bin + +%.o: %.cpp + @rm -f $@ + $(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ -Wno-deprecated-declarations + +list_test: + $(CXX) -O3 -o list_test list_test.cpp + +omxplayer.bin: $(OBJS) + $(CXX) $(LDFLAGS) -o omxplayer.bin -Wl,--whole-archive $(OBJS) -Wl,--no-whole-archive -rdynamic -lavutil -lavcodec -lavformat -lswscale -lpcre + #arm-unknown-linux-gnueabi-strip omxplayer.bin + +clean: + for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done + @rm -f omxplayer.old.log omxplayer.log + @rm -f omxplayer.bin + @rm -rf omxplayer-dist + @rm -f omxplayer-dist.tar.gz + make -f Makefile.ffmpeg clean + +ffmpeg: + @rm -rf ffmpeg + make -f Makefile.ffmpeg + make -f Makefile.ffmpeg install + +dist: omxplayer.bin + mkdir -p omxplayer-dist/usr/lib/omxplayer + mkdir -p omxplayer-dist/usr/usr/bin + mkdir -p omxplayer-dist/usr/share/doc + cp omxplayer omxplayer.bin omxplayer-dist/usr/usr/bin + cp README COPYING omxplayer-dist/usr/share/doc/ + cp -a ffmpeg_compiled/usr/local/lib/*.so* omxplayer-dist/usr/lib/omxplayer/ + tar -czf omxplayer-dist.tar.gz omxplayer-dist diff --git a/Makefile.ffmpeg b/Makefile.ffmpeg new file mode 100644 index 00000000..34134378 --- /dev/null +++ b/Makefile.ffmpeg @@ -0,0 +1,70 @@ +include Makefile.include + +CFLAGS=-D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_HAVE_SBRK -D_LARGEFILE64_SOURCE -DHAVE_CMAKE_CONFIG -DHAVE_VMCS_CONFIG -D_REENTRANT -DUSE_VCHIQ_ARM -DVCHI_BULK_ALIGN=1 -DVCHI_BULK_GRANULARITY=1 -DEGL_SERVER_DISPMANX -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D__VIDEOCORE4__ -DGRAPHICS_X_VG=1 -U_FORTIFY_SOURCE -Wall -DHAVE_OMXLIB -DUSE_EXTERNAL_FFMPEG -DHAVE_LIBAVCODEC_AVCODEC_H -DHAVE_LIBAVUTIL_MEM_H -DHAVE_LIBAVUTIL_AVUTIL_H -DHAVE_LIBAVFORMAT_AVFORMAT_H -DHAVE_LIBAVFILTER_AVFILTER_H -DOMX -DOMX_SKIP64BIT + +WORK=$(PWD) + +all: checkout configure compile + +copy: + find ffmpeg -name '*.so*' -exec cp {} . \; + $(HOST)-strip *.so* + +compile: + cd ffmpeg; \ + make -j9 + +configure: + cd ffmpeg; \ + CFLAGS="$(CFLAGS) ${INCLUDES}" \ + LDFLAGS="" \ + ./configure \ + --extra-cflags="-mfpu=vfp -mfloat-abi=softfp -mno-apcs-stack-check -mstructure-size-boundary=32 -mno-sched-prolog" \ + --enable-cross-compile \ + --enable-shared \ + --disable-static \ + --arch=arm \ + --cpu=arm1176jzf-s \ + --target-os=linux \ + --disable-muxers \ + --enable-muxer=spdif \ + --enable-muxer=adts \ + --disable-encoders \ + --enable-encoder=ac3 \ + --enable-encoder=aac \ + --disable-decoder=mpeg_xvmc \ + --disable-devices \ + --disable-ffprobe \ + --disable-ffplay \ + --disable-ffserver \ + --disable-ffmpeg \ + --enable-shared \ + --disable-doc \ + --enable-postproc \ + --enable-gpl \ + --enable-protocol=http \ + --enable-pthreads \ + --disable-runtime-cpudetect \ + --enable-pic \ + --disable-armv5te \ + --disable-neon \ + --enable-armv6t2 \ + --enable-armv6 \ + --enable-armvfp \ + --enable-hardcoded-tables \ + --disable-runtime-cpudetect \ + --disable-debug \ + --cross-prefix=$(HOST)- + +clean: + @rm -rf ffmpeg + +checkout: + git clone git://git.videolan.org/ffmpeg.git ffmpeg; \ + cd ffmpeg; git checkout master; git checkout 67f5650a78de2567c58dbd7545434cc6d3ef9b7e + #cd ffmpeg; git checkout master; git checkout ec09230c9a11fbac602380614b35a51ad3a8dc3a + +install: + cd ffmpeg; make -j9 DESTDIR="$(WORK)/ffmpeg_compiled" install + $(HOST)-strip ffmpeg_compiled/usr/local/lib/*.so + diff --git a/Makefile.include b/Makefile.include new file mode 100644 index 00000000..b5d62227 --- /dev/null +++ b/Makefile.include @@ -0,0 +1,37 @@ +USE_BUILDROOT=1 + +ifeq ($(USE_BUILDROOT), 1) +BUILDROOT :=/opt/xbmc-bcm/buildroot +SDKSTAGE :=$(BUILDROOT)/output/staging +TARGETFS :=$(BUILDROOT)/output/target +TOOLCHAIN :=$(BUILDROOT)/output/host/usr/ +HOST :=arm-unknown-linux-gnueabi +SYSROOT :=$(BUILDROOT)/output/host/usr/arm-unknown-linux-gnueabi/sysroot +else +BUILDROOT :=/opt/bcm-rootfs +SDKSTAGE :=/opt/bcm-rootfs +TARGETFS :=/opt/bcm-rootfs +TOOLCHAIN :=/usr/local/bcm-gcc +HOST :=bcm2708 +SYSROOT :=$(TOOLCHAIN)/arm-bcm2708-linux-gnueabi/sys-root +endif + +JOBS=7 + +CFLAGS := -isystem$(PREFIX)/include +CXXFLAGS := $(CFLAGS) +CPPFLAGS := $(CFLAGS) +LDFLAGS := -L$(BUILDROOT)/lib +LD := $(TOOLCHAIN)/bin/$(HOST)-ld --sysroot=$(SYSROOT) +CC := $(TOOLCHAIN)/bin/$(HOST)-gcc --sysroot=$(SYSROOT) +CXX := $(TOOLCHAIN)/bin/$(HOST)-g++ --sysroot=$(SYSROOT) +OBJDUMP := $(TOOLCHAIN)/bin/$(HOST)-objdump +RANLIB := $(TOOLCHAIN)/bin/$(HOST)-ranlib +STRIP := $(TOOLCHAIN)/bin/$(HOST)-strip +AR := $(TOOLCHAIN)/bin/$(HOST)-ar +CXXCP := $(CXX) -E +PATH := $(PREFIX)/bin:$(BUILDROOT)/output/host/usr/bin:$(PATH) + +CFLAGS += -pipe -mfloat-abi=softfp -mcpu=arm1176jzf-s -fomit-frame-pointer -mabi=aapcs-linux -mtune=arm1176jzf-s -mfpu=vfp -Wno-psabi -mno-apcs-stack-check -O3 -mstructure-size-boundary=32 -mno-sched-prolog +LDFLAGS += -L$(SDKSTAGE)/lib -L$(SDKSTAGE)/usr/lib -L$(SDKSTAGE)/opt/vc/lib/ +INCLUDES += -isystem$(SDKSTAGE)/staging/usr/include -isystem$(SDKSTAGE)/staging/opt/vc/include -isystem$(SYSROOT)/usr/include diff --git a/OMXAudio.cpp b/OMXAudio.cpp new file mode 100644 index 00000000..22736746 --- /dev/null +++ b/OMXAudio.cpp @@ -0,0 +1,1466 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 d7o3g4q and RUNTiME +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* 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 of the License, 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 this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include "OMXAudio.h" +#include "utils/log.h" + +#define CLASSNAME "COMXAudio" + +#include "linux/XMemUtils.h" + +#ifndef STANDALONE +#include "guilib/AudioContext.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "guilib/LocalizeStrings.h" +#endif + +#ifndef VOLUME_MINIMUM +#define VOLUME_MINIMUM -6000 // -60dB +#endif + +using namespace std; + +#define OMX_MAX_CHANNELS 8 + +static enum PCMChannels OMXChannelMap[OMX_MAX_CHANNELS] = +{ + PCM_FRONT_LEFT , PCM_FRONT_RIGHT , + PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, + PCM_BACK_LEFT , PCM_BACK_RIGHT , + PCM_SIDE_LEFT , PCM_SIDE_RIGHT +}; + +static enum OMX_AUDIO_CHANNELTYPE OMXChannels[OMX_MAX_CHANNELS] = +{ + OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, + OMX_AUDIO_ChannelCF, OMX_AUDIO_ChannelLFE, + OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR , + OMX_AUDIO_ChannelLS, OMX_AUDIO_ChannelRS +}; + +static unsigned int WAVEChannels[OMX_MAX_CHANNELS] = +{ + SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, + SPEAKER_TOP_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, + SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, + SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT +}; + +static const uint16_t AC3Bitrates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640}; +static const uint16_t AC3FSCod [] = {48000, 44100, 32000, 0}; + +static const uint16_t DTSFSCod [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0}; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +//*********************************************************************************************** +COMXAudio::COMXAudio() : + m_pCallback (NULL ), + m_Initialized (false ), + m_Pause (false ), + m_CanPause (false ), + m_CurrentVolume (0 ), + m_Passthrough (false ), + m_HWDecode (false ), + m_BytesPerSec (0 ), + m_BufferLen (0 ), + m_ChunkLen (0 ), + m_InputChannels (0 ), + m_OutputChannels (0 ), + m_BitsPerSample (0 ), + m_omx_clock (NULL ), + m_av_clock (NULL ), + m_external_clock (false ), + m_setStartTime (false ), + m_SampleSize (0 ), + m_first_frame (true ), + m_LostSync (true ), + m_SampleRate (0 ), + m_eEncoding (OMX_AUDIO_CodingPCM), + m_extradata (NULL ), + m_extrasize (0 ), + m_visBufferLength (0 ), + m_last_pts (DVD_NOPTS_VALUE) +{ +} + +COMXAudio::~COMXAudio() +{ + if(m_Initialized) + Deinitialize(); +} + + +bool COMXAudio::Initialize(IAudioCallback* pCallback, const CStdString& device, enum PCMChannels *channelMap, + COMXStreamInfo &hints, OMXClock *clock, EEncoded bPassthrough, bool bUseHWDecode) +{ + m_HWDecode = false; + m_Passthrough = false; + + if(bPassthrough != IAudioRenderer::ENCODED_NONE) + { + m_Passthrough = true; + SetCodingType(hints.codec); + } + else if(bUseHWDecode) + { + m_HWDecode = CanHWDecode(hints.codec); + } + else + { + SetCodingType(CODEC_ID_PCM_S16LE); + } + + SetClock(clock); + + if(hints.extrasize > 0 && hints.extradata != NULL) + { + m_extrasize = hints.extrasize; + m_extradata = (uint8_t *)malloc(m_extrasize); + memcpy(m_extradata, hints.extradata, hints.extrasize); + } + + return Initialize(pCallback, device, hints.channels, channelMap, hints.samplerate, hints.bitspersample, false, false, bPassthrough); +} + +bool COMXAudio::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded bPassthrough) +{ + CStdString deviceuse; + if(device == "hdmi") { + deviceuse = "hdmi"; + } else { + deviceuse = "local"; + } + + if(!m_dllAvUtil.Load()) + return false; + + m_Passthrough = false; + + if(bPassthrough != IAudioRenderer::ENCODED_NONE) + m_Passthrough =true; + + m_drc = 0; + + memset(&m_wave_header, 0x0, sizeof(m_wave_header)); + +#ifndef STANDALONE + bool bAudioOnAllSpeakers(false); + g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic); + + if(bPassthrough) + { + g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE_DIGITAL); + } else { + g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE); + } + + m_CurrentVolume = g_settings.m_nVolumeLevel; +#else + m_CurrentVolume = 0; +#endif + + m_InputChannels = iChannels; + m_remap.Reset(); + + OMX_INIT_STRUCTURE(m_pcm_output); + m_OutputChannels = 2; + m_pcm_output.nChannels = m_OutputChannels; + m_pcm_output.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + m_pcm_output.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + m_pcm_output.eChannelMapping[2] = OMX_AUDIO_ChannelMax; + + OMX_INIT_STRUCTURE(m_pcm_input); + m_pcm_input.nChannels = m_OutputChannels; + m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax; + + m_wave_header.Format.nChannels = m_OutputChannels; + m_wave_header.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + + // set the input format, and get the channel layout so we know what we need to open + enum PCMChannels *outLayout = m_remap.SetInputFormat (iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);; + + if (!m_Passthrough && channelMap && outLayout) + { + /* setup output channel map */ + m_OutputChannels = 0; + int ch = 0, map; + int chan = 0; + while(outLayout[ch] != PCM_INVALID && chan < OMX_MAX_CHANNELS) + { + for(map = 0; map < OMX_MAX_CHANNELS; ++map) + { + if (outLayout[ch] == OMXChannelMap[map]) + { + m_pcm_output.eChannelMapping[chan] = OMXChannels[map]; + chan++; + break; + } + } + ++ch; + } + + m_OutputChannels = chan; + + /* setup input channel map */ + ch = 0; + map = 0; + chan = 0; + + while(channelMap[ch] != PCM_INVALID && chan < iChannels) + { + for(map = 0; map < OMX_MAX_CHANNELS; ++map) + { + if (channelMap[ch] == OMXChannelMap[map]) + { + m_pcm_input.eChannelMapping[chan] = OMXChannels[map]; + m_wave_header.dwChannelMask |= WAVEChannels[map]; + chan++; + break; + } + } + ++ch; + } + } + + // set the m_pcm_output parameters + m_pcm_output.eNumData = OMX_NumericalDataSigned; + m_pcm_output.eEndian = OMX_EndianLittle; + m_pcm_output.bInterleaved = OMX_TRUE; + m_pcm_output.nBitPerSample = uiBitsPerSample; + m_pcm_output.ePCMMode = OMX_AUDIO_PCMModeLinear; + m_pcm_output.nChannels = m_OutputChannels; + m_pcm_output.nSamplingRate = uiSamplesPerSec; + + m_SampleRate = uiSamplesPerSec; + m_BitsPerSample = uiBitsPerSample; + m_BufferLen = m_BytesPerSec = uiSamplesPerSec * (uiBitsPerSample >> 3) * m_InputChannels; + m_BufferLen *= AUDIO_BUFFER_SECONDS; + m_ChunkLen = 6144; + //m_ChunkLen = 2048; + + m_wave_header.Samples.wValidBitsPerSample = uiBitsPerSample; + m_wave_header.Samples.wSamplesPerBlock = 0; + m_wave_header.Format.nChannels = m_InputChannels; + m_wave_header.Format.nBlockAlign = m_InputChannels * (uiBitsPerSample >> 3); + m_wave_header.Format.wFormatTag = WAVE_FORMAT_PCM; + m_wave_header.Format.nSamplesPerSec = uiSamplesPerSec; + m_wave_header.Format.nAvgBytesPerSec = m_BytesPerSec; + m_wave_header.Format.wBitsPerSample = uiBitsPerSample; + m_wave_header.Samples.wValidBitsPerSample = uiBitsPerSample; + m_wave_header.Format.cbSize = 0; + m_wave_header.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + //m_SampleSize = (m_pcm_output.nChannels * m_pcm_output.nBitPerSample * m_pcm_output.nSamplingRate)>>3; + m_SampleSize = (m_pcm_input.nChannels * m_pcm_output.nBitPerSample * m_pcm_output.nSamplingRate)>>3; + + m_pcm_input.eNumData = OMX_NumericalDataSigned; + m_pcm_input.eEndian = OMX_EndianLittle; + m_pcm_input.bInterleaved = OMX_TRUE; + m_pcm_input.nBitPerSample = uiBitsPerSample; + m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear; + m_pcm_input.nChannels = m_InputChannels; + m_pcm_input.nSamplingRate = uiSamplesPerSec; + + PrintPCM(&m_pcm_input); + PrintPCM(&m_pcm_output); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + CStdString componentName = ""; + + componentName = "OMX.broadcom.audio_render"; + if(!m_omx_render.Initialize((const CStdString)componentName, OMX_IndexParamAudioInit)) + return false; + + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; + OMX_INIT_STRUCTURE(audioDest); + strncpy((char *)audioDest.sName, device.c_str(), strlen(device.c_str())); + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest); + if (omx_err != OMX_ErrorNone) + return false; + + componentName = "OMX.broadcom.audio_decode"; + if(!m_omx_decoder.Initialize((const CStdString)componentName, OMX_IndexParamAudioInit)) + return false; + + if(!m_Passthrough) + { + componentName = "OMX.broadcom.audio_mixer"; + if(!m_omx_mixer.Initialize((const CStdString)componentName, OMX_IndexParamAudioInit)) + return false; + } + + if(m_Passthrough) + { + OMX_CONFIG_BOOLEANTYPE boolType; + OMX_INIT_STRUCTURE(boolType); + boolType.bEnabled = OMX_TRUE; + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmDecoderPassThrough, &boolType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error OMX_IndexParamBrcmDecoderPassThrough 0x%08x", omx_err); + printf("OMX_IndexParamBrcmDecoderPassThrough omx_err(0x%08x)\n", omx_err); + return false; + } + } + + // set up the number/size of buffers + OMX_PARAM_PORTDEFINITIONTYPE port_param; + OMX_INIT_STRUCTURE(port_param); + port_param.nPortIndex = m_omx_decoder.GetInputPort(); + + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_param); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize error get OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err); + return false; + } + + port_param.format.audio.eEncoding = m_eEncoding; + + port_param.nBufferSize = m_ChunkLen; + port_param.nBufferCountActual = m_BufferLen / m_ChunkLen; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &port_param); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize error set OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err); + return false; + } + + if(m_HWDecode) + { + OMX_AUDIO_PARAM_PORTFORMATTYPE formatType; + OMX_INIT_STRUCTURE(formatType); + formatType.nPortIndex = m_omx_decoder.GetInputPort(); + + formatType.eEncoding = m_eEncoding; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamAudioPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize error OMX_IndexParamAudioPortFormat omx_err(0x%08x)\n", omx_err); + return false; + } + } + + if(m_av_clock == NULL) + { + /* no external clock set. generate one */ + m_external_clock = false; + + m_av_clock = new OMXClock(); + + if(!m_av_clock->OMXInitialize(false, true)) + { + delete m_av_clock; + m_av_clock = NULL; + CLog::Log(LOGERROR, "COMXAudio::Initialize error creating av clock\n"); + return false; + } + } + + m_omx_clock = m_av_clock->GetOMXClock(); + + m_omx_tunnel_clock.Initialize(m_omx_clock, m_omx_clock->GetInputPort(), &m_omx_render, m_omx_render.GetInputPort()+1); + + omx_err = m_omx_tunnel_clock.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize m_omx_tunnel_clock.Establish\n"); + return false; + } + + if(!m_external_clock) + { + omx_err = m_omx_clock->SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize m_omx_clock.SetStateForComponent\n"); + return false; + } + } + + /* + m_pcm_output.nPortIndex = m_omx_render.GetInputPort(); + omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize OMX_IndexParamAudioPcm omx_err(0x%08x)\n", omx_err); + return false; + } + */ + + omx_err = m_omx_decoder.AllocInputBuffers(); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error alloc buffers 0x%08x", omx_err); + return false; + } + + if(!m_Passthrough) + { + m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_mixer, m_omx_mixer.GetInputPort()); + omx_err = m_omx_tunnel_decoder.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_decoder.Establish 0x%08x", omx_err); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + + m_omx_tunnel_mixer.Initialize(&m_omx_mixer, m_omx_mixer.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort()); + omx_err = m_omx_tunnel_mixer.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_decoder.Establish 0x%08x", omx_err); + return false; + } + + omx_err = m_omx_mixer.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + } + else + { + m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort()); + omx_err = m_omx_tunnel_decoder.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_decoder.Establish 0x%08x", omx_err); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + } + + omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + + if(m_eEncoding == OMX_AUDIO_CodingPCM) + { + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - buffer error 0x%08x", omx_err); + return false; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = sizeof(m_wave_header); + if(omx_buffer->nFilledLen > omx_buffer->nAllocLen) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - omx_buffer->nFilledLen > omx_buffer->nAllocLen"); + return false; + } + memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen); + memcpy((unsigned char *)omx_buffer->pBuffer, &m_wave_header, omx_buffer->nFilledLen); + omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + } + else if(m_HWDecode) + { + // send decoder config + if(m_extrasize > 0 && m_extradata != NULL) + { + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err); + return false; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = m_extrasize; + if(omx_buffer->nFilledLen > omx_buffer->nAllocLen) + { + CLog::Log(LOGERROR, "%s::%s - omx_buffer->nFilledLen > omx_buffer->nAllocLen", CLASSNAME, __func__); + return false; + } + + memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen); + memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen); + omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + } + } + + m_Initialized = true; + m_setStartTime = true; + m_first_frame = true; + m_last_pts = DVD_NOPTS_VALUE; + + SetCurrentVolume(m_CurrentVolume); + + CLog::Log(LOGDEBUG, "COMXAudio::Initialize Ouput bps %d samplerate %d channels %d device %s buffer size %d bytes per second %d passthrough %d hwdecode %d", + (int)m_pcm_output.nBitPerSample, (int)m_pcm_output.nSamplingRate, (int)m_pcm_output.nChannels, deviceuse.c_str(), m_BufferLen, m_BytesPerSec, m_Passthrough, m_HWDecode); + CLog::Log(LOGDEBUG, "COMXAudio::Initialize Input bps %d samplerate %d channels %d device %s buffer size %d bytes per second %d passthrough %d hwdecode %d", + (int)m_pcm_input.nBitPerSample, (int)m_pcm_input.nSamplingRate, (int)m_pcm_input.nChannels, deviceuse.c_str(), m_BufferLen, m_BytesPerSec, m_Passthrough, m_HWDecode); + + return true; +} + +//*********************************************************************************************** +bool COMXAudio::Deinitialize() +{ + if(!m_Initialized) + return true; + + if(!m_external_clock && m_av_clock != NULL) + m_av_clock->OMXStop(); + + m_omx_tunnel_decoder.Flush(); + if(!m_Passthrough) + m_omx_tunnel_mixer.Flush(); + m_omx_tunnel_clock.Flush(); + + m_omx_tunnel_clock.Deestablish(); + if(!m_Passthrough) + m_omx_tunnel_mixer.Deestablish(); + m_omx_tunnel_decoder.Deestablish(); + + m_omx_decoder.FlushInput(); + + m_omx_render.Deinitialize(); + if(!m_Passthrough) + m_omx_mixer.Deinitialize(); + m_omx_decoder.Deinitialize(); + + m_Initialized = false; + m_BytesPerSec = 0; + m_BufferLen = 0; + + if(!m_external_clock && m_av_clock != NULL) + { + delete m_av_clock; + m_av_clock = NULL; + m_external_clock = false; + } + + m_omx_clock = NULL; + m_av_clock = NULL; + + m_Initialized = false; + m_LostSync = true; + m_HWDecode = false; + + if(m_extradata) + free(m_extradata); + m_extradata = NULL; + m_extrasize = 0; + + m_dllAvUtil.Unload(); + + m_setStartTime = true; + m_first_frame = true; + m_last_pts = DVD_NOPTS_VALUE; + + return true; +} + +void COMXAudio::Flush() +{ + if(!m_Initialized) + return; + + m_omx_decoder.FlushInput(); + m_omx_tunnel_decoder.Flush(); + if(!m_Passthrough) + m_omx_tunnel_mixer.Flush(); + + m_setStartTime = true; + m_last_pts = DVD_NOPTS_VALUE; + m_LostSync = true; + //m_first_frame = true; +} + +//*********************************************************************************************** +bool COMXAudio::Pause() +{ + if (!m_Initialized) + return -1; + + if(m_Pause) return true; + m_Pause = true; + + m_omx_decoder.SetStateForComponent(OMX_StatePause); + + return true; +} + +//*********************************************************************************************** +bool COMXAudio::Resume() +{ + if (!m_Initialized) + return -1; + + if(!m_Pause) return true; + m_Pause = false; + + m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + + return true; +} + +//*********************************************************************************************** +bool COMXAudio::Stop() +{ + if (!m_Initialized) + return -1; + + Flush(); + + m_Pause = false; + + return true; +} + +//*********************************************************************************************** +long COMXAudio::GetCurrentVolume() const +{ + return m_CurrentVolume; +} + +//*********************************************************************************************** +void COMXAudio::Mute(bool bMute) +{ + if(!m_Initialized) + return; + + if (bMute) + SetCurrentVolume(VOLUME_MINIMUM); + else + SetCurrentVolume(m_CurrentVolume); +} + +//*********************************************************************************************** +bool COMXAudio::SetCurrentVolume(long nVolume) +{ + if(!m_Initialized || m_Passthrough) + return -1; + + m_CurrentVolume = nVolume; + + OMX_AUDIO_CONFIG_VOLUMETYPE volume; + OMX_INIT_STRUCTURE(volume); + volume.nPortIndex = m_omx_render.GetInputPort(); + + volume.sVolume.nValue = nVolume; + + m_omx_render.SetConfig(OMX_IndexConfigAudioVolume, &volume); + + return true; +} + + +//*********************************************************************************************** +unsigned int COMXAudio::GetSpace() +{ + int free = m_omx_decoder.GetInputBufferSpace(); + return free; +} + +unsigned int COMXAudio::AddPackets(const void* data, unsigned int len) +{ + return AddPackets(data, len, 0, 0); +} + +//*********************************************************************************************** +unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dts, double pts) +{ + if(!m_Initialized) { + CLog::Log(LOGERROR,"COMXAudio::AddPackets - sanity failed. no valid play handle!"); + return len; + } + + if (!m_Passthrough && m_pCallback) + { + unsigned int mylen = std::min(len, sizeof m_visBuffer); + memcpy(m_visBuffer, data, mylen); + m_visBufferLength = mylen; + } + + if(m_eEncoding == OMX_AUDIO_CodingDTS && m_LostSync && m_Passthrough) + { + int skip = SyncDTS((uint8_t *)data, len); + if(skip > 0) + return len; + } + + if(m_eEncoding == OMX_AUDIO_CodingDDP && m_LostSync && m_Passthrough) + { + int skip = SyncAC3((uint8_t *)data, len); + if(skip > 0) + return len; + } + + unsigned int demuxer_bytes = (unsigned int)len; + uint8_t *demuxer_content = (uint8_t *)data; + + OMX_ERRORTYPE omx_err; + + OMX_BUFFERHEADERTYPE *omx_buffer = NULL; + + while(demuxer_bytes) + { + // 200ms timeout + omx_buffer = m_omx_decoder.GetInputBuffer(200); + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "COMXAudio::Decode timeout\n"); + printf("COMXAudio::Decode timeout\n"); + return len; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFlags = 0; + + omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes; + memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen); + + /* + if (m_SampleSize > 0 && pts != DVD_NOPTS_VALUE && !(omx_buffer->nFlags & OMX_BUFFERFLAG_TIME_UNKNOWN) && !m_Passthrough && !m_HWDecode) + { + pts += ((double)omx_buffer->nFilledLen * DVD_TIME_BASE) / m_SampleSize; + } + */ + + uint64_t val = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts;; + if(m_setStartTime) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME; + + m_setStartTime = false; + m_last_pts = pts; + } + else + { + if(pts == DVD_NOPTS_VALUE) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + m_last_pts = pts; + } + else if (m_last_pts != pts) + { + if(pts > m_last_pts) + m_last_pts = pts; + else + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;; + } + else if (m_last_pts == pts) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + } + } + + omx_buffer->nTimeStamp = ToOMXTime(val); + + demuxer_bytes -= omx_buffer->nFilledLen; + demuxer_content += omx_buffer->nFilledLen; + + if(demuxer_bytes == 0) + omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + int nRetry = 0; + while(true) + { + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err == OMX_ErrorNone) + { + break; + } + else + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + nRetry++; + } + if(nRetry == 5) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() finaly failed\n", CLASSNAME, __func__); + printf("%s::%s - OMX_EmptyThisBuffer() finaly failed\n", CLASSNAME, __func__); + return 0; + } + } + + if(m_first_frame) + { + m_first_frame = false; + //m_omx_render.WaitForEvent(OMX_EventPortSettingsChanged); + + m_omx_render.DisablePort(m_omx_render.GetInputPort(), false); + if(!m_Passthrough) + { + m_omx_mixer.DisablePort(m_omx_mixer.GetOutputPort(), false); + m_omx_mixer.DisablePort(m_omx_mixer.GetInputPort(), false); + } + m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), false); + + if(!m_Passthrough) + { + OMX_INIT_STRUCTURE(m_pcm_input); + m_pcm_input.nPortIndex = m_omx_decoder.GetOutputPort(); + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 1 omx_err(0x%08x)\n", omx_err); + } + + //printf("m_omx_mixer.GetInputPort() %d m_omx_mixer.GetPutputPort() %d\n", m_omx_mixer.GetInputPort(), m_omx_mixer.GetOutputPort()); + + /* setup mixer input */ + m_pcm_input.nPortIndex = m_omx_mixer.GetInputPort(); + omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error SetParameter 1 omx_err(0x%08x)\n", omx_err); + } + omx_err = m_omx_mixer.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 2 omx_err(0x%08x)\n", omx_err); + } + + /* setup mixer output */ + m_pcm_output.nPortIndex = m_omx_mixer.GetOutputPort(); + omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error SetParameter 1 omx_err(0x%08x)\n", omx_err); + } + omx_err = m_omx_mixer.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 2 omx_err(0x%08x)\n", omx_err); + } + + m_pcm_output.nPortIndex = m_omx_render.GetInputPort(); + omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error SetParameter 1 omx_err(0x%08x)\n", omx_err); + } + omx_err = m_omx_render.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 2 omx_err(0x%08x)\n", omx_err); + } + + PrintPCM(&m_pcm_input); + PrintPCM(&m_pcm_output); + } + else + { + m_pcm_output.nPortIndex = m_omx_decoder.GetOutputPort(); + m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + PrintPCM(&m_pcm_output); + + OMX_AUDIO_PARAM_PORTFORMATTYPE formatType; + OMX_INIT_STRUCTURE(formatType); + formatType.nPortIndex = m_omx_render.GetInputPort(); + + omx_err = m_omx_render.GetParameter(OMX_IndexParamAudioPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error OMX_IndexParamAudioPortFormat omx_err(0x%08x)\n", omx_err); + assert(0); + } + + formatType.eEncoding = m_eEncoding; + + omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error OMX_IndexParamAudioPortFormat omx_err(0x%08x)\n", omx_err); + assert(0); + } + + if(m_eEncoding == OMX_AUDIO_CodingDDP) + { + OMX_AUDIO_PARAM_DDPTYPE m_ddParam; + OMX_INIT_STRUCTURE(m_ddParam); + + m_ddParam.nPortIndex = m_omx_render.GetInputPort(); + + m_ddParam.nChannels = m_InputChannels; //(m_InputChannels == 6) ? 8 : m_InputChannels; + m_ddParam.nSampleRate = m_SampleRate; + m_ddParam.eBitStreamId = OMX_AUDIO_DDPBitStreamIdAC3; + m_ddParam.nBitRate = 0; + + for(unsigned int i = 0; i < OMX_MAX_CHANNELS; i++) + { + if(i >= m_ddParam.nChannels) + break; + + m_ddParam.eChannelMapping[i] = OMXChannels[i]; + } + + m_omx_render.SetParameter(OMX_IndexParamAudioDdp, &m_ddParam); + m_omx_render.GetParameter(OMX_IndexParamAudioDdp, &m_ddParam); + PrintDDP(&m_ddParam); + } + else if(m_eEncoding == OMX_AUDIO_CodingDTS) + { + m_dtsParam.nPortIndex = m_omx_render.GetInputPort(); + + m_dtsParam.nChannels = m_InputChannels; //(m_InputChannels == 6) ? 8 : m_InputChannels; + m_dtsParam.nBitRate = 0; + + for(unsigned int i = 0; i < OMX_MAX_CHANNELS; i++) + { + if(i >= m_dtsParam.nChannels) + break; + + m_dtsParam.eChannelMapping[i] = OMXChannels[i]; + } + + m_omx_render.SetParameter(OMX_IndexParamAudioDts, &m_dtsParam); + m_omx_render.GetParameter(OMX_IndexParamAudioDts, &m_dtsParam); + PrintDTS(&m_dtsParam); + } + } + + m_omx_render.EnablePort(m_omx_render.GetInputPort(), false); + if(!m_Passthrough) + { + m_omx_mixer.EnablePort(m_omx_mixer.GetOutputPort(), false); + m_omx_mixer.EnablePort(m_omx_mixer.GetInputPort(), false); + } + m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false); + } + + } + + return len; +} + +//*********************************************************************************************** +float COMXAudio::GetDelay() +{ + unsigned int free = m_omx_decoder.GetInputBufferSize() - m_omx_decoder.GetInputBufferSpace(); + return (float)free / (float)m_BytesPerSec; +} + +float COMXAudio::GetCacheTime() +{ + float fBufferLenFull = (float)m_BufferLen - (float)GetSpace(); + if(fBufferLenFull < 0) + fBufferLenFull = 0; + float ret = fBufferLenFull / (float)m_BytesPerSec; + return ret; +} + +float COMXAudio::GetCacheTotal() +{ + return (float)m_BufferLen / (float)m_BytesPerSec; +} + +//*********************************************************************************************** +unsigned int COMXAudio::GetChunkLen() +{ + return m_ChunkLen; +} +//*********************************************************************************************** +int COMXAudio::SetPlaySpeed(int iSpeed) +{ + return 0; +} + +void COMXAudio::RegisterAudioCallback(IAudioCallback *pCallback) +{ + m_pCallback = pCallback; + if (m_pCallback && !m_Passthrough && !m_HWDecode) + m_pCallback->OnInitialize(m_OutputChannels, m_SampleRate, m_BitsPerSample); +} + +void COMXAudio::UnRegisterAudioCallback() +{ + m_pCallback = NULL; +} + +void COMXAudio::DoAudioWork() +{ + if (m_pCallback && m_visBufferLength) + { + m_pCallback->OnAudioData((BYTE*)m_visBuffer, m_visBufferLength); + m_visBufferLength = 0; + } +} + +void COMXAudio::WaitCompletion() +{ + if(!m_Initialized || m_Pause) + return; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + struct timespec starttime, endtime; + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err); + return; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = 0; + omx_buffer->nTimeStamp = ToOMXTime(0LL); + + omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS | OMX_BUFFERFLAG_TIME_UNKNOWN; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return; + } + + clock_gettime(CLOCK_REALTIME, &starttime); + + while(true) + { + if(m_omx_render.IsEOS()) + break; + clock_gettime(CLOCK_REALTIME, &endtime); + if((endtime.tv_sec - starttime.tv_sec) > 2) + { + CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__); + break; + } + OMXClock::OMXSleep(50); + } + + return; +} + +void COMXAudio::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) +{ + return ; +} + +void COMXAudio::EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough) +{ +#ifndef STANDALONE + if (!passthrough) + { + vAudioSinks.push_back(AudioSink(g_localizeStrings.Get(409) + " (OMX)", "omx:default")); + vAudioSinks.push_back(AudioSink("analog (OMX)" , "omx:analog")); + vAudioSinks.push_back(AudioSink("hdmi (OMX)" , "omx:hdmi")); + } + else + { + vAudioSinks.push_back(AudioSink("hdmi (OMX)" , "omx:hdmi")); + } +#endif +} + +bool COMXAudio::SetClock(OMXClock *clock) +{ + if(m_av_clock != NULL) + return false; + + m_av_clock = clock; + m_external_clock = true; + return true; +} + +void COMXAudio::SetCodingType(CodecID codec) +{ + switch(codec) + { + case CODEC_ID_DTS: + CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingDTS\n"); + m_eEncoding = OMX_AUDIO_CodingDTS; + break; + case CODEC_ID_AC3: + case CODEC_ID_EAC3: + CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingDDP\n"); + m_eEncoding = OMX_AUDIO_CodingDDP; + break; + default: + CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingPCM\n"); + m_eEncoding = OMX_AUDIO_CodingPCM; + break; + } +} + +bool COMXAudio::CanHWDecode(CodecID codec) +{ + switch(codec) + { + /* + case CODEC_ID_VORBIS: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingVORBIS\n"); + m_eEncoding = OMX_AUDIO_CodingVORBIS; + m_HWDecode = true; + break; + case CODEC_ID_AAC: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingAAC\n"); + m_eEncoding = OMX_AUDIO_CodingAAC; + m_HWDecode = true; + break; + */ + case CODEC_ID_MP2: + case CODEC_ID_MP3: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingMP3\n"); + m_eEncoding = OMX_AUDIO_CodingMP3; + m_HWDecode = true; + break; + case CODEC_ID_DTS: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingDTS\n"); + m_eEncoding = OMX_AUDIO_CodingDTS; + m_HWDecode = true; + break; + case CODEC_ID_AC3: + case CODEC_ID_EAC3: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingDDP\n"); + m_eEncoding = OMX_AUDIO_CodingDDP; + m_HWDecode = true; + break; + default: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingPCM\n"); + m_eEncoding = OMX_AUDIO_CodingPCM; + m_HWDecode = false; + break; + } + + return m_HWDecode; +} + +bool COMXAudio::HWDecode(CodecID codec) +{ + bool ret = false; + + switch(codec) + { + /* + case CODEC_ID_VORBIS: + CLog::Log(LOGDEBUG, "COMXAudio::HWDecode CODEC_ID_VORBIS\n"); + ret = true; + break; + case CODEC_ID_AAC: + CLog::Log(LOGDEBUG, "COMXAudio::HWDecode CODEC_ID_AAC\n"); + ret = true; + break; + */ + case CODEC_ID_MP2: + case CODEC_ID_MP3: + CLog::Log(LOGDEBUG, "COMXAudio::HWDecode CODEC_ID_MP2 / CODEC_ID_MP3\n"); + ret = true; + break; + case CODEC_ID_DTS: + CLog::Log(LOGDEBUG, "COMXAudio::HWDecode CODEC_ID_DTS\n"); + ret = true; + break; + case CODEC_ID_AC3: + case CODEC_ID_EAC3: + CLog::Log(LOGDEBUG, "COMXAudio::HWDecode CODEC_ID_AC3 / CODEC_ID_EAC3\n"); + ret = true; + break; + default: + ret = false; + break; + } + + return ret; +} + +void COMXAudio::PrintChannels(OMX_AUDIO_CHANNELTYPE eChannelMapping[]) +{ + for(int i = 0; i < OMX_AUDIO_MAXCHANNELS; i++) + { + switch(eChannelMapping[i]) + { + case OMX_AUDIO_ChannelLF: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLF\n"); + break; + case OMX_AUDIO_ChannelRF: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelRF\n"); + break; + case OMX_AUDIO_ChannelCF: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelCF\n"); + break; + case OMX_AUDIO_ChannelLS: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLS\n"); + break; + case OMX_AUDIO_ChannelRS: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelRS\n"); + break; + case OMX_AUDIO_ChannelLFE: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLFE\n"); + break; + case OMX_AUDIO_ChannelCS: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelCS\n"); + break; + case OMX_AUDIO_ChannelLR: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLR\n"); + break; + case OMX_AUDIO_ChannelRR: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelRR\n"); + break; + case OMX_AUDIO_ChannelNone: + case OMX_AUDIO_ChannelKhronosExtensions: + case OMX_AUDIO_ChannelVendorStartUnused: + case OMX_AUDIO_ChannelMax: + default: + break; + } + } +} + +void COMXAudio::PrintPCM(OMX_AUDIO_PARAM_PCMMODETYPE *pcm) +{ + CLog::Log(LOGDEBUG, "pcm->nPortIndex : %d\n", (int)pcm->nPortIndex); + CLog::Log(LOGDEBUG, "pcm->eNumData : %d\n", pcm->eNumData); + CLog::Log(LOGDEBUG, "pcm->eEndian : %d\n", pcm->eEndian); + CLog::Log(LOGDEBUG, "pcm->bInterleaved : %d\n", (int)pcm->bInterleaved); + CLog::Log(LOGDEBUG, "pcm->nBitPerSample : %d\n", (int)pcm->nBitPerSample); + CLog::Log(LOGDEBUG, "pcm->ePCMMode : %d\n", pcm->ePCMMode); + CLog::Log(LOGDEBUG, "pcm->nChannels : %d\n", (int)pcm->nChannels); + CLog::Log(LOGDEBUG, "pcm->nSamplingRate : %d\n", (int)pcm->nSamplingRate); + + PrintChannels(pcm->eChannelMapping); +} + +void COMXAudio::PrintDDP(OMX_AUDIO_PARAM_DDPTYPE *ddparm) +{ + CLog::Log(LOGDEBUG, "ddparm->nPortIndex : %d\n", (int)ddparm->nPortIndex); + CLog::Log(LOGDEBUG, "ddparm->nChannels : %d\n", (int)ddparm->nChannels); + CLog::Log(LOGDEBUG, "ddparm->nBitRate : %d\n", (int)ddparm->nBitRate); + CLog::Log(LOGDEBUG, "ddparm->nSampleRate : %d\n", (int)ddparm->nSampleRate); + CLog::Log(LOGDEBUG, "ddparm->eBitStreamId : %d\n", (int)ddparm->eBitStreamId); + CLog::Log(LOGDEBUG, "ddparm->eBitStreamMode : %d\n", (int)ddparm->eBitStreamMode); + CLog::Log(LOGDEBUG, "ddparm->eDolbySurroundMode : %d\n", (int)ddparm->eDolbySurroundMode); + + PrintChannels(ddparm->eChannelMapping); +} + +void COMXAudio::PrintDTS(OMX_AUDIO_PARAM_DTSTYPE *dtsparam) +{ + CLog::Log(LOGDEBUG, "dtsparam->nPortIndex : %d\n", (int)dtsparam->nPortIndex); + CLog::Log(LOGDEBUG, "dtsparam->nChannels : %d\n", (int)dtsparam->nChannels); + CLog::Log(LOGDEBUG, "dtsparam->nBitRate : %d\n", (int)dtsparam->nBitRate); + CLog::Log(LOGDEBUG, "dtsparam->nSampleRate : %d\n", (int)dtsparam->nSampleRate); + CLog::Log(LOGDEBUG, "dtsparam->nFormat : 0x%08x\n", (int)dtsparam->nFormat); + CLog::Log(LOGDEBUG, "dtsparam->nDtsType : %d\n", (int)dtsparam->nDtsType); + CLog::Log(LOGDEBUG, "dtsparam->nDtsFrameSizeBytes : %d\n", (int)dtsparam->nDtsFrameSizeBytes); + + PrintChannels(dtsparam->eChannelMapping); +} + +/* ========================== SYNC FUNCTIONS ========================== */ +unsigned int COMXAudio::SyncDTS(BYTE* pData, unsigned int iSize) +{ + OMX_INIT_STRUCTURE(m_dtsParam); + + unsigned int skip; + unsigned int srCode; + unsigned int dtsBlocks; + bool littleEndian; + + for(skip = 0; iSize - skip > 8; ++skip, ++pData) + { + if (pData[0] == 0x7F && pData[1] == 0xFE && pData[2] == 0x80 && pData[3] == 0x01) + { + /* 16bit le */ + littleEndian = true; + dtsBlocks = ((pData[4] >> 2) & 0x7f) + 1; + m_dtsParam.nFormat = 0x1 | 0x2; + } + else if (pData[0] == 0x1F && pData[1] == 0xFF && pData[2] == 0xE8 && pData[3] == 0x00 && pData[4] == 0x07 && (pData[5] & 0xF0) == 0xF0) + { + /* 14bit le */ + littleEndian = true; + dtsBlocks = (((pData[4] & 0x7) << 4) | (pData[7] & 0x3C) >> 2) + 1; + m_dtsParam.nFormat = 0x1 | 0x0; + } + else if (pData[1] == 0x7F && pData[0] == 0xFE && pData[3] == 0x80 && pData[2] == 0x01) + { + /* 16bit be */ + littleEndian = false; + dtsBlocks = ((pData[5] >> 2) & 0x7f) + 1; + m_dtsParam.nFormat = 0x0 | 0x2; + } + else if (pData[1] == 0x1F && pData[0] == 0xFF && pData[3] == 0xE8 && pData[2] == 0x00 && pData[5] == 0x07 && (pData[4] & 0xF0) == 0xF0) + { + /* 14bit be */ + littleEndian = false; + dtsBlocks = (((pData[5] & 0x7) << 4) | (pData[6] & 0x3C) >> 2) + 1; + m_dtsParam.nFormat = 0x0 | 0x0; + } + else + { + continue; + } + + if (littleEndian) + { + /* if it is not a termination frame, check the next 6 bits are set */ + if ((pData[4] & 0x80) == 0x80 && (pData[4] & 0x7C) != 0x7C) + continue; + + /* get the frame size */ + m_dtsParam.nDtsFrameSizeBytes = ((((pData[5] & 0x3) << 8 | pData[6]) << 4) | ((pData[7] & 0xF0) >> 4)) + 1; + srCode = (pData[8] & 0x3C) >> 2; + } + else + { + /* if it is not a termination frame, check the next 6 bits are set */ + if ((pData[5] & 0x80) == 0x80 && (pData[5] & 0x7C) != 0x7C) + continue; + + /* get the frame size */ + m_dtsParam.nDtsFrameSizeBytes = ((((pData[4] & 0x3) << 8 | pData[7]) << 4) | ((pData[6] & 0xF0) >> 4)) + 1; + srCode = (pData[9] & 0x3C) >> 2; + } + + /* make sure the framesize is sane */ + if (m_dtsParam.nDtsFrameSizeBytes < 96 || m_dtsParam.nDtsFrameSizeBytes > 16384) + continue; + + m_dtsParam.nSampleRate = DTSFSCod[srCode]; + + switch(dtsBlocks << 5) + { + case 512 : + m_dtsParam.nDtsType = 1; + break; + case 1024: + m_dtsParam.nDtsType = 2; + break; + case 2048: + m_dtsParam.nDtsType = 3; + break; + default: + m_dtsParam.nDtsType = 0; + break; + } + + //m_dtsParam.nFormat = 1; + m_dtsParam.nDtsType = 1; + + m_LostSync = false; + + return skip; + } + + m_LostSync = true; + return iSize; +} + +unsigned int COMXAudio::SyncAC3(BYTE* pData, unsigned int iSize) +{ + unsigned int skip = 0; + //unsigned int fSize = 0; + + for(skip = 0; iSize - skip > 6; ++skip, ++pData) + { + /* search for an ac3 sync word */ + if(pData[0] != 0x0b || pData[1] != 0x77) + continue; + + uint8_t fscod = pData[4] >> 6; + uint8_t frmsizecod = pData[4] & 0x3F; + uint8_t bsid = pData[5] >> 3; + + /* sanity checks on the header */ + if ( + fscod == 3 || + frmsizecod > 37 || + bsid > 0x11 + ) continue; + + /* get the details we need to check crc1 and framesize */ + uint16_t bitrate = AC3Bitrates[frmsizecod >> 1]; + unsigned int framesize = 0; + switch(fscod) + { + case 0: framesize = bitrate * 2; break; + case 1: framesize = (320 * bitrate / 147 + (frmsizecod & 1 ? 1 : 0)); break; + case 2: framesize = bitrate * 4; break; + } + + //fSize = framesize * 2; + m_SampleRate = AC3FSCod[fscod]; + + /* dont do extensive testing if we have not lost sync */ + if (!m_LostSync && skip == 0) + return 0; + + unsigned int crc_size; + /* if we have enough data, validate the entire packet, else try to validate crc2 (5/8 of the packet) */ + if (framesize <= iSize - skip) + crc_size = framesize - 1; + else crc_size = (framesize >> 1) + (framesize >> 3) - 1; + + if (crc_size <= iSize - skip) + if(m_dllAvUtil.av_crc(m_dllAvUtil.av_crc_get_table(AV_CRC_16_ANSI), 0, &pData[2], crc_size * 2)) + continue; + + /* if we get here, we can sync */ + m_LostSync = false; + return skip; + } + + /* if we get here, the entire packet is invalid and we have lost sync */ + m_LostSync = true; + return iSize; +} + diff --git a/OMXAudio.h b/OMXAudio.h new file mode 100644 index 00000000..e4fb1312 --- /dev/null +++ b/OMXAudio.h @@ -0,0 +1,143 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 d7o3g4q and RUNTiME +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* 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 of the License, 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 this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +////////////////////////////////////////////////////////////////////// + +#ifndef __OPENMAXAUDIORENDER_H__ +#define __OPENMAXAUDIORENDER_H__ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +//#define STANDALONE + +#ifdef STANDALONE +#include "IAudioRenderer.h" +#else +#include "../AudioRenderers/IAudioRenderer.h" +#endif +#include "cores/IAudioCallback.h" +#include "linux/PlatformDefs.h" +#include "DllAvCodec.h" +#include "DllAvUtil.h" +#include "OMXCore.h" +#include "OMXClock.h" +#include "OMXStreamInfo.h" +#include "BitstreamConverter.h" + +#define AUDIO_BUFFER_SECONDS 2 +#define VIS_PACKET_SIZE 3840 + +class COMXAudio : public IAudioRenderer +{ +public: + void UnRegisterAudioCallback(); + void RegisterAudioCallback(IAudioCallback* pCallback); + unsigned int GetChunkLen(); + float GetDelay(); + float GetCacheTime(); + float GetCacheTotal(); + COMXAudio(); + bool Initialize(IAudioCallback* pCallback, const CStdString& device, enum PCMChannels *channelMap, + COMXStreamInfo &hints, OMXClock *clock, EEncoded bPassthrough, bool bUseHWDecode); + bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded bPassthrough = IAudioRenderer::ENCODED_NONE); + ~COMXAudio(); + + unsigned int AddPackets(const void* data, unsigned int len); + unsigned int AddPackets(const void* data, unsigned int len, double dts, double pts); + unsigned int GetSpace(); + bool Deinitialize(); + bool Pause(); + bool Stop(); + bool Resume(); + + long GetCurrentVolume() const; + void Mute(bool bMute); + bool SetCurrentVolume(long nVolume); + void SetDynamicRangeCompression(long drc) { m_drc = drc; } + int SetPlaySpeed(int iSpeed); + void WaitCompletion(); + void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers); + + void Flush(); + void DoAudioWork(); + static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough); + + void Process(); + + bool SetClock(OMXClock *clock); + void SetCodingType(CodecID codec); + bool CanHWDecode(CodecID codec); + static bool HWDecode(CodecID codec); + + void PrintChannels(OMX_AUDIO_CHANNELTYPE eChannelMapping[]); + void PrintPCM(OMX_AUDIO_PARAM_PCMMODETYPE *pcm); + void PrintDDP(OMX_AUDIO_PARAM_DDPTYPE *ddparm); + void PrintDTS(OMX_AUDIO_PARAM_DTSTYPE *dtsparam); + unsigned int SyncDTS(BYTE* pData, unsigned int iSize); + unsigned int SyncAC3(BYTE* pData, unsigned int iSize); + +private: + IAudioCallback* m_pCallback; + bool m_Initialized; + bool m_Pause; + bool m_CanPause; + long m_CurrentVolume; + long m_drc; + bool m_Passthrough; + bool m_HWDecode; + unsigned int m_BytesPerSec; + unsigned int m_BufferLen; + unsigned int m_ChunkLen; + unsigned int m_InputChannels; + unsigned int m_OutputChannels; + unsigned int m_BitsPerSample; + COMXCoreComponent *m_omx_clock; + OMXClock *m_av_clock; + bool m_external_clock; + bool m_setStartTime; + int m_SampleSize; + bool m_first_frame; + bool m_LostSync; + int m_SampleRate; + OMX_AUDIO_CODINGTYPE m_eEncoding; + uint8_t *m_extradata; + int m_extrasize; + // stuff for visualisation + unsigned int m_visBufferLength; + double m_last_pts; + short m_visBuffer[VIS_PACKET_SIZE+2]; + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_output; + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_input; + OMX_AUDIO_PARAM_DTSTYPE m_dtsParam; + WAVEFORMATEXTENSIBLE m_wave_header; + +protected: + COMXCoreComponent m_omx_render; + COMXCoreComponent m_omx_mixer; + COMXCoreComponent m_omx_decoder; + COMXCoreTunel m_omx_tunnel_clock; + COMXCoreTunel m_omx_tunnel_mixer; + COMXCoreTunel m_omx_tunnel_decoder; + DllAvUtil m_dllAvUtil; +}; +#endif + diff --git a/OMXAudioCodecOMX.cpp b/OMXAudioCodecOMX.cpp new file mode 100644 index 00000000..b028cd75 --- /dev/null +++ b/OMXAudioCodecOMX.cpp @@ -0,0 +1,351 @@ +/* + * 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 + * + */ + +#include "OMXAudioCodecOMX.h" +#ifdef _LINUX +#include "XMemUtils.h" +#endif +#include "utils/log.h" + +#define MAX_AUDIO_FRAME_SIZE (AVCODEC_MAX_AUDIO_FRAME_SIZE*1.5) + +COMXAudioCodecOMX::COMXAudioCodecOMX() +{ + m_iBufferSize1 = 0; + m_pBuffer1 = (BYTE*)_aligned_malloc(MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16); + memset(m_pBuffer1, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + + m_iBufferSize2 = 0; + m_pBuffer2 = (BYTE*)_aligned_malloc(MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16); + memset(m_pBuffer2, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + + m_iBuffered = 0; + m_pCodecContext = NULL; + m_pConvert = NULL; + m_bOpenedCodec = false; + + m_channelMap[0] = PCM_INVALID; + m_channels = 0; + m_layout = 0; +} + +COMXAudioCodecOMX::~COMXAudioCodecOMX() +{ + _aligned_free(m_pBuffer1); + _aligned_free(m_pBuffer2); + + Dispose(); +} + +bool COMXAudioCodecOMX::Open(COMXStreamInfo &hints) +{ + AVCodec* pCodec; + m_bOpenedCodec = false; + + if (!m_dllAvCore.Load() || !m_dllAvUtil.Load() || !m_dllAvCodec.Load()) + return false; + + m_dllAvCodec.avcodec_register_all(); + m_pCodecContext = m_dllAvCodec.avcodec_alloc_context(); + m_dllAvCodec.avcodec_get_context_defaults(m_pCodecContext); + + pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec); + if (!pCodec) + { + CLog::Log(LOGDEBUG,"COMXAudioCodecOMX::Open() Unable to find codec %d", hints.codec); + return false; + } + + m_pCodecContext->debug_mv = 0; + m_pCodecContext->debug = 0; + m_pCodecContext->workaround_bugs = 1; + + if (pCodec->capabilities & CODEC_CAP_TRUNCATED) + m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; + + m_channels = 0; + m_pCodecContext->channels = hints.channels; + m_pCodecContext->sample_rate = hints.samplerate; + m_pCodecContext->block_align = hints.blockalign; + m_pCodecContext->bit_rate = hints.bitrate; + m_pCodecContext->bits_per_coded_sample = hints.bitspersample; + + if(m_pCodecContext->bits_per_coded_sample == 0) + m_pCodecContext->bits_per_coded_sample = 16; + + if( hints.extradata && hints.extrasize > 0 ) + { + m_pCodecContext->extradata_size = hints.extrasize; + m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE); + memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize); + } + + if (m_dllAvCodec.avcodec_open(m_pCodecContext, pCodec) < 0) + { + CLog::Log(LOGDEBUG,"COMXAudioCodecOMX::Open() Unable to open codec"); + Dispose(); + return false; + } + + m_bOpenedCodec = true; + m_iSampleFormat = AV_SAMPLE_FMT_NONE; + return true; +} + +void COMXAudioCodecOMX::Dispose() +{ + if (m_pConvert) + { + m_dllAvCodec.av_audio_convert_free(m_pConvert); + m_pConvert = NULL; + + } + + if (m_pCodecContext) + { + if (m_bOpenedCodec) m_dllAvCodec.avcodec_close(m_pCodecContext); + m_bOpenedCodec = false; + m_dllAvUtil.av_free(m_pCodecContext); + m_pCodecContext = NULL; + } + + m_dllAvCodec.Unload(); + m_dllAvUtil.Unload(); + + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + m_iBuffered = 0; +} + +int COMXAudioCodecOMX::Decode(BYTE* pData, int iSize) +{ + int iBytesUsed; + if (!m_pCodecContext) return -1; + if (iSize < 1) return iSize; + + m_iBufferSize1 = AVCODEC_MAX_AUDIO_FRAME_SIZE; + m_iBufferSize2 = 0; + + AVPacket avpkt; + m_dllAvCodec.av_init_packet(&avpkt); + avpkt.data = pData; + avpkt.size = iSize; + iBytesUsed = m_dllAvCodec.avcodec_decode_audio3( m_pCodecContext + , (int16_t*)m_pBuffer1 + , &m_iBufferSize1 + , &avpkt); + + /* some codecs will attempt to consume more data than what we gave */ + if (iBytesUsed > iSize) + { + CLog::Log(LOGWARNING, "COMXAudioCodecOMX::Decode - decoder attempted to consume more data than given"); + iBytesUsed = iSize; + } + + if(m_iBufferSize1 == 0 && iBytesUsed >= 0) + m_iBuffered += iBytesUsed; + else + m_iBuffered = 0; + + if(m_pCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 && m_iBufferSize1 > 0) + { + if(m_pConvert && m_pCodecContext->sample_fmt != m_iSampleFormat) + { + m_dllAvCodec.av_audio_convert_free(m_pConvert); + m_pConvert = NULL; + } + + if(!m_pConvert) + { + m_iSampleFormat = m_pCodecContext->sample_fmt; + m_pConvert = m_dllAvCodec.av_audio_convert_alloc(AV_SAMPLE_FMT_S16, 1, m_pCodecContext->sample_fmt, 1, NULL, 0); + } + + if(!m_pConvert) + { + CLog::Log(LOGERROR, "COMXAudioCodecOMX::Decode - Unable to convert %d to AV_SAMPLE_FMT_S16", m_pCodecContext->sample_fmt); + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + return iBytesUsed; + } + + const void *ibuf[6] = { m_pBuffer1 }; + void *obuf[6] = { m_pBuffer2 }; + int istr[6] = { m_dllAvCore.av_get_bits_per_sample_fmt(m_pCodecContext->sample_fmt)/8 }; + int ostr[6] = { 2 }; + int len = m_iBufferSize1 / istr[0]; + if(m_dllAvCodec.av_audio_convert(m_pConvert, obuf, ostr, ibuf, istr, len) < 0) + { + CLog::Log(LOGERROR, "COMXAudioCodecOMX::Decode - Unable to convert %d to AV_SAMPLE_FMT_S16", (int)m_pCodecContext->sample_fmt); + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + return iBytesUsed; + } + + m_iBufferSize1 = 0; + m_iBufferSize2 = len * ostr[0]; + } + + return iBytesUsed; +} + +int COMXAudioCodecOMX::GetData(BYTE** dst) +{ + // TODO: Use a third buffer and decide which is our source data + if(m_pCodecContext->channels == 6 && m_iBufferSize1) + { + int16_t *pDst = (int16_t *)m_pBuffer2; + int16_t *pSrc = (int16_t *)m_pBuffer1; + + //printf("\ncopy_chunk_len %d, omx_chunk_len %d\n", copy_chunk_len, omx_chunk_len); + memset(m_pBuffer2, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + + m_iBufferSize2 = 0; + int size = m_iBufferSize1 / 2; + int gap = 8 - m_pCodecContext->channels; + int samples = 0; + + for(int i = 0; i < size; pDst++, pSrc++, i++, samples++) + { + if( (i%m_pCodecContext->channels) == 0) + { + pDst += gap; + samples += gap; + } + + *pDst = *pSrc; + } + + m_iBufferSize2 = samples * 2; + + *dst = m_pBuffer2; + return m_iBufferSize2; + } + + if(m_iBufferSize1) + { + *dst = m_pBuffer1; + return m_iBufferSize1; + } + if(m_iBufferSize2) + { + *dst = m_pBuffer2; + return m_iBufferSize2; + } + return 0; +} + +void COMXAudioCodecOMX::Reset() +{ + if (m_pCodecContext) m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext); + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + m_iBuffered = 0; +} + +int COMXAudioCodecOMX::GetChannels() +{ + return (m_pCodecContext->channels == 6) ? 8 : m_pCodecContext->channels; +} + +int COMXAudioCodecOMX::GetSampleRate() +{ + if (m_pCodecContext) return m_pCodecContext->sample_rate; + return 0; +} + +int COMXAudioCodecOMX::GetBitsPerSample() +{ + return 16; +} + +int COMXAudioCodecOMX::GetBitRate() +{ + if (m_pCodecContext) return m_pCodecContext->bit_rate; + return 0; +} + +static unsigned count_bits(int64_t value) +{ + unsigned bits = 0; + for(;value;++bits) + value &= value - 1; + return bits; +} + +void COMXAudioCodecOMX::BuildChannelMap() +{ + if (m_channels == m_pCodecContext->channels && m_layout == m_pCodecContext->channel_layout) + return; //nothing to do here + + m_channels = m_pCodecContext->channels; + m_layout = m_pCodecContext->channel_layout; + + int64_t layout; + + int bits = count_bits(m_pCodecContext->channel_layout); + if (bits == m_pCodecContext->channels) + layout = m_pCodecContext->channel_layout; + else + { + CLog::Log(LOGINFO, "COMXAudioCodecOMX::GetChannelMap - FFmpeg reported %d channels, but the layout contains %d ignoring", m_pCodecContext->channels, bits); + layout = m_dllAvCodec.avcodec_guess_channel_layout(m_pCodecContext->channels, m_pCodecContext->codec_id, NULL); + } + + int index = 0; + if (layout & AV_CH_FRONT_LEFT ) m_channelMap[index++] = PCM_FRONT_LEFT ; + if (layout & AV_CH_FRONT_RIGHT ) m_channelMap[index++] = PCM_FRONT_RIGHT ; + if (layout & AV_CH_FRONT_CENTER ) m_channelMap[index++] = PCM_FRONT_CENTER ; + if (layout & AV_CH_LOW_FREQUENCY ) m_channelMap[index++] = PCM_LOW_FREQUENCY ; + if (layout & AV_CH_BACK_LEFT ) m_channelMap[index++] = PCM_BACK_LEFT ; + if (layout & AV_CH_BACK_RIGHT ) m_channelMap[index++] = PCM_BACK_RIGHT ; + if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelMap[index++] = PCM_FRONT_LEFT_OF_CENTER ; + if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelMap[index++] = PCM_FRONT_RIGHT_OF_CENTER; + if (layout & AV_CH_BACK_CENTER ) m_channelMap[index++] = PCM_BACK_CENTER ; + if (layout & AV_CH_SIDE_LEFT ) m_channelMap[index++] = PCM_SIDE_LEFT ; + if (layout & AV_CH_SIDE_RIGHT ) m_channelMap[index++] = PCM_SIDE_RIGHT ; + if (layout & AV_CH_TOP_CENTER ) m_channelMap[index++] = PCM_TOP_CENTER ; + if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelMap[index++] = PCM_TOP_FRONT_LEFT ; + if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelMap[index++] = PCM_TOP_FRONT_CENTER ; + if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelMap[index++] = PCM_TOP_FRONT_RIGHT ; + if (layout & AV_CH_TOP_BACK_LEFT ) m_channelMap[index++] = PCM_TOP_BACK_LEFT ; + if (layout & AV_CH_TOP_BACK_CENTER ) m_channelMap[index++] = PCM_TOP_BACK_CENTER ; + if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelMap[index++] = PCM_TOP_BACK_RIGHT ; + + //terminate the channel map + m_channelMap[index] = PCM_INVALID; + if(m_pCodecContext->channels == 6) + { + m_channelMap[6] = PCM_INVALID; + m_channelMap[7] = PCM_INVALID; + m_channelMap[8] = PCM_INVALID; + } +} + +enum PCMChannels* COMXAudioCodecOMX::GetChannelMap() +{ + BuildChannelMap(); + + if (m_channelMap[0] == PCM_INVALID) + return NULL; + + return m_channelMap; +} diff --git a/OMXAudioCodecOMX.h b/OMXAudioCodecOMX.h new file mode 100644 index 00000000..1339c847 --- /dev/null +++ b/OMXAudioCodecOMX.h @@ -0,0 +1,74 @@ +#pragma once + +/* + * 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 + * + */ + +#include "DllAvCodec.h" +#include "DllAvCore.h" +#include "DllAvFormat.h" +#include "DllAvUtil.h" + +#include "OMXStreamInfo.h" +#include "utils/PCMRemap.h" +#include "linux/PlatformDefs.h" + +class COMXAudioCodecOMX +{ +public: + COMXAudioCodecOMX(); + ~COMXAudioCodecOMX(); + bool Open(COMXStreamInfo &hints); + void Dispose(); + int Decode(BYTE* pData, int iSize); + int GetData(BYTE** dst); + void Reset(); + int GetChannels(); + enum PCMChannels *GetChannelMap(); + int GetSampleRate(); + int GetBitsPerSample(); + const char* GetName() { return "FFmpeg"; } + int GetBufferSize() { return m_iBuffered; } + int GetBitRate(); + +protected: + AVCodecContext* m_pCodecContext; + AVAudioConvert* m_pConvert;; + enum AVSampleFormat m_iSampleFormat; + enum PCMChannels m_channelMap[PCM_MAX_CH + 1]; + + BYTE *m_pBuffer1; + int m_iBufferSize1; + + BYTE *m_pBuffer2; + int m_iBufferSize2; + + bool m_bOpenedCodec; + int m_iBuffered; + + int m_channels; + uint64_t m_layout; + + DllAvCodec m_dllAvCodec; + DllAvCore m_dllAvCore; + DllAvUtil m_dllAvUtil; + + void BuildChannelMap(); +}; diff --git a/OMXClock.cpp b/OMXClock.cpp new file mode 100644 index 00000000..4a214b47 --- /dev/null +++ b/OMXClock.cpp @@ -0,0 +1,688 @@ +/* + * 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 "OMXClock.h" + +int64_t OMXClock::m_systemOffset; +int64_t OMXClock::m_systemFrequency; +bool OMXClock::m_ismasterclock; + +OMXClock::OMXClock() +{ + m_dllAvFormat.Load(); + + m_video_clock = DVD_NOPTS_VALUE; + m_audio_clock = DVD_NOPTS_VALUE; + m_has_video = false; + m_has_audio = false; + m_play_speed = 1; + m_pause = false; + m_iCurrentPts = DVD_NOPTS_VALUE; + + m_systemFrequency = CurrentHostFrequency(); + m_systemUsed = m_systemFrequency; + m_pauseClock = 0; + m_bReset = true; + m_iDisc = 0; + m_maxspeedadjust = 0.0; + m_speedadjust = false; + m_ismasterclock = true; + m_ClockOffset = 0; + m_fps = 25.0f; + + pthread_mutex_init(&m_lock, NULL); + + CheckSystemClock(); + + OMXReset(); +} + +OMXClock::~OMXClock() +{ + Deinitialize(); + + m_dllAvFormat.Unload(); + pthread_mutex_destroy(&m_lock); +} + +void OMXClock::Lock() +{ + pthread_mutex_lock(&m_lock); +} + +void OMXClock::UnLock() +{ + pthread_mutex_unlock(&m_lock); +} + +double OMXClock::SystemToAbsolute(int64_t system) +{ + return DVD_TIME_BASE * (double)(system - m_systemOffset) / m_systemFrequency; +} + +double OMXClock::SystemToPlaying(int64_t system) +{ + int64_t current; + + if (m_bReset) + { + m_startClock = system; + m_systemUsed = m_systemFrequency; + m_pauseClock = 0; + m_iDisc = 0; + m_bReset = false; + } + + if (m_pauseClock) + current = m_pauseClock; + else + current = system; + + return DVD_TIME_BASE * (double)(current - m_startClock) / m_systemUsed + m_iDisc; +} + +int64_t OMXClock::GetFrequency() +{ + return m_systemFrequency; +} + +int64_t OMXClock::Wait(int64_t Target) +{ + int64_t Now; + int SleepTime; + int64_t ClockOffset = m_ClockOffset; + + Now = CurrentHostCounter(); + //sleep until the timestamp has passed + SleepTime = (int)((Target - (Now + ClockOffset)) * 1000 / m_systemFrequency); + if (SleepTime > 0) + OMXSleep(SleepTime); + + Now = CurrentHostCounter(); + return Now; +} + +double OMXClock::WaitAbsoluteClock(double target) +{ + Lock(); + int64_t systemtarget, freq, offset; + freq = m_systemFrequency; + offset = m_systemOffset; + UnLock(); + + systemtarget = (int64_t)(target / DVD_TIME_BASE * (double)freq); + systemtarget += offset; + systemtarget = Wait(systemtarget); + systemtarget -= offset; + return (double)systemtarget / freq * DVD_TIME_BASE; +} + +// Returns the current absolute clock in units of DVD_TIME_BASE (usually microseconds). +double OMXClock::GetAbsoluteClock(bool interpolated /*= true*/) +{ + Lock(); + CheckSystemClock(); + double current = GetTime(); + UnLock(); + return SystemToAbsolute(current); +} + +int64_t OMXClock::GetTime(bool interpolated) +{ + return CurrentHostCounter() + m_ClockOffset; +} + +void OMXClock::CheckSystemClock() +{ + if(!m_systemFrequency) + m_systemFrequency = GetFrequency(); + + if(!m_systemOffset) + m_systemOffset = GetTime(); +} + +double OMXClock::GetClock(bool interpolated /*= true*/) +{ + Lock(); + double clock = GetTime(interpolated); + UnLock(); + return SystemToPlaying(clock); +} + +double OMXClock::GetClock(double& absolute, bool interpolated /*= true*/) +{ + int64_t current = GetTime(interpolated); + + Lock(); + CheckSystemClock(); + absolute = SystemToAbsolute(current); + UnLock(); + + return SystemToPlaying(current); +} + +void OMXClock::SetSpeed(int iSpeed) +{ + // this will sometimes be a little bit of due to rounding errors, ie clock might jump abit when changing speed + Lock(); + + if(iSpeed == DVD_PLAYSPEED_PAUSE) + { + if(!m_pauseClock) + m_pauseClock = GetTime(); + UnLock(); + return; + } + + int64_t current; + int64_t newfreq = m_systemFrequency * DVD_PLAYSPEED_NORMAL / iSpeed; + + current = GetTime(); + if( m_pauseClock ) + { + m_startClock += current - m_pauseClock; + m_pauseClock = 0; + } + + m_startClock = current - (int64_t)((double)(current - m_startClock) * newfreq / m_systemUsed); + m_systemUsed = newfreq; + UnLock(); +} + +void OMXClock::Discontinuity(double currentPts) +{ + Lock(); + m_startClock = GetTime(); + if(m_pauseClock) + m_pauseClock = m_startClock; + m_iDisc = currentPts; + m_bReset = false; + UnLock(); +} + +void OMXClock::Pause() +{ + Lock(); + if(!m_pauseClock) + m_pauseClock = GetTime(); + UnLock(); +} + +void OMXClock::Resume() +{ + Lock(); + if( m_pauseClock ) + { + int64_t current; + current = GetTime(); + + m_startClock += current - m_pauseClock; + m_pauseClock = 0; + } + UnLock(); +} + +bool OMXClock::SetMaxSpeedAdjust(double speed) +{ + Lock(); + m_maxspeedadjust = speed; + UnLock(); + return m_speedadjust; +} + +//returns the refreshrate if the videoreferenceclock is running, -1 otherwise +int OMXClock::UpdateFramerate(double fps, double* interval /*= NULL*/) +{ + //sent with fps of 0 means we are not playing video + if(fps == 0.0) + { + Lock(); + m_speedadjust = false; + UnLock(); + return -1; + } + + return -1; +} + +bool OMXClock::OMXReset() +{ + m_iCurrentPts = DVD_NOPTS_VALUE; + + m_video_clock = DVD_NOPTS_VALUE; + m_audio_clock = DVD_NOPTS_VALUE; + + if(m_omx_clock.GetComponent() != NULL) + { + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + OMXStop(); + + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + if(m_has_audio) + { + clock.nWaitMask |= OMX_CLOCKPORT0; + } + if(m_has_video) + { + clock.nWaitMask |= OMX_CLOCKPORT1; + clock.nWaitMask |= OMX_CLOCKPORT2; + } + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Reset error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + OMXStart(); + } + + return true; +} + +bool OMXClock::OMXInitialize(bool has_video, bool has_audio) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + CStdString componentName = ""; + + m_has_video = has_video; + m_has_audio = has_audio; + + componentName = "OMX.broadcom.clock"; + if(!m_omx_clock.Initialize((const CStdString)componentName, OMX_IndexParamOtherInit)) + return false; + + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + + if(m_has_audio) + { + clock.nWaitMask |= OMX_CLOCKPORT0; + } + if(m_has_video) + { + clock.nWaitMask |= OMX_CLOCKPORT1; + clock.nWaitMask |= OMX_CLOCKPORT2; + } + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Initialize error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock; + OMX_INIT_STRUCTURE(refClock); + + if(m_has_audio) + refClock.eClock = OMX_TIME_RefClockAudio; + else + refClock.eClock = OMX_TIME_RefClockVideo; + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeActiveRefClock, &refClock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Initialize error setting OMX_IndexConfigTimeCurrentAudioReference\n"); + return false; + } + + return true; +} + +void OMXClock::Deinitialize() +{ + m_omx_clock.Deinitialize(); +} + +bool OMXClock::OMXStatePause() +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(m_omx_clock.GetState() != OMX_StatePause) + { + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + omx_err = m_omx_clock.SetStateForComponent(OMX_StatePause); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::StatePause m_omx_clock.SetStateForComponent\n"); + return false; + } + } + + return true; +} + +bool OMXClock::OMXStateExecute() +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(m_omx_clock.GetState() != OMX_StateExecuting) + { + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + omx_err = m_omx_clock.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::StateExecute m_omx_clock.SetStateForComponent\n"); + return false; + } + } + + return true; +} + +void OMXClock::OMXStateIdle() +{ + if(m_omx_clock.GetComponent() == NULL) + return; + + if(m_omx_clock.GetState() == OMX_StateExecuting) + m_omx_clock.SetStateForComponent(OMX_StatePause); + + if(m_omx_clock.GetState() != OMX_StateIdle) + m_omx_clock.SetStateForComponent(OMX_StateIdle); +} + +COMXCoreComponent *OMXClock::GetOMXClock() +{ + if(!m_omx_clock.GetComponent()) + return NULL; + + return &m_omx_clock; +} + +bool OMXClock::OMXStop() +{ + if(m_omx_clock.GetComponent() == NULL) + { + return false; + } + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateStopped; + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Stop error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + return true; +} + +bool OMXClock::OMXStart() +{ + if(m_omx_clock.GetComponent() == NULL) + { + return false; + } + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateRunning; + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Start error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + return true; +} + +bool OMXClock::OMXPause() +{ + if(m_omx_clock.GetComponent() == NULL) + { + return false; + } + + if(m_pause) + return true; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = 0; // pause + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Pause error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + m_pause = true; + + return true; +} + +bool OMXClock::OMXResume() +{ + if(m_omx_clock.GetComponent() == NULL) + { + return false; + } + + if(!m_pause) + return true; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = (1<<16); // normal speed + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Resume error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + m_pause = false; + + return true; +} + +bool OMXClock::OMXWaitStart(double pts) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + if(pts == DVD_NOPTS_VALUE) + pts = 0; + + clock.nStartTime = ToOMXTime((uint64_t)pts); + + if(pts == DVD_NOPTS_VALUE) + { + clock.eState = OMX_TIME_ClockStateRunning; + clock.nWaitMask = 0; + } + else + { + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + + if(m_has_audio) + { + clock.nWaitMask |= OMX_CLOCKPORT0; + } + if(m_has_video) + { + clock.nWaitMask |= OMX_CLOCKPORT1; + clock.nWaitMask |= OMX_CLOCKPORT2; + } + } + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Initialize error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + return true; +} + +bool OMXClock::OMXSpeed(int speed) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = (speed << 16); + + m_play_speed = speed; + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Speed error setting OMX_IndexConfigTimeClockState\n"); + return false; + } + + return true; +} + +void OMXClock::AddTimespecs(struct timespec &time, long millisecs) +{ + time.tv_sec += millisecs / 1000; + time.tv_nsec += (millisecs % 1000) * 1000000; + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + +double OMXClock::GetPTS() +{ + Lock(); + double pts = m_iCurrentPts; + UnLock(); + return pts; +} + +void OMXClock::SetPTS(double pts) +{ + Lock(); + m_iCurrentPts = pts; + UnLock(); +}; + +bool OMXClock::HDMIClockSync() +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_CONFIG_LATENCYTARGETTYPE latencyTarget; + OMX_INIT_STRUCTURE(latencyTarget); + + latencyTarget.nPortIndex = OMX_ALL; + latencyTarget.bEnabled = OMX_TRUE; + latencyTarget.nFilter = 10; + latencyTarget.nTarget = 0; + latencyTarget.nShift = 3; + latencyTarget.nSpeedFactor = -200; + latencyTarget.nInterFactor = 100; + latencyTarget.nAdjCap = 100; + + omx_err = OMX_SetConfig(m_omx_clock.GetComponent(), OMX_IndexConfigLatencyTarget, &latencyTarget); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Speed error setting OMX_IndexConfigLatencyTarget\n"); + return false; + } + + return true; +} + +int64_t OMXClock::CurrentHostCounter(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return( ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec ); +} + +int64_t OMXClock::CurrentHostFrequency(void) +{ + return( (int64_t)1000000000L ); +} + +void OMXClock::AddTimeSpecNano(struct timespec &time, uint64_t nanoseconds) +{ + time.tv_sec += nanoseconds / 1000000000; + time.tv_nsec += (nanoseconds % 1000000000); + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + +void OMXClock::OMXSleep(unsigned int dwMilliSeconds) +{ + struct timespec req; + req.tv_sec = dwMilliSeconds / 1000; + req.tv_nsec = (dwMilliSeconds % 1000) * 1000000; + + while ( nanosleep(&req, &req) == -1 && errno == EINTR && (req.tv_nsec > 0 || req.tv_sec > 0)); +} + +int OMXClock::GetRefreshRate(double* interval) +{ + if(!interval) + return false; + + *interval = m_fps; + return true; +} diff --git a/OMXClock.h b/OMXClock.h new file mode 100644 index 00000000..d3a4752e --- /dev/null +++ b/OMXClock.h @@ -0,0 +1,156 @@ +/* + * 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 + * + */ + +#ifndef _AVCLOCK_H_ +#define _AVCLOCK_H_ + +#include "DllAvFormat.h" + +#include "OMXCore.h" + +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_PERCENT_MAX 10 +#define AUDIO_DIFF_AVG_NB 20 + +#define DVD_TIME_BASE 1000000 +#define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64 + +#define DVD_TIME_TO_SEC(x) ((int)((double)(x) / DVD_TIME_BASE)) +#define DVD_TIME_TO_MSEC(x) ((int)((double)(x) * 1000 / DVD_TIME_BASE)) +#define DVD_SEC_TO_TIME(x) ((double)(x) * DVD_TIME_BASE) +#define DVD_MSEC_TO_TIME(x) ((double)(x) * DVD_TIME_BASE / 1000) + +#define DVD_PLAYSPEED_PAUSE 0 // frame stepping +#define DVD_PLAYSPEED_NORMAL 1000 + +#ifdef OMX_SKIP64BIT +static inline OMX_TICKS ToOMXTime(int64_t pts) +{ + OMX_TICKS ticks; + ticks.nLowPart = pts; + ticks.nHighPart = pts >> 32; + return ticks; +} +static inline uint64_t FromOMXTime(OMX_TICKS ticks) +{ + uint64_t pts = ticks.nLowPart | ((uint64_t)ticks.nHighPart << 32); + return pts; +} +#else +#define FromOMXTime(x) (x) +#define ToOMXTime(x) (x) +#endif + +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, +}; + +class OMXClock +{ +protected: + double m_video_clock; + double m_audio_clock; + bool m_pause; + double m_iCurrentPts; + bool m_has_video; + bool m_has_audio; + int m_play_speed; + pthread_mutex_t m_lock; + void CheckSystemClock(); + double SystemToAbsolute(int64_t system); + double SystemToPlaying(int64_t system); + int64_t m_systemUsed; + int64_t m_startClock; + int64_t m_pauseClock; + double m_iDisc; + bool m_bReset; + static int64_t m_systemFrequency; + static int64_t m_systemOffset; + int64_t m_ClockOffset; + double m_maxspeedadjust; + bool m_speedadjust; + static bool m_ismasterclock; + double m_fps; +private: + COMXCoreComponent m_omx_clock; + DllAvFormat m_dllAvFormat; +public: + OMXClock(); + ~OMXClock(); + void Lock(); + void UnLock(); + int64_t GetFrequency(); + int64_t GetTime(bool interpolated = true); + double GetAbsoluteClock(bool interpolated = true); + int64_t Wait(int64_t Target); + double WaitAbsoluteClock(double target); + double GetClock(bool interpolated = true); + double GetClock(double& absolute, bool interpolated = true); + void SetSpeed(int iSpeed); + void SetMasterClock(bool ismasterclock) { m_ismasterclock = ismasterclock; } + bool IsMasterClock() { return m_ismasterclock; } + void Discontinuity(double currentPts = 0LL); + + void Reset() { m_bReset = true; } + void Pause(); + void Resume(); + + int UpdateFramerate(double fps, double* interval = NULL); + bool SetMaxSpeedAdjust(double speed); + + bool OMXReset(); + bool OMXInitialize(bool has_video, bool has_audio); + void Deinitialize(); + bool OMXIsPaused() { return m_pause; }; + bool OMXStop(); + bool OMXStart(); + bool OMXPause(); + bool OMXResume(); + bool OMXWaitStart(double pts); + bool OMXSpeed(int speed); + int OMXPlaySpeed() { return m_play_speed; }; + COMXCoreComponent *GetOMXClock(); + bool OMXStatePause(); + bool OMXStateExecute(); + void OMXStateIdle(); + double GetPTS(); + void SetPTS(double pts); + static void AddTimespecs(struct timespec &time, long millisecs); + bool HDMIClockSync(); + static int64_t CurrentHostCounter(void); + static int64_t CurrentHostFrequency(void); + void SetVideoClock(double video_clock) { m_video_clock = video_clock; }; + void SetAudioClock(double audio_clock) { m_audio_clock = audio_clock; }; + double GetVideoClock() { return m_video_clock; }; + double GetAudioClock() { return m_audio_clock; }; + bool HasVideo() { return m_has_video; }; + bool HasAudio() { return m_has_audio; }; + static void AddTimeSpecNano(struct timespec &time, uint64_t nanoseconds); + static void OMXSleep(unsigned int dwMilliSeconds); + + int GetRefreshRate(double* interval = NULL); + void SetRefreshRate(double fps) { m_fps = fps; }; +}; + +#endif diff --git a/OMXCore.cpp b/OMXCore.cpp new file mode 100644 index 00000000..b5023afc --- /dev/null +++ b/OMXCore.cpp @@ -0,0 +1,1642 @@ +/* + * Copyright (C) 2010 Team XBMCn + * 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 +#include + +//#include "linux/XTimeUtils.h" + +#if defined(HAVE_OMXLIB) +#include "OMXCore.h" +#include "utils/log.h" + +#include "OMXClock.h" + +#ifdef _LINUX +#include "XMemUtils.h" +#endif + +//#define OMX_USEBUFFER + +//#define OMX_DEBUG_EVENTS +//#define OMX_DEBUG_EVENTHANDLER + +//////////////////////////////////////////////////////////////////////////////////////////// +#define CLASSNAME "COMXCoreComponent" +//////////////////////////////////////////////////////////////////////////////////////////// + +static void add_timespecs(struct timespec &time, long millisecs) +{ + time.tv_sec += millisecs / 1000; + time.tv_nsec += (millisecs % 1000) * 1000000; + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + + +COMXCoreTunel::COMXCoreTunel() +{ + m_src_component = NULL; + m_dst_component = NULL; + m_src_port = 0; + m_dst_port = 0; + m_portSettingsChanged = false; + m_DllOMX = new DllOMX(); + m_DllOMXOpen = m_DllOMX->Load(); +} + +COMXCoreTunel::~COMXCoreTunel() +{ + Deestablish(); + if(m_DllOMXOpen) + m_DllOMX->Unload(); + delete m_DllOMX; +} + +void COMXCoreTunel::Initialize(COMXCoreComponent *src_component, unsigned int src_port, COMXCoreComponent *dst_component, unsigned int dst_port) +{ + if(!m_DllOMXOpen) + return; + m_src_component = src_component; + m_src_port = src_port; + m_dst_component = dst_component; + m_dst_port = dst_port; +} + +OMX_ERRORTYPE COMXCoreTunel::Flush() +{ + if(!m_DllOMXOpen) + return OMX_ErrorUndefined; + + if(!m_src_component || !m_dst_component) + return OMX_ErrorUndefined; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + if(m_src_component->GetComponent()) + { + omx_err = OMX_SendCommand(m_src_component->GetComponent(), OMX_CommandFlush, m_src_port, NULL); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Flush - Error flush port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = OMX_SendCommand(m_dst_component->GetComponent(), OMX_CommandFlush, m_dst_port, NULL); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Flush - Error flush port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->WaitForCommand(OMX_CommandFlush, m_src_port); + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->WaitForCommand(OMX_CommandFlush, m_dst_port); + } + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreTunel::Deestablish(bool noWait) +{ + if(!m_DllOMXOpen) + return OMX_ErrorUndefined; + + if(!m_src_component || !m_dst_component) + return OMX_ErrorUndefined; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(m_src_component->GetComponent() && m_portSettingsChanged && !noWait) + { + omx_err = m_src_component->WaitForEvent(OMX_EventPortSettingsChanged); + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->DisablePort(m_src_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - Error disable port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->DisablePort(m_dst_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - Error disable port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_DllOMX->OMX_SetupTunnel(m_src_component->GetComponent(), m_src_port, NULL, 0); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorIncorrectStateOperation) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - could not unset tunnel on comp src %s port %d omx_err(0x%08x)\n", + m_src_component->GetName().c_str(), m_src_port, (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_DllOMX->OMX_SetupTunnel(m_dst_component->GetComponent(), m_dst_port, NULL, 0); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorIncorrectStateOperation) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - could not unset tunnel on comp dst %s port %d omx_err(0x%08x)\n", + m_dst_component->GetName().c_str(), m_dst_port, (int)omx_err); + } + } + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreTunel::Establish(bool portSettingsChanged) +{ + if(!m_DllOMXOpen) + return OMX_ErrorUndefined; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_PARAM_U32TYPE param; + OMX_INIT_STRUCTURE(param); + + if(!m_src_component || !m_dst_component) + return OMX_ErrorUndefined; + + if(m_src_component->GetState() == OMX_StateLoaded) + { + omx_err = m_src_component->SetStateForComponent(OMX_StateIdle); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error setting state to idle %s omx_err(0x%08x)", + m_src_component->GetName().c_str(), (int)omx_err); + return omx_err; + } + } + + if(portSettingsChanged) + { + omx_err = m_src_component->WaitForEvent(OMX_EventPortSettingsChanged); + if(omx_err != OMX_ErrorNone) + { + return omx_err; + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->DisablePort(m_src_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error disable port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->DisablePort(m_dst_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error disable port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + } + } + + /* + OMX_INIT_STRUCTURE(param); + param.nPortIndex = m_src_port; + omx_err = m_src_component->GetParameter(OMX_IndexParamNumAvailableStreams, ¶m); + if(omx_err == OMX_ErrorNone) + { + param.nU32 = 0; + m_src_component->SetParameter(OMX_IndexParamActiveStream, ¶m); + } + */ + + if(m_src_component->GetComponent() && m_dst_component->GetComponent()) + { + omx_err = m_DllOMX->OMX_SetupTunnel(m_src_component->GetComponent(), m_src_port, m_dst_component->GetComponent(), m_dst_port); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - could not setup tunnel src %s port %d dst %s port %d omx_err(0x%08x)\n", + m_src_component->GetName().c_str(), m_src_port, m_dst_component->GetName().c_str(), m_dst_port, (int)omx_err); + return omx_err; + } + } + else + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - could not setup tunnel\n"); + return OMX_ErrorUndefined; + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->EnablePort(m_src_port, false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error enable port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + return omx_err; + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->EnablePort(m_dst_port, false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error enable port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + return omx_err; + } + } + + if(m_dst_component->GetComponent()) + { + if(m_dst_component->GetState() == OMX_StateLoaded) + { + omx_err = m_dst_component->WaitForCommand(OMX_CommandPortEnable, m_dst_port); + if(omx_err != OMX_ErrorNone) + return omx_err; + + omx_err = m_dst_component->SetStateForComponent(OMX_StateIdle); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error setting state to idle %s omx_err(0x%08x)", + m_src_component->GetName().c_str(), (int)omx_err); + return omx_err; + } + } + else + { + omx_err = m_dst_component->WaitForCommand(OMX_CommandPortEnable, m_dst_port); + if(omx_err != OMX_ErrorNone) + return omx_err; + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->WaitForCommand(OMX_CommandPortEnable, m_src_port); + if(omx_err != OMX_ErrorNone) + return omx_err; + } + + m_portSettingsChanged = portSettingsChanged; + + return OMX_ErrorNone; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +COMXCoreComponent::COMXCoreComponent() +{ + m_input_port = 0; + m_output_port = 0; + m_handle = NULL; + + m_input_alignment = 0; + m_input_buffer_size = 0; + m_input_buffer_count = 0; + + m_output_alignment = 0; + m_output_buffer_size = 0; + m_output_buffer_count = 0; + m_flush_input = false; + m_flush_output = false; + + m_eos = false; + + m_exit = false; + m_DllOMXOpen = false; + + pthread_mutex_init(&m_omx_input_mutex, NULL); + pthread_mutex_init(&m_omx_output_mutex, NULL); + pthread_mutex_init(&m_omx_event_mutex, NULL); + pthread_cond_init(&m_input_buffer_cond, NULL); + pthread_cond_init(&m_output_buffer_cond, NULL); + pthread_cond_init(&m_omx_event_cond, NULL); + + for(int i = 0; i < OMX_MAX_PORTS; i++) + m_ports_enabled[i] = -1; + + m_DllOMX = new DllOMX(); +} +COMXCoreComponent::~COMXCoreComponent() +{ + Deinitialize(); + + pthread_mutex_destroy(&m_omx_input_mutex); + pthread_mutex_destroy(&m_omx_output_mutex); + pthread_mutex_destroy(&m_omx_event_mutex); + pthread_cond_destroy(&m_input_buffer_cond); + pthread_cond_destroy(&m_output_buffer_cond); + pthread_cond_destroy(&m_omx_event_cond); + + delete m_DllOMX; +} +OMX_ERRORTYPE COMXCoreComponent::EmptyThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle || !omx_buffer) + return OMX_ErrorUndefined; + + omx_err = OMX_EmptyThisBuffer(m_handle, omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::EmptyThisBuffer component(%s) - failed with result(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FillThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle || !omx_buffer) + return OMX_ErrorUndefined; + + omx_err = OMX_FillThisBuffer(m_handle, omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FillThisBuffer component(%s) - failed with result(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FreeOutputBuffer(OMX_BUFFERHEADERTYPE *omx_buffer) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle || !omx_buffer) + return OMX_ErrorUndefined; + + omx_err = OMX_FreeBuffer(m_handle, m_output_port, omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FreeOutputBuffer component(%s) - failed with result(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + return omx_err; +} + +unsigned int COMXCoreComponent::GetInputBufferSize() +{ + int free = m_input_buffer_count * m_input_buffer_size; + return free; +} + +unsigned int COMXCoreComponent::GetOutputBufferSize() +{ + int free = m_output_buffer_count * m_output_buffer_size; + return free; +} + +unsigned int COMXCoreComponent::GetInputBufferSpace() +{ + int free = m_omx_input_avaliable.size() * m_input_buffer_size; + return free; +} + +unsigned int COMXCoreComponent::GetOutputBufferSpace() +{ + int free = m_omx_output_avaliable.size() * m_output_buffer_size; + return free; +} + +void COMXCoreComponent::FlushAll() +{ + FlushInput(); + FlushOutput(); +} + +void COMXCoreComponent::FlushInput() +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + omx_err = SendCommand(OMX_CommandFlush, m_input_port, NULL); + + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FlushInput - Error on component %s omx_err(0x%08x)", + m_componentName.c_str(), (int)omx_err); + } + WaitForCommand(OMX_CommandFlush, m_input_port); +} + +void COMXCoreComponent::FlushOutput() +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + omx_err = SendCommand(OMX_CommandFlush, m_output_port, NULL); + + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FlushOutput - Error on component %s omx_err(0x%08x)", + m_componentName.c_str(), (int)omx_err); + } + WaitForCommand(OMX_CommandFlush, m_output_port); +} + +// timeout in milliseconds +OMX_BUFFERHEADERTYPE *COMXCoreComponent::GetInputBuffer(long timeout) +{ + OMX_BUFFERHEADERTYPE *omx_input_buffer = NULL; + + if(!m_handle) + return NULL; + + pthread_mutex_lock(&m_omx_input_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + while (1 && !m_flush_input) + { + if(!m_omx_input_avaliable.empty()) + { + omx_input_buffer = m_omx_input_avaliable.front(); + m_omx_input_avaliable.pop(); + break; + } + + int retcode = pthread_cond_timedwait(&m_input_buffer_cond, &m_omx_input_mutex, &endtime); + if (retcode != 0) { + CLog::Log(LOGERROR, "COMXCoreComponent::GetInputBuffer %s wait event timeout\n", m_componentName.c_str()); + break; + } + } + pthread_mutex_unlock(&m_omx_input_mutex); + return omx_input_buffer; +} + +// timeout in milliseconds +OMX_BUFFERHEADERTYPE *COMXCoreComponent::GetOutputBuffer(long timeout) +{ + OMX_BUFFERHEADERTYPE *omx_output_buffer = NULL; + + if(!m_handle) + return NULL; + + pthread_mutex_lock(&m_omx_output_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + //while (1 && !m_flush_output) + //{ + if(!m_omx_output_avaliable.empty()) + { + omx_output_buffer = m_omx_output_avaliable.front(); + m_omx_output_avaliable.pop(); + //break; + } + + // int retcode = pthread_cond_timedwait(&m_output_buffer_cond, &m_omx_output_mutex, &endtime); + // if (retcode != 0) { + // CLog::Log(LOGERROR, "COMXCoreComponent::GetOutputBuffer %s wait event timeout\n", m_componentName.c_str()); + // break; + // } + //} + pthread_mutex_unlock(&m_omx_output_mutex); + return omx_output_buffer; +} + +OMX_ERRORTYPE COMXCoreComponent::AllocInputBuffers(void) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + OMX_PARAM_PORTDEFINITIONTYPE portFormat; + OMX_INIT_STRUCTURE(portFormat); + portFormat.nPortIndex = m_input_port; + + omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat); + if(omx_err != OMX_ErrorNone) + return omx_err; + + if(GetState() != OMX_StateIdle) + { + if(GetState() != OMX_StateLoaded) + SetStateForComponent(OMX_StateLoaded); + + SetStateForComponent(OMX_StateIdle); + } + + omx_err = EnablePort(m_input_port, false); + if(omx_err != OMX_ErrorNone) + return omx_err; + + if(GetState() == OMX_StateLoaded) + SetStateForComponent(OMX_StateIdle); + + m_input_alignment = portFormat.nBufferAlignment; + m_input_buffer_count = portFormat.nBufferCountActual; + m_input_buffer_size = portFormat.nBufferSize; + + CLog::Log(LOGDEBUG, "COMXCoreComponent::AllocInputBuffers component(%s) - iport(%d), nBufferCountMin(%lu), nBufferCountActual(%lu), nBufferSize(%lu), nBufferAlignmen(%lu)\n", + m_componentName.c_str(), GetInputPort(), portFormat.nBufferCountMin, + portFormat.nBufferCountActual, portFormat.nBufferSize, portFormat.nBufferAlignment); + + for (size_t i = 0; i < portFormat.nBufferCountActual; i++) + { + OMX_BUFFERHEADERTYPE *buffer = NULL; +#ifdef OMX_USEBUFFER + OMX_U8* data = (OMX_U8*)_aligned_malloc(portFormat.nBufferSize, m_input_alignment); + omx_err = OMX_UseBuffer(m_handle, &buffer, m_input_port, NULL, portFormat.nBufferSize, data); +#else + omx_err = OMX_AllocateBuffer(m_handle, &buffer, m_input_port, NULL, portFormat.nBufferSize); +#endif + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::AllocInputBuffers component(%s) - OMX_UseBuffer failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); +#ifdef OMX_USEBUFFER + _aligned_free(data); +#endif + return omx_err; + } + buffer->nInputPortIndex = m_input_port; + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->pAppPrivate = (void*)i; + m_omx_input_buffers.push_back(buffer); + m_omx_input_avaliable.push(buffer); + } + + omx_err = WaitForCommand(OMX_CommandPortEnable, m_input_port); + + m_flush_input = false; + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::AllocOutputBuffers(void) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + OMX_PARAM_PORTDEFINITIONTYPE portFormat; + OMX_INIT_STRUCTURE(portFormat); + portFormat.nPortIndex = m_output_port; + + omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat); + if(omx_err != OMX_ErrorNone) + return omx_err; + + if(GetState() != OMX_StateIdle) + { + if(GetState() != OMX_StateLoaded) + SetStateForComponent(OMX_StateLoaded); + + SetStateForComponent(OMX_StateIdle); + } + + omx_err = EnablePort(m_output_port, false); + if(omx_err != OMX_ErrorNone) + return omx_err; + + if(GetState() == OMX_StateLoaded) + SetStateForComponent(OMX_StateIdle); + + m_output_alignment = portFormat.nBufferAlignment; + m_output_buffer_count = portFormat.nBufferCountActual; + m_output_buffer_size = portFormat.nBufferSize; + + CLog::Log(LOGDEBUG, "COMXCoreComponent::AllocOutputBuffers component(%s) - iport(%d), nBufferCountMin(%lu), nBufferCountActual(%lu), nBufferSize(%lu) nBufferAlignmen(%lu)\n", + m_componentName.c_str(), m_output_port, portFormat.nBufferCountMin, + portFormat.nBufferCountActual, portFormat.nBufferSize, portFormat.nBufferAlignment); + + for (size_t i = 0; i < portFormat.nBufferCountActual; i++) + { + OMX_BUFFERHEADERTYPE *buffer = NULL; +#ifdef OMX_USEBUFFER + OMX_U8* data = (OMX_U8*)_aligned_malloc(portFormat.nBufferSize, m_output_alignment); + omx_err = OMX_UseBuffer(m_handle, &buffer, m_output_port, NULL, portFormat.nBufferSize, data); +#else + omx_err = OMX_AllocateBuffer(m_handle, &buffer, m_output_port, NULL, portFormat.nBufferSize); +#endif + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::AllocOutputBuffers component(%s) - OMX_UseBuffer failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); +#ifdef OMX_USEBUFFER + _aligned_free(data); +#endif + return omx_err; + } + buffer->nOutputPortIndex = m_output_port; + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->pAppPrivate = (void*)i; + m_omx_output_buffers.push_back(buffer); + m_omx_output_avaliable.push(buffer); + } + + omx_err = WaitForCommand(OMX_CommandPortEnable, m_output_port); + + m_flush_output = false; + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FreeInputBuffers(bool wait) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + if(m_omx_input_buffers.empty()) + return OMX_ErrorNone; + + m_flush_input = true; + + pthread_mutex_lock(&m_omx_input_mutex); + pthread_cond_broadcast(&m_input_buffer_cond); + + omx_err = DisablePort(m_input_port, wait); + + assert(m_omx_input_buffers.size() == m_omx_input_avaliable.size()); + + for (size_t i = 0; i < m_omx_input_buffers.size(); i++) + { +#ifdef OMX_USEBUFFER + uint8_t *buf = m_omx_input_buffers[i]->pBuffer; +#endif + omx_err = OMX_FreeBuffer(m_handle, m_input_port, m_omx_input_buffers[i]); +#ifdef OMX_USEBUFFER + if(buf) + _aligned_free(buf); +#endif + //m_omx_input_buffers[i]->pBuffer = NULL; + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FreeInputBuffers error deallocate omx input buffer on component %s omx_err(0x%08x)\n", m_componentName.c_str(), omx_err); + } + } + + m_omx_input_buffers.clear(); + + while (!m_omx_input_avaliable.empty()) + m_omx_input_avaliable.pop(); + + m_input_alignment = 0; + m_input_buffer_size = 0; + m_input_buffer_count = 0; + + pthread_mutex_unlock(&m_omx_input_mutex); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FreeOutputBuffers(bool wait) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + if(m_omx_output_buffers.empty()) + return OMX_ErrorNone; + + m_flush_output = true; + + pthread_mutex_lock(&m_omx_output_mutex); + pthread_cond_broadcast(&m_output_buffer_cond); + + omx_err = DisablePort(m_output_port, false); + + assert(m_omx_output_buffers.size() == m_omx_output_avaliable.size()); + + for (size_t i = 0; i < m_omx_output_buffers.size(); i++) + { +#ifdef OMX_USEBUFFER + uint8_t *buf = m_omx_output_buffers[i]->pBuffer; +#endif + omx_err = OMX_FreeBuffer(m_handle, m_output_port, m_omx_output_buffers[i]); +#ifdef OMX_USEBUFFER + if(buf) + _aligned_free(buf); +#endif + //m_omx_output_buffers[i]->pBuffer = NULL; + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FreeOutputBuffers error deallocate omx output buffer on component %s omx_err(0x%08x)\n", m_componentName.c_str(), omx_err); + } + } + + m_omx_output_buffers.clear(); + + while (!m_omx_output_avaliable.empty()) + m_omx_output_avaliable.pop(); + + m_output_alignment = 0; + m_output_buffer_size = 0; + m_output_buffer_count = 0; + + pthread_mutex_unlock(&m_omx_output_mutex); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::DisableAllPorts() +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + OMX_INDEXTYPE idxTypes[] = { + OMX_IndexParamAudioInit, + OMX_IndexParamImageInit, + OMX_IndexParamVideoInit, + OMX_IndexParamOtherInit + }; + + OMX_PORT_PARAM_TYPE ports; + OMX_INIT_STRUCTURE(ports); + + int i; + for(i=0; i < 4; i++) + { + omx_err = OMX_GetParameter(m_handle, idxTypes[i], &ports); + if(omx_err == OMX_ErrorNone) { + + uint32_t j; + for(j=0; j::iterator it = m_omx_events.begin(); it != m_omx_events.end(); it++) { + omx_event event = *it; + + if(event.eEvent == eEvent && event.nData1 == nData1 && event.nData2 == nData2) { + m_omx_events.erase(it); + return; + } + } +} + +OMX_ERRORTYPE COMXCoreComponent::AddEvent(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2) +{ + omx_event event; + + event.eEvent = eEvent; + event.nData1 = nData1; + event.nData2 = nData2; + + pthread_mutex_lock(&m_omx_event_mutex); + Remove(eEvent, nData1, nData2); + m_omx_events.push_back(event); + // this allows (all) blocked tasks to be awoken + pthread_cond_broadcast(&m_omx_event_cond); + pthread_mutex_unlock(&m_omx_event_mutex); + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::AddEvent %s add event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + return OMX_ErrorNone; +} + + +/* +bool COMXCoreComponent::GotError(OMX_ERRORTYPE errorType) +{ +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::GotError %s search error 0x%08x\n", + m_componentName.c_str(), (int)errorType); +#endif + + long timeout = 300; // milliseconds + pthread_mutex_lock(&m_omx_event_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + + while(true) { + for (std::vector::iterator it = m_omx_events.begin(); it != m_omx_events.end(); it++) { + omx_event event = *it; + + printf("hansi\n"); +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::GotError %s inlist error event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + if(event.eEvent == OMX_EventError && (OMX_S32)event.nData1 == errorType) + { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::GotError %s remove error event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + //pthread_mutex_lock(&m_omx_event_mutex); + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return true; + } + } + + int retcode = pthread_cond_timedwait(&m_omx_event_cond, &m_omx_event_mutex, &endtime); + if (retcode != 0) { + CLog::Log(LOGERROR, "COMXCoreComponent::GotError %s wait event timeout 0x%08x\n", + m_componentName.c_str(), (int)errorType); + pthread_mutex_unlock(&m_omx_event_mutex); + return false; + } + } + + pthread_mutex_unlock(&m_omx_event_mutex); + return false; +} +*/ + +// timeout in milliseconds +OMX_ERRORTYPE COMXCoreComponent::WaitForEvent(OMX_EVENTTYPE eventType, long timeout) +{ +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s wait event 0x%08x\n", + m_componentName.c_str(), (int)eventType); +#endif + + pthread_mutex_lock(&m_omx_event_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + while(true) { + + for (std::vector::iterator it = m_omx_events.begin(); it != m_omx_events.end(); it++) { + omx_event event = *it; + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s inlist event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + + if(event.eEvent == OMX_EventError && event.nData1 == (OMX_U32)OMX_ErrorSameState && event.nData2 == 1) + { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } else if(event.eEvent == OMX_EventError) { + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return (OMX_ERRORTYPE)event.nData1; + } else if(event.eEvent == eventType) { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } + } + + int retcode = pthread_cond_timedwait(&m_omx_event_cond, &m_omx_event_mutex, &endtime); + if (retcode != 0) { + CLog::Log(LOGERROR, "COMXCoreComponent::WaitForEvent %s wait event 0x%08x timeout %ld\n", + m_componentName.c_str(), (int)eventType, timeout); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorHardware; + } + } + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; +} + +// timeout in milliseconds +OMX_ERRORTYPE COMXCoreComponent::WaitForCommand(OMX_U32 command, OMX_U32 nData2, long timeout) +{ +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s wait event.eEvent 0x%08x event.command 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)OMX_EventCmdComplete, (int)command, (int)nData2); +#endif + + //printf("COMXCoreComponent::WaitForCommand %s 0x%08x,0x%08x,0x%08x\n", m_componentName.c_str(), (int)OMX_EventCmdComplete, (int)command, (int)nData2); + pthread_mutex_lock(&m_omx_event_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + while(true) { + + for (std::vector::iterator it = m_omx_events.begin(); it != m_omx_events.end(); it++) { + omx_event event = *it; + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s inlist event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + //printf("COMXCoreComponent::WaitForCommand %s inlist event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); + if(event.eEvent == OMX_EventError && event.nData1 == (OMX_U32)OMX_ErrorSameState && event.nData2 == 1) + { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } else if(event.eEvent == OMX_EventError) { + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return (OMX_ERRORTYPE)event.nData1; + } else if(event.eEvent == OMX_EventCmdComplete && event.nData1 == command && event.nData2 == nData2) { + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } + } + + int retcode = pthread_cond_timedwait(&m_omx_event_cond, &m_omx_event_mutex, &endtime); + if (retcode != 0) { + CLog::Log(LOGERROR, "COMXCoreComponent::WaitForCommand %s wait timeout event.eEvent 0x%08x event.command 0x%08x event.nData2 0x%08x\n", + m_componentName.c_str(), (int)OMX_EventCmdComplete, (int)command, (int)nData2); + + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorHardware; + } + } + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreComponent::SetStateForComponent(OMX_STATETYPE state) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_STATETYPE state_actual = OMX_StateMax; + + if(!m_handle) + return OMX_ErrorUndefined; + + OMX_GetState(m_handle, &state_actual); + if(state == state_actual) + return OMX_ErrorNone; + + omx_err = OMX_SendCommand(m_handle, OMX_CommandStateSet, state, 0); + if (omx_err != OMX_ErrorNone) + { + if(omx_err == OMX_ErrorSameState) + { + omx_err = OMX_ErrorNone; + } + else + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetStateForComponent - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + } + else + { + omx_err = WaitForCommand(OMX_CommandStateSet, state); + if(omx_err == OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetStateForComponent - %s ignore OMX_ErrorSameState\n", + m_componentName.c_str()); + return OMX_ErrorNone; + } + } + + return omx_err; +} + +OMX_STATETYPE COMXCoreComponent::GetState() +{ + OMX_STATETYPE state; + if(m_handle) + { + OMX_GetState(m_handle, &state); + return state; + } + + return (OMX_STATETYPE)0; +} + +OMX_ERRORTYPE COMXCoreComponent::SetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct) +{ + OMX_ERRORTYPE omx_err; + + omx_err = OMX_SetParameter(m_handle, paramIndex, paramStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetParameter - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::GetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct) +{ + OMX_ERRORTYPE omx_err; + + omx_err = OMX_GetParameter(m_handle, paramIndex, paramStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::GetParameter - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::SetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct) +{ + OMX_ERRORTYPE omx_err; + + omx_err = OMX_SetConfig(m_handle, configIndex, configStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetConfig - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::GetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct) +{ + OMX_ERRORTYPE omx_err; + + omx_err = OMX_GetConfig(m_handle, configIndex, configStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::GetConfig - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::SendCommand(OMX_COMMANDTYPE cmd, OMX_U32 cmdParam, OMX_PTR cmdParamData) +{ + OMX_ERRORTYPE omx_err; + + omx_err = OMX_SendCommand(m_handle, cmd, cmdParam, cmdParamData); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SendCommand - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::EnablePort(unsigned int port, bool wait) +{ + bool bEnabled = false; + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + for(int i = 0; i < OMX_MAX_PORTS; i++) + { + if(m_ports_enabled[i] == (int)port) + { + bEnabled = true; + break; + } + } + + if(!bEnabled) + { + omx_err = SendCommand(OMX_CommandPortEnable, port, NULL); + //omx_err = OMX_SendCommand(m_handle, OMX_CommandPortEnable, port, NULL); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::EnablePort - Error enable port %d on component %s omx_err(0x%08x)", + port, m_componentName.c_str(), (int)omx_err); + return omx_err; + } + else + { + if(wait) + omx_err = WaitForEvent(OMX_EventCmdComplete); + + for(int i = 0; i < OMX_MAX_PORTS; i++) + { + if(m_ports_enabled[i] == -1) + { + m_ports_enabled[i] = (int)port; + break; + } + } + } + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::DisablePort(unsigned int port, bool wait) +{ + bool bEnabled = false; + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + for(int i = 0; i < OMX_MAX_PORTS; i++) + { + if(m_ports_enabled[i] == (int)port) + { + bEnabled = true; + break; + } + } + + if(bEnabled) + { + omx_err = SendCommand(OMX_CommandPortDisable, port, NULL); + //omx_err = OMX_SendCommand(m_handle, OMX_CommandPortDisable, port, NULL); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::DIsablePort - Error disable port %d on component %s omx_err(0x%08x)", + port, m_componentName.c_str(), (int)omx_err); + return omx_err; + } + else + { + if(wait) + omx_err = WaitForEvent(OMX_EventCmdComplete); + + for(int i = 0; i < OMX_MAX_PORTS; i++) + { + if(m_ports_enabled[i] == (int)port) + { + m_ports_enabled[i] = -1; + break; + } + } + } + } + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage) +{ + OMX_ERRORTYPE omx_err; + + omx_err = OMX_UseEGLImage(m_handle, ppBufferHdr, nPortIndex, pAppPrivate, eglImage); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::UseEGLImage - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + return omx_err; +} + +bool COMXCoreComponent::Initialize( const CStdString &component_name, OMX_INDEXTYPE index) +{ + OMX_ERRORTYPE omx_err; + + if(!m_DllOMX->Load()) + return false; + + m_DllOMXOpen = true; + + m_componentName = component_name; + + m_callbacks.EventHandler = &COMXCoreComponent::DecoderEventHandlerCallback; + m_callbacks.EmptyBufferDone = &COMXCoreComponent::DecoderEmptyBufferDoneCallback; + m_callbacks.FillBufferDone = &COMXCoreComponent::DecoderFillBufferDoneCallback; + + // Get video component handle setting up callbacks, component is in loaded state on return. + omx_err = m_DllOMX->OMX_GetHandle(&m_handle, (char*)component_name.c_str(), this, &m_callbacks); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Initialize - could not get component handle for %s omx_err(0x%08x)\n", + component_name.c_str(), (int)omx_err); + Deinitialize(); + return false; + } + + OMX_PORT_PARAM_TYPE port_param; + OMX_INIT_STRUCTURE(port_param); + + omx_err = OMX_GetParameter(m_handle, index, &port_param); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Initialize - could not get port_param for component %s omx_err(0x%08x)\n", + component_name.c_str(), (int)omx_err); + } + + omx_err = DisableAllPorts(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Initialize - error disable ports on component %s omx_err(0x%08x)\n", + component_name.c_str(), (int)omx_err); + } + + m_input_port = port_param.nStartPortNumber; + m_output_port = m_input_port + 1; + + if(m_componentName == "OMX.broadcom.audio_mixer") + { + m_input_port = port_param.nStartPortNumber + 1; + m_output_port = port_param.nStartPortNumber; + } + + if (m_output_port > port_param.nStartPortNumber+port_param.nPorts-1) + m_output_port = port_param.nStartPortNumber+port_param.nPorts-1; + + CLog::Log(LOGDEBUG, "COMXCoreComponent::Initialize %s input port %d output port %d\n", + m_componentName.c_str(), m_input_port, m_output_port); + + m_exit = false; + m_flush_input = false; + m_flush_output = false; + + return true; +} + +bool COMXCoreComponent::Deinitialize() +{ + OMX_ERRORTYPE omx_err; + + if(!m_DllOMXOpen) + return false; + + m_exit = true; + + m_flush_input = true; + m_flush_output = true; + + if(m_handle) + { + + FlushAll(); + + if(GetState() == OMX_StateExecuting) + SetStateForComponent(OMX_StatePause); + + if(GetState() != OMX_StateIdle) + SetStateForComponent(OMX_StateIdle); + + FreeOutputBuffers(true); + FreeInputBuffers(true); + + if(GetState() != OMX_StateIdle) + SetStateForComponent(OMX_StateIdle); + + if(GetState() != OMX_StateLoaded) + SetStateForComponent(OMX_StateLoaded); + + omx_err = m_DllOMX->OMX_FreeHandle(m_handle); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deinitialize - failed to free handle for component %s omx_err(0x%08x)", + m_componentName.c_str(), omx_err); + } + + m_handle = NULL; + } + + m_input_port = 0; + m_output_port = 0; + m_componentName = ""; + m_DllOMXOpen = false; + + m_DllOMX->Unload(); + + for(int i = 0; i < OMX_MAX_PORTS; i++) + m_ports_enabled[i] = -1; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// DecoderEventHandler -- OMX event callback +OMX_ERRORTYPE COMXCoreComponent::DecoderEventHandlerCallback( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, + OMX_U32 nData1, + OMX_U32 nData2, + OMX_PTR pEventData) +{ + if(!pAppData) + return OMX_ErrorNone; + + COMXCoreComponent *comp = static_cast(pAppData); + return comp->DecoderEventHandler(hComponent, pAppData, eEvent, nData1, nData2, pEventData); +} + +// DecoderEmptyBufferDone -- OMXCore input buffer has been emptied +OMX_ERRORTYPE COMXCoreComponent::DecoderEmptyBufferDoneCallback( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData) + return OMX_ErrorNone; + + COMXCoreComponent *comp = static_cast(pAppData); + return comp->DecoderEmptyBufferDone( hComponent, pAppData, pBuffer); +} + +// DecoderFillBufferDone -- OMXCore output buffer has been filled +OMX_ERRORTYPE COMXCoreComponent::DecoderFillBufferDoneCallback( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData) + return OMX_ErrorNone; + + COMXCoreComponent *comp = static_cast(pAppData); + return comp->DecoderFillBufferDone(hComponent, pAppData, pBuffer); +} + +OMX_ERRORTYPE COMXCoreComponent::DecoderEmptyBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData || m_exit) + return OMX_ErrorNone; + + COMXCoreComponent *ctx = static_cast(pAppData); + + pthread_mutex_lock(&ctx->m_omx_input_mutex); + ctx->m_omx_input_avaliable.push(pBuffer); + + // this allows (all) blocked tasks to be awoken + pthread_cond_broadcast(&m_input_buffer_cond); + + pthread_mutex_unlock(&ctx->m_omx_input_mutex); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreComponent::DecoderFillBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData || m_exit) + return OMX_ErrorNone; + + COMXCoreComponent *ctx = static_cast(pAppData); + + pthread_mutex_lock(&ctx->m_omx_output_mutex); + ctx->m_omx_output_avaliable.push(pBuffer); + + // this allows (all) blocked tasks to be awoken + pthread_cond_broadcast(&m_output_buffer_cond); + + pthread_mutex_unlock(&ctx->m_omx_output_mutex); + + return OMX_ErrorNone; +} + +// DecoderEmptyBufferDone -- OMXCore input buffer has been emptied +//////////////////////////////////////////////////////////////////////////////////////////// +// Component event handler -- OMX event callback +OMX_ERRORTYPE COMXCoreComponent::DecoderEventHandler( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, + OMX_U32 nData1, + OMX_U32 nData2, + OMX_PTR pEventData) +{ + COMXCoreComponent *comp = static_cast(pAppData); + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, + "COMXCore::%s - %s eEvent(0x%x), nData1(0x%lx), nData2(0x%lx), pEventData(0x%p)\n", + __func__, (char *)m_componentName.c_str(), eEvent, nData1, nData2, pEventData); +#endif + + AddEvent(eEvent, nData1, nData2); + + switch (eEvent) + { + case OMX_EventCmdComplete: + + switch(nData1) + { + case OMX_CommandStateSet: + switch ((int)nData2) + { + case OMX_StateInvalid: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateInvalid\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateLoaded: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateLoaded\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateIdle: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateIdle\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateExecuting: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateExecuting\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StatePause: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StatePause\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateWaitForResources: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateWaitForResources\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + default: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, + "%s::%s %s - Unknown OMX_Statexxxxx, state(%d)\n", CLASSNAME, __func__, comp->GetName().c_str(), (int)nData2); + #endif + break; + } + break; + case OMX_CommandFlush: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandFlush, nData2(0x%lx)\n", CLASSNAME, __func__, comp->GetName().c_str(), nData2); + #endif + break; + case OMX_CommandPortDisable: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandPortDisable, nData1(0x%lx), nData2(0x%lx)\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, nData2); + #endif + break; + case OMX_CommandPortEnable: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandPortEnable, nData1(0x%lx), nData2(0x%lx)\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, nData2); + #endif + break; + #if defined(OMX_DEBUG_EVENTHANDLER) + case OMX_CommandMarkBuffer: + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandMarkBuffer, nData1(0x%lx), nData2(0x%lx)\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, nData2); + break; + #endif + } + break; + case OMX_EventBufferFlag: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_EventBufferFlag(input)\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + switch(nData2) + { + case OMX_BUFFERFLAG_EOS: + m_eos = true; + break; + default: + break; + } + break; + case OMX_EventPortSettingsChanged: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_EventPortSettingsChanged(output)\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + /* + if((unsigned int)nData1 == comp->GetOutputPort()) + { + comp->SendCommand(OMX_CommandPortDisable, comp->GetInputPort(), NULL); + comp->SendCommand(OMX_CommandPortDisable, comp->GetOutputPort(), NULL); + } + */ + break; + #if defined(OMX_DEBUG_EVENTHANDLER) + case OMX_EventMark: + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_EventMark\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + case OMX_EventResourcesAcquired: + CLog::Log(LOGDEBUG, "%s::%s %s- OMX_EventResourcesAcquired\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + #endif + case OMX_EventError: + switch((OMX_S32)nData1) + { + case OMX_ErrorSameState: + break; + case OMX_ErrorInsufficientResources: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorInsufficientResources, insufficient resources\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + case OMX_ErrorFormatNotDetected: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorFormatNotDetected, cannot parse input stream\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + case OMX_ErrorPortUnpopulated: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorPortUnpopulated (%ld), cannot parse input stream\n", CLASSNAME, __func__, comp->GetName().c_str(), (OMX_S32)nData2); + break; + case OMX_ErrorStreamCorrupt: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorStreamCorrupt, Bitstream corrupt\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + default: + CLog::Log(LOGERROR, "%s::%s %s - OMX_EventError detected, nData1(0x%lx), nData2(0x%lx)\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, nData2); + break; + } + break; + default: + CLog::Log(LOGWARNING, "%s::%s %s - Unknown eEvent(0x%x), nData1(0x%lx), nData2(0x%lx)\n", CLASSNAME, __func__, comp->GetName().c_str(), eEvent, nData1, nData2); + break; + } + + return OMX_ErrorNone; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +COMXCore::COMXCore() +{ + m_is_open = false; + + m_DllOMX = new DllOMX(); +} + +COMXCore::~COMXCore() +{ + delete m_DllOMX; +} + +bool COMXCore::Initialize() +{ + if(!m_DllOMX->Load()) + return false; + + OMX_ERRORTYPE omx_err = m_DllOMX->OMX_Init(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCore::Initialize - OMXCore failed to init, omx_err(0x%08x)", omx_err); + return false; + } + + m_is_open = true; + return true; +} + +void COMXCore::Deinitialize() +{ + if(m_is_open) + { + OMX_ERRORTYPE omx_err = m_DllOMX->OMX_Deinit(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCore::Deinitialize - OMXCore failed to deinit, omx_err(0x%08x)", omx_err); + } + m_DllOMX->Unload(); + } +} + +#endif diff --git a/OMXCore.h b/OMXCore.h new file mode 100644 index 00000000..dbb31ae7 --- /dev/null +++ b/OMXCore.h @@ -0,0 +1,216 @@ +#pragma once +/* + * Copyright (C) 2010 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_OMXLIB) + +#include "utils/StdString.h" + +#include + +// TODO: should this be in configure +#ifndef OMX_SKIP64BIT +#define OMX_SKIP64BIT +#endif + +#include "DllOMX.h" + +#include + +//////////////////////////////////////////////////////////////////////////////////////////// +// debug spew defines +#if 0 +#define OMX_DEBUG_VERBOSE +#define OMX_DEBUG_EVENTHANDLER +#endif + +#define OMX_INIT_STRUCTURE(a) \ + memset(&(a), 0, sizeof(a)); \ + (a).nSize = sizeof(a); \ + (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ + (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ + (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ + (a).nVersion.s.nStep = OMX_VERSION_STEP + +#include "DllAvFormat.h" + +#define OMX_MAX_PORTS 10 + +typedef struct omx_event { + OMX_EVENTTYPE eEvent; + OMX_U32 nData1; + OMX_U32 nData2; +} omx_event; + +class DllLibOMXCore; +class COMXCore; +class COMXCoreComponent; +class COMXCoreTunel; +class COMXCoreClock; + +class COMXCoreTunel +{ +public: + COMXCoreTunel(); + ~COMXCoreTunel(); + + void Initialize(COMXCoreComponent *src_component, unsigned int src_port, COMXCoreComponent *dst_component, unsigned int dst_port); + OMX_ERRORTYPE Flush(); + OMX_ERRORTYPE Deestablish(bool noWait = false); + OMX_ERRORTYPE Establish(bool portSettingsChanged); +private: + bool m_portSettingsChanged; + COMXCoreComponent *m_src_component; + COMXCoreComponent *m_dst_component; + unsigned int m_src_port; + unsigned int m_dst_port; + DllOMX *m_DllOMX; + bool m_DllOMXOpen; +}; + +class COMXCoreComponent +{ +public: + COMXCoreComponent(); + ~COMXCoreComponent(); + + OMX_HANDLETYPE GetComponent() { return m_handle; }; + unsigned int GetInputPort() { return m_input_port; }; + unsigned int GetOutputPort() { return m_output_port; }; + CStdString GetName() { return m_componentName; }; + + OMX_ERRORTYPE DisableAllPorts(); + void Remove(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2); + OMX_ERRORTYPE AddEvent(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2); + //bool GotError(OMX_ERRORTYPE errorType); + OMX_ERRORTYPE WaitForEvent(OMX_EVENTTYPE event, long timeout = 300); + OMX_ERRORTYPE WaitForCommand(OMX_U32 command, OMX_U32 nData2, long timeout = 2000); + OMX_ERRORTYPE SetStateForComponent(OMX_STATETYPE state); + OMX_STATETYPE GetState(); + OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct); + OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct); + OMX_ERRORTYPE SetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct); + OMX_ERRORTYPE GetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct); + OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE cmd, OMX_U32 cmdParam, OMX_PTR cmdParamData); + OMX_ERRORTYPE EnablePort(unsigned int port, bool wait = true); + OMX_ERRORTYPE DisablePort(unsigned int port, bool wait = true); + OMX_ERRORTYPE UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage); + + bool Initialize( const CStdString &component_name, OMX_INDEXTYPE index); + bool Deinitialize(); + + // OMXCore Decoder delegate callback routines. + static OMX_ERRORTYPE DecoderEventHandlerCallback(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); + static OMX_ERRORTYPE DecoderEmptyBufferDoneCallback( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); + static OMX_ERRORTYPE DecoderFillBufferDoneCallback( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader); + + // OMXCore decoder callback routines. + OMX_ERRORTYPE DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); + OMX_ERRORTYPE DecoderEmptyBufferDone( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); + OMX_ERRORTYPE DecoderFillBufferDone( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); + + OMX_ERRORTYPE EmptyThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer); + OMX_ERRORTYPE FillThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer); + OMX_ERRORTYPE FreeOutputBuffer(OMX_BUFFERHEADERTYPE *omx_buffer); + + unsigned int GetInputBufferSize(); + unsigned int GetOutputBufferSize(); + + unsigned int GetInputBufferSpace(); + unsigned int GetOutputBufferSpace(); + + void FlushAll(); + void FlushInput(); + void FlushOutput(); + + OMX_BUFFERHEADERTYPE *GetInputBuffer(long timeout=200); + OMX_BUFFERHEADERTYPE *GetOutputBuffer(long timeout=200); + + OMX_ERRORTYPE AllocInputBuffers(void); + OMX_ERRORTYPE AllocOutputBuffers(void); + + OMX_ERRORTYPE FreeInputBuffers(bool wait); + OMX_ERRORTYPE FreeOutputBuffers(bool wait); + + bool IsEOS() { return m_eos; }; + +private: + OMX_HANDLETYPE m_handle; + unsigned int m_input_port; + unsigned int m_output_port; + int m_ports_enabled[OMX_MAX_PORTS]; + CStdString m_componentName; + pthread_mutex_t m_omx_event_mutex; + std::vector m_omx_events; + + OMX_CALLBACKTYPE m_callbacks; + + // OMXCore input buffers (demuxer packets) + pthread_mutex_t m_omx_input_mutex; + std::queue m_omx_input_avaliable; + std::vector m_omx_input_buffers; + unsigned int m_input_alignment; + unsigned int m_input_buffer_size; + unsigned int m_input_buffer_count; + + // OMXCore output buffers (video frames) + pthread_mutex_t m_omx_output_mutex; + std::queue m_omx_output_avaliable; + std::vector m_omx_output_buffers; + unsigned int m_output_alignment; + unsigned int m_output_buffer_size; + unsigned int m_output_buffer_count; + + bool m_exit; + DllOMX *m_DllOMX; + bool m_DllOMXOpen; + pthread_cond_t m_input_buffer_cond; + pthread_cond_t m_output_buffer_cond; + pthread_cond_t m_omx_event_cond; + bool m_eos; + bool m_flush_input; + bool m_flush_output; +}; + +class COMXCore +{ +public: + COMXCore(); + ~COMXCore(); + + // initialize OMXCore and get decoder component + bool Initialize(); + void Deinitialize(); + +protected: + bool m_is_open; + bool m_Initialized; + DllOMX *m_DllOMX; +}; + +#endif + diff --git a/OMXOverlay.h b/OMXOverlay.h new file mode 100644 index 00000000..c4c15884 --- /dev/null +++ b/OMXOverlay.h @@ -0,0 +1,78 @@ +#pragma once + +/* + * Copyright (C) 2006-2010 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 + * + */ + +#include +#include +#include + +enum OMXOverlayType +{ + OMXOVERLAY_TYPE_NONE = -1, + OMXOVERLAY_TYPE_SPU = 1, + OMXOVERLAY_TYPE_TEXT = 2, + OMXOVERLAY_TYPE_IMAGE = 3, + OMXOVERLAY_TYPE_SSA = 4 +}; + +class COMXOverlay +{ +public: + COMXOverlay(OMXOverlayType type) + { + m_type = type; + + iPTSStartTime = 0LL; + iPTSStopTime = 0LL; + bForced = false; + replace = false; + + iGroupId = 0; + } + + COMXOverlay(const COMXOverlay& src) + { + m_type = src.m_type; + iPTSStartTime = src.iPTSStartTime; + iPTSStopTime = src.iPTSStopTime; + bForced = src.bForced; + replace = src.replace; + iGroupId = src.iGroupId; + } + + virtual ~COMXOverlay() + { + } + + bool IsOverlayType(OMXOverlayType type) { return (m_type == type); } + + double iPTSStartTime; + double iPTSStopTime; + bool bForced; // display, no matter what + bool replace; // replace by next nomatter what stoptime it has + int iGroupId; +protected: + OMXOverlayType m_type; +}; + +typedef std::vector VecOMXOverlays; +typedef std::vector::iterator VecOMXOverlaysIter; diff --git a/OMXOverlayCodec.h b/OMXOverlayCodec.h new file mode 100644 index 00000000..9eba0817 --- /dev/null +++ b/OMXOverlayCodec.h @@ -0,0 +1,88 @@ +#pragma once + +/* + * 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 + * + */ + +#include "OMXOverlay.h" +#include "PlatformDefs.h" + +#include + +// VC_ messages, messages can be combined +#define OC_ERROR 0x00000001 // an error occured, no other messages will be returned +#define OC_BUFFER 0x00000002 // the decoder needs more data +#define OC_OVERLAY 0x00000004 // the decoder decoded an overlay, call Decode(NULL, 0) again to parse the rest of the data + +class COMXStreamInfo; + +class COMXOverlayCodec +{ +public: + + COMXOverlayCodec(const char* name) + { + m_codecName = name; + } + + virtual ~COMXOverlayCodec() {} + + /* + * Open the decoder, returns true on success + */ + virtual bool Open(COMXStreamInfo &hints) = 0; + + /* + * Dispose, Free all resources + */ + virtual void Dispose() = 0; + + /* + * returns one or a combination of VC_ messages + * pData and iSize can be NULL, this means we should flush the rest of the data. + */ + virtual int Decode(BYTE* data, int size, double pts, double duration) = 0; + + /* + * Reset the decoder. + * Should be the same as calling Dispose and Open after each other + */ + virtual void Reset() = 0; + + /* + * Flush the current working packet + * This may leave the internal state intact + */ + virtual void Flush() = 0; + + /* + * returns a valid overlay or NULL + * the data is valid until the next Decode call + */ + virtual COMXOverlay* GetOverlay() = 0; + + /* + * return codecs name + */ + virtual const char* GetName() { return m_codecName.c_str(); } + +private: + std::string m_codecName; +}; diff --git a/OMXOverlayCodecText.cpp b/OMXOverlayCodecText.cpp new file mode 100644 index 00000000..c8f167da --- /dev/null +++ b/OMXOverlayCodecText.cpp @@ -0,0 +1,152 @@ +/* + * 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 + * + */ + +#include "system.h" +#include "OMXOverlayCodecText.h" +#include "OMXOverlayText.h" +#include "OMXStreamInfo.h" +#include "utils/log.h" +#include "OMXSubtitleTagSami.h" + +COMXOverlayCodecText::COMXOverlayCodecText() : COMXOverlayCodec("Text Subtitle Decoder") +{ + m_pOverlay = NULL; + m_bIsSSA = false; +} + +COMXOverlayCodecText::~COMXOverlayCodecText() +{ + if(m_pOverlay) + delete m_pOverlay; + m_pOverlay = NULL; +} + +bool COMXOverlayCodecText::Open(COMXStreamInfo &hints) +{ + m_bIsSSA = (hints.codec == CODEC_ID_SSA); + if(hints.codec == CODEC_ID_TEXT || hints.codec == CODEC_ID_SSA) + return true; + return false; +} + +void COMXOverlayCodecText::Dispose() +{ + if(m_pOverlay) + delete m_pOverlay; + m_pOverlay = NULL; +} + +int COMXOverlayCodecText::Decode(BYTE* data, int size, double pts, double duration) +{ + if(m_pOverlay) + delete m_pOverlay; + m_pOverlay = NULL; + + m_pOverlay = new COMXOverlayText(); + m_pOverlay->iPTSStartTime = 0; + m_pOverlay->iPTSStopTime = 0; + + + char *start, *end, *p; + start = (char*)data; + end = (char*)data + size; + p = (char*)data; + + if (m_bIsSSA) + { + // currently just skip the prefixed ssa fields (8 fields) + int nFieldCount = 8; + while (nFieldCount > 0 && start < end) + { + if (*start == ',') + nFieldCount--; + + start++; + p++; + } + } + + COMXSubtitleTagSami TagConv; + bool Taginit = TagConv.Init(); + + while(pstart) + { + if(Taginit) + TagConv.ConvertLine(m_pOverlay, start, p-start); + else + m_pOverlay->AddElement(new COMXOverlayText::CElementText(start, p-start)); + } + start = p+1; + + while(*p != '}' && pstart) + { + if(Taginit) + { + TagConv.ConvertLine(m_pOverlay, start, p-start); + TagConv.CloseTag(m_pOverlay); + } + else + m_pOverlay->AddElement(new COMXOverlayText::CElementText(start, p-start)); + } + return OC_OVERLAY; +} + +void COMXOverlayCodecText::Reset() +{ + if(m_pOverlay) + delete m_pOverlay; + m_pOverlay = NULL; +} + +void COMXOverlayCodecText::Flush() +{ + if(m_pOverlay) + delete m_pOverlay; + m_pOverlay = NULL; +} + +COMXOverlay* COMXOverlayCodecText::GetOverlay() +{ + if(m_pOverlay) + { + COMXOverlay* overlay = m_pOverlay; + m_pOverlay = NULL; + return overlay; + } + return NULL; +} diff --git a/OMXOverlayCodecText.h b/OMXOverlayCodecText.h new file mode 100644 index 00000000..4fffd9d7 --- /dev/null +++ b/OMXOverlayCodecText.h @@ -0,0 +1,43 @@ +#pragma once + +/* + * 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 + * + */ + +#include "OMXOverlayCodec.h" + +class COMXOverlayText; + +class COMXOverlayCodecText : public COMXOverlayCodec +{ +public: + COMXOverlayCodecText(); + virtual ~COMXOverlayCodecText(); + virtual bool Open(COMXStreamInfo &hints); + virtual void Dispose(); + virtual int Decode(BYTE* data, int size, double pts, double duration); + virtual void Reset(); + virtual void Flush(); + virtual COMXOverlay* GetOverlay(); + +private: + bool m_bIsSSA; + COMXOverlayText* m_pOverlay; +}; diff --git a/OMXOverlayText.h b/OMXOverlayText.h new file mode 100644 index 00000000..835df145 --- /dev/null +++ b/OMXOverlayText.h @@ -0,0 +1,129 @@ +#pragma once + +/* + * 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 + * + */ + +#include "OMXOverlay.h" +#include + +class COMXOverlayText : public COMXOverlay +{ +public: + + enum ElementType + { + ELEMENT_TYPE_NONE = -1, + ELEMENT_TYPE_TEXT = 1, + ELEMENT_TYPE_PROPERTY = 2 + }; + + class CElement + { + public: + CElement(ElementType type) + { + m_type = type; + } + + virtual ~CElement() + { + } + + bool IsElementType(ElementType type) { return (type == m_type); } + + CElement* pNext; + ElementType m_type; + }; + + class CElementText : public CElement + { + public: + CElementText(const char* strText, int size = -1) : CElement(ELEMENT_TYPE_TEXT) + { + if(size == -1) + m_text = strdup(strText); + else + { + m_text = (char*)malloc(size+1); + memcpy(m_text, strText, size); + m_text[size] = '\0'; + } + } + + virtual ~CElementText() + { + if (m_text) free(m_text); + } + + char* m_text; + }; + + class CElementProperty : public CElement + { + CElementProperty() : CElement(ELEMENT_TYPE_PROPERTY) + { + bItalic = false; + bBold = false; + } + + public: + bool bItalic; + bool bBold; + // color + }; + + COMXOverlayText() : COMXOverlay(OMXOVERLAY_TYPE_TEXT) + { + m_pHead = NULL; + m_pEnd = NULL; + } + + virtual ~COMXOverlayText() + { + CElement* pTemp; + while (m_pHead) + { + pTemp = m_pHead; + m_pHead = m_pHead->pNext; + delete pTemp; + } + } + + void AddElement(COMXOverlayText::CElement* pElement) + { + pElement->pNext = NULL; + + if (!m_pHead) + { // first element - set our head to this element, and update the end to the new element + m_pHead = pElement; + m_pEnd = pElement; + } + else + { // extra element - add to the end and update the end to the new element + m_pEnd->pNext = pElement; + m_pEnd = pElement; + } + } + + CElement* m_pHead; + CElement* m_pEnd; +}; + diff --git a/OMXPlayerAudio.cpp b/OMXPlayerAudio.cpp new file mode 100644 index 00000000..b9aaa4dc --- /dev/null +++ b/OMXPlayerAudio.cpp @@ -0,0 +1,721 @@ +/* + * 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 "OMXPlayerAudio.h" + +#include +#include + +#ifndef STANDALONE +#include "FileItem.h" +#endif + +#include "linux/XMemUtils.h" +#ifndef STANDALONE +#include "utils/BitstreamStats.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#endif + +#define MAX_DATA_SIZE 3 * 1024 * 1024 + +OMXPlayerAudio::OMXPlayerAudio() +{ + m_open = false; + m_stream_id = -1; + m_pStream = NULL; + m_av_clock = NULL; + m_omx_reader = NULL; + m_decoder = NULL; + m_flush = false; + m_cached_size = 0; + m_pChannelMap = NULL; + m_pAudioCodec = NULL; + m_speed = DVD_PLAYSPEED_NORMAL; + m_player_error = true; + + pthread_cond_init(&m_packet_cond, NULL); + pthread_cond_init(&m_audio_cond, NULL); + pthread_mutex_init(&m_lock, NULL); + pthread_mutex_init(&m_lock_decoder, NULL); +} + +OMXPlayerAudio::~OMXPlayerAudio() +{ + Close(); + + pthread_cond_destroy(&m_audio_cond); + pthread_cond_destroy(&m_packet_cond); + pthread_mutex_destroy(&m_lock); + pthread_mutex_destroy(&m_lock_decoder); +} + +void OMXPlayerAudio::Lock() +{ + if(m_use_thread) + pthread_mutex_lock(&m_lock); +} + +void OMXPlayerAudio::UnLock() +{ + if(m_use_thread) + pthread_mutex_unlock(&m_lock); +} + +void OMXPlayerAudio::LockDecoder() +{ + if(m_use_thread) + pthread_mutex_lock(&m_lock_decoder); +} + +void OMXPlayerAudio::UnLockDecoder() +{ + if(m_use_thread) + pthread_mutex_unlock(&m_lock_decoder); +} + +bool OMXPlayerAudio::Open(COMXStreamInfo &hints, OMXClock *av_clock, OMXReader *omx_reader, CStdString device, + bool passthrough, bool hw_decode, bool use_thread) +{ + if(ThreadHandle()) + Close(); + + if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load() || !av_clock) + return false; + + m_dllAvFormat.av_register_all(); + + m_hints = hints; + m_av_clock = av_clock; + m_omx_reader = omx_reader; + m_device = device; + m_passthrough = IAudioRenderer::ENCODED_NONE; + m_hw_decode = false; + m_use_passthrough = passthrough; + m_use_hw_decode = hw_decode; + m_iCurrentPts = DVD_NOPTS_VALUE; + m_bAbort = false; + m_bMpeg = m_omx_reader->IsMpegVideo(); + m_use_thread = use_thread; + m_flush = false; + m_cached_size = 0; + m_pAudioCodec = NULL; + m_pChannelMap = NULL; + m_speed = DVD_PLAYSPEED_NORMAL; + + m_error = 0; + m_errorbuff = 0; + m_errorcount = 0; + m_integral = 0; + m_skipdupcount = 0; + m_prevskipped = false; + m_syncclock = true; + m_errortime = m_av_clock->CurrentHostCounter(); + + m_freq = m_av_clock->CurrentHostFrequency(); + + m_av_clock->SetMasterClock(false); + + m_player_error = OpenAudioCodec(); + if(!m_player_error) + { + Close(); + return false; + } + + m_player_error = OpenDecoder(); + if(!m_player_error) + { + Close(); + return false; + } + + if(m_use_thread) + Create(); + + m_open = true; + + return true; +} + +bool OMXPlayerAudio::Close() +{ + m_bAbort = true; + m_flush = true; + + Flush(); + + if(ThreadHandle()) + { + Lock(); + pthread_cond_broadcast(&m_packet_cond); + UnLock(); + + StopThread(); + } + + CloseDecoder(); + CloseAudioCodec(); + + m_open = false; + m_stream_id = -1; + m_iCurrentPts = DVD_NOPTS_VALUE; + m_pStream = NULL; + m_speed = DVD_PLAYSPEED_NORMAL; + + m_dllAvUtil.Unload(); + m_dllAvCodec.Unload(); + m_dllAvFormat.Unload(); + + return true; +} + +void OMXPlayerAudio::HandleSyncError(double duration, double pts) +{ + double clock = m_av_clock->GetClock(); + double error = pts - clock; + int64_t now; + + if( fabs(error) > DVD_MSEC_TO_TIME(100) || m_syncclock ) + { + m_av_clock->Discontinuity(clock+error); + /* + if(m_speed == DVD_PLAYSPEED_NORMAL) + printf("OMXPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f\n", clock, clock+error, error); + */ + + m_errorbuff = 0; + m_errorcount = 0; + m_skipdupcount = 0; + m_error = 0; + m_syncclock = false; + m_errortime = m_av_clock->CurrentHostCounter(); + + return; + } + + if (m_speed != DVD_PLAYSPEED_NORMAL) + { + m_errorbuff = 0; + m_errorcount = 0; + m_integral = 0; + m_skipdupcount = 0; + m_error = 0; + m_errortime = m_av_clock->CurrentHostCounter(); + return; + } + + //check if measured error for 1 second + now = m_av_clock->CurrentHostCounter(); + if ((now - m_errortime) >= m_freq) + { + m_errortime = now; + m_error = m_errorbuff / m_errorcount; + + m_errorbuff = 0; + m_errorcount = 0; + +/* + if (m_synctype == SYNC_DISCON) + { +*/ + double limit, error; + if (m_av_clock->GetRefreshRate(&limit) > 0) + { + //when the videoreferenceclock is running, the discontinuity limit is one vblank period + limit *= DVD_TIME_BASE; + + //make error a multiple of limit, rounded towards zero, + //so it won't interfere with the sync methods in CXBMCRenderManager::WaitPresentTime + if (m_error > 0.0) + error = limit * floor(m_error / limit); + else + error = limit * ceil(m_error / limit); + } + else + { + limit = DVD_MSEC_TO_TIME(10); + error = m_error; + } + + if (fabs(error) > limit - 0.001) + { + m_av_clock->Discontinuity(clock+error); + /* + if(m_speed == DVD_PLAYSPEED_NORMAL) + CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f", clock, clock+error, error); + */ + } + } +/* + else if (m_synctype == SYNC_SKIPDUP && m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) + if (m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) + { + //check how many packets to skip/duplicate + m_skipdupcount = (int)(m_error / duration); + //if less than one frame off, see if it's more than two thirds of a frame, so we can get better in sync + if (m_skipdupcount == 0 && fabs(m_error) > duration / 3 * 2) + m_skipdupcount = (int)(m_error / (duration / 3 * 2)); + + if (m_skipdupcount > 0) + CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Duplicating %i packet(s) of %.2f ms duration", + m_skipdupcount, duration / DVD_TIME_BASE * 1000.0); + else if (m_skipdupcount < 0) + CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Skipping %i packet(s) of %.2f ms duration ", + m_skipdupcount * -1, duration / DVD_TIME_BASE * 1000.0); + } + } +*/ +} + +bool OMXPlayerAudio::Decode(OMXPacket *pkt) +{ + if(!pkt) + return false; + + /* last decoder reinit went wrong */ + if(!m_decoder || !m_pAudioCodec) + return true; + + if(!m_omx_reader->IsActive(OMXSTREAM_AUDIO, pkt->stream_index)) + return true; + + int channels = pkt->hints.channels; + + /* 6 channel have to be mapped to 8 for PCM */ + if(!m_passthrough && !m_hw_decode) + { + if(channels == 6) + channels = 8; + } + + unsigned int old_bitrate = m_hints.bitrate; + unsigned int new_bitrate = pkt->hints.bitrate; + + /* only check bitrate changes on CODEC_ID_DTS, CODEC_ID_AC3, CODEC_ID_EAC3 */ + if(m_hints.codec != CODEC_ID_DTS && m_hints.codec != CODEC_ID_AC3 && m_hints.codec != CODEC_ID_EAC3) + { + new_bitrate = old_bitrate = 0; + } + + /* audio codec changed. reinit device and decoder */ + if(m_hints.codec != pkt->hints.codec || + m_hints.channels != channels || + m_hints.samplerate != pkt->hints.samplerate || + old_bitrate != new_bitrate || + m_hints.bitspersample != pkt->hints.bitspersample) + { + printf("C : %d %d %d %d %d\n", m_hints.codec, m_hints.channels, m_hints.samplerate, m_hints.bitrate, m_hints.bitspersample); + printf("N : %d %d %d %d %d\n", pkt->hints.codec, channels, pkt->hints.samplerate, pkt->hints.bitrate, pkt->hints.bitspersample); + + m_av_clock->OMXPause(); + + CloseDecoder(); + CloseAudioCodec(); + + m_hints = pkt->hints; + + m_player_error = OpenAudioCodec(); + if(!m_player_error) + return false; + + m_player_error = OpenDecoder(); + if(!m_player_error) + return false; + + m_av_clock->OMXStateExecute(); + m_av_clock->OMXReset(); + m_av_clock->OMXResume(); + + } + + if(!((unsigned long)m_decoder->GetSpace() > pkt->size)) + OMXClock::OMXSleep(10); + + if((unsigned long)m_decoder->GetSpace() > pkt->size) + { + if(pkt->dts != DVD_NOPTS_VALUE) + m_iCurrentPts = pkt->dts; + + m_av_clock->SetPTS(m_iCurrentPts); + + const uint8_t *data_dec = pkt->data; + int data_len = pkt->size; + + if(!m_passthrough && !m_hw_decode) + { + while(data_len > 0) + { + int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len); + if( (len < 0) || (len > data_len) ) + { + m_pAudioCodec->Reset(); + break; + } + + data_dec+= len; + data_len -= len; + + uint8_t *decoded; + int decoded_size = m_pAudioCodec->GetData(&decoded); + + if(decoded_size <=0) + continue; + + int ret = 0; + + if(m_bMpeg) + ret = m_decoder->AddPackets(decoded, decoded_size, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); + else + ret = m_decoder->AddPackets(decoded, decoded_size, m_iCurrentPts, m_iCurrentPts); + + if(ret != decoded_size) + { + printf("error ret %d decoded_size %d\n", ret, decoded_size); + } + + int n = (m_hints.channels * m_hints.bitspersample * m_hints.samplerate)>>3; + if (n > 0 && m_iCurrentPts != DVD_NOPTS_VALUE) + m_iCurrentPts += ((double)decoded_size * DVD_TIME_BASE) / n; + + HandleSyncError((((double)decoded_size * DVD_TIME_BASE) / n), m_iCurrentPts); + } + } + else + { + if(m_bMpeg) + m_decoder->AddPackets(pkt->data, pkt->size, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); + else + m_decoder->AddPackets(pkt->data, pkt->size, m_iCurrentPts, m_iCurrentPts); + + HandleSyncError(0, m_iCurrentPts); + } + + m_av_clock->SetAudioClock(m_iCurrentPts); + return true; + } + else + { + return false; + } +} + +void OMXPlayerAudio::Process() +{ + OMXPacket *omx_pkt = NULL; + + while(!m_bStop && !m_bAbort) + { + Lock(); + if(m_packets.empty()) + pthread_cond_wait(&m_packet_cond, &m_lock); + UnLock(); + + if(m_bAbort) + break; + + Lock(); + if(m_flush && omx_pkt) + { + OMXReader::FreePacket(omx_pkt); + omx_pkt = NULL; + m_flush = false; + } + else if(!omx_pkt && !m_packets.empty()) + { + omx_pkt = m_packets.front(); + m_cached_size -= omx_pkt->size; + m_packets.pop_front(); + } + UnLock(); + + LockDecoder(); + if(m_flush && omx_pkt) + { + OMXReader::FreePacket(omx_pkt); + omx_pkt = NULL; + m_flush = false; + } + else if(omx_pkt && Decode(omx_pkt)) + { + OMXReader::FreePacket(omx_pkt); + omx_pkt = NULL; + } + UnLockDecoder(); + } + + if(omx_pkt) + OMXReader::FreePacket(omx_pkt); +} + +void OMXPlayerAudio::Flush() +{ + Lock(); + LockDecoder(); + m_flush = true; + while (!m_packets.empty()) + { + OMXPacket *pkt = m_packets.front(); + m_packets.pop_front(); + OMXReader::FreePacket(pkt); + } + m_iCurrentPts = DVD_NOPTS_VALUE; + m_cached_size = 0; + if(m_decoder) + m_decoder->Flush(); + m_syncclock = true; + UnLockDecoder(); + UnLock(); +} + +bool OMXPlayerAudio::AddPacket(OMXPacket *pkt) +{ + bool ret = false; + + if(!pkt) + return ret; + + if(m_bStop || m_bAbort) + return ret; + + if((m_cached_size + pkt->size) < MAX_DATA_SIZE) + { + Lock(); + m_cached_size += pkt->size; + m_packets.push_back(pkt); + UnLock(); + ret = true; + pthread_cond_broadcast(&m_packet_cond); + } + + return ret; +} + +bool OMXPlayerAudio::OpenAudioCodec() +{ + m_pAudioCodec = new COMXAudioCodecOMX(); + + if(!m_pAudioCodec->Open(m_hints)) + { + delete m_pAudioCodec; m_pAudioCodec = NULL; + return false; + } + + m_pChannelMap = m_pAudioCodec->GetChannelMap(); + return true; +} + +void OMXPlayerAudio::CloseAudioCodec() +{ + if(m_pAudioCodec) + delete m_pAudioCodec; + m_pAudioCodec = NULL; +} + +IAudioRenderer::EEncoded OMXPlayerAudio::IsPassthrough(COMXStreamInfo hints) +{ +#ifndef STANDALONE + int m_outputmode = 0; + bool bitstream = false; + IAudioRenderer::EEncoded passthrough = IAudioRenderer::ENCODED_NONE; + + m_outputmode = g_guiSettings.GetInt("audiooutput.mode"); + + switch(m_outputmode) + { + case 0: + passthrough = IAudioRenderer::ENCODED_NONE; + break; + case 1: + bitstream = true; + break; + case 2: + bitstream = true; + break; + } + + if(bitstream) + { + if(hints.codec == CODEC_ID_AC3 && g_guiSettings.GetBool("audiooutput.ac3passthrough")) + { + passthrough = IAudioRenderer::ENCODED_IEC61937_AC3; + } + if(hints.codec == CODEC_ID_DTS && g_guiSettings.GetBool("audiooutput.dtspassthrough")) + { + passthrough = IAudioRenderer::ENCODED_IEC61937_DTS; + } + } + + return passthrough; +#else + if(m_device == "omx:local") + return IAudioRenderer::ENCODED_NONE; + + IAudioRenderer::EEncoded passthrough = IAudioRenderer::ENCODED_NONE; + + if(hints.codec == CODEC_ID_AC3) + { + passthrough = IAudioRenderer::ENCODED_IEC61937_AC3; + } + if(hints.codec == CODEC_ID_EAC3) + { + passthrough = IAudioRenderer::ENCODED_IEC61937_EAC3; + } + if(hints.codec == CODEC_ID_DTS) + { + passthrough = IAudioRenderer::ENCODED_IEC61937_DTS; + } + + return passthrough; +#endif +} + +bool OMXPlayerAudio::OpenDecoder() +{ + bool bAudioRenderOpen = false; + + m_decoder = new COMXAudio(); + m_decoder->SetClock(m_av_clock); + + if(m_use_passthrough) + m_passthrough = IsPassthrough(m_hints); + + if(!m_passthrough && m_use_hw_decode) + m_hw_decode = COMXAudio::HWDecode(m_hints.codec); + + if(m_passthrough || m_use_hw_decode) + { + if(m_passthrough) + m_hw_decode = false; + bAudioRenderOpen = m_decoder->Initialize(NULL, m_device.substr(4), m_pChannelMap, + m_hints, m_av_clock, m_passthrough, m_hw_decode); + } + else + { + /* omx needs 6 channels packed into 8 for PCM */ + if(m_hints.channels == 6) + m_hints.channels = 8; + + bAudioRenderOpen = m_decoder->Initialize(NULL, m_device.substr(4), m_hints.channels, m_pChannelMap, + m_hints.samplerate, m_hints.bitspersample, + false, false, m_passthrough); + } + + m_codec_name = m_omx_reader->GetCodecName(OMXSTREAM_AUDIO); + + if(!bAudioRenderOpen) + { + delete m_decoder; + m_decoder = NULL; + return false; + } + else + { + if(m_passthrough) + { + printf("Audio codec %s channels %d samplerate %d bitspersample %d\n", + m_codec_name.c_str(), 2, m_hints.samplerate, m_hints.bitspersample); + } + else + { + printf("Audio codec %s channels %d samplerate %d bitspersample %d\n", + m_codec_name.c_str(), m_hints.channels, m_hints.samplerate, m_hints.bitspersample); + } + } + + return true; +} + +bool OMXPlayerAudio::CloseDecoder() +{ + if(m_decoder) + delete m_decoder; + m_decoder = NULL; + return true; +} + +double OMXPlayerAudio::GetDelay() +{ + if(m_decoder) + return m_decoder->GetDelay(); + else + return 0; +} + +double OMXPlayerAudio::GetCacheTime() +{ + if(m_decoder) + return m_decoder->GetCacheTime(); + else + return 0; +} + +void OMXPlayerAudio::WaitCompletion() +{ + if(!m_decoder) + return; + + while(true) + { + Lock(); + if(m_packets.empty()) + { + UnLock(); + break; + } + UnLock(); + OMXClock::OMXSleep(50); + } + + m_decoder->WaitCompletion(); +} + +void OMXPlayerAudio::RegisterAudioCallback(IAudioCallback *pCallback) +{ + if(m_decoder) m_decoder->RegisterAudioCallback(pCallback); + +} +void OMXPlayerAudio::UnRegisterAudioCallback() +{ + if(m_decoder) m_decoder->UnRegisterAudioCallback(); +} + +void OMXPlayerAudio::DoAudioWork() +{ + if(m_decoder) m_decoder->DoAudioWork(); +} + +void OMXPlayerAudio::SetCurrentVolume(long nVolume) +{ + if(m_decoder) m_decoder->SetCurrentVolume(nVolume); +} + +void OMXPlayerAudio::SetSpeed(int speed) +{ + m_speed = speed; +} + diff --git a/OMXPlayerAudio.h b/OMXPlayerAudio.h new file mode 100644 index 00000000..b61bf727 --- /dev/null +++ b/OMXPlayerAudio.h @@ -0,0 +1,135 @@ +/* + * 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 + * + */ + +#ifndef _OMX_PLAYERAUDIO_H_ +#define _OMX_PLAYERAUDIO_H_ + +#include "utils/StdString.h" +#include "DllAvUtil.h" +#include "DllAvFormat.h" +#include "DllAvFilter.h" +#include "DllAvCodec.h" +#include "DllAvCore.h" + +#include "utils/PCMRemap.h" + +#include "OMXReader.h" +#include "OMXClock.h" +#include "OMXStreamInfo.h" +#include "OMXAudio.h" +#include "OMXAudioCodecOMX.h" +#ifdef STANDALONE +#include "OMXThread.h" +#else +#include "threads/Thread.h" +#endif + +#include +#include + +using namespace std; + +#ifdef STANDALONE +class OMXPlayerAudio : public OMXThread +#else +class OMXPlayerAudio : public CThread +#endif +{ +protected: + AVStream *m_pStream; + int m_stream_id; + std::deque m_packets; + DllAvUtil m_dllAvUtil; + DllAvCodec m_dllAvCodec; + DllAvFormat m_dllAvFormat; + bool m_open; + COMXStreamInfo m_hints; + double m_iCurrentPts; + pthread_cond_t m_packet_cond; + pthread_cond_t m_audio_cond; + pthread_mutex_t m_lock; + pthread_mutex_t m_lock_decoder; + OMXClock *m_av_clock; + OMXReader *m_omx_reader; + COMXAudio *m_decoder; + CStdString m_codec_name; + CStdString m_device; + bool m_use_passthrough; + bool m_use_hw_decode; + IAudioRenderer::EEncoded m_passthrough; + bool m_hw_decode; + bool m_bMpeg; + bool m_bAbort; + bool m_use_thread; + bool m_flush; + enum PCMChannels *m_pChannelMap; + unsigned int m_cached_size; + COMXAudioCodecOMX *m_pAudioCodec; + int m_speed; + double m_error; //last average error + + int64_t m_errortime; //timestamp of last time we measured + int64_t m_freq; + + void HandleSyncError(double duration, double pts); + double m_errorbuff; //place to store average errors + int m_errorcount;//number of errors stored + bool m_syncclock; + + bool m_player_error; + + double m_integral; //integral correction for resampler + int m_skipdupcount; //counter for skip/duplicate synctype + bool m_prevskipped; + + void Lock(); + void UnLock(); + void LockDecoder(); + void UnLockDecoder(); +private: +public: + OMXPlayerAudio(); + ~OMXPlayerAudio(); + bool Open(COMXStreamInfo &hints, OMXClock *av_clock, OMXReader *omx_reader, CStdString device, + bool passthrough, bool hw_decode, bool use_thread); + bool Close(); + bool Decode(OMXPacket *pkt); + void Process(); + void Flush(); + bool AddPacket(OMXPacket *pkt); + bool OpenAudioCodec(); + void CloseAudioCodec(); + IAudioRenderer::EEncoded IsPassthrough(COMXStreamInfo hints); + bool OpenDecoder(); + bool CloseDecoder(); + double GetDelay(); + double GetCacheTime(); + double GetCurrentPTS() { return m_iCurrentPts; }; + void WaitCompletion(); + unsigned int GetCached() { return m_cached_size; }; + void RegisterAudioCallback(IAudioCallback* pCallback); + void UnRegisterAudioCallback(); + void DoAudioWork(); + void SetCurrentVolume(long nVolume); + void SetSpeed(int iSpeed); + bool Error() { return !m_player_error; }; +}; +#endif diff --git a/OMXPlayerVideo.cpp b/OMXPlayerVideo.cpp new file mode 100644 index 00000000..4dbbb4ef --- /dev/null +++ b/OMXPlayerVideo.cpp @@ -0,0 +1,630 @@ +/* + * 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 "OMXPlayerVideo.h" + +#include +#include +#include + +#ifndef STANDALONE +#include "FileItem.h" +#endif + +#include "linux/XMemUtils.h" +#ifndef STANDALONE +#include "utils/BitstreamStats.h" +#endif + +#define MAX_DATA_SIZE 10 * 1024 * 1024 + +OMXPlayerVideo::OMXPlayerVideo() +{ + m_open = false; + m_stream_id = -1; + m_pStream = NULL; + m_av_clock = NULL; + m_decoder = NULL; + m_fps = 25.0f; + m_flush = false; + m_cached_size = 0; + m_hdmi_clock_sync = false; + m_iVideoDelay = 0; + m_pts = 0; + m_syncclock = true; + m_speed = DVD_PLAYSPEED_NORMAL; + m_iSubtitleDelay = 0; + m_pSubtitleCodec = NULL; + + pthread_cond_init(&m_packet_cond, NULL); + pthread_cond_init(&m_picture_cond, NULL); + pthread_mutex_init(&m_lock, NULL); + pthread_mutex_init(&m_lock_decoder, NULL); + pthread_mutex_init(&m_lock_subtitle, NULL); +} + +OMXPlayerVideo::~OMXPlayerVideo() +{ + Close(); + + pthread_cond_destroy(&m_packet_cond); + pthread_cond_destroy(&m_picture_cond); + pthread_mutex_destroy(&m_lock); + pthread_mutex_destroy(&m_lock_decoder); + pthread_mutex_destroy(&m_lock_subtitle); +} + +void OMXPlayerVideo::Lock() +{ + if(m_use_thread) + pthread_mutex_lock(&m_lock); +} + +void OMXPlayerVideo::UnLock() +{ + if(m_use_thread) + pthread_mutex_unlock(&m_lock); +} + +void OMXPlayerVideo::LockDecoder() +{ + if(m_use_thread) + pthread_mutex_lock(&m_lock_decoder); +} + +void OMXPlayerVideo::UnLockDecoder() +{ + if(m_use_thread) + pthread_mutex_unlock(&m_lock_decoder); +} + +void OMXPlayerVideo::LockSubtitles() +{ + if(m_use_thread) + pthread_mutex_lock(&m_lock_subtitle); +} + +void OMXPlayerVideo::UnLockSubtitles() +{ + if(m_use_thread) + pthread_mutex_unlock(&m_lock_subtitle); +} + +bool OMXPlayerVideo::Open(COMXStreamInfo &hints, OMXClock *av_clock, bool deinterlace, bool mpeg, int has_audio, bool hdmi_clock_sync, bool use_thread) +{ + if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load() || !av_clock) + return false; + + if(ThreadHandle()) + Close(); + + m_dllAvFormat.av_register_all(); + + m_hints = hints; + m_av_clock = av_clock; + m_fps = 25.0f; + m_frametime = 0; + m_Deinterlace = deinterlace; + m_bMpeg = mpeg; + m_iCurrentPts = DVD_NOPTS_VALUE; + m_has_audio = has_audio; + m_bAbort = false; + m_use_thread = use_thread; + m_flush = false; + m_cached_size = 0; + m_iVideoDelay = 0; + m_hdmi_clock_sync = hdmi_clock_sync; + m_pts = 0; + m_syncclock = true; + m_speed = DVD_PLAYSPEED_NORMAL; + m_iSubtitleDelay = 0; + m_pSubtitleCodec = NULL; + + m_FlipTimeStamp = m_av_clock->GetAbsoluteClock(); + + if(!OpenDecoder()) + { + Close(); + return false; + } + + if(m_use_thread) + Create(); + + m_open = true; + + return true; +} + +bool OMXPlayerVideo::Close() +{ + m_bAbort = true; + m_flush = true; + + Flush(); + + if(ThreadHandle()) + { + Lock(); + pthread_cond_broadcast(&m_packet_cond); + UnLock(); + + StopThread(); + } + + CloseDecoder(); + + m_dllAvUtil.Unload(); + m_dllAvCodec.Unload(); + m_dllAvFormat.Unload(); + + m_open = false; + m_stream_id = -1; + m_iCurrentPts = DVD_NOPTS_VALUE; + m_pStream = NULL; + m_pts = 0; + m_syncclock = true; + m_speed = DVD_PLAYSPEED_NORMAL; + m_pSubtitleCodec = NULL; + + return true; +} + +void OMXPlayerVideo::Output(double pts) +{ + if(m_syncclock) + { + double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock(); + if( delay > m_frametime ) delay = m_frametime; + else if( delay < 0 ) delay = 0; + + //printf("OMXPlayerVideo - GENERAL_RESYNC(%f, 1) delay %f\n", pts, m_FlipTimeStamp); + m_av_clock->Discontinuity(pts - delay); + m_syncclock = false; + } + + double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; + iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock + iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison + iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame + iFrameDuration = m_frametime; + + // correct sleep times based on speed + if(m_speed) + { + iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; + iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); + iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); + } + else + { + iClockSleep = 0; + iFrameSleep = 0; + } + // dropping to a very low framerate is not correct (it should not happen at all) + iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); + iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); + + bool m_stalled = false; + int m_autosync = 1; + if( m_stalled ) + iSleepTime = iFrameSleep; + else + iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; + + // present the current pts of this frame to user, and include the actual + // presentation delay, to allow him to adjust for it + if( m_stalled ) + m_iCurrentPts = DVD_NOPTS_VALUE; + else + m_iCurrentPts = pts - max(0.0, iSleepTime); + + // timestamp when we think next picture should be displayed based on current duration + m_FlipTimeStamp = iCurrentClock; + m_FlipTimeStamp += max(0.0, iSleepTime); + m_FlipTimeStamp += iFrameDuration; + + while(m_av_clock->GetAbsoluteClock(false) < (iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500)) ) + { + OMXClock::OMXSleep(10); + } + + /* + printf("iPlayingClock %f iCurrentClock %f iClockSleep %f iFrameSleep %f iFrameDuration %f WaitAbsolut %f m_FlipTimeStamp %f pts %f\n", + iPlayingClock / DVD_TIME_BASE, iCurrentClock / DVD_TIME_BASE, + iClockSleep / DVD_TIME_BASE, iFrameSleep / DVD_TIME_BASE, + iFrameDuration / DVD_TIME_BASE, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, m_FlipTimeStamp / DVD_TIME_BASE, + pts / DVD_TIME_BASE); + */ + + //g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, mDisplayField); + + m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime)); + + // guess next frame pts. iDuration is always valid + if (m_speed != 0) + m_pts += m_frametime * m_speed / abs(m_speed); +} + +bool OMXPlayerVideo::Decode(OMXPacket *pkt) +{ + if(!pkt) + return false; + + bool ret = false; + + if(!((unsigned long)m_decoder->GetFreeSpace() > pkt->size)) + OMXClock::OMXSleep(10); + + if (pkt->dts == DVD_NOPTS_VALUE && pkt->pts == DVD_NOPTS_VALUE) + pkt->pts = m_pts; + else if (pkt->pts == DVD_NOPTS_VALUE) + pkt->pts = pkt->dts; + + if(pkt->pts != DVD_NOPTS_VALUE) + { + m_pts = pkt->pts; + m_pts += m_iVideoDelay; + } + + if(pkt->hints.codec == CODEC_ID_TEXT || + pkt->hints.codec == CODEC_ID_SSA ) + { + if(!m_pSubtitleCodec) + { + m_pSubtitleCodec = new COMXOverlayCodecText(); + m_pSubtitleCodec->Open( pkt->hints ); + } + int result = m_pSubtitleCodec->Decode(pkt->data, pkt->size, pkt->pts, pkt->duration); + COMXOverlay* overlay; + + CStdString strSubtitle = ""; + + double pts = pkt->dts != DVD_NOPTS_VALUE ? pkt->dts : pkt->pts; + double duration = pkt->duration; + + if(result == OC_OVERLAY) + { + + while((overlay = m_pSubtitleCodec->GetOverlay()) != NULL) + { + if(overlay->iPTSStopTime > overlay->iPTSStartTime) + duration = overlay->iPTSStopTime - overlay->iPTSStartTime; + else if(pkt->duration != DVD_NOPTS_VALUE) + duration = pkt->duration; + else + duration = 0.0; + + if (pkt->pts != DVD_NOPTS_VALUE) + pts = pkt->pts; + else if(pkt->dts != DVD_NOPTS_VALUE) + pts = pkt->dts; + else + pts = overlay->iPTSStartTime; + + pts -= m_iSubtitleDelay; + + overlay->iPTSStartTime = pts; + if(duration) + overlay->iPTSStopTime = pts + duration; + else + { + overlay->iPTSStopTime = 0; + overlay->replace = true; + } + + COMXOverlayText::CElement* e = ((COMXOverlayText*)overlay)->m_pHead; + while (e) + { + if (e->IsElementType(COMXOverlayText::ELEMENT_TYPE_TEXT)) + { + COMXOverlayText::CElementText* t = (COMXOverlayText::CElementText*)e; + strSubtitle += t->m_text; + strSubtitle += "\n"; + } + e = e->pNext; + } + + m_overlays.push_back(overlay); + + if(strSubtitle.length()) + m_decoder->DecodeText((uint8_t *)strSubtitle.c_str(), strSubtitle.length(), overlay->iPTSStartTime, overlay->iPTSStartTime); + } + } + + ret = true; + } + else if((unsigned long)m_decoder->GetFreeSpace() > pkt->size) + { + if(m_bMpeg) + m_decoder->Decode(pkt->data, pkt->size, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); + else + m_decoder->Decode(pkt->data, pkt->size, m_pts, m_pts); + + m_av_clock->SetVideoClock(m_pts); + + Output(m_pts); + + ret = true; + } + + return ret; +} + +void OMXPlayerVideo::Process() +{ + OMXPacket *omx_pkt = NULL; + + m_pts = 0; + + while(!m_bStop && !m_bAbort) + { + Lock(); + if(m_packets.empty()) + pthread_cond_wait(&m_packet_cond, &m_lock); + UnLock(); + + if(m_bAbort) + break; + + Lock(); + if(m_flush && omx_pkt) + { + OMXReader::FreePacket(omx_pkt); + omx_pkt = NULL; + m_flush = false; + } + else if(!omx_pkt && !m_packets.empty()) + { + omx_pkt = m_packets.front(); + m_cached_size -= omx_pkt->size; + m_packets.pop_front(); + } + UnLock(); + + LockDecoder(); + if(m_flush && omx_pkt) + { + OMXReader::FreePacket(omx_pkt); + omx_pkt = NULL; + m_flush = false; + } + else if(omx_pkt && Decode(omx_pkt)) + { + OMXReader::FreePacket(omx_pkt); + omx_pkt = NULL; + } + UnLockDecoder(); + + OMXPacket *subtitle_pkt = m_decoder->GetText(); + + if(subtitle_pkt) + { + LockSubtitles(); + subtitle_pkt->pts = m_av_clock->GetClock(); + m_subtitle_packets.push_back(subtitle_pkt); + UnLockSubtitles(); + } + } + + if(omx_pkt) + OMXReader::FreePacket(omx_pkt); +} + +void OMXPlayerVideo::FlushSubtitles() +{ + LockDecoder(); + LockSubtitles(); + while (!m_subtitle_packets.empty()) + { + OMXPacket *pkt = m_subtitle_packets.front(); + m_subtitle_packets.pop_front(); + OMXReader::FreePacket(pkt); + } + while (!m_overlays.empty()) + { + COMXOverlay *overlay = m_overlays.front(); + m_overlays.pop_front(); + delete overlay; + } + if(m_pSubtitleCodec) + delete m_pSubtitleCodec; + m_pSubtitleCodec = NULL; + UnLockSubtitles(); + UnLockDecoder(); +} + +void OMXPlayerVideo::Flush() +{ + Lock(); + LockDecoder(); + m_flush = true; + while (!m_packets.empty()) + { + OMXPacket *pkt = m_packets.front(); + m_packets.pop_front(); + OMXReader::FreePacket(pkt); + } + m_iCurrentPts = DVD_NOPTS_VALUE; + m_cached_size = 0; + if(m_decoder) + m_decoder->Reset(); + m_syncclock = true; + UnLockDecoder(); + FlushSubtitles(); + UnLock(); +} + +bool OMXPlayerVideo::AddPacket(OMXPacket *pkt) +{ + bool ret = false; + + if(!pkt) + return ret; + + if(m_bStop || m_bAbort) + return ret; + + if((m_cached_size + pkt->size) < MAX_DATA_SIZE) + { + Lock(); + m_cached_size += pkt->size; + m_packets.push_back(pkt); + UnLock(); + ret = true; + pthread_cond_broadcast(&m_packet_cond); + } + + return ret; +} + +bool OMXPlayerVideo::OpenDecoder() +{ + if (m_hints.fpsrate && m_hints.fpsscale) + m_fps = DVD_TIME_BASE / OMXReader::NormalizeFrameduration((double)DVD_TIME_BASE * m_hints.fpsscale / m_hints.fpsrate); + else + m_fps = 25; + + if( m_fps > 100 || m_fps < 5 ) + { + printf("Invalid framerate %d, using forced 25fps and just trust timestamps\n", (int)m_fps); + m_fps = 25; + } + + m_frametime = (double)DVD_TIME_BASE / m_fps; + + m_decoder = new COMXVideo(); + if(!m_decoder->Open(m_hints, m_av_clock, m_Deinterlace, m_hdmi_clock_sync)) + { + CloseDecoder(); + return false; + } + else + { + printf("Video codec %s width %d height %d profile %d fps %f\n", + m_decoder->GetDecoderName().c_str() , m_hints.width, m_hints.height, m_hints.profile, m_fps); + } + + if(m_av_clock) + m_av_clock->SetRefreshRate(m_fps); + + return true; +} + +bool OMXPlayerVideo::CloseDecoder() +{ + if(m_decoder) + delete m_decoder; + m_decoder = NULL; + return true; +} + +int OMXPlayerVideo::GetDecoderBufferSize() +{ + if(m_decoder) + return m_decoder->GetInputBufferSize(); + else + return 0; +} + +int OMXPlayerVideo::GetDecoderFreeSpace() +{ + if(m_decoder) + return m_decoder->GetFreeSpace(); + else + return 0; +} + +void OMXPlayerVideo::WaitCompletion() +{ + if(!m_decoder) + return; + + while(true) + { + Lock(); + if(m_packets.empty()) + { + UnLock(); + break; + } + UnLock(); + OMXClock::OMXSleep(50); + } + + m_decoder->WaitCompletion(); +} + +void OMXPlayerVideo::SetSpeed(int speed) +{ + m_speed = speed; +} + +CStdString OMXPlayerVideo::GetText() +{ + OMXPacket *pkt = NULL; + CStdString strSubtitle = ""; + + LockSubtitles(); + if (!m_subtitle_packets.empty()) + { + pkt = m_subtitle_packets.front(); + if(!m_overlays.empty()) + { + COMXOverlay *overlay = m_overlays.front(); + double now = m_av_clock->GetClock(); + double iPTSStartTime = pkt->pts; + double iPTSStopTime = (overlay->iPTSStartTime > 0) ? iPTSStartTime + (overlay->iPTSStopTime - overlay->iPTSStartTime) : 0LL; + + if((iPTSStartTime <= now) + && (iPTSStopTime >= now || iPTSStopTime == 0LL)) + { + COMXOverlayText::CElement* e = ((COMXOverlayText*)overlay)->m_pHead; + while (e) + { + if (e->IsElementType(COMXOverlayText::ELEMENT_TYPE_TEXT)) + { + COMXOverlayText::CElementText* t = (COMXOverlayText::CElementText*)e; + strSubtitle += t->m_text; + strSubtitle += "\n"; + } + e = e->pNext; + } + } + else if(iPTSStopTime < now) + { + m_subtitle_packets.pop_front(); + m_overlays.pop_front(); + delete overlay; + OMXReader::FreePacket(pkt); + } + } + } + UnLockSubtitles(); + + return strSubtitle; +} diff --git a/OMXPlayerVideo.h b/OMXPlayerVideo.h new file mode 100644 index 00000000..869a2b43 --- /dev/null +++ b/OMXPlayerVideo.h @@ -0,0 +1,128 @@ +/* + * 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 + * + */ + +#ifndef _OMX_PLAYERVIDEO_H_ +#define _OMX_PLAYERVIDEO_H_ + +#include "utils/StdString.h" +#include "DllAvUtil.h" +#include "DllAvFormat.h" +#include "DllAvFilter.h" +#include "DllAvCodec.h" +#include "DllAvCore.h" + +#include "OMXReader.h" +#include "OMXClock.h" +#include "OMXStreamInfo.h" +#include "OMXVideo.h" +#ifdef STANDALONE +#include "OMXThread.h" +#else +#include "threads/Thread.h" +#endif + +#include +#include + +#include "OMXOverlayCodec.h" +#include "OMXOverlayText.h" +#include "OMXOverlayCodecText.h" + +using namespace std; + +#ifdef STANDALONE +class OMXPlayerVideo : public OMXThread +#else +class OMXPlayerVideo : public CThread +#endif +{ +protected: + AVStream *m_pStream; + int m_stream_id; + std::deque m_subtitle_packets; + std::deque m_packets; + std::deque m_overlays; + DllAvUtil m_dllAvUtil; + DllAvCodec m_dllAvCodec; + DllAvFormat m_dllAvFormat; + bool m_open; + COMXStreamInfo m_hints; + double m_iCurrentPts; + pthread_cond_t m_packet_cond; + pthread_cond_t m_picture_cond; + pthread_mutex_t m_lock; + pthread_mutex_t m_subtitle; + pthread_mutex_t m_lock_decoder; + pthread_mutex_t m_lock_subtitle; + OMXClock *m_av_clock; + COMXVideo *m_decoder; + float m_fps; + double m_frametime; + bool m_Deinterlace; + bool m_bMpeg; + int m_has_audio; + bool m_bAbort; + bool m_use_thread; + bool m_flush; + unsigned int m_cached_size; + bool m_hdmi_clock_sync; + double m_iVideoDelay; + double m_pts; + bool m_syncclock; + int m_speed; + double m_FlipTimeStamp; // time stamp of last flippage. used to play at a forced framerate + double m_iSubtitleDelay; + COMXOverlayCodec *m_pSubtitleCodec; + + void Lock(); + void UnLock(); + void LockDecoder(); + void UnLockDecoder(); + void LockSubtitles(); + void UnLockSubtitles(); +private: +public: + OMXPlayerVideo(); + ~OMXPlayerVideo(); + bool Open(COMXStreamInfo &hints, OMXClock *av_clock, bool deinterlace, bool mpeg, int has_audio, bool hdmi_clock_sync, bool use_thread); + bool Close(); + void Output(double pts); + bool Decode(OMXPacket *pkt); + void Process(); + void FlushSubtitles(); + void Flush(); + bool AddPacket(OMXPacket *pkt); + bool OpenDecoder(); + bool CloseDecoder(); + int GetDecoderBufferSize(); + int GetDecoderFreeSpace(); + double GetCurrentPTS() { return m_pts; }; + double GetFPS() { return m_fps; }; + unsigned int GetCached() { return m_cached_size; }; + void WaitCompletion(); + void SetDelay(double delay) { m_iVideoDelay = delay; } + double GetDelay() { return m_iVideoDelay; } + void SetSpeed(int iSpeed); + double GetSubtitleDelay() { return m_iSubtitleDelay; } + void SetSubtitleDelay(double delay) { m_iSubtitleDelay = delay; } + CStdString GetText(); +}; +#endif diff --git a/OMXReader.cpp b/OMXReader.cpp new file mode 100644 index 00000000..2e54c58f --- /dev/null +++ b/OMXReader.cpp @@ -0,0 +1,1429 @@ +/* + * 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 +#include + +#ifndef STANDALONE +#include "FileItem.h" +#endif + +#include "linux/XMemUtils.h" +#ifndef STANDALONE +#include "utils/BitstreamStats.h" +#endif + +#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; + +void OMXReader::AddTimespecs(struct timespec &time, long millisecs) +{ + time.tv_sec += millisecs / 1000; + time.tv_nsec += (millisecs % 1000) * 1000000; + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + +#ifdef STANDALONE +/* Taken from libavformat/utils.c */ +void OMXReader::flush_packet_queue(AVFormatContext *s) +{ + AVPacketList *pktl; + + for(;;) { + pktl = s->packet_buffer; + if (!pktl) + break; + s->packet_buffer = pktl->next; + m_dllAvCodec.av_free_packet(&pktl->pkt); + m_dllAvUtil.av_free(pktl); + } + while(s->raw_packet_buffer){ + pktl = s->raw_packet_buffer; + s->raw_packet_buffer = pktl->next; + m_dllAvCodec.av_free_packet(&pktl->pkt); + m_dllAvUtil.av_free(pktl); + } + s->packet_buffer_end= + s->raw_packet_buffer_end= NULL; +#ifdef RAW_PACKET_BUFFER_SIZE + // Added on: 2009-06-25 + s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; +#endif +} + +/* Taken from libavformat/utils.c */ +void OMXReader::av_read_frame_flush(AVFormatContext *s) +{ + AVStream *st; + unsigned int i, j; + + flush_packet_queue(s); + + s->cur_st = NULL; + + /* for each stream, reset read state */ + for(i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + + if (st->parser) { + av_parser_close(st->parser); + st->parser = NULL; + m_dllAvCodec.av_free_packet(&st->cur_pkt); + } + st->last_IP_pts = AV_NOPTS_VALUE; + st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */ + st->reference_dts = AV_NOPTS_VALUE; + /* fail safe */ + /* fail safe */ + st->cur_ptr = NULL; + st->cur_len = 0; + + for(j=0; jpts_buffer[j]= AV_NOPTS_VALUE; + } +} +#endif + +OMXReader::OMXReader() +{ + m_open = false; + m_filename = ""; + m_bMatroska = false; + m_bAVI = false; + m_bMpeg = 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; + m_seek_ms = 0; + m_seek_req = false; + + 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) +{ + if(g_abort) + return 1; + return 0; +} + +static int dvd_file_read(void *h, uint8_t* buf, int size) +{ + if(interrupt_cb()) + return -1; + + XFILE::CFile *pFile = (XFILE::CFile *)h; + return pFile->Read(buf, size); +} + +static offset_t dvd_file_seek(void *h, offset_t pos, int whence) +{ + if(interrupt_cb()) + 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(CStdString filename, bool dump_format) +{ + if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load()) + return false; + + m_iCurrentPts = DVD_NOPTS_VALUE; + m_filename = filename; + m_speed = DVD_PLAYSPEED_NORMAL; + m_program = UINT_MAX; + + ClearStreams(); + + m_dllAvFormat.av_register_all(); + m_dllAvFormat.url_set_interrupt_cb(interrupt_cb); + + int result = -1; + AVInputFormat *iformat = NULL; + unsigned char *buffer = NULL; + unsigned int flags = READ_TRUNCATED | READ_BITRATE | READ_CHUNKED; +#ifndef STANDALONE + if( CFileItem(m_filename, false).IsInternetStream() ) + flags |= READ_CACHED; +#endif + + 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) == "http://" || m_filename.substr(0,7) == "rtmp://") + { + result = m_dllAvFormat.av_open_input_file(&m_pFormatContext, m_filename.c_str(), iformat, FFMPEG_FILE_BUFFER_SIZE, NULL); + if(result < 0) + { + CLog::Log(LOGERROR, "COMXPlayer::OpenFile - av_open_input_file %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.av_alloc_put_byte(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->is_streamed = 1; + + 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; + } + + result = m_dllAvFormat.av_open_input_stream(&m_pFormatContext, m_ioContext, m_filename.c_str(), iformat, NULL); + 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; + m_bMpeg = strcmp(m_pFormatContext->iformat->name, "mpeg") == 0; + + // if format can be nonblocking, let's use that + m_pFormatContext->flags |= AVFMT_FLAG_NONBLOCK; + + // analyse very short to speed up mjpeg playback start + if (iformat && (strcmp(iformat->name, "mjpeg") == 0) && m_ioContext->is_streamed) + m_pFormatContext->max_analyze_duration = 500000; + +#ifdef STANDALONE + if(/*m_bAVI || */m_bMatroska) + m_pFormatContext->max_analyze_duration = 0; +#endif + + result = m_dllAvFormat.av_find_stream_info(m_pFormatContext); + if(result < 0) + { + m_dllAvFormat.av_close_input_file(m_pFormatContext); + Close(); + return false; + } + + if(!GetStreams()) + { + Close(); + return false; + } + + if(m_pFile) + { + int64_t len = m_pFile->GetLength(); + int64_t tim = (int)(m_pFormatContext->duration / (AV_TIME_BASE / 1000)); + + 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); + } + } + + printf("file : %s reult %d format %s audio streams %d video streams %d chapters %d subtitles %d\n", + m_filename.c_str(), result, m_pFormatContext->iformat->name, m_audio_count, m_video_count, m_chapter_count, m_subtitle_count); + + + m_speed = DVD_PLAYSPEED_NORMAL; + + if(dump_format) + m_dllAvFormat.dump_format(m_pFormatContext, 0, m_filename.c_str(), 0); + + UpdateCurrentPTS(); + + m_open = true; + + m_duration_ms = (int)(m_pFormatContext->duration / (AV_TIME_BASE / 1000)); + 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) + { + if(m_pFormatContext->pb && m_pFormatContext->pb != m_ioContext) + { + CLog::Log(LOGWARNING, "OMXReader::Close - demuxer changed our byte context behind our back, possible memleak"); + m_ioContext = m_pFormatContext->pb; + } + m_dllAvFormat.av_close_input_stream(m_pFormatContext); + if (m_ioContext->buffer) + m_dllAvUtil.av_free(m_ioContext->buffer); + m_dllAvUtil.av_free(m_ioContext); + } + else + m_dllAvFormat.av_close_input_file(m_pFormatContext); + } + + if(m_pFile) + { + m_pFile->Close(); + delete m_pFile; + m_pFile = NULL; + } + + m_dllAvUtil.Unload(); + m_dllAvCodec.Unload(); + m_dllAvFormat.Unload(); + + m_ioContext = NULL; + m_pFormatContext = NULL; + m_open = false; + m_filename = ""; + m_bMatroska = false; + m_bAVI = false; + m_bMpeg = 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_seek_ms = 0; + m_seek_req = false; + m_speed = DVD_PLAYSPEED_NORMAL; + + ClearStreams(); + + return true; +} + +void OMXReader::FlushRead() +{ + if(!m_pFormatContext) + return; + +#ifdef STANDALONE + av_read_frame_flush(m_pFormatContext); +#else + m_dllAvFormat.av_read_frame_flush(m_pFormatContext); +#endif + m_iCurrentPts = DVD_NOPTS_VALUE; +} + +bool OMXReader::SeekTime(int64_t seek_ms, int seek_flags, double *startpts) +{ + Lock(); + + m_seek_flags = seek_flags; + if(m_pFile && m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL)) + { + m_seek_ms = seek_ms; + m_seek_req = true; + } + + //printf("m_seek_ms %lld seek_ms %lld\n", m_seek_ms, seek_ms); + if(m_seek_req) + { + int64_t pos = m_seek_ms * 1000; + if(pos < 0) + pos = 0; + + FlushRead(); + + /* + int stream_index = -1; + int64_t seek_target = pos; + + if((m_video_index != -1) && m_streams[m_video_index].stream) + stream_index = m_streams[m_video_index].stream->index; + else if((m_audio_index != -1) && m_streams[m_audio_index].stream) + stream_index = m_streams[m_audio_index].stream->index; + + if(stream_index >= 0) + { + seek_target = m_dllAvUtil.av_rescale_q(seek_target, AV_TIME_BASE_Q, m_pFormatContext->streams[stream_index]->time_base); + } + else + { + if(m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) + seek_target += m_pFormatContext->start_time; + } + + int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, stream_index, seek_target, m_seek_flags); + */ + + int64_t seek_pts = (int64_t)seek_ms * 1000; + if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) + seek_pts += m_pFormatContext->start_time; + + int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, m_seek_flags); + + if(ret < 0) + { + printf("error while seeking seek_flags %d pos %f\n", m_seek_flags, (double)pos / AV_TIME_BASE); + UnLock(); + return false; + } + else + { + UpdateCurrentPTS(); + } + + if(startpts) + *startpts = DVD_MSEC_TO_TIME(seek_ms); + + m_seek_req = false; + m_seek_ms = 0; + } + + UnLock(); + return true; +} + +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) + return NULL; + + Lock(); + + // assume we are not eof + if(m_pFormatContext->pb) + m_pFormatContext->pb->eof_reached = 0; + + m_dllAvCodec.av_init_packet(&pkt); + + pkt.size = 0; + pkt.data = NULL; + pkt.stream_index = MAX_OMX_STREAMS; + + 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; + } + else if (pkt.size < 0 || pkt.stream_index >= MAX_OMX_STREAMS) + { + // 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(pkt.dts == 0) + pkt.dts = AV_NOPTS_VALUE; + if(pkt.pts == 0) + pkt.pts = AV_NOPTS_VALUE; + + 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 == CODEC_ID_TEXT && 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->dts = ConvertTimestamp(pkt.dts, &pStream->time_base); + //m_omx_pkt->pts = ConvertTimestamp(pkt.pts, &pStream->time_base); + 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->file_size > 0) + || (m_pFormatContext->duration != (int64_t)AV_NOPTS_VALUE && duration > m_pFormatContext->duration)) + m_pFormatContext->duration = duration; + } + } + + // check if stream seem to have grown since start + if(m_pFormatContext->file_size > 0 && m_pFormatContext->pb) + { + if(m_pFormatContext->pb->pos > m_pFormatContext->file_size) + m_pFormatContext->file_size = m_pFormatContext->pb->pos; + } + + 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) + { + // TODO: build stream array + // 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 there were no programs or they were all empty, add all streams + if (m_program == UINT_MAX) + { + // TODO: build stream array + 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].seekto_ms = ConvertTimestamp(chapter->start, &chapter->time_base) / 1000; + m_chapters[i].ts = m_chapters[i].seekto_ms / 1000; + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,83,0) + AVMetadataTag *titleTag = m_dllAvFormat.av_metadata_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]; + + 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) + AVMetadataTag *langTag = m_dllAvFormat.av_metadata_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) + AVMetadataTag *titleTag = m_dllAvFormat.av_metadata_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(index > (m_audio_count - 1)) + index = (m_audio_count - 1); + break; + case OMXSTREAM_VIDEO: + if(index > (m_video_count - 1)) + index = (m_video_count - 1); + break; + case OMXSTREAM_SUBTITLE: + if(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; +} + +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->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; + + 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; + } + + if (stream->sample_aspect_ratio.num == 0) + hints->aspect = 0.0f; + else + hints->aspect = av_q2d(stream->sample_aspect_ratio) * stream->codec->width / stream->codec->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 + FF_INPUT_BUFFER_PADDING_SIZE); + if(!pkt->data) + { + free(pkt); + pkt = NULL; + } + else + { + memset(pkt->data + size, 0, FF_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); + //double dts = ConvertTimestamp(ch->start, &ch->time_base); + 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 false; + + 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; +} + +double OMXReader::ConvertTimestamp(int64_t pts, AVRational *time_base) +{ + double new_pts = pts; + + if(m_pFormatContext == NULL) + return false; + + if (pts == (int64_t)AV_NOPTS_VALUE) + return DVD_NOPTS_VALUE; + + if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) + new_pts += m_pFormatContext->start_time; + + new_pts *= av_q2d(*time_base); + + return (double)new_pts * 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)) + //if(m_iCurrentPts >= ConvertTimestamp(chapter->start, &chapter->time_base) + // && m_iCurrentPts < ConvertTimestamp(chapter->end, &chapter->time_base)) + 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) + AVMetadataTag *titleTag = m_dllAvFormat.av_metadata_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); + //double ts = ConvertTimestamp(stream->cur_dts, &stream->time_base); + 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; + + /* apperently ffmpeg messes up sometimes, so check for negative value too */ + if (m_pFormatContext->duration == (int64_t)AV_NOPTS_VALUE || m_pFormatContext->duration < 0LL) + { + // no duration is available for us + // try to calculate it + int iLength = 0; + if (m_iCurrentPts != DVD_NOPTS_VALUE && m_pFormatContext->file_size > 0 && m_pFormatContext->pb && m_pFormatContext->pb->pos > 0) + { + iLength = (int)(((m_iCurrentPts * m_pFormatContext->file_size) / m_pFormatContext->pb->pos) / 1000) & 0xFFFFFFFF; + } + return iLength; + } + + 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; +} + +CStdString OMXReader::GetStreamCodecName(AVStream *stream) +{ + CStdString 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; + strStreamName.MakeLower(); + return strStreamName; + } + } + +#ifdef FF_PROFILE_DTS_HD_MA + /* use profile to determine the DTS type */ + if (stream->codec->codec_id == 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; +} + +CStdString OMXReader::GetCodecName(OMXStreamType type) +{ + CStdString 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; +} + +CStdString OMXReader::GetCodecName(OMXStreamType type, unsigned int index) +{ + CStdString 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; +} + +CStdString OMXReader::GetStreamLanguage(OMXStreamType type, unsigned int index) +{ + CStdString 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; +} + +CStdString OMXReader::GetStreamName(OMXStreamType type, unsigned int index) +{ + CStdString 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; +} + +CStdString OMXReader::GetStreamType(OMXStreamType type, unsigned int index) +{ + CStdString 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 == CODEC_ID_AC3) strcpy(sInfo, "AC3 "); + else if (m_streams[i].hints.codec == 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 == 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; +} + +#ifndef STANDALONE +int OMXReader::GetSourceBitrate() +{ + int ret = 0; + + if(!m_pFile) + return 0; + + if(m_pFile->GetBitstreamStats()) + { + BitstreamStats *status = m_pFile->GetBitstreamStats(); + ret = status->GetBitrate(); + } + + return ret; +} +#endif diff --git a/OMXReader.h b/OMXReader.h new file mode 100644 index 00000000..cb5f72b7 --- /dev/null +++ b/OMXReader.h @@ -0,0 +1,202 @@ +/* + * 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 + * + */ + +#ifndef _OMX_READER_H_ +#define _OMX_READER_H_ + +#include "utils/StdString.h" +#include "DllAvUtil.h" +#include "DllAvFormat.h" +#include "DllAvFilter.h" +#include "DllAvCodec.h" +#include "DllAvCore.h" +#include "OMXStreamInfo.h" +#ifdef STANDALONE +#include "OMXThread.h" +#else +#include "threads/Thread.h" +#endif +#include + +#include "OMXStreamInfo.h" + +#ifdef STANDALONE +#include "File.h" +#else +#include "xbmc/filesystem/File.h" +#endif + +#include + +using namespace XFILE; +using namespace std; + +#define MAX_OMX_CHAPTERS 64 + +#define MAX_OMX_STREAMS 100 + +#define OMX_PLAYSPEED_PAUSE 0 +#define OMX_PLAYSPEED_NORMAL 1 + +#ifndef FFMPEG_FILE_BUFFER_SIZE +#define FFMPEG_FILE_BUFFER_SIZE 32768 // default reading size for ffmpeg +#endif +#ifndef MAX_STREAMS +#define MAX_STREAMS 100 +#endif + +typedef struct OMXChapter +{ + std::string name; + int64_t seekto_ms; + double ts; +} OMXChapter; + +class OMXReader; + +typedef struct OMXPacket +{ + double pts; // pts in DVD_TIME_BASE + double dts; // dts in DVD_TIME_BASE + double now; // dts in DVD_TIME_BASE + double duration; // duration in DVD_TIME_BASE if available + int size; + uint8_t *data; + int stream_index; + COMXStreamInfo hints; + enum AVMediaType codec_type; +} OMXPacket; + +enum OMXStreamType +{ + OMXSTREAM_NONE = 0, + OMXSTREAM_AUDIO = 1, + OMXSTREAM_VIDEO = 2, + OMXSTREAM_SUBTITLE = 3 +}; + +typedef struct OMXStream +{ + char language[4]; + std::string name; + std::string codec_name; + AVStream *stream; + OMXStreamType type; + int id; + void *extradata; + unsigned int extrasize; + unsigned int index; + COMXStreamInfo hints; +} OMXStream; + +class OMXReader +{ +protected: + int m_video_index; + int m_audio_index; + int m_subtitle_index; + int m_video_count; + int m_audio_count; + int m_subtitle_count; + DllAvUtil m_dllAvUtil; + DllAvCodec m_dllAvCodec; + DllAvFormat m_dllAvFormat; + bool m_open; + CStdString m_filename; + bool m_bMatroska; + bool m_bAVI; + bool m_bMpeg; + XFILE::CFile *m_pFile; + AVFormatContext *m_pFormatContext; + ByteIOContext *m_ioContext; + bool m_eof; + OMXChapter m_chapters[MAX_OMX_CHAPTERS]; + OMXStream m_streams[MAX_STREAMS]; + int m_chapter_count; + double m_iCurrentPts; + int64_t m_seek_ms; + int m_seek_req; + int m_seek_flags; + int m_speed; + int64_t m_duration_ms; + unsigned int m_program; + void AddTimespecs(struct timespec &time, long millisecs); +#ifdef STANDALONE + void flush_packet_queue(AVFormatContext *s); + void av_read_frame_flush(AVFormatContext *s); +#endif + pthread_mutex_t m_lock; + void Lock(); + void UnLock(); + bool SetActiveStreamInternal(OMXStreamType type, unsigned int index); +private: +public: + OMXReader(); + ~OMXReader(); + bool Open(CStdString filename, bool dump_format); + void ClearStreams(); + bool Close(); + void FlushRead(); + bool SeekTime(int64_t seek_ms, int seek_flags, double *startpts); + AVMediaType PacketType(OMXPacket *pkt); + OMXPacket *Read(); + void Process(); + bool GetStreams(); + void AddStream(int id); + bool IsActive(int stream_index); + bool IsActive(OMXStreamType type, int stream_index); + bool GetHints(AVStream *stream, COMXStreamInfo *hints); + bool GetHints(OMXStreamType type, unsigned int index, COMXStreamInfo &hints); + bool GetHints(OMXStreamType type, COMXStreamInfo &hints); + bool IsEof(); + int AudioStreamCount() { return m_audio_count; }; + int VideoStreamCount() { return m_video_count; }; + int SubtitleStreamCount() { return m_subtitle_count; }; + bool SetActiveStream(OMXStreamType type, unsigned int index); + int GetChapterCount() { return m_chapter_count; }; + OMXChapter GetChapter(unsigned int chapter) { return m_chapters[(chapter > MAX_OMX_CHAPTERS) ? MAX_OMX_CHAPTERS : chapter]; }; + static void FreePacket(OMXPacket *pkt); + static OMXPacket *AllocPacket(int size); + void SetSpeed(int iSpeed); + void UpdateCurrentPTS(); + double ConvertTimestamp(int64_t pts, int den, int num); + double ConvertTimestamp(int64_t pts, AVRational *time_base); + int GetChapter(); + void GetChapterName(std::string& strChapterName); + bool SeekChapter(int chapter, double* startpts); + bool GetAudioIndex() { return m_audio_index; }; + bool GetSubtitleIndex() { return m_subtitle_index; }; + int GetStreamLength(); + static double NormalizeFrameduration(double frameduration); + bool IsMpegVideo() { return m_bMpeg; }; + bool IsMatroska() { return m_bMatroska; }; + CStdString GetCodecName(OMXStreamType type); + CStdString GetCodecName(OMXStreamType type, unsigned int index); + CStdString GetStreamCodecName(AVStream *stream); + CStdString GetStreamLanguage(OMXStreamType type, unsigned int index); + CStdString GetStreamName(OMXStreamType type, unsigned int index); + int64_t GetDuration() { return m_duration_ms; }; + CStdString GetStreamType(OMXStreamType type, unsigned int index); +#ifndef STANDALONE + int GetSourceBitrate(); +#endif +}; +#endif diff --git a/OMXStreamInfo.cpp b/OMXStreamInfo.cpp new file mode 100644 index 00000000..781e8106 --- /dev/null +++ b/OMXStreamInfo.cpp @@ -0,0 +1,71 @@ +/* + * 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 + * + */ + +#include "OMXStreamInfo.h" + +COMXStreamInfo::COMXStreamInfo() +{ + extradata = NULL; + Clear(); +} + +COMXStreamInfo::~COMXStreamInfo() +{ + //if( extradata && extrasize ) free(extradata); + + extradata = NULL; + extrasize = 0; +} + + +void COMXStreamInfo::Clear() +{ + codec = CODEC_ID_NONE; + software = false; + codec_tag = 0; + + //if( extradata && extrasize ) free(extradata); + + extradata = NULL; + extrasize = 0; + + fpsscale = 0; + fpsrate = 0; + height = 0; + width = 0; + aspect = 0.0; + vfr = false; + stills = false; + level = 0; + profile = 0; + ptsinvalid = false; + + channels = 0; + samplerate = 0; + blockalign = 0; + bitrate = 0; + bitspersample = 0; + + identifier = 0; + + framesize = 0; + syncword = 0; +} diff --git a/OMXStreamInfo.h b/OMXStreamInfo.h new file mode 100644 index 00000000..e853abbd --- /dev/null +++ b/OMXStreamInfo.h @@ -0,0 +1,80 @@ +/* + * 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 + * + */ + +#pragma once + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#ifndef _LINUX +enum CodecID; +#else +extern "C" { + #include "libavcodec/avcodec.h" +} +#endif + +class CDemuxStream; + +class COMXStreamInfo +{ +public: + COMXStreamInfo(); + + ~COMXStreamInfo(); + + void Clear(); // clears current information + + CodecID codec; + bool software; //force software decoding + + + // VIDEO + int fpsscale; // scale of 1000 and a rate of 29970 will result in 29.97 fps + int fpsrate; + int height; // height of the stream reported by the demuxer + int width; // width of the stream reported by the demuxer + float aspect; // display aspect as reported by demuxer + bool vfr; // variable framerate + bool stills; // there may be odd still frames in video + int level; // encoder level of the stream reported by the decoder. used to qualify hw decoders. + int profile; // encoder profile of the stream reported by the decoder. used to qualify hw decoders. + bool ptsinvalid; // pts cannot be trusted (avi's). + + // AUDIO + int channels; + int samplerate; + int bitrate; + int blockalign; + int bitspersample; + + // SUBTITLE + int identifier; + + // CODEC EXTRADATA + void* extradata; // extra data for codec to use + unsigned int extrasize; // size of extra data + unsigned int codec_tag; // extra identifier hints for decoding + + /* ac3/dts indof */ + unsigned int framesize; + uint32_t syncword; +}; diff --git a/OMXSubtitleTagSami.cpp b/OMXSubtitleTagSami.cpp new file mode 100644 index 00000000..bc4b3a65 --- /dev/null +++ b/OMXSubtitleTagSami.cpp @@ -0,0 +1,261 @@ +/* + * 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 + * + */ + +#include "OMXSubtitleTagSami.h" +//#include "DVDSubtitleStream.h" +#include "linux/PlatformDefs.h" +#include "OMXOverlayText.h" +#include "utils/RegExp.h" + +COMXSubtitleTagSami::~COMXSubtitleTagSami() +{ + delete m_tags; + delete m_tagOptions; +} + +bool COMXSubtitleTagSami::Init() +{ + m_tags = new CRegExp(true); + if (!m_tags->RegComp("(<[^>]*>|\\{[^\\}]*\\})")) + return false; + + m_tagOptions = new CRegExp(true); + if (!m_tagOptions->RegComp("([a-z]+)[ \t]*=[ \t]*(?:[\"'])?([^\"'> ]+)(?:[\"'])?(?:>)?")) + return false; + + return true; +} + +void COMXSubtitleTagSami::ConvertLine(COMXOverlayText* pOverlay, const char* line, int len, const char* lang) +{ + CStdStringA strUTF8; + strUTF8.assign(line, len); + strUTF8.Trim(); + + int pos = 0; + int del_start = 0; + while ((pos=m_tags->RegFind(strUTF8.c_str(), pos)) >= 0) + { + // Parse Tags + CStdString fullTag = m_tags->GetMatch(0); + fullTag.ToLower(); + strUTF8.erase(pos, fullTag.length()); + if (fullTag == "" || fullTag == "{\\b1}") + { + m_flag[FLAG_BOLD] = true; + strUTF8.insert(pos, "[B]"); + pos += 3; + } + else if ((fullTag == "" || fullTag == "{\\b0}") && m_flag[FLAG_BOLD]) + { + m_flag[FLAG_BOLD] = false; + strUTF8.insert(pos, "[/B]"); + pos += 4; + } + else if (fullTag == "" || fullTag == "{\\i1}") + { + m_flag[FLAG_ITALIC] = true; + strUTF8.insert(pos, "[I]"); + pos += 3; + } + else if ((fullTag == "" || fullTag == "{\\i0}") && m_flag[FLAG_ITALIC]) + { + m_flag[FLAG_ITALIC] = false; + strUTF8.insert(pos, "[/I]"); + pos += 4; + } + else if ((fullTag == "" || fullTag == "{\\c}") && m_flag[FLAG_COLOR]) + { + m_flag[FLAG_COLOR] = false; + strUTF8.insert(pos, "[/COLOR]"); + pos += 8; + } + else if (fullTag.Left(5) == "{\\c&h" || fullTag.Left(6) == "{\\1c&h") + { + m_flag[FLAG_COLOR] = true; + CStdString tempColorTag = "[COLOR FF"; + CStdString tagOptionValue; + if (fullTag.Left(5) == "{\\c&h") + tagOptionValue = fullTag.substr(5,6); + else + tagOptionValue = fullTag.substr(6,6); + tempColorTag += tagOptionValue.substr(4,2); + tempColorTag += tagOptionValue.substr(2,2); + tempColorTag += tagOptionValue.substr(0,2); + tempColorTag += "]"; + strUTF8.insert(pos, tempColorTag); + pos += tempColorTag.length(); + } + else if (fullTag.Left(5) == "RegFind(fullTag.c_str(), pos2)) >= 0) + { + CStdString tagOptionName = m_tagOptions->GetMatch(1); + CStdString tagOptionValue = m_tagOptions->GetMatch(2); + pos2 += tagOptionName.length() + tagOptionValue.length(); + if (tagOptionName == "color") + { + m_flag[FLAG_COLOR] = true; + CStdString tempColorTag = "[COLOR "; + if (tagOptionValue[0] == '#') + { + tagOptionValue.erase(0, 1); + tempColorTag += "FF"; + } + else if( tagOptionValue.size() == 6 ) + { + bool bHex = true; + for( int i=0 ; i<6 ; i++ ) + { + char temp = tagOptionValue[i]; + if( !(('0' <= temp && temp <= '9') || + ('a' <= temp && temp <= 'f') || + ('A' <= temp && temp <= 'F') )) + { + bHex = false; + break; + } + } + if( bHex ) tempColorTag += "FF"; + } + tempColorTag += tagOptionValue; + tempColorTag += "]"; + strUTF8.insert(pos, tempColorTag); + pos += tempColorTag.length(); + } + } + } + else if (lang && (fullTag.Left(3) == "

RegFind(fullTag.c_str(), pos2)) >= 0) + { + CStdString tagOptionName = m_tagOptions->GetMatch(1); + CStdString tagOptionValue = m_tagOptions->GetMatch(2); + pos2 += tagOptionName.length() + tagOptionValue.length(); + if (tagOptionName == "class") + { + if (m_flag[FLAG_LANGUAGE]) + { + strUTF8.erase(del_start, pos - del_start); + pos = del_start; + } + if (!tagOptionValue.Compare(lang)) + { + m_flag[FLAG_LANGUAGE] = false; + } + else + { + m_flag[FLAG_LANGUAGE] = true; + del_start = pos; + } + break; + } + } + } + else if (fullTag == "

" && m_flag[FLAG_LANGUAGE]) + { + strUTF8.erase(del_start, pos - del_start); + pos = del_start; + m_flag[FLAG_LANGUAGE] = false; + } + else if (fullTag == "
" && !strUTF8.IsEmpty()) + { + strUTF8.Insert(pos, "\n"); + pos += 1; + } + } + + if(m_flag[FLAG_LANGUAGE]) + strUTF8.erase(del_start); + + if (strUTF8.IsEmpty()) + return; + + if( strUTF8[strUTF8.size()-1] == '\n' ) + strUTF8.Delete(strUTF8.size()-1); + + // add a new text element to our container + pOverlay->AddElement(new COMXOverlayText::CElementText(strUTF8.c_str())); +} + +void COMXSubtitleTagSami::CloseTag(COMXOverlayText* pOverlay) +{ + if (m_flag[FLAG_BOLD]) + { + pOverlay->AddElement(new COMXOverlayText::CElementText("[/B]")); + m_flag[FLAG_BOLD] = false; + } + if (m_flag[FLAG_ITALIC]) + { + pOverlay->AddElement(new COMXOverlayText::CElementText("[/I]")); + m_flag[FLAG_ITALIC] = false; + } + if (m_flag[FLAG_COLOR]) + { + pOverlay->AddElement(new COMXOverlayText::CElementText("[/COLOR]")); + m_flag[FLAG_COLOR] = false; + } + m_flag[FLAG_LANGUAGE] = false; +} + +/* +void COMXSubtitleTagSami::LoadHead(CDVDSubtitleStream* samiStream) +{ + char line[1024]; + bool inSTYLE = false; + CRegExp reg(true); + if (!reg.RegComp("\\.([a-z]+)[ \t]*\\{[ \t]*name:([^;]*?);[ \t]*lang:([^;]*?);[ \t]*SAMIType:([^;]*?);[ \t]*\\}")) + return; + + while (samiStream->ReadLine(line, sizeof(line))) + { + if (!strnicmp(line, "", 6)) + break; + if (inSTYLE) + { + if (!strnicmp(line, "", 8)) + break; + else + { + if (reg.RegFind(line) > -1) + { + SLangclass lc; + lc.ID = reg.GetMatch(1); + lc.Name = reg.GetMatch(2); + lc.Lang = reg.GetMatch(3); + lc.SAMIType = reg.GetMatch(4); + lc.Name.Trim(); + lc.Lang.Trim(); + lc.SAMIType.Trim(); + m_Langclass.push_back(lc); + } + } + } + else + { + if (!strnicmp(line, "