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, "