aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgimli <ebsi4711@gmail.com>2012-08-10 22:15:58 +0200
committergimli <ebsi4711@gmail.com>2012-08-10 22:15:58 +0200
commita91e8749e76f61e2c24ee87c16bae375674f8476 (patch)
tree5811e8fe4c0fe159714a7341d95e7e7c40ec4606
parent18c401cbfaa7ca339734b41d52fb4460454496ad (diff)
[rbp] added raspberrypi omxplayer
-rw-r--r--Makefile.in4
-rw-r--r--Makefile.include.in4
-rw-r--r--tools/rbp/depends/xbmc/Makefile2
-rw-r--r--xbmc/Application.cpp4
-rw-r--r--xbmc/cores/dvdplayer/DVDMessageQueue.cpp2
-rw-r--r--xbmc/cores/omxplayer/BitstreamConverter.cpp911
-rw-r--r--xbmc/cores/omxplayer/BitstreamConverter.h171
-rw-r--r--xbmc/cores/omxplayer/DllOMX.h123
-rw-r--r--xbmc/cores/omxplayer/Makefile.in20
-rw-r--r--xbmc/cores/omxplayer/OMXAudio.cpp1478
-rw-r--r--xbmc/cores/omxplayer/OMXAudio.h155
-rw-r--r--xbmc/cores/omxplayer/OMXAudioCodec.h119
-rw-r--r--xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp382
-rw-r--r--xbmc/cores/omxplayer/OMXAudioCodecOMX.h77
-rw-r--r--xbmc/cores/omxplayer/OMXImage.cpp1114
-rw-r--r--xbmc/cores/omxplayer/OMXImage.h107
-rw-r--r--xbmc/cores/omxplayer/OMXPlayer.cpp3816
-rw-r--r--xbmc/cores/omxplayer/OMXPlayer.h469
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerAudio.cpp847
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerAudio.h130
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerVideo.cpp816
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerVideo.h135
-rw-r--r--xbmc/cores/omxplayer/OMXVideo.cpp966
-rw-r--r--xbmc/cores/omxplayer/OMXVideo.h98
-rw-r--r--xbmc/cores/omxplayer/OMXVideoCodec.h240
-rw-r--r--xbmc/cores/omxplayer/omxplayer_advancedsettings.xml6
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreConfig.h6
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreFactory.cpp7
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreFactory.h10
-rw-r--r--xbmc/video/windows/GUIWindowFullScreen.cpp3
30 files changed, 12219 insertions, 3 deletions
diff --git a/Makefile.in b/Makefile.in
index 856a341def..895adf57df 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -143,6 +143,10 @@ ifeq (@USE_AMLPLAYER@,1)
DIRECTORY_ARCHIVES += xbmc/cores/amlplayer/amlplayer.a
endif
+ifeq (@USE_OMXPLAYER@,1)
+DIRECTORY_ARCHIVES += xbmc/cores/omxplayer/omxplayer.a
+endif
+
PAPCODECS_DIRS= \
lib/xbadpcm \
lib/nosefart \
diff --git a/Makefile.include.in b/Makefile.include.in
index a517dd269e..704a4bc7f2 100644
--- a/Makefile.include.in
+++ b/Makefile.include.in
@@ -40,6 +40,10 @@ ifneq (@USE_EXTERNAL_FFMPEG@,1)
endif
INCLUDES+=-I@abs_top_srcdir@/xbmc/linux
INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/dvdplayer
+ifeq (@USE_OMXPLAYER@,1)
+INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/AudioEngine
+INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/AudioEngine/Utils
+endif
DEFINES+= \
@ARCH_DEFINES@ \
-D_FILE_DEFINED \
diff --git a/tools/rbp/depends/xbmc/Makefile b/tools/rbp/depends/xbmc/Makefile
index 34ddd6ade7..66dc8dff58 100644
--- a/tools/rbp/depends/xbmc/Makefile
+++ b/tools/rbp/depends/xbmc/Makefile
@@ -12,7 +12,7 @@ CONFIGURE=./configure --prefix=$(PREFIX) --build=$(BUILD) --host=$(HOST) \
--disable-optical-drive --disable-dvdcss --disable-joystick --disable-debug \
--disable-crystalhd --disable-vtbdecoder --disable-vaapi --disable-vdpau \
--disable-pulse --disable-projectm --with-platform=raspberry-pi --disable-optimizations \
- --enable-rpi-cec-api
+ --enable-rpi-cec-api --enable-player=omxplayer
all: configure
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index a5f288530e..f737abfd38 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -5428,6 +5428,10 @@ void CApplication::SetHardwareVolume(float hardwareVolume)
value = 1.0f;
CAEFactory::SetVolume(value);
+
+ /* for platforms where we do not have AE */
+ if (m_pPlayer)
+ m_pPlayer->SetVolume(g_settings.m_fVolumeLevel);
}
int CApplication::GetVolume() const
diff --git a/xbmc/cores/dvdplayer/DVDMessageQueue.cpp b/xbmc/cores/dvdplayer/DVDMessageQueue.cpp
index 75b663d5a4..cbce73b046 100644
--- a/xbmc/cores/dvdplayer/DVDMessageQueue.cpp
+++ b/xbmc/cores/dvdplayer/DVDMessageQueue.cpp
@@ -163,7 +163,9 @@ MsgQueueReturnCode CDVDMessageQueue::Get(CDVDMsg** pMsg, unsigned int iTimeoutIn
if(m_list.empty() && m_bEmptied == false && priority == 0 && m_owner != "teletext")
{
+#if !defined(TARGET_RASPBERRY_PI)
CLog::Log(LOGWARNING, "CDVDMessageQueue(%s)::Get - asked for new data packet, with nothing available", m_owner.c_str());
+#endif
m_bEmptied = true;
}
diff --git a/xbmc/cores/omxplayer/BitstreamConverter.cpp b/xbmc/cores/omxplayer/BitstreamConverter.cpp
new file mode 100644
index 0000000000..13b6f1fdbb
--- /dev/null
+++ b/xbmc/cores/omxplayer/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; i<nbytes; i++ )
+ ret += buf[i]<<((nbytes-i-1)*8);
+ i = (4-nbytes)*8+br->offbits;
+ ret = ((ret<<i)>>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; i<nbytes; i++ )
+ ret += buf[i]<<((nbytes-i-1)*8);
+ i = (4-nbytes)*8+br->offbits;
+ ret = ((ret<<i)>>i)>>((nbytes*8)-nbits-br->offbits);
+
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+// GStreamer h264 parser
+// Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+// (C) 2008 Wim Taymans <wim.taymans@gmail.com>
+// 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 (p<out && out<end && !out[-1])
+ out--;
+ return out;
+}
+
+const int CBitstreamConverter::avc_parse_nal_units(AVIOContext *pb, const uint8_t *buf_in, int size)
+{
+ const uint8_t *p = buf_in;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ size = 0;
+ nal_start = avc_find_startcode(p, end);
+
+ for (;;) {
+ while (nal_start < end && !*(nal_start++));
+ if (nal_start == end)
+ break;
+
+ nal_end = avc_find_startcode(nal_start, end);
+ m_dllAvFormat->avio_wb32(pb, nal_end - nal_start);
+ m_dllAvFormat->avio_write(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)
+{
+ AVIOContext *pb;
+ int ret = m_dllAvFormat->avio_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->avio_close_dyn_buf(pb, buf);
+ return 0;
+}
+
+const int CBitstreamConverter::isom_write_avcc(AVIOContext *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->avio_w8(pb, 1); /* version */
+ m_dllAvFormat->avio_w8(pb, sps[1]); /* profile */
+ m_dllAvFormat->avio_w8(pb, sps[2]); /* profile compat */
+ m_dllAvFormat->avio_w8(pb, sps[3]); /* level */
+ m_dllAvFormat->avio_w8(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
+ m_dllAvFormat->avio_w8(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */
+
+ m_dllAvFormat->avio_wb16(pb, sps_size);
+ m_dllAvFormat->avio_write(pb, sps, sps_size);
+ if (pps)
+ {
+ m_dllAvFormat->avio_w8(pb, 1); /* number of pps */
+ m_dllAvFormat->avio_wb16(pb, pps_size);
+ m_dllAvFormat->avio_write(pb, pps, pps_size);
+ }
+ m_dllAvUtil->av_free(start);
+ }
+ else
+ {
+ m_dllAvFormat->avio_write(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;
+
+ AVIOContext *pb;
+ if (m_dllAvFormat->avio_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->avio_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
+ AVIOContext *pb;
+
+ if(m_dllAvFormat->avio_open_dyn_buf(&pb) < 0)
+ {
+ return false;
+ }
+ m_convertSize = avc_parse_nal_units(pb, pData, iSize);
+ m_convertSize = m_dllAvFormat->avio_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
+ AVIOContext *pb;
+ if (m_dllAvFormat->avio_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->avio_wb16(pb, nal_size);
+ nal_start += 3;
+ m_dllAvFormat->avio_write(pb, nal_start, nal_size);
+ nal_start += nal_size;
+ }
+
+ m_convertSize = m_dllAvFormat->avio_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;
+
+ AVIOContext *pb;
+ if (m_dllAvFormat->avio_open_dyn_buf(&pb) < 0)
+ return false;
+
+ m_dllAvFormat->avio_w8(pb, 0);
+ m_dllAvFormat->avio_w8(pb, 0);
+ m_dllAvFormat->avio_w8(pb, !m_convert_vc1 ? 0 : 1);
+ m_dllAvFormat->avio_w8(pb, !m_convert_vc1 ? 0 : 0xd);
+ m_dllAvFormat->avio_write(pb, pData, iSize);
+ m_convertSize = m_dllAvFormat->avio_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 <benoit.fouet@free.fr>
+ // 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 <benoit.fouet@free.fr>
+ // 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 <benoit.fouet@free.fr>
+ // 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/xbmc/cores/omxplayer/BitstreamConverter.h b/xbmc/cores/omxplayer/BitstreamConverter.h
new file mode 100644
index 0000000000..85b0e44e95
--- /dev/null
+++ b/xbmc/cores/omxplayer/BitstreamConverter.h
@@ -0,0 +1,171 @@
+/*
+ * 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 <stdint.h>
+#include "DllAvUtil.h"
+#include "DllAvFormat.h"
+#include "DllAvFilter.h"
+#include "DllAvCodec.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 <baptiste.coudurier@smartjog.com>
+// 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(AVIOContext *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(AVIOContext *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/xbmc/cores/omxplayer/DllOMX.h b/xbmc/cores/omxplayer/DllOMX.h
new file mode 100644
index 0000000000..4bad4604c1
--- /dev/null
+++ b/xbmc/cores/omxplayer/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 <IL/OMX_Core.h>
+#include <IL/OMX_Component.h>
+#include <IL/OMX_Index.h>
+#include <IL/OMX_Image.h>
+#include <IL/OMX_Video.h>
+#include <IL/OMX_Broadcom.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+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/xbmc/cores/omxplayer/Makefile.in b/xbmc/cores/omxplayer/Makefile.in
new file mode 100644
index 0000000000..0d6ec5c00e
--- /dev/null
+++ b/xbmc/cores/omxplayer/Makefile.in
@@ -0,0 +1,20 @@
+CXXFLAGS += -D__STDC_FORMAT_MACROS
+
+SRCS= \
+ OMXPlayer.cpp \
+ OMXAudio.cpp \
+ OMXVideo.cpp \
+ OMXAudioCodecOMX.cpp \
+ OMXPlayerAudio.cpp \
+ OMXPlayerVideo.cpp \
+ OMXImage.cpp \
+ BitstreamConverter.cpp
+
+LIB= omxplayer.a
+
+@abs_top_srcdir@/system/advancedsettings.xml: $(LIB)
+ cp -f omxplayer_advancedsettings.xml $@
+
+include @abs_top_srcdir@/Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+
diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
new file mode 100644
index 0000000000..8018826eee
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -0,0 +1,1478 @@
+/*
+* 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"
+
+#include "settings/AdvancedSettings.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "guilib/LocalizeStrings.h"
+#include "cores/AudioEngine/Utils/AEConvert.h"
+
+#ifndef VOLUME_MINIMUM
+#define VOLUME_MINIMUM -6000 // -60dB
+#endif
+
+using namespace std;
+
+#define OMX_MAX_CHANNELS 9
+
+static enum AEChannel OMXChannelMap[OMX_MAX_CHANNELS] =
+{
+ AE_CH_FL , AE_CH_FR,
+ AE_CH_FC , AE_CH_LFE,
+ AE_CH_BL , AE_CH_BR,
+ AE_CH_SL , AE_CH_SR,
+ AE_CH_RAW
+};
+
+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,
+ OMX_AUDIO_ChannelNone
+};
+
+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,
+ 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_OutputChannels (0 ),
+ m_BitsPerSample (0 ),
+ m_omx_clock (NULL ),
+ m_av_clock (NULL ),
+ m_external_clock (false ),
+ m_first_frame (true ),
+ m_LostSync (true ),
+ m_SampleRate (0 ),
+ m_eEncoding (OMX_AUDIO_CodingPCM),
+ m_extradata (NULL ),
+ m_extrasize (0 ),
+ m_last_pts (DVD_NOPTS_VALUE)
+{
+ m_vizBufferSize = m_vizRemapBufferSize = 4096;
+ m_vizRemapBuffer = (uint8_t *)_aligned_malloc(m_vizRemapBufferSize,16);
+ m_vizBuffer = (uint8_t *)_aligned_malloc(m_vizBufferSize,16);
+}
+
+COMXAudio::~COMXAudio()
+{
+ if(m_Initialized)
+ Deinitialize();
+
+ _aligned_free(m_vizRemapBuffer);
+ _aligned_free(m_vizBuffer);
+}
+
+
+CAEChannelInfo COMXAudio::GetChannelLayout(AEAudioFormat format)
+{
+ unsigned int count = 0;
+
+ if(format.m_dataFormat == AE_FMT_AC3 ||
+ format.m_dataFormat == AE_FMT_DTS ||
+ format.m_dataFormat == AE_FMT_EAC3)
+ count = 2;
+ else if (format.m_dataFormat == AE_FMT_TRUEHD ||
+ format.m_dataFormat == AE_FMT_DTSHD)
+ count = 8;
+ else
+ {
+ for (unsigned int c = 0; c < 8; ++c)
+ {
+ for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
+ {
+ if (format.m_channelLayout[i] == OMXChannelMap[c])
+ {
+ count = c + 1;
+ break;
+ }
+ }
+ }
+ }
+
+ CAEChannelInfo info;
+ for (unsigned int i = 0; i < count; ++i)
+ info += OMXChannelMap[i];
+
+ return info;
+}
+
+bool COMXAudio::Initialize(AEAudioFormat format, std::string& device, OMXClock *clock, CDVDStreamInfo &hints, bool bUsePassthrough, bool bUseHWDecode)
+{
+ m_HWDecode = bUseHWDecode;
+ m_Passthrough = bUsePassthrough;
+
+ m_format = format;
+
+ if(hints.samplerate == 0)
+ return false;
+
+ /* passthrough overwrites hw decode */
+ if(m_Passthrough)
+ {
+ m_HWDecode = false;
+ }
+ else if(m_HWDecode)
+ {
+ /* check again if we are capable to hw decode the format */
+ m_HWDecode = CanHWDecode(hints.codec);
+ }
+ SetCodingType(format.m_dataFormat);
+
+ 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(format, device);
+}
+
+bool COMXAudio::Initialize(AEAudioFormat format, std::string& device)
+{
+ if(m_Initialized)
+ Deinitialize();
+
+ m_format = format;
+
+ if(m_format.m_channelLayout.Count() == 0)
+ return false;
+
+ if(!m_dllAvUtil.Load())
+ 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_Passthrough = false;
+
+ if(OMX_IS_RAW(m_format.m_dataFormat))
+ m_Passthrough =true;
+ */
+
+ m_drc = 0;
+
+ m_CurrentVolume = g_settings.m_fVolumeLevel;
+
+ memset(m_input_channels, 0x0, sizeof(m_input_channels));
+ memset(m_output_channels, 0x0, sizeof(m_output_channels));
+ memset(&m_wave_header, 0x0, sizeof(m_wave_header));
+
+ m_output_channels[0] = OMX_AUDIO_ChannelLF;
+ m_output_channels[1] = OMX_AUDIO_ChannelRF;
+ m_output_channels[2] = OMX_AUDIO_ChannelMax;
+
+ m_input_channels[0] = OMX_AUDIO_ChannelLF;
+ m_input_channels[1] = OMX_AUDIO_ChannelRF;
+ m_input_channels[2] = OMX_AUDIO_ChannelMax;
+
+ m_OutputChannels = 2;
+ m_wave_header.Format.nChannels = m_OutputChannels;
+ m_wave_header.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+
+ if (!m_Passthrough)
+ {
+ /* setup output channel map */
+ /*
+ int ch = 0, map;
+ int chan = 0;
+ m_OutputChannels = 0;
+
+ for (unsigned int ch = 0; ch < m_format.m_channelLayout.Count(); ++ch)
+ {
+ for(map = 0; map < OMX_MAX_CHANNELS; ++map)
+ {
+ if (m_output_channels[ch] == OMXChannelMap[map])
+ {
+ printf("output %d\n", chan);
+ m_output_channels[chan] = OMXChannels[map];
+ chan++;
+ break;
+ }
+ }
+ }
+
+ m_OutputChannels = chan;
+ */
+
+ /* setup input channel map */
+ int map = 0;
+ int chan = 0;
+
+ for (unsigned int ch = 0; ch < m_format.m_channelLayout.Count(); ++ch)
+ {
+ for(map = 0; map < OMX_MAX_CHANNELS; ++map)
+ {
+ if (m_format.m_channelLayout[ch] == OMXChannelMap[map])
+ {
+ m_input_channels[chan] = OMXChannels[map];
+ m_wave_header.dwChannelMask |= WAVEChannels[map];
+ chan++;
+ break;
+ }
+ }
+ }
+
+ m_vizRemap.Initialize(m_format.m_channelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true);
+ }
+
+ OMX_INIT_STRUCTURE(m_pcm_output);
+ OMX_INIT_STRUCTURE(m_pcm_input);
+
+ memcpy(m_pcm_output.eChannelMapping, m_output_channels, sizeof(m_output_channels));
+ memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
+
+ // 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 = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+ m_pcm_output.ePCMMode = OMX_AUDIO_PCMModeLinear;
+ m_pcm_output.nChannels = m_OutputChannels;
+ m_pcm_output.nSamplingRate = m_format.m_sampleRate;
+
+ m_SampleRate = m_format.m_sampleRate;
+ m_BitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+ m_BufferLen = m_BytesPerSec = m_format.m_sampleRate *
+ (CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3) *
+ m_format.m_channelLayout.Count();
+ m_BufferLen *= AUDIO_BUFFER_SECONDS;
+ m_ChunkLen = 6144;
+
+ m_wave_header.Samples.wSamplesPerBlock = 0;
+ m_wave_header.Format.nChannels = m_format.m_channelLayout.Count();
+ m_wave_header.Format.nBlockAlign = m_format.m_channelLayout.Count() *
+ (CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3);
+ m_wave_header.Format.wFormatTag = WAVE_FORMAT_PCM;
+ m_wave_header.Format.nSamplesPerSec = m_format.m_sampleRate;
+ m_wave_header.Format.nAvgBytesPerSec = m_BytesPerSec;
+ m_wave_header.Format.wBitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+ m_wave_header.Samples.wValidBitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+ m_wave_header.Format.cbSize = 0;
+ m_wave_header.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+
+ m_pcm_input.eNumData = OMX_NumericalDataSigned;
+ m_pcm_input.eEndian = OMX_EndianLittle;
+ m_pcm_input.bInterleaved = OMX_TRUE;
+ m_pcm_input.nBitPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+ m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear;
+ m_pcm_input.nChannels = m_format.m_channelLayout.Count();
+ m_pcm_input.nSamplingRate = m_format.m_sampleRate;
+
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ std::string componentName = "";
+
+ componentName = "OMX.broadcom.audio_render";
+ if(!m_omx_render.Initialize((const std::string)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;
+
+ OMX_CONFIG_BOOLEANTYPE configBool;
+ OMX_INIT_STRUCTURE(configBool);
+ configBool.bEnabled = OMX_FALSE;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmClockReferenceSource, &configBool);
+ if (omx_err != OMX_ErrorNone)
+ return false;
+
+ componentName = "OMX.broadcom.audio_decode";
+ if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamAudioInit))
+ return false;
+
+ if(!m_Passthrough)
+ {
+ componentName = "OMX.broadcom.audio_mixer";
+ if(!m_omx_mixer.Initialize((const std::string)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);
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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_first_frame = true;
+ m_last_pts = DVD_NOPTS_VALUE;
+
+ SetCurrentVolume(m_CurrentVolume);
+
+ CLog::Log(LOGDEBUG, "COMXAudio::Initialize Ouput bps %d samplerate %d channels %d buffer size %d bytes per second %d",
+ (int)m_pcm_output.nBitPerSample, (int)m_pcm_output.nSamplingRate, (int)m_pcm_output.nChannels, m_BufferLen, m_BytesPerSec);
+ CLog::Log(LOGDEBUG, "COMXAudio::Initialize Input bps %d samplerate %d channels %d buffer size %d bytes per second %d",
+ (int)m_pcm_input.nBitPerSample, (int)m_pcm_input.nSamplingRate, (int)m_pcm_input.nChannels, m_BufferLen, m_BytesPerSec);
+ CLog::Log(LOGDEBUG, "COMXAudio::Initialize device %s passthrough %d hwdecode %d external clock %d",
+ device.c_str(), m_Passthrough, m_HWDecode, m_external_clock);
+
+ m_av_clock->OMXStateExecute(false);
+
+ return true;
+}
+
+//***********************************************************************************************
+bool COMXAudio::Deinitialize()
+{
+ if(!m_Initialized)
+ return true;
+
+ /*
+ if(m_av_clock && !m_external_clock)
+ m_av_clock->OMXStop();
+ */
+
+ if(m_av_clock && !m_external_clock)
+ {
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ }
+
+ 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_av_clock && !m_external_clock)
+ {
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ }
+
+ 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_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_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(float fVolume)
+{
+ if(!m_Initialized || m_Passthrough)
+ return -1;
+
+ m_CurrentVolume = fVolume;
+
+ OMX_AUDIO_CONFIG_VOLUMETYPE volume;
+ OMX_INIT_STRUCTURE(volume);
+ volume.nPortIndex = m_omx_render.GetInputPort();
+
+ volume.bLinear = OMX_TRUE;
+ float hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, fVolume)) * 100.0f;
+ volume.sVolume.nValue = (int)hardwareVolume;
+
+ m_omx_render.SetConfig(OMX_IndexConfigAudioVolume, &volume);
+
+ return true;
+}
+
+
+//***********************************************************************************************
+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;
+ }
+
+ m_vizBufferSamples = 0;
+
+ if (!m_Passthrough && m_pCallback && len)
+ {
+ /* unput samples */
+ m_vizBufferSamples = len / (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3);
+ CAEConvert::AEConvertToFn m_convertFn = CAEConvert::ToFloat(AE_FMT_S16LE);
+ /* input frames */
+ unsigned int frames = m_vizBufferSamples / m_format.m_channelLayout.Count();
+
+ /* check convert buffer */
+ CheckOutputBufferSize((void **)&m_vizBuffer, &m_vizBufferSize, m_vizBufferSamples * (CAEUtil::DataFormatToBits(AE_FMT_FLOAT) >> 3));
+
+ /* convert to float */
+ m_convertFn((uint8_t *)data, m_vizBufferSamples, (float *)m_vizBuffer);
+
+ /* check remap buffer */
+ CheckOutputBufferSize((void **)&m_vizRemapBuffer, &m_vizRemapBufferSize, frames * 2 * (CAEUtil::DataFormatToBits(AE_FMT_FLOAT) >> 3));
+
+ /* remap */
+ m_vizRemap.Remap((float *)m_vizBuffer, (float*)m_vizRemapBuffer, frames);
+
+ /* output samples */
+ m_vizBufferSamples = m_vizBufferSamples / m_format.m_channelLayout.Count() * 2;
+
+ /* viz size is limited */
+ if(m_vizBufferSamples > VIS_PACKET_SIZE)
+ m_vizBufferSamples = VIS_PACKET_SIZE;
+
+ if(m_pCallback)
+ m_pCallback->OnAudioData((float *)m_vizRemapBuffer, m_vizBufferSamples);
+ }
+
+ if(m_eEncoding == OMX_AUDIO_CodingDTS && m_LostSync && (m_Passthrough || m_HWDecode))
+ {
+ int skip = SyncDTS((uint8_t *)data, len);
+ if(skip > 0)
+ return len;
+ }
+
+ if(m_eEncoding == OMX_AUDIO_CodingDDP && m_LostSync && (m_Passthrough || m_HWDecode))
+ {
+ 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");
+ 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);
+
+ uint64_t val = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts;
+
+ if(m_av_clock->AudioStart())
+ {
+ omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME;
+
+ m_last_pts = pts;
+
+ CLog::Log(LOGDEBUG, "ADec : setStartTime %f\n", (float)val / DVD_TIME_BASE);
+ m_av_clock->AudioStart(false);
+ }
+ 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__);
+ 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);
+ }
+
+ if (g_guiSettings.GetBool("audiooutput.normalizelevels"))
+ {
+ OMX_AUDIO_CONFIG_VOLUMETYPE volume;
+ OMX_INIT_STRUCTURE(volume);
+ volume.nPortIndex = m_omx_mixer.GetInputPort();
+ volume.bLinear = OMX_FALSE;
+ volume.sVolume.nValue = (int)(g_advancedSettings.m_ac3Gain*100.0f+0.5f);
+ m_omx_mixer.SetConfig(OMX_IndexConfigAudioVolume, &volume);
+ }
+
+ memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
+ m_pcm_input.nSamplingRate = m_format.m_sampleRate;
+
+ /* 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 input 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 input omx_err(0x%08x)\n", omx_err);
+ }
+
+ m_pcm_output.nSamplingRate = m_format.m_sampleRate;
+
+ /* 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 output 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 output omx_err(0x%08x)\n", omx_err);
+ }
+
+ m_pcm_output.nSamplingRate = m_format.m_sampleRate;
+
+ 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 render 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 render omx_err(0x%08x)\n", omx_err);
+ }
+
+ PrintPCM(&m_pcm_input, std::string("input"));
+ PrintPCM(&m_pcm_output, std::string("output"));
+ }
+ else
+ {
+ m_pcm_output.nPortIndex = m_omx_decoder.GetOutputPort();
+ m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output);
+ PrintPCM(&m_pcm_output, std::string("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_format.m_channelLayout.Count(); //(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_format.m_channelLayout.Count(); //(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;
+}
+
+//***********************************************************************************************
+unsigned int COMXAudio::GetSpace()
+{
+ int free = m_omx_decoder.GetInputBufferSpace();
+ return free;
+}
+
+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_vizBufferSamples)
+ {
+ m_pCallback->OnAudioData((float*)m_vizBuffer, m_vizBufferSamples);
+ m_vizBufferSamples = 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;
+ }
+ Sleep(50);
+ }
+
+ return;
+}
+
+void COMXAudio::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers)
+{
+ return ;
+}
+
+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(AEDataFormat dataFormat)
+{
+ switch(dataFormat)
+ {
+ case AE_FMT_DTS:
+ CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingDTS\n");
+ m_eEncoding = OMX_AUDIO_CodingDTS;
+ break;
+ case AE_FMT_AC3:
+ case AE_FMT_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)
+{
+ bool ret = false;
+ switch(codec)
+ {
+ case CODEC_ID_DTS:
+ CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingDTS\n");
+ ret = true;
+ break;
+ case CODEC_ID_AC3:
+ case CODEC_ID_EAC3:
+ CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingDDP\n");
+ ret = true;
+ break;
+ default:
+ CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingPCM\n");
+ 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, std::string direction)
+{
+ CLog::Log(LOGDEBUG, "pcm->direction : %s\n", direction.c_str());
+ 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;
+}
+
+void COMXAudio::CheckOutputBufferSize(void **buffer, int *oldSize, int newSize)
+{
+ if (newSize > *oldSize)
+ {
+ if (*buffer)
+ _aligned_free(*buffer);
+ *buffer = _aligned_malloc(newSize, 16);
+ *oldSize = newSize;
+ }
+ memset(*buffer, 0x0, *oldSize);
+}
+
diff --git a/xbmc/cores/omxplayer/OMXAudio.h b/xbmc/cores/omxplayer/OMXAudio.h
new file mode 100644
index 0000000000..55caef178e
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXAudio.h
@@ -0,0 +1,155 @@
+/*
+* 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
+
+#include "cores/AudioEngine/AEAudioFormat.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "cores/AudioEngine/Utils/AERemap.h"
+#include "cores/IAudioCallback.h"
+#include "linux/PlatformDefs.h"
+#include "DllAvCodec.h"
+#include "DllAvUtil.h"
+#include "OMXCore.h"
+#include "OMXClock.h"
+#include "DVDStreamInfo.h"
+#include "BitstreamConverter.h"
+
+#define AUDIO_BUFFER_SECONDS 2
+#define VIS_PACKET_SIZE 512
+
+#define OMX_IS_RAW(x) \
+( \
+ (x) == AE_FMT_AC3 || \
+ (x) == AE_FMT_DTS \
+)
+
+class CAERemap;
+
+class COMXAudio
+{
+public:
+ void UnRegisterAudioCallback();
+ void RegisterAudioCallback(IAudioCallback* pCallback);
+ unsigned int GetChunkLen();
+ float GetDelay();
+ float GetCacheTime();
+ float GetCacheTotal();
+ COMXAudio();
+ bool Initialize(AEAudioFormat format, std::string& device, OMXClock *clock, CDVDStreamInfo &hints, bool bUsePassthrough, bool bUseHWDecode);
+ bool Initialize(AEAudioFormat format, std::string& device);
+ ~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(float fVolume);
+ void SetDynamicRangeCompression(long drc) { m_drc = drc; }
+ int SetPlaySpeed(int iSpeed);
+ void WaitCompletion();
+ void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers);
+
+ void Flush();
+ void DoAudioWork();
+
+ void Process();
+
+ bool SetClock(OMXClock *clock);
+ void SetCodingType(AEDataFormat dataFormat);
+ static bool CanHWDecode(CodecID codec);
+
+ void PrintChannels(OMX_AUDIO_CHANNELTYPE eChannelMapping[]);
+ void PrintPCM(OMX_AUDIO_PARAM_PCMMODETYPE *pcm, std::string direction);
+ 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;
+ float 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_OutputChannels;
+ unsigned int m_BitsPerSample;
+ COMXCoreComponent *m_omx_clock;
+ OMXClock *m_av_clock;
+ bool m_external_clock;
+ 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_vizBufferSamples;
+ double m_last_pts;
+ int m_vizBufferSize;
+ uint8_t *m_vizBuffer;
+ int m_vizRemapBufferSize;
+ uint8_t *m_vizRemapBuffer;
+ CAERemap m_vizRemap;
+
+ OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_output;
+ OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_input;
+ OMX_AUDIO_PARAM_DTSTYPE m_dtsParam;
+ WAVEFORMATEXTENSIBLE m_wave_header;
+ AEAudioFormat m_format;
+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;
+
+ OMX_AUDIO_CHANNELTYPE m_input_channels[OMX_AUDIO_MAXCHANNELS];
+ OMX_AUDIO_CHANNELTYPE m_output_channels[OMX_AUDIO_MAXCHANNELS];
+
+ CAEChannelInfo m_channelLayout;
+
+ CAEChannelInfo GetChannelLayout(AEAudioFormat format);
+
+ void CheckOutputBufferSize(void **buffer, int *oldSize, int newSize);
+};
+#endif
+
diff --git a/xbmc/cores/omxplayer/OMXAudioCodec.h b/xbmc/cores/omxplayer/OMXAudioCodec.h
new file mode 100644
index 0000000000..76d7049829
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXAudioCodec.h
@@ -0,0 +1,119 @@
+#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 "system.h"
+
+#if (defined HAVE_CONFIG_H) && (!defined WIN32)
+ #include "config.h"
+#endif
+#include <vector>
+#include "DllAvCodec.h"
+
+struct AVStream;
+
+class COMXStreamInfo;
+
+class COMXAudioCodec
+{
+public:
+
+ COMXAudioCodec() {}
+ virtual ~COMXAudioCodec() {}
+
+ /*
+ * Open the decoder, returns true on success
+ */
+ virtual bool Open(COMXStreamInfo &hints) = 0;
+
+ /*
+ * Dispose, Free all resources
+ */
+ virtual void Dispose() = 0;
+
+ /*
+ * returns bytes used or -1 on error
+ *
+ */
+ virtual int Decode(BYTE* pData, int iSize) = 0;
+
+ /*
+ * returns nr of bytes used or -1 on error
+ * the data is valid until the next Decode call
+ */
+ virtual int GetData(BYTE** dst) = 0;
+
+ /*
+ * resets the decoder
+ */
+ virtual void Reset() = 0;
+
+ /*
+ * returns the nr of channels for the decoded audio stream
+ */
+ virtual int GetChannels() = 0;
+
+ /*
+ * returns the channel mapping
+ */
+ virtual enum PCMChannels* GetChannelMap() = 0;
+
+ /*
+ * returns the samplerate for the decoded audio stream
+ */
+ virtual int GetSampleRate() = 0;
+
+ /*
+ * returns the bitspersample for the decoded audio stream (eg 16 bits)
+ */
+ virtual int GetBitsPerSample() = 0;
+
+ /*
+ * returns the framesize for bitstreams
+ */
+ virtual int GetFrameSize() = 0;
+
+ /*
+ * returns the syncword for bitstreams
+ */
+ virtual uint32_t GetSyncWord() = 0;
+
+ /*
+ * should return the average input bit rate
+ */
+ virtual int GetBitRate() { return 0; }
+
+ /*
+ * returns if the codec requests to use passtrough
+ */
+ virtual bool NeedPassthrough() { return false; }
+
+ /*
+ * should return codecs name
+ */
+ virtual const char* GetName() = 0;
+
+ /*
+ * should return amount of data decoded has buffered in preparation for next audio frame
+ */
+ virtual int GetBufferSize() { return 0; }
+};
diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
new file mode 100644
index 0000000000..b8b3372e48
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
@@ -0,0 +1,382 @@
+/*
+ * 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"
+
+#include "cores/AudioEngine/Utils/AEUtil.h"
+
+#define MAX_AUDIO_FRAME_SIZE (AVCODEC_MAX_AUDIO_FRAME_SIZE*2)
+
+template <class AudioDataType>
+static inline void _Upmix(AudioDataType *input,
+ unsigned int channelsInput, AudioDataType *output,
+ unsigned int channelsOutput, unsigned int frames)
+{
+ unsigned int unused = channelsOutput - channelsInput;
+ AudioDataType *_input = input;
+ AudioDataType *_output = output;
+
+ for (unsigned int i = 0; i < frames; i++)
+ {
+ // get input channels
+ for(unsigned int j = 0; j < channelsInput; j++)
+ *_output++ = *_input++;
+ // set unused channels
+ for(unsigned int j = 0; j < unused; j++)
+ *_output++ = 0;
+ }
+}
+
+void COMXAudioCodecOMX::Upmix(void *input,
+ unsigned int channelsInput, void *output,
+ unsigned int channelsOutput, unsigned int frames, AEDataFormat dataFormat)
+{
+ // input channels must be less than output channels
+ if (channelsInput >= channelsOutput)
+ return;
+
+ switch (CAEUtil::DataFormatToBits(dataFormat))
+ {
+ case 8: _Upmix ( (unsigned char *) input, channelsInput, (unsigned char *) output, channelsOutput, frames ); break;
+ case 16: _Upmix ( (short *) input, channelsInput, (short *) output, channelsOutput, frames ); break;
+ case 32: _Upmix ( (float *) input, channelsInput, (float *) output, channelsOutput, frames ); break;
+ default: _Upmix ( (int *) input, channelsInput, (int *) output, channelsOutput, frames ); break;
+ }
+}
+
+COMXAudioCodecOMX::COMXAudioCodecOMX()
+{
+ 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_iBufferUpmixSize = 0;
+ m_pBufferUpmix = (BYTE*)_aligned_malloc(MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16);
+ memset(m_pBufferUpmix, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
+
+ m_iBuffered = 0;
+ m_pCodecContext = NULL;
+ m_pConvert = NULL;
+ m_bOpenedCodec = false;
+
+ m_channels = 0;
+ m_layout = 0;
+ m_pFrame1 = NULL;
+}
+
+COMXAudioCodecOMX::~COMXAudioCodecOMX()
+{
+ _aligned_free(m_pBuffer2);
+ _aligned_free(m_pBufferUpmix);
+ Dispose();
+}
+
+bool COMXAudioCodecOMX::Open(CDVDStreamInfo &hints)
+{
+ AVCodec* pCodec;
+ m_bOpenedCodec = false;
+
+ if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load())
+ return false;
+
+ m_dllAvCodec.avcodec_register_all();
+
+ 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 = m_dllAvCodec.avcodec_alloc_context3(pCodec);
+ 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_open2(m_pCodecContext, pCodec, NULL) < 0)
+ {
+ CLog::Log(LOGDEBUG,"COMXAudioCodecOMX::Open() Unable to open codec");
+ Dispose();
+ return false;
+ }
+
+ m_pFrame1 = m_dllAvCodec.avcodec_alloc_frame();
+ m_bOpenedCodec = true;
+ m_iSampleFormat = AV_SAMPLE_FMT_NONE;
+ return true;
+}
+
+void COMXAudioCodecOMX::Dispose()
+{
+ if (m_pFrame1) m_dllAvUtil.av_free(m_pFrame1);
+ m_pFrame1 = NULL;
+
+ 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, got_frame;
+ if (!m_pCodecContext) return -1;
+
+ 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_audio4( m_pCodecContext
+ , m_pFrame1
+ , &got_frame
+ , &avpkt);
+ if (iBytesUsed < 0 || !got_frame)
+ {
+ m_iBufferSize1 = 0;
+ m_iBufferSize2 = 0;
+ return iBytesUsed;
+ }
+ m_iBufferSize1 = m_dllAvUtil.av_samples_get_buffer_size(NULL, m_pCodecContext->channels, m_pFrame1->nb_samples, m_pCodecContext->sample_fmt, 1);
+
+ /* 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_pFrame1->data[0] };
+ void *obuf[6] = { m_pBuffer2 };
+ int istr[6] = { m_dllAvUtil.av_get_bytes_per_sample(m_pCodecContext->sample_fmt) };
+ int ostr[6] = { (int) (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3) };
+ 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)
+{
+ unsigned int size = 0;
+ BYTE *src = NULL;
+
+ if(m_iBufferSize1)
+ {
+ *dst = m_pFrame1->data[0];
+ src = m_pFrame1->data[0];
+ size = m_iBufferSize1;
+ }
+ if(m_iBufferSize2)
+ {
+ *dst = m_pBuffer2;
+ src = m_pBuffer2;
+ size = m_iBufferSize2;
+ }
+
+ if(m_pCodecContext->channels > 4 && size)
+ {
+ unsigned int m_frameSize = (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3) * m_pCodecContext->channels;
+ unsigned int frames = size / m_frameSize;
+
+ memset(m_pBufferUpmix, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
+
+ Upmix(src, m_pCodecContext->channels, m_pBufferUpmix, 8, frames, AE_FMT_S16LE);
+
+ m_iBufferUpmixSize = frames * 8 * (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3);
+
+ *dst = m_pBufferUpmix;
+ size = m_iBufferUpmixSize;
+ }
+
+ return size;
+}
+
+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 > 4) ? 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_dllAvUtil.av_get_default_channel_layout(m_pCodecContext->channels);
+ }
+
+ m_channelLayout.Reset();
+
+ if (layout & AV_CH_FRONT_LEFT ) m_channelLayout += AE_CH_FL ;
+ if (layout & AV_CH_FRONT_RIGHT ) m_channelLayout += AE_CH_FR ;
+ if (layout & AV_CH_FRONT_CENTER ) m_channelLayout += AE_CH_FC ;
+ if (layout & AV_CH_LOW_FREQUENCY ) m_channelLayout += AE_CH_LFE ;
+ if (layout & AV_CH_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
+ if (layout & AV_CH_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+ if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelLayout += AE_CH_FLOC;
+ if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelLayout += AE_CH_FROC;
+ if (layout & AV_CH_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_SIDE_LEFT ) m_channelLayout += AE_CH_SL ;
+ if (layout & AV_CH_SIDE_RIGHT ) m_channelLayout += AE_CH_SR ;
+ if (layout & AV_CH_TOP_CENTER ) m_channelLayout += AE_CH_TC ;
+ if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelLayout += AE_CH_TFL ;
+ if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelLayout += AE_CH_TFC ;
+ if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelLayout += AE_CH_TFR ;
+ if (layout & AV_CH_TOP_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
+ if (layout & AV_CH_TOP_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+
+ //terminate the channel map
+ if(m_pCodecContext->channels > 4)
+ {
+ for(int i = m_pCodecContext->channels; i < 8; i++)
+ m_channelLayout += AE_CH_RAW;
+ }
+}
+
+CAEChannelInfo COMXAudioCodecOMX::GetChannelMap()
+{
+ BuildChannelMap();
+ return m_channelLayout;
+}
diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.h b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
new file mode 100644
index 0000000000..e740e5b47c
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
@@ -0,0 +1,77 @@
+#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 "cores/AudioEngine/AEAudioFormat.h"
+#include "DllAvCodec.h"
+#include "DllAvFormat.h"
+#include "DllAvUtil.h"
+
+#include "DVDStreamInfo.h"
+#include "linux/PlatformDefs.h"
+
+class COMXAudioCodecOMX
+{
+public:
+ void Upmix(void *input, unsigned int channelsInput, void *output,
+ unsigned int channelsOutput, unsigned int frames, AEDataFormat dataFormat);
+ COMXAudioCodecOMX();
+ virtual ~COMXAudioCodecOMX();
+ bool Open(CDVDStreamInfo &hints);
+ void Dispose();
+ int Decode(BYTE* pData, int iSize);
+ int GetData(BYTE** dst);
+ void Reset();
+ int GetChannels();
+ virtual CAEChannelInfo 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;
+ CAEChannelInfo m_channelLayout;
+
+ AVFrame* m_pFrame1;
+ int m_iBufferSize1;
+
+ BYTE *m_pBuffer2;
+ int m_iBufferSize2;
+
+ BYTE *m_pBufferUpmix;
+ int m_iBufferUpmixSize;
+
+ bool m_bOpenedCodec;
+ int m_iBuffered;
+
+ int m_channels;
+ uint64_t m_layout;
+
+ DllAvCodec m_dllAvCodec;
+ DllAvUtil m_dllAvUtil;
+
+ void BuildChannelMap();
+};
diff --git a/xbmc/cores/omxplayer/OMXImage.cpp b/xbmc/cores/omxplayer/OMXImage.cpp
new file mode 100644
index 0000000000..7eb2e99bd3
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXImage.cpp
@@ -0,0 +1,1114 @@
+/*
+ * 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_CONFIG_H) && (!defined WIN32)
+ #include "config.h"
+#elif defined(_WIN32)
+#include "system.h"
+#endif
+
+#include "OMXImage.h"
+
+#include "utils/log.h"
+#include "linux/XMemUtils.h"
+
+#include "BitstreamConverter.h"
+
+#include <sys/time.h>
+#include <inttypes.h>
+#ifndef STANDALONE
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+#endif
+
+#ifdef CLASSNAME
+#undef CLASSNAME
+#endif
+#define CLASSNAME "COMXImage"
+
+#define CONTENTURI_MAXLEN 256
+
+#define EXIF_TAG_ORIENTATION 0x0112
+
+static CCriticalSection g_OMXSection;
+
+COMXImage::COMXImage()
+{
+ m_is_open = false;
+ m_image_size = 0;
+ m_image_buffer = NULL;
+ m_progressive = false;
+ m_alpha = false;
+ m_orientation = 0;
+ m_width = 0;
+ m_height = 0;
+
+ m_is_open = false;
+ m_decoded_buffer = NULL;
+ m_encoded_buffer = NULL;
+ m_decoder_open = false;
+ m_encoder_open = false;
+
+ OMX_INIT_STRUCTURE(m_decoded_format);
+ OMX_INIT_STRUCTURE(m_encoded_format);
+ memset(&m_omx_image, 0x0, sizeof(OMX_IMAGE_PORTDEFINITIONTYPE));
+}
+
+COMXImage::~COMXImage()
+{
+ Close();
+}
+
+void COMXImage::Close()
+{
+ OMX_INIT_STRUCTURE(m_decoded_format);
+ OMX_INIT_STRUCTURE(m_encoded_format);
+ memset(&m_omx_image, 0x0, sizeof(OMX_IMAGE_PORTDEFINITIONTYPE));
+
+ if(m_image_buffer)
+ free(m_image_buffer);
+
+ m_image_buffer = NULL;
+ m_image_size = 0;
+ m_width = 0;
+ m_height = 0;
+ m_is_open = false;
+ m_progressive = false;
+ m_orientation = 0;
+ m_decoded_buffer = NULL;
+ m_encoded_buffer = NULL;
+
+ if(m_decoder_open)
+ {
+ m_omx_decoder.FlushInput();
+ m_omx_decoder.FreeInputBuffers(true);
+ m_omx_resize.FlushOutput();
+ m_omx_resize.FreeOutputBuffers(true);
+
+ m_omx_tunnel_decode.Flush();
+ m_omx_tunnel_decode.Flush();
+ m_omx_tunnel_decode.Deestablish();
+ m_omx_decoder.Deinitialize();
+ m_omx_resize.Deinitialize();
+ m_decoder_open = false;
+ }
+
+ if(m_encoder_open)
+ {
+ m_omx_encoder.Deinitialize();
+ m_encoder_open = false;
+ }
+
+ m_pFile.Close();
+}
+
+typedef enum { /* JPEG marker codes */
+ M_SOF0 = 0xc0,
+ M_SOF1 = 0xc1,
+ M_SOF2 = 0xc2,
+ M_SOF3 = 0xc3,
+ M_SOF5 = 0xc5,
+ M_SOF6 = 0xc6,
+ M_SOF7 = 0xc7,
+ M_JPG = 0xc8,
+ M_SOF9 = 0xc9,
+ M_SOF10 = 0xca,
+ M_SOF11 = 0xcb,
+ M_SOF13 = 0xcd,
+ M_SOF14 = 0xce,
+ M_SOF15 = 0xcf,
+
+ M_DHT = 0xc4,
+
+ M_RST0 = 0xd0,
+ M_RST1 = 0xd1,
+ M_RST2 = 0xd2,
+ M_RST3 = 0xd3,
+ M_RST4 = 0xd4,
+ M_RST5 = 0xd5,
+ M_RST6 = 0xd6,
+ M_RST7 = 0xd7,
+
+ M_SOI = 0xd8,
+ M_EOI = 0xd9,
+ M_SOS = 0xda,
+ M_DQT = 0xdb,
+ M_DNL = 0xdc,
+ M_DRI = 0xdd,
+ M_DHP = 0xde,
+ M_EXP = 0xdf,
+
+ M_APP0 = 0xe0,
+ M_APP1 = 0xe1,
+ M_APP2 = 0xe2,
+ M_APP3 = 0xe3,
+ M_APP4 = 0xe4,
+ M_APP5 = 0xe5,
+ M_APP6 = 0xe6,
+ M_APP7 = 0xe7,
+ M_APP8 = 0xe8,
+ M_APP9 = 0xe9,
+ M_APP10 = 0xea,
+ M_APP11 = 0xeb,
+ M_APP12 = 0xec,
+ M_APP13 = 0xed,
+ M_APP14 = 0xee,
+ M_APP15 = 0xef,
+
+ M_TEM = 0x01,
+} JPEG_MARKER;
+
+OMX_IMAGE_CODINGTYPE COMXImage::GetCodingType()
+{
+ memset(&m_omx_image, 0x0, sizeof(OMX_IMAGE_PORTDEFINITIONTYPE));
+ m_width = 0;
+ m_height = 0;
+ m_progressive = false;
+ m_orientation = 0;
+
+ m_omx_image.eCompressionFormat = OMX_IMAGE_CodingMax;
+
+ if(!m_image_size)
+ return OMX_IMAGE_CodingMax;
+
+ bits_reader_t br;
+ CBitstreamConverter::bits_reader_set( &br, m_image_buffer, m_image_size );
+
+ /* JPEG Header */
+ if(CBitstreamConverter::read_bits(&br, 16) == 0xFFD8)
+ {
+ m_omx_image.eCompressionFormat = OMX_IMAGE_CodingJPEG;
+
+ CBitstreamConverter::read_bits(&br, 8);
+ unsigned char marker = CBitstreamConverter::read_bits(&br, 8);
+ unsigned short block_size = 0;
+ bool nMarker = false;
+
+ while(!br.oflow) {
+
+ switch(marker)
+ {
+ case M_TEM:
+ case M_DRI:
+ CBitstreamConverter::skip_bits(&br, 16);
+ continue;
+ case M_SOI:
+ case M_EOI:
+ continue;
+
+ case M_SOS:
+ case M_DQT:
+ case M_DNL:
+ case M_DHP:
+ case M_EXP:
+
+ case M_DHT:
+
+ case M_SOF0:
+ case M_SOF1:
+ case M_SOF2:
+ case M_SOF3:
+
+ case M_SOF5:
+ case M_SOF6:
+ case M_SOF7:
+
+ case M_JPG:
+ case M_SOF9:
+ case M_SOF10:
+ case M_SOF11:
+
+ case M_SOF13:
+ case M_SOF14:
+ case M_SOF15:
+
+ case M_APP0:
+ case M_APP1:
+ case M_APP2:
+ case M_APP3:
+ case M_APP4:
+ case M_APP5:
+ case M_APP6:
+ case M_APP7:
+ case M_APP8:
+ case M_APP9:
+ case M_APP10:
+ case M_APP11:
+ case M_APP12:
+ case M_APP13:
+ case M_APP14:
+ case M_APP15:
+ block_size = CBitstreamConverter::read_bits(&br, 16);
+ nMarker = true;
+ break;
+
+ default:
+ nMarker = false;
+ break;
+ }
+
+ if(!nMarker)
+ {
+ break;
+ }
+
+ if(marker >= M_SOF0 && marker <= M_SOF15)
+ {
+ if(marker == M_SOF2 || marker == M_SOF6 || marker == M_SOF10 || marker == M_SOF14)
+ {
+ m_progressive = true;
+ }
+ CBitstreamConverter::skip_bits(&br, 8);
+ m_omx_image.nFrameHeight = CBitstreamConverter::read_bits(&br, 16);
+ m_omx_image.nFrameWidth = CBitstreamConverter::read_bits(&br, 16);
+
+ CBitstreamConverter::skip_bits(&br, 8 * (block_size - 9));
+ }
+ else if(marker == M_APP1)
+ {
+ int readBits = 2;
+ bool bMotorolla = false;
+ bool bError = false;
+
+ // Exif header
+ if(CBitstreamConverter::read_bits(&br, 32) == 0x45786966)
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * 2);
+ readBits += 2;
+
+ char o1 = CBitstreamConverter::read_bits(&br, 8);
+ char o2 = CBitstreamConverter::read_bits(&br, 8);
+ readBits += 2;
+
+ /* Discover byte order */
+ if(o1 == 'M' && o2 == 'M')
+ bMotorolla = true;
+ else if(o1 == 'I' && o2 == 'I')
+ bMotorolla = false;
+ else
+ bError = true;
+
+ CBitstreamConverter::skip_bits(&br, 8 * 2);
+ readBits += 2;
+
+ if(!bError)
+ {
+ unsigned int offset, a, b, numberOfTags, tagNumber;
+
+ // Get first IFD offset (offset to IFD0)
+ if(bMotorolla)
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * 2);
+ readBits += 2;
+
+ a = CBitstreamConverter::read_bits(&br, 8);
+ b = CBitstreamConverter::read_bits(&br, 8);
+ readBits += 2;
+ offset = (a << 8) + b;
+ }
+ else
+ {
+ a = CBitstreamConverter::read_bits(&br, 8);
+ b = CBitstreamConverter::read_bits(&br, 8);
+ readBits += 2;
+ offset = (b << 8) + a;
+
+ CBitstreamConverter::skip_bits(&br, 8 * 2);
+ readBits += 2;
+ }
+
+ offset -= 8;
+ if(offset > 0)
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * offset);
+ readBits += offset;
+ }
+
+ // Get the number of directory entries contained in this IFD
+ if(bMotorolla)
+ {
+ a = CBitstreamConverter::read_bits(&br, 8);
+ b = CBitstreamConverter::read_bits(&br, 8);
+ numberOfTags = (a << 8) + b;
+ }
+ else
+ {
+ a = CBitstreamConverter::read_bits(&br, 8);
+ b = CBitstreamConverter::read_bits(&br, 8);
+ numberOfTags = (b << 8) + a;
+ }
+ readBits += 2;
+
+ while(numberOfTags && !br.oflow)
+ {
+ // Get Tag number
+ if(bMotorolla)
+ {
+ a = CBitstreamConverter::read_bits(&br, 8);
+ b = CBitstreamConverter::read_bits(&br, 8);
+ tagNumber = (a << 8) + b;
+ readBits += 2;
+ }
+ else
+ {
+ a = CBitstreamConverter::read_bits(&br, 8);
+ b = CBitstreamConverter::read_bits(&br, 8);
+ tagNumber = (b << 8) + a;
+ readBits += 2;
+ }
+
+ //found orientation tag
+ if(tagNumber == EXIF_TAG_ORIENTATION)
+ {
+ if(bMotorolla)
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * 7);
+ readBits += 7;
+ m_orientation = CBitstreamConverter::read_bits(&br, 8);
+ readBits += 1;
+ CBitstreamConverter::skip_bits(&br, 8 * 2);
+ readBits += 2;
+ }
+ else
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * 6);
+ readBits += 6;
+ m_orientation = CBitstreamConverter::read_bits(&br, 8);
+ readBits += 1;
+ CBitstreamConverter::skip_bits(&br, 8 * 3);
+ readBits += 3;
+ }
+ break;
+ }
+ else
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * 10);
+ readBits += 10;
+ }
+ numberOfTags--;
+ }
+ }
+ }
+ readBits += 4;
+ CBitstreamConverter::skip_bits(&br, 8 * (block_size - readBits));
+ }
+ else
+ {
+ CBitstreamConverter::skip_bits(&br, 8 * (block_size - 2));
+ }
+
+ CBitstreamConverter::read_bits(&br, 8);
+ marker = CBitstreamConverter::read_bits(&br, 8);
+
+ }
+
+ }
+
+ CBitstreamConverter::bits_reader_set( &br, m_image_buffer, m_image_size );
+
+ /* PNG Header */
+ if(CBitstreamConverter::read_bits(&br, 32) == 0x89504E47)
+ {
+ m_omx_image.eCompressionFormat = OMX_IMAGE_CodingPNG;
+ CBitstreamConverter::skip_bits(&br, 32 * 2);
+ if(CBitstreamConverter::read_bits(&br, 32) == 0x49484452)
+ {
+ m_omx_image.nFrameWidth = CBitstreamConverter::read_bits(&br, 32);
+ m_omx_image.nFrameHeight = CBitstreamConverter::read_bits(&br, 32);
+ (void)CBitstreamConverter::read_bits(&br, 8); // bit depth
+ unsigned int coding_type = CBitstreamConverter::read_bits(&br, 8);
+ m_alpha = coding_type==4 || coding_type==6;
+ }
+ }
+
+ if(m_orientation > 8)
+ m_orientation = 0;
+
+ m_width = m_omx_image.nFrameWidth;
+ m_height = m_omx_image.nFrameHeight;
+
+ return m_omx_image.eCompressionFormat;
+}
+
+bool COMXImage::ReadFile(const CStdString& inputFile)
+{
+ if(!m_pFile.Open(inputFile, 0))
+ {
+ CLog::Log(LOGERROR, "%s::%s %s not found\n", CLASSNAME, __func__, inputFile.c_str());
+ return false;
+ }
+
+ if(m_image_buffer)
+ free(m_image_buffer);
+ m_image_buffer = NULL;
+
+ m_image_size = m_pFile.GetLength();
+
+ if(!m_image_size)
+ return false;
+
+ m_image_buffer = (uint8_t *)malloc(m_image_size);
+ if(!m_image_buffer)
+ return false;
+
+ m_pFile.Read(m_image_buffer, m_image_size);
+
+ GetCodingType();
+
+ if(m_width < 1 || m_height < 1)
+ return false;
+
+ // ensure not too big for hardware
+ while (m_width > 2048 || m_height > 2048)
+ m_width >>= 1, m_height >>= 1;
+ // ensure not too small
+ while (m_width <= 32 || m_height <= 32)
+ m_width <<= 1, m_height <<= 1;
+ // surely not going to happen?
+ if (m_width > 2048 || m_height > 2048)
+ m_width = 256, m_height = 256;
+
+ m_width = (m_width + 15) & ~15;
+ m_height = (m_height + 15) & ~15;
+
+ m_is_open = true;
+
+ return true;
+}
+
+bool COMXImage::Decode(unsigned width, unsigned height)
+{
+ std::string componentName = "";
+ bool m_firstFrame = true;
+ unsigned int demuxer_bytes = 0;
+ const uint8_t *demuxer_content = NULL;
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
+
+ OMX_INIT_STRUCTURE(m_decoded_format);
+
+ CSingleLock lock(g_OMXSection);
+
+ if(!m_image_buffer)
+ {
+ CLog::Log(LOGERROR, "%s::%s no input buffer\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ if(GetCompressionFormat() == OMX_IMAGE_CodingMax)
+ {
+ CLog::Log(LOGERROR, "%s::%s error unsupported image format\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ if(IsProgressive())
+ {
+ CLog::Log(LOGWARNING, "%s::%s progressive images not supported by decoder\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ if(!m_is_open)
+ {
+ CLog::Log(LOGERROR, "%s::%s error not opened\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ componentName = "OMX.broadcom.image_decode";
+ if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamImageInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s error m_omx_decoder.Initialize\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ componentName = "OMX.broadcom.resize";
+ if(!m_omx_resize.Initialize((const std::string)componentName, OMX_IndexParamImageInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ m_decoder_open = true;
+
+ m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_resize, m_omx_resize.GetInputPort());
+
+ omx_err = m_omx_tunnel_decode.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ if(width == 0 || height == 0)
+ {
+#ifndef STANDALONE
+ height = g_advancedSettings.m_imageRes;
+ if (g_advancedSettings.m_fanartRes > g_advancedSettings.m_imageRes)
+ { // a separate fanart resolution is specified - check if the image is exactly equal to this res
+ if (m_width == (unsigned int)g_advancedSettings.m_fanartRes * 16/9 &&
+ m_height == (unsigned int)g_advancedSettings.m_fanartRes)
+ { // special case for fanart res
+ height = g_advancedSettings.m_fanartRes;
+ }
+ }
+ width = height * 16/9;
+ if(!width || !height)
+ {
+ //width = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iScreenWidth;
+ //height = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iScreenHeight;
+ width = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iWidth;
+ height = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iHeight;
+ }
+#else
+ width = 2048;
+ height = 2048;
+#endif
+ }
+
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+ OMX_INIT_STRUCTURE(port_def);
+ port_def.nPortIndex = m_omx_decoder.GetInputPort();
+
+ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ port_def.format.image.eCompressionFormat = GetCompressionFormat();
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatUnused;
+ port_def.format.image.nFrameWidth = 0;
+ port_def.format.image.nFrameHeight = 0;
+ port_def.format.image.nStride = 0;
+ port_def.format.image.nSliceHeight = 0;
+ port_def.format.image.bFlagErrorConcealment = OMX_FALSE;
+
+ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_decoder.AllocInputBuffers();
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.AllocInputBuffers result(0x%x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ OMX_INIT_STRUCTURE(port_def);
+ port_def.nPortIndex = m_omx_resize.GetOutputPort();
+
+ omx_err = m_omx_resize.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+
+ port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
+ port_def.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888;
+ if((((width + 15)&~15) > width) || (((height + 15)&~15) > height))
+ {
+ port_def.format.image.nFrameWidth = (width + 15)&~15;
+ port_def.format.image.nFrameHeight = (height + 15)&~15;
+ }
+ else
+ {
+ port_def.format.image.nFrameWidth = width;
+ port_def.format.image.nFrameHeight = height;
+ }
+ port_def.format.image.nStride = 0;
+ port_def.format.image.nSliceHeight = 0;
+ port_def.format.image.bFlagErrorConcealment = OMX_FALSE;
+
+ omx_err = m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_resize.AllocOutputBuffers();
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.AllocOutputBuffers result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_resize.SetStateForComponent(OMX_StateExecuting);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ demuxer_bytes = GetImageSize();
+ demuxer_content = GetImageBuffer();
+ if(!demuxer_bytes || !demuxer_content)
+ return false;
+
+ m_firstFrame = true;
+
+ while(demuxer_bytes > 0)
+ {
+ omx_buffer = m_omx_decoder.GetInputBuffer(1000);
+ if(omx_buffer == NULL)
+ return false;
+
+ omx_buffer->nOffset = 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);
+
+ demuxer_content += omx_buffer->nFilledLen;
+ demuxer_bytes -= omx_buffer->nFilledLen;
+
+ if(demuxer_bytes == 0)
+ omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ 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);
+ break;
+ }
+ if(m_firstFrame)
+ {
+ m_firstFrame = false;
+
+ m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), NULL);
+ m_omx_resize.DisablePort(m_omx_resize.GetInputPort(), NULL);
+ //m_omx_resize.DisablePort(m_omx_resize.GetOutputPort(), NULL);
+
+ OMX_PARAM_PORTDEFINITIONTYPE port_image;
+ OMX_INIT_STRUCTURE(port_image);
+
+ port_image.nPortIndex = m_omx_decoder.GetOutputPort();
+ m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_image);
+
+ port_image.nPortIndex = m_omx_resize.GetInputPort();
+ m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_image);
+
+ m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), NULL);
+ //m_omx_decoder.EnablePort(m_omx_decoder.GetInputPort(), NULL);
+ omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged);
+ if(omx_err == OMX_ErrorStreamCorrupt)
+ {
+ CLog::Log(LOGERROR, "%s::%s image not unsupported\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ m_omx_resize.EnablePort(m_omx_resize.GetInputPort(), NULL);
+ omx_err = m_omx_resize.WaitForEvent(OMX_EventPortSettingsChanged);
+ if(omx_err == OMX_ErrorStreamCorrupt)
+ {
+ CLog::Log(LOGERROR, "%s::%s image not unsupported\n", CLASSNAME, __func__);
+ return false;
+ }
+ }
+ }
+
+ /*
+ omx_err = m_omx_resize.EnablePort(m_omx_resize.GetOutputPort(), NULL);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.EnablePort result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+ */
+
+ omx_err = m_omx_decoder.WaitForEvent(OMX_EventBufferFlag, 1000);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.WaitForEvent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ m_decoded_buffer = m_omx_resize.GetOutputBuffer(2000);
+
+ if(!m_decoded_buffer)
+ {
+ CLog::Log(LOGERROR, "%s::%s no output buffer\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ omx_err = m_omx_resize.FillThisBuffer(m_decoded_buffer);
+
+ omx_err = m_omx_resize.WaitForEvent(OMX_EventBufferFlag, 1000);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize WaitForEvent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ m_decoded_format.nPortIndex = m_omx_resize.GetOutputPort();
+ omx_err = m_omx_resize.GetParameter(OMX_IndexParamPortDefinition, &m_decoded_format);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ m_omx_tunnel_decode.Deestablish();
+
+ SwapBlueRed(m_decoded_buffer->pBuffer, GetDecodedHeight(), GetDecodedWidth() * 4);
+
+ return true;
+}
+
+bool COMXImage::Encode(unsigned char *buffer, int size, unsigned width, unsigned height)
+{
+ std::string componentName = "";
+ unsigned int demuxer_bytes = 0;
+ const uint8_t *demuxer_content = NULL;
+ uint8_t *internalBuffer = NULL;
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
+ OMX_INIT_STRUCTURE(m_encoded_format);
+
+ CSingleLock lock(g_OMXSection);
+
+ if (!buffer || !size)
+ {
+ CLog::Log(LOGERROR, "%s::%s error no buffer\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ componentName = "OMX.broadcom.image_encode";
+ if(!m_omx_encoder.Initialize((const std::string)componentName, OMX_IndexParamImageInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s error m_omx_encoder.Initialize\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ m_encoder_open = true;
+
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+ OMX_INIT_STRUCTURE(port_def);
+ port_def.nPortIndex = m_omx_encoder.GetInputPort();
+
+ omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
+ port_def.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888;
+ port_def.format.image.nFrameWidth = width;
+ port_def.format.image.nFrameHeight = height;
+ port_def.format.image.nStride = width * 4;
+ port_def.format.image.nSliceHeight = height;
+ port_def.format.image.bFlagErrorConcealment = OMX_FALSE;
+
+ omx_err = m_omx_encoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ OMX_INIT_STRUCTURE(port_def);
+ port_def.nPortIndex = m_omx_encoder.GetOutputPort();
+
+ omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingJPEG;
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatUnused;
+ port_def.format.image.nFrameWidth = width;
+ port_def.format.image.nFrameHeight = height;
+ port_def.format.image.nStride = 0;
+ port_def.format.image.nSliceHeight = 0;
+ port_def.format.image.bFlagErrorConcealment = OMX_FALSE;
+
+ omx_err = m_omx_encoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ OMX_IMAGE_PARAM_QFACTORTYPE qfactor;
+ OMX_INIT_STRUCTURE(qfactor);
+ qfactor.nPortIndex = m_omx_encoder.GetOutputPort();
+ qfactor.nQFactor = 16;
+
+ omx_err = m_omx_encoder.SetParameter(OMX_IndexParamQFactor, &qfactor);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter OMX_IndexParamQFactor result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_encoder.AllocInputBuffers();
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.AllocInputBuffers result(0x%x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_encoder.AllocOutputBuffers();
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.AllocOutputBuffers result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_encoder.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ internalBuffer = (uint8_t *)malloc(size);
+ memcpy(internalBuffer, buffer, size);
+ demuxer_bytes = size;
+ demuxer_content = internalBuffer;
+ SwapBlueRed(internalBuffer, height, width * 4);
+
+ if(!demuxer_bytes || !demuxer_content)
+ return false;
+
+ while(demuxer_bytes > 0)
+ {
+ omx_buffer = m_omx_encoder.GetInputBuffer(1000);
+ if(omx_buffer == NULL)
+ {
+ if(internalBuffer) free(internalBuffer);
+ return false;
+ }
+
+ omx_buffer->nOffset = 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);
+
+ demuxer_content += omx_buffer->nFilledLen;
+ demuxer_bytes -= omx_buffer->nFilledLen;
+
+ if(demuxer_bytes == 0)
+ omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ omx_err = m_omx_encoder.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);
+ break;
+ }
+ }
+
+ if(internalBuffer) free(internalBuffer);
+
+ m_encoded_buffer = m_omx_encoder.GetOutputBuffer(2000);
+
+ if(!m_encoded_buffer)
+ {
+ CLog::Log(LOGERROR, "%s::%s no output buffer\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ omx_err = m_omx_encoder.FillThisBuffer(m_encoded_buffer);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ omx_err = m_omx_encoder.WaitForEvent(OMX_EventBufferFlag, 1000);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder WaitForEvent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ m_encoded_format.nPortIndex = m_omx_encoder.GetOutputPort();
+ omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &m_encoded_format);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ return true;
+}
+
+unsigned char *COMXImage::GetDecodedData()
+{
+ if(!m_decoded_buffer)
+ return NULL;
+
+ return (unsigned char *)m_decoded_buffer->pBuffer;
+}
+
+unsigned int COMXImage::GetDecodedSize()
+{
+ if(!m_decoded_buffer)
+ return 0;
+ return (unsigned int)m_decoded_buffer->nFilledLen;
+}
+
+unsigned char *COMXImage::GetEncodedData()
+{
+ if(!m_encoded_buffer)
+ return NULL;
+
+ return (unsigned char *)m_encoded_buffer->pBuffer;
+}
+
+unsigned int COMXImage::GetEncodedSize()
+{
+ if(!m_encoded_buffer)
+ return 0;
+ return (unsigned int)m_encoded_buffer->nFilledLen;
+}
+
+bool COMXImage::SwapBlueRed(unsigned char *pixels, unsigned int height, unsigned int pitch,
+ unsigned int elements, unsigned int offset)
+{
+ if (!pixels) return false;
+ unsigned char *dst = pixels;
+ for (unsigned int y = 0; y < height; y++)
+ {
+ dst = pixels + (y * pitch);
+ for (unsigned int x = 0; x < pitch; x+=elements)
+ std::swap(dst[x+offset], dst[x+2+offset]);
+ }
+ return true;
+}
+
+bool COMXImage::CreateThumbnail(const CStdString& sourceFile, const CStdString& destFile,
+ int minx, int miny, bool rotateExif)
+{
+ if (!ReadFile(sourceFile))
+ return false;
+
+ return CreateThumbnailFromMemory(m_image_buffer, m_image_size, destFile, minx, miny);
+}
+
+bool COMXImage::CreateThumbnailFromMemory(unsigned char* buffer, unsigned int bufSize, const CStdString& destFile,
+ unsigned int minx, unsigned int miny)
+{
+ if(!bufSize || !buffer)
+ return false;
+
+ if(!m_is_open)
+ {
+ m_image_size = bufSize;
+ m_image_buffer = (uint8_t *)malloc(m_image_size);
+ if(!m_image_buffer)
+ return false;
+
+ memcpy(m_image_buffer, buffer, m_image_size);
+
+ GetCodingType();
+
+ // ensure not too big for hardware
+ while (m_width > 2048 || m_height > 2048)
+ m_width >>= 1, m_height >>= 1;
+ // ensure not too small
+ while (m_width <= 32 || m_height <= 32)
+ m_width <<= 1, m_height <<= 1;
+ // surely not going to happen?
+ if (m_width > 2048 || m_height > 2048)
+ m_width = 256, m_height = 256;
+
+ m_width = (m_width + 15) & ~15;
+ m_height = (m_height + 15) & ~15;
+
+ m_is_open = true;
+ }
+
+ if(!Decode(minx, miny))
+ return false;
+
+ return CreateThumbnailFromSurface(GetDecodedData(), GetDecodedWidth(), GetDecodedHeight(),
+ XB_FMT_A8R8G8B8, GetDecodedWidth() * 4, destFile);
+}
+
+bool COMXImage::CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height,
+ unsigned int format, unsigned int pitch, const CStdString& destFile)
+{
+ if(format != XB_FMT_A8R8G8B8 || !buffer)
+ return false;
+
+ // the omx encoder needs alligned sizes
+ if(width%16 || height%16)
+ {
+ unsigned int new_width = (width + 15)&~15;
+ unsigned int new_height = (height + 15)&~15;
+ unsigned int new_pitch = new_width * 4;
+
+ unsigned int size = new_height * new_pitch;
+ unsigned char *dstBuffer = (unsigned char *)malloc(size);
+ unsigned char *dst = dstBuffer;
+ unsigned char *src = buffer;
+
+ if(!dstBuffer)
+ return false;
+
+ memset(dst, 0x0, size);
+
+ for(unsigned int y = 0; y < height; y++)
+ {
+ memcpy(dst, src, pitch);
+ src += pitch;
+ dst += new_pitch;
+ }
+ if(!Encode(dstBuffer, size, new_width, new_height))
+ {
+ free(dstBuffer);
+ return false;
+ }
+ free(dstBuffer);
+ }
+ else
+ {
+ if(!Encode(buffer, height * pitch, width, height))
+ return false;
+ }
+
+ XFILE::CFile file;
+ if (file.OpenForWrite(destFile, true))
+ {
+ CLog::Log(LOGDEBUG, "%s::%s : %s width %d height %d\n", CLASSNAME, __func__, destFile.c_str(), width, height);
+
+ file.Write(GetEncodedData(), GetEncodedSize());
+ file.Close();
+ return true;
+ }
+
+ return false;
+}
diff --git a/xbmc/cores/omxplayer/OMXImage.h b/xbmc/cores/omxplayer/OMXImage.h
new file mode 100644
index 0000000000..f1e4672ce3
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXImage.h
@@ -0,0 +1,107 @@
+#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 "OMXCore.h"
+
+#include <IL/OMX_Video.h>
+
+#include "OMXClock.h"
+#if defined(STANDALONE)
+#define XB_FMT_A8R8G8B8 1
+#include "File.h"
+#else
+#include "filesystem/File.h"
+#include "guilib/XBTF.h"
+#endif
+
+using namespace XFILE;
+using namespace std;
+
+class COMXImage
+{
+public:
+ COMXImage();
+ virtual ~COMXImage();
+
+ // Required overrides
+ void Close(void);
+ bool ReadFile(const CStdString& inputFile);
+ bool IsProgressive() { return m_progressive; };
+ bool IsAlpha() { return m_alpha; };
+ int GetOrientation() { return m_orientation; };
+ unsigned int GetOriginalWidth() { return m_omx_image.nFrameWidth; };
+ unsigned int GetOriginalHeight() { return m_omx_image.nFrameHeight; };
+ unsigned int GetWidth() { return m_width; };
+ unsigned int GetHeight() { return m_height; };
+ OMX_IMAGE_CODINGTYPE GetCodingType();
+ const uint8_t *GetImageBuffer() { return (const uint8_t *)m_image_buffer; };
+ unsigned long GetImageSize() { return m_image_size; };
+ OMX_IMAGE_CODINGTYPE GetCompressionFormat() { return m_omx_image.eCompressionFormat; };
+ bool Decode(unsigned width, unsigned height);
+ bool Encode(unsigned char *buffer, int size, unsigned width, unsigned height);
+ int GetDecodedWidth() { return (int)m_decoded_format.format.image.nFrameWidth; };
+ int GetDecodedHeight() { return (int)m_decoded_format.format.image.nFrameHeight; };
+ int GetDecodedStride() { return (int)m_decoded_format.format.image.nStride; };
+ unsigned char *GetDecodedData();
+ unsigned int GetDecodedSize();
+ int GetEncodedWidth() { return (int)m_encoded_format.format.image.nFrameWidth; };
+ int GetEncodedHeight() { return (int)m_encoded_format.format.image.nFrameHeight; };
+ int GetEncodedStride() { return (int)m_encoded_format.format.image.nStride; };
+ unsigned char *GetEncodedData();
+ unsigned int GetEncodedSize();
+ bool SwapBlueRed(unsigned char *pixels, unsigned int height, unsigned int pitch,
+ unsigned int elements = 4, unsigned int offset=0);
+ bool CreateThumbnail(const CStdString& sourceFile, const CStdString& destFile,
+ int minx, int miny, bool rotateExif);
+ bool CreateThumbnailFromMemory(unsigned char* buffer, unsigned int bufSize,
+ const CStdString& destFile, unsigned int minx, unsigned int miny);
+ bool CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height,
+ unsigned int format, unsigned int pitch, const CStdString& destFile);
+protected:
+ uint8_t *m_image_buffer;
+ bool m_is_open;
+ unsigned long m_image_size;
+ unsigned int m_width;
+ unsigned int m_height;
+ bool m_progressive;
+ bool m_alpha;
+ int m_orientation;
+ XFILE::CFile m_pFile;
+ OMX_IMAGE_PORTDEFINITIONTYPE m_omx_image;
+
+ // Components
+ COMXCoreComponent m_omx_decoder;
+ COMXCoreComponent m_omx_encoder;
+ COMXCoreComponent m_omx_resize;
+ COMXCoreTunel m_omx_tunnel_decode;
+ OMX_BUFFERHEADERTYPE *m_decoded_buffer;
+ OMX_BUFFERHEADERTYPE *m_encoded_buffer;
+ OMX_PARAM_PORTDEFINITIONTYPE m_decoded_format;
+ OMX_PARAM_PORTDEFINITIONTYPE m_encoded_format;
+
+ bool m_decoder_open;
+ bool m_encoder_open;
+};
+
+#endif
diff --git a/xbmc/cores/omxplayer/OMXPlayer.cpp b/xbmc/cores/omxplayer/OMXPlayer.cpp
new file mode 100644
index 0000000000..f0dc764884
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXPlayer.cpp
@@ -0,0 +1,3816 @@
+/*
+ * Copyright (C) 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
+ *
+ */
+
+#include "system.h"
+
+#if defined (HAS_OMXPLAYER)
+#include "OMXPlayerAudio.h"
+#include "OMXPlayerVideo.h"
+#include "OMXPlayer.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "GUIInfoManager.h"
+#include "filesystem/File.h"
+#include "cores/VideoRenderers/RenderManager.h"
+#include "cores/VideoRenderers/RenderFlags.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/GUIWindowManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "threads/SingleLock.h"
+#include "windowing/WindowingFactory.h"
+#include "utils/log.h"
+#include "utils/MathUtils.h"
+#include "utils/TimeUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/XMLUtils.h"
+#include "utils/Variant.h"
+#include "xbmc/playlists/PlayListM3U.h"
+
+#include "FileItem.h"
+#include "filesystem/File.h"
+#include "utils/BitstreamStats.h"
+
+#include "utils/LangCodeExpander.h"
+#include "guilib/LocalizeStrings.h"
+#include "utils/StreamDetails.h"
+
+#include "DVDFileInfo.h"
+
+#include <sstream>
+#include <iomanip>
+
+#include "BitstreamConverter.h"
+#include "DVDInputStreams/DVDInputStreamNavigator.h"
+#include "DVDInputStreams/DVDInputStreamTV.h"
+#include "Util.h"
+#include "storage/MediaManager.h"
+#include "GUIUserMessages.h"
+#include "utils/StreamUtils.h"
+
+#include "DVDDemuxers/DVDDemuxUtils.h"
+#include "DVDDemuxers/DVDDemuxVobsub.h"
+#include "DVDDemuxers/DVDFactoryDemuxer.h"
+#include "DVDDemuxers/DVDDemuxFFmpeg.h"
+
+#include "LangInfo.h"
+
+#include "utils/JobManager.h"
+#include "cores/AudioEngine/AEFactory.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "xbmc/ThumbLoader.h"
+
+using namespace XFILE;
+
+// ****************************************************************
+void COMXSelectionStreams::Clear(StreamType type, StreamSource source)
+{
+ CSingleLock lock(m_section);
+ for(int i=m_Streams.size()-1;i>=0;i--)
+ {
+ if(type && m_Streams[i].type != type)
+ continue;
+
+ if(source && m_Streams[i].source != source)
+ continue;
+
+ m_Streams.erase(m_Streams.begin() + i);
+ }
+}
+
+void COMXPlayer::GetAudioStreamLanguage(int iStream, CStdString &strLanguage)
+{
+ strLanguage = "";
+ OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
+ if(s.language.length() > 0)
+ strLanguage = s.language;
+}
+
+OMXSelectionStream& COMXSelectionStreams::Get(StreamType type, int index)
+{
+ CSingleLock lock(m_section);
+ int count = -1;
+ for(int i=0;i<(int)m_Streams.size();i++)
+ {
+ if(m_Streams[i].type != type)
+ continue;
+ count++;
+ if(count == index)
+ return m_Streams[i];
+ }
+ CLog::Log(LOGERROR, "%s - failed to get stream", __FUNCTION__);
+ return m_invalid;
+}
+
+bool COMXSelectionStreams::Get(StreamType type, CDemuxStream::EFlags flag, OMXSelectionStream& out)
+{
+ CSingleLock lock(m_section);
+ for(int i=0;i<(int)m_Streams.size();i++)
+ {
+ if(m_Streams[i].type != type)
+ continue;
+ if((m_Streams[i].flags & flag) != flag)
+ continue;
+ out = m_Streams[i];
+ return true;
+ }
+ return false;
+}
+
+std::vector<OMXSelectionStream> COMXSelectionStreams::Get(StreamType type)
+{
+ std::vector<OMXSelectionStream> streams;
+ int count = Count(type);
+ for(int index = 0; index < count; ++index){
+ streams.push_back(Get(type, index));
+ }
+ return streams;
+}
+
+#define PREDICATE_RETURN(lh, rh) \
+ do { \
+ if((lh) != (rh)) \
+ return (lh) > (rh); \
+ } while(0)
+
+static bool PredicateAudioPriority(const OMXSelectionStream& lh, const OMXSelectionStream& rh)
+{
+ PREDICATE_RETURN(lh.type_index == g_settings.m_currentVideoSettings.m_AudioStream
+ , rh.type_index == g_settings.m_currentVideoSettings.m_AudioStream);
+
+ if(!g_guiSettings.GetString("locale.audiolanguage").Equals("original"))
+ {
+ CStdString audio_language = g_langInfo.GetAudioLanguage();
+ PREDICATE_RETURN(audio_language.Equals(lh.language.c_str())
+ , audio_language.Equals(rh.language.c_str()));
+ }
+
+ PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
+ , rh.flags & CDemuxStream::FLAG_DEFAULT);
+
+ PREDICATE_RETURN(lh.channels
+ , rh.channels);
+
+ PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec)
+ , StreamUtils::GetCodecPriority(rh.codec));
+ return false;
+}
+
+static bool PredicateSubtitlePriority(const OMXSelectionStream& lh, const OMXSelectionStream& rh)
+{
+ if(!g_settings.m_currentVideoSettings.m_SubtitleOn)
+ {
+ PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_FORCED
+ , rh.flags & CDemuxStream::FLAG_FORCED);
+ }
+
+ PREDICATE_RETURN(lh.type_index == g_settings.m_currentVideoSettings.m_SubtitleStream
+ , rh.type_index == g_settings.m_currentVideoSettings.m_SubtitleStream);
+
+ CStdString subtitle_language = g_langInfo.GetSubtitleLanguage();
+ if(!g_guiSettings.GetString("locale.subtitlelanguage").Equals("original"))
+ {
+ PREDICATE_RETURN((lh.source == STREAM_SOURCE_DEMUX_SUB || lh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(lh.language.c_str())
+ , (rh.source == STREAM_SOURCE_DEMUX_SUB || rh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(rh.language.c_str()));
+ }
+
+ PREDICATE_RETURN(lh.source == STREAM_SOURCE_DEMUX_SUB
+ , rh.source == STREAM_SOURCE_DEMUX_SUB);
+
+ PREDICATE_RETURN(lh.source == STREAM_SOURCE_TEXT
+ , rh.source == STREAM_SOURCE_TEXT);
+
+ if(!g_guiSettings.GetString("locale.subtitlelanguage").Equals("original"))
+ {
+ PREDICATE_RETURN(subtitle_language.Equals(lh.language.c_str())
+ , subtitle_language.Equals(rh.language.c_str()));
+ }
+
+ PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
+ , rh.flags & CDemuxStream::FLAG_DEFAULT);
+
+ return false;
+}
+
+static bool PredicateVideoPriority(const OMXSelectionStream& lh, const OMXSelectionStream& rh)
+{
+ PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
+ , rh.flags & CDemuxStream::FLAG_DEFAULT);
+ return false;
+}
+
+int COMXSelectionStreams::IndexOf(StreamType type, int source, int id) const
+{
+ CSingleLock lock(m_section);
+ int count = -1;
+ for(int i=0;i<(int)m_Streams.size();i++)
+ {
+ if(type && m_Streams[i].type != type)
+ continue;
+ count++;
+ if(source && m_Streams[i].source != source)
+ continue;
+ if(id < 0)
+ continue;
+ if(m_Streams[i].id == id)
+ return count;
+ }
+ if(id < 0)
+ return count;
+ else
+ return -1;
+}
+
+int COMXSelectionStreams::IndexOf(StreamType type, COMXPlayer& p) const
+{
+ if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ int id = -1;
+ if(type == STREAM_AUDIO)
+ id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream();
+ else if(type == STREAM_VIDEO)
+ id = p.m_CurrentVideo.id;
+ else if(type == STREAM_SUBTITLE)
+ id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream();
+
+ return IndexOf(type, STREAM_SOURCE_NAV, id);
+ }
+
+ if(type == STREAM_AUDIO)
+ return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id);
+ else if(type == STREAM_VIDEO)
+ return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id);
+ else if(type == STREAM_SUBTITLE)
+ return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id);
+ else if(type == STREAM_TELETEXT)
+ return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id);
+
+ return -1;
+}
+
+int COMXSelectionStreams::Source(StreamSource source, std::string filename)
+{
+ CSingleLock lock(m_section);
+ int index = source - 1;
+ for(int i=0;i<(int)m_Streams.size();i++)
+ {
+ OMXSelectionStream &s = m_Streams[i];
+ if(STREAM_SOURCE_MASK(s.source) != source)
+ continue;
+ // if it already exists, return same
+ if(s.filename == filename)
+ return s.source;
+ if(index < s.source)
+ index = s.source;
+ }
+ // return next index
+ return index + 1;
+}
+
+void COMXSelectionStreams::Update(OMXSelectionStream& s)
+{
+ CSingleLock lock(m_section);
+ int index = IndexOf(s.type, s.source, s.id);
+ if(index >= 0)
+ {
+ OMXSelectionStream& o = Get(s.type, index);
+ s.type_index = o.type_index;
+ o = s;
+ }
+ else
+ {
+ s.type_index = Count(s.type);
+ m_Streams.push_back(s);
+ }
+}
+
+void COMXSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer)
+{
+ if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input;
+ string filename = nav->GetFileName();
+ int source = Source(STREAM_SOURCE_NAV, filename);
+
+ int count;
+ count = nav->GetAudioStreamCount();
+ for(int i=0;i<count;i++)
+ {
+ OMXSelectionStream s;
+ s.source = source;
+ s.type = STREAM_AUDIO;
+ s.id = i;
+ s.name = nav->GetAudioStreamLanguage(i);
+ s.flags = CDemuxStream::FLAG_NONE;
+ s.filename = filename;
+ s.channels = 0;
+ Update(s);
+ }
+
+ count = nav->GetSubTitleStreamCount();
+ for(int i=0;i<count;i++)
+ {
+ OMXSelectionStream s;
+ s.source = source;
+ s.type = STREAM_SUBTITLE;
+ s.id = i;
+ s.name = nav->GetSubtitleStreamLanguage(i);
+ s.flags = CDemuxStream::FLAG_NONE;
+ s.filename = filename;
+ s.channels = 0;
+ Update(s);
+ }
+ }
+ else if(demuxer)
+ {
+ string filename = demuxer->GetFileName();
+ int count = demuxer->GetNrOfStreams();
+ int source;
+ if(input) /* hack to know this is sub decoder */
+ source = Source(STREAM_SOURCE_DEMUX, filename);
+ else
+ source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
+
+
+ for(int i=0;i<count;i++)
+ {
+ CDemuxStream* stream = demuxer->GetStream(i);
+ /* make sure stream is marked with right source */
+ stream->source = source;
+
+ OMXSelectionStream s;
+ s.source = source;
+ s.type = stream->type;
+ s.id = stream->iId;
+ s.language = stream->language;
+ s.flags = stream->flags;
+ s.filename = demuxer->GetFileName();
+ stream->GetStreamName(s.name);
+ CStdString codec;
+ demuxer->GetStreamCodecName(stream->iId, codec);
+ s.codec = codec;
+ s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
+ if(stream->type == STREAM_AUDIO)
+ {
+ std::string type;
+ ((CDemuxStreamAudio*)stream)->GetStreamType(type);
+ if(type.length() > 0)
+ {
+ if(s.name.length() > 0)
+ s.name += " - ";
+ s.name += type;
+ }
+ s.channels = ((CDemuxStreamAudio*)stream)->iChannels;
+ }
+ Update(s);
+ }
+ }
+}
+
+// ****************************************************************
+COMXPlayer::COMXPlayer(IPlayerCallback &callback)
+ : IPlayer(callback),
+ CThread("COMXPlayer"),
+ m_ready(true),
+ m_CurrentAudio(STREAM_AUDIO, DVDPLAYER_AUDIO),
+ m_CurrentVideo(STREAM_VIDEO, DVDPLAYER_VIDEO),
+ m_CurrentSubtitle(STREAM_SUBTITLE, DVDPLAYER_SUBTITLE),
+ m_CurrentTeletext(STREAM_TELETEXT, DVDPLAYER_TELETEXT),
+ m_player_video(&m_av_clock, &m_overlayContainer, m_messenger),
+ m_player_audio(&m_av_clock, m_messenger),
+ m_player_subtitle(&m_overlayContainer),
+ m_messenger("player")
+{
+ m_bAbortRequest = false;
+ m_pDemuxer = NULL;
+ m_pSubtitleDemuxer = NULL;
+ m_pInputStream = NULL;
+ m_UpdateApplication = 0;
+ m_caching = CACHESTATE_DONE;
+ m_playSpeed = DVD_PLAYSPEED_NORMAL;
+
+ m_State.Clear();
+ m_dvd.Clear();
+}
+
+COMXPlayer::~COMXPlayer()
+{
+ CloseFile();
+}
+
+bool COMXPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options)
+{
+ try
+ {
+ CLog::Log(LOGNOTICE, "COMXPlayer: Opening: %s", file.GetPath().c_str());
+ // if playing a file close it first
+ // this has to be changed so we won't have to close it.
+ if(IsRunning())
+ CloseFile();
+
+ m_playSpeed = DVD_PLAYSPEED_NORMAL;
+ SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
+
+ m_PlayerOptions = options;
+ m_bAbortRequest = false;
+
+ m_UpdateApplication = 0;
+ m_offset_pts = 0;
+ m_current_volume = 0;
+ m_change_volume = true;
+
+ m_item = file;
+ m_mimetype = file.GetMimeType();
+ m_filename = file.GetPath();
+
+ m_State.Clear();
+
+ m_ready.Reset();
+
+ g_renderManager.PreInit();
+
+ Create();
+
+ if(!m_ready.WaitMSec(100))
+ {
+ CGUIDialogBusy* dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
+ dialog->Show();
+ while(!m_ready.WaitMSec(1))
+ g_windowManager.ProcessRenderLoop(false);
+ dialog->Close();
+ }
+
+ // Playback might have been stopped due to some error
+ if (m_bStop || m_bAbortRequest)
+ return false;
+
+ return true;
+ }
+ catch(...)
+ {
+ CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
+ return false;
+ }
+}
+
+bool COMXPlayer::CloseFile()
+{
+ CLog::Log(LOGDEBUG, "COMXPlayer::CloseFile");
+
+ // unpause the player
+ SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
+
+ // set the abort request so that other threads can finish up
+ m_bAbortRequest = true;
+
+ // tell demuxer to abort
+ if(m_pDemuxer)
+ m_pDemuxer->Abort();
+
+ if(m_pSubtitleDemuxer)
+ m_pSubtitleDemuxer->Abort();
+
+ CLog::Log(LOGDEBUG, "COMXPlayer: waiting for threads to exit");
+ // wait for the main thread to finish up
+ // since this main thread cleans up all other resources and threads
+ // we are done after the StopThread call
+ if(IsRunning())
+ StopThread();
+
+ CLog::Log(LOGDEBUG, "COMXPlayer: finished waiting");
+
+ m_Edl.Clear();
+ m_EdlAutoSkipMarkers.Clear();
+
+ g_renderManager.UnInit();
+ return true;
+}
+
+bool COMXPlayer::IsPlaying() const
+{
+ return !m_bStop;
+}
+
+void COMXPlayer::OnStartup()
+{
+ m_CurrentVideo.Clear();
+ m_CurrentAudio.Clear();
+ m_CurrentSubtitle.Clear();
+
+ m_messenger.Init();
+
+ CUtil::ClearTempFonts();
+}
+
+bool COMXPlayer::OpenInputStream()
+{
+ if(m_pInputStream)
+ SAFE_DELETE(m_pInputStream);
+
+ CLog::Log(LOGNOTICE, "Creating InputStream");
+
+ // correct the filename if needed
+ CStdString filename(m_filename);
+ if (filename.Find("dvd://") == 0
+ || filename.CompareNoCase("iso9660://video_ts/video_ts.ifo") == 0)
+ {
+ m_filename = g_mediaManager.TranslateDevicePath("");
+ }
+retry:
+ // before creating the input stream, if this is an HLS playlist then get the
+ // most appropriate bitrate based on our network settings
+ if (filename.Left(7) == "http://" && filename.Right(5) == ".m3u8")
+ {
+ // get the available bandwidth (as per user settings)
+ int maxrate = g_guiSettings.GetInt("network.bandwidth");
+ if(maxrate <= 0)
+ maxrate = INT_MAX;
+
+ // determine the most appropriate stream
+ m_filename = PLAYLIST::CPlayListM3U::GetBestBandwidthStream(m_filename, (size_t)maxrate);
+ }
+
+ m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_mimetype);
+ if(m_pInputStream == NULL)
+ {
+ CLog::Log(LOGERROR, "COMXPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str());
+ return false;
+ }
+ else
+ m_pInputStream->SetFileItem(m_item);
+
+ if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype))
+ {
+ if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CLog::Log(LOGERROR, "COMXPlayer::OpenInputStream - failed to open [%s] as DVD ISO, trying Bluray", m_filename.c_str());
+ m_mimetype = "bluray/iso";
+ filename = m_filename;
+ filename = filename + "/BDMV/index.bdmv";
+ int title = (int)m_item.GetProperty("BlurayStartingTitle").asInteger();
+ if( title )
+ filename.AppendFormat("?title=%d",title);
+
+ m_filename = filename;
+ goto retry;
+ }
+ CLog::Log(LOGERROR, "COMXPlayer::OpenInputStream - error opening [%s]", m_filename.c_str());
+ return false;
+ }
+
+ if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
+ || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) )
+ {
+ CLog::Log(LOGINFO, "COMXPlayer::OpenInputStream - DVD/BD not supported");
+ return false;
+ }
+
+ // find any available external subtitles for non dvd files
+ if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
+ && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
+ && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
+ {
+ // find any available external subtitles
+ std::vector<CStdString> filenames;
+ CUtil::ScanForExternalSubtitles( m_filename, filenames );
+
+ // find any upnp subtitles
+ CStdString key("upnp:subtitle:1");
+ for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s))
+ filenames.push_back(m_item.GetProperty(key).asString());
+
+ for(unsigned int i=0;i<filenames.size();i++)
+ {
+ // if vobsub subtitle:
+ if (URIUtils::GetExtension(filenames[i]) == ".idx")
+ {
+ CStdString strSubFile;
+ if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
+ AddSubtitleFile(filenames[i], strSubFile);
+ }
+ else
+ {
+ if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
+ {
+ AddSubtitleFile(filenames[i]);
+ }
+ }
+ } // end loop over all subtitle files
+
+ g_settings.m_currentVideoSettings.m_SubtitleCached = true;
+ }
+
+ SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay);
+ SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay);
+ m_av_clock.Reset();
+ m_av_clock.OMXReset();
+ m_dvd.Clear();
+
+ return true;
+}
+
+bool COMXPlayer::OpenDemuxStream()
+{
+ if(m_pDemuxer)
+ SAFE_DELETE(m_pDemuxer);
+
+ CLog::Log(LOGNOTICE, "Creating Demuxer");
+
+ try
+ {
+ int attempts = 10;
+ while(!m_bStop && attempts-- > 0)
+ {
+ m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
+ if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
+ {
+ CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
+ continue;
+ }
+ break;
+ }
+
+ if(!m_pDemuxer)
+ {
+ CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
+ return false;
+ }
+
+ }
+ catch(...)
+ {
+ CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
+ return false;
+ }
+
+ m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
+ m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
+ m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
+
+ int64_t len = m_pInputStream->GetLength();
+ int64_t tim = m_pDemuxer->GetStreamLength();
+ if(len > 0 && tim > 0)
+ m_pInputStream->SetReadRate(len * 1000 / tim);
+
+ return true;
+}
+
+void COMXPlayer::OpenDefaultStreams()
+{
+ //int count;
+ OMXSelectionStreams streams;
+ bool valid;
+
+ // open video stream
+ streams = m_SelectionStreams.Get(STREAM_VIDEO, PredicateVideoPriority);
+ valid = false;
+ for(OMXSelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
+ {
+ if(OpenVideoStream(it->id, it->source))
+ valid = true;;
+ }
+ if(!valid)
+ CloseVideoStream(true);
+
+ // open audio stream
+ if(m_PlayerOptions.video_only)
+ streams.clear();
+ else
+ streams = m_SelectionStreams.Get(STREAM_AUDIO, PredicateAudioPriority);
+ valid = false;
+
+ for(OMXSelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
+ {
+ if(OpenAudioStream(it->id, it->source))
+ valid = true;
+ }
+ if(!valid)
+ CloseAudioStream(true);
+
+ // enable subtitles
+ m_player_video.EnableSubtitle(g_settings.m_currentVideoSettings.m_SubtitleOn);
+
+ // open subtitle stream
+ streams = m_SelectionStreams.Get(STREAM_SUBTITLE, PredicateSubtitlePriority);
+ valid = false;
+ for(OMXSelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
+ {
+ if(OpenSubtitleStream(it->id, it->source))
+ {
+ valid = true;
+ if(it->flags & CDemuxStream::FLAG_FORCED)
+ m_player_video.EnableSubtitle(true);
+ }
+ }
+ if(!valid)
+ CloseSubtitleStream(true);
+
+ // open teletext stream
+ /*
+ streams = m_SelectionStreams.Get(STREAM_TELETEXT);
+ valid = false;
+ for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
+ {
+ if(OpenTeletextStream(it->id, it->source))
+ valid = true;
+ }
+ if(!valid)
+ CloseTeletextStream(true);
+ */
+
+ m_av_clock.OMXStop();
+ m_av_clock.OMXReset();
+}
+
+bool COMXPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
+{
+
+ // check if we should read from subtitle demuxer
+ if(m_player_subtitle.AcceptsData() && m_pSubtitleDemuxer)
+ {
+ if(m_pSubtitleDemuxer)
+ packet = m_pSubtitleDemuxer->Read();
+
+ if(packet)
+ {
+ UpdateCorrection(packet, m_offset_pts);
+ if(packet->iStreamId < 0)
+ return true;
+
+ stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId);
+ if (!stream)
+ {
+ CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
+ return false;
+ }
+ if(stream->source == STREAM_SOURCE_NONE)
+ {
+ m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
+ m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer);
+ }
+ return true;
+ }
+ }
+
+ // read a data frame from stream.
+ if(m_pDemuxer)
+ packet = m_pDemuxer->Read();
+
+ if(packet)
+ {
+ UpdateCorrection(packet, m_offset_pts);
+ // this groupId stuff is getting a bit messy, need to find a better way
+ // currently it is used to determine if a menu overlay is associated with a picture
+ // for dvd's we use as a group id, the current cell and the current title
+ // to be a bit more precise we alse count the number of disc's in case of a pts wrap back in the same cell / title
+ packet->iGroupId = m_pInputStream->GetCurrentGroupId();
+
+ if(packet->iStreamId < 0)
+ return true;
+
+ stream = m_pDemuxer->GetStream(packet->iStreamId);
+ if (!stream)
+ {
+ CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
+ return false;
+ }
+ if(stream->source == STREAM_SOURCE_NONE)
+ {
+ m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
+ m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool COMXPlayer::IsValidStream(COMXCurrentStream& stream)
+{
+ if(stream.id<0)
+ return true; // we consider non selected as valid
+
+ int source = STREAM_SOURCE_MASK(stream.source);
+ if(source == STREAM_SOURCE_TEXT)
+ return true;
+ if(source == STREAM_SOURCE_DEMUX_SUB)
+ {
+ CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id);
+ if(st == NULL || st->disabled)
+ return false;
+ if(st->type != stream.type)
+ return false;
+ return true;
+ }
+ if(source == STREAM_SOURCE_DEMUX)
+ {
+ CDemuxStream* st = m_pDemuxer->GetStream(stream.id);
+ if(st == NULL || st->disabled)
+ return false;
+ if(st->type != stream.type)
+ return false;
+
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ if(stream.type == STREAM_AUDIO && st->iPhysicalId != m_dvd.iSelectedAudioStream)
+ return false;
+ if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream)
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool COMXPlayer::IsBetterStream(COMXCurrentStream& current, CDemuxStream* stream)
+{
+ // Do not reopen non-video streams if we're in video-only mode
+ //if(m_PlayerOptions.video_only && current.type != STREAM_VIDEO)
+ // return false;
+
+ if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
+ || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) )
+ {
+ int source_type;
+
+ source_type = STREAM_SOURCE_MASK(current.source);
+ if(source_type != STREAM_SOURCE_DEMUX
+ && source_type != STREAM_SOURCE_NONE)
+ return false;
+
+ source_type = STREAM_SOURCE_MASK(stream->source);
+ if(source_type != STREAM_SOURCE_DEMUX
+ || stream->type != current.type
+ || stream->iId == current.id)
+ return false;
+
+ if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
+ return true;
+ if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
+ return true;
+ if(current.type == STREAM_VIDEO && current.id < 0)
+ return true;
+ }
+ else
+ {
+ if(stream->source == current.source
+ && stream->iId == current.id)
+ return false;
+
+ if(stream->disabled)
+ return false;
+
+ if(stream->type != current.type)
+ return false;
+
+ if(current.type == STREAM_SUBTITLE)
+ return false;
+
+ if(current.type == STREAM_TELETEXT)
+ return false;
+
+ if(current.id < 0)
+ return true;
+ }
+ return false;
+}
+
+bool COMXPlayer::WaitForPausedThumbJobs(int timeout_ms)
+{
+ // use m_bStop and Sleep so we can get canceled.
+ while (!m_bStop && (timeout_ms > 0))
+ {
+ if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0)
+ {
+ Sleep(100);
+ timeout_ms -= 100;
+ }
+ else
+ return true;
+ }
+
+ return false;
+}
+
+void COMXPlayer::Process()
+{
+ bool bAEStopped = false;
+
+ if(!m_av_clock.OMXInitialize(false, false))
+ {
+ m_bAbortRequest = true;
+ return;
+ }
+ if(g_guiSettings.GetBool("videoplayer.adjustrefreshrate"))
+ m_av_clock.HDMIClockSync();
+
+ m_av_clock.OMXStateExecute();
+ m_av_clock.OMXStart();
+
+ //CLog::Log(LOGDEBUG, "COMXPlayer: Thread started");
+
+ try
+ {
+ m_stats = false;
+
+ if(!OpenInputStream())
+ {
+ m_bAbortRequest = true;
+ return;
+ }
+
+ if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CLog::Log(LOGNOTICE, "OMXPlayer: playing a dvd with menu's");
+ m_PlayerOptions.starttime = 0;
+
+ if(m_PlayerOptions.state.size() > 0)
+ ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(m_PlayerOptions.state);
+ else
+ ((CDVDInputStreamNavigator*)m_pInputStream)->EnableSubtitleStream(g_settings.m_currentVideoSettings.m_SubtitleOn);
+
+ g_settings.m_currentVideoSettings.m_SubtitleCached = true;
+ }
+
+ if(!OpenDemuxStream())
+ {
+ m_bAbortRequest = true;
+ return;
+ }
+
+ m_player_video.EnableFullscreen(true);
+
+ OpenDefaultStreams();
+
+ // look for any EDL files
+ m_Edl.Clear();
+ m_EdlAutoSkipMarkers.Clear();
+ float fFramesPerSecond;
+ if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0)
+ {
+ fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale;
+ m_Edl.ReadEditDecisionLists(m_filename, fFramesPerSecond, m_CurrentVideo.hint.height);
+ }
+
+ /*
+ * Check to see if the demuxer should start at something other than time 0. This will be the case
+ * if there was a start time specified as part of the "Start from where last stopped" (aka
+ * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
+ */
+ CEdl::Cut cut;
+ int starttime = 0;
+ if(m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0)
+ {
+ if (m_PlayerOptions.startpercent > 0 && m_pDemuxer)
+ {
+ int64_t playerStartTime = (int64_t) ( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) );
+ starttime = m_Edl.RestoreCutTime(playerStartTime);
+ }
+ else
+ {
+ starttime = m_Edl.RestoreCutTime((int64_t)m_PlayerOptions.starttime * 1000); // s to ms
+ }
+ CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime);
+ }
+ else if(m_Edl.InCut(0, &cut)
+ && (cut.action == CEdl::CUT || cut.action == CEdl::COMM_BREAK))
+ {
+ starttime = cut.end;
+ CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut or commercial break: %d", __FUNCTION__, starttime);
+ if(cut.action == CEdl::COMM_BREAK)
+ {
+ /*
+ * Setup auto skip markers as if the commercial break had been skipped using standard
+ * detection.
+ */
+ m_EdlAutoSkipMarkers.commbreak_start = cut.start;
+ m_EdlAutoSkipMarkers.commbreak_end = cut.end;
+ m_EdlAutoSkipMarkers.seek_to_start = true;
+ }
+ }
+ if(starttime > 0)
+ {
+ double startpts = DVD_NOPTS_VALUE;
+ if(m_pDemuxer)
+ {
+ //m_messenger.Put(new CDVDMsgPlayerSeek((int)starttime, true, true, true));
+ if (m_pDemuxer->SeekTime(starttime, false, &startpts))
+ CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime);
+ else
+ CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime);
+ }
+ if(m_pSubtitleDemuxer)
+ {
+ if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts))
+ CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime);
+ else
+ CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime);
+ }
+ }
+
+ UpdateApplication(0);
+ UpdatePlayState(0);
+
+ if (m_PlayerOptions.identify == false)
+ m_callback.OnPlayBackStarted();
+
+ // drop CGUIDialogBusy, and release the hold in OpenFile
+ m_ready.Set();
+
+ SetCaching(CACHESTATE_FLUSH);
+
+ // shutdown AE
+ CAEFactory::Shutdown();
+ bAEStopped = true;
+
+ // stop thumb jobs
+ CJobManager::GetInstance().Pause(kJobTypeMediaFlags);
+
+ if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0)
+ {
+ if (!WaitForPausedThumbJobs(20000))
+ {
+ CJobManager::GetInstance().UnPause(kJobTypeMediaFlags);
+ CLog::Log(LOGINFO, "COMXPlayer::Process:thumbgen jobs still running !!!");
+ }
+ }
+
+ while (!m_bAbortRequest)
+ {
+ // handle messages send to this thread, like seek or demuxer reset requests
+ HandleMessages();
+
+ if(m_bAbortRequest)
+ break;
+
+ // should we open a new input stream?
+ if(!m_pInputStream)
+ {
+ if (OpenInputStream() == false)
+ {
+ m_bAbortRequest = true;
+ break;
+ }
+ }
+
+ // should we open a new demuxer?
+ if(!m_pDemuxer)
+ {
+ if (m_pInputStream->NextStream() == false)
+ break;
+
+ if (m_pInputStream->IsEOF())
+ break;
+
+ if (OpenDemuxStream() == false)
+ {
+ m_bAbortRequest = true;
+ break;
+ }
+
+ OpenDefaultStreams();
+ UpdateApplication(0);
+ UpdatePlayState(0);
+ }
+
+ // handle eventual seeks due to playspeed
+ HandlePlaySpeed();
+
+ // on seek back the omx clock is reset to 0
+ //m_av_clock.OMXHandleBackward();
+
+ // update player state
+ UpdatePlayState(200);
+
+ // update application with our state
+ UpdateApplication(1000);
+
+ // if the queues are full, no need to read more
+ if ((!m_player_audio.AcceptsData() && m_CurrentAudio.id >= 0)
+ || (!m_player_video.AcceptsData() && m_CurrentVideo.id >= 0))
+ {
+ Sleep(10);
+ continue;
+ }
+
+ // always yield to players if they have data
+ if((m_player_audio.HasData() || m_CurrentAudio.id < 0)
+ && (m_player_video.HasData() || m_CurrentVideo.id < 0))
+ Sleep(0);
+
+ DemuxPacket* pPacket = NULL;
+ CDemuxStream *pStream = NULL;
+ ReadPacket(pPacket, pStream);
+
+ if (pPacket && !pStream)
+ {
+ /* probably a empty packet, just free it and move on */
+ CDVDDemuxUtils::FreeDemuxPacket(pPacket);
+ continue;
+ }
+
+ if (!pPacket)
+ {
+ // when paused, demuxer could be be returning empty
+ if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
+ continue;
+
+ // check for a still frame state
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CDVDInputStreamNavigator* pStream = static_cast<CDVDInputStreamNavigator*>(m_pInputStream);
+
+ // stills will be skipped
+ if(m_dvd.state == DVDSTATE_STILL)
+ {
+ if (m_dvd.iDVDStillTime > 0)
+ {
+ if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime)
+ {
+ m_dvd.iDVDStillTime = 0;
+ m_dvd.iDVDStillStartTime = 0;
+ m_dvd.state = DVDSTATE_NORMAL;
+ pStream->SkipStill();
+ continue;
+ }
+ }
+ }
+ }
+
+ // if there is another stream available, reopen demuxer
+ CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
+ if(next == CDVDInputStream::NEXTSTREAM_OPEN)
+ {
+ SAFE_DELETE(m_pDemuxer);
+ continue;
+ }
+
+ // input stream asked us to just retry
+ if(next == CDVDInputStream::NEXTSTREAM_RETRY)
+ {
+ Sleep(100);
+ continue;
+ }
+
+ // make sure we tell all players to finish it's data
+ if(m_CurrentAudio.inited)
+ m_player_audio.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
+ if(m_CurrentVideo.inited)
+ m_player_video.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
+ if(m_CurrentSubtitle.inited)
+ m_player_subtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
+ m_CurrentAudio.inited = false;
+ m_CurrentVideo.inited = false;
+ m_CurrentSubtitle.inited = false;
+ m_CurrentAudio.started = false;
+ m_CurrentVideo.started = false;
+ m_CurrentSubtitle.started = false;
+
+ // if we are caching, start playing it again
+ SetCaching(CACHESTATE_DONE);
+
+ if(m_player_video.HasData()
+ || m_player_audio.HasData())
+ {
+ Sleep(100);
+ continue;
+ }
+
+ if (!m_pInputStream->IsEOF())
+ CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
+
+ break;
+ }
+
+ // check so that none of our streams has become invalid
+ if (!IsValidStream(m_CurrentAudio) && m_player_audio.IsStalled()) CloseAudioStream(true);
+ if (!IsValidStream(m_CurrentVideo) && m_player_video.IsStalled()) CloseVideoStream(true);
+ if (!IsValidStream(m_CurrentSubtitle) && m_player_subtitle.IsStalled()) CloseSubtitleStream(true);
+
+ // see if we can find something better to play
+ if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream (pStream->iId, pStream->source);
+ if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream (pStream->iId, pStream->source);
+ if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source);
+
+ if(m_change_volume)
+ {
+ m_player_audio.SetCurrentVolume(m_current_volume);
+ m_change_volume = false;
+ }
+
+ ProcessPacket(pStream, pPacket);
+
+ // check if in a cut or commercial break that should be automatically skipped
+ CheckAutoSceneSkip();
+
+ /*
+ printf("V : %8.02f %8d %8d A : %8.02f %8.02f \r",
+ m_player_video.GetCurrentPTS() / DVD_TIME_BASE, m_player_video.GetDecoderBufferSize(),
+ m_player_video.GetDecoderFreeSpace(), m_player_audio.GetCurrentPTS() / DVD_TIME_BASE,
+ m_player_audio.GetDelay());
+ */
+ }
+ }
+ catch(...)
+ {
+ CLog::Log(LOGERROR, "COMXPlayer::Process: Exception thrown");
+ }
+
+ if(bAEStopped)
+ {
+ // start AE
+ CAEFactory::LoadEngine();
+ CAEFactory::StartEngine();
+
+ CAEFactory::SetMute (g_settings.m_bMute);
+ CAEFactory::SetSoundMode(g_guiSettings.GetInt("audiooutput.guisoundmode"));
+ }
+
+ // let thumbgen jobs resume.
+ CJobManager::GetInstance().UnPause(kJobTypeMediaFlags);
+}
+
+void COMXPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
+{
+ /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/
+ OMXStreamLock lock(this);
+
+ try
+ {
+ if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO)
+ ProcessAudioData(pStream, pPacket);
+ else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO)
+ ProcessVideoData(pStream, pPacket);
+ else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE)
+ ProcessSubData(pStream, pPacket);
+ else
+ {
+ pStream->SetDiscard(AVDISCARD_ALL);
+ CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
+ }
+ }
+ catch(...)
+ {
+ CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__);
+ }
+
+}
+
+void COMXPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
+{
+ if (m_CurrentAudio.stream != (void*)pStream)
+ {
+ /* check so that dmuxer hints or extra data hasn't changed */
+ /* if they have, reopen stream */
+
+ if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true))
+ OpenAudioStream( pPacket->iStreamId, pStream->source );
+
+ m_CurrentAudio.stream = (void*)pStream;
+ }
+
+ // check if we are too slow and need to recache
+ CheckStartCaching(m_CurrentAudio);
+
+ CheckContinuity(m_CurrentAudio, pPacket);
+ UpdateTimestamps(m_CurrentAudio, pPacket);
+ bool drop = false;
+ if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO))
+ drop = true;
+
+ /*
+ * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
+ * If not inside a hard cut, but the demux point has reached an EDL mute section then trigger the
+ * AUDIO_SILENCE state. The AUDIO_SILENCE state is reverted as soon as the demux point is outside
+ * of any EDL section while EDL mute is still active.
+ */
+ CEdl::Cut cut;
+ if (CheckSceneSkip(m_CurrentAudio))
+ drop = true;
+ else if (m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) && cut.action == CEdl::MUTE // Inside EDL mute
+ && !m_EdlAutoSkipMarkers.mute) // Mute not already triggered
+ {
+ m_player_audio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, true));
+ m_EdlAutoSkipMarkers.mute = true;
+ }
+ else if (!m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) // Outside of any EDL
+ && m_EdlAutoSkipMarkers.mute) // But the mute hasn't been removed yet
+ {
+ m_player_audio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, false));
+ m_EdlAutoSkipMarkers.mute = false;
+ }
+
+ m_player_audio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
+}
+
+void COMXPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
+{
+ if (m_CurrentVideo.stream != (void*)pStream)
+ {
+ /* check so that dmuxer hints or extra data hasn't changed */
+ /* if they have reopen stream */
+
+ if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true))
+ OpenVideoStream(pPacket->iStreamId, pStream->source);
+
+ m_CurrentVideo.stream = (void*)pStream;
+ }
+
+ // check if we are too slow and need to recache
+ CheckStartCaching(m_CurrentVideo);
+
+ if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
+ {
+ CheckContinuity(m_CurrentVideo, pPacket);
+ UpdateTimestamps(m_CurrentVideo, pPacket);
+ }
+
+ bool drop = false;
+ if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO))
+ drop = true;
+
+ if (CheckSceneSkip(m_CurrentVideo))
+ drop = true;
+
+ m_player_video.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
+}
+
+void COMXPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
+{
+ if (m_CurrentSubtitle.stream != (void*)pStream)
+ {
+ /* check so that dmuxer hints or extra data hasn't changed */
+ /* if they have reopen stream */
+
+ if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true))
+ OpenSubtitleStream(pPacket->iStreamId, pStream->source);
+
+ m_CurrentSubtitle.stream = (void*)pStream;
+ }
+
+ UpdateTimestamps(m_CurrentSubtitle, pPacket);
+
+ bool drop = false;
+ if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE))
+ drop = true;
+
+ if (CheckSceneSkip(m_CurrentSubtitle))
+ drop = true;
+
+ m_player_subtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
+
+ if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ m_player_subtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
+}
+
+bool COMXPlayer::GetCachingTimes(double& level, double& delay, double& offset)
+{
+ if(!m_pInputStream || !m_pDemuxer)
+ return false;
+
+ XFILE::SCacheStatus status;
+ if (!m_pInputStream->GetCacheStatus(&status))
+ return false;
+
+ int64_t cached = status.forward;
+ unsigned currate = status.currate;
+ unsigned maxrate = status.maxrate;
+ bool full = status.full;
+
+ int64_t length = m_pInputStream->GetLength();
+ int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
+
+ if(cached < 0 || length <= 0 || remain < 0)
+ return false;
+
+ double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
+ double queued = 1000.0 * GetQueueTime() / play_sbp;
+
+ delay = 0.0;
+ level = 0.0;
+ offset = (double)(cached + queued) / length;
+
+ if (currate == 0)
+ return true;
+
+ double cache_sbp = 1.1 * (double)DVD_TIME_BASE / currate; /* underestimate by 10 % */
+ double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */
+ double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */
+ double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */
+
+ delay = cache_left - play_left;
+
+ if (full && (currate < maxrate) )
+ level = -1.0; /* buffer is full & our read rate is too low */
+ else
+ level = (cached + queued) / (cache_need + queued);
+
+ return true;
+}
+
+void COMXPlayer::HandlePlaySpeed()
+{
+ ECacheState caching = m_caching;
+
+ if(IsInMenu() && caching != CACHESTATE_DONE)
+ caching = CACHESTATE_DONE;
+
+ if(caching == CACHESTATE_FULL)
+ {
+ double level, delay, offset;
+ if(GetCachingTimes(level, delay, offset))
+ {
+ if(level < 0.0)
+ caching = CACHESTATE_INIT;
+ if(level >= 1.0)
+ caching = CACHESTATE_INIT;
+ }
+ else
+ {
+ if ((!m_player_audio.AcceptsData() && m_CurrentAudio.id >= 0)
+ || (!m_player_video.AcceptsData() && m_CurrentVideo.id >= 0))
+ caching = CACHESTATE_INIT;
+ }
+ }
+
+ if(caching == CACHESTATE_INIT)
+ {
+ // if all enabled streams have been inited we are done
+ if((m_CurrentVideo.id < 0 || m_CurrentVideo.started)
+ && (m_CurrentAudio.id < 0 || m_CurrentAudio.started))
+ caching = CACHESTATE_PLAY;
+
+ // handle situation that we get no data on one stream
+ if(m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
+ {
+ if ((!m_player_audio.AcceptsData() && !m_CurrentVideo.started)
+ || (!m_player_video.AcceptsData() && !m_CurrentAudio.started))
+ {
+ caching = CACHESTATE_DONE;
+ }
+ }
+ }
+
+ if(caching == CACHESTATE_PLAY)
+ {
+ // if all enabled streams have started playing we are done
+ if((m_CurrentVideo.id < 0 || !m_player_video.IsStalled())
+ && (m_CurrentAudio.id < 0 || !m_player_audio.IsStalled()))
+ caching = CACHESTATE_DONE;
+ }
+
+ if(m_caching != caching)
+ SetCaching(caching);
+
+ if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE)
+ {
+ if (IsInMenu())
+ {
+ // this can't be done in menu
+ SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
+
+ }
+ else if (m_CurrentVideo.id >= 0
+ && m_CurrentVideo.inited == true
+ && m_SpeedState.lastpts != m_player_video.GetCurrentPTS()
+ && m_SpeedState.lasttime != GetTime())
+ {
+ m_SpeedState.lastpts = m_player_video.GetCurrentPTS();
+ m_SpeedState.lasttime = GetTime();
+ // check how much off clock video is when ff/rw:ing
+ // a problem here is that seeking isn't very accurate
+ // and since the clock will be resynced after seek
+ // we might actually not really be playing at the wanted
+ // speed. we'd need to have some way to not resync the clock
+ // after a seek to remember timing. still need to handle
+ // discontinuities somehow
+
+ // when seeking, give the player a headstart to make sure
+ // the time it takes to seek doesn't make a difference.
+ double error;
+ error = m_av_clock.GetClock() - m_SpeedState.lastpts;
+ error *= m_playSpeed / abs(m_playSpeed);
+
+ if(error > DVD_MSEC_TO_TIME(1000))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayer::Process - Seeking to catch up");
+ int64_t iTime = (int64_t)DVD_TIME_TO_MSEC(m_av_clock.GetClock() + m_State.time_offset + 500000.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL);
+ m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false, false, true));
+ }
+ }
+ }
+}
+
+bool COMXPlayer::CheckStartCaching(COMXCurrentStream& current)
+{
+ if(m_caching != CACHESTATE_DONE
+ || m_playSpeed != DVD_PLAYSPEED_NORMAL)
+ return false;
+
+ if(IsInMenu())
+ return false;
+
+ if((current.type == STREAM_AUDIO && m_player_audio.IsStalled())
+ || (current.type == STREAM_VIDEO && m_player_video.IsStalled()))
+ {
+ // don't start caching if it's only a single stream that has run dry
+ if(m_player_audio.GetLevel() > 50
+ || m_player_video.GetLevel() > 50)
+ return false;
+
+ if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)
+ || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+ SetCaching(CACHESTATE_INIT);
+ else
+ {
+ if(current.inited)
+ SetCaching(CACHESTATE_FULL);
+ else
+ SetCaching(CACHESTATE_INIT);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool COMXPlayer::CheckPlayerInit(COMXCurrentStream& current, unsigned int source)
+{
+ if(current.inited)
+ return false;
+
+ if(current.startpts != DVD_NOPTS_VALUE)
+ {
+ if(current.dts == DVD_NOPTS_VALUE)
+ {
+ CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
+ return true;
+ }
+
+ if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
+ {
+ CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__);
+ if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
+ m_CurrentAudio.startpts = current.dts;
+ if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
+ m_CurrentVideo.startpts = current.dts;
+ if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
+ m_CurrentSubtitle.startpts = current.dts;
+ if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
+ m_CurrentTeletext.startpts = current.dts;
+ }
+
+ if(current.dts < current.startpts)
+ {
+ CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
+ return true;
+ }
+ }
+
+ //If this is the first packet after a discontinuity, send it as a resync
+ if (current.dts != DVD_NOPTS_VALUE)
+ {
+ current.inited = true;
+ current.startpts = current.dts;
+
+ bool setclock = false;
+ if(m_playSpeed == DVD_PLAYSPEED_NORMAL)
+ {
+ if( source == DVDPLAYER_AUDIO)
+ setclock = !m_CurrentVideo.inited;
+ else if(source == DVDPLAYER_VIDEO)
+ setclock = !m_CurrentAudio.inited;
+ }
+ else
+ {
+ if(source == DVDPLAYER_VIDEO)
+ setclock = true;
+ }
+
+ double starttime = current.startpts;
+ if(m_CurrentAudio.inited
+ && m_CurrentAudio.startpts != DVD_NOPTS_VALUE
+ && m_CurrentAudio.startpts < starttime)
+ starttime = m_CurrentAudio.startpts;
+ if(m_CurrentVideo.inited
+ && m_CurrentVideo.startpts != DVD_NOPTS_VALUE
+ && m_CurrentVideo.startpts < starttime)
+ starttime = m_CurrentVideo.startpts;
+
+ starttime = current.startpts - starttime;
+ if(starttime > 0 && setclock)
+ {
+ if(starttime > DVD_SEC_TO_TIME(2))
+ CLog::Log(LOGWARNING, "COMXPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime);
+ else
+ SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source);
+ }
+
+ SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source);
+ }
+ return false;
+}
+
+void COMXPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
+{
+ if(pkt->dts != DVD_NOPTS_VALUE) pkt->dts -= correction;
+ if(pkt->pts != DVD_NOPTS_VALUE) pkt->pts -= correction;
+}
+
+void COMXPlayer::UpdateTimestamps(COMXCurrentStream& current, DemuxPacket* pPacket)
+{
+ double dts = current.dts;
+ /* update stored values */
+ if(pPacket->dts != DVD_NOPTS_VALUE)
+ dts = pPacket->dts;
+ else if(pPacket->pts != DVD_NOPTS_VALUE)
+ dts = pPacket->pts;
+
+ /* calculate some average duration */
+ if(pPacket->duration != DVD_NOPTS_VALUE)
+ current.dur = pPacket->duration;
+ else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
+ current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
+
+ current.dts = dts;
+}
+
+void COMXPlayer::UpdateLimits(double& minimum, double& maximum, double dts)
+{
+ if(dts == DVD_NOPTS_VALUE)
+ return;
+ if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
+ if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
+}
+
+void COMXPlayer::CheckContinuity(COMXCurrentStream& current, DemuxPacket* pPacket)
+{
+ if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
+ return;
+
+ if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
+ return;
+
+ double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
+ UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
+ UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
+ UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
+ UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
+
+ /* if we don't have max and min, we can't do anything more */
+ if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
+ return;
+
+ double correction = 0.0;
+ if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayer::CheckContinuity - resync forward :%d, prev:%f, curr:%f, diff:%f"
+ , current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
+ correction = pPacket->dts - maxdts;
+ }
+
+ /* if it's large scale jump, correct for it */
+ if(pPacket->dts + DVD_MSEC_TO_TIME(100) < current.dts_end())
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayer::CheckContinuity - resync backward :%d, prev:%f, curr:%f, diff:%f"
+ , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
+ correction = pPacket->dts - current.dts_end();
+ }
+ else if(pPacket->dts < current.dts)
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayer::CheckContinuity - wrapback :%d, prev:%f, curr:%f, diff:%f"
+ , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
+ }
+
+ if(correction != 0.0)
+ {
+ /* disable detection on next packet on other stream to avoid ping pong-ing */
+ if(m_CurrentAudio.player != current.player) m_CurrentAudio.dts = DVD_NOPTS_VALUE;
+ if(m_CurrentVideo.player != current.player) m_CurrentVideo.dts = DVD_NOPTS_VALUE;
+
+ m_offset_pts += correction;
+ UpdateCorrection(pPacket, correction);
+ }
+}
+
+bool COMXPlayer::CheckSceneSkip(COMXCurrentStream& current)
+{
+ if(!m_Edl.HasCut())
+ return false;
+
+ if(current.dts == DVD_NOPTS_VALUE)
+ return false;
+
+ if(current.inited == false)
+ return false;
+
+ CEdl::Cut cut;
+ return m_Edl.InCut(DVD_TIME_TO_MSEC(current.dts + m_offset_pts), &cut) && cut.action == CEdl::CUT;
+}
+
+void COMXPlayer::CheckAutoSceneSkip()
+{
+ if(!m_Edl.HasCut())
+ return;
+
+ /*
+ * Check that there is an audio and video stream.
+ */
+ if(m_CurrentAudio.id < 0
+ || m_CurrentVideo.id < 0)
+ return;
+
+ /*
+ * If there is a startpts defined for either the audio or video stream then dvdplayer is still
+ * still decoding frames to get to the previously requested seek point.
+ */
+ if(m_CurrentAudio.inited == false
+ || m_CurrentVideo.inited == false)
+ return;
+
+ if(m_CurrentAudio.dts == DVD_NOPTS_VALUE
+ || m_CurrentVideo.dts == DVD_NOPTS_VALUE)
+ return;
+
+ const int64_t clock = DVD_TIME_TO_MSEC(min(m_CurrentAudio.dts, m_CurrentVideo.dts) + m_offset_pts);
+
+ CEdl::Cut cut;
+ if(!m_Edl.InCut(clock, &cut))
+ return;
+
+ if(cut.action == CEdl::CUT
+ && !(cut.end == m_EdlAutoSkipMarkers.cut || cut.start == m_EdlAutoSkipMarkers.cut)) // To prevent looping if same cut again
+ {
+ CLog::Log(LOGDEBUG, "%s - Clock in EDL cut [%s - %s]: %s. Automatically skipping over.",
+ __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(),
+ CEdl::MillisecondsToTimeString(cut.end).c_str(), CEdl::MillisecondsToTimeString(clock).c_str());
+ /*
+ * Seeking either goes to the start or the end of the cut depending on the play direction.
+ */
+ int64_t seek = GetPlaySpeed() >= 0 ? cut.end : cut.start;
+ /*
+ * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
+ */
+ m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false, true));
+ /*
+ * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping
+ * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the
+ * cut. The cut automatic skip marker is reset every 500ms allowing another attempt at the seek.
+ */
+ m_EdlAutoSkipMarkers.cut = GetPlaySpeed() >= 0 ? cut.end : cut.start;
+ }
+ else if(cut.action == CEdl::COMM_BREAK
+ && GetPlaySpeed() >= 0
+ && cut.start > m_EdlAutoSkipMarkers.commbreak_end)
+ {
+ CLog::Log(LOGDEBUG, "%s - Clock in commercial break [%s - %s]: %s. Automatically skipping to end of commercial break (only done once per break)",
+ __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), CEdl::MillisecondsToTimeString(cut.end).c_str(),
+ CEdl::MillisecondsToTimeString(clock).c_str());
+ /*
+ * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
+ */
+ m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
+ /*
+ * Each commercial break is only skipped once so poorly detected commercial breaks can be
+ * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back
+ * to the start of the commercial break if incorrectly flagged.
+ */
+ m_EdlAutoSkipMarkers.commbreak_start = cut.start;
+ m_EdlAutoSkipMarkers.commbreak_end = cut.end;
+ m_EdlAutoSkipMarkers.seek_to_start = true; // Allow backwards Seek() to go directly to the start
+ }
+}
+
+void COMXPlayer::SynchronizeDemuxer(unsigned int timeout)
+{
+ if(IsCurrentThread())
+ return;
+ if(!m_messenger.IsInited())
+ return;
+
+ CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0);
+ m_messenger.Put(message->Acquire());
+ message->Wait(&m_bStop, 0);
+ message->Release();
+}
+
+void COMXPlayer::SynchronizePlayers(unsigned int sources)
+{
+ /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */
+ const int timeout = 10*1000; // in milliseconds
+
+ CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources);
+ if (m_CurrentAudio.id >= 0)
+ m_player_audio.SendMessage(message->Acquire());
+
+ if (m_CurrentVideo.id >= 0)
+ m_player_video.SendMessage(message->Acquire());
+/* TODO - we have to rewrite the sync class, to not require
+ all other players waiting for subtitle, should only
+ be the oposite way
+ if (m_CurrentSubtitle.id >= 0)
+ m_player_subtitle.SendMessage(message->Acquire());
+*/
+ message->Release();
+}
+
+void COMXPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target)
+{
+ if(target == DVDPLAYER_AUDIO)
+ m_player_audio.SendMessage(pMsg);
+ if(target == DVDPLAYER_VIDEO)
+ m_player_video.SendMessage(pMsg);
+ if(target == DVDPLAYER_SUBTITLE)
+ m_player_subtitle.SendMessage(pMsg);
+}
+
+void COMXPlayer::OnExit()
+{
+ try
+ {
+ CLog::Log(LOGNOTICE, "COMXPlayer::OnExit()");
+
+ m_av_clock.OMXStop();
+ m_av_clock.OMXStateIdle();
+
+ // set event to inform openfile something went wrong in case openfile is still waiting for this event
+ SetCaching(CACHESTATE_DONE);
+
+ // close each stream
+ if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "OMXPlayer: eof, waiting for queues to empty");
+ if (m_CurrentAudio.id >= 0)
+ {
+ CLog::Log(LOGNOTICE, "OMXPlayer: closing audio stream");
+ CloseAudioStream(!m_bAbortRequest);
+ }
+ if (m_CurrentVideo.id >= 0)
+ {
+ CLog::Log(LOGNOTICE, "OMXPlayer: closing video stream");
+ CloseVideoStream(!m_bAbortRequest);
+ }
+ if (m_CurrentSubtitle.id >= 0)
+ {
+ CLog::Log(LOGNOTICE, "OMXPlayer: closing subtitle stream");
+ CloseSubtitleStream(!m_bAbortRequest);
+ }
+ /*
+ if (m_CurrentTeletext.id >= 0)
+ {
+ CLog::Log(LOGNOTICE, "OMXPlayer: closing teletext stream");
+ CloseTeletextStream(!m_bAbortRequest);
+ }
+ */
+ // destroy the demuxer
+ if (m_pDemuxer)
+ {
+ CLog::Log(LOGNOTICE, "COMXPlayer::OnExit() deleting demuxer");
+ delete m_pDemuxer;
+ }
+ m_pDemuxer = NULL;
+
+ if (m_pSubtitleDemuxer)
+ {
+ CLog::Log(LOGNOTICE, "COMXPlayer::OnExit() deleting subtitle demuxer");
+ delete m_pSubtitleDemuxer;
+ }
+ m_pSubtitleDemuxer = NULL;
+
+ // destroy the inputstream
+ if (m_pInputStream)
+ {
+ CLog::Log(LOGNOTICE, "COMXPlayer::OnExit() deleting input stream");
+ delete m_pInputStream;
+ }
+ m_pInputStream = NULL;
+
+ // clean up all selection streams
+ m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
+
+ m_messenger.End();
+
+ m_av_clock.OMXDeinitialize();
+
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__);
+ m_pInputStream = NULL;
+ m_pDemuxer = NULL;
+ }
+
+ m_bStop = true;
+ // if we didn't stop playing, advance to the next item in xbmc's playlist
+ if(m_PlayerOptions.identify == false)
+ {
+ if (m_bAbortRequest)
+ m_callback.OnPlayBackStopped();
+ else
+ m_callback.OnPlayBackEnded();
+ }
+
+ // set event to inform openfile something went wrong in case openfile is still waiting for this event
+ m_ready.Set();
+}
+
+void COMXPlayer::HandleMessages()
+{
+ CDVDMsg* pMsg;
+ OMXStreamLock lock(this);
+
+ while (m_messenger.Get(&pMsg, 0) == MSGQ_OK)
+ {
+
+ try
+ {
+ if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
+ && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
+ {
+ CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg));
+
+ if(!msg.GetTrickPlay())
+ {
+ g_infoManager.SetDisplayAfterSeek(100000);
+ if(msg.GetFlush())
+ SetCaching(CACHESTATE_FLUSH);
+ }
+
+ double start = DVD_NOPTS_VALUE;
+
+ int time = msg.GetRestore() ? (int)m_Edl.RestoreCutTime(msg.GetTime()) : msg.GetTime();
+ CLog::Log(LOGDEBUG, "demuxer seek to: %d", time);
+ if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
+ {
+ CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", time);
+ if(m_pSubtitleDemuxer)
+ {
+ if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
+ CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", time);
+ }
+ FlushBuffers(!msg.GetFlush(), start, msg.GetAccurate());
+ }
+ else
+ CLog::Log(LOGWARNING, "error while seeking");
+
+ // set flag to indicate we have finished a seeking request
+ if(!msg.GetTrickPlay())
+ {
+ g_infoManager.m_performingSeek = false;
+ g_infoManager.SetDisplayAfterSeek();
+ }
+
+ // dvd's will issue a HOP_CHANNEL that we need to skip
+ if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ m_dvd.state = DVDSTATE_SEEK;
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
+ && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
+ {
+ g_infoManager.SetDisplayAfterSeek(100000);
+ SetCaching(CACHESTATE_FLUSH);
+
+ CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg));
+ double start = DVD_NOPTS_VALUE;
+
+ // This should always be the case.
+ if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
+ {
+ FlushBuffers(false, start, true);
+ m_callback.OnPlayBackSeekChapter(msg.GetChapter());
+ }
+
+ g_infoManager.SetDisplayAfterSeek();
+ }
+ else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
+ {
+ m_CurrentAudio.stream = NULL;
+ m_CurrentVideo.stream = NULL;
+ m_CurrentSubtitle.stream = NULL;
+
+ // we need to reset the demuxer, probably because the streams have changed
+ if(m_pDemuxer)
+ m_pDemuxer->Reset();
+ if(m_pSubtitleDemuxer)
+ m_pSubtitleDemuxer->Reset();
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
+ {
+ CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg;
+
+ OMXSelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
+ if(st.source != STREAM_SOURCE_NONE)
+ {
+ if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
+ if(pStream->SetActiveAudioStream(st.id))
+ {
+ m_dvd.iSelectedAudioStream = -1;
+ CloseAudioStream(false);
+ CloseVideoStream(false);
+ m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true));
+ }
+ }
+ else
+ {
+ CloseAudioStream(false);
+ OpenAudioStream(st.id, st.source);
+ m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true));
+ }
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
+ {
+ CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg;
+
+ OMXSelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
+ if(st.source != STREAM_SOURCE_NONE)
+ {
+ if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
+ if(pStream->SetActiveSubtitleStream(st.id))
+ {
+ m_dvd.iSelectedSPUStream = -1;
+ CloseSubtitleStream(false);
+ }
+ }
+ else
+ {
+ CloseSubtitleStream(false);
+ OpenSubtitleStream(st.id, st.source);
+ }
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
+ {
+ CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg;
+
+ m_player_video.EnableSubtitle(pValue->m_value);
+
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->EnableSubtitleStream(pValue->m_value);
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
+ {
+ g_infoManager.SetDisplayAfterSeek(100000);
+ SetCaching(CACHESTATE_FLUSH);
+
+ CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg;
+
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ std::string s = pMsgPlayerSetState->GetState();
+ ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(s);
+ m_dvd.state = DVDSTATE_NORMAL;
+ m_dvd.iDVDStillStartTime = 0;
+ m_dvd.iDVDStillTime = 0;
+ }
+
+ g_infoManager.SetDisplayAfterSeek();
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
+ {
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+ static_cast<CDVDInputStreamTV*>(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg);
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
+ {
+ FlushBuffers(false);
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
+ {
+ int speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
+
+ // correct our current clock, as it would start going wrong otherwise
+ if(m_State.timestamp > 0)
+ {
+ double offset;
+ offset = m_av_clock.GetAbsoluteClock() - m_State.timestamp;
+ offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
+ if(offset > 1000) offset = 1000;
+ if(offset < -1000) offset = -1000;
+ m_State.time += DVD_TIME_TO_MSEC(offset);
+ m_State.timestamp = m_av_clock.GetAbsoluteClock();
+ }
+
+ if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
+ m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
+
+ // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE
+ // audioplayer, stops outputing audio to audiorendere, but still tries to
+ // sleep an correct amount for each packet
+ // videoplayer just plays faster after the clock speed has been increased
+ // 1. disable audio
+ // 2. skip frames and adjust their pts or the clock
+ m_playSpeed = speed;
+ m_caching = CACHESTATE_DONE;
+ m_av_clock.SetSpeed(speed);
+ m_player_audio.SetSpeed(speed);
+ m_player_video.SetSpeed(speed);
+ m_av_clock.OMXSetSpeed(m_playSpeed);
+
+ // TODO - we really shouldn't pause demuxer
+ // until our buffers are somewhat filled
+ if(m_pDemuxer)
+ m_pDemuxer->SetSpeed(speed);
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) ||
+ pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV) ||
+ (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0))
+ {
+ CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
+ if(input)
+ {
+ g_infoManager.SetDisplayAfterSeek(100000);
+
+ bool result;
+ if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT))
+ result = input->SelectChannel(static_cast<CDVDMsgInt*>(pMsg)->m_value);
+ else if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
+ result = input->NextChannel();
+ else
+ result = input->PrevChannel();
+
+ if(result)
+ {
+ FlushBuffers(false);
+ SAFE_DELETE(m_pDemuxer);
+ }
+
+ g_infoManager.SetDisplayAfterSeek();
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
+ OnAction(((CDVDMsgType<CAction>*)pMsg)->m_value);
+ else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
+ {
+ int player = ((CDVDMsgInt*)pMsg)->m_value;
+ if(player == DVDPLAYER_AUDIO)
+ m_CurrentAudio.started = true;
+ if(player == DVDPLAYER_VIDEO)
+ m_CurrentVideo.started = true;
+ CLog::Log(LOGDEBUG, "COMXPlayer::HandleMessages - player started %d", player);
+ }
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__);
+ }
+
+ pMsg->Release();
+ }
+}
+
+void COMXPlayer::SetCaching(ECacheState state)
+{
+ if(state == CACHESTATE_FLUSH)
+ {
+ double level, delay, offset;
+ if(GetCachingTimes(level, delay, offset))
+ state = CACHESTATE_FULL;
+ else
+ state = CACHESTATE_INIT;
+ }
+
+ if(m_caching == state)
+ return;
+
+ CLog::Log(LOGDEBUG, "COMXPlayer::SetCaching - caching state %d", state);
+ if(state == CACHESTATE_FULL
+ || state == CACHESTATE_INIT)
+ {
+ m_av_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_av_clock.OMXSetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_player_audio.SetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
+ m_player_video.SetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_player_video.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
+ }
+
+ if(state == CACHESTATE_PLAY
+ ||(state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
+ {
+ m_av_clock.SetSpeed(m_playSpeed);
+ m_av_clock.OMXSetSpeed(m_playSpeed);
+ m_player_audio.SetSpeed(m_playSpeed);
+ m_player_video.SetSpeed(m_playSpeed);
+ }
+ m_caching = state;
+}
+
+void COMXPlayer::SetPlaySpeed(int speed)
+{
+ /* only pause and normal playspeeds are allowed */
+ if(speed < 0 || speed > DVD_PLAYSPEED_NORMAL)
+ return;
+
+ m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed));
+ m_player_audio.SetSpeed(speed);
+ m_player_video.SetSpeed(speed);
+ SynchronizeDemuxer(100);
+}
+
+void COMXPlayer::Pause()
+{
+ if(m_playSpeed != DVD_PLAYSPEED_PAUSE && m_caching == CACHESTATE_FULL)
+ {
+ SetCaching(CACHESTATE_DONE);
+ return;
+ }
+
+ if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
+ {
+ SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
+ m_callback.OnPlayBackResumed();
+ }
+ else
+ {
+ SetPlaySpeed(DVD_PLAYSPEED_PAUSE);
+ m_callback.OnPlayBackPaused();
+ }
+}
+
+bool COMXPlayer::IsPaused() const
+{
+ return (m_playSpeed == DVD_PLAYSPEED_PAUSE) || m_caching == CACHESTATE_FULL;
+}
+
+bool COMXPlayer::HasVideo() const
+{
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) return true;
+
+ return m_SelectionStreams.Count(STREAM_VIDEO) > 0 ? true : false;
+}
+
+bool COMXPlayer::HasAudio() const
+{
+ return m_SelectionStreams.Count(STREAM_AUDIO) > 0 ? true : false;
+}
+
+bool COMXPlayer::IsPassthrough() const
+{
+ return m_player_audio.Passthrough();
+}
+
+bool COMXPlayer::CanSeek()
+{
+ return GetTotalTime() > 0;
+}
+
+void COMXPlayer::Seek(bool bPlus, bool bLargeStep)
+{
+ if(((bPlus && GetChapter() < GetChapterCount())
+ || (!bPlus && GetChapter() > 1)) && bLargeStep)
+ {
+ if(bPlus)
+ SeekChapter(GetChapter() + 1);
+ else
+ SeekChapter(GetChapter() - 1);
+ return;
+ }
+
+ int64_t seek;
+ if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2*g_advancedSettings.m_videoTimeSeekForwardBig)
+ {
+ if (bLargeStep)
+ seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
+ else
+ seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
+ seek *= 1000;
+ seek += GetTime();
+ }
+ else
+ {
+ float percent;
+ if (bLargeStep)
+ percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
+ else
+ percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
+ seek = (int64_t)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100);
+ }
+
+ bool restore = true;
+
+ if (m_Edl.HasCut())
+ {
+ /*
+ * Alter the standard seek position based on whether any commercial breaks have been
+ * automatically skipped.
+ */
+ const int clock = DVD_TIME_TO_MSEC(m_av_clock.GetClock());
+ /*
+ * If a large backwards seek occurs within 10 seconds of the end of the last automated
+ * commercial skip, then seek back to the start of the commercial break under the assumption
+ * it was flagged incorrectly. 10 seconds grace period is allowed in case the watcher has to
+ * fumble around finding the remote. Only happens once per commercial break.
+ *
+ * Small skip does not trigger this in case the start of the commercial break was in fact fine
+ * but it skipped too far into the program. In that case small skip backwards behaves as normal.
+ */
+ if (!bPlus && bLargeStep
+ && m_EdlAutoSkipMarkers.seek_to_start
+ && clock >= m_EdlAutoSkipMarkers.commbreak_end
+ && clock <= m_EdlAutoSkipMarkers.commbreak_end + 10*1000) // Only if within 10 seconds of the end (in msec)
+ {
+ CLog::Log(LOGDEBUG, "%s - Seeking back to start of commercial break [%s - %s] as large backwards skip activated within 10 seconds of the automatic commercial skip (only done once per break).",
+ __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
+ CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
+ seek = m_EdlAutoSkipMarkers.commbreak_start;
+ restore = false;
+ m_EdlAutoSkipMarkers.seek_to_start = false; // So this will only happen within the 10 second grace period once.
+ }
+ /*
+ * If big skip forward within the last "reverted" commercial break, seek to the end of the
+ * commercial break under the assumption that the break was incorrectly flagged and playback has
+ * now reached the actual start of the commercial break. Assume that the end is flagged more
+ * correctly than the landing point for a standard big skip (ends seem to be flagged more
+ * accurately than the start).
+ */
+ else if (bPlus && bLargeStep
+ && clock >= m_EdlAutoSkipMarkers.commbreak_start
+ && clock <= m_EdlAutoSkipMarkers.commbreak_end)
+ {
+ CLog::Log(LOGDEBUG, "%s - Seeking to end of previously skipped commercial break [%s - %s] as big forwards skip activated within the break.",
+ __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
+ CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
+ seek = m_EdlAutoSkipMarkers.commbreak_end;
+ restore = false;
+ }
+ }
+
+ int64_t time = GetTime();
+ if(g_application.CurrentFileItem().IsStack()
+ && (seek > GetTotalTimeInMsec() || seek < 0))
+ {
+ g_application.SeekTime((seek - time) * 0.001 + g_application.GetTime());
+ // warning, don't access any dvdplayer variables here as
+ // the dvdplayer object may have been destroyed
+ return;
+ }
+
+ m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false, restore));
+ SynchronizeDemuxer(100);
+ if (seek < 0) seek = 0;
+ m_callback.OnPlayBackSeek((int)seek, (int)(seek - time));
+}
+
+bool COMXPlayer::SeekScene(bool bPlus)
+{
+ if (!m_Edl.HasSceneMarker())
+ return false;
+
+ /*
+ * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
+ * grace period applied it is impossible to go backwards past a scene marker.
+ */
+ int64_t clock = GetTime();
+ if (!bPlus && clock > 5 * 1000) // 5 seconds
+ clock -= 5 * 1000;
+
+ int64_t iScenemarker;
+ if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker))
+ {
+ /*
+ * Seeking is flushed and inaccurate, just like Seek()
+ */
+ m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, !bPlus, true, false, false));
+ SynchronizeDemuxer(100);
+ return true;
+ }
+ return false;
+}
+
+void COMXPlayer::GetAudioInfo(CStdString &strAudioInfo)
+{
+ { CSingleLock lock(m_StateSection);
+ strAudioInfo.Format("D(%s)", m_State.demux_audio.c_str());
+ }
+ strAudioInfo.AppendFormat(" P(%s)", m_player_audio.GetPlayerInfo().c_str());
+}
+
+void COMXPlayer::GetVideoInfo(CStdString &strVideoInfo)
+{
+ { CSingleLock lock(m_StateSection);
+ strVideoInfo.Format("D(%s)", m_State.demux_video.c_str());
+ }
+ strVideoInfo.AppendFormat(" P(%s)", m_player_video.GetPlayerInfo().c_str());
+}
+
+void COMXPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
+{
+ if (!m_bStop)
+ {
+ double dDelay = 0;
+
+ double apts = m_player_audio.GetCurrentPTS();
+ double vpts = m_player_video.GetCurrentPTS();
+ double dDiff = 0;
+
+ if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE )
+ dDiff = (apts - vpts) / DVD_TIME_BASE;
+
+ CStdString strEDL;
+ strEDL.AppendFormat(", edl:%s", m_Edl.GetInfo().c_str());
+
+ CStdString strBuf;
+ CSingleLock lock(m_StateSection);
+ if(m_State.cache_bytes >= 0)
+ {
+ strBuf.AppendFormat(" cache:%s %2.0f%%"
+ , StringUtils::SizeToString(m_State.cache_bytes).c_str()
+ , m_State.cache_level * 100);
+ if(m_playSpeed == 0 || m_caching == CACHESTATE_FULL)
+ strBuf.AppendFormat(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay));
+ }
+
+ strGeneralInfo.Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s, omx vb:%8d ad:% 6.3f )"
+ , dDelay
+ , dDiff
+ , strEDL.c_str()
+ , (int)(CThread::GetRelativeUsage()*100)
+ , (int)(m_player_audio.GetRelativeUsage()*100)
+ , (int)(m_player_video.GetRelativeUsage()*100)
+ , strBuf.c_str()
+ , m_player_video.GetFreeSpace()
+ , m_player_audio.GetDelay());
+
+ }
+}
+
+void COMXPlayer::SeekPercentage(float fPercent)
+{
+ int64_t iTotalTime = GetTotalTimeInMsec();
+
+ if (!iTotalTime)
+ return;
+
+ SeekTime((int64_t)(iTotalTime * fPercent / 100));
+}
+
+float COMXPlayer::GetPercentage()
+{
+ int64_t iTotalTime = GetTotalTimeInMsec();
+
+ if (!iTotalTime)
+ return 0.0f;
+
+ return GetTime() * 100 / (float)iTotalTime;
+}
+
+float COMXPlayer::GetCachePercentage()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.cache_offset * 100; // NOTE: Percentage returned is relative
+}
+
+void COMXPlayer::SetAVDelay(float fValue)
+{
+ m_player_video.SetDelay(fValue * DVD_TIME_BASE);
+}
+
+float COMXPlayer::GetAVDelay()
+{
+ return m_player_video.GetDelay() / (float)DVD_TIME_BASE;
+}
+
+void COMXPlayer::SetSubTitleDelay(float fValue)
+{
+ m_player_video.SetSubtitleDelay(-fValue * DVD_TIME_BASE);
+}
+
+float COMXPlayer::GetSubTitleDelay()
+{
+ return -m_player_video.GetSubtitleDelay() / DVD_TIME_BASE;
+}
+
+int COMXPlayer::GetSubtitleCount()
+{
+ OMXStreamLock lock(this);
+ m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
+ return m_SelectionStreams.Count(STREAM_SUBTITLE);
+}
+
+int COMXPlayer::GetSubtitle()
+{
+ return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this);
+}
+
+void COMXPlayer::GetSubtitleName(int iStream, CStdString &strStreamName)
+{
+ strStreamName = "";
+ OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
+ if(s.name.length() > 0)
+ strStreamName = s.name;
+ else
+ strStreamName = g_localizeStrings.Get(13205); // Unknown
+
+ if(s.type == STREAM_NONE)
+ strStreamName += "(Invalid)";
+}
+
+void COMXPlayer::GetSubtitleLanguage(int iStream, CStdString &strStreamLang)
+{
+ OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
+ if (!g_LangCodeExpander.Lookup(strStreamLang, s.language))
+ strStreamLang = g_localizeStrings.Get(13205); // Unknown
+}
+
+void COMXPlayer::SetSubtitle(int iStream)
+{
+ m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream));
+}
+
+bool COMXPlayer::GetSubtitleVisible()
+{
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
+ if(pStream->IsInMenu())
+ return g_settings.m_currentVideoSettings.m_SubtitleOn;
+ else
+ return pStream->IsSubtitleStreamEnabled();
+ }
+
+ return m_player_video.IsSubtitleEnabled();
+}
+
+void COMXPlayer::SetSubtitleVisible(bool bVisible)
+{
+ g_settings.m_currentVideoSettings.m_SubtitleOn = bVisible;
+ m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
+}
+
+int COMXPlayer::GetAudioStreamCount()
+{
+ OMXStreamLock lock(this);
+ m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
+ return m_SelectionStreams.Count(STREAM_AUDIO);
+}
+
+int COMXPlayer::GetAudioStream()
+{
+ return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this);
+}
+
+void COMXPlayer::GetAudioStreamName(int iStream, CStdString &strStreamName)
+{
+ strStreamName = "";
+ OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
+ if(s.name.length() > 0)
+ strStreamName += s.name;
+ else
+ strStreamName += "Unknown";
+
+ if(s.type == STREAM_NONE)
+ strStreamName += " (Invalid)";
+}
+
+void COMXPlayer::SetAudioStream(int iStream)
+{
+ m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
+ SynchronizeDemuxer(100);
+}
+
+void COMXPlayer::SeekTime(int64_t iTime)
+{
+ int seekOffset = (int)(iTime - GetTime());
+ m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true));
+ SynchronizeDemuxer(100);
+ m_callback.OnPlayBackSeek((int)iTime, seekOffset);
+}
+
+// return the time in milliseconds
+int64_t COMXPlayer::GetTime()
+{
+ CSingleLock lock(m_StateSection);
+ double offset = 0;
+ if(m_State.timestamp > 0)
+ {
+ offset = m_av_clock.GetAbsoluteClock() - m_State.timestamp;
+ offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
+ if(offset > 1000) offset = 1000;
+ if(offset < -1000) offset = -1000;
+ }
+ //printf("COMXPlayer::GetTime %Lf offset %Lf %Lf\n", m_State.time, offset, m_av_clock.GetClock());
+ return llrint(m_State.time + DVD_TIME_TO_MSEC(offset));
+}
+
+// return length in msec
+int64_t COMXPlayer::GetTotalTimeInMsec()
+{
+ CSingleLock lock(m_StateSection);
+ return llrint(m_State.time_total);
+}
+
+// return length in seconds.. this should be changed to return in milleseconds throughout xbmc
+int64_t COMXPlayer::GetTotalTime()
+{
+ return GetTotalTimeInMsec();
+}
+
+void COMXPlayer::ToFFRW(int iSpeed)
+{
+ // can't rewind in menu as seeking isn't possible
+ // forward is fine
+ if (iSpeed < 0 && IsInMenu()) return;
+
+ /* only pause and normal playspeeds are allowed */
+ if(iSpeed > 1 || iSpeed < 0)
+ return;
+
+ SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL);
+}
+
+bool COMXPlayer::OpenAudioStream(int iStream, int source)
+{
+ CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source);
+
+ if (!m_pDemuxer)
+ return false;
+
+ CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
+ if (!pStream || pStream->disabled)
+ return false;
+
+ if( m_CurrentAudio.id < 0 && m_CurrentVideo.id >= 0 )
+ {
+ // up until now we wheren't playing audio, but we did play video
+ // this will change what is used to sync the dvdclock.
+ // since the new audio data doesn't have to have any relation
+ // to the current video data in the packet que, we have to
+ // wait for it to empty
+
+ // this happens if a new cell has audio data, but previous didn't
+ // and both have video data
+
+ SynchronizePlayers(SYNCSOURCE_AUDIO);
+ }
+
+ CDVDStreamInfo hint(*pStream, true);
+
+ if(m_CurrentAudio.id < 0
+ || m_CurrentAudio.hint != hint)
+ {
+ if(!m_player_audio.OpenStream(hint))
+ {
+ /* mark stream as disabled, to disallaw further attempts*/
+ CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
+ pStream->disabled = true;
+ pStream->SetDiscard(AVDISCARD_ALL);
+ return false;
+ }
+ m_av_clock.SetSpeed(DVD_PLAYSPEED_NORMAL);
+ m_av_clock.OMXSetSpeed(DVD_PLAYSPEED_NORMAL);
+ }
+ else
+ m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
+
+ /* store information about stream */
+ m_CurrentAudio.id = iStream;
+ m_CurrentAudio.source = source;
+ m_CurrentAudio.hint = hint;
+ m_CurrentAudio.stream = (void*)pStream;
+ m_CurrentAudio.started = false;
+
+ /* we are potentially going to be waiting on this */
+ m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
+
+ /* software decoding normaly consumes full cpu time so prio it */
+ m_player_audio.SetPriority(GetPriority()+1);
+
+ return true;
+}
+
+bool COMXPlayer::OpenVideoStream(int iStream, int source)
+{
+ CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source);
+
+ if (!m_pDemuxer)
+ return false;
+
+ CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
+ if(!pStream || pStream->disabled)
+ return false;
+ pStream->SetDiscard(AVDISCARD_NONE);
+
+ CDVDStreamInfo hint(*pStream, true);
+
+ if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) )
+ {
+ /* set aspect ratio as requested by navigator for dvd's */
+ float aspect = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
+ if(aspect != 0.0)
+ {
+ hint.aspect = aspect;
+ hint.forced_aspect = true;
+ }
+ hint.software = true;
+ }
+
+ CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
+ if(pMenus && pMenus->IsInMenu())
+ hint.stills = true;
+
+ if(m_CurrentVideo.id < 0
+ || m_CurrentVideo.hint != hint)
+ {
+ if(!m_player_video.OpenStream(hint))
+ {
+ /* mark stream as disabled, to disallaw further attempts */
+ CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
+ pStream->disabled = true;
+ pStream->SetDiscard(AVDISCARD_ALL);
+ return false;
+ }
+ m_av_clock.SetSpeed(DVD_PLAYSPEED_NORMAL);
+ m_av_clock.OMXSetSpeed(DVD_PLAYSPEED_NORMAL);
+ }
+ else
+ m_player_video.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
+
+ unsigned flags = 0;
+ if(m_filename.find("3DSBS") != string::npos)
+ flags = CONF_FLAGS_FORMAT_SBS;
+ m_player_video.SetFlags(flags);
+
+ /* store information about stream */
+ m_CurrentVideo.id = iStream;
+ m_CurrentVideo.source = source;
+ m_CurrentVideo.hint = hint;
+ m_CurrentVideo.stream = (void*)pStream;
+ m_CurrentVideo.started = false;
+
+ /* we are potentially going to be waiting on this */
+ m_player_video.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
+
+ /* use same priority for video thread as demuxing thread, as */
+ /* otherwise demuxer will starve if video consumes the full cpu */
+ m_player_video.SetPriority(GetPriority());
+
+ return true;
+}
+
+bool COMXPlayer::OpenSubtitleStream(int iStream, int source)
+{
+ CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source);
+
+ CDemuxStream* pStream = NULL;
+ std::string filename;
+ CDVDStreamInfo hint;
+
+ if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
+ {
+ int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
+ if(index < 0)
+ return false;
+ OMXSelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
+
+ if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
+ {
+ CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
+ auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
+ if(!demux->Open(st.filename, st.filename2))
+ return false;
+ m_pSubtitleDemuxer = demux.release();
+ }
+
+ pStream = m_pSubtitleDemuxer->GetStream(iStream);
+ if(!pStream || pStream->disabled)
+ return false;
+ pStream->SetDiscard(AVDISCARD_NONE);
+ double pts = m_player_video.GetCurrentPTS();
+ if(pts == DVD_NOPTS_VALUE)
+ pts = m_CurrentVideo.dts;
+ if(pts == DVD_NOPTS_VALUE)
+ pts = 0;
+ pts += m_offset_pts;
+ m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
+
+ hint.Assign(*pStream, true);
+ }
+ else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
+ {
+ int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
+ if(index < 0)
+ return false;
+ filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename;
+
+ hint.Clear();
+ hint.fpsscale = m_CurrentVideo.hint.fpsscale;
+ hint.fpsrate = m_CurrentVideo.hint.fpsrate;
+ }
+ else
+ {
+ if(!m_pDemuxer)
+ return false;
+ pStream = m_pDemuxer->GetStream(iStream);
+ if(!pStream || pStream->disabled)
+ return false;
+ pStream->SetDiscard(AVDISCARD_NONE);
+
+ hint.Assign(*pStream, true);
+
+ if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ filename = "dvd";
+ }
+
+ if(m_CurrentSubtitle.id < 0
+ || m_CurrentSubtitle.hint != hint)
+ {
+ if(m_CurrentSubtitle.id >= 0)
+ {
+ CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
+ CloseSubtitleStream(false);
+ }
+
+ if(!m_player_subtitle.OpenStream(hint, filename))
+ {
+ CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
+ if(pStream)
+ {
+ pStream->disabled = true;
+ pStream->SetDiscard(AVDISCARD_ALL);
+ }
+ return false;
+ }
+ }
+ else
+ m_player_subtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
+
+ m_CurrentSubtitle.id = iStream;
+ m_CurrentSubtitle.source = source;
+ m_CurrentSubtitle.hint = hint;
+ m_CurrentSubtitle.stream = (void*)pStream;
+ m_CurrentSubtitle.started = false;
+
+ return true;
+}
+
+bool COMXPlayer::CloseAudioStream(bool bWaitForBuffers)
+{
+ if (m_CurrentAudio.id < 0)
+ return false;
+
+ CLog::Log(LOGNOTICE, "Closing audio stream");
+
+ if(bWaitForBuffers)
+ SetCaching(CACHESTATE_DONE);
+
+ m_player_audio.CloseStream(bWaitForBuffers);
+
+ m_CurrentAudio.Clear();
+ return true;
+}
+
+bool COMXPlayer::CloseVideoStream(bool bWaitForBuffers)
+{
+ if (m_CurrentVideo.id < 0)
+ return false;
+
+ CLog::Log(LOGNOTICE, "Closing video stream");
+
+ if(bWaitForBuffers)
+ SetCaching(CACHESTATE_DONE);
+
+ m_player_video.CloseStream(bWaitForBuffers);
+
+ m_CurrentVideo.Clear();
+ return true;
+}
+
+bool COMXPlayer::CloseSubtitleStream(bool bKeepOverlays)
+{
+ if (m_CurrentSubtitle.id < 0)
+ return false;
+
+ CLog::Log(LOGNOTICE, "Closing subtitle stream");
+
+ m_player_subtitle.CloseStream(!bKeepOverlays);
+
+ m_CurrentSubtitle.Clear();
+ return true;
+}
+
+void COMXPlayer::FlushBuffers(bool queued, double pts, bool accurate)
+{
+ double startpts;
+ if(accurate)
+ startpts = pts;
+ else
+ startpts = DVD_NOPTS_VALUE;
+
+ /* call with demuxer pts */
+ if(startpts != DVD_NOPTS_VALUE)
+ startpts -= m_offset_pts;
+
+ m_CurrentAudio.inited = false;
+ m_CurrentAudio.dts = DVD_NOPTS_VALUE;
+ m_CurrentAudio.startpts = startpts;
+
+ m_CurrentVideo.inited = false;
+ m_CurrentVideo.dts = DVD_NOPTS_VALUE;
+ m_CurrentVideo.startpts = startpts;
+
+ m_CurrentSubtitle.inited = false;
+ m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
+ m_CurrentSubtitle.startpts = startpts;
+
+ m_CurrentTeletext.inited = false;
+ m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
+ m_CurrentTeletext.startpts = startpts;
+
+ if(queued)
+ {
+ m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
+ m_player_video.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
+ m_player_video.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
+ m_player_subtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
+ SynchronizePlayers(SYNCSOURCE_ALL);
+ }
+ else
+ {
+ m_player_video.Flush();
+ m_player_audio.Flush();
+ m_player_subtitle.Flush();
+
+ // clear subtitle and menu overlays
+ m_overlayContainer.Clear();
+
+ if(m_playSpeed == DVD_PLAYSPEED_NORMAL
+ || m_playSpeed == DVD_PLAYSPEED_PAUSE)
+ {
+ // make sure players are properly flushed, should put them in stalled state
+ CDVDMsgGeneralSynchronize* msg = new CDVDMsgGeneralSynchronize(1000, 0);
+ m_player_video.SendMessage(msg->Acquire(), 1);
+ m_player_audio.SendMessage(msg->Acquire(), 1);
+ msg->Wait(&m_bStop, 0);
+ msg->Release();
+
+ // purge any pending PLAYER_STARTED messages
+ m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
+
+ // we should now wait for init cache
+ SetCaching(CACHESTATE_FLUSH);
+ m_CurrentAudio.started = false;
+ m_CurrentVideo.started = false;
+ m_CurrentSubtitle.started = false;
+ m_CurrentTeletext.started = false;
+ }
+
+ if(pts != DVD_NOPTS_VALUE)
+ m_av_clock.Discontinuity(pts);
+ UpdatePlayState(0);
+
+ /*
+ CloseVideoStream(false);
+ if(m_CurrentVideo.id >= 0)
+ OpenVideoStream(m_CurrentVideo.id, m_CurrentVideo.source);
+ */
+ }
+}
+
+// since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
+int COMXPlayer::OnDVDNavResult(void* pData, int iMessage)
+{
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
+ {
+ if(iMessage == 0)
+ m_overlayContainer.Add((CDVDOverlay*)pData);
+ else if(iMessage == 1)
+ m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
+ else if(iMessage == 2)
+ m_dvd.iSelectedAudioStream = *(int*)pData;
+ else if(iMessage == 3)
+ m_dvd.iSelectedSPUStream = *(int*)pData;
+ else if(iMessage == 4)
+ m_player_video.EnableSubtitle(*(int*)pData ? true: false);
+
+ return 0;
+ }
+
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
+
+ switch (iMessage)
+ {
+ case DVDNAV_STILL_FRAME:
+ {
+ //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
+
+ dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData;
+ // should wait the specified time here while we let the player running
+ // after that call dvdnav_still_skip(m_dvdnav);
+
+ if (m_dvd.state != DVDSTATE_STILL)
+ {
+ // else notify the player we have received a still frame
+
+ if(still_event->length < 0xff)
+ m_dvd.iDVDStillTime = still_event->length * 1000;
+ else
+ m_dvd.iDVDStillTime = 0;
+
+ m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
+
+ /* adjust for the output delay in the video queue */
+ DWORD time = 0;
+ if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
+ {
+ time = (DWORD)(m_player_video.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
+ if( time < 10000 && time > 0 )
+ m_dvd.iDVDStillTime += time;
+ }
+ m_dvd.state = DVDSTATE_STILL;
+ CLog::Log(LOGDEBUG,
+ "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
+ still_event->length, time / 1000);
+ }
+ return NAVRESULT_HOLD;
+ }
+ break;
+ case DVDNAV_SPU_CLUT_CHANGE:
+ {
+ m_player_subtitle.SendMessage(new CDVDMsgSubtitleClutChange((BYTE*)pData));
+ }
+ break;
+ case DVDNAV_SPU_STREAM_CHANGE:
+ {
+ dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData;
+
+ int iStream = event->physical_wide;
+ bool visible = !(iStream & 0x80);
+
+ m_player_video.EnableSubtitle(visible);
+
+ if (iStream >= 0)
+ m_dvd.iSelectedSPUStream = (iStream & ~0x80);
+ else
+ m_dvd.iSelectedSPUStream = -1;
+
+ m_CurrentSubtitle.stream = NULL;
+ }
+ break;
+ case DVDNAV_AUDIO_STREAM_CHANGE:
+ {
+ // This should be the correct way i think, however we don't have any streams right now
+ // since the demuxer hasn't started so it doesn't change. not sure how to do this.
+ dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData;
+
+ // Tell system what audiostream should be opened by default
+ if (event->logical >= 0)
+ m_dvd.iSelectedAudioStream = event->physical;
+ else
+ m_dvd.iSelectedAudioStream = -1;
+
+ m_CurrentAudio.stream = NULL;
+ }
+ break;
+ case DVDNAV_HIGHLIGHT:
+ {
+ //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
+ int iButton = pStream->GetCurrentButton();
+ CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton);
+ m_player_subtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
+ }
+ break;
+ case DVDNAV_VTS_CHANGE:
+ {
+ //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
+ CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
+
+ //Make sure we clear all the old overlays here, or else old forced items are left.
+ m_overlayContainer.Clear();
+
+ //Force an aspect ratio that is set in the dvdheaders if available
+ m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio();
+ if( m_player_audio.IsInited() )
+ m_player_video.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
+
+ m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
+ m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
+ }
+ break;
+ case DVDNAV_CELL_CHANGE:
+ {
+ //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
+ CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
+
+ m_dvd.state = DVDSTATE_NORMAL;
+
+ if( m_player_video.IsInited() )
+ m_player_video.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
+ }
+ break;
+ case DVDNAV_NAV_PACKET:
+ {
+ //pci_t* pci = (pci_t*)pData;
+
+ // this should be possible to use to make sure we get
+ // seamless transitions over these boundaries
+ // if we remember the old vobunits boundaries
+ // when a packet comes out of demuxer that has
+ // pts values outside that boundary, it belongs
+ // to the new vobunit, wich has new timestamps
+ UpdatePlayState(0);
+ }
+ break;
+ case DVDNAV_HOP_CHANNEL:
+ {
+ // This event is issued whenever a non-seamless operation has been executed.
+ // Applications with fifos should drop the fifos content to speed up responsiveness.
+ CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
+ if(m_dvd.state == DVDSTATE_SEEK)
+ m_dvd.state = DVDSTATE_NORMAL;
+ else
+ m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
+
+ return NAVRESULT_ERROR;
+ }
+ break;
+ case DVDNAV_STOP:
+ {
+ CLog::Log(LOGDEBUG, "DVDNAV_STOP");
+ m_dvd.state = DVDSTATE_NORMAL;
+ }
+ break;
+ default:
+ {}
+ break;
+ }
+ }
+ return NAVRESULT_NOP;
+}
+
+bool COMXPlayer::OnAction(const CAction &action)
+{
+#define THREAD_ACTION(action) \
+ do { \
+ if (!IsCurrentThread()) { \
+ m_messenger.Put(new CDVDMsgType<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
+ return true; \
+ } \
+ } while(false)
+
+ CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
+ if (pMenus)
+ {
+ if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pMenus->GetTotalButtons() == 0 )
+ {
+ switch(action.GetID())
+ {
+ case ACTION_NEXT_ITEM:
+ case ACTION_MOVE_RIGHT:
+ case ACTION_MOVE_UP:
+ case ACTION_SELECT_ITEM:
+ {
+ THREAD_ACTION(action);
+ /* this will force us out of the stillframe */
+ CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__);
+ m_dvd.iDVDStillStartTime = 0;
+ m_dvd.iDVDStillTime = 1;
+ }
+ return true;
+ }
+ }
+
+
+ switch (action.GetID())
+ {
+/* this code is disabled to allow switching playlist items (dvdimage "stacks") */
+#if 0
+ case ACTION_PREV_ITEM: // SKIP-:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - pushed prev");
+ pMenus->OnPrevious();
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ }
+ break;
+ case ACTION_NEXT_ITEM: // SKIP+:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - pushed next");
+ pMenus->OnNext();
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ }
+ break;
+#endif
+ case ACTION_SHOW_VIDEOMENU: // start button
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - go to menu");
+ pMenus->OnMenu();
+ // send a message to everyone that we've gone to the menu
+ CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
+ g_windowManager.SendMessage(msg);
+ return true;
+ }
+ break;
+ }
+ if (pMenus->IsInMenu())
+ {
+ switch (action.GetID())
+ {
+ case ACTION_NEXT_ITEM:
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
+ pMenus->OnNext();
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ case ACTION_PREV_ITEM:
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
+ pMenus->OnPrevious();
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ case ACTION_PREVIOUS_MENU:
+ case ACTION_NAV_BACK:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - menu back");
+ pMenus->OnBack();
+ }
+ break;
+ case ACTION_MOVE_LEFT:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - move left");
+ pMenus->OnLeft();
+ }
+ break;
+ case ACTION_MOVE_RIGHT:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - move right");
+ pMenus->OnRight();
+ }
+ break;
+ case ACTION_MOVE_UP:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - move up");
+ pMenus->OnUp();
+ }
+ break;
+ case ACTION_MOVE_DOWN:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - move down");
+ pMenus->OnDown();
+ }
+ break;
+
+ case ACTION_MOUSE_MOVE:
+ case ACTION_MOUSE_LEFT_CLICK:
+ {
+ CRect rs, rd;
+ GetVideoRect(rs, rd);
+ CPoint pt(action.GetAmount(), action.GetAmount(1));
+ if (!rd.PtInRect(pt))
+ return false; // out of bounds
+ THREAD_ACTION(action);
+ // convert to video coords...
+ pt -= CPoint(rd.x1, rd.y1);
+ pt.x *= rs.Width() / rd.Width();
+ pt.y *= rs.Height() / rd.Height();
+ pt += CPoint(rs.x1, rs.y1);
+ if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
+ return pMenus->OnMouseClick(pt);
+ return pMenus->OnMouseMove(pt);
+ }
+ break;
+ case ACTION_SELECT_ITEM:
+ {
+ THREAD_ACTION(action);
+ CLog::Log(LOGDEBUG, " - button select");
+ // show button pushed overlay
+ if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ m_player_subtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED);
+
+ pMenus->ActivateButton();
+ }
+ break;
+ case REMOTE_0:
+ case REMOTE_1:
+ case REMOTE_2:
+ case REMOTE_3:
+ case REMOTE_4:
+ case REMOTE_5:
+ case REMOTE_6:
+ case REMOTE_7:
+ case REMOTE_8:
+ case REMOTE_9:
+ {
+ THREAD_ACTION(action);
+ // Offset from key codes back to button number
+ int button = action.GetID() - REMOTE_0;
+ CLog::Log(LOGDEBUG, " - button pressed %d", button);
+ pMenus->SelectButton(button);
+ }
+ break;
+ default:
+ return false;
+ break;
+ }
+ return true; // message is handled
+ }
+ }
+ if (dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream))
+ {
+ switch (action.GetID())
+ {
+ case ACTION_NEXT_ITEM:
+ m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ break;
+
+ case ACTION_PREV_ITEM:
+ m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ break;
+
+ case ACTION_CHANNEL_SWITCH:
+ {
+ // Offset from key codes back to button number
+ int channel = action.GetAmount();
+ m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ }
+ break;
+ }
+ }
+
+ switch (action.GetID())
+ {
+ case ACTION_NEXT_ITEM:
+ if(GetChapterCount() > 0)
+ {
+ m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()+1));
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ }
+ else
+ break;
+ case ACTION_PREV_ITEM:
+ if(GetChapterCount() > 0)
+ {
+ m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()-1));
+ g_infoManager.SetDisplayAfterSeek();
+ return true;
+ }
+ else
+ break;
+ }
+
+ // return false to inform the caller we didn't handle the message
+ return false;
+}
+
+bool COMXPlayer::IsInMenu() const
+{
+ CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
+ if (pStream)
+ {
+ if( m_dvd.state == DVDSTATE_STILL )
+ return true;
+ else
+ return pStream->IsInMenu();
+ }
+ return false;
+}
+
+bool COMXPlayer::HasMenu()
+{
+ CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
+ if (pStream)
+ return true;
+ else
+ return false;
+}
+
+bool COMXPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
+{
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ return false;
+
+ double pts = m_av_clock.OMXMediaTime();
+
+ m_player_subtitle.GetCurrentSubtitle(strSubtitle, pts - m_player_video.GetSubtitleDelay());
+
+ // In case we stalled, don't output any subs
+ if ((m_player_video.IsStalled() && HasVideo()) || (m_player_audio.IsStalled() && HasAudio()))
+ strSubtitle = m_lastSub;
+ else
+ m_lastSub = strSubtitle;
+
+ return !strSubtitle.IsEmpty();
+}
+
+CStdString COMXPlayer::GetPlayerState()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.player_state;
+}
+
+bool COMXPlayer::SetPlayerState(CStdString state)
+{
+ m_messenger.Put(new CDVDMsgPlayerSetState(state));
+ return true;
+}
+
+int COMXPlayer::GetChapterCount()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.chapter_count;
+}
+
+int COMXPlayer::GetChapter()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.chapter;
+}
+
+void COMXPlayer::GetChapterName(CStdString& strChapterName)
+{
+ CSingleLock lock(m_StateSection);
+ strChapterName = m_State.chapter_name;
+}
+
+int COMXPlayer::SeekChapter(int iChapter)
+{
+ if (GetChapterCount() > 0)
+ {
+ if (iChapter < 0)
+ iChapter = 0;
+ if (iChapter > GetChapterCount())
+ return 0;
+
+ // Seek to the chapter.
+ m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter));
+ SynchronizeDemuxer(100);
+ }
+ else
+ {
+ // Do a regular big jump.
+ if (GetChapter() > 0 && iChapter > GetChapter())
+ Seek(true, true);
+ else
+ Seek(false, true);
+ }
+ return 0;
+}
+
+int COMXPlayer::AddSubtitle(const CStdString& strSubPath)
+{
+ return AddSubtitleFile(strSubPath);
+}
+
+int COMXPlayer::GetCacheLevel() const
+{
+ CSingleLock lock(m_StateSection);
+ return (int)(m_State.cache_level * 100);
+}
+
+double COMXPlayer::GetQueueTime()
+{
+ int a = m_player_video.GetLevel();
+ int v = m_player_audio.GetLevel();
+ return max(a, v) * 8000.0 / 100;
+}
+
+int COMXPlayer::GetAudioBitrate()
+{
+ return m_player_audio.GetAudioBitrate();
+}
+
+int COMXPlayer::GetVideoBitrate()
+{
+ return m_player_video.GetVideoBitrate();
+}
+
+int COMXPlayer::GetSourceBitrate()
+{
+ if (m_pInputStream)
+ return (int)m_pInputStream->GetBitstreamStats().GetBitrate();
+
+ return 0;
+}
+
+int COMXPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename, CDemuxStream::EFlags flags)
+{
+ std::string ext = URIUtils::GetExtension(filename);
+ std::string vobsubfile = subfilename;
+ if(ext == ".idx")
+ {
+ if (vobsubfile.empty())
+ vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
+
+ CDVDDemuxVobsub v;
+ if(!v.Open(filename, vobsubfile))
+ return -1;
+ m_SelectionStreams.Update(NULL, &v);
+ int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
+ m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
+ m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile;
+ return index;
+ }
+ if(ext == ".sub")
+ {
+ CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
+ if (XFILE::CFile::Exists(strReplace))
+ return -1;
+ }
+ OMXSelectionStream s;
+ s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
+ s.type = STREAM_SUBTITLE;
+ s.id = 0;
+ s.filename = filename;
+ s.name = URIUtils::GetFileName(filename);
+ s.flags = flags;
+ m_SelectionStreams.Update(s);
+ return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, s.source, s.id);
+}
+
+void COMXPlayer::UpdatePlayState(double timeout)
+{
+ if(m_State.timestamp != 0
+ && m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > m_av_clock.GetAbsoluteClock())
+ return;
+
+ SPlayerState state(m_State);
+
+ if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
+ state.dts = m_CurrentVideo.dts;
+ else if(m_CurrentAudio.dts != DVD_NOPTS_VALUE)
+ state.dts = m_CurrentAudio.dts;
+ else
+ state.dts = m_av_clock.GetClock();
+
+ if(m_pDemuxer)
+ {
+ state.chapter = m_pDemuxer->GetChapter();
+ state.chapter_count = m_pDemuxer->GetChapterCount();
+ m_pDemuxer->GetChapterName(state.chapter_name);
+
+ state.time = DVD_TIME_TO_MSEC(m_av_clock.GetClock() + m_offset_pts);
+ state.time_total = m_pDemuxer->GetStreamLength();
+ }
+
+ if(m_pInputStream)
+ {
+ // override from input stream if needed
+
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+ {
+ state.canrecord = static_cast<CDVDInputStreamTV*>(m_pInputStream)->CanRecord();
+ state.recording = static_cast<CDVDInputStreamTV*>(m_pInputStream)->IsRecording();
+ }
+
+ CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
+ if (pDisplayTime)
+ {
+ state.time = pDisplayTime->GetTime();
+ state.time_total = pDisplayTime->GetTotalTime();
+ }
+
+ if (dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
+ {
+ if(m_dvd.state == DVDSTATE_STILL)
+ {
+ state.time = XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime;
+ state.time_total = m_dvd.iDVDStillTime;
+ }
+ }
+
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+ {
+ if(((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime() > 0)
+ {
+ state.time -= ((CDVDInputStreamTV*)m_pInputStream)->GetStartTime();
+ state.time_total = ((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime();
+ }
+ }
+ }
+
+ if (m_Edl.HasCut())
+ {
+ state.time = m_Edl.RemoveCutTime(llrint(state.time));
+ state.time_total = m_Edl.RemoveCutTime(llrint(state.time_total));
+ }
+
+ state.player_state = "";
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+ {
+ state.time_offset = DVD_MSEC_TO_TIME(state.time) - state.dts;
+ if(!((CDVDInputStreamNavigator*)m_pInputStream)->GetNavigatorState(state.player_state))
+ state.player_state = "";
+ }
+ else
+ state.time_offset = 0;
+
+ if (m_CurrentAudio.id >= 0 && m_pDemuxer)
+ {
+ CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentAudio.id);
+ if (pStream && pStream->type == STREAM_AUDIO)
+ ((CDemuxStreamAudio*)pStream)->GetStreamInfo(state.demux_audio);
+ }
+ else
+ state.demux_audio = "";
+
+ if (m_CurrentVideo.id >= 0 && m_pDemuxer)
+ {
+ CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentVideo.id);
+ if (pStream && pStream->type == STREAM_VIDEO)
+ ((CDemuxStreamVideo*)pStream)->GetStreamInfo(state.demux_video);
+ }
+ else
+ state.demux_video = "";
+
+ double level, delay, offset;
+ if(GetCachingTimes(level, delay, offset))
+ {
+ state.cache_delay = max(0.0, delay);
+ state.cache_level = max(0.0, min(1.0, level));
+ state.cache_offset = offset;
+ }
+ else
+ {
+ state.cache_delay = 0.0;
+ state.cache_level = min(1.0, GetQueueTime() / 8000.0);
+ state.cache_offset = GetQueueTime() / state.time_total;
+ }
+
+ XFILE::SCacheStatus status;
+ if(m_pInputStream && m_pInputStream->GetCacheStatus(&status) && status.forward >=0)
+ {
+ state.cache_bytes = status.forward;
+ if(state.time_total)
+ state.cache_bytes += m_pInputStream->GetLength() * GetQueueTime() / state.time_total;
+ }
+ else
+ state.cache_bytes = 0;
+
+ state.timestamp = m_av_clock.GetAbsoluteClock();
+
+ CSingleLock lock(m_StateSection);
+ m_State = state;
+}
+
+void COMXPlayer::UpdateApplication(double timeout)
+{
+ if(m_UpdateApplication != 0
+ && m_UpdateApplication + DVD_MSEC_TO_TIME(timeout) > m_av_clock.GetAbsoluteClock())
+ return;
+
+ CDVDInputStream::IChannel* pStream = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
+ if(pStream)
+ {
+ CFileItem item(g_application.CurrentFileItem());
+ if(pStream->UpdateItem(item))
+ {
+ g_application.CurrentFileItem() = item;
+ g_infoManager.SetCurrentItem(item);
+ }
+ }
+ m_UpdateApplication = m_av_clock.GetAbsoluteClock();
+}
+
+bool COMXPlayer::CanRecord()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.canrecord;
+}
+
+bool COMXPlayer::IsRecording()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.recording;
+}
+
+bool COMXPlayer::Record(bool bOnOff)
+{
+ if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+ {
+ m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
+ return true;
+ }
+ return false;
+}
+
+int COMXPlayer::GetChannels()
+{
+ if (m_pDemuxer && (m_CurrentAudio.id != -1))
+ {
+ CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStream(m_CurrentAudio.id));
+ if (stream)
+ return stream->iChannels;
+ }
+ return -1;
+}
+
+CStdString COMXPlayer::GetAudioCodecName()
+{
+ CStdString retVal;
+ if (m_pDemuxer && (m_CurrentAudio.id != -1))
+ m_pDemuxer->GetStreamCodecName(m_CurrentAudio.id, retVal);
+ return retVal;
+}
+
+CStdString COMXPlayer::GetVideoCodecName()
+{
+ CStdString retVal;
+ if (m_pDemuxer && (m_CurrentVideo.id != -1))
+ m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal);
+ return retVal;
+}
+
+int COMXPlayer::GetPictureWidth()
+{
+ if (m_pDemuxer && (m_CurrentVideo.id != -1))
+ {
+ CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
+ if (stream)
+ return stream->iWidth;
+ }
+ return 0;
+}
+
+int COMXPlayer::GetPictureHeight()
+{
+ if (m_pDemuxer && (m_CurrentVideo.id != -1))
+ {
+ CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
+ if (stream)
+ return stream->iHeight;
+ }
+ return 0;
+}
+
+bool COMXPlayer::GetStreamDetails(CStreamDetails &details)
+{
+ if (m_pDemuxer)
+ {
+ bool result=CDVDFileInfo::DemuxerToStreamDetails(m_pInputStream, m_pDemuxer, details);
+ if (result && details.GetStreamCount(CStreamDetail::VIDEO) > 0) // this is more correct (dvds in particular)
+ {
+ ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_fAspect = m_CurrentVideo.hint.aspect;
+ ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = GetTotalTime() / 1000;
+ }
+ return result;
+ }
+ else
+ return false;
+}
+
+CStdString COMXPlayer::GetPlayingTitle()
+{
+ return "";
+}
+
+void COMXPlayer::GetVideoRect(CRect& SrcRect, CRect& DestRect)
+{
+ g_renderManager.GetVideoRect(SrcRect, DestRect);
+}
+
+void COMXPlayer::SetVolume(float fVolume)
+{
+ m_current_volume = fVolume;
+ m_change_volume = true;
+}
+
+void COMXPlayer::Update(bool bPauseDrawing)
+{
+ g_renderManager.Update(bPauseDrawing);
+}
+
+void COMXPlayer::GetVideoAspectRatio(float &fAR)
+{
+ fAR = g_renderManager.GetAspectRatio();
+}
+
+#endif
diff --git a/xbmc/cores/omxplayer/OMXPlayer.h b/xbmc/cores/omxplayer/OMXPlayer.h
new file mode 100644
index 0000000000..ca2a0eb873
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXPlayer.h
@@ -0,0 +1,469 @@
+#pragma once
+/*
+ * Copyright (C) 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(TARGET_WINDOWS)
+#include "config.h"
+#define DECLARE_UNUSED(a,b) a __attribute__((unused)) b;
+#endif
+
+#include "FileItem.h"
+#include "cores/IPlayer.h"
+#include "cores/dvdplayer/IDVDPlayer.h"
+#include "dialogs/GUIDialogBusy.h"
+#include "threads/Thread.h"
+#include <semaphore.h>
+
+#include "threads/SingleLock.h"
+
+#include "OMXCore.h"
+#include "OMXClock.h"
+#include "OMXPlayerAudio.h"
+#include "OMXPlayerVideo.h"
+//#include "OMXPlayerSubtitle.h"
+#include "DVDPlayerSubtitle.h"
+
+#include "utils/BitstreamStats.h"
+
+#include "linux/DllBCM.h"
+
+#include "DVDStreamInfo.h"
+#include "DVDInputStreams/DVDInputStream.h"
+#include "DVDInputStreams/DVDFactoryInputStream.h"
+#include "DVDDemuxers/DVDDemuxFFmpeg.h"
+#include "DVDDemuxers/DVDDemuxUtils.h"
+#include "DVDDemuxers/DVDFactoryDemuxer.h"
+#include "DVDMessageQueue.h"
+#include "DVDSubtitles/DVDFactorySubtitle.h"
+#include "Edl.h"
+
+#include <deque>
+
+#define MAX_CHAPTERS 64
+
+#define DVDPLAYER_AUDIO 1
+#define DVDPLAYER_VIDEO 2
+#define DVDPLAYER_SUBTITLE 3
+#define DVDPLAYER_TELETEXT 4
+
+#define DVDSTATE_NORMAL 0x00000001 // normal dvd state
+#define DVDSTATE_STILL 0x00000002 // currently displaying a still frame
+#define DVDSTATE_WAIT 0x00000003 // waiting for demuxer read error
+#define DVDSTATE_SEEK 0x00000004 // we are finishing a seek request
+
+class COMXPlayer;
+class OMXPlayerVideo;
+class OMXPlayerAudio;
+
+class COMXCurrentStream
+{
+public:
+ int id; // demuxerid of current playing stream
+ int source;
+ double dts; // last dts from demuxer, used to find disncontinuities
+ double dur; // last frame expected duration
+ CDVDStreamInfo hint; // stream hints, used to notice stream changes
+ void* stream; // pointer or integer, identifying stream playing. if it changes stream changed
+ bool inited;
+ bool started; // has the player started
+ const StreamType type;
+ const int player;
+ // stuff to handle starting after seek
+ double startpts;
+
+ COMXCurrentStream(StreamType t, int i)
+ : type(t)
+ , player(i)
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ id = -1;
+ source = STREAM_SOURCE_NONE;
+ dts = DVD_NOPTS_VALUE;
+ dur = DVD_NOPTS_VALUE;
+ hint.Clear();
+ stream = NULL;
+ inited = false;
+ started = false;
+ startpts = DVD_NOPTS_VALUE;
+ }
+ double dts_end()
+ {
+ if(dts == DVD_NOPTS_VALUE)
+ return DVD_NOPTS_VALUE;
+ if(dur == DVD_NOPTS_VALUE)
+ return dts;
+ return dts + dur;
+ }
+};
+
+typedef struct
+{
+ StreamType type;
+ int type_index;
+ std::string filename;
+ std::string filename2; // for vobsub subtitles, 2 files are necessary (idx/sub)
+ std::string language;
+ std::string name;
+ CDemuxStream::EFlags flags;
+ int source;
+ int id;
+ std::string codec;
+ int channels;
+} OMXSelectionStream;
+
+typedef std::vector<OMXSelectionStream> OMXSelectionStreams;
+
+class COMXSelectionStreams
+{
+ CCriticalSection m_section;
+ OMXSelectionStream m_invalid;
+public:
+ COMXSelectionStreams()
+ {
+ m_invalid.id = -1;
+ m_invalid.source = STREAM_SOURCE_NONE;
+ m_invalid.type = STREAM_NONE;
+ }
+ std::vector<OMXSelectionStream> m_Streams;
+
+ int IndexOf (StreamType type, int source, int id) const;
+ int IndexOf (StreamType type, COMXPlayer& p) const;
+ int Count (StreamType type) const { return IndexOf(type, STREAM_SOURCE_NONE, -1) + 1; }
+ OMXSelectionStream& Get (StreamType type, int index);
+ bool Get (StreamType type, CDemuxStream::EFlags flag, OMXSelectionStream& out);
+
+ OMXSelectionStreams Get(StreamType type);
+ template<typename Compare> OMXSelectionStreams Get(StreamType type, Compare compare)
+ {
+ OMXSelectionStreams streams = Get(type);
+ std::stable_sort(streams.begin(), streams.end(), compare);
+ return streams;
+ }
+
+ void Clear (StreamType type, StreamSource source);
+ int Source (StreamSource source, std::string filename);
+
+ void Update (OMXSelectionStream& s);
+ void Update (CDVDInputStream* input, CDVDDemux* demuxer);
+};
+
+
+class COMXPlayer : public IPlayer, public CThread, public IDVDPlayer
+{
+public:
+
+ COMXPlayer(IPlayerCallback &callback);
+ virtual ~COMXPlayer();
+
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback) { m_player_audio.RegisterAudioCallback(pCallback); };
+ virtual void UnRegisterAudioCallback() { m_player_audio.UnRegisterAudioCallback(); };
+
+ virtual bool IsValidStream(COMXCurrentStream& stream);
+ virtual bool IsBetterStream(COMXCurrentStream& current, CDemuxStream* stream);
+ virtual bool ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream);
+ virtual bool CloseAudioStream(bool bWaitForBuffers);
+ virtual bool CloseVideoStream(bool bWaitForBuffers);
+ virtual bool CloseSubtitleStream(bool bKeepOverlays);
+ virtual bool OpenAudioStream(int iStream, int source);
+ virtual bool OpenVideoStream(int iStream, int source);
+ virtual bool OpenSubtitleStream(int iStream, int source);
+ virtual void OpenDefaultStreams();
+ virtual bool OpenDemuxStream();
+ virtual bool OpenInputStream();
+ virtual bool CheckPlayerInit(COMXCurrentStream& current, unsigned int source);
+ virtual void UpdateCorrection(DemuxPacket* pkt, double correction);
+ virtual void UpdateTimestamps(COMXCurrentStream& current, DemuxPacket* pPacket);
+ virtual void UpdateLimits(double& minimum, double& maximum, double dts);
+ virtual bool CheckSceneSkip(COMXCurrentStream& current);
+ virtual void CheckAutoSceneSkip();
+ virtual void CheckContinuity(COMXCurrentStream& current, DemuxPacket* pPacket);
+ virtual void ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket);
+ virtual void ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket);
+ virtual void ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket);
+ virtual void ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket);
+ virtual void SynchronizeDemuxer(unsigned int timeout);
+ virtual void SynchronizePlayers(unsigned int sources);
+ virtual void SendPlayerMessage(CDVDMsg* pMsg, unsigned int target);
+ virtual void HandleMessages();
+
+ virtual bool OpenFile(const CFileItem &file, const CPlayerOptions &options);
+ virtual bool QueueNextFile(const CFileItem &file) {return false;}
+ virtual void OnNothingToQueueNotify() {}
+ virtual bool CloseFile();
+ virtual bool IsPlaying() const;
+ virtual void SetPlaySpeed(int speed);
+ int GetPlaySpeed() { return m_playSpeed; }
+ virtual void Pause();
+ virtual bool IsPaused() const;
+ virtual bool HasVideo() const;
+ virtual bool HasAudio() const;
+ virtual bool IsPassthrough() const;
+ virtual bool CanSeek();
+ virtual void Seek(bool bPlus = true, bool bLargeStep = false);
+ virtual bool SeekScene(bool bPlus = true);
+ virtual void SeekPercentage(float fPercent = 0.0f);
+ virtual float GetPercentage();
+ virtual float GetCachePercentage();
+
+ virtual void SetVolume(float fVolume);
+ virtual void SetDynamicRangeCompression(long drc) {}
+ virtual void GetAudioInfo(CStdString &strAudioInfo);
+ virtual void GetVideoInfo(CStdString &strVideoInfo);
+ virtual void GetGeneralInfo(CStdString &strVideoInfo);
+ virtual void Update(bool bPauseDrawing);
+ virtual void GetVideoRect(CRect& SrcRect, CRect& DestRect);
+ virtual void GetVideoAspectRatio(float &fAR);
+ virtual void UpdateApplication(double timeout);
+ virtual bool CanRecord();
+ virtual bool IsRecording();
+ virtual bool Record(bool bOnOff);
+ virtual void SetAVDelay(float fValue = 0.0f);
+ virtual float GetAVDelay();
+
+ virtual void SetSubTitleDelay(float fValue = 0.0f);
+ virtual float GetSubTitleDelay();
+ virtual int GetSubtitleCount();
+ virtual int GetSubtitle();
+ virtual void GetSubtitleName(int iStream, CStdString &strStreamName);
+ virtual void GetSubtitleLanguage(int iStream, CStdString &strStreamLang);
+ virtual void SetSubtitle(int iStream);
+ virtual bool GetSubtitleVisible();
+ virtual void SetSubtitleVisible(bool bVisible);
+ virtual bool GetSubtitleExtension(CStdString &strSubtitleExtension) { return false; }
+ virtual int AddSubtitle(const CStdString& strSubPath);
+
+ virtual int GetAudioStreamCount();
+ virtual int GetAudioStream();
+ virtual void GetAudioStreamName(int iStream, CStdString &strStreamName);
+ virtual void SetAudioStream(int iStream);
+ virtual void GetAudioStreamLanguage(int iStream, CStdString &strLanguage);
+
+ virtual TextCacheStruct_t* GetTeletextCache() {return NULL;};
+ virtual void LoadPage(int p, int sp, unsigned char* buffer) {};
+
+ virtual int GetChapterCount();
+ virtual int GetChapter();
+ virtual void GetChapterName(CStdString& strChapterName);
+ virtual int SeekChapter(int iChapter);
+
+ virtual void SeekTime(int64_t iTime = 0);
+ virtual int64_t GetTotalTimeInMsec();
+ virtual int64_t GetTime();
+ virtual int64_t GetTotalTime();
+ virtual void ToFFRW(int iSpeed = 0);
+ virtual int GetAudioBitrate();
+ virtual int GetVideoBitrate();
+ virtual int GetSourceBitrate();
+ virtual int GetChannels();
+ virtual CStdString GetAudioCodecName();
+ virtual CStdString GetVideoCodecName();
+ virtual int GetPictureWidth();
+ virtual int GetPictureHeight();
+ virtual bool GetStreamDetails(CStreamDetails &details);
+
+ virtual bool IsInMenu() const;
+ virtual bool HasMenu();
+
+ virtual bool GetCurrentSubtitle(CStdString& strSubtitle);
+ //returns a state that is needed for resuming from a specific time
+ virtual CStdString GetPlayerState();
+ virtual bool SetPlayerState(CStdString state);
+
+ virtual CStdString GetPlayingTitle();
+
+ enum ECacheState
+ { CACHESTATE_DONE = 0
+ , CACHESTATE_FULL // player is filling up the demux queue
+ , CACHESTATE_INIT // player is waiting for first packet of each stream
+ , CACHESTATE_PLAY // player is waiting for players to not be stalled
+ , CACHESTATE_FLUSH // temporary state player will choose startup between init or full
+ };
+
+ int m_playSpeed;
+ struct SSpeedState
+ {
+ double lastpts; // holds last display pts during ff/rw operations
+ double lasttime;
+ } m_SpeedState;
+
+ void HandlePlaySpeed();
+ bool GetCachingTimes(double& play_left, double& cache_left, double& file_offset);
+ bool CheckStartCaching(COMXCurrentStream& current);
+ void SetCaching(ECacheState state);
+ double GetQueueTime();
+ virtual bool IsCaching() const { return m_caching == CACHESTATE_FULL; }
+ virtual int GetCacheLevel() const;
+
+ virtual int OnDVDNavResult(void* pData, int iMessage);
+ virtual bool OnAction(const CAction &action);
+protected:
+ friend class COMXSelectionStreams;
+
+ class OMXStreamLock : public CSingleLock
+ {
+ public:
+ inline OMXStreamLock(COMXPlayer* comxplayer) : CSingleLock(comxplayer->m_critStreamSection) {}
+ };
+
+ virtual void OnStartup();
+ virtual void OnExit();
+ bool WaitForPausedThumbJobs(int timeout_ms);
+ virtual void Process();
+
+ CEvent m_ready;
+ std::string m_filename; // holds the actual filename
+ CDVDInputStream *m_pInputStream;
+ CDVDDemux *m_pDemuxer;
+ CDVDDemux* m_pSubtitleDemuxer;
+ COMXSelectionStreams m_SelectionStreams;
+ std::string m_mimetype;
+ COMXCurrentStream m_CurrentAudio;
+ COMXCurrentStream m_CurrentVideo;
+ COMXCurrentStream m_CurrentSubtitle;
+ COMXCurrentStream m_CurrentTeletext;
+
+ struct SDVDInfo
+ {
+ void Clear()
+ {
+ state = DVDSTATE_NORMAL;
+ iSelectedSPUStream = -1;
+ iSelectedAudioStream = -1;
+ iDVDStillTime = 0;
+ iDVDStillStartTime = 0;
+ }
+
+ int state; // current dvdstate
+ unsigned int iDVDStillTime; // total time in ticks we should display the still before continuing
+ unsigned int iDVDStillStartTime; // time in ticks when we started the still
+ int iSelectedSPUStream; // mpeg stream id, or -1 if disabled
+ int iSelectedAudioStream; // mpeg stream id, or -1 if disabled
+ } m_dvd;
+
+ struct SPlayerState
+ {
+ SPlayerState() { Clear(); }
+ void Clear()
+ {
+ timestamp = 0;
+ time = 0;
+ time_total = 0;
+ time_offset = 0;
+ dts = DVD_NOPTS_VALUE;
+ player_state = "";
+ chapter = 0;
+ chapter_name = "";
+ chapter_count = 0;
+ canrecord = false;
+ recording = false;
+ demux_video = "";
+ demux_audio = "";
+ cache_bytes = 0;
+ cache_level = 0.0;
+ cache_delay = 0.0;
+ cache_offset = 0.0;
+ }
+
+ double timestamp; // last time of update
+ double time_offset; // difference between time and pts
+
+ double time; // current playback time
+ double time_total; // total playback time
+ double dts; // last known dts
+
+ std::string player_state; // full player state
+
+ int chapter; // current chapter
+ std::string chapter_name; // name of current chapter
+ int chapter_count;// number of chapter
+
+ bool canrecord; // can input stream record
+ bool recording; // are we currently recording
+
+ std::string demux_video;
+ std::string demux_audio;
+
+ int64_t cache_bytes; // number of bytes current's cached
+ double cache_level; // current estimated required cache level
+ double cache_delay; // time until cache is expected to reach estimated level
+ double cache_offset; // percentage of file ahead of current position
+ } m_State;
+ CCriticalSection m_StateSection;
+
+ CEdl m_Edl;
+
+ struct SEdlAutoSkipMarkers {
+
+ void Clear()
+ {
+ cut = -1;
+ commbreak_start = -1;
+ commbreak_end = -1;
+ seek_to_start = false;
+ mute = false;
+ }
+
+ int cut; // last automatically skipped EDL cut seek position
+ int commbreak_start; // start time of the last commercial break automatically skipped
+ int commbreak_end; // end time of the last commercial break automatically skipped
+ bool seek_to_start; // whether seeking can go back to the start of a previously skipped break
+ bool mute; // whether EDL mute is on
+
+ } m_EdlAutoSkipMarkers;
+
+ int AddSubtitleFile(const std::string& filename, const std::string& subfilename = "", CDemuxStream::EFlags flags = CDemuxStream::FLAG_NONE);
+ virtual void UpdatePlayState(double timeout);
+
+ double m_UpdateApplication;
+
+ void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect);
+
+private:
+ void FlushBuffers(bool queued, double pts = DVD_NOPTS_VALUE, bool accurate = true);
+
+ CCriticalSection m_critStreamSection;
+
+ bool m_paused;
+ bool m_bAbortRequest;
+ CFileItem m_item;
+ CPlayerOptions m_PlayerOptions;
+
+ std::string m_lastSub;
+
+ double m_offset_pts;
+
+ OMXClock m_av_clock;
+ OMXPlayerVideo m_player_video;
+ OMXPlayerAudio m_player_audio;
+ CDVDPlayerSubtitle m_player_subtitle;
+
+ CDVDMessageQueue m_messenger;
+
+ float m_current_volume;
+ bool m_change_volume;
+ bool m_stats;
+ CDVDOverlayContainer m_overlayContainer;
+ ECacheState m_caching;
+};
diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
new file mode 100644
index 0000000000..511ead7e4c
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
@@ -0,0 +1,847 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <iomanip>
+
+#include "FileItem.h"
+#include "linux/XMemUtils.h"
+#include "utils/BitstreamStats.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+
+#include "DVDDemuxers/DVDDemuxUtils.h"
+#include "utils/MathUtils.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "video/VideoReferenceClock.h"
+#include "utils/TimeUtils.h"
+
+#include "OMXPlayer.h"
+
+#include <iostream>
+#include <sstream>
+
+class COMXMsgAudioCodecChange : public CDVDMsg
+{
+public:
+ COMXMsgAudioCodecChange(const CDVDStreamInfo &hints, COMXAudioCodecOMX* codec)
+ : CDVDMsg(GENERAL_STREAMCHANGE)
+ , m_codec(codec)
+ , m_hints(hints)
+ {}
+ ~COMXMsgAudioCodecChange()
+ {
+ delete m_codec;
+ }
+ COMXAudioCodecOMX *m_codec;
+ CDVDStreamInfo m_hints;
+};
+
+OMXPlayerAudio::OMXPlayerAudio(OMXClock *av_clock,
+ CDVDMessageQueue& parent)
+: CThread("COMXPlayerAudio")
+, m_messageQueue("audio")
+, m_messageParent(parent)
+{
+ m_av_clock = av_clock;
+ m_pAudioCodec = NULL;
+ m_speed = DVD_PLAYSPEED_NORMAL;
+ m_started = false;
+ m_stalled = false;
+ m_audioClock = 0;
+ m_buffer_empty = false;
+ m_nChannels = 0;
+ m_DecoderOpen = false;
+ m_freq = CurrentHostFrequency();
+ m_hints_current.Clear();
+
+ m_av_clock->SetMasterClock(false);
+
+ m_messageQueue.SetMaxDataSize(3 * 1024 * 1024);
+ m_messageQueue.SetMaxTimeSize(8.0);
+}
+
+
+OMXPlayerAudio::~OMXPlayerAudio()
+{
+ CloseStream(false);
+
+ m_DllBcmHost.Unload();
+}
+
+bool OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints)
+{
+ /*
+ if(IsRunning())
+ CloseStream(false);
+ */
+
+ if(!m_DllBcmHost.Load())
+ return false;
+
+ COMXAudioCodecOMX *codec = new COMXAudioCodecOMX();
+
+ if(!codec || !codec->Open(hints))
+ {
+ CLog::Log(LOGERROR, "Unsupported audio codec");
+ delete codec; codec = NULL;
+ return false;
+ }
+
+ if(m_messageQueue.IsInited())
+ m_messageQueue.Put(new COMXMsgAudioCodecChange(hints, codec), 0);
+ else
+ {
+ if(!OpenStream(hints, codec))
+ return false;
+ CLog::Log(LOGNOTICE, "Creating audio thread");
+ m_messageQueue.Init();
+ Create();
+ }
+
+ /*
+ if(!OpenStream(hints, codec))
+ return false;
+
+ CLog::Log(LOGNOTICE, "Creating audio thread");
+ m_messageQueue.Init();
+ Create();
+ */
+
+ return true;
+}
+
+bool OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec)
+{
+ SAFE_DELETE(m_pAudioCodec);
+
+ m_hints = hints;
+ m_pAudioCodec = codec;
+
+ if(m_hints.bitspersample == 0)
+ m_hints.bitspersample = 16;
+
+ m_speed = DVD_PLAYSPEED_NORMAL;
+ m_audioClock = 0;
+ m_error = 0;
+ m_errorbuff = 0;
+ m_errorcount = 0;
+ m_integral = 0;
+ m_skipdupcount = 0;
+ m_prevskipped = false;
+ m_syncclock = true;
+ m_hw_decode = false;
+ m_errortime = CurrentHostCounter();
+ m_silence = false;
+ m_started = false;
+ m_flush = false;
+ m_nChannels = 0;
+ m_synctype = SYNC_DISCON;
+ m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
+ m_use_passthrough = (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_HDMI) ? true : false ;
+ m_use_hw_decode = g_advancedSettings.m_omxHWAudioDecode;
+
+ return true /*OpenDecoder()*/;
+}
+
+bool OMXPlayerAudio::CloseStream(bool bWaitForBuffers)
+{
+ // wait until buffers are empty
+ if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
+
+ m_messageQueue.Abort();
+
+ if(IsRunning())
+ StopThread();
+
+ m_messageQueue.End();
+
+ if (m_pAudioCodec)
+ {
+ m_pAudioCodec->Dispose();
+ delete m_pAudioCodec;
+ m_pAudioCodec = NULL;
+ }
+
+ CloseDecoder();
+
+ m_speed = DVD_PLAYSPEED_NORMAL;
+ m_started = false;
+
+ return true;
+}
+
+void OMXPlayerAudio::OnStartup()
+{
+}
+
+void OMXPlayerAudio::OnExit()
+{
+ CLog::Log(LOGNOTICE, "thread end: OMXPlayerAudio::OnExit()");
+}
+
+
+
+void OMXPlayerAudio::HandleSyncError(double duration)
+{
+ double clock = m_av_clock->GetClock();
+ double error = m_audioClock - 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)
+ CLog::Log(LOGDEBUG, "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;
+ }
+
+ /*
+ 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, "COMXPlayerAudio:: 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::CodecChange()
+{
+ unsigned int old_bitrate = m_hints.bitrate;
+ unsigned int new_bitrate = m_hints_current.bitrate;
+
+ if(m_pAudioCodec)
+ {
+ m_hints.channels = m_pAudioCodec->GetChannels();
+ m_hints.samplerate = m_pAudioCodec->GetSampleRate();
+ }
+
+ /* 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;
+
+ if(m_hints_current.codec != m_hints.codec ||
+ m_hints_current.channels != m_hints.channels ||
+ m_hints_current.samplerate != m_hints.samplerate ||
+ m_hints_current.bitspersample != m_hints.bitspersample ||
+ old_bitrate != new_bitrate ||
+ !m_DecoderOpen)
+ {
+ m_hints_current = m_hints;
+ return true;
+ }
+
+ return false;
+}
+
+bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
+{
+ if(!pkt)
+ return false;
+
+ /* last decoder reinit went wrong */
+ if(!m_pAudioCodec)
+ return true;
+
+ if(pkt->dts != DVD_NOPTS_VALUE)
+ m_audioClock = pkt->dts;
+
+ const uint8_t *data_dec = pkt->pData;
+ int data_len = pkt->iSize;
+
+ if(!OMX_IS_RAW(m_format.m_dataFormat))
+ {
+ while(!m_bStop && 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;
+
+ m_audioStats.AddSampleBytes(decoded_size);
+
+ if(CodecChange())
+ {
+ CloseDecoder();
+
+ m_DecoderOpen = OpenDecoder();
+ if(!m_DecoderOpen)
+ return false;
+ }
+
+ while(!m_bStop)
+ {
+ if(m_flush)
+ {
+ m_flush = false;
+ break;
+ }
+
+ if(m_omxAudio.GetSpace() < (unsigned int)pkt->iSize)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ if(!bDropPacket)
+ {
+ // Zero out the frame data if we are supposed to silence the audio
+ if(m_silence)
+ memset(decoded, 0x0, decoded_size);
+
+ ret = m_omxAudio.AddPackets(decoded, decoded_size, m_audioClock, m_audioClock);
+
+ if(ret != decoded_size)
+ {
+ CLog::Log(LOGERROR, "error ret %d decoded_size %d\n", ret, decoded_size);
+ }
+ }
+
+ int n = (m_nChannels * m_hints.bitspersample * m_hints.samplerate)>>3;
+ if (n > 0)
+ m_audioClock += ((double)decoded_size * DVD_TIME_BASE) / n;
+
+ if(m_speed == DVD_PLAYSPEED_NORMAL)
+ HandleSyncError((((double)decoded_size * DVD_TIME_BASE) / n));
+ break;
+
+ }
+ }
+ }
+ else
+ {
+ if(CodecChange())
+ {
+ CloseDecoder();
+
+ m_DecoderOpen = OpenDecoder();
+ if(!m_DecoderOpen)
+ return false;
+ }
+
+ while(!m_bStop)
+ {
+ if(m_flush)
+ {
+ m_flush = false;
+ break;
+ }
+
+ if(m_omxAudio.GetSpace() < (unsigned int)pkt->iSize)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ if(!bDropPacket)
+ {
+ if(m_silence)
+ memset(pkt->pData, 0x0, pkt->iSize);
+
+ m_omxAudio.AddPackets(pkt->pData, pkt->iSize, m_audioClock, m_audioClock);
+ }
+
+ if(m_speed == DVD_PLAYSPEED_NORMAL)
+ HandleSyncError(0);
+
+ m_audioStats.AddSampleBytes(pkt->iSize);
+
+ break;
+ }
+ }
+
+ if(bDropPacket)
+ m_stalled = false;
+
+ if(m_omxAudio.GetDelay() < 0.1)
+ m_stalled = true;
+
+ // signal to our parent that we have initialized
+ if(m_started == false)
+ {
+ m_started = true;
+ m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO));
+ }
+
+ if(!bDropPacket && m_speed == DVD_PLAYSPEED_NORMAL)
+ {
+ if(GetDelay() < 0.1f && !m_av_clock->OMXAudioBuffer())
+ {
+ clock_gettime(CLOCK_REALTIME, &m_starttime);
+ m_av_clock->OMXAudioBufferStart();
+ }
+ else if(GetDelay() > (AUDIO_BUFFER_SECONDS * 0.75f) && m_av_clock->OMXAudioBuffer())
+ {
+ m_av_clock->OMXAudioBufferStop();
+ }
+ else if(m_av_clock->OMXAudioBuffer())
+ {
+ clock_gettime(CLOCK_REALTIME, &m_endtime);
+ if((m_endtime.tv_sec - m_starttime.tv_sec) > 1)
+ {
+ m_av_clock->OMXAudioBufferStop();
+ }
+ }
+ }
+
+ return true;
+}
+
+void OMXPlayerAudio::Process()
+{
+ m_audioStats.Start();
+
+ while(!m_bStop)
+ {
+ CDVDMsg* pMsg;
+ int priority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
+ int timeout = 1000;
+
+ MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority);
+
+ if (ret == MSGQ_TIMEOUT)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
+ {
+ DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
+ bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
+
+ if(Decode(pPacket, m_speed > DVD_PLAYSPEED_NORMAL || m_speed < 0 || bPacketDrop))
+ {
+ if (m_stalled && (m_omxAudio.GetDelay() > (AUDIO_BUFFER_SECONDS * 0.75f)))
+ {
+ CLog::Log(LOGINFO, "COMXPlayerAudio - Switching to normal playback");
+ m_stalled = false;
+ }
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
+ {
+ if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait( 100, SYNCSOURCE_AUDIO ))
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE");
+ else
+ m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
+ { //player asked us to set internal clock
+ CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
+
+ if (pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
+ m_audioClock = pMsgGeneralResync->m_timestamp;
+
+ if (pMsgGeneralResync->m_clock)
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 1)", m_audioClock);
+ m_av_clock->Discontinuity(m_audioClock);
+ //m_av_clock->OMXUpdateClock(m_audioClock);
+ }
+ else
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 0)", m_audioClock);
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
+ {
+ if (m_pAudioCodec)
+ m_pAudioCodec->Reset();
+ m_started = false;
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_FLUSH");
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ m_omxAudio.Flush();
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ m_syncclock = true;
+ m_stalled = true;
+ m_started = false;
+
+ if (m_pAudioCodec)
+ m_pAudioCodec->Reset();
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
+ {
+ if(m_started)
+ m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO));
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_EOF))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_EOF");
+ WaitCompletion();
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
+ {
+ if (m_speed != DVD_PLAYSPEED_PAUSE)
+ {
+ double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
+
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_DELAY(%f)", timeout);
+
+ timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed);
+ timeout += m_av_clock->GetAbsoluteClock();
+
+ while(!m_bStop && m_av_clock->GetAbsoluteClock() < timeout)
+ Sleep(1);
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::PLAYER_SETSPEED");
+ m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
+ if (m_speed != DVD_PLAYSPEED_NORMAL)
+ {
+ m_syncclock = true;
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE))
+ {
+ m_silence = static_cast<CDVDMsgBool*>(pMsg)->m_value;
+ if (m_silence)
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 1)", m_audioClock);
+ else
+ CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 0)", m_audioClock);
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
+ {
+ COMXMsgAudioCodecChange* msg(static_cast<COMXMsgAudioCodecChange*>(pMsg));
+ OpenStream(msg->m_hints, msg->m_codec);
+ msg->m_codec = NULL;
+ }
+
+ pMsg->Release();
+ }
+}
+
+void OMXPlayerAudio::Flush()
+{
+ m_flush = true;
+ m_messageQueue.Flush();
+ m_messageQueue.Put( new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1);
+}
+
+void OMXPlayerAudio::WaitForBuffers()
+{
+ // make sure there are no more packets available
+ m_messageQueue.WaitUntilEmpty();
+
+ // make sure almost all has been rendered
+ // leave 500ms to avound buffer underruns
+ double delay = GetCacheTime();
+ if(delay > 0.5)
+ Sleep((int)(1000 * (delay - 0.5)));
+}
+
+bool OMXPlayerAudio::Passthrough() const
+{
+ return m_passthrough;
+}
+
+AEDataFormat OMXPlayerAudio::GetDataFormat(CDVDStreamInfo hints)
+{
+ AEDataFormat dataFormat = AE_FMT_S16NE;
+ bool hdmi_passthrough_dts = false;
+ bool hdmi_passthrough_ac3 = false;
+
+ if (m_DllBcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eAC3, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0)
+ hdmi_passthrough_ac3 = true;
+ if (m_DllBcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eDTS, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0)
+ hdmi_passthrough_dts = true;
+ //printf("Audio support AC3=%d, DTS=%d\n", hdmi_passthrough_ac3, hdmi_passthrough_dts);
+
+ m_passthrough = false;
+ m_hw_decode = false;
+
+ /* check our audio capabilties */
+
+ /* pathrought is overriding hw decode*/
+ if(AUDIO_IS_BITSTREAM(g_guiSettings.GetInt("audiooutput.mode")) && m_use_passthrough)
+ {
+ if(hints.codec == CODEC_ID_AC3 && g_guiSettings.GetBool("audiooutput.ac3passthrough") && hdmi_passthrough_ac3)
+ {
+ dataFormat = AE_FMT_AC3;
+ m_passthrough = true;
+ }
+ if(hints.codec == CODEC_ID_DTS && g_guiSettings.GetBool("audiooutput.dtspassthrough") && hdmi_passthrough_dts)
+ {
+ dataFormat = AE_FMT_DTS;
+ m_passthrough = true;
+ }
+ }
+
+ /* hw decode */
+ if(m_use_hw_decode && !m_passthrough)
+ {
+ if(hints.codec == CODEC_ID_AC3 && COMXAudio::CanHWDecode(m_hints.codec))
+ {
+ dataFormat = AE_FMT_AC3;
+ m_hw_decode = true;
+ }
+ if(hints.codec == CODEC_ID_DTS && COMXAudio::CanHWDecode(m_hints.codec))
+ {
+ dataFormat = AE_FMT_DTS;
+ m_hw_decode = true;
+ }
+ }
+
+ /* software path */
+ if(!m_passthrough && !m_hw_decode)
+ {
+ /* 6 channel have to be mapped to 8 for PCM */
+ if(m_nChannels > 4)
+ m_nChannels = 8;
+ dataFormat = AE_FMT_S16NE;
+ }
+
+ return dataFormat;
+}
+
+bool OMXPlayerAudio::OpenDecoder()
+{
+ bool bAudioRenderOpen = false;
+
+ m_nChannels = m_hints.channels;
+ m_passthrough = false;
+ m_hw_decode = false;
+
+ m_omxAudio.SetClock(m_av_clock);
+
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ m_av_clock->HasAudio(false);
+
+ /* setup audi format for audio render */
+ m_format.m_sampleRate = m_hints.samplerate;
+ m_format.m_channelLayout = m_pAudioCodec->GetChannelMap();
+ /* GetDataFormat is setting up evrything */
+ m_format.m_dataFormat = GetDataFormat(m_hints);
+
+ std::string device = "";
+
+ if(g_guiSettings.GetInt("audiooutput.mode") == AUDIO_HDMI)
+ device = "hdmi";
+ else
+ device = "local";
+
+ bAudioRenderOpen = m_omxAudio.Initialize(m_format, device, m_av_clock, m_hints, m_passthrough, m_hw_decode);
+
+ m_codec_name = "";
+
+ if(!bAudioRenderOpen)
+ {
+ CLog::Log(LOGERROR, "OMXPlayerAudio : Error open audio output");
+ m_av_clock->HasAudio(false);
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ return false;
+ }
+ else
+ {
+ CLog::Log(LOGINFO, "Audio codec %s channels %d samplerate %d bitspersample %d\n",
+ m_codec_name.c_str(), m_nChannels, m_hints.samplerate, m_hints.bitspersample);
+ }
+
+ m_av_clock->HasAudio(true);
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ return true;
+}
+
+void OMXPlayerAudio::CloseDecoder()
+{
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ m_omxAudio.Deinitialize();
+ m_av_clock->HasAudio(false);
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+
+ m_DecoderOpen = false;
+}
+
+double OMXPlayerAudio::GetDelay()
+{
+ return m_omxAudio.GetDelay();
+}
+
+double OMXPlayerAudio::GetCacheTime()
+{
+ return m_omxAudio.GetCacheTime();
+}
+
+void OMXPlayerAudio::WaitCompletion()
+{
+ m_omxAudio.WaitCompletion();
+}
+
+void OMXPlayerAudio::RegisterAudioCallback(IAudioCallback *pCallback)
+{
+ m_omxAudio.RegisterAudioCallback(pCallback);
+}
+
+void OMXPlayerAudio::UnRegisterAudioCallback()
+{
+ m_omxAudio.UnRegisterAudioCallback();
+}
+
+void OMXPlayerAudio::DoAudioWork()
+{
+ m_omxAudio.DoAudioWork();
+}
+
+void OMXPlayerAudio::SetCurrentVolume(float fVolume)
+{
+ m_omxAudio.SetCurrentVolume(fVolume);
+}
+
+void OMXPlayerAudio::SetSpeed(int speed)
+{
+ if(m_messageQueue.IsInited())
+ m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
+ else
+ m_speed = speed;
+}
+
+int OMXPlayerAudio::GetAudioBitrate()
+{
+ return (int)m_audioStats.GetBitrate();
+}
+
+std::string OMXPlayerAudio::GetPlayerInfo()
+{
+ std::ostringstream s;
+ s << "aq:" << setw(2) << min(99,m_messageQueue.GetLevel() + MathUtils::round_int(100.0/8.0*GetCacheTime())) << "%";
+ s << ", kB/s:" << fixed << setprecision(2) << (double)GetAudioBitrate() / 1024.0;
+
+ return s.str();
+}
diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.h b/xbmc/cores/omxplayer/OMXPlayerAudio.h
new file mode 100644
index 0000000000..c2dec91f33
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXPlayerAudio.h
@@ -0,0 +1,130 @@
+/*
+ * 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 "OMXClock.h"
+#include "DVDStreamInfo.h"
+#include "OMXAudio.h"
+#include "OMXAudioCodecOMX.h"
+#include "threads/Thread.h"
+
+#include <deque>
+#include <sys/types.h>
+
+#include "DVDDemuxers/DVDDemux.h"
+#include "DVDMessageQueue.h"
+#include "utils/BitstreamStats.h"
+#include "xbmc/linux/DllBCM.h"
+
+using namespace std;
+
+class OMXPlayerAudio : public CThread
+{
+protected:
+ CDVDMessageQueue m_messageQueue;
+ CDVDMessageQueue &m_messageParent;
+
+ CDVDStreamInfo m_hints_current;
+ CDVDStreamInfo m_hints;
+ OMXClock *m_av_clock;
+ COMXAudio m_omxAudio;
+ std::string m_codec_name;
+ bool m_use_passthrough;
+ bool m_passthrough;
+ bool m_use_hw_decode;
+ bool m_hw_decode;
+ AEAudioFormat m_format;
+ CAEChannelInfo m_channelLayout;
+ COMXAudioCodecOMX *m_pAudioCodec;
+ unsigned int m_speed;
+ bool m_silence;
+ double m_audioClock;
+ 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 m_errorbuff; //place to store average errors
+ int m_errorcount;//number of errors stored
+ bool m_syncclock;
+
+ double m_integral; //integral correction for resampler
+ int m_skipdupcount; //counter for skip/duplicate synctype
+ bool m_prevskipped;
+
+ bool m_stalled;
+ bool m_started;
+
+ BitstreamStats m_audioStats;
+
+ struct timespec m_starttime, m_endtime;
+ bool m_buffer_empty;
+ bool m_flush;
+ //SYNC_DISCON, SYNC_SKIPDUP, SYNC_RESAMPLE
+ int m_synctype;
+ int m_nChannels;
+ bool m_DecoderOpen;
+
+ DllBcmHost m_DllBcmHost;
+
+ virtual void OnStartup();
+ virtual void OnExit();
+ virtual void Process();
+private:
+public:
+ OMXPlayerAudio(OMXClock *av_clock, CDVDMessageQueue& parent);
+ ~OMXPlayerAudio();
+ bool OpenStream(CDVDStreamInfo &hints);
+ bool OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec);
+ void SendMessage(CDVDMsg* pMsg, int priority = 0) { m_messageQueue.Put(pMsg, priority); }
+ bool AcceptsData() const { return !m_messageQueue.IsFull(); }
+ bool HasData() const { return m_messageQueue.GetDataSize() > 0; }
+ bool IsInited() const { return m_messageQueue.IsInited(); }
+ int GetLevel() const { return m_messageQueue.GetLevel(); }
+ bool IsStalled() { return m_stalled; }
+ void WaitForBuffers();
+ bool CloseStream(bool bWaitForBuffers);
+ bool CodecChange();
+ bool Decode(DemuxPacket *pkt, bool bDropPacket);
+ void Flush();
+ bool AddPacket(DemuxPacket *pkt);
+ AEDataFormat GetDataFormat(CDVDStreamInfo hints);
+ bool Passthrough() const;
+ bool OpenDecoder();
+ void CloseDecoder();
+ double GetDelay();
+ double GetCacheTime();
+ double GetCurrentPTS() { return m_audioClock; };
+ void WaitCompletion();
+ void RegisterAudioCallback(IAudioCallback* pCallback);
+ void UnRegisterAudioCallback();
+ void DoAudioWork();
+ void SetCurrentVolume(float fVolume);
+ void SetSpeed(int iSpeed);
+ int GetAudioBitrate();
+ std::string GetPlayerInfo();
+};
+#endif
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
new file mode 100644
index 0000000000..b0bc571dc6
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -0,0 +1,816 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <iomanip>
+
+#include "FileItem.h"
+#include "linux/XMemUtils.h"
+#include "utils/BitstreamStats.h"
+
+#include "DVDDemuxers/DVDDemuxUtils.h"
+#include "DVDCodecs/DVDCodecUtils.h"
+#include "windowing/WindowingFactory.h"
+#include "DVDOverlayRenderer.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "cores/VideoRenderers/RenderFormats.h"
+#include "cores/VideoRenderers/RenderFlags.h"
+
+#include "OMXPlayer.h"
+
+#include <iostream>
+#include <sstream>
+
+class COMXMsgAudioCodecChange : public CDVDMsg
+{
+public:
+ COMXMsgAudioCodecChange(const CDVDStreamInfo &hints, COMXVideo *codec)
+ : CDVDMsg(GENERAL_STREAMCHANGE)
+ , m_codec(codec)
+ , m_hints(hints)
+ {}
+ ~COMXMsgAudioCodecChange()
+ {
+ delete m_codec;
+ }
+ COMXVideo *m_codec;
+ CDVDStreamInfo m_hints;
+};
+
+OMXPlayerVideo::OMXPlayerVideo(OMXClock *av_clock,
+ CDVDOverlayContainer* pOverlayContainer,
+ CDVDMessageQueue& parent)
+: CThread("COMXPlayerVideo")
+, m_messageQueue("video")
+, m_messageParent(parent)
+{
+ m_av_clock = av_clock;
+ m_pOverlayContainer = pOverlayContainer;
+ m_pTempOverlayPicture = NULL;
+ m_open = false;
+ m_stream_id = -1;
+ m_fFrameRate = 25.0f;
+ m_flush = false;
+ m_hdmi_clock_sync = false;
+ m_iVideoDelay = 0;
+ m_speed = DVD_PLAYSPEED_NORMAL;
+ m_stalled = false;
+ m_codecname = "";
+ m_iSubtitleDelay = 0;
+ m_bRenderSubs = false;
+ m_width = 0;
+ m_height = 0;
+ m_fps = 0.0f;
+ m_flags = 0;
+ m_bAllowFullscreen = false;
+ m_iCurrentPts = DVD_NOPTS_VALUE;
+ m_fFrameRate = 25.0f;
+ m_iVideoDelay = 0;
+ m_messageQueue.SetMaxDataSize(10 * 1024 * 1024);
+ m_messageQueue.SetMaxTimeSize(8.0);
+
+ RESOLUTION res = g_graphicsContext.GetVideoResolution();
+ m_video_width = g_settings.m_ResInfo[res].iWidth;
+ m_video_height = g_settings.m_ResInfo[res].iHeight;
+
+ m_dst_rect.SetRect(0, 0, 0, 0);
+
+}
+
+OMXPlayerVideo::~OMXPlayerVideo()
+{
+ CloseStream(false);
+}
+
+bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints)
+{
+ /*
+ if(IsRunning())
+ CloseStream(false);
+ */
+
+ m_hints = hints;
+ m_Deinterlace = ( g_settings.m_currentVideoSettings.m_DeinterlaceMode == VS_DEINTERLACEMODE_OFF ) ? false : true;
+ m_flush = false;
+ m_hdmi_clock_sync = g_guiSettings.GetBool("videoplayer.adjustrefreshrate");
+ m_started = false;
+ m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
+ m_autosync = 1;
+
+ m_audio_count = m_av_clock->HasAudio();
+
+ if (!m_DllBcmHost.Load())
+ return false;
+
+ if(!OpenDecoder())
+ {
+ return false;
+ }
+
+ if(m_messageQueue.IsInited())
+ m_messageQueue.Put(new COMXMsgAudioCodecChange(hints, NULL), 0);
+ else
+ {
+ if(!OpenStream(hints, NULL))
+ return false;
+ CLog::Log(LOGNOTICE, "Creating video thread");
+ m_messageQueue.Init();
+ Create();
+ }
+
+ /*
+ if(!OpenStream(hints, NULL))
+ return false;
+
+ CLog::Log(LOGNOTICE, "Creating video thread");
+ m_messageQueue.Init();
+ Create();
+ */
+
+ m_open = true;
+
+ return true;
+}
+
+bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints, COMXVideo *codec)
+{
+ return true;
+}
+
+bool OMXPlayerVideo::CloseStream(bool bWaitForBuffers)
+{
+ m_flush = true;
+
+ // wait until buffers are empty
+ if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
+
+ m_messageQueue.Abort();
+
+ if(IsRunning())
+ StopThread();
+
+ m_messageQueue.End();
+
+ m_open = false;
+ m_stream_id = -1;
+ m_speed = DVD_PLAYSPEED_NORMAL;
+ m_started = false;
+
+ if (m_pTempOverlayPicture)
+ {
+ CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
+ m_pTempOverlayPicture = NULL;
+ }
+
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ m_omxVideo.Close();
+ m_av_clock->HasVideo(false);
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+
+ if(m_DllBcmHost.IsLoaded())
+ m_DllBcmHost.Unload();
+
+ return true;
+}
+
+void OMXPlayerVideo::OnStartup()
+{
+ m_iCurrentPts = DVD_NOPTS_VALUE;
+ m_FlipTimeStamp = m_av_clock->GetAbsoluteClock();
+}
+
+void OMXPlayerVideo::OnExit()
+{
+ CLog::Log(LOGNOTICE, "thread end: video_thread");
+}
+
+void OMXPlayerVideo::ProcessOverlays(int iGroupId, double pts)
+{
+ // remove any overlays that are out of time
+ if (m_started)
+ m_pOverlayContainer->CleanUp(pts - m_iSubtitleDelay);
+
+ enum EOverlay
+ { OVERLAY_AUTO // select mode auto
+ , OVERLAY_GPU // render osd using gpu
+ , OVERLAY_BUF // render osd on buffer
+ } render = OVERLAY_AUTO;
+
+ /*
+ if(m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SPU)
+ || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_IMAGE)
+ || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SSA) )
+ render = OVERLAY_BUF;
+ */
+
+ if(render == OVERLAY_BUF)
+ {
+ // rendering spu overlay types directly on video memory costs a lot of processing power.
+ // thus we allocate a temp picture, copy the original to it (needed because the same picture can be used more than once).
+ // then do all the rendering on that temp picture and finaly copy it to video memory.
+ // In almost all cases this is 5 or more times faster!.
+
+ if(m_pTempOverlayPicture && ( m_pTempOverlayPicture->iWidth != m_width
+ || m_pTempOverlayPicture->iHeight != m_height))
+ {
+ CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
+ m_pTempOverlayPicture = NULL;
+ }
+
+ if(!m_pTempOverlayPicture)
+ m_pTempOverlayPicture = CDVDCodecUtils::AllocatePicture(m_width, m_height);
+ if(!m_pTempOverlayPicture)
+ return;
+ m_pTempOverlayPicture->format = RENDER_FMT_YUV420P;
+ }
+
+ if(render == OVERLAY_AUTO)
+ render = OVERLAY_GPU;
+
+ VecOverlays overlays;
+
+ {
+ CSingleLock lock(*m_pOverlayContainer);
+
+ VecOverlays* pVecOverlays = m_pOverlayContainer->GetOverlays();
+ VecOverlaysIter it = pVecOverlays->begin();
+
+ //Check all overlays and render those that should be rendered, based on time and forced
+ //Both forced and subs should check timeing, pts == 0 in the stillframe case
+ while (it != pVecOverlays->end())
+ {
+ CDVDOverlay* pOverlay = *it++;
+ if(!pOverlay->bForced && !m_bRenderSubs)
+ continue;
+
+ if(pOverlay->iGroupId != iGroupId)
+ continue;
+
+ double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay;
+
+ if((pOverlay->iPTSStartTime <= pts2 && (pOverlay->iPTSStopTime > pts2 || pOverlay->iPTSStopTime == 0LL)) || pts == 0)
+ {
+ if(pOverlay->IsOverlayType(DVDOVERLAY_TYPE_GROUP))
+ overlays.insert(overlays.end(), static_cast<CDVDOverlayGroup*>(pOverlay)->m_overlays.begin()
+ , static_cast<CDVDOverlayGroup*>(pOverlay)->m_overlays.end());
+ else
+ overlays.push_back(pOverlay);
+
+ }
+ }
+
+ for(it = overlays.begin(); it != overlays.end(); ++it)
+ {
+ double pts2 = (*it)->bForced ? pts : pts - m_iSubtitleDelay;
+
+ if (render == OVERLAY_GPU)
+ g_renderManager.AddOverlay(*it, pts2);
+
+ /*
+ printf("subtitle : DVDOVERLAY_TYPE_SPU %d DVDOVERLAY_TYPE_IMAGE %d DVDOVERLAY_TYPE_SSA %d\n",
+ m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SPU),
+ m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_IMAGE),
+ m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SSA) );
+ */
+
+ if (render == OVERLAY_BUF)
+ CDVDOverlayRenderer::Render(m_pTempOverlayPicture, *it, pts2);
+ }
+ }
+}
+
+void OMXPlayerVideo::Output(int iGroupId, double pts, bool bDropPacket)
+{
+
+ if (!g_renderManager.IsConfigured()
+ || m_video_width != m_width
+ || m_video_height != m_height
+ || m_fps != m_fFrameRate)
+ {
+ m_width = m_video_width;
+ m_height = m_video_height;
+ m_fps = m_fFrameRate;
+
+ unsigned flags = 0;
+ ERenderFormat format = RENDER_FMT_BYPASS;
+
+ if(m_bAllowFullscreen)
+ {
+ flags |= CONF_FLAGS_FULLSCREEN;
+ m_bAllowFullscreen = false; // only allow on first configure
+ }
+
+ if(m_flags & CONF_FLAGS_FORMAT_SBS)
+ {
+ if(g_Windowing.Support3D(m_video_width, m_video_height, D3DPRESENTFLAG_MODE3DSBS))
+ {
+ CLog::Log(LOGNOTICE, "3DSBS movie found");
+ flags |= CONF_FLAGS_FORMAT_SBS;
+ }
+ }
+
+ CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: BYPASS",
+ __FUNCTION__, m_width, m_height, m_fps);
+
+ if(!g_renderManager.Configure(m_video_width, m_video_height,
+ m_video_width, m_video_height, m_fps, flags, format, 0,
+ m_hints.orientation))
+ {
+ CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__);
+ return;
+ }
+
+ g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack);
+ }
+
+ if (!g_renderManager.IsStarted()) {
+ CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__);
+ return;
+ }
+
+ // calculate the time we need to delay this picture before displaying
+ 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 = (double)DVD_TIME_BASE / m_fFrameRate; //pPacket->duration;
+
+ // 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));
+
+ 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;
+
+ if( m_speed < 0 )
+ {
+ if( iClockSleep < -DVD_MSEC_TO_TIME(200))
+ return;
+ }
+
+ if(bDropPacket)
+ return;
+
+#if 0
+ if( m_speed != DVD_PLAYSPEED_NORMAL)
+ {
+ // calculate frame dropping pattern to render at this speed
+ // we do that by deciding if this or next frame is closest
+ // to the flip timestamp
+ double current = fabs(m_dropbase - m_droptime);
+ double next = fabs(m_dropbase - (m_droptime + iFrameDuration));
+ double frametime = (double)DVD_TIME_BASE / m_fFrameRate;
+
+ m_droptime += iFrameDuration;
+#ifndef PROFILE
+ if( next < current /*&& !(pPicture->iFlags & DVP_FLAG_NOSKIP) */)
+ return /*result | EOS_DROPPED*/;
+#endif
+
+ while(!m_bStop && m_dropbase < m_droptime) m_dropbase += frametime;
+ while(!m_bStop && m_dropbase - frametime > m_droptime) m_dropbase -= frametime;
+ }
+ else
+ {
+ m_droptime = 0.0f;
+ m_dropbase = 0.0f;
+ }
+#else
+ m_droptime = 0.0f;
+ m_dropbase = 0.0f;
+#endif
+
+ double pts_media = m_av_clock->OMXMediaTime();
+ ProcessOverlays(iGroupId, pts_media);
+
+ while(!CThread::m_bStop && m_av_clock->GetAbsoluteClock(false) < (iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500)) )
+ Sleep(1);
+
+ g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, FS_NONE);
+
+ //m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime));
+}
+
+void OMXPlayerVideo::Process()
+{
+ double pts = 0;
+ double frametime = (double)DVD_TIME_BASE / m_fFrameRate;
+ bool bRequestDrop = false;
+
+ m_videoStats.Start();
+
+ while(!m_bStop)
+ {
+ CDVDMsg* pMsg;
+ int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000;
+ int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
+ MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority);
+
+ if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT)
+ {
+ CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true");
+ break;
+ }
+ else if (ret == MSGQ_TIMEOUT)
+ {
+ // if we only wanted priority messages, this isn't a stall
+ if( iPriority )
+ continue;
+
+ //Okey, start rendering at stream fps now instead, we are likely in a stillframe
+ if( !m_stalled )
+ {
+ if(m_started)
+ CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate);
+ m_stalled = true;
+ pts += frametime*4;
+ }
+
+ pts += frametime;
+
+ continue;
+ }
+
+ if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
+ {
+ if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE");
+
+ }
+ else
+ m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */
+
+ pMsg->Release();
+
+ continue;
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
+ {
+ CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
+
+ if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
+ pts = pMsgGeneralResync->m_timestamp;
+
+ double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock();
+ if( delay > frametime ) delay = frametime;
+ else if( delay < 0 ) delay = 0;
+
+ if(pMsgGeneralResync->m_clock)
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts);
+ m_av_clock->Discontinuity(pts - delay);
+ //m_av_clock->OMXUpdateClock(pts - delay);
+ }
+ else
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts);
+
+ pMsgGeneralResync->Release();
+ continue;
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
+ {
+ if (m_speed != DVD_PLAYSPEED_PAUSE)
+ {
+ double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
+
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout);
+
+ timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed);
+ timeout += m_av_clock->GetAbsoluteClock();
+
+ while(!m_bStop && m_av_clock->GetAbsoluteClock() < timeout)
+ Sleep(1);
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESET");
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ m_omxVideo.Reset();
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ m_started = false;
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (COMXPlayerVideo::Flush())
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_FLUSH");
+ m_stalled = true;
+ m_started = false;
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ m_omxVideo.Reset();
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
+ {
+ m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
+ {
+ if(m_started)
+ m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
+ {
+ COMXMsgAudioCodecChange* msg(static_cast<COMXMsgAudioCodecChange*>(pMsg));
+ OpenStream(msg->m_hints, msg->m_codec);
+ msg->m_codec = NULL;
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_EOF) && !m_audio_count)
+ {
+ CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_EOF");
+ WaitCompletion();
+ }
+ else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
+ {
+ DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
+ bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
+
+ if (m_messageQueue.GetDataSize() == 0
+ || m_speed < 0)
+ {
+ bRequestDrop = false;
+ }
+
+ // if player want's us to drop this packet, do so nomatter what
+ if(bPacketDrop)
+ bRequestDrop = true;
+
+ m_omxVideo.SetDropState(bRequestDrop);
+
+ while (!m_bStop)
+ {
+ if(m_flush)
+ {
+ m_flush = false;
+ break;
+ }
+
+ if((unsigned long)m_omxVideo.GetFreeSpace() < pPacket->iSize)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ if (m_stalled)
+ {
+ CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe left, switching to normal playback");
+ m_stalled = false;
+ }
+
+ // validate picture timing,
+ // if both dts/pts invalid, use pts calulated from picture.iDuration
+ // if pts invalid use dts, else use picture.pts as passed
+ if (pPacket->dts == DVD_NOPTS_VALUE && pPacket->pts == DVD_NOPTS_VALUE)
+ pPacket->pts = pts;
+ else if (pPacket->pts == DVD_NOPTS_VALUE)
+ pPacket->pts = pPacket->dts;
+
+ if(pPacket->pts != DVD_NOPTS_VALUE)
+ pPacket->pts += m_iVideoDelay;
+
+ if(pPacket->duration == 0)
+ pPacket->duration = frametime;
+
+ m_omxVideo.Decode(pPacket->pData, pPacket->iSize, pPacket->pts, pPacket->pts);
+ Output(pPacket->iGroupId, pPacket->pts, bRequestDrop);
+
+ if(m_started == false)
+ {
+ m_codecname = m_omxVideo.GetDecoderName();
+ m_started = true;
+ m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
+ }
+
+ // guess next frame pts. iDuration is always valid
+ if (m_speed != 0)
+ pts += pPacket->duration * m_speed / abs(m_speed);
+
+ break;
+ }
+
+ bRequestDrop = false;
+
+ m_videoStats.AddSampleBytes(pPacket->iSize);
+ }
+ pMsg->Release();
+
+ }
+}
+
+void OMXPlayerVideo::Flush()
+{
+ m_flush = true;
+ m_messageQueue.Flush();
+ m_messageQueue.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1);
+}
+
+bool OMXPlayerVideo::OpenDecoder()
+{
+ if(!m_av_clock)
+ return false;
+
+ if (m_hints.fpsrate && m_hints.fpsscale)
+ m_fFrameRate = DVD_TIME_BASE / OMXClock::NormalizeFrameduration((double)DVD_TIME_BASE * m_hints.fpsscale / m_hints.fpsrate);
+ else
+ m_fFrameRate = 25;
+
+ if( m_fFrameRate > 100 || m_fFrameRate < 5 )
+ {
+ CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder : Invalid framerate %d, using forced 25fps and just trust timestamps\n", (int)m_fFrameRate);
+ m_fFrameRate = 25;
+ }
+
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ if(!m_omxVideo.Open(m_hints, m_av_clock, m_Deinterlace, m_hdmi_clock_sync))
+ {
+ CLog::Log(LOGERROR, "OMXPlayerAudio : Error open video output");
+ m_av_clock->HasVideo(false);
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ return false;
+ }
+ else
+ CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder : Video codec %s width %d height %d profile %d fps %f\n",
+ m_omxVideo.GetDecoderName().c_str() , m_hints.width, m_hints.height, m_hints.profile, m_fFrameRate);
+
+ m_codecname = m_omxVideo.GetDecoderName();
+
+ // if we are closer to ntsc version of framerate, let gpu know
+ int iFrameRate = (int)(m_fFrameRate + 0.5f);
+ bool bNtscFreq = fabs(m_fFrameRate * 1001.0f / 1000.0f - iFrameRate) < fabs(m_fFrameRate - iFrameRate);
+ char response[80], command[80];
+ sprintf(command, "hdmi_ntsc_freqs %d", bNtscFreq);
+ CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder fps: %f %s\n", m_fFrameRate, command);
+ m_DllBcmHost.vc_gencmd(response, sizeof response, command);
+
+ if(m_av_clock)
+ m_av_clock->SetRefreshRate(m_fFrameRate);
+
+ m_av_clock->HasVideo(true);
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ return true;
+}
+
+int OMXPlayerVideo::GetDecoderBufferSize()
+{
+ return m_omxVideo.GetInputBufferSize();
+}
+
+int OMXPlayerVideo::GetDecoderFreeSpace()
+{
+ return m_omxVideo.GetFreeSpace();
+}
+
+void OMXPlayerVideo::WaitCompletion()
+{
+ m_omxVideo.WaitCompletion();
+}
+
+void OMXPlayerVideo::SetSpeed(int speed)
+{
+ if(m_messageQueue.IsInited())
+ m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
+ else
+ m_speed = speed;
+}
+
+std::string OMXPlayerVideo::GetPlayerInfo()
+{
+ std::ostringstream s;
+ s << "fr:" << fixed << setprecision(3) << m_fFrameRate;
+ s << ", vq:" << setw(2) << min(99,GetLevel()) << "%";
+ s << ", dc:" << m_codecname;
+ s << ", Mb/s:" << fixed << setprecision(2) << (double)GetVideoBitrate() / (1024.0*1024.0);
+
+ return s.str();
+}
+
+int OMXPlayerVideo::GetVideoBitrate()
+{
+ return (int)m_videoStats.GetBitrate();
+}
+
+double OMXPlayerVideo::GetOutputDelay()
+{
+ double time = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET);
+ if( m_fFrameRate )
+ time = (time * DVD_TIME_BASE) / m_fFrameRate;
+ else
+ time = 0.0;
+
+ if( m_speed != 0 )
+ time = time * DVD_PLAYSPEED_NORMAL / abs(m_speed);
+
+ return time;
+}
+
+int OMXPlayerVideo::GetFreeSpace()
+{
+ return m_omxVideo.GetFreeSpace();
+}
+
+void OMXPlayerVideo::SetVideoRect(const CRect &SrcRect, const CRect &DestRect)
+{
+ // check if destination rect or video view mode has changed
+ if ((m_dst_rect != DestRect) || (m_view_mode != g_settings.m_currentVideoSettings.m_ViewMode))
+ {
+ m_dst_rect = DestRect;
+ m_view_mode = g_settings.m_currentVideoSettings.m_ViewMode;
+ }
+ else
+ {
+ return;
+ }
+
+ // might need to scale up m_dst_rect to display size as video decodes
+ // to separate video plane that is at display size.
+ CRect gui, display, dst_rect;
+ RESOLUTION res = g_graphicsContext.GetVideoResolution();
+ gui.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight);
+ display.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight);
+
+ dst_rect = m_dst_rect;
+ if (gui != display)
+ {
+ float xscale = display.Width() / gui.Width();
+ float yscale = display.Height() / gui.Height();
+ dst_rect.x1 *= xscale;
+ dst_rect.x2 *= xscale;
+ dst_rect.y1 *= yscale;
+ dst_rect.y2 *= yscale;
+ }
+
+ m_omxVideo.SetVideoRect(SrcRect, m_dst_rect);
+}
+
+void OMXPlayerVideo::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect)
+{
+ OMXPlayerVideo *player = (OMXPlayerVideo*)ctx;
+ player->SetVideoRect(SrcRect, DestRect);
+}
+
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.h b/xbmc/cores/omxplayer/OMXPlayerVideo.h
new file mode 100644
index 0000000000..449400584f
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.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_PLAYERVIDEO_H_
+#define _OMX_PLAYERVIDEO_H_
+
+#include "utils/StdString.h"
+
+#include "OMXClock.h"
+#include "DVDStreamInfo.h"
+#include "OMXVideo.h"
+#include "threads/Thread.h"
+
+#include <deque>
+#include <sys/types.h>
+
+#include "DVDDemuxers/DVDDemux.h"
+#include "DVDStreamInfo.h"
+#include "DVDCodecs/Video/DVDVideoCodec.h"
+#include "DVDOverlayContainer.h"
+#include "DVDMessageQueue.h"
+#include "utils/BitstreamStats.h"
+#include "linux/DllBCM.h"
+
+using namespace std;
+
+class OMXPlayerVideo : public CThread
+{
+protected:
+ CDVDMessageQueue m_messageQueue;
+ int m_stream_id;
+ bool m_open;
+ CDVDStreamInfo m_hints;
+ double m_iCurrentPts;
+ OMXClock *m_av_clock;
+ COMXVideo m_omxVideo;
+ float m_fFrameRate;
+ bool m_Deinterlace;
+ bool m_flush;
+ bool m_hdmi_clock_sync;
+ double m_iVideoDelay;
+ int m_speed;
+ double m_FlipTimeStamp; // time stamp of last flippage. used to play at a forced framerate
+ int m_audio_count;
+ bool m_stalled;
+ bool m_started;
+ std::string m_codecname;
+ double m_droptime;
+ double m_dropbase;
+ unsigned int m_autosync;
+ double m_iSubtitleDelay;
+ bool m_bRenderSubs;
+ bool m_bAllowFullscreen;
+
+ unsigned int m_width;
+ unsigned int m_height;
+ unsigned int m_video_width;
+ unsigned int m_video_height;
+ unsigned m_flags;
+ float m_fps;
+
+ CRect m_dst_rect;
+ int m_view_mode;
+
+ DllBcmHost m_DllBcmHost;
+
+ CDVDOverlayContainer *m_pOverlayContainer;
+ CDVDMessageQueue &m_messageParent;
+
+ BitstreamStats m_videoStats;
+
+ DVDVideoPicture* m_pTempOverlayPicture;
+
+ void ProcessOverlays(int iGroupId, double pts);
+
+ virtual void OnStartup();
+ virtual void OnExit();
+ virtual void Process();
+private:
+public:
+ OMXPlayerVideo(OMXClock *av_clock, CDVDOverlayContainer* pOverlayContainer, CDVDMessageQueue& parent);
+ ~OMXPlayerVideo();
+ bool OpenStream(CDVDStreamInfo &hints);
+ bool OpenStream(CDVDStreamInfo &hints, COMXVideo *codec);
+ void SendMessage(CDVDMsg* pMsg, int priority = 0) { m_messageQueue.Put(pMsg, priority); }
+ bool AcceptsData() const { return !m_messageQueue.IsFull(); }
+ bool HasData() const { return m_messageQueue.GetDataSize() > 0; }
+ bool IsInited() const { return m_messageQueue.IsInited(); }
+ void WaitForBuffers() { m_messageQueue.WaitUntilEmpty(); }
+ int GetLevel() const { return m_messageQueue.GetLevel(); }
+ bool IsStalled() { return m_stalled; }
+ bool CloseStream(bool bWaitForBuffers);
+ void Output(int iGroupId, double pts, bool bDropPacket);
+ void Flush();
+ bool OpenDecoder();
+ int GetDecoderBufferSize();
+ int GetDecoderFreeSpace();
+ double GetCurrentPTS() { return m_iCurrentPts; };
+ double GetFPS() { return m_fFrameRate; };
+ void WaitCompletion();
+ void SetDelay(double delay) { m_iVideoDelay = delay; }
+ double GetDelay() { return m_iVideoDelay; }
+ void SetSpeed(int iSpeed);
+ std::string GetPlayerInfo();
+ int GetVideoBitrate();
+ double GetOutputDelay();
+ double GetSubtitleDelay() { return m_iSubtitleDelay; }
+ void SetSubtitleDelay(double delay) { m_iSubtitleDelay = delay; }
+ void EnableSubtitle(bool bEnable) { m_bRenderSubs = bEnable; }
+ bool IsSubtitleEnabled() { return m_bRenderSubs; }
+ void EnableFullscreen(bool bEnable) { m_bAllowFullscreen = bEnable; }
+ void SetFlags(unsigned flags) { m_flags = flags; };
+ int GetFreeSpace();
+ void SetVideoRect(const CRect &SrcRect, const CRect &DestRect);
+ static void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect);
+};
+#endif
diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp
new file mode 100644
index 0000000000..9b6bd6bf97
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXVideo.cpp
@@ -0,0 +1,966 @@
+/*
+ * 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_CONFIG_H) && (!defined WIN32)
+ #include "config.h"
+#elif defined(_WIN32)
+#include "system.h"
+#endif
+
+#include "OMXVideo.h"
+
+#include "utils/log.h"
+#include "linux/XMemUtils.h"
+#include "DVDDemuxers/DVDDemuxUtils.h"
+#include "settings/AdvancedSettings.h"
+
+#include <sys/time.h>
+#include <inttypes.h>
+
+#ifdef CLASSNAME
+#undef CLASSNAME
+#endif
+#define CLASSNAME "COMXVideo"
+
+#if 0
+// TODO: These are Nvidia Tegra2 dependent, need to dynamiclly find the
+// right codec matched to video format.
+#define OMX_H264BASE_DECODER "OMX.Nvidia.h264.decode"
+// OMX.Nvidia.h264ext.decode segfaults, not sure why.
+//#define OMX_H264MAIN_DECODER "OMX.Nvidia.h264ext.decode"
+#define OMX_H264MAIN_DECODER "OMX.Nvidia.h264.decode"
+#define OMX_H264HIGH_DECODER "OMX.Nvidia.h264ext.decode"
+#define OMX_MPEG4_DECODER "OMX.Nvidia.mp4.decode"
+#define OMX_MPEG4EXT_DECODER "OMX.Nvidia.mp4ext.decode"
+#define OMX_MPEG2V_DECODER "OMX.Nvidia.mpeg2v.decode"
+#define OMX_VC1_DECODER "OMX.Nvidia.vc1.decode"
+#endif
+
+#define OMX_VIDEO_DECODER "OMX.broadcom.video_decode"
+#define OMX_H264BASE_DECODER OMX_VIDEO_DECODER
+#define OMX_H264MAIN_DECODER OMX_VIDEO_DECODER
+#define OMX_H264HIGH_DECODER OMX_VIDEO_DECODER
+#define OMX_MPEG4_DECODER OMX_VIDEO_DECODER
+#define OMX_MSMPEG4V1_DECODER OMX_VIDEO_DECODER
+#define OMX_MSMPEG4V2_DECODER OMX_VIDEO_DECODER
+#define OMX_MSMPEG4V3_DECODER OMX_VIDEO_DECODER
+#define OMX_MPEG4EXT_DECODER OMX_VIDEO_DECODER
+#define OMX_MPEG2V_DECODER OMX_VIDEO_DECODER
+#define OMX_VC1_DECODER OMX_VIDEO_DECODER
+#define OMX_WMV3_DECODER OMX_VIDEO_DECODER
+#define OMX_VP8_DECODER OMX_VIDEO_DECODER
+
+#define MAX_TEXT_LENGTH 1024
+
+COMXVideo::COMXVideo()
+{
+ m_is_open = false;
+ m_Pause = false;
+ m_extradata = NULL;
+ m_extrasize = 0;
+ m_converter = NULL;
+ m_video_convert = false;
+ m_video_codec_name = "";
+ m_deinterlace = false;
+ m_hdmi_clock_sync = false;
+ m_first_frame = true;
+}
+
+COMXVideo::~COMXVideo()
+{
+ if (m_is_open)
+ Close();
+}
+
+bool COMXVideo::SendDecoderConfig()
+{
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
+ /* 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;
+ }
+ }
+ return true;
+}
+
+bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace, bool hdmi_clock_sync)
+{
+ if(m_is_open)
+ Close();
+
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ std::string decoder_name;
+
+ m_video_codec_name = "";
+ m_codingType = OMX_VIDEO_CodingUnused;
+
+ m_decoded_width = hints.width;
+ m_decoded_height = hints.height;
+
+ m_hdmi_clock_sync = hdmi_clock_sync;
+
+ if(!m_decoded_width || !m_decoded_height)
+ return false;
+
+ m_converter = new CBitstreamConverter();
+ m_video_convert = m_converter->Open(hints.codec, (uint8_t *)hints.extradata, hints.extrasize, false);
+
+ if(m_video_convert)
+ {
+ if(m_converter->GetExtraData() != NULL && m_converter->GetExtraSize() > 0)
+ {
+ m_extrasize = m_converter->GetExtraSize();
+ m_extradata = (uint8_t *)malloc(m_extrasize);
+ memcpy(m_extradata, m_converter->GetExtraData(), m_converter->GetExtraSize());
+ }
+ }
+ else
+ {
+ 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);
+ }
+ }
+
+ switch (hints.codec)
+ {
+ case CODEC_ID_H264:
+ {
+ switch(hints.profile)
+ {
+ case FF_PROFILE_H264_BASELINE:
+ // (role name) video_decoder.avc
+ // H.264 Baseline profile
+ decoder_name = OMX_H264BASE_DECODER;
+ m_codingType = OMX_VIDEO_CodingAVC;
+ m_video_codec_name = "omx-h264";
+ break;
+ case FF_PROFILE_H264_MAIN:
+ // (role name) video_decoder.avc
+ // H.264 Main profile
+ decoder_name = OMX_H264MAIN_DECODER;
+ m_codingType = OMX_VIDEO_CodingAVC;
+ m_video_codec_name = "omx-h264";
+ break;
+ case FF_PROFILE_H264_HIGH:
+ // (role name) video_decoder.avc
+ // H.264 Main profile
+ decoder_name = OMX_H264HIGH_DECODER;
+ m_codingType = OMX_VIDEO_CodingAVC;
+ m_video_codec_name = "omx-h264";
+ break;
+ case FF_PROFILE_UNKNOWN:
+ decoder_name = OMX_H264HIGH_DECODER;
+ m_codingType = OMX_VIDEO_CodingAVC;
+ m_video_codec_name = "omx-h264";
+ break;
+ default:
+ decoder_name = OMX_H264HIGH_DECODER;
+ m_codingType = OMX_VIDEO_CodingAVC;
+ m_video_codec_name = "omx-h264";
+ break;
+ }
+ }
+ break;
+ case CODEC_ID_MPEG4:
+ // (role name) video_decoder.mpeg4
+ // MPEG-4, DivX 4/5 and Xvid compatible
+ decoder_name = OMX_MPEG4_DECODER;
+ m_codingType = OMX_VIDEO_CodingMPEG4;
+ m_video_codec_name = "omx-mpeg4";
+ break;
+ case CODEC_ID_MPEG1VIDEO:
+ case CODEC_ID_MPEG2VIDEO:
+ // (role name) video_decoder.mpeg2
+ // MPEG-2
+ decoder_name = OMX_MPEG2V_DECODER;
+ m_codingType = OMX_VIDEO_CodingMPEG2;
+ m_video_codec_name = "omx-mpeg2";
+ break;
+ case CODEC_ID_H263:
+ // (role name) video_decoder.mpeg4
+ // MPEG-4, DivX 4/5 and Xvid compatible
+ decoder_name = OMX_MPEG4_DECODER;
+ m_codingType = OMX_VIDEO_CodingMPEG4;
+ m_video_codec_name = "omx-h263";
+ break;
+ case CODEC_ID_VP8:
+ // (role name) video_decoder.vp8
+ // VP8
+ decoder_name = OMX_VP8_DECODER;
+ m_codingType = OMX_VIDEO_CodingVP8;
+ m_video_codec_name = "omx-vp8";
+ break;
+ case CODEC_ID_VC1:
+ // (role name) video_decoder.vc1
+ // VC-1, WMV9
+ decoder_name = OMX_VC1_DECODER;
+ m_codingType = OMX_VIDEO_CodingWMV;
+ m_video_codec_name = "omx-vc1";
+ break;
+ /*
+ case CODEC_ID_WMV3:
+ // (role name) video_decoder.wmv3
+ //WMV3
+ decoder_name = OMX_WMV3_DECODER;
+ m_codingType = OMX_VIDEO_CodingWMV;
+ m_video_codec_name = "omx-wmv3";
+ break;
+ */
+ default:
+ return false;
+ break;
+ }
+
+ if(m_decoded_width <= 720 && m_decoded_height <=576 && deinterlace)
+ {
+ CLog::Log(LOGDEBUG, "COMXVideo::Open : enable deinterlace\n");
+ m_deinterlace = true;
+ }
+ else
+ {
+ m_deinterlace = false;
+ }
+
+ std::string componentName = "";
+
+ componentName = decoder_name;
+ if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamVideoInit))
+ return false;
+
+ componentName = "OMX.broadcom.video_render";
+ if(!m_omx_render.Initialize((const std::string)componentName, OMX_IndexParamVideoInit))
+ return false;
+
+ componentName = "OMX.broadcom.video_scheduler";
+ if(!m_omx_sched.Initialize((const std::string)componentName, OMX_IndexParamVideoInit))
+ return false;
+
+ if(m_deinterlace)
+ {
+ componentName = "OMX.broadcom.image_fx";
+ if(!m_omx_image_fx.Initialize((const std::string)componentName, OMX_IndexParamImageInit))
+ return false;
+ }
+
+ OMX_VIDEO_PARAM_PORTFORMATTYPE formatType;
+ /*
+ OMX_INIT_STRUCTURE(formatType);
+ formatType.nPortIndex = m_omx_decoder.GetInputPort();
+ OMX_U32 nIndex = 1;
+ bool bFound = false;
+
+ omx_err = OMX_ErrorNone;
+ do
+ {
+ formatType.nIndex = nIndex;
+ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamVideoPortFormat, &formatType);
+ if(formatType.eCompressionFormat == m_codingType)
+ {
+ bFound = true;
+ break;
+ }
+ nIndex++;
+ }
+ while(omx_err == OMX_ErrorNone);
+
+ if(!bFound)
+ {
+ CLog::Log(LOGINFO, "COMXVideo::Open coding : %s not supported\n", m_video_codec_name.c_str());
+ return false;
+ }
+ */
+
+ if(clock == NULL)
+ return false;
+
+ m_av_clock = clock;
+ m_omx_clock = m_av_clock->GetOMXClock();
+
+ if(m_omx_clock->GetComponent() == NULL)
+ {
+ m_av_clock = NULL;
+ m_omx_clock = NULL;
+ return false;
+ }
+
+ if(m_deinterlace)
+ {
+ m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_image_fx, m_omx_image_fx.GetInputPort());
+ m_omx_tunnel_image_fx.Initialize(&m_omx_image_fx, m_omx_image_fx.GetOutputPort(), &m_omx_sched, m_omx_sched.GetInputPort());
+ }
+ else
+ {
+ m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_sched, m_omx_sched.GetInputPort());
+ }
+ m_omx_tunnel_sched.Initialize(&m_omx_sched, m_omx_sched.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort());
+
+ m_omx_tunnel_clock.Initialize(m_omx_clock, m_omx_clock->GetInputPort() + 1, &m_omx_sched, m_omx_sched.GetOutputPort() + 1);
+
+ omx_err = m_omx_tunnel_clock.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_clock.Establish\n");
+ return false;
+ }
+
+ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateIdle);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open m_omx_decoder.SetStateForComponent\n");
+ return false;
+ }
+
+ OMX_INIT_STRUCTURE(formatType);
+ formatType.nPortIndex = m_omx_decoder.GetInputPort();
+ formatType.eCompressionFormat = m_codingType;
+
+ if (hints.fpsscale > 0 && hints.fpsrate > 0)
+ {
+ formatType.xFramerate = (long long)(1<<16)*hints.fpsrate / hints.fpsscale;
+ }
+ else
+ {
+ formatType.xFramerate = 25 * (1<<16);
+ }
+
+ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamVideoPortFormat, &formatType);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ OMX_PARAM_PORTDEFINITIONTYPE portParam;
+ OMX_INIT_STRUCTURE(portParam);
+ portParam.nPortIndex = m_omx_decoder.GetInputPort();
+
+ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &portParam);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err);
+ return false;
+ }
+
+ portParam.nPortIndex = m_omx_decoder.GetInputPort();
+ portParam.nBufferCountActual = VIDEO_BUFFERS;
+
+ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &portParam);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err);
+ return false;
+ }
+
+ OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE concanParam;
+ OMX_INIT_STRUCTURE(concanParam);
+ if(g_advancedSettings.m_omxDecodeStartWithValidFrame)
+ concanParam.bStartWithValidFrame = OMX_TRUE;
+ else
+ concanParam.bStartWithValidFrame = OMX_FALSE;
+
+ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmVideoDecodeErrorConcealment, &concanParam);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamBrcmVideoDecodeErrorConcealment omx_err(0x%08x)\n", omx_err);
+ return false;
+ }
+
+ if(m_hdmi_clock_sync)
+ {
+ OMX_CONFIG_LATENCYTARGETTYPE latencyTarget;
+ OMX_INIT_STRUCTURE(latencyTarget);
+ latencyTarget.nPortIndex = m_omx_render.GetInputPort();
+ latencyTarget.bEnabled = OMX_TRUE;
+ latencyTarget.nFilter = 2;
+ latencyTarget.nTarget = 4000;
+ latencyTarget.nShift = 3;
+ latencyTarget.nSpeedFactor = -135;
+ latencyTarget.nInterFactor = 500;
+ latencyTarget.nAdjCap = 20;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigLatencyTarget, &latencyTarget);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexConfigLatencyTarget error (0%08x)\n", omx_err);
+ return false;
+ }
+ }
+
+ // Alloc buffers for the omx intput port.
+ omx_err = m_omx_decoder.AllocInputBuffers();
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open AllocOMXInputBuffers error (0%08x)\n", omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_tunnel_decoder.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_decoder.Establish\n");
+ return false;
+ }
+
+ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_decoder.SetStateForComponent\n");
+ return false;
+ }
+
+ if(m_deinterlace)
+ {
+ OMX_CONFIG_IMAGEFILTERPARAMSTYPE image_filter;
+ OMX_INIT_STRUCTURE(image_filter);
+
+ image_filter.nPortIndex = m_omx_image_fx.GetOutputPort();
+ image_filter.nNumParams = 1;
+ image_filter.nParams[0] = 3;
+ image_filter.eImageFilter = OMX_ImageFilterDeInterlaceAdvanced;
+
+ omx_err = m_omx_image_fx.SetConfig(OMX_IndexConfigCommonImageFilterParameters, &image_filter);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexConfigCommonImageFilterParameters omx_err(0x%08x)\n", omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_tunnel_image_fx.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_image_fx.Establish\n");
+ return false;
+ }
+
+ omx_err = m_omx_image_fx.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_image_fx.SetStateForComponent\n");
+ return false;
+ }
+
+ m_omx_image_fx.DisablePort(m_omx_image_fx.GetInputPort(), false);
+ m_omx_image_fx.DisablePort(m_omx_image_fx.GetOutputPort(), false);
+ }
+
+ omx_err = m_omx_tunnel_sched.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_sched.Establish\n");
+ return false;
+ }
+
+ omx_err = m_omx_sched.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_sched.SetStateForComponent\n");
+ return false;
+ }
+
+ omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_render.SetStateForComponent\n");
+ return false;
+ }
+
+ if(!SendDecoderConfig())
+ return false;
+
+ m_is_open = true;
+ m_drop_state = false;
+
+ OMX_CONFIG_DISPLAYREGIONTYPE configDisplay;
+ OMX_INIT_STRUCTURE(configDisplay);
+ configDisplay.nPortIndex = m_omx_render.GetInputPort();
+
+ configDisplay.set = OMX_DISPLAY_SET_TRANSFORM;
+
+ switch(hints.orientation)
+ {
+ case 90:
+ configDisplay.transform = OMX_DISPLAY_ROT90;
+ break;
+ case 180:
+ configDisplay.transform = OMX_DISPLAY_ROT180;
+ break;
+ case 270:
+ configDisplay.transform = OMX_DISPLAY_ROT270;
+ break;
+ default:
+ configDisplay.transform = OMX_DISPLAY_ROT0;
+ break;
+ }
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGWARNING, "COMXVideo::Open could not set orientation : %d\n", hints.orientation);
+ }
+
+ /*
+ configDisplay.set = OMX_DISPLAY_SET_LAYER;
+ configDisplay.layer = 2;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ configDisplay.set = OMX_DISPLAY_SET_DEST_RECT;
+ configDisplay.dest_rect.x_offset = 100;
+ configDisplay.dest_rect.y_offset = 100;
+ configDisplay.dest_rect.width = 640;
+ configDisplay.dest_rect.height = 480;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ configDisplay.set = OMX_DISPLAY_SET_TRANSFORM;
+ configDisplay.transform = OMX_DISPLAY_ROT180;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ configDisplay.set = OMX_DISPLAY_SET_FULLSCREEN;
+ configDisplay.fullscreen = OMX_FALSE;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ configDisplay.set = OMX_DISPLAY_SET_MODE;
+ configDisplay.mode = OMX_DISPLAY_MODE_FILL; //OMX_DISPLAY_MODE_LETTERBOX;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ configDisplay.set = OMX_DISPLAY_SET_LAYER;
+ configDisplay.layer = 1;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ configDisplay.set = OMX_DISPLAY_SET_ALPHA;
+ configDisplay.alpha = OMX_FALSE;
+
+ omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+ if(omx_err != OMX_ErrorNone)
+ return false;
+
+ */
+
+ CLog::Log(LOGDEBUG,
+ "%s::%s - decoder_component(0x%p), input_port(0x%x), output_port(0x%x) deinterlace %d hdmiclocksync %d\n",
+ CLASSNAME, __func__, m_omx_decoder.GetComponent(), m_omx_decoder.GetInputPort(), m_omx_decoder.GetOutputPort(),
+ m_deinterlace, m_hdmi_clock_sync);
+
+ m_av_clock->OMXStateExecute(false);
+
+ m_first_frame = true;
+ return true;
+}
+
+void COMXVideo::Close()
+{
+ if(!m_is_open)
+ return;
+
+ /*
+ if(m_av_clock)
+ {
+ m_av_clock->Lock();
+ m_av_clock->OMXStop(false);
+ }
+ */
+
+ m_omx_tunnel_decoder.Flush();
+ if(m_deinterlace)
+ m_omx_tunnel_image_fx.Flush();
+ m_omx_tunnel_clock.Flush();
+ m_omx_tunnel_sched.Flush();
+
+ m_omx_tunnel_clock.Deestablish();
+ m_omx_tunnel_decoder.Deestablish();
+ if(m_deinterlace)
+ m_omx_tunnel_image_fx.Deestablish();
+ m_omx_tunnel_sched.Deestablish();
+
+ m_omx_decoder.FlushInput();
+
+ m_omx_sched.Deinitialize();
+ if(m_deinterlace)
+ m_omx_image_fx.Deinitialize();
+ m_omx_decoder.Deinitialize();
+ m_omx_render.Deinitialize();
+
+ /*
+ if(m_av_clock)
+ {
+ m_av_clock->OMXReset(false);
+ m_av_clock->UnLock();
+ }
+ */
+
+ m_is_open = false;
+
+ if(m_extradata)
+ free(m_extradata);
+ m_extradata = NULL;
+ m_extrasize = 0;
+
+ if(m_converter)
+ delete m_converter;
+ m_converter = NULL;
+ m_video_convert = false;
+ m_video_codec_name = "";
+ m_deinterlace = false;
+ m_first_frame = true;
+}
+
+void COMXVideo::SetDropState(bool bDrop)
+{
+ m_drop_state = bDrop;
+}
+
+unsigned int COMXVideo::GetFreeSpace()
+{
+ return m_omx_decoder.GetInputBufferSpace();
+}
+
+unsigned int COMXVideo::GetSize()
+{
+ return m_omx_decoder.GetInputBufferSize();
+}
+
+int COMXVideo::Decode(uint8_t *pData, int iSize, double dts, double pts)
+{
+ OMX_ERRORTYPE omx_err;
+
+ if( m_drop_state )
+ return true;
+
+ if (pData || iSize > 0)
+ {
+ unsigned int demuxer_bytes = (unsigned int)iSize;
+ uint8_t *demuxer_content = pData;
+
+ if(m_video_convert)
+ {
+ m_converter->Convert(pData, iSize);
+ demuxer_bytes = m_converter->GetConvertSize();
+ demuxer_content = m_converter->GetConvertBuffer();
+ if(!demuxer_bytes && demuxer_bytes < 1)
+ {
+ return false;
+ }
+ }
+
+ while(demuxer_bytes)
+ {
+ // 500ms timeout
+ OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(500);
+ if(omx_buffer == NULL)
+ {
+ CLog::Log(LOGERROR, "OMXVideo::Decode timeout\n");
+ return false;
+ }
+
+ /*
+ CLog::Log(DEBUG, "COMXVideo::Video VDec : pts %lld omx_buffer 0x%08x buffer 0x%08x number %d\n",
+ pts, omx_buffer, omx_buffer->pBuffer, (int)omx_buffer->pAppPrivate);
+ if(pts == DVD_NOPTS_VALUE)
+ {
+ CLog::Log(LOGDEBUG, "VDec : pts %f omx_buffer 0x%08x buffer 0x%08x number %d\n",
+ (float)pts / AV_TIME_BASE, (int)omx_buffer, (int)omx_buffer->pBuffer, (int)omx_buffer->pAppPrivate);
+ }
+ */
+
+ omx_buffer->nFlags = 0;
+ omx_buffer->nOffset = 0;
+
+ uint64_t val = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts;
+
+ if(m_av_clock->VideoStart())
+ {
+ omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME;
+ CLog::Log(LOGDEBUG, "VDec : setStartTime %f\n", (float)val / DVD_TIME_BASE);
+ m_av_clock->VideoStart(false);
+ }
+ else
+ {
+ if(pts == DVD_NOPTS_VALUE)
+ omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;
+ }
+
+ omx_buffer->nTimeStamp = ToOMXTime(val);
+
+ omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes;
+ memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen);
+
+ 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__);
+ return false;
+ }
+ }
+
+ if(m_first_frame && m_deinterlace)
+ {
+ OMX_PARAM_PORTDEFINITIONTYPE port_image;
+ OMX_INIT_STRUCTURE(port_image);
+ port_image.nPortIndex = m_omx_decoder.GetOutputPort();
+
+ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_image);
+ if(omx_err != OMX_ErrorNone)
+ CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 1 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
+
+ /* we assume when the sizes equal we have the first decoded frame */
+ if(port_image.format.video.nFrameWidth == m_decoded_width && port_image.format.video.nFrameHeight == m_decoded_height)
+ {
+ m_first_frame = false;
+
+ omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged);
+ if(omx_err == OMX_ErrorStreamCorrupt)
+ {
+ CLog::Log(LOGERROR, "%s::%s - image not unsupported\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), false);
+ m_omx_sched.DisablePort(m_omx_sched.GetInputPort(), false);
+
+ if(m_deinterlace)
+ {
+ m_omx_image_fx.DisablePort(m_omx_image_fx.GetOutputPort(), false);
+ m_omx_image_fx.DisablePort(m_omx_image_fx.GetInputPort(), false);
+
+ port_image.nPortIndex = m_omx_image_fx.GetInputPort();
+ omx_err = m_omx_image_fx.SetParameter(OMX_IndexParamPortDefinition, &port_image);
+ if(omx_err != OMX_ErrorNone)
+ CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 2 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
+
+ port_image.nPortIndex = m_omx_image_fx.GetOutputPort();
+ omx_err = m_omx_image_fx.SetParameter(OMX_IndexParamPortDefinition, &port_image);
+ if(omx_err != OMX_ErrorNone)
+ CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 3 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
+ }
+
+ m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false);
+
+ if(m_deinterlace)
+ {
+ m_omx_image_fx.EnablePort(m_omx_image_fx.GetOutputPort(), false);
+ m_omx_image_fx.EnablePort(m_omx_image_fx.GetInputPort(), false);
+ }
+
+ m_omx_sched.EnablePort(m_omx_sched.GetInputPort(), false);
+ }
+ }
+ }
+
+ return true;
+
+ }
+
+ return false;
+}
+
+void COMXVideo::Reset(void)
+{
+ if(!m_is_open)
+ return;
+
+ m_omx_decoder.FlushInput();
+ m_omx_tunnel_decoder.Flush();
+
+ /*
+ OMX_ERRORTYPE omx_err;
+ OMX_CONFIG_BOOLEANTYPE configBool;
+ OMX_INIT_STRUCTURE(configBool);
+ configBool.bEnabled = OMX_TRUE;
+
+ omx_err = m_omx_decoder.SetConfig(OMX_IndexConfigRefreshCodec, &configBool);
+ if (omx_err != OMX_ErrorNone)
+ CLog::Log(LOGERROR, "%s::%s - error reopen codec omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
+
+ SendDecoderConfig();
+
+ m_first_frame = true;
+ */
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+bool COMXVideo::Pause()
+{
+ if(m_omx_render.GetComponent() == NULL)
+ return false;
+
+ if(m_Pause) return true;
+ m_Pause = true;
+
+ m_omx_sched.SetStateForComponent(OMX_StatePause);
+ m_omx_render.SetStateForComponent(OMX_StatePause);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+bool COMXVideo::Resume()
+{
+ if(m_omx_render.GetComponent() == NULL)
+ return false;
+
+ if(!m_Pause) return true;
+ m_Pause = false;
+
+ m_omx_sched.SetStateForComponent(OMX_StateExecuting);
+ m_omx_render.SetStateForComponent(OMX_StateExecuting);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect)
+{
+ if(!m_is_open)
+ return;
+
+ OMX_CONFIG_DISPLAYREGIONTYPE configDisplay;
+ OMX_INIT_STRUCTURE(configDisplay);
+ configDisplay.nPortIndex = m_omx_render.GetInputPort();
+
+ configDisplay.set = OMX_DISPLAY_SET_FULLSCREEN;
+ configDisplay.fullscreen = OMX_FALSE;
+
+ m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+
+ configDisplay.set = OMX_DISPLAY_SET_DEST_RECT;
+ configDisplay.dest_rect.x_offset = DestRect.x1;
+ configDisplay.dest_rect.y_offset = DestRect.y1;
+ configDisplay.dest_rect.width = DestRect.Width();
+ configDisplay.dest_rect.height = DestRect.Height();
+
+ m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
+
+ CLog::Log(LOGDEBUG, "dest_rect.x_offset %d dest_rect.y_offset %d dest_rect.width %d dest_rect.height %d\n",
+ configDisplay.dest_rect.x_offset, configDisplay.dest_rect.y_offset,
+ configDisplay.dest_rect.width, configDisplay.dest_rect.height);
+}
+
+int COMXVideo::GetInputBufferSize()
+{
+ return m_omx_decoder.GetInputBufferSize();
+}
+
+void COMXVideo::WaitCompletion()
+{
+ if(!m_is_open)
+ 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) > 5)
+ {
+ CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__);
+ break;
+ }
+ Sleep(50);
+ }
+
+ return;
+}
diff --git a/xbmc/cores/omxplayer/OMXVideo.h b/xbmc/cores/omxplayer/OMXVideo.h
new file mode 100644
index 0000000000..7bfca9f8ef
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXVideo.h
@@ -0,0 +1,98 @@
+#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 "OMXCore.h"
+#include "DVDStreamInfo.h"
+
+#include <IL/OMX_Video.h>
+
+#include "BitstreamConverter.h"
+
+#include "OMXClock.h"
+
+#include "guilib/Geometry.h"
+#include "DVDDemuxers/DVDDemux.h"
+#include <string>
+
+#define VIDEO_BUFFERS 60
+
+#define CLASSNAME "COMXVideo"
+
+class COMXVideo
+{
+public:
+ COMXVideo();
+ ~COMXVideo();
+
+ // Required overrides
+ bool SendDecoderConfig();
+ bool Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace = false, bool hdmi_clock_sync = false);
+ void Close(void);
+ unsigned int GetFreeSpace();
+ unsigned int GetSize();
+ int Decode(uint8_t *pData, int iSize, double dts, double pts);
+ void Reset(void);
+ void SetDropState(bool bDrop);
+ bool Pause();
+ bool Resume();
+ std::string GetDecoderName() { return m_video_codec_name; };
+ void SetVideoRect(const CRect& SrcRect, const CRect& DestRect);
+ int GetInputBufferSize();
+ void WaitCompletion();
+protected:
+ // Video format
+ bool m_drop_state;
+ unsigned int m_decoded_width;
+ unsigned int m_decoded_height;
+
+ OMX_VIDEO_CODINGTYPE m_codingType;
+
+ COMXCoreComponent m_omx_decoder;
+ COMXCoreComponent m_omx_render;
+ COMXCoreComponent m_omx_sched;
+ COMXCoreComponent m_omx_image_fx;
+ COMXCoreComponent *m_omx_clock;
+ OMXClock *m_av_clock;
+
+ COMXCoreTunel m_omx_tunnel_decoder;
+ COMXCoreTunel m_omx_tunnel_clock;
+ COMXCoreTunel m_omx_tunnel_sched;
+ COMXCoreTunel m_omx_tunnel_image_fx;
+ bool m_is_open;
+
+ bool m_Pause;
+
+ uint8_t *m_extradata;
+ int m_extrasize;
+
+ CBitstreamConverter *m_converter;
+ bool m_video_convert;
+ std::string m_video_codec_name;
+
+ bool m_deinterlace;
+ bool m_hdmi_clock_sync;
+ bool m_first_frame;
+};
+
+#endif
diff --git a/xbmc/cores/omxplayer/OMXVideoCodec.h b/xbmc/cores/omxplayer/OMXVideoCodec.h
new file mode 100644
index 0000000000..6990f31df2
--- /dev/null
+++ b/xbmc/cores/omxplayer/OMXVideoCodec.h
@@ -0,0 +1,240 @@
+#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 "system.h"
+
+#include <vector>
+
+// when modifying these structures, make sure you update all codecs accordingly
+#define FRAME_TYPE_UNDEF 0
+#define FRAME_TYPE_I 1
+#define FRAME_TYPE_P 2
+#define FRAME_TYPE_B 3
+#define FRAME_TYPE_D 4
+
+namespace DXVA { class CSurfaceContext; }
+namespace VAAPI { struct CHolder; }
+class CVDPAU;
+class COMXCore;
+class COMXCoreVideo;
+struct OMXCoreVideoBuffer;
+#ifdef HAVE_VIDEOTOOLBOXDECODER
+ class COMXVideoCodecVideoToolBox;
+ struct __CVBuffer;
+#endif
+
+// should be entirely filled by all codecs
+struct DVDVideoPicture
+{
+ double pts; // timestamp in seconds, used in the CDVDPlayer class to keep track of pts
+ double dts;
+
+ union
+ {
+ struct {
+ BYTE* data[4]; // [4] = alpha channel, currently not used
+ int iLineSize[4]; // [4] = alpha channel, currently not used
+ };
+ struct {
+ DXVA::CSurfaceContext* context;
+ };
+ struct {
+ CVDPAU* vdpau;
+ };
+ struct {
+ VAAPI::CHolder* vaapi;
+ };
+
+ struct {
+ COMXCore *openMax;
+ OMXCoreVideoBuffer *openMaxBuffer;
+ };
+#ifdef HAVE_VIDEOTOOLBOXDECODER
+ struct {
+ COMXVideoCodecVideoToolBox *vtb;
+ struct __CVBuffer *cvBufferRef;
+ };
+#endif
+ };
+
+ unsigned int iFlags;
+
+ double iRepeatPicture;
+ double iDuration;
+ unsigned int iFrameType : 4; // see defines above // 1->I, 2->P, 3->B, 0->Undef
+ unsigned int color_matrix : 4;
+ unsigned int color_range : 1; // 1 indicate if we have a full range of color
+ unsigned int chroma_position;
+ unsigned int color_primaries;
+ unsigned int color_transfer;
+ unsigned int extended_format;
+ int iGroupId;
+
+ int8_t* qscale_table; // Quantization parameters, primarily used by filters
+ int qscale_stride;
+ int qscale_type;
+
+ unsigned int iWidth;
+ unsigned int iHeight;
+ unsigned int iDisplayWidth; // width of the picture without black bars
+ unsigned int iDisplayHeight; // height of the picture without black bars
+
+ enum EFormat {
+ FMT_YUV420P = 0,
+ FMT_VDPAU,
+ FMT_NV12,
+ FMT_UYVY,
+ FMT_YUY2,
+ FMT_DXVA,
+ FMT_VAAPI,
+ FMT_OMXEGL,
+ FMT_CVBREF,
+ } format;
+};
+
+struct DVDVideoUserData
+{
+ BYTE* data;
+ int size;
+};
+
+#define DVP_FLAG_TOP_FIELD_FIRST 0x00000001
+#define DVP_FLAG_REPEAT_TOP_FIELD 0x00000002 //Set to indicate that the top field should be repeated
+#define DVP_FLAG_ALLOCATED 0x00000004 //Set to indicate that this has allocated data
+#define DVP_FLAG_INTERLACED 0x00000008 //Set to indicate that this frame is interlaced
+
+#define DVP_FLAG_NOSKIP 0x00000010 // indicate this picture should never be dropped
+#define DVP_FLAG_DROPPED 0x00000020 // indicate that this picture has been dropped in decoder stage, will have no data
+
+// DVP_FLAG 0x00000100 - 0x00000f00 is in use by libmpeg2!
+
+#define DVP_QSCALE_UNKNOWN 0
+#define DVP_QSCALE_MPEG1 1
+#define DVP_QSCALE_MPEG2 2
+#define DVP_QSCALE_H264 3
+
+class COMXStreamInfo;
+class CDVDCodecOption;
+class CDVDCodecOptions;
+
+// VC_ messages, messages can be combined
+#define VC_ERROR 0x00000001 // an error occured, no other messages will be returned
+#define VC_BUFFER 0x00000002 // the decoder needs more data
+#define VC_PICTURE 0x00000004 // the decoder got a picture, call Decode(NULL, 0) again to parse the rest of the data
+#define VC_USERDATA 0x00000008 // the decoder found some userdata, call Decode(NULL, 0) again to parse the rest of the data
+#define VC_FLUSHED 0x00000010 // the decoder lost it's state, we need to restart decoding again
+class COMXVideoCodec
+{
+public:
+
+ COMXVideoCodec() {}
+ virtual ~COMXVideoCodec() {}
+
+ /*
+ * Open the decoder, returns true on success
+ */
+ virtual bool Open(COMXStreamInfo &hints, CDVDCodecOptions &options) = 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* pData, int iSize, double dts, double pts) = 0;
+
+ /*
+ * Reset the decoder.
+ * Should be the same as calling Dispose and Open after each other
+ */
+ virtual void Reset() = 0;
+
+ /*
+ * returns true if successfull
+ * the data is valid until the next Decode call
+ */
+ virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture) = 0;
+
+
+ /*
+ * returns true if successfull
+ * the data is cleared to zero
+ */
+ virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture)
+ {
+ memset(pDvdVideoPicture, 0, sizeof(DVDVideoPicture));
+ return true;
+ }
+
+ /*
+ * returns true if successfull
+ * the data is valid until the next Decode call
+ * userdata can be anything, for now we use it for closed captioning
+ */
+ virtual bool GetUserData(DVDVideoUserData* pDvdVideoUserData)
+ {
+ pDvdVideoUserData->data = NULL;
+ pDvdVideoUserData->size = 0;
+ return false;
+ }
+
+ /*
+ * will be called by video player indicating if a frame will eventually be dropped
+ * codec can then skip actually decoding the data, just consume the data set picture headers
+ */
+ virtual void SetDropState(bool bDrop) = 0;
+
+
+ enum EFilterFlags {
+ FILTER_NONE = 0x0,
+ FILTER_DEINTERLACE_YADIF = 0x1, /* use first deinterlace mode */
+ FILTER_DEINTERLACE_ANY = 0xf, /* use any deinterlace mode */
+ FILTER_DEINTERLACE_FLAGGED = 0x10, /* only deinterlace flagged frames */
+ FILTER_DEINTERLACE_HALFED = 0x20, /* do half rate deinterlacing */
+ };
+
+ /*
+ * set the type of filters that should be applied at decoding stage if possible
+ */
+ virtual unsigned int SetFilters(unsigned int filters) { return 0u; }
+
+ /*
+ *
+ * should return codecs name
+ */
+ virtual const char* GetName() = 0;
+
+ /*
+ *
+ * How many packets should player remember, so codec
+ * can recover should something cause it to flush
+ * outside of players control
+ */
+ virtual unsigned GetConvergeCount()
+ {
+ return 0;
+ }
+};
diff --git a/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml
new file mode 100644
index 0000000000..d2100397b6
--- /dev/null
+++ b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml
@@ -0,0 +1,6 @@
+<advancedsettings>
+ <video>
+ <defaultplayer>omxplayer</defaultplayer>
+ <defaultdvdplayer>omxplayer</defaultdvdplayer>
+ </video>
+</advancedsettings>
diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h
index 486c09f0e1..255633d02b 100644
--- a/xbmc/cores/playercorefactory/PlayerCoreConfig.h
+++ b/xbmc/cores/playercorefactory/PlayerCoreConfig.h
@@ -28,6 +28,9 @@
#if defined(HAS_AMLPLAYER)
#include "cores/amlplayer/AMLPlayer.h"
#endif
+#if defined(HAS_OMXPLAYER)
+#include "cores/omxplayer/OMXPlayer.h"
+#endif
#include "cores/ExternalPlayer/ExternalPlayer.h"
#include "utils/log.h"
@@ -80,6 +83,9 @@ public:
#if defined(HAS_AMLPLAYER)
case EPC_AMLPLAYER: pPlayer = new CAMLPlayer(callback); break;
#endif
+#if defined(HAS_OMXPLAYER)
+ case EPC_OMXPLAYER: pPlayer = new COMXPlayer(callback); break;
+#endif
default: return NULL;
}
diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
index 9a4506bbf9..96f2973823 100644
--- a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
+++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
@@ -287,6 +287,13 @@ bool CPlayerCoreFactory::LoadConfiguration(TiXmlElement* pConfig, bool clear)
s_vecCoreConfigs.push_back(amlplayer);
#endif
+#if defined(HAS_OMXPLAYER)
+ CPlayerCoreConfig* omxplayer = new CPlayerCoreConfig("OMXPlayer", EPC_OMXPLAYER, NULL);
+ omxplayer->m_bPlaysAudio = true;
+ omxplayer->m_bPlaysVideo = true;
+ s_vecCoreConfigs.push_back(omxplayer);
+#endif
+
for(std::vector<CPlayerSelectionRule *>::iterator it = s_vecCoreSelectionRules.begin(); it != s_vecCoreSelectionRules.end(); it++)
delete *it;
s_vecCoreSelectionRules.clear();
diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.h b/xbmc/cores/playercorefactory/PlayerCoreFactory.h
index 69070e0908..012a27cac1 100644
--- a/xbmc/cores/playercorefactory/PlayerCoreFactory.h
+++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.h
@@ -37,9 +37,12 @@ enum EPLAYERCORES
EPC_DVDPLAYER,
EPC_MPLAYER,
EPC_PAPLAYER,
- EPC_EXTPLAYER,
+ EPC_EXTPLAYER
#if defined(HAS_AMLPLAYER)
- EPC_AMLPLAYER
+ , EPC_AMLPLAYER
+#endif
+#if defined(HAS_OMXPLAYER)
+ , EPC_OMXPLAYER
#endif
};
@@ -52,6 +55,9 @@ const PLAYERCOREID PCID_PAPLAYER = EPC_PAPLAYER;
#if defined(HAS_AMLPLAYER)
const PLAYERCOREID PCID_AMLPLAYER = EPC_AMLPLAYER;
#endif
+#if defined(HAS_OMXPLAYER)
+const PLAYERCOREID PCID_OMXPLAYER = EPC_OMXPLAYER;
+#endif
class CPlayerCoreFactory
{
diff --git a/xbmc/video/windows/GUIWindowFullScreen.cpp b/xbmc/video/windows/GUIWindowFullScreen.cpp
index 1f0fc6c5c1..15fae284bd 100644
--- a/xbmc/video/windows/GUIWindowFullScreen.cpp
+++ b/xbmc/video/windows/GUIWindowFullScreen.cpp
@@ -979,6 +979,9 @@ void CGUIWindowFullScreen::RenderTTFSubtitles()
#if defined(HAS_AMLPLAYER)
g_application.GetCurrentPlayer() == EPC_AMLPLAYER ||
#endif
+#if defined(HAS_OMXPLAYER)
+ g_application.GetCurrentPlayer() == EPC_OMXPLAYER ||
+#endif
g_application.GetCurrentPlayer() == EPC_DVDPLAYER) &&
CUtil::IsUsingTTFSubtitles() && (g_application.m_pPlayer->GetSubtitleVisible()))
{