summaryrefslogtreecommitdiff
path: root/package/xbmc/patches
diff options
context:
space:
mode:
authorWaldemar Brodkorb <wbx@openadk.org>2014-06-25 17:16:55 +0200
committerWaldemar Brodkorb <wbx@openadk.org>2014-06-25 17:16:55 +0200
commit65d1906183283a885b61bb87c023938f29145fe0 (patch)
treea1a7ca582307a90cbb1bf229edcda2a8f786f0d5 /package/xbmc/patches
parent5fccf9a1157ab2a4f2159ab475852ef8a37f9b5b (diff)
add support for hifiberry from Openelec git
Diffstat (limited to 'package/xbmc/patches')
-rw-r--r--package/xbmc/patches/xbmc-gotham_rbp_backports.patch20665
1 files changed, 20665 insertions, 0 deletions
diff --git a/package/xbmc/patches/xbmc-gotham_rbp_backports.patch b/package/xbmc/patches/xbmc-gotham_rbp_backports.patch
new file mode 100644
index 000000000..9a4772437
--- /dev/null
+++ b/package/xbmc/patches/xbmc-gotham_rbp_backports.patch
@@ -0,0 +1,20665 @@
+From 1353d8feca19f2f84019797942d70864054db1b0 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Mon, 5 Aug 2013 13:12:46 +0100
+Subject: [PATCH 01/94] h264_parser: Initialize the h264dsp context in the
+ parser as well
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Each AVStream struct for an H.264 elementary stream actually has two
+copies of the H264DSPContext struct (and in fact all the other members
+of H264Context as well):
+
+((H264Context *) ((AVStream *)st)->codec->priv_data)->h264dsp
+((H264Context *) ((AVStream *)st)->parser->priv_data)->h264dsp
+
+but only the first of these was actually being initialised. This
+prevented the addition of platform-specific implementations of
+parser-related functions.
+
+Signed-off-by: Martin Storsjö <martin@martin.st>
+---
+ lib/ffmpeg/libavcodec/h264_parser.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/lib/ffmpeg/libavcodec/h264_parser.c b/lib/ffmpeg/libavcodec/h264_parser.c
+index aff9ba1..a732f79 100644
+--- a/lib/ffmpeg/libavcodec/h264_parser.c
++++ b/lib/ffmpeg/libavcodec/h264_parser.c
+@@ -386,6 +386,7 @@ static int init(AVCodecParserContext *s)
+ H264Context *h = s->priv_data;
+ h->thread_context[0] = h;
+ h->slice_context_count = 1;
++ ff_h264dsp_init(&h->h264dsp, 8, 1);
+ return 0;
+ }
+
+--
+1.9.3
+
+
+From 7ea2cb68f6fb1149fce70854e36ed6357a267238 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Mon, 5 Aug 2013 13:12:47 +0100
+Subject: [PATCH 02/94] h264dsp: Factorize code into a new function,
+ h264_find_start_code_candidate
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This performs the start code search which was previously part of
+h264_find_frame_end() - the most CPU intensive part of the function.
+
+By itself, this results in a performance regression:
+ Before After
+ Mean StdDev Mean StdDev Change
+Overall time 2925.6 26.2 3068.5 31.7 -4.7%
+
+but this can more than be made up for by platform-optimised
+implementations of the function.
+
+Signed-off-by: Martin Storsjö <martin@martin.st>
+---
+ lib/ffmpeg/libavcodec/h264_parser.c | 20 +++-----------------
+ lib/ffmpeg/libavcodec/h264dsp.c | 29 +++++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/h264dsp.h | 9 +++++++++
+ 3 files changed, 41 insertions(+), 17 deletions(-)
+
+diff --git a/lib/ffmpeg/libavcodec/h264_parser.c b/lib/ffmpeg/libavcodec/h264_parser.c
+index a732f79..972aace 100644
+--- a/lib/ffmpeg/libavcodec/h264_parser.c
++++ b/lib/ffmpeg/libavcodec/h264_parser.c
+@@ -62,23 +62,9 @@ static int ff_h264_find_frame_end(H264Context *h, const uint8_t *buf, int buf_si
+ }
+
+ if(state==7){
+-#if HAVE_FAST_UNALIGNED
+- /* we check i<buf_size instead of i+3/7 because its simpler
+- * and there should be FF_INPUT_BUFFER_PADDING_SIZE bytes at the end
+- */
+-# if HAVE_FAST_64BIT
+- while(i<next_avc && !((~*(const uint64_t*)(buf+i) & (*(const uint64_t*)(buf+i) - 0x0101010101010101ULL)) & 0x8080808080808080ULL))
+- i+=8;
+-# else
+- while(i<next_avc && !((~*(const uint32_t*)(buf+i) & (*(const uint32_t*)(buf+i) - 0x01010101U)) & 0x80808080U))
+- i+=4;
+-# endif
+-#endif
+- for(; i<next_avc; i++){
+- if(!buf[i]){
+- state=2;
+- break;
+- }
++ i += h->h264dsp.h264_find_start_code_candidate(buf + i, buf_size - i);
++ if (i < buf_size)
++ state = 2;
+ }
+ }else if(state<=2){
+ if(buf[i]==1) state^= 5; //2->7, 1->4, 0->5
+diff --git a/lib/ffmpeg/libavcodec/h264dsp.c b/lib/ffmpeg/libavcodec/h264dsp.c
+index da9e417..b7d61cd 100644
+--- a/lib/ffmpeg/libavcodec/h264dsp.c
++++ b/lib/ffmpeg/libavcodec/h264dsp.c
+@@ -60,6 +60,34 @@
+ #include "h264addpx_template.c"
+ #undef BIT_DEPTH
+
++static int h264_find_start_code_candidate_c(const uint8_t *buf, int size)
++{
++ int i = 0;
++#if HAVE_FAST_UNALIGNED
++ /* we check i < size instead of i + 3 / 7 because it is
++ * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE
++ * bytes at the end.
++ */
++#if HAVE_FAST_64BIT
++ while (i < size &&
++ !((~*(const uint64_t *)(buf + i) &
++ (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) &
++ 0x8080808080808080ULL))
++ i += 8;
++#else
++ while (i < size &&
++ !((~*(const uint32_t *)(buf + i) &
++ (*(const uint32_t *)(buf + i) - 0x01010101U)) &
++ 0x80808080U))
++ i += 4;
++#endif
++#endif
++ for (; i < size; i++)
++ if (!buf[i])
++ break;
++ return i;
++}
++
+ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_format_idc)
+ {
+ #undef FUNC
+@@ -146,6 +174,7 @@ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_fo
+ H264_DSP(8);
+ break;
+ }
++ c->h264_find_start_code_candidate = h264_find_start_code_candidate_c;
+
+ if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc);
+ if (HAVE_ALTIVEC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc);
+diff --git a/lib/ffmpeg/libavcodec/h264dsp.h b/lib/ffmpeg/libavcodec/h264dsp.h
+index 98ea15c..1be4804 100644
+--- a/lib/ffmpeg/libavcodec/h264dsp.h
++++ b/lib/ffmpeg/libavcodec/h264dsp.h
+@@ -105,6 +105,15 @@ typedef struct H264DSPContext {
+ /* bypass-transform */
+ void (*h264_add_pixels8_clear)(uint8_t *dst, int16_t *block, int stride);
+ void (*h264_add_pixels4_clear)(uint8_t *dst, int16_t *block, int stride);
++
++ /**
++ * Search buf from the start for up to size bytes. Return the index
++ * of a zero byte, or >= size if not found. Ideally, use lookahead
++ * to filter out any zero bytes that are known to not be followed by
++ * one or more further zero bytes and a one byte. Better still, filter
++ * out any bytes that form the trailing_zero_8bits syntax element too.
++ */
++ int (*h264_find_start_code_candidate)(const uint8_t *buf, int size);
+ } H264DSPContext;
+
+ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth,
+--
+1.9.3
+
+
+From 458ff4b6c1855c529f563dbbd15e35aaab50adae Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Mon, 5 Aug 2013 13:12:48 +0100
+Subject: [PATCH 03/94] arm: Add assembly version of
+ h264_find_start_code_candidate
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+ Before After
+ Mean StdDev Mean StdDev Change
+This function 508.8 23.4 185.4 9.0 +174.4%
+Overall 3068.5 31.7 2752.1 29.4 +11.5%
+
+In combination with the preceding patch:
+ Before After
+ Mean StdDev Mean StdDev Change
+Overall 2925.6 26.2 2752.1 29.4 +6.3%
+
+Signed-off-by: Martin Storsjö <martin@martin.st>
+---
+ lib/ffmpeg/libavcodec/arm/Makefile | 1 +
+ lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S | 253 +++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c | 4 +
+ lib/ffmpeg/libavcodec/h264_parser.c | 1 -
+ 4 files changed, 258 insertions(+), 1 deletion(-)
+ create mode 100644 lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S
+
+diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile
+index 7390a8b..480000b71 100644
+--- a/lib/ffmpeg/libavcodec/arm/Makefile
++++ b/lib/ffmpeg/libavcodec/arm/Makefile
+@@ -9,6 +9,7 @@ OBJS-$(CONFIG_AAC_DECODER) += arm/sbrdsp_init_arm.o \
+ OBJS-$(CONFIG_DCA_DECODER) += arm/dcadsp_init_arm.o \
+
+ ARMV6-OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_armv6.o
++ARMV6-OBJS-$(CONFIG_H264DSP) += arm/h264dsp_armv6.o
+
+ OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \
+ arm/flacdsp_arm.o \
+diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S b/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S
+new file mode 100644
+index 0000000..c4f12a6
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S
+@@ -0,0 +1,253 @@
++/*
++ * Copyright (c) 2013 RISC OS Open Ltd
++ * Author: Ben Avison <bavison@riscosopen.org>
++ *
++ * This file is part of Libav.
++ *
++ * Libav is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * Libav 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with Libav; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include "libavutil/arm/asm.S"
++
++RESULT .req a1
++BUF .req a1
++SIZE .req a2
++PATTERN .req a3
++PTR .req a4
++DAT0 .req v1
++DAT1 .req v2
++DAT2 .req v3
++DAT3 .req v4
++TMP0 .req v5
++TMP1 .req v6
++TMP2 .req ip
++TMP3 .req lr
++
++#define PRELOAD_DISTANCE 4
++
++.macro innerloop4
++ ldr DAT0, [PTR], #4
++ subs SIZE, SIZE, #4 @ C flag survives rest of macro
++ sub TMP0, DAT0, PATTERN, lsr #14
++ bic TMP0, TMP0, DAT0
++ ands TMP0, TMP0, PATTERN
++.endm
++
++.macro innerloop16 decrement, do_preload
++ ldmia PTR!, {DAT0,DAT1,DAT2,DAT3}
++ .ifnc "\do_preload",""
++ pld [PTR, #PRELOAD_DISTANCE*32]
++ .endif
++ .ifnc "\decrement",""
++ subs SIZE, SIZE, #\decrement @ C flag survives rest of macro
++ .endif
++ sub TMP0, DAT0, PATTERN, lsr #14
++ sub TMP1, DAT1, PATTERN, lsr #14
++ bic TMP0, TMP0, DAT0
++ bic TMP1, TMP1, DAT1
++ sub TMP2, DAT2, PATTERN, lsr #14
++ sub TMP3, DAT3, PATTERN, lsr #14
++ ands TMP0, TMP0, PATTERN
++ bic TMP2, TMP2, DAT2
++ it eq
++ andseq TMP1, TMP1, PATTERN
++ bic TMP3, TMP3, DAT3
++ itt eq
++ andseq TMP2, TMP2, PATTERN
++ andseq TMP3, TMP3, PATTERN
++.endm
++
++/* int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size) */
++function ff_h264_find_start_code_candidate_armv6, export=1
++ push {v1-v6,lr}
++ mov PTR, BUF
++ @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go
++ @ before using code that does preloads
++ cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1
++ blo 60f
++
++ @ Get to word-alignment, 1 byte at a time
++ tst PTR, #3
++ beq 2f
++1: ldrb DAT0, [PTR], #1
++ sub SIZE, SIZE, #1
++ teq DAT0, #0
++ beq 90f
++ tst PTR, #3
++ bne 1b
++2: @ Get to 4-word alignment, 1 word at a time
++ ldr PATTERN, =0x80008000
++ setend be
++ tst PTR, #12
++ beq 4f
++3: innerloop4
++ bne 91f
++ tst PTR, #12
++ bne 3b
++4: @ Get to cacheline (8-word) alignment
++ tst PTR, #16
++ beq 5f
++ innerloop16 16
++ bne 93f
++5: @ Check complete cachelines, with preloading
++ @ We need to stop when there are still (PRELOAD_DISTANCE+1)
++ @ complete cachelines to go
++ sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32
++6: innerloop16 , do_preload
++ bne 93f
++ innerloop16 32
++ bne 93f
++ bcs 6b
++ @ Preload trailing part-cacheline, if any
++ tst SIZE, #31
++ beq 7f
++ pld [PTR, #(PRELOAD_DISTANCE+1)*32]
++ @ Check remaining data without doing any more preloads. First
++ @ do in chunks of 4 words:
++7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16
++ bmi 9f
++8: innerloop16 16
++ bne 93f
++ bcs 8b
++ @ Then in words:
++9: adds SIZE, SIZE, #16 - 4
++ bmi 11f
++10: innerloop4
++ bne 91f
++ bcs 10b
++11: setend le
++ @ Check second byte of final halfword
++ ldrb DAT0, [PTR, #-1]
++ teq DAT0, #0
++ beq 90f
++ @ Check any remaining bytes
++ tst SIZE, #3
++ beq 13f
++12: ldrb DAT0, [PTR], #1
++ sub SIZE, SIZE, #1
++ teq DAT0, #0
++ beq 90f
++ tst SIZE, #3
++ bne 12b
++ @ No candidate found
++13: sub RESULT, PTR, BUF
++ b 99f
++
++60: @ Small buffer - simply check by looping over bytes
++ subs SIZE, SIZE, #1
++ bcc 99f
++61: ldrb DAT0, [PTR], #1
++ subs SIZE, SIZE, #1
++ teq DAT0, #0
++ beq 90f
++ bcs 61b
++ @ No candidate found
++ sub RESULT, PTR, BUF
++ b 99f
++
++90: @ Found a candidate at the preceding byte
++ sub RESULT, PTR, BUF
++ sub RESULT, RESULT, #1
++ b 99f
++
++91: @ Found a candidate somewhere in the preceding 4 bytes
++ sub RESULT, PTR, BUF
++ sub RESULT, RESULT, #4
++ sub TMP0, DAT0, #0x20000
++ bics TMP0, TMP0, DAT0
++ itt pl
++ ldrbpl DAT0, [PTR, #-3]
++ addpl RESULT, RESULT, #2
++ bpl 92f
++ teq RESULT, #0
++ beq 98f @ don't look back a byte if found at first byte in buffer
++ ldrb DAT0, [PTR, #-5]
++92: teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++
++93: @ Found a candidate somewhere in the preceding 16 bytes
++ sub RESULT, PTR, BUF
++ sub RESULT, RESULT, #16
++ teq TMP0, #0
++ beq 95f @ not in first 4 bytes
++ sub TMP0, DAT0, #0x20000
++ bics TMP0, TMP0, DAT0
++ itt pl
++ ldrbpl DAT0, [PTR, #-15]
++ addpl RESULT, RESULT, #2
++ bpl 94f
++ teq RESULT, #0
++ beq 98f @ don't look back a byte if found at first byte in buffer
++ ldrb DAT0, [PTR, #-17]
++94: teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++95: add RESULT, RESULT, #4
++ teq TMP1, #0
++ beq 96f @ not in next 4 bytes
++ sub TMP1, DAT1, #0x20000
++ bics TMP1, TMP1, DAT1
++ itee mi
++ ldrbmi DAT0, [PTR, #-13]
++ ldrbpl DAT0, [PTR, #-11]
++ addpl RESULT, RESULT, #2
++ teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++96: add RESULT, RESULT, #4
++ teq TMP2, #0
++ beq 97f @ not in next 4 bytes
++ sub TMP2, DAT2, #0x20000
++ bics TMP2, TMP2, DAT2
++ itee mi
++ ldrbmi DAT0, [PTR, #-9]
++ ldrbpl DAT0, [PTR, #-7]
++ addpl RESULT, RESULT, #2
++ teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++97: add RESULT, RESULT, #4
++ sub TMP3, DAT3, #0x20000
++ bics TMP3, TMP3, DAT3
++ itee mi
++ ldrbmi DAT0, [PTR, #-5]
++ ldrbpl DAT0, [PTR, #-3]
++ addpl RESULT, RESULT, #2
++ teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ @ drop through to 98f
++98: setend le
++99: pop {v1-v6,pc}
++.endfunc
++
++ .unreq RESULT
++ .unreq BUF
++ .unreq SIZE
++ .unreq PATTERN
++ .unreq PTR
++ .unreq DAT0
++ .unreq DAT1
++ .unreq DAT2
++ .unreq DAT3
++ .unreq TMP0
++ .unreq TMP1
++ .unreq TMP2
++ .unreq TMP3
+diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c
+index 785b604..2804e56 100644
+--- a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c
++++ b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c
+@@ -24,6 +24,8 @@
+ #include "libavutil/arm/cpu.h"
+ #include "libavcodec/h264dsp.h"
+
++int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size);
++
+ void ff_h264_v_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha,
+ int beta, int8_t *tc0);
+ void ff_h264_h_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha,
+@@ -106,6 +108,8 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth,
+ {
+ int cpu_flags = av_get_cpu_flags();
+
++ if (have_armv6(cpu_flags))
++ c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6;
+ if (have_neon(cpu_flags))
+ ff_h264dsp_init_neon(c, bit_depth, chroma_format_idc);
+ }
+diff --git a/lib/ffmpeg/libavcodec/h264_parser.c b/lib/ffmpeg/libavcodec/h264_parser.c
+index 972aace..363843c 100644
+--- a/lib/ffmpeg/libavcodec/h264_parser.c
++++ b/lib/ffmpeg/libavcodec/h264_parser.c
+@@ -65,7 +65,6 @@ static int ff_h264_find_frame_end(H264Context *h, const uint8_t *buf, int buf_si
+ i += h->h264dsp.h264_find_start_code_candidate(buf + i, buf_size - i);
+ if (i < buf_size)
+ state = 2;
+- }
+ }else if(state<=2){
+ if(buf[i]==1) state^= 5; //2->7, 1->4, 0->5
+ else if(buf[i]) state = 7;
+--
+1.9.3
+
+
+From 5841d5b69f0df2f286c0a8e419deb16d927e864e Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 19 Aug 2013 22:48:05 +0100
+Subject: [PATCH 04/94] [ffmpeg] Backport of h264_find_start_code_candidate
+ optimisation
+
+---
+ ...-Initialize-the-h264dsp-context-in-the-pa.patch | 39 +++
+ ...torize-code-into-a-new-function-h264_find.patch | 134 +++++++++
+ ...embly-version-of-h264_find_start_code_can.patch | 322 +++++++++++++++++++++
+ 3 files changed, 495 insertions(+)
+ create mode 100644 lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch
+ create mode 100644 lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch
+ create mode 100644 lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch
+
+diff --git a/lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch b/lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch
+new file mode 100644
+index 0000000..263578d
+--- /dev/null
++++ b/lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch
+@@ -0,0 +1,39 @@
++From 7a82022ee2f9b1fad991ace0936901e7419444be Mon Sep 17 00:00:00 2001
++From: Ben Avison <bavison@riscosopen.org>
++Date: Mon, 5 Aug 2013 13:12:46 +0100
++Subject: [PATCH 1/3] h264_parser: Initialize the h264dsp context in the
++ parser as well
++MIME-Version: 1.0
++Content-Type: text/plain; charset=UTF-8
++Content-Transfer-Encoding: 8bit
++
++Each AVStream struct for an H.264 elementary stream actually has two
++copies of the H264DSPContext struct (and in fact all the other members
++of H264Context as well):
++
++((H264Context *) ((AVStream *)st)->codec->priv_data)->h264dsp
++((H264Context *) ((AVStream *)st)->parser->priv_data)->h264dsp
++
++but only the first of these was actually being initialised. This
++prevented the addition of platform-specific implementations of
++parser-related functions.
++
++Signed-off-by: Martin Storsjö <martin@martin.st>
++---
++ libavcodec/h264_parser.c | 1 +
++ 1 file changed, 1 insertion(+)
++
++diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
++index 2ed155c..da2a5f9 100644
++--- a/libavcodec/h264_parser.c
+++++ b/libavcodec/h264_parser.c
++@@ -417,6 +417,7 @@ static av_cold int init(AVCodecParserContext *s)
++ H264Context *h = s->priv_data;
++ h->thread_context[0] = h;
++ h->slice_context_count = 1;
+++ ff_h264dsp_init(&h->h264dsp, 8, 1);
++ return 0;
++ }
++
++--
++1.7.9.5
+diff --git a/lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch b/lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch
+new file mode 100644
+index 0000000..0151d85
+--- /dev/null
++++ b/lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch
+@@ -0,0 +1,134 @@
++From 218d6844b37d339ffbf2044ad07d8be7767e2734 Mon Sep 17 00:00:00 2001
++From: Ben Avison <bavison@riscosopen.org>
++Date: Mon, 5 Aug 2013 13:12:47 +0100
++Subject: [PATCH 2/3] h264dsp: Factorize code into a new function,
++ h264_find_start_code_candidate
++MIME-Version: 1.0
++Content-Type: text/plain; charset=UTF-8
++Content-Transfer-Encoding: 8bit
++
++This performs the start code search which was previously part of
++h264_find_frame_end() - the most CPU intensive part of the function.
++
++By itself, this results in a performance regression:
++ Before After
++ Mean StdDev Mean StdDev Change
++Overall time 2925.6 26.2 3068.5 31.7 -4.7%
++
++but this can more than be made up for by platform-optimised
++implementations of the function.
++
++Signed-off-by: Martin Storsjö <martin@martin.st>
++---
++ libavcodec/h264_parser.c | 27 +++------------------------
++ libavcodec/h264dsp.c | 29 +++++++++++++++++++++++++++++
++ libavcodec/h264dsp.h | 9 +++++++++
++ 3 files changed, 41 insertions(+), 24 deletions(-)
++
++diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
++index da2a5f9..ef5da98 100644
++--- a/libavcodec/h264_parser.c
+++++ b/libavcodec/h264_parser.c
++@@ -47,30 +47,9 @@ static int h264_find_frame_end(H264Context *h, const uint8_t *buf,
++
++ for (i = 0; i < buf_size; i++) {
++ if (state == 7) {
++-#if HAVE_FAST_UNALIGNED
++- /* we check i < buf_size instead of i + 3 / 7 because it is
++- * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE
++- * bytes at the end.
++- */
++-#if HAVE_FAST_64BIT
++- while (i < buf_size &&
++- !((~*(const uint64_t *)(buf + i) &
++- (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) &
++- 0x8080808080808080ULL))
++- i += 8;
++-#else
++- while (i < buf_size &&
++- !((~*(const uint32_t *)(buf + i) &
++- (*(const uint32_t *)(buf + i) - 0x01010101U)) &
++- 0x80808080U))
++- i += 4;
++-#endif
++-#endif
++- for (; i < buf_size; i++)
++- if (!buf[i]) {
++- state = 2;
++- break;
++- }
+++ i += h->h264dsp.h264_find_start_code_candidate(buf + i, buf_size - i);
+++ if (i < buf_size)
+++ state = 2;
++ } else if (state <= 2) {
++ if (buf[i] == 1)
++ state ^= 5; // 2->7, 1->4, 0->5
++diff --git a/libavcodec/h264dsp.c b/libavcodec/h264dsp.c
++index 3ca6abe..a901dbb 100644
++--- a/libavcodec/h264dsp.c
+++++ b/libavcodec/h264dsp.c
++@@ -53,6 +53,34 @@
++ #include "h264addpx_template.c"
++ #undef BIT_DEPTH
++
+++static int h264_find_start_code_candidate_c(const uint8_t *buf, int size)
+++{
+++ int i = 0;
+++#if HAVE_FAST_UNALIGNED
+++ /* we check i < size instead of i + 3 / 7 because it is
+++ * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE
+++ * bytes at the end.
+++ */
+++#if HAVE_FAST_64BIT
+++ while (i < size &&
+++ !((~*(const uint64_t *)(buf + i) &
+++ (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) &
+++ 0x8080808080808080ULL))
+++ i += 8;
+++#else
+++ while (i < size &&
+++ !((~*(const uint32_t *)(buf + i) &
+++ (*(const uint32_t *)(buf + i) - 0x01010101U)) &
+++ 0x80808080U))
+++ i += 4;
+++#endif
+++#endif
+++ for (; i < size; i++)
+++ if (!buf[i])
+++ break;
+++ return i;
+++}
+++
++ av_cold void ff_h264dsp_init(H264DSPContext *c, const int bit_depth,
++ const int chroma_format_idc)
++ {
++@@ -133,6 +161,7 @@ av_cold void ff_h264dsp_init(H264DSPContext *c, const int bit_depth,
++ H264_DSP(8);
++ break;
++ }
+++ c->h264_find_start_code_candidate = h264_find_start_code_candidate_c;
++
++ if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc);
++ if (ARCH_PPC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc);
++diff --git a/libavcodec/h264dsp.h b/libavcodec/h264dsp.h
++index 1f9f8fe..6249ba7 100644
++--- a/libavcodec/h264dsp.h
+++++ b/libavcodec/h264dsp.h
++@@ -105,6 +105,15 @@ typedef struct H264DSPContext {
++ /* bypass-transform */
++ void (*h264_add_pixels8_clear)(uint8_t *dst, int16_t *block, int stride);
++ void (*h264_add_pixels4_clear)(uint8_t *dst, int16_t *block, int stride);
+++
+++ /**
+++ * Search buf from the start for up to size bytes. Return the index
+++ * of a zero byte, or >= size if not found. Ideally, use lookahead
+++ * to filter out any zero bytes that are known to not be followed by
+++ * one or more further zero bytes and a one byte. Better still, filter
+++ * out any bytes that form the trailing_zero_8bits syntax element too.
+++ */
+++ int (*h264_find_start_code_candidate)(const uint8_t *buf, int size);
++ } H264DSPContext;
++
++ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth,
++--
++1.7.9.5
+diff --git a/lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch b/lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch
+new file mode 100644
+index 0000000..cdc2d1e
+--- /dev/null
++++ b/lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch
+@@ -0,0 +1,322 @@
++From 45e10e5c8d3df09c80a4d80483bff2712367f3fa Mon Sep 17 00:00:00 2001
++From: Ben Avison <bavison@riscosopen.org>
++Date: Mon, 5 Aug 2013 13:12:48 +0100
++Subject: [PATCH 3/3] arm: Add assembly version of
++ h264_find_start_code_candidate
++MIME-Version: 1.0
++Content-Type: text/plain; charset=UTF-8
++Content-Transfer-Encoding: 8bit
++
++ Before After
++ Mean StdDev Mean StdDev Change
++This function 508.8 23.4 185.4 9.0 +174.4%
++Overall 3068.5 31.7 2752.1 29.4 +11.5%
++
++In combination with the preceding patch:
++ Before After
++ Mean StdDev Mean StdDev Change
++Overall 2925.6 26.2 2752.1 29.4 +6.3%
++
++Signed-off-by: Martin Storsjö <martin@martin.st>
++---
++ libavcodec/arm/Makefile | 1 +
++ libavcodec/arm/h264dsp_armv6.S | 253 +++++++++++++++++++++++++++++++++++++
++ libavcodec/arm/h264dsp_init_arm.c | 4 +
++ 3 files changed, 258 insertions(+)
++ create mode 100644 libavcodec/arm/h264dsp_armv6.S
++
++diff --git a/libavcodec/arm/Makefile b/libavcodec/arm/Makefile
++index e941aaa..9c64b36 100644
++--- a/libavcodec/arm/Makefile
+++++ b/libavcodec/arm/Makefile
++@@ -45,6 +45,7 @@ ARMV6-OBJS-$(CONFIG_DSPUTIL) += arm/dsputil_init_armv6.o \
++ arm/simple_idct_armv6.o \
++
++ ARMV6-OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_armv6.o
+++ARMV6-OBJS-$(CONFIG_H264DSP) += arm/h264dsp_armv6.o
++ ARMV6-OBJS-$(CONFIG_HPELDSP) += arm/hpeldsp_init_armv6.o \
++ arm/hpeldsp_armv6.o
++ ARMV6-OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_fixed_armv6.o
++diff --git a/libavcodec/arm/h264dsp_armv6.S b/libavcodec/arm/h264dsp_armv6.S
++new file mode 100644
++index 0000000..c4f12a6
++--- /dev/null
+++++ b/libavcodec/arm/h264dsp_armv6.S
++@@ -0,0 +1,253 @@
+++/*
+++ * Copyright (c) 2013 RISC OS Open Ltd
+++ * Author: Ben Avison <bavison@riscosopen.org>
+++ *
+++ * This file is part of Libav.
+++ *
+++ * Libav is free software; you can redistribute it and/or
+++ * modify it under the terms of the GNU Lesser General Public
+++ * License as published by the Free Software Foundation; either
+++ * version 2.1 of the License, or (at your option) any later version.
+++ *
+++ * Libav 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
+++ * Lesser General Public License for more details.
+++ *
+++ * You should have received a copy of the GNU Lesser General Public
+++ * License along with Libav; if not, write to the Free Software
+++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+++ */
+++
+++#include "libavutil/arm/asm.S"
+++
+++RESULT .req a1
+++BUF .req a1
+++SIZE .req a2
+++PATTERN .req a3
+++PTR .req a4
+++DAT0 .req v1
+++DAT1 .req v2
+++DAT2 .req v3
+++DAT3 .req v4
+++TMP0 .req v5
+++TMP1 .req v6
+++TMP2 .req ip
+++TMP3 .req lr
+++
+++#define PRELOAD_DISTANCE 4
+++
+++.macro innerloop4
+++ ldr DAT0, [PTR], #4
+++ subs SIZE, SIZE, #4 @ C flag survives rest of macro
+++ sub TMP0, DAT0, PATTERN, lsr #14
+++ bic TMP0, TMP0, DAT0
+++ ands TMP0, TMP0, PATTERN
+++.endm
+++
+++.macro innerloop16 decrement, do_preload
+++ ldmia PTR!, {DAT0,DAT1,DAT2,DAT3}
+++ .ifnc "\do_preload",""
+++ pld [PTR, #PRELOAD_DISTANCE*32]
+++ .endif
+++ .ifnc "\decrement",""
+++ subs SIZE, SIZE, #\decrement @ C flag survives rest of macro
+++ .endif
+++ sub TMP0, DAT0, PATTERN, lsr #14
+++ sub TMP1, DAT1, PATTERN, lsr #14
+++ bic TMP0, TMP0, DAT0
+++ bic TMP1, TMP1, DAT1
+++ sub TMP2, DAT2, PATTERN, lsr #14
+++ sub TMP3, DAT3, PATTERN, lsr #14
+++ ands TMP0, TMP0, PATTERN
+++ bic TMP2, TMP2, DAT2
+++ it eq
+++ andseq TMP1, TMP1, PATTERN
+++ bic TMP3, TMP3, DAT3
+++ itt eq
+++ andseq TMP2, TMP2, PATTERN
+++ andseq TMP3, TMP3, PATTERN
+++.endm
+++
+++/* int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size) */
+++function ff_h264_find_start_code_candidate_armv6, export=1
+++ push {v1-v6,lr}
+++ mov PTR, BUF
+++ @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go
+++ @ before using code that does preloads
+++ cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1
+++ blo 60f
+++
+++ @ Get to word-alignment, 1 byte at a time
+++ tst PTR, #3
+++ beq 2f
+++1: ldrb DAT0, [PTR], #1
+++ sub SIZE, SIZE, #1
+++ teq DAT0, #0
+++ beq 90f
+++ tst PTR, #3
+++ bne 1b
+++2: @ Get to 4-word alignment, 1 word at a time
+++ ldr PATTERN, =0x80008000
+++ setend be
+++ tst PTR, #12
+++ beq 4f
+++3: innerloop4
+++ bne 91f
+++ tst PTR, #12
+++ bne 3b
+++4: @ Get to cacheline (8-word) alignment
+++ tst PTR, #16
+++ beq 5f
+++ innerloop16 16
+++ bne 93f
+++5: @ Check complete cachelines, with preloading
+++ @ We need to stop when there are still (PRELOAD_DISTANCE+1)
+++ @ complete cachelines to go
+++ sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32
+++6: innerloop16 , do_preload
+++ bne 93f
+++ innerloop16 32
+++ bne 93f
+++ bcs 6b
+++ @ Preload trailing part-cacheline, if any
+++ tst SIZE, #31
+++ beq 7f
+++ pld [PTR, #(PRELOAD_DISTANCE+1)*32]
+++ @ Check remaining data without doing any more preloads. First
+++ @ do in chunks of 4 words:
+++7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16
+++ bmi 9f
+++8: innerloop16 16
+++ bne 93f
+++ bcs 8b
+++ @ Then in words:
+++9: adds SIZE, SIZE, #16 - 4
+++ bmi 11f
+++10: innerloop4
+++ bne 91f
+++ bcs 10b
+++11: setend le
+++ @ Check second byte of final halfword
+++ ldrb DAT0, [PTR, #-1]
+++ teq DAT0, #0
+++ beq 90f
+++ @ Check any remaining bytes
+++ tst SIZE, #3
+++ beq 13f
+++12: ldrb DAT0, [PTR], #1
+++ sub SIZE, SIZE, #1
+++ teq DAT0, #0
+++ beq 90f
+++ tst SIZE, #3
+++ bne 12b
+++ @ No candidate found
+++13: sub RESULT, PTR, BUF
+++ b 99f
+++
+++60: @ Small buffer - simply check by looping over bytes
+++ subs SIZE, SIZE, #1
+++ bcc 99f
+++61: ldrb DAT0, [PTR], #1
+++ subs SIZE, SIZE, #1
+++ teq DAT0, #0
+++ beq 90f
+++ bcs 61b
+++ @ No candidate found
+++ sub RESULT, PTR, BUF
+++ b 99f
+++
+++90: @ Found a candidate at the preceding byte
+++ sub RESULT, PTR, BUF
+++ sub RESULT, RESULT, #1
+++ b 99f
+++
+++91: @ Found a candidate somewhere in the preceding 4 bytes
+++ sub RESULT, PTR, BUF
+++ sub RESULT, RESULT, #4
+++ sub TMP0, DAT0, #0x20000
+++ bics TMP0, TMP0, DAT0
+++ itt pl
+++ ldrbpl DAT0, [PTR, #-3]
+++ addpl RESULT, RESULT, #2
+++ bpl 92f
+++ teq RESULT, #0
+++ beq 98f @ don't look back a byte if found at first byte in buffer
+++ ldrb DAT0, [PTR, #-5]
+++92: teq DAT0, #0
+++ it eq
+++ subeq RESULT, RESULT, #1
+++ b 98f
+++
+++93: @ Found a candidate somewhere in the preceding 16 bytes
+++ sub RESULT, PTR, BUF
+++ sub RESULT, RESULT, #16
+++ teq TMP0, #0
+++ beq 95f @ not in first 4 bytes
+++ sub TMP0, DAT0, #0x20000
+++ bics TMP0, TMP0, DAT0
+++ itt pl
+++ ldrbpl DAT0, [PTR, #-15]
+++ addpl RESULT, RESULT, #2
+++ bpl 94f
+++ teq RESULT, #0
+++ beq 98f @ don't look back a byte if found at first byte in buffer
+++ ldrb DAT0, [PTR, #-17]
+++94: teq DAT0, #0
+++ it eq
+++ subeq RESULT, RESULT, #1
+++ b 98f
+++95: add RESULT, RESULT, #4
+++ teq TMP1, #0
+++ beq 96f @ not in next 4 bytes
+++ sub TMP1, DAT1, #0x20000
+++ bics TMP1, TMP1, DAT1
+++ itee mi
+++ ldrbmi DAT0, [PTR, #-13]
+++ ldrbpl DAT0, [PTR, #-11]
+++ addpl RESULT, RESULT, #2
+++ teq DAT0, #0
+++ it eq
+++ subeq RESULT, RESULT, #1
+++ b 98f
+++96: add RESULT, RESULT, #4
+++ teq TMP2, #0
+++ beq 97f @ not in next 4 bytes
+++ sub TMP2, DAT2, #0x20000
+++ bics TMP2, TMP2, DAT2
+++ itee mi
+++ ldrbmi DAT0, [PTR, #-9]
+++ ldrbpl DAT0, [PTR, #-7]
+++ addpl RESULT, RESULT, #2
+++ teq DAT0, #0
+++ it eq
+++ subeq RESULT, RESULT, #1
+++ b 98f
+++97: add RESULT, RESULT, #4
+++ sub TMP3, DAT3, #0x20000
+++ bics TMP3, TMP3, DAT3
+++ itee mi
+++ ldrbmi DAT0, [PTR, #-5]
+++ ldrbpl DAT0, [PTR, #-3]
+++ addpl RESULT, RESULT, #2
+++ teq DAT0, #0
+++ it eq
+++ subeq RESULT, RESULT, #1
+++ @ drop through to 98f
+++98: setend le
+++99: pop {v1-v6,pc}
+++.endfunc
+++
+++ .unreq RESULT
+++ .unreq BUF
+++ .unreq SIZE
+++ .unreq PATTERN
+++ .unreq PTR
+++ .unreq DAT0
+++ .unreq DAT1
+++ .unreq DAT2
+++ .unreq DAT3
+++ .unreq TMP0
+++ .unreq TMP1
+++ .unreq TMP2
+++ .unreq TMP3
++diff --git a/libavcodec/arm/h264dsp_init_arm.c b/libavcodec/arm/h264dsp_init_arm.c
++index bb8b3b9..b206a1b 100644
++--- a/libavcodec/arm/h264dsp_init_arm.c
+++++ b/libavcodec/arm/h264dsp_init_arm.c
++@@ -24,6 +24,8 @@
++ #include "libavutil/arm/cpu.h"
++ #include "libavcodec/h264dsp.h"
++
+++int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size);
+++
++ void ff_h264_v_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha,
++ int beta, int8_t *tc0);
++ void ff_h264_h_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha,
++@@ -102,6 +104,8 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth,
++ {
++ int cpu_flags = av_get_cpu_flags();
++
+++ if (have_armv6(cpu_flags))
+++ c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6;
++ if (have_neon(cpu_flags))
++ h264dsp_init_neon(c, bit_depth, chroma_format_idc);
++ }
++--
++1.7.9.5
+--
+1.9.3
+
+
+From db098a580259625bb7b7385a48cb0756aea5cafe Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 16 Apr 2014 01:51:31 +0100
+Subject: [PATCH 05/94] h264: Move search code search functions into separate
+ source files.
+
+This permits re-use with parsers for codecs which use similar start codes.
+
+Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
+---
+ lib/ffmpeg/libavcodec/Makefile | 2 +-
+ lib/ffmpeg/libavcodec/arm/Makefile | 2 +-
+ lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S | 253 ---------------------------
+ lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c | 4 +-
+ lib/ffmpeg/libavcodec/arm/startcode_armv6.S | 253 +++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/h264dsp.c | 31 +---
+ lib/ffmpeg/libavcodec/startcode.c | 57 ++++++
+ lib/ffmpeg/libavcodec/startcode.h | 35 ++++
+ 8 files changed, 351 insertions(+), 286 deletions(-)
+ delete mode 100644 lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S
+ create mode 100644 lib/ffmpeg/libavcodec/arm/startcode_armv6.S
+ create mode 100644 lib/ffmpeg/libavcodec/startcode.c
+ create mode 100644 lib/ffmpeg/libavcodec/startcode.h
+
+diff --git a/lib/ffmpeg/libavcodec/Makefile b/lib/ffmpeg/libavcodec/Makefile
+index dc065a5..460f42c 100644
+--- a/lib/ffmpeg/libavcodec/Makefile
++++ b/lib/ffmpeg/libavcodec/Makefile
+@@ -49,7 +49,7 @@ OBJS-$(CONFIG_FFT) += avfft.o fft_fixed.o fft_float.o \
+ $(FFT-OBJS-yes)
+ OBJS-$(CONFIG_GOLOMB) += golomb.o
+ OBJS-$(CONFIG_H264CHROMA) += h264chroma.o
+-OBJS-$(CONFIG_H264DSP) += h264dsp.o h264idct.o
++OBJS-$(CONFIG_H264DSP) += h264dsp.o h264idct.o startcode.o
+ OBJS-$(CONFIG_H264PRED) += h264pred.o
+ OBJS-$(CONFIG_H264QPEL) += h264qpel.o
+ OBJS-$(CONFIG_HUFFMAN) += huffman.o
+diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile
+index 480000b71..0b432e3 100644
+--- a/lib/ffmpeg/libavcodec/arm/Makefile
++++ b/lib/ffmpeg/libavcodec/arm/Makefile
+@@ -9,7 +9,7 @@ OBJS-$(CONFIG_AAC_DECODER) += arm/sbrdsp_init_arm.o \
+ OBJS-$(CONFIG_DCA_DECODER) += arm/dcadsp_init_arm.o \
+
+ ARMV6-OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_armv6.o
+-ARMV6-OBJS-$(CONFIG_H264DSP) += arm/h264dsp_armv6.o
++ARMV6-OBJS-$(CONFIG_H264DSP) += arm/startcode_armv6.o
+
+ OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \
+ arm/flacdsp_arm.o \
+diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S b/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S
+deleted file mode 100644
+index c4f12a6..0000000
+--- a/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S
++++ /dev/null
+@@ -1,253 +0,0 @@
+-/*
+- * Copyright (c) 2013 RISC OS Open Ltd
+- * Author: Ben Avison <bavison@riscosopen.org>
+- *
+- * This file is part of Libav.
+- *
+- * Libav is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU Lesser General Public
+- * License as published by the Free Software Foundation; either
+- * version 2.1 of the License, or (at your option) any later version.
+- *
+- * Libav 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
+- * Lesser General Public License for more details.
+- *
+- * You should have received a copy of the GNU Lesser General Public
+- * License along with Libav; if not, write to the Free Software
+- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+- */
+-
+-#include "libavutil/arm/asm.S"
+-
+-RESULT .req a1
+-BUF .req a1
+-SIZE .req a2
+-PATTERN .req a3
+-PTR .req a4
+-DAT0 .req v1
+-DAT1 .req v2
+-DAT2 .req v3
+-DAT3 .req v4
+-TMP0 .req v5
+-TMP1 .req v6
+-TMP2 .req ip
+-TMP3 .req lr
+-
+-#define PRELOAD_DISTANCE 4
+-
+-.macro innerloop4
+- ldr DAT0, [PTR], #4
+- subs SIZE, SIZE, #4 @ C flag survives rest of macro
+- sub TMP0, DAT0, PATTERN, lsr #14
+- bic TMP0, TMP0, DAT0
+- ands TMP0, TMP0, PATTERN
+-.endm
+-
+-.macro innerloop16 decrement, do_preload
+- ldmia PTR!, {DAT0,DAT1,DAT2,DAT3}
+- .ifnc "\do_preload",""
+- pld [PTR, #PRELOAD_DISTANCE*32]
+- .endif
+- .ifnc "\decrement",""
+- subs SIZE, SIZE, #\decrement @ C flag survives rest of macro
+- .endif
+- sub TMP0, DAT0, PATTERN, lsr #14
+- sub TMP1, DAT1, PATTERN, lsr #14
+- bic TMP0, TMP0, DAT0
+- bic TMP1, TMP1, DAT1
+- sub TMP2, DAT2, PATTERN, lsr #14
+- sub TMP3, DAT3, PATTERN, lsr #14
+- ands TMP0, TMP0, PATTERN
+- bic TMP2, TMP2, DAT2
+- it eq
+- andseq TMP1, TMP1, PATTERN
+- bic TMP3, TMP3, DAT3
+- itt eq
+- andseq TMP2, TMP2, PATTERN
+- andseq TMP3, TMP3, PATTERN
+-.endm
+-
+-/* int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size) */
+-function ff_h264_find_start_code_candidate_armv6, export=1
+- push {v1-v6,lr}
+- mov PTR, BUF
+- @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go
+- @ before using code that does preloads
+- cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1
+- blo 60f
+-
+- @ Get to word-alignment, 1 byte at a time
+- tst PTR, #3
+- beq 2f
+-1: ldrb DAT0, [PTR], #1
+- sub SIZE, SIZE, #1
+- teq DAT0, #0
+- beq 90f
+- tst PTR, #3
+- bne 1b
+-2: @ Get to 4-word alignment, 1 word at a time
+- ldr PATTERN, =0x80008000
+- setend be
+- tst PTR, #12
+- beq 4f
+-3: innerloop4
+- bne 91f
+- tst PTR, #12
+- bne 3b
+-4: @ Get to cacheline (8-word) alignment
+- tst PTR, #16
+- beq 5f
+- innerloop16 16
+- bne 93f
+-5: @ Check complete cachelines, with preloading
+- @ We need to stop when there are still (PRELOAD_DISTANCE+1)
+- @ complete cachelines to go
+- sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32
+-6: innerloop16 , do_preload
+- bne 93f
+- innerloop16 32
+- bne 93f
+- bcs 6b
+- @ Preload trailing part-cacheline, if any
+- tst SIZE, #31
+- beq 7f
+- pld [PTR, #(PRELOAD_DISTANCE+1)*32]
+- @ Check remaining data without doing any more preloads. First
+- @ do in chunks of 4 words:
+-7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16
+- bmi 9f
+-8: innerloop16 16
+- bne 93f
+- bcs 8b
+- @ Then in words:
+-9: adds SIZE, SIZE, #16 - 4
+- bmi 11f
+-10: innerloop4
+- bne 91f
+- bcs 10b
+-11: setend le
+- @ Check second byte of final halfword
+- ldrb DAT0, [PTR, #-1]
+- teq DAT0, #0
+- beq 90f
+- @ Check any remaining bytes
+- tst SIZE, #3
+- beq 13f
+-12: ldrb DAT0, [PTR], #1
+- sub SIZE, SIZE, #1
+- teq DAT0, #0
+- beq 90f
+- tst SIZE, #3
+- bne 12b
+- @ No candidate found
+-13: sub RESULT, PTR, BUF
+- b 99f
+-
+-60: @ Small buffer - simply check by looping over bytes
+- subs SIZE, SIZE, #1
+- bcc 99f
+-61: ldrb DAT0, [PTR], #1
+- subs SIZE, SIZE, #1
+- teq DAT0, #0
+- beq 90f
+- bcs 61b
+- @ No candidate found
+- sub RESULT, PTR, BUF
+- b 99f
+-
+-90: @ Found a candidate at the preceding byte
+- sub RESULT, PTR, BUF
+- sub RESULT, RESULT, #1
+- b 99f
+-
+-91: @ Found a candidate somewhere in the preceding 4 bytes
+- sub RESULT, PTR, BUF
+- sub RESULT, RESULT, #4
+- sub TMP0, DAT0, #0x20000
+- bics TMP0, TMP0, DAT0
+- itt pl
+- ldrbpl DAT0, [PTR, #-3]
+- addpl RESULT, RESULT, #2
+- bpl 92f
+- teq RESULT, #0
+- beq 98f @ don't look back a byte if found at first byte in buffer
+- ldrb DAT0, [PTR, #-5]
+-92: teq DAT0, #0
+- it eq
+- subeq RESULT, RESULT, #1
+- b 98f
+-
+-93: @ Found a candidate somewhere in the preceding 16 bytes
+- sub RESULT, PTR, BUF
+- sub RESULT, RESULT, #16
+- teq TMP0, #0
+- beq 95f @ not in first 4 bytes
+- sub TMP0, DAT0, #0x20000
+- bics TMP0, TMP0, DAT0
+- itt pl
+- ldrbpl DAT0, [PTR, #-15]
+- addpl RESULT, RESULT, #2
+- bpl 94f
+- teq RESULT, #0
+- beq 98f @ don't look back a byte if found at first byte in buffer
+- ldrb DAT0, [PTR, #-17]
+-94: teq DAT0, #0
+- it eq
+- subeq RESULT, RESULT, #1
+- b 98f
+-95: add RESULT, RESULT, #4
+- teq TMP1, #0
+- beq 96f @ not in next 4 bytes
+- sub TMP1, DAT1, #0x20000
+- bics TMP1, TMP1, DAT1
+- itee mi
+- ldrbmi DAT0, [PTR, #-13]
+- ldrbpl DAT0, [PTR, #-11]
+- addpl RESULT, RESULT, #2
+- teq DAT0, #0
+- it eq
+- subeq RESULT, RESULT, #1
+- b 98f
+-96: add RESULT, RESULT, #4
+- teq TMP2, #0
+- beq 97f @ not in next 4 bytes
+- sub TMP2, DAT2, #0x20000
+- bics TMP2, TMP2, DAT2
+- itee mi
+- ldrbmi DAT0, [PTR, #-9]
+- ldrbpl DAT0, [PTR, #-7]
+- addpl RESULT, RESULT, #2
+- teq DAT0, #0
+- it eq
+- subeq RESULT, RESULT, #1
+- b 98f
+-97: add RESULT, RESULT, #4
+- sub TMP3, DAT3, #0x20000
+- bics TMP3, TMP3, DAT3
+- itee mi
+- ldrbmi DAT0, [PTR, #-5]
+- ldrbpl DAT0, [PTR, #-3]
+- addpl RESULT, RESULT, #2
+- teq DAT0, #0
+- it eq
+- subeq RESULT, RESULT, #1
+- @ drop through to 98f
+-98: setend le
+-99: pop {v1-v6,pc}
+-.endfunc
+-
+- .unreq RESULT
+- .unreq BUF
+- .unreq SIZE
+- .unreq PATTERN
+- .unreq PTR
+- .unreq DAT0
+- .unreq DAT1
+- .unreq DAT2
+- .unreq DAT3
+- .unreq TMP0
+- .unreq TMP1
+- .unreq TMP2
+- .unreq TMP3
+diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c
+index 2804e56..842fb9f 100644
+--- a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c
++++ b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c
+@@ -24,7 +24,7 @@
+ #include "libavutil/arm/cpu.h"
+ #include "libavcodec/h264dsp.h"
+
+-int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size);
++int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size);
+
+ void ff_h264_v_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha,
+ int beta, int8_t *tc0);
+@@ -109,7 +109,7 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth,
+ int cpu_flags = av_get_cpu_flags();
+
+ if (have_armv6(cpu_flags))
+- c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6;
++ c->h264_find_start_code_candidate = ff_startcode_find_candidate_armv6;
+ if (have_neon(cpu_flags))
+ ff_h264dsp_init_neon(c, bit_depth, chroma_format_idc);
+ }
+diff --git a/lib/ffmpeg/libavcodec/arm/startcode_armv6.S b/lib/ffmpeg/libavcodec/arm/startcode_armv6.S
+new file mode 100644
+index 0000000..a46f009
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/arm/startcode_armv6.S
+@@ -0,0 +1,253 @@
++/*
++ * Copyright (c) 2013 RISC OS Open Ltd
++ * Author: Ben Avison <bavison@riscosopen.org>
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * FFmpeg 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with FFmpeg; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include "libavutil/arm/asm.S"
++
++RESULT .req a1
++BUF .req a1
++SIZE .req a2
++PATTERN .req a3
++PTR .req a4
++DAT0 .req v1
++DAT1 .req v2
++DAT2 .req v3
++DAT3 .req v4
++TMP0 .req v5
++TMP1 .req v6
++TMP2 .req ip
++TMP3 .req lr
++
++#define PRELOAD_DISTANCE 4
++
++.macro innerloop4
++ ldr DAT0, [PTR], #4
++ subs SIZE, SIZE, #4 @ C flag survives rest of macro
++ sub TMP0, DAT0, PATTERN, lsr #14
++ bic TMP0, TMP0, DAT0
++ ands TMP0, TMP0, PATTERN
++.endm
++
++.macro innerloop16 decrement, do_preload
++ ldmia PTR!, {DAT0,DAT1,DAT2,DAT3}
++ .ifnc "\do_preload",""
++ pld [PTR, #PRELOAD_DISTANCE*32]
++ .endif
++ .ifnc "\decrement",""
++ subs SIZE, SIZE, #\decrement @ C flag survives rest of macro
++ .endif
++ sub TMP0, DAT0, PATTERN, lsr #14
++ sub TMP1, DAT1, PATTERN, lsr #14
++ bic TMP0, TMP0, DAT0
++ bic TMP1, TMP1, DAT1
++ sub TMP2, DAT2, PATTERN, lsr #14
++ sub TMP3, DAT3, PATTERN, lsr #14
++ ands TMP0, TMP0, PATTERN
++ bic TMP2, TMP2, DAT2
++ it eq
++ andseq TMP1, TMP1, PATTERN
++ bic TMP3, TMP3, DAT3
++ itt eq
++ andseq TMP2, TMP2, PATTERN
++ andseq TMP3, TMP3, PATTERN
++.endm
++
++/* int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size) */
++function ff_startcode_find_candidate_armv6, export=1
++ push {v1-v6,lr}
++ mov PTR, BUF
++ @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go
++ @ before using code that does preloads
++ cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1
++ blo 60f
++
++ @ Get to word-alignment, 1 byte at a time
++ tst PTR, #3
++ beq 2f
++1: ldrb DAT0, [PTR], #1
++ sub SIZE, SIZE, #1
++ teq DAT0, #0
++ beq 90f
++ tst PTR, #3
++ bne 1b
++2: @ Get to 4-word alignment, 1 word at a time
++ ldr PATTERN, =0x80008000
++ setend be
++ tst PTR, #12
++ beq 4f
++3: innerloop4
++ bne 91f
++ tst PTR, #12
++ bne 3b
++4: @ Get to cacheline (8-word) alignment
++ tst PTR, #16
++ beq 5f
++ innerloop16 16
++ bne 93f
++5: @ Check complete cachelines, with preloading
++ @ We need to stop when there are still (PRELOAD_DISTANCE+1)
++ @ complete cachelines to go
++ sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32
++6: innerloop16 , do_preload
++ bne 93f
++ innerloop16 32
++ bne 93f
++ bcs 6b
++ @ Preload trailing part-cacheline, if any
++ tst SIZE, #31
++ beq 7f
++ pld [PTR, #(PRELOAD_DISTANCE+1)*32]
++ @ Check remaining data without doing any more preloads. First
++ @ do in chunks of 4 words:
++7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16
++ bmi 9f
++8: innerloop16 16
++ bne 93f
++ bcs 8b
++ @ Then in words:
++9: adds SIZE, SIZE, #16 - 4
++ bmi 11f
++10: innerloop4
++ bne 91f
++ bcs 10b
++11: setend le
++ @ Check second byte of final halfword
++ ldrb DAT0, [PTR, #-1]
++ teq DAT0, #0
++ beq 90f
++ @ Check any remaining bytes
++ tst SIZE, #3
++ beq 13f
++12: ldrb DAT0, [PTR], #1
++ sub SIZE, SIZE, #1
++ teq DAT0, #0
++ beq 90f
++ tst SIZE, #3
++ bne 12b
++ @ No candidate found
++13: sub RESULT, PTR, BUF
++ b 99f
++
++60: @ Small buffer - simply check by looping over bytes
++ subs SIZE, SIZE, #1
++ bcc 99f
++61: ldrb DAT0, [PTR], #1
++ subs SIZE, SIZE, #1
++ teq DAT0, #0
++ beq 90f
++ bcs 61b
++ @ No candidate found
++ sub RESULT, PTR, BUF
++ b 99f
++
++90: @ Found a candidate at the preceding byte
++ sub RESULT, PTR, BUF
++ sub RESULT, RESULT, #1
++ b 99f
++
++91: @ Found a candidate somewhere in the preceding 4 bytes
++ sub RESULT, PTR, BUF
++ sub RESULT, RESULT, #4
++ sub TMP0, DAT0, #0x20000
++ bics TMP0, TMP0, DAT0
++ itt pl
++ ldrbpl DAT0, [PTR, #-3]
++ addpl RESULT, RESULT, #2
++ bpl 92f
++ teq RESULT, #0
++ beq 98f @ don't look back a byte if found at first byte in buffer
++ ldrb DAT0, [PTR, #-5]
++92: teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++
++93: @ Found a candidate somewhere in the preceding 16 bytes
++ sub RESULT, PTR, BUF
++ sub RESULT, RESULT, #16
++ teq TMP0, #0
++ beq 95f @ not in first 4 bytes
++ sub TMP0, DAT0, #0x20000
++ bics TMP0, TMP0, DAT0
++ itt pl
++ ldrbpl DAT0, [PTR, #-15]
++ addpl RESULT, RESULT, #2
++ bpl 94f
++ teq RESULT, #0
++ beq 98f @ don't look back a byte if found at first byte in buffer
++ ldrb DAT0, [PTR, #-17]
++94: teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++95: add RESULT, RESULT, #4
++ teq TMP1, #0
++ beq 96f @ not in next 4 bytes
++ sub TMP1, DAT1, #0x20000
++ bics TMP1, TMP1, DAT1
++ itee mi
++ ldrbmi DAT0, [PTR, #-13]
++ ldrbpl DAT0, [PTR, #-11]
++ addpl RESULT, RESULT, #2
++ teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++96: add RESULT, RESULT, #4
++ teq TMP2, #0
++ beq 97f @ not in next 4 bytes
++ sub TMP2, DAT2, #0x20000
++ bics TMP2, TMP2, DAT2
++ itee mi
++ ldrbmi DAT0, [PTR, #-9]
++ ldrbpl DAT0, [PTR, #-7]
++ addpl RESULT, RESULT, #2
++ teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ b 98f
++97: add RESULT, RESULT, #4
++ sub TMP3, DAT3, #0x20000
++ bics TMP3, TMP3, DAT3
++ itee mi
++ ldrbmi DAT0, [PTR, #-5]
++ ldrbpl DAT0, [PTR, #-3]
++ addpl RESULT, RESULT, #2
++ teq DAT0, #0
++ it eq
++ subeq RESULT, RESULT, #1
++ @ drop through to 98f
++98: setend le
++99: pop {v1-v6,pc}
++endfunc
++
++ .unreq RESULT
++ .unreq BUF
++ .unreq SIZE
++ .unreq PATTERN
++ .unreq PTR
++ .unreq DAT0
++ .unreq DAT1
++ .unreq DAT2
++ .unreq DAT3
++ .unreq TMP0
++ .unreq TMP1
++ .unreq TMP2
++ .unreq TMP3
+diff --git a/lib/ffmpeg/libavcodec/h264dsp.c b/lib/ffmpeg/libavcodec/h264dsp.c
+index b7d61cd..a84ae59 100644
+--- a/lib/ffmpeg/libavcodec/h264dsp.c
++++ b/lib/ffmpeg/libavcodec/h264dsp.c
+@@ -30,6 +30,7 @@
+ #include "avcodec.h"
+ #include "h264dsp.h"
+ #include "h264idct.h"
++#include "startcode.h"
+ #include "libavutil/common.h"
+
+ #define BIT_DEPTH 8
+@@ -60,34 +61,6 @@
+ #include "h264addpx_template.c"
+ #undef BIT_DEPTH
+
+-static int h264_find_start_code_candidate_c(const uint8_t *buf, int size)
+-{
+- int i = 0;
+-#if HAVE_FAST_UNALIGNED
+- /* we check i < size instead of i + 3 / 7 because it is
+- * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE
+- * bytes at the end.
+- */
+-#if HAVE_FAST_64BIT
+- while (i < size &&
+- !((~*(const uint64_t *)(buf + i) &
+- (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) &
+- 0x8080808080808080ULL))
+- i += 8;
+-#else
+- while (i < size &&
+- !((~*(const uint32_t *)(buf + i) &
+- (*(const uint32_t *)(buf + i) - 0x01010101U)) &
+- 0x80808080U))
+- i += 4;
+-#endif
+-#endif
+- for (; i < size; i++)
+- if (!buf[i])
+- break;
+- return i;
+-}
+-
+ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_format_idc)
+ {
+ #undef FUNC
+@@ -174,7 +147,7 @@ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_fo
+ H264_DSP(8);
+ break;
+ }
+- c->h264_find_start_code_candidate = h264_find_start_code_candidate_c;
++ c->h264_find_start_code_candidate = ff_startcode_find_candidate_c;
+
+ if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc);
+ if (HAVE_ALTIVEC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc);
+diff --git a/lib/ffmpeg/libavcodec/startcode.c b/lib/ffmpeg/libavcodec/startcode.c
+new file mode 100644
+index 0000000..5df7695
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/startcode.c
+@@ -0,0 +1,57 @@
++/*
++ * Copyright (c) 2003-2010 Michael Niedermayer <michaelni@gmx.at>
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * FFmpeg 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with FFmpeg; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/**
++ * @file
++ * Accelerated start code search function for start codes common to
++ * MPEG-1/2/4 video, VC-1, H.264/5
++ * @author Michael Niedermayer <michaelni@gmx.at>
++ */
++
++#include "startcode.h"
++#include "config.h"
++
++int ff_startcode_find_candidate_c(const uint8_t *buf, int size)
++{
++ int i = 0;
++#if HAVE_FAST_UNALIGNED
++ /* we check i < size instead of i + 3 / 7 because it is
++ * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE
++ * bytes at the end.
++ */
++# if HAVE_FAST_64BIT
++ while (i < size &&
++ !((~*(const uint64_t *)(buf + i) &
++ (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) &
++ 0x8080808080808080ULL))
++ i += 8;
++# else
++ while (i < size &&
++ !((~*(const uint32_t *)(buf + i) &
++ (*(const uint32_t *)(buf + i) - 0x01010101U)) &
++ 0x80808080U))
++ i += 4;
++# endif
++#endif
++ for (; i < size; i++)
++ if (!buf[i])
++ break;
++ return i;
++}
+diff --git a/lib/ffmpeg/libavcodec/startcode.h b/lib/ffmpeg/libavcodec/startcode.h
+new file mode 100644
+index 0000000..cc55d5f
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/startcode.h
+@@ -0,0 +1,35 @@
++/*
++ * Copyright (c) 2003-2010 Michael Niedermayer <michaelni@gmx.at>
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * FFmpeg 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with FFmpeg; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/**
++ * @file
++ * Accelerated start code search function for start codes common to
++ * MPEG-1/2/4 video, VC-1, H.264/5
++ * @author Michael Niedermayer <michaelni@gmx.at>
++ */
++
++#ifndef AVCODEC_STARTCODE_H
++#define AVCODEC_STARTCODE_H
++
++#include <stdint.h>
++
++int ff_startcode_find_candidate_c(const uint8_t *buf, int size);
++
++#endif /* AVCODEC_STARTCODE_H */
+--
+1.9.3
+
+
+From 7d95eb8e026582e5446e7e11d75ba999286a34d0 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 16 Apr 2014 01:51:32 +0100
+Subject: [PATCH 06/94] vc-1: Add platform-specific start code search routine
+ to VC1DSPContext.
+
+Initialise VC1DSPContext for parser as well as for decoder.
+Note, the VC-1 code doesn't actually use the function pointer yet.
+
+Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
+---
+ lib/ffmpeg/libavcodec/Makefile | 7 +++---
+ lib/ffmpeg/libavcodec/arm/Makefile | 3 +++
+ lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c | 33 +++++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/vc1.c | 2 ++
+ lib/ffmpeg/libavcodec/vc1dec.c | 1 -
+ lib/ffmpeg/libavcodec/vc1dsp.c | 5 +++++
+ lib/ffmpeg/libavcodec/vc1dsp.h | 9 ++++++++
+ 7 files changed, 56 insertions(+), 4 deletions(-)
+ create mode 100644 lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c
+
+diff --git a/lib/ffmpeg/libavcodec/Makefile b/lib/ffmpeg/libavcodec/Makefile
+index 460f42c..8d8a548 100644
+--- a/lib/ffmpeg/libavcodec/Makefile
++++ b/lib/ffmpeg/libavcodec/Makefile
+@@ -455,7 +455,7 @@ OBJS-$(CONFIG_VB_DECODER) += vb.o
+ OBJS-$(CONFIG_VBLE_DECODER) += vble.o
+ OBJS-$(CONFIG_VC1_DECODER) += vc1dec.o vc1.o vc1data.o vc1dsp.o \
+ msmpeg4.o msmpeg4data.o \
+- intrax8.o intrax8dsp.o
++ intrax8.o intrax8dsp.o startcode.o
+ OBJS-$(CONFIG_VC1_DXVA2_HWACCEL) += dxva2_vc1.o
+ OBJS-$(CONFIG_VC1_VAAPI_HWACCEL) += vaapi_vc1.o
+ OBJS-$(CONFIG_VC1_VDPAU_HWACCEL) += vdpau_vc1.o
+@@ -487,6 +487,7 @@ OBJS-$(CONFIG_WMAVOICE_DECODER) += wmavoice.o \
+ celp_filters.o \
+ acelp_vectors.o acelp_filters.o
+ OBJS-$(CONFIG_WMV1_DECODER) += msmpeg4.o msmpeg4data.o
++
+ OBJS-$(CONFIG_WMV2_DECODER) += wmv2dec.o wmv2.o wmv2dsp.o \
+ msmpeg4.o msmpeg4data.o \
+ intrax8.o intrax8dsp.o
+@@ -746,9 +747,9 @@ OBJS-$(CONFIG_PNM_PARSER) += pnm_parser.o pnm.o
+ OBJS-$(CONFIG_RV30_PARSER) += rv34_parser.o
+ OBJS-$(CONFIG_RV40_PARSER) += rv34_parser.o
+ OBJS-$(CONFIG_TAK_PARSER) += tak_parser.o tak.o
+-OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.o \
++OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.o vc1dsp.o \
+ msmpeg4.o msmpeg4data.o mpeg4video.o \
+- h263.o
++ h263.o startcode.o
+ OBJS-$(CONFIG_VORBIS_PARSER) += vorbis_parser.o xiph.o
+ OBJS-$(CONFIG_VP3_PARSER) += vp3_parser.o
+ OBJS-$(CONFIG_VP8_PARSER) += vp8_parser.o
+diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile
+index 0b432e3..715eed7 100644
+--- a/lib/ffmpeg/libavcodec/arm/Makefile
++++ b/lib/ffmpeg/libavcodec/arm/Makefile
+@@ -16,6 +16,9 @@ OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \
+
+ OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_init_arm.o
+ ARMV6-OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_fixed_armv6.o
++ARMV6-OBJS-$(CONFIG_VC1_DECODER) += arm/startcode_armv6.o
++OBJS-$(CONFIG_VC1_DECODER) += arm/vc1dsp_init_arm.o
++ARMV6-OBJS-$(CONFIG_VC1_PARSER) += arm/startcode_armv6.o
+
+ OBJS-$(CONFIG_MPEGVIDEO) += arm/mpegvideo_arm.o
+ OBJS-$(CONFIG_VORBIS_DECODER) += arm/vorbisdsp_init_arm.o
+diff --git a/lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c
+new file mode 100644
+index 0000000..fec5e78
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c
+@@ -0,0 +1,33 @@
++/*
++ * This file is part of Libav.
++ *
++ * Libav is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * Libav 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with Libav; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <stdint.h>
++
++#include "libavutil/attributes.h"
++#include "libavutil/arm/cpu.h"
++#include "libavcodec/vc1dsp.h"
++
++int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size);
++
++av_cold void ff_vc1dsp_init_arm(VC1DSPContext *dsp)
++{
++ int cpu_flags = av_get_cpu_flags();
++
++ if (have_armv6(cpu_flags))
++ dsp->vc1_find_start_code_candidate = ff_startcode_find_candidate_armv6;
++}
+diff --git a/lib/ffmpeg/libavcodec/vc1.c b/lib/ffmpeg/libavcodec/vc1.c
+index e2e90a8..9b15809 100644
+--- a/lib/ffmpeg/libavcodec/vc1.c
++++ b/lib/ffmpeg/libavcodec/vc1.c
+@@ -1663,5 +1663,7 @@ int ff_vc1_init_common(VC1Context *v)
+ v->pq = -1;
+ v->mvrange = 0; /* 7.1.1.18, p80 */
+
++ ff_vc1dsp_init(&v->vc1dsp);
++
+ return 0;
+ }
+diff --git a/lib/ffmpeg/libavcodec/vc1dec.c b/lib/ffmpeg/libavcodec/vc1dec.c
+index 2130c74..9fd3cae 100644
+--- a/lib/ffmpeg/libavcodec/vc1dec.c
++++ b/lib/ffmpeg/libavcodec/vc1dec.c
+@@ -5193,7 +5193,6 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx)
+ ff_vc1_decode_end(avctx);
+
+ ff_h264chroma_init(&v->h264chroma, 8);
+- ff_vc1dsp_init(&v->vc1dsp);
+
+ if (avctx->codec_id == AV_CODEC_ID_WMV3 || avctx->codec_id == AV_CODEC_ID_WMV3IMAGE) {
+ int count = 0;
+diff --git a/lib/ffmpeg/libavcodec/vc1dsp.c b/lib/ffmpeg/libavcodec/vc1dsp.c
+index 260eda4..3e3f00e 100644
+--- a/lib/ffmpeg/libavcodec/vc1dsp.c
++++ b/lib/ffmpeg/libavcodec/vc1dsp.c
+@@ -30,6 +30,7 @@
+ #include "h264chroma.h"
+ #include "rnd_avg.h"
+ #include "vc1dsp.h"
++#include "startcode.h"
+
+
+ /** Apply overlap transform to horizontal edge
+@@ -861,8 +862,12 @@ av_cold void ff_vc1dsp_init(VC1DSPContext* dsp) {
+ dsp->sprite_v_double_twoscale = sprite_v_double_twoscale_c;
+ #endif
+
++ dsp->vc1_find_start_code_candidate = ff_startcode_find_candidate_c;
++
+ if (HAVE_ALTIVEC)
+ ff_vc1dsp_init_altivec(dsp);
++ if (ARCH_ARM)
++ ff_vc1dsp_init_arm(dsp);
+ if (ARCH_X86)
+ ff_vc1dsp_init_x86(dsp);
+ }
+diff --git a/lib/ffmpeg/libavcodec/vc1dsp.h b/lib/ffmpeg/libavcodec/vc1dsp.h
+index 6540eff..302e4a8 100644
+--- a/lib/ffmpeg/libavcodec/vc1dsp.h
++++ b/lib/ffmpeg/libavcodec/vc1dsp.h
+@@ -73,10 +73,19 @@ typedef struct VC1DSPContext {
+ void (*sprite_v_double_twoscale)(uint8_t *dst, const uint8_t *src1a, const uint8_t *src1b, int offset1,
+ const uint8_t *src2a, const uint8_t *src2b, int offset2,
+ int alpha, int width);
++
++ /**
++ * Search buf from the start for up to size bytes. Return the index
++ * of a zero byte, or >= size if not found. Ideally, use lookahead
++ * to filter out any zero bytes that are known to not be followed by
++ * one or more further zero bytes and a one byte.
++ */
++ int (*vc1_find_start_code_candidate)(const uint8_t *buf, int size);
+ } VC1DSPContext;
+
+ void ff_vc1dsp_init(VC1DSPContext* c);
+ void ff_vc1dsp_init_altivec(VC1DSPContext* c);
++void ff_vc1dsp_init_arm(VC1DSPContext* dsp);
+ void ff_vc1dsp_init_x86(VC1DSPContext* dsp);
+
+ #endif /* AVCODEC_VC1DSP_H */
+--
+1.9.3
+
+
+From 9b459c3c4130299099b2e5aca5bff3d6f8d60e72 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 23 Apr 2014 01:41:04 +0100
+Subject: [PATCH 07/94] vc-1: Optimise parser (with special attention to ARM)
+
+The previous implementation of the parser made four passes over each input
+buffer (reduced to two if the container format already guaranteed the input
+buffer corresponded to frames, such as with MKV). But these buffers are
+often 200K in size, certainly enough to flush the data out of L1 cache, and
+for many CPUs, all the way out to main memory. The passes were:
+
+1) locate frame boundaries (not needed for MKV etc)
+2) copy the data into a contiguous block (not needed for MKV etc)
+3) locate the start codes within each frame
+4) unescape the data between start codes
+
+After this, the unescaped data was parsed to extract certain header fields,
+but because the unescape operation was so large, this was usually also
+effectively operating on uncached memory. Most of the unescaped data was
+simply thrown away and never processed further. Only step 2 - because it
+used memcpy - was using prefetch, making things even worse.
+
+This patch reorganises these steps so that, aside from the copying, the
+operations are performed in parallel, maximising cache utilisation. No more
+than the worst-case number of bytes needed for header parsing is unescaped.
+Most of the data is, in practice, only read in order to search for a start
+code, for which optimised implementations already existed in the H264 codec
+(notably the ARM version uses prefetch, so we end up doing both remaining
+passes at maximum speed). For MKV files, we know when we've found the last
+start code of interest in a given frame, so we are able to avoid doing even
+that one remaining pass for most of the buffer.
+
+In some use-cases (such as the Raspberry Pi) video decode is handled by the
+GPU, but the entire elementary stream is still fed through the parser to
+pick out certain elements of the header which are necessary to manage the
+decode process. As you might expect, in these cases, the performance of the
+parser is significant.
+
+To measure parser performance, I used the same VC-1 elementary stream in
+either an MPEG-2 transport stream or a MKV file, and fed it through ffmpeg
+with -c:v copy -c:a copy -f null. These are the gperftools counts for
+those streams, both filtered to only include vc1_parse() and its callees,
+and unfiltered (to include the whole binary). Lower numbers are better:
+
+ Before After
+File Filtered Mean StdDev Mean StdDev Confidence Change
+M2TS No 861.7 8.2 650.5 8.1 100.0% +32.5%
+MKV No 868.9 7.4 731.7 9.0 100.0% +18.8%
+M2TS Yes 250.0 11.2 27.2 3.4 100.0% +817.9%
+MKV Yes 149.0 12.8 1.7 0.8 100.0% +8526.3%
+
+Yes, that last case shows vc1_parse() running 86 times faster! The M2TS
+case does show a larger absolute improvement though, since it was worse
+to begin with.
+
+This patch has been tested with the FATE suite (albeit on x86 for speed).
+
+Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
+---
+ lib/ffmpeg/libavcodec/vc1_parser.c | 269 ++++++++++++++++++++++++-------------
+ 1 file changed, 175 insertions(+), 94 deletions(-)
+
+diff --git a/lib/ffmpeg/libavcodec/vc1_parser.c b/lib/ffmpeg/libavcodec/vc1_parser.c
+index 53af61c..af601ad 100644
+--- a/lib/ffmpeg/libavcodec/vc1_parser.c
++++ b/lib/ffmpeg/libavcodec/vc1_parser.c
+@@ -29,112 +29,83 @@
+ #include "vc1.h"
+ #include "get_bits.h"
+
++/** The maximum number of bytes of a sequence, entry point or
++ * frame header whose values we pay any attention to */
++#define UNESCAPED_THRESHOLD 37
++
++/** The maximum number of bytes of a sequence, entry point or
++ * frame header which must be valid memory (because they are
++ * used to update the bitstream cache in skip_bits() calls)
++ */
++#define UNESCAPED_LIMIT 144
++
++typedef enum {
++ NO_MATCH,
++ ONE_ZERO,
++ TWO_ZEROS,
++ ONE
++} VC1ParseSearchState;
++
+ typedef struct {
+ ParseContext pc;
+ VC1Context v;
++ uint8_t prev_start_code;
++ size_t bytes_to_skip;
++ uint8_t unesc_buffer[UNESCAPED_LIMIT];
++ size_t unesc_index;
++ VC1ParseSearchState search_state;
+ } VC1ParseContext;
+
+-static void vc1_extract_headers(AVCodecParserContext *s, AVCodecContext *avctx,
+- const uint8_t *buf, int buf_size)
++static void vc1_extract_header(AVCodecParserContext *s, AVCodecContext *avctx,
++ const uint8_t *buf, int buf_size)
+ {
++ /* Parse the header we just finished unescaping */
+ VC1ParseContext *vpc = s->priv_data;
+ GetBitContext gb;
+- const uint8_t *start, *end, *next;
+- uint8_t *buf2 = av_mallocz(buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
+-
++ int ret;
+ vpc->v.s.avctx = avctx;
+ vpc->v.parse_only = 1;
+- next = buf;
+- s->repeat_pict = 0;
+-
+- for(start = buf, end = buf + buf_size; next < end; start = next){
+- int buf2_size, size;
+-
+- next = find_next_marker(start + 4, end);
+- size = next - start - 4;
+- buf2_size = vc1_unescape_buffer(start + 4, size, buf2);
+- init_get_bits(&gb, buf2, buf2_size * 8);
+- if(size <= 0) continue;
+- switch(AV_RB32(start)){
+- case VC1_CODE_SEQHDR:
+- ff_vc1_decode_sequence_header(avctx, &vpc->v, &gb);
+- break;
+- case VC1_CODE_ENTRYPOINT:
+- ff_vc1_decode_entry_point(avctx, &vpc->v, &gb);
+- break;
+- case VC1_CODE_FRAME:
+- if(vpc->v.profile < PROFILE_ADVANCED)
+- ff_vc1_parse_frame_header (&vpc->v, &gb);
+- else
+- ff_vc1_parse_frame_header_adv(&vpc->v, &gb);
+-
+- /* keep AV_PICTURE_TYPE_BI internal to VC1 */
+- if (vpc->v.s.pict_type == AV_PICTURE_TYPE_BI)
+- s->pict_type = AV_PICTURE_TYPE_B;
+- else
+- s->pict_type = vpc->v.s.pict_type;
+-
+- if (avctx->ticks_per_frame > 1){
+- // process pulldown flags
+- s->repeat_pict = 1;
+- // Pulldown flags are only valid when 'broadcast' has been set.
+- // So ticks_per_frame will be 2
+- if (vpc->v.rff){
+- // repeat field
+- s->repeat_pict = 2;
+- }else if (vpc->v.rptfrm){
+- // repeat frames
+- s->repeat_pict = vpc->v.rptfrm * 2 + 1;
+- }
+- }
++ init_get_bits(&gb, buf, buf_size * 8);
++ switch (vpc->prev_start_code) {
++ case VC1_CODE_SEQHDR & 0xFF:
++ ff_vc1_decode_sequence_header(avctx, &vpc->v, &gb);
++ break;
++ case VC1_CODE_ENTRYPOINT & 0xFF:
++ ff_vc1_decode_entry_point(avctx, &vpc->v, &gb);
++ break;
++ case VC1_CODE_FRAME & 0xFF:
++ if(vpc->v.profile < PROFILE_ADVANCED)
++ ret = ff_vc1_parse_frame_header (&vpc->v, &gb);
++ else
++ ret = ff_vc1_parse_frame_header_adv(&vpc->v, &gb);
+
++ if (ret < 0)
+ break;
+- }
+- }
+
+- av_free(buf2);
+-}
++ /* keep AV_PICTURE_TYPE_BI internal to VC1 */
++ if (vpc->v.s.pict_type == AV_PICTURE_TYPE_BI)
++ s->pict_type = AV_PICTURE_TYPE_B;
++ else
++ s->pict_type = vpc->v.s.pict_type;
+
+-/**
+- * Find the end of the current frame in the bitstream.
+- * @return the position of the first byte of the next frame, or -1
+- */
+-static int vc1_find_frame_end(ParseContext *pc, const uint8_t *buf,
+- int buf_size) {
+- int pic_found, i;
+- uint32_t state;
+-
+- pic_found= pc->frame_start_found;
+- state= pc->state;
+-
+- i=0;
+- if(!pic_found){
+- for(i=0; i<buf_size; i++){
+- state= (state<<8) | buf[i];
+- if(state == VC1_CODE_FRAME || state == VC1_CODE_FIELD){
+- i++;
+- pic_found=1;
+- break;
++ if (avctx->ticks_per_frame > 1){
++ // process pulldown flags
++ s->repeat_pict = 1;
++ // Pulldown flags are only valid when 'broadcast' has been set.
++ // So ticks_per_frame will be 2
++ if (vpc->v.rff){
++ // repeat field
++ s->repeat_pict = 2;
++ }else if (vpc->v.rptfrm){
++ // repeat frames
++ s->repeat_pict = vpc->v.rptfrm * 2 + 1;
+ }
++ }else{
++ s->repeat_pict = 0;
+ }
+- }
+
+- if(pic_found){
+- /* EOF considered as end of frame */
+- if (buf_size == 0)
+- return 0;
+- for(; i<buf_size; i++){
+- state= (state<<8) | buf[i];
+- if(IS_MARKER(state) && state != VC1_CODE_FIELD && state != VC1_CODE_SLICE){
+- pc->frame_start_found=0;
+- pc->state=-1;
+- return i-3;
+- }
+- }
++ break;
+ }
+- pc->frame_start_found= pic_found;
+- pc->state= state;
+- return END_NOT_FOUND;
+ }
+
+ static int vc1_parse(AVCodecParserContext *s,
+@@ -142,22 +113,127 @@ static int vc1_parse(AVCodecParserContext *s,
+ const uint8_t **poutbuf, int *poutbuf_size,
+ const uint8_t *buf, int buf_size)
+ {
++ /* Here we do the searching for frame boundaries and headers at
++ * the same time. Only a minimal amount at the start of each
++ * header is unescaped. */
+ VC1ParseContext *vpc = s->priv_data;
+- int next;
++ int pic_found = vpc->pc.frame_start_found;
++ uint8_t *unesc_buffer = vpc->unesc_buffer;
++ size_t unesc_index = vpc->unesc_index;
++ VC1ParseSearchState search_state = vpc->search_state;
++ int next = END_NOT_FOUND;
++ int i = vpc->bytes_to_skip;
+
+- if(s->flags & PARSER_FLAG_COMPLETE_FRAMES){
+- next= buf_size;
+- }else{
+- next= vc1_find_frame_end(&vpc->pc, buf, buf_size);
++ if (pic_found && buf_size == 0) {
++ /* EOF considered as end of frame */
++ memset(unesc_buffer + unesc_index, 0, UNESCAPED_THRESHOLD - unesc_index);
++ vc1_extract_header(s, avctx, unesc_buffer, unesc_index);
++ next = 0;
++ }
++ while (i < buf_size) {
++ int start_code_found = 0;
++ uint8_t b;
++ while (i < buf_size && unesc_index < UNESCAPED_THRESHOLD) {
++ b = buf[i++];
++ unesc_buffer[unesc_index++] = b;
++ if (search_state <= ONE_ZERO)
++ search_state = b ? NO_MATCH : search_state + 1;
++ else if (search_state == TWO_ZEROS) {
++ if (b == 1)
++ search_state = ONE;
++ else if (b > 1) {
++ if (b == 3)
++ unesc_index--; // swallow emulation prevention byte
++ search_state = NO_MATCH;
++ }
++ }
++ else { // search_state == ONE
++ // Header unescaping terminates early due to detection of next start code
++ search_state = NO_MATCH;
++ start_code_found = 1;
++ break;
++ }
++ }
++ if ((s->flags & PARSER_FLAG_COMPLETE_FRAMES) &&
++ unesc_index >= UNESCAPED_THRESHOLD &&
++ vpc->prev_start_code == (VC1_CODE_FRAME & 0xFF))
++ {
++ // No need to keep scanning the rest of the buffer for
++ // start codes if we know it contains a complete frame and
++ // we've already unescaped all we need of the frame header
++ vc1_extract_header(s, avctx, unesc_buffer, unesc_index);
++ break;
++ }
++ if (unesc_index >= UNESCAPED_THRESHOLD && !start_code_found) {
++ while (i < buf_size) {
++ if (search_state == NO_MATCH) {
++ i += vpc->v.vc1dsp.vc1_find_start_code_candidate(buf + i, buf_size - i);
++ if (i < buf_size) {
++ search_state = ONE_ZERO;
++ }
++ i++;
++ } else {
++ b = buf[i++];
++ if (search_state == ONE_ZERO)
++ search_state = b ? NO_MATCH : TWO_ZEROS;
++ else if (search_state == TWO_ZEROS) {
++ if (b >= 1)
++ search_state = b == 1 ? ONE : NO_MATCH;
++ }
++ else { // search_state == ONE
++ search_state = NO_MATCH;
++ start_code_found = 1;
++ break;
++ }
++ }
++ }
++ }
++ if (start_code_found) {
++ vc1_extract_header(s, avctx, unesc_buffer, unesc_index);
++
++ vpc->prev_start_code = b;
++ unesc_index = 0;
++
++ if (!(s->flags & PARSER_FLAG_COMPLETE_FRAMES)) {
++ if (!pic_found && (b == (VC1_CODE_FRAME & 0xFF) || b == (VC1_CODE_FIELD & 0xFF))) {
++ pic_found = 1;
++ }
++ else if (pic_found && b != (VC1_CODE_FIELD & 0xFF) && b != (VC1_CODE_SLICE & 0xFF)) {
++ next = i - 4;
++ pic_found = b == (VC1_CODE_FRAME & 0xFF);
++ break;
++ }
++ }
++ }
++ }
+
++ vpc->pc.frame_start_found = pic_found;
++ vpc->unesc_index = unesc_index;
++ vpc->search_state = search_state;
++
++ if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
++ next = buf_size;
++ } else {
+ if (ff_combine_frame(&vpc->pc, next, &buf, &buf_size) < 0) {
++ vpc->bytes_to_skip = 0;
+ *poutbuf = NULL;
+ *poutbuf_size = 0;
+ return buf_size;
+ }
+ }
+
+- vc1_extract_headers(s, avctx, buf, buf_size);
++ vpc->v.first_pic_header_flag = 1;
++
++ /* If we return with a valid pointer to a combined frame buffer
++ * then on the next call then we'll have been unhelpfully rewound
++ * by up to 4 bytes (depending upon whether the start code
++ * overlapped the input buffer, and if so by how much). We don't
++ * want this: it will either cause spurious second detections of
++ * the start code we've already seen, or cause extra bytes to be
++ * inserted at the start of the unescaped buffer. */
++ vpc->bytes_to_skip = 4;
++ if (next < 0)
++ vpc->bytes_to_skip += next;
+
+ *poutbuf = buf;
+ *poutbuf_size = buf_size;
+@@ -188,6 +264,11 @@ static int vc1_parse_init(AVCodecParserContext *s)
+ {
+ VC1ParseContext *vpc = s->priv_data;
+ vpc->v.s.slice_context_count = 1;
++ vpc->v.first_pic_header_flag = 1;
++ vpc->prev_start_code = 0;
++ vpc->bytes_to_skip = 0;
++ vpc->unesc_index = 0;
++ vpc->search_state = NO_MATCH;
+ return ff_vc1_init_common(&vpc->v);
+ }
+
+--
+1.9.3
+
+
+From c2ebe54fe1d7c7a6cee7282bcf2668a826006ade Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 19 Mar 2014 17:44:59 +0000
+Subject: [PATCH 08/94] truehd: add hand-scheduled ARM asm version of
+ mlp_filter_channel.
+
+Profiling results for overall audio decode and the mlp_filter_channel(_arm)
+function in particular are as follows:
+
+ Before After
+ Mean StdDev Mean StdDev Confidence Change
+6:2 total 380.4 22.0 370.8 17.0 87.4% +2.6% (insignificant)
+6:2 function 60.7 7.2 36.6 8.1 100.0% +65.8%
+8:2 total 357.0 17.5 343.2 19.0 97.8% +4.0% (insignificant)
+8:2 function 60.3 8.8 37.3 3.8 100.0% +61.8%
+6:6 total 717.2 23.2 658.4 15.7 100.0% +8.9%
+6:6 function 140.4 12.9 81.5 9.2 100.0% +72.4%
+8:8 total 981.9 16.2 896.2 24.5 100.0% +9.6%
+8:8 function 193.4 15.0 103.3 11.5 100.0% +87.2%
+
+Experiments with adding preload instructions to this function yielded no
+useful benefit, so these have not been included.
+
+The assembly version has also been tested with a fuzz tester to ensure that
+any combinations of inputs not exercised by my available test streams still
+generate mathematically identical results to the C version.
+---
+ lib/ffmpeg/libavcodec/arm/Makefile | 5 +-
+ lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S | 430 ++++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c | 36 +++
+ lib/ffmpeg/libavcodec/mlpdsp.h | 1 +
+ 4 files changed, 471 insertions(+), 1 deletion(-)
+ create mode 100644 lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+ create mode 100644 lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+
+diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile
+index 715eed7..5b0edf0 100644
+--- a/lib/ffmpeg/libavcodec/arm/Makefile
++++ b/lib/ffmpeg/libavcodec/arm/Makefile
+@@ -14,6 +14,8 @@ ARMV6-OBJS-$(CONFIG_H264DSP) += arm/startcode_armv6.o
+ OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \
+ arm/flacdsp_arm.o \
+
++OBJS-$(CONFIG_MLP_DECODER) += arm/mlpdsp_init_arm.o \
++ arm/mlpdsp_arm.o
+ OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_init_arm.o
+ ARMV6-OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_fixed_armv6.o
+ ARMV6-OBJS-$(CONFIG_VC1_DECODER) += arm/startcode_armv6.o
+@@ -21,6 +23,8 @@ OBJS-$(CONFIG_VC1_DECODER) += arm/vc1dsp_init_arm.o
+ ARMV6-OBJS-$(CONFIG_VC1_PARSER) += arm/startcode_armv6.o
+
+ OBJS-$(CONFIG_MPEGVIDEO) += arm/mpegvideo_arm.o
++OBJS-$(CONFIG_TRUEHD_DECODER) += arm/mlpdsp_init_arm.o \
++ arm/mlpdsp_arm.o
+ OBJS-$(CONFIG_VORBIS_DECODER) += arm/vorbisdsp_init_arm.o
+ OBJS-$(CONFIG_VP3DSP) += arm/vp3dsp_init_arm.o
+ OBJS-$(CONFIG_VP5_DECODER) += arm/vp56dsp_init_arm.o
+@@ -34,7 +38,6 @@ OBJS-$(CONFIG_H264CHROMA) += arm/h264chroma_init_arm.o
+ OBJS-$(CONFIG_H264DSP) += arm/h264dsp_init_arm.o
+ OBJS-$(CONFIG_H264PRED) += arm/h264pred_init_arm.o
+ OBJS-$(CONFIG_H264QPEL) += arm/h264qpel_init_arm.o
+-
+ OBJS-$(CONFIG_RV30_DECODER) += arm/rv34dsp_init_arm.o
+ OBJS-$(CONFIG_RV40_DECODER) += arm/rv34dsp_init_arm.o \
+ arm/rv40dsp_init_arm.o \
+diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+new file mode 100644
+index 0000000..114496f
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+@@ -0,0 +1,430 @@
++/*
++ * Copyright (c) 2014 RISC OS Open Ltd
++ * Author: Ben Avison <bavison@riscosopen.org>
++ *
++ * This file is part of Libav.
++ *
++ * Libav is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * Libav 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with Libav; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++// This code uses too many ARM-only tricks to easily assemble as Thumb
++.arm
++#undef CONFIG_THUMB
++#define CONFIG_THUMB 0
++
++#include "libavutil/arm/asm.S"
++
++#define MAX_CHANNELS 8
++#define MAX_FIR_ORDER 8
++#define MAX_IIR_ORDER 4
++#define MAX_RATEFACTOR 4
++#define MAX_BLOCKSIZE (40 * MAX_RATEFACTOR)
++
++PST .req a1
++PCO .req a2
++AC0 .req a3
++AC1 .req a4
++CO0 .req v1
++CO1 .req v2
++CO2 .req v3
++CO3 .req v4
++ST0 .req v5
++ST1 .req v6
++ST2 .req sl
++ST3 .req fp
++I .req ip
++PSAMP .req lr
++
++
++// Some macros that do loads/multiplies where the register number is determined
++// from an assembly-time expression. Boy is GNU assembler's syntax ugly...
++
++.macro load group, index, base, offset
++ .altmacro
++ load_ \group, %(\index), \base, \offset
++ .noaltmacro
++.endm
++
++.macro load_ group, index, base, offset
++ ldr \group\index, [\base, #\offset]
++.endm
++
++.macro loadd group, index, base, offset
++ .altmacro
++ loadd_ \group, %(\index), %(\index+1), \base, \offset
++ .noaltmacro
++.endm
++
++.macro loadd_ group, index0, index1, base, offset
++A .if offset >= 256
++A ldr \group\index0, [\base, #\offset]
++A ldr \group\index1, [\base, #(\offset) + 4]
++A .else
++ ldrd \group\index0, \group\index1, [\base, #\offset]
++A .endif
++.endm
++
++.macro multiply index, accumulate, long
++ .altmacro
++ multiply_ %(\index), \accumulate, \long
++ .noaltmacro
++.endm
++
++.macro multiply_ index, accumulate, long
++ .if \long
++ .if \accumulate
++ smlal AC0, AC1, CO\index, ST\index
++ .else
++ smull AC0, AC1, CO\index, ST\index
++ .endif
++ .else
++ .if \accumulate
++ mla AC0, CO\index, ST\index, AC0
++ .else
++ mul AC0, CO\index, ST\index
++ .endif
++ .endif
++.endm
++
++// A macro to update the load register number and load offsets
++
++.macro inc howmany
++ .set LOAD_REG, (LOAD_REG + \howmany) & 3
++ .set OFFSET_CO, OFFSET_CO + 4 * \howmany
++ .set OFFSET_ST, OFFSET_ST + 4 * \howmany
++ .if FIR_REMAIN > 0
++ .set FIR_REMAIN, FIR_REMAIN - \howmany
++ .if FIR_REMAIN == 0
++ .set OFFSET_CO, 4 * MAX_FIR_ORDER
++ .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)
++ .endif
++ .elseif IIR_REMAIN > 0
++ .set IIR_REMAIN, IIR_REMAIN - \howmany
++ .endif
++.endm
++
++// Macro to implement the inner loop for one specific combination of parameters
++
++.macro implement_filter mask_minus1, shift_0, shift_8, iir_taps, fir_taps
++ .set TOTAL_TAPS, \iir_taps + \fir_taps
++
++ // Deal with register allocation...
++ .set DEFINED_SHIFT, 0
++ .set DEFINED_MASK, 0
++ .set SHUFFLE_SHIFT, 0
++ .set SHUFFLE_MASK, 0
++ .set SPILL_SHIFT, 0
++ .set SPILL_MASK, 0
++ .if TOTAL_TAPS == 0
++ // Little register pressure in this case - just keep MASK where it was
++ .if !\mask_minus1
++ MASK .req ST1
++ .set DEFINED_MASK, 1
++ .endif
++ .else
++ .if \shift_0
++ .if !\mask_minus1
++ // AC1 is unused with shift 0
++ MASK .req AC1
++ .set DEFINED_MASK, 1
++ .set SHUFFLE_MASK, 1
++ .endif
++ .elseif \shift_8
++ .if !\mask_minus1
++ .if TOTAL_TAPS <= 4
++ // All coefficients are preloaded (so pointer not needed)
++ MASK .req PCO
++ .set DEFINED_MASK, 1
++ .set SHUFFLE_MASK, 1
++ .else
++ .set SPILL_MASK, 1
++ .endif
++ .endif
++ .else // shift not 0 or 8
++ .if TOTAL_TAPS <= 3
++ // All coefficients are preloaded, and at least one CO register is unused
++ .if \fir_taps & 1
++ SHIFT .req CO0
++ .set DEFINED_SHIFT, 1
++ .set SHUFFLE_SHIFT, 1
++ .else
++ SHIFT .req CO3
++ .set DEFINED_SHIFT, 1
++ .set SHUFFLE_SHIFT, 1
++ .endif
++ .if !\mask_minus1
++ MASK .req PCO
++ .set DEFINED_MASK, 1
++ .set SHUFFLE_MASK, 1
++ .endif
++ .elseif TOTAL_TAPS == 4
++ // All coefficients are preloaded
++ SHIFT .req PCO
++ .set DEFINED_SHIFT, 1
++ .set SHUFFLE_SHIFT, 1
++ .if !\mask_minus1
++ .set SPILL_MASK, 1
++ .endif
++ .else
++ .set SPILL_SHIFT, 1
++ .if !\mask_minus1
++ .set SPILL_MASK, 1
++ .endif
++ .endif
++ .endif
++ .endif
++ .if SPILL_SHIFT
++ SHIFT .req ST0
++ .set DEFINED_SHIFT, 1
++ .endif
++ .if SPILL_MASK
++ MASK .req ST1
++ .set DEFINED_MASK, 1
++ .endif
++
++ // Preload coefficients if possible
++ .if TOTAL_TAPS <= 4
++ .set OFFSET_CO, 0
++ .if \fir_taps & 1
++ .set LOAD_REG, 1
++ .else
++ .set LOAD_REG, 0
++ .endif
++ .rept \fir_taps
++ load CO, LOAD_REG, PCO, OFFSET_CO
++ .set LOAD_REG, (LOAD_REG + 1) & 3
++ .set OFFSET_CO, OFFSET_CO + 4
++ .endr
++ .set OFFSET_CO, 4 * MAX_FIR_ORDER
++ .rept \iir_taps
++ load CO, LOAD_REG, PCO, OFFSET_CO
++ .set LOAD_REG, (LOAD_REG + 1) & 3
++ .set OFFSET_CO, OFFSET_CO + 4
++ .endr
++ .endif
++
++ // Move mask/shift to final positions if necessary
++ // Need to do this after preloading, because in some cases we
++ // reuse the coefficient pointer register
++ .if SHUFFLE_SHIFT
++ mov SHIFT, ST0
++ .endif
++ .if SHUFFLE_MASK
++ mov MASK, ST1
++ .endif
++
++ // Begin loop
++01:
++ .if TOTAL_TAPS == 0
++ // Things simplify a lot in this case
++ // In fact this could be pipelined further if it's worth it...
++ ldr ST0, [PSAMP]
++ subs I, I, #1
++ .if !\mask_minus1
++ and ST0, ST0, MASK
++ .endif
++ str ST0, [PST, #-4]!
++ str ST0, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)]
++ str ST0, [PSAMP], #4 * MAX_CHANNELS
++ bne 01b
++ .else
++ .if \fir_taps & 1
++ .set LOAD_REG, 1
++ .else
++ .set LOAD_REG, 0
++ .endif
++ .set LOAD_BANK, 0
++ .set FIR_REMAIN, \fir_taps
++ .set IIR_REMAIN, \iir_taps
++ .if FIR_REMAIN == 0 // only IIR terms
++ .set OFFSET_CO, 4 * MAX_FIR_ORDER
++ .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)
++ .else
++ .set OFFSET_CO, 0
++ .set OFFSET_ST, 0
++ .endif
++ .set MUL_REG, LOAD_REG
++ .set COUNTER, 0
++ .rept TOTAL_TAPS + 2
++ // Do load(s)
++ .if FIR_REMAIN != 0 || IIR_REMAIN != 0
++ .if COUNTER == 0
++ .if TOTAL_TAPS > 4
++ load CO, LOAD_REG, PCO, OFFSET_CO
++ .endif
++ load ST, LOAD_REG, PST, OFFSET_ST
++ inc 1
++ .elseif COUNTER == 1 && (\fir_taps & 1) == 0
++ .if TOTAL_TAPS > 4
++ load CO, LOAD_REG, PCO, OFFSET_CO
++ .endif
++ load ST, LOAD_REG, PST, OFFSET_ST
++ inc 1
++ .elseif LOAD_BANK == 0
++ .if TOTAL_TAPS > 4
++ .if FIR_REMAIN == 0 && IIR_REMAIN == 1
++ load CO, LOAD_REG, PCO, OFFSET_CO
++ .else
++ loadd CO, LOAD_REG, PCO, OFFSET_CO
++ .endif
++ .endif
++ .set LOAD_BANK, 1
++ .else
++ .if FIR_REMAIN == 0 && IIR_REMAIN == 1
++ load ST, LOAD_REG, PST, OFFSET_ST
++ inc 1
++ .else
++ loadd ST, LOAD_REG, PST, OFFSET_ST
++ inc 2
++ .endif
++ .set LOAD_BANK, 0
++ .endif
++ .endif
++
++ // Do interleaved multiplies, slightly delayed
++ .if COUNTER >= 2
++ multiply MUL_REG, COUNTER > 2, !\shift_0
++ .set MUL_REG, (MUL_REG + 1) & 3
++ .endif
++ .set COUNTER, COUNTER + 1
++ .endr
++
++ // Post-process the result of the multiplies
++ .if SPILL_SHIFT
++ ldr SHIFT, [sp, #9*4 + 0*4]
++ .endif
++ .if SPILL_MASK
++ ldr MASK, [sp, #9*4 + 1*4]
++ .endif
++ ldr ST2, [PSAMP]
++ subs I, I, #1
++ .if \shift_8
++ mov AC0, AC0, lsr #8
++ orr AC0, AC0, AC1, lsl #24
++ .elseif !\shift_0
++ rsb ST3, SHIFT, #32
++ mov AC0, AC0, lsr SHIFT
++ orr AC0, AC0, AC1, lsl ST3
++ .endif
++ .if \mask_minus1
++ add ST3, ST2, AC0
++ .else
++ add ST2, ST2, AC0
++ and ST3, ST2, MASK
++ sub ST2, ST3, AC0
++ .endif
++ str ST3, [PST, #-4]!
++ str ST2, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)]
++ str ST3, [PSAMP], #4 * MAX_CHANNELS
++ bne 01b
++ .endif
++ b 99f
++
++ .if DEFINED_SHIFT
++ .unreq SHIFT
++ .endif
++ .if DEFINED_MASK
++ .unreq MASK
++ .endif
++.endm
++
++.macro switch_on_fir_taps mask_minus1, shift_0, shift_8, iir_taps
++ ldr pc, [pc, a3, LSL #2] // firorder is in range 0-(8-iir_taps)
++ .word 0
++ .word 70f
++ .word 71f
++ .word 72f
++ .word 73f
++ .word 74f
++ .word 75f
++ .if \iir_taps <= 2
++ .word 76f
++ .if \iir_taps <= 1
++ .word 77f
++ .if \iir_taps == 0
++ .word 78f
++ .endif
++ .endif
++ .endif
++70: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 0
++71: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 1
++72: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 2
++73: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 3
++74: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 4
++75: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 5
++ .if \iir_taps <= 2
++76: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 6
++ .if \iir_taps <= 1
++77: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 7
++ .if \iir_taps == 0
++78: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 8
++ .endif
++ .endif
++ .endif
++.endm
++
++.macro switch_on_iir_taps mask_minus1, shift_0, shift_8
++ ldr pc, [pc, a4, LSL #2] // irorder is in range 0-3
++ .word 0
++ .word 60f
++ .word 61f
++ .word 62f
++ .word 63f
++60: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 0
++61: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 1
++62: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 2
++63: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 3
++.endm
++
++/* void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff,
++ * int firorder, int iirorder,
++ * unsigned int filter_shift, int32_t mask,
++ * int blocksize, int32_t *sample_buffer);
++ */
++function ff_mlp_filter_channel_arm, export=1
++ push {v1-fp,lr}
++ add v1, sp, #9*4 // point at arguments on stack
++ ldm v1, {ST0,ST1,I,PSAMP}
++ cmp ST1, #-1
++ bne 30f
++ movs ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8
++ bne 20f
++ bcs 10f
++ switch_on_iir_taps 1, 1, 0
++10: switch_on_iir_taps 1, 0, 1
++20: switch_on_iir_taps 1, 0, 0
++30: movs ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8
++ bne 50f
++ bcs 40f
++ switch_on_iir_taps 0, 1, 0
++40: switch_on_iir_taps 0, 0, 1
++50: switch_on_iir_taps 0, 0, 0
++99: pop {v1-fp,pc}
++endfunc
++
++ .unreq PST
++ .unreq PCO
++ .unreq AC0
++ .unreq AC1
++ .unreq CO0
++ .unreq CO1
++ .unreq CO2
++ .unreq CO3
++ .unreq ST0
++ .unreq ST1
++ .unreq ST2
++ .unreq ST3
++ .unreq I
++ .unreq PSAMP
+diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+new file mode 100644
+index 0000000..f0ea285
+--- /dev/null
++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+@@ -0,0 +1,36 @@
++/*
++ * Copyright (c) 2014 RISC OS Open Ltd
++ * Author: Ben Avison <bavison@riscosopen.org>
++ *
++ * This file is part of Libav.
++ *
++ * Libav is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * Libav 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with Libav; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <stdint.h>
++
++#include "libavutil/arm/cpu.h"
++#include "libavutil/attributes.h"
++#include "libavcodec/mlpdsp.h"
++
++void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff,
++ int firorder, int iirorder,
++ unsigned int filter_shift, int32_t mask,
++ int blocksize, int32_t *sample_buffer);
++
++av_cold void ff_mlpdsp_init_arm(MLPDSPContext *c)
++{
++ c->mlp_filter_channel = ff_mlp_filter_channel_arm;
++}
+diff --git a/lib/ffmpeg/libavcodec/mlpdsp.h b/lib/ffmpeg/libavcodec/mlpdsp.h
+index 84a8aa3..129bcfe 100644
+--- a/lib/ffmpeg/libavcodec/mlpdsp.h
++++ b/lib/ffmpeg/libavcodec/mlpdsp.h
+@@ -32,6 +32,7 @@ typedef struct MLPDSPContext {
+ } MLPDSPContext;
+
+ void ff_mlpdsp_init(MLPDSPContext *c);
++void ff_mlpdsp_init_arm(MLPDSPContext *c);
+ void ff_mlpdsp_init_x86(MLPDSPContext *c);
+
+ #endif /* AVCODEC_MLPDSP_H */
+--
+1.9.3
+
+
+From 904cb11e58484c5d0bca17b8c209916d106d2079 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 19 Mar 2014 17:48:54 +0000
+Subject: [PATCH 09/94] truehd: break out part of rematrix_channels into
+ platform-specific callback.
+
+Verified with profiling that this doesn't have a measurable effect upon
+overall performance.
+---
+ lib/ffmpeg/libavcodec/mlpdec.c | 37 ++++++++++++-------------------------
+ lib/ffmpeg/libavcodec/mlpdsp.c | 35 ++++++++++++++++++++++++++++++++++-
+ lib/ffmpeg/libavcodec/mlpdsp.h | 23 +++++++++++++++++++++++
+ 3 files changed, 69 insertions(+), 26 deletions(-)
+
+diff --git a/lib/ffmpeg/libavcodec/mlpdec.c b/lib/ffmpeg/libavcodec/mlpdec.c
+index c763624..e9343a5 100644
+--- a/lib/ffmpeg/libavcodec/mlpdec.c
++++ b/lib/ffmpeg/libavcodec/mlpdec.c
+@@ -958,7 +958,7 @@ static void fill_noise_buffer(MLPDecodeContext *m, unsigned int substr)
+ static void rematrix_channels(MLPDecodeContext *m, unsigned int substr)
+ {
+ SubStream *s = &m->substream[substr];
+- unsigned int mat, src_ch, i;
++ unsigned int mat;
+ unsigned int maxchan;
+
+ maxchan = s->max_matrix_channel;
+@@ -970,31 +970,18 @@ static void rematrix_channels(MLPDecodeContext *m, unsigned int substr)
+ }
+
+ for (mat = 0; mat < s->num_primitive_matrices; mat++) {
+- int matrix_noise_shift = s->matrix_noise_shift[mat];
+ unsigned int dest_ch = s->matrix_out_ch[mat];
+- int32_t mask = MSB_MASK(s->quant_step_size[dest_ch]);
+- int32_t *coeffs = s->matrix_coeff[mat];
+- int index = s->num_primitive_matrices - mat;
+- int index2 = 2 * index + 1;
+-
+- /* TODO: DSPContext? */
+-
+- for (i = 0; i < s->blockpos; i++) {
+- int32_t bypassed_lsb = m->bypassed_lsbs[i][mat];
+- int32_t *samples = m->sample_buffer[i];
+- int64_t accum = 0;
+-
+- for (src_ch = 0; src_ch <= maxchan; src_ch++)
+- accum += (int64_t) samples[src_ch] * coeffs[src_ch];
+-
+- if (matrix_noise_shift) {
+- index &= m->access_unit_size_pow2 - 1;
+- accum += m->noise_buffer[index] << (matrix_noise_shift + 7);
+- index += index2;
+- }
+-
+- samples[dest_ch] = ((accum >> 14) & mask) + bypassed_lsb;
+- }
++ m->dsp.mlp_rematrix_channel(&m->sample_buffer[0][0],
++ s->matrix_coeff[mat],
++ &m->bypassed_lsbs[0][mat],
++ m->noise_buffer,
++ s->num_primitive_matrices - mat,
++ dest_ch,
++ s->blockpos,
++ maxchan,
++ s->matrix_noise_shift[mat],
++ m->access_unit_size_pow2,
++ MSB_MASK(s->quant_step_size[dest_ch]));
+ }
+ }
+
+diff --git a/lib/ffmpeg/libavcodec/mlpdsp.c b/lib/ffmpeg/libavcodec/mlpdsp.c
+index 9a376e2..1f912fb 100644
+--- a/lib/ffmpeg/libavcodec/mlpdsp.c
++++ b/lib/ffmpeg/libavcodec/mlpdsp.c
+@@ -56,9 +56,42 @@ static void ff_mlp_filter_channel(int32_t *state, const int32_t *coeff,
+ }
+ }
+
+-void ff_mlpdsp_init(MLPDSPContext *c)
++void ff_mlp_rematrix_channel(int32_t *samples,
++ const int32_t *coeffs,
++ const uint8_t *bypassed_lsbs,
++ const int8_t *noise_buffer,
++ int index,
++ unsigned int dest_ch,
++ uint16_t blockpos,
++ unsigned int maxchan,
++ int matrix_noise_shift,
++ int access_unit_size_pow2,
++ int32_t mask)
++{
++ unsigned int src_ch, i;
++ int index2 = 2 * index + 1;
++ for (i = 0; i < blockpos; i++) {
++ int64_t accum = 0;
++
++ for (src_ch = 0; src_ch <= maxchan; src_ch++)
++ accum += (int64_t) samples[src_ch] * coeffs[src_ch];
++
++ if (matrix_noise_shift) {
++ index &= access_unit_size_pow2 - 1;
++ accum += noise_buffer[index] << (matrix_noise_shift + 7);
++ index += index2;
++ }
++
++ samples[dest_ch] = ((accum >> 14) & mask) + *bypassed_lsbs;
++ bypassed_lsbs += MAX_CHANNELS;
++ samples += MAX_CHANNELS;
++ }
++}
++
++av_cold void ff_mlpdsp_init(MLPDSPContext *c)
+ {
+ c->mlp_filter_channel = ff_mlp_filter_channel;
++ c->mlp_rematrix_channel = ff_mlp_rematrix_channel;
+ if (ARCH_X86)
+ ff_mlpdsp_init_x86(c);
+ }
+diff --git a/lib/ffmpeg/libavcodec/mlpdsp.h b/lib/ffmpeg/libavcodec/mlpdsp.h
+index 129bcfe..f98e9be 100644
+--- a/lib/ffmpeg/libavcodec/mlpdsp.h
++++ b/lib/ffmpeg/libavcodec/mlpdsp.h
+@@ -24,11 +24,34 @@
+
+ #include <stdint.h>
+
++void ff_mlp_rematrix_channel(int32_t *samples,
++ const int32_t *coeffs,
++ const uint8_t *bypassed_lsbs,
++ const int8_t *noise_buffer,
++ int index,
++ unsigned int dest_ch,
++ uint16_t blockpos,
++ unsigned int maxchan,
++ int matrix_noise_shift,
++ int access_unit_size_pow2,
++ int32_t mask);
++
+ typedef struct MLPDSPContext {
+ void (*mlp_filter_channel)(int32_t *state, const int32_t *coeff,
+ int firorder, int iirorder,
+ unsigned int filter_shift, int32_t mask,
+ int blocksize, int32_t *sample_buffer);
++ void (*mlp_rematrix_channel)(int32_t *samples,
++ const int32_t *coeffs,
++ const uint8_t *bypassed_lsbs,
++ const int8_t *noise_buffer,
++ int index,
++ unsigned int dest_ch,
++ uint16_t blockpos,
++ unsigned int maxchan,
++ int matrix_noise_shift,
++ int access_unit_size_pow2,
++ int32_t mask);
+ } MLPDSPContext;
+
+ void ff_mlpdsp_init(MLPDSPContext *c);
+--
+1.9.3
+
+
+From 0bb8daacca4b35d716addbc591fec43fd4fe6467 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 19 Mar 2014 17:49:48 +0000
+Subject: [PATCH 10/94] truehd: add hand-scheduled ARM asm version of
+ ff_mlp_rematrix_channel.
+
+Profiling results for overall audio decode and the rematrix_channels function
+in particular are as follows:
+
+ Before After
+ Mean StdDev Mean StdDev Confidence Change
+6:2 total 370.8 17.0 348.8 20.1 99.9% +6.3%
+6:2 function 46.4 8.4 45.8 6.6 18.0% +1.2% (insignificant)
+8:2 total 343.2 19.0 339.1 15.4 54.7% +1.2% (insignificant)
+8:2 function 38.9 3.9 40.2 6.9 52.4% -3.2% (insignificant)
+6:6 total 658.4 15.7 604.6 20.8 100.0% +8.9%
+6:6 function 109.0 8.7 59.5 5.4 100.0% +83.3%
+8:8 total 896.2 24.5 766.4 17.6 100.0% +16.9%
+8:8 function 223.4 12.8 93.8 5.0 100.0% +138.3%
+
+The assembly version has also been tested with a fuzz tester to ensure that
+any combinations of inputs not exercised by my available test streams still
+generate mathematically identical results to the C version.
+---
+ lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S | 231 ++++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c | 12 ++
+ 2 files changed, 243 insertions(+)
+
+diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+index 114496f..10008fe 100644
+--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+@@ -428,3 +428,234 @@ endfunc
+ .unreq ST3
+ .unreq I
+ .unreq PSAMP
++
++/********************************************************************/
++
++PSA .req a1 // samples
++PCO .req a2 // coeffs
++PBL .req a3 // bypassed_lsbs
++INDEX .req a4
++CO0 .req v1
++CO1 .req v2
++CO2 .req v3
++CO3 .req v4
++SA0 .req v5
++SA1 .req v6
++SA2 .req sl
++SA3 .req fp
++AC0 .req ip
++AC1 .req lr
++NOISE .req SA0
++LSB .req SA1
++DCH .req SA2 // dest_ch
++MASK .req SA3
++
++ // INDEX is used as follows:
++ // bits 0..6 index2 (values up to 17, but wider so that we can
++ // add to index field without needing to mask)
++ // bits 7..14 i (values up to 160)
++ // bit 15 underflow detect for i
++ // bits 25..31 (if access_unit_size_pow2 == 128) \ index
++ // bits 26..31 (if access_unit_size_pow2 == 64) /
++
++.macro implement_rematrix shift, index_mask, mask_minus1, maxchan
++ .if \maxchan == 1
++ // We can just leave the coefficients in registers in this case
++ ldrd CO0, CO1, [PCO]
++ .endif
++1:
++ .if \maxchan == 1
++ ldrd SA0, SA1, [PSA]
++ smull AC0, AC1, CO0, SA0
++ .elseif \maxchan == 5
++ ldr CO0, [PCO, #0]
++ ldr SA0, [PSA, #0]
++ ldr CO1, [PCO, #4]
++ ldr SA1, [PSA, #4]
++ ldrd CO2, CO3, [PCO, #8]
++ smull AC0, AC1, CO0, SA0
++ ldrd SA2, SA3, [PSA, #8]
++ smlal AC0, AC1, CO1, SA1
++ ldrd CO0, CO1, [PCO, #16]
++ smlal AC0, AC1, CO2, SA2
++ ldrd SA0, SA1, [PSA, #16]
++ smlal AC0, AC1, CO3, SA3
++ smlal AC0, AC1, CO0, SA0
++ .else // \maxchan == 7
++ ldr CO2, [PCO, #0]
++ ldr SA2, [PSA, #0]
++ ldr CO3, [PCO, #4]
++ ldr SA3, [PSA, #4]
++ ldrd CO0, CO1, [PCO, #8]
++ smull AC0, AC1, CO2, SA2
++ ldrd SA0, SA1, [PSA, #8]
++ smlal AC0, AC1, CO3, SA3
++ ldrd CO2, CO3, [PCO, #16]
++ smlal AC0, AC1, CO0, SA0
++ ldrd SA2, SA3, [PSA, #16]
++ smlal AC0, AC1, CO1, SA1
++ ldrd CO0, CO1, [PCO, #24]
++ smlal AC0, AC1, CO2, SA2
++ ldrd SA0, SA1, [PSA, #24]
++ smlal AC0, AC1, CO3, SA3
++ smlal AC0, AC1, CO0, SA0
++ .endif
++ ldm sp, {NOISE, DCH, MASK}
++ smlal AC0, AC1, CO1, SA1
++ .if \shift != 0
++ .if \index_mask == 63
++ add NOISE, NOISE, INDEX, lsr #32-6
++ ldrb LSB, [PBL], #MAX_CHANNELS
++ ldrsb NOISE, [NOISE]
++ add INDEX, INDEX, INDEX, lsl #32-6
++ .else // \index_mask == 127
++ add NOISE, NOISE, INDEX, lsr #32-7
++ ldrb LSB, [PBL], #MAX_CHANNELS
++ ldrsb NOISE, [NOISE]
++ add INDEX, INDEX, INDEX, lsl #32-7
++ .endif
++ sub INDEX, INDEX, #1<<7
++ adds AC0, AC0, NOISE, lsl #\shift + 7
++ adc AC1, AC1, NOISE, asr #31
++ .else
++ ldrb LSB, [PBL], #MAX_CHANNELS
++ sub INDEX, INDEX, #1<<7
++ .endif
++ add PSA, PSA, #MAX_CHANNELS*4
++ mov AC0, AC0, lsr #14
++ orr AC0, AC0, AC1, lsl #18
++ .if !\mask_minus1
++ and AC0, AC0, MASK
++ .endif
++ add AC0, AC0, LSB
++ tst INDEX, #1<<15
++ str AC0, [PSA, DCH, lsl #2] // DCH is precompensated for the early increment of PSA
++ beq 1b
++ b 98f
++.endm
++
++.macro switch_on_maxchan shift, index_mask, mask_minus1
++ cmp v4, #5
++ blo 51f
++ beq 50f
++ implement_rematrix \shift, \index_mask, \mask_minus1, 7
++50: implement_rematrix \shift, \index_mask, \mask_minus1, 5
++51: implement_rematrix \shift, \index_mask, \mask_minus1, 1
++.endm
++
++.macro switch_on_mask shift, index_mask
++ cmp sl, #-1
++ bne 40f
++ switch_on_maxchan \shift, \index_mask, 1
++40: switch_on_maxchan \shift, \index_mask, 0
++.endm
++
++.macro switch_on_au_size shift
++ .if \shift == 0
++ switch_on_mask \shift, undefined
++ .else
++ teq v6, #64
++ bne 30f
++ orr INDEX, INDEX, v1, lsl #32-6
++ switch_on_mask \shift, 63
++30: orr INDEX, INDEX, v1, lsl #32-7
++ switch_on_mask \shift, 127
++ .endif
++.endm
++
++/* void ff_mlp_rematrix_channel_arm(int32_t *samples,
++ * const int32_t *coeffs,
++ * const uint8_t *bypassed_lsbs,
++ * const int8_t *noise_buffer,
++ * int index,
++ * unsigned int dest_ch,
++ * uint16_t blockpos,
++ * unsigned int maxchan,
++ * int matrix_noise_shift,
++ * int access_unit_size_pow2,
++ * int32_t mask);
++ */
++function ff_mlp_rematrix_channel_arm, export=1
++ push {v1-fp,lr}
++ add v1, sp, #9*4 // point at arguments on stack
++ ldm v1, {v1-sl}
++ teq v4, #1
++ teqne v4, #5
++ teqne v4, #7
++ bne 99f
++ teq v6, #64
++ teqne v6, #128
++ bne 99f
++ sub v2, v2, #MAX_CHANNELS
++ push {a4,v2,sl} // initialise NOISE,DCH,MASK; make sp dword-aligned
++ movs INDEX, v3, lsl #7
++ beq 98f // just in case, do nothing if blockpos = 0
++ subs INDEX, INDEX, #1<<7 // offset by 1 so we borrow at the right time
++ adc lr, v1, v1 // calculate index2 (C was set by preceding subs)
++ orr INDEX, INDEX, lr
++ // Switch on matrix_noise_shift: values 0 and 1 are
++ // disproportionately common so do those in a form the branch
++ // predictor can accelerate. Values can only go up to 15.
++ cmp v5, #1
++ beq 11f
++ blo 10f
++ ldr pc, [pc, v5, lsl #2]
++ .word 0
++ .word 0
++ .word 0
++ .word 12f
++ .word 13f
++ .word 14f
++ .word 15f
++ .word 16f
++ .word 17f
++ .word 18f
++ .word 19f
++ .word 20f
++ .word 21f
++ .word 22f
++ .word 23f
++ .word 24f
++ .word 25f
++10: switch_on_au_size 0
++11: switch_on_au_size 1
++12: switch_on_au_size 2
++13: switch_on_au_size 3
++14: switch_on_au_size 4
++15: switch_on_au_size 5
++16: switch_on_au_size 6
++17: switch_on_au_size 7
++18: switch_on_au_size 8
++19: switch_on_au_size 9
++20: switch_on_au_size 10
++21: switch_on_au_size 11
++22: switch_on_au_size 12
++23: switch_on_au_size 13
++24: switch_on_au_size 14
++25: switch_on_au_size 15
++
++98: add sp, sp, #3*4
++ pop {v1-fp,pc}
++99: // Can't handle these parameters, drop back to C
++ pop {v1-fp,lr}
++ b X(ff_mlp_rematrix_channel)
++endfunc
++
++ .unreq PSA
++ .unreq PCO
++ .unreq PBL
++ .unreq INDEX
++ .unreq CO0
++ .unreq CO1
++ .unreq CO2
++ .unreq CO3
++ .unreq SA0
++ .unreq SA1
++ .unreq SA2
++ .unreq SA3
++ .unreq AC0
++ .unreq AC1
++ .unreq NOISE
++ .unreq LSB
++ .unreq DCH
++ .unreq MASK
+diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+index f0ea285..268dfdd 100644
+--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+@@ -29,8 +29,20 @@ void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff,
+ int firorder, int iirorder,
+ unsigned int filter_shift, int32_t mask,
+ int blocksize, int32_t *sample_buffer);
++void ff_mlp_rematrix_channel_arm(int32_t *samples,
++ const int32_t *coeffs,
++ const uint8_t *bypassed_lsbs,
++ const int8_t *noise_buffer,
++ int index,
++ unsigned int dest_ch,
++ uint16_t blockpos,
++ unsigned int maxchan,
++ int matrix_noise_shift,
++ int access_unit_size_pow2,
++ int32_t mask);
+
+ av_cold void ff_mlpdsp_init_arm(MLPDSPContext *c)
+ {
+ c->mlp_filter_channel = ff_mlp_filter_channel_arm;
++ c->mlp_rematrix_channel = ff_mlp_rematrix_channel_arm;
+ }
+--
+1.9.3
+
+
+From 034e1a8920aec0fa36ffc7da8f63e48c68364e15 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 19 Mar 2014 17:50:36 +0000
+Subject: [PATCH 11/94] truehd: tune VLC decoding for ARM.
+
+Profiling on a Raspberry Pi revealed the best performance to correspond
+with VLC_BITS = 5. Results for overall audio decode and the get_vlc2 function
+in particular are as follows:
+
+ Before After
+ Mean StdDev Mean StdDev Confidence Change
+6:2 total 348.8 20.1 339.6 15.1 88.8% +2.7% (insignificant)
+6:2 function 38.1 8.1 26.4 4.1 100.0% +44.5%
+8:2 total 339.1 15.4 324.5 15.5 99.4% +4.5%
+8:2 function 33.8 7.0 27.3 5.6 99.7% +23.6%
+6:6 total 604.6 20.8 572.8 20.6 100.0% +5.6%
+6:6 function 95.8 8.4 68.9 8.2 100.0% +39.1%
+8:8 total 766.4 17.6 741.5 21.2 100.0% +3.4%
+8:8 function 106.0 11.4 86.1 9.9 100.0% +23.1%
+---
+ lib/ffmpeg/libavcodec/mlpdec.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/lib/ffmpeg/libavcodec/mlpdec.c b/lib/ffmpeg/libavcodec/mlpdec.c
+index e9343a5..a998dac 100644
+--- a/lib/ffmpeg/libavcodec/mlpdec.c
++++ b/lib/ffmpeg/libavcodec/mlpdec.c
+@@ -36,9 +36,16 @@
+ #include "mlp_parser.h"
+ #include "mlpdsp.h"
+ #include "mlp.h"
++#include "config.h"
+
+ /** number of bits used for VLC lookup - longest Huffman code is 9 */
++#if ARCH_ARM == 1
++#define VLC_BITS 5
++#define VLC_STATIC_SIZE 64
++#else
+ #define VLC_BITS 9
++#define VLC_STATIC_SIZE 512
++#endif
+
+ typedef struct SubStream {
+ /// Set if a valid restart header has been read. Otherwise the substream cannot be decoded.
+@@ -190,13 +197,13 @@ static av_cold void init_static(void)
+ if (!huff_vlc[0].bits) {
+ INIT_VLC_STATIC(&huff_vlc[0], VLC_BITS, 18,
+ &ff_mlp_huffman_tables[0][0][1], 2, 1,
+- &ff_mlp_huffman_tables[0][0][0], 2, 1, 512);
++ &ff_mlp_huffman_tables[0][0][0], 2, 1, VLC_STATIC_SIZE);
+ INIT_VLC_STATIC(&huff_vlc[1], VLC_BITS, 16,
+ &ff_mlp_huffman_tables[1][0][1], 2, 1,
+- &ff_mlp_huffman_tables[1][0][0], 2, 1, 512);
++ &ff_mlp_huffman_tables[1][0][0], 2, 1, VLC_STATIC_SIZE);
+ INIT_VLC_STATIC(&huff_vlc[2], VLC_BITS, 15,
+ &ff_mlp_huffman_tables[2][0][1], 2, 1,
+- &ff_mlp_huffman_tables[2][0][0], 2, 1, 512);
++ &ff_mlp_huffman_tables[2][0][0], 2, 1, VLC_STATIC_SIZE);
+ }
+
+ ff_mlp_init_crc();
+--
+1.9.3
+
+
+From 25ab0401ebb7f035bcf7291452e6772a9c7b233a Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 19 Mar 2014 17:54:07 +0000
+Subject: [PATCH 12/94] truehd: break out part of output_data into
+ platform-specific callback.
+
+Verified with profiling that this doesn't have a measurable effect upon
+overall performance.
+---
+ lib/ffmpeg/libavcodec/mlpdec.c | 40 +++++++++++++++++++++++-----------------
+ lib/ffmpeg/libavcodec/mlpdsp.c | 36 ++++++++++++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/mlpdsp.h | 22 ++++++++++++++++++++++
+ 3 files changed, 81 insertions(+), 17 deletions(-)
+
+diff --git a/lib/ffmpeg/libavcodec/mlpdec.c b/lib/ffmpeg/libavcodec/mlpdec.c
+index a998dac..6d7c803 100644
+--- a/lib/ffmpeg/libavcodec/mlpdec.c
++++ b/lib/ffmpeg/libavcodec/mlpdec.c
+@@ -359,6 +359,10 @@ static int read_major_sync(MLPDecodeContext *m, GetBitContext *gb)
+ m->avctx->sample_fmt = AV_SAMPLE_FMT_S32;
+ else
+ m->avctx->sample_fmt = AV_SAMPLE_FMT_S16;
++ m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(m->substream[m->max_decoded_substream].max_matrix_channel,
++ m->avctx->sample_fmt == AV_SAMPLE_FMT_S32,
++ m->substream[m->max_decoded_substream].ch_assign,
++ m->substream[m->max_decoded_substream].output_shift);
+
+ m->params_valid = 1;
+ for (substr = 0; substr < MAX_SUBSTREAMS; substr++)
+@@ -553,6 +557,10 @@ static int read_restart_header(MLPDecodeContext *m, GetBitContext *gbp,
+ if (substr == m->max_decoded_substream) {
+ m->avctx->channels = s->max_matrix_channel + 1;
+ m->avctx->channel_layout = s->ch_layout;
++ m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(s->max_matrix_channel,
++ m->avctx->sample_fmt == AV_SAMPLE_FMT_S32,
++ s->ch_assign,
++ s->output_shift);
+
+ if (m->avctx->codec_id == AV_CODEC_ID_MLP && m->needs_reordering) {
+ if (m->avctx->channel_layout == (AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY) ||
+@@ -798,9 +806,15 @@ static int read_decoding_params(MLPDecodeContext *m, GetBitContext *gbp,
+ return ret;
+
+ if (s->param_presence_flags & PARAM_OUTSHIFT)
+- if (get_bits1(gbp))
++ if (get_bits1(gbp)) {
+ for (ch = 0; ch <= s->max_matrix_channel; ch++)
+ s->output_shift[ch] = get_sbits(gbp, 4);
++ if (substr == m->max_decoded_substream)
++ m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(s->max_matrix_channel,
++ m->avctx->sample_fmt == AV_SAMPLE_FMT_S32,
++ s->ch_assign,
++ s->output_shift);
++ }
+
+ if (s->param_presence_flags & PARAM_QUANTSTEP)
+ if (get_bits1(gbp))
+@@ -999,9 +1013,6 @@ static int output_data(MLPDecodeContext *m, unsigned int substr,
+ {
+ AVCodecContext *avctx = m->avctx;
+ SubStream *s = &m->substream[substr];
+- unsigned int i, out_ch = 0;
+- int32_t *data_32;
+- int16_t *data_16;
+ int ret;
+ int is32 = (m->avctx->sample_fmt == AV_SAMPLE_FMT_S32);
+
+@@ -1021,19 +1032,14 @@ static int output_data(MLPDecodeContext *m, unsigned int substr,
+ av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+ return ret;
+ }
+- data_32 = (int32_t *)frame->data[0];
+- data_16 = (int16_t *)frame->data[0];
+-
+- for (i = 0; i < s->blockpos; i++) {
+- for (out_ch = 0; out_ch <= s->max_matrix_channel; out_ch++) {
+- int mat_ch = s->ch_assign[out_ch];
+- int32_t sample = m->sample_buffer[i][mat_ch]
+- << s->output_shift[mat_ch];
+- s->lossless_check_data ^= (sample & 0xffffff) << mat_ch;
+- if (is32) *data_32++ = sample << 8;
+- else *data_16++ = sample >> 8;
+- }
+- }
++ s->lossless_check_data = m->dsp.mlp_pack_output(s->lossless_check_data,
++ m->sample_buffer,
++ frame->data[0],
++ s->blockpos,
++ s->max_matrix_channel,
++ is32,
++ s->ch_assign,
++ s->output_shift);
+
+ *got_frame_ptr = 1;
+
+diff --git a/lib/ffmpeg/libavcodec/mlpdsp.c b/lib/ffmpeg/libavcodec/mlpdsp.c
+index 1f912fb..2bb5cec 100644
+--- a/lib/ffmpeg/libavcodec/mlpdsp.c
++++ b/lib/ffmpeg/libavcodec/mlpdsp.c
+@@ -88,10 +88,46 @@ void ff_mlp_rematrix_channel(int32_t *samples,
+ }
+ }
+
++static int32_t (*mlp_select_pack_output(uint8_t max_matrix_channel,
++ int is32,
++ uint8_t *ch_assign,
++ int8_t *output_shift))(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *)
++{
++ return ff_mlp_pack_output;
++}
++
++int32_t ff_mlp_pack_output(int32_t lossless_check_data,
++ int32_t (*sample_buffer)[MAX_CHANNELS],
++ void *data,
++ uint16_t blockpos,
++ uint8_t max_matrix_channel,
++ int is32,
++ uint8_t *ch_assign,
++ int8_t *output_shift)
++{
++ unsigned int i, out_ch = 0;
++ int32_t *data_32 = (int32_t *)data;
++ int16_t *data_16 = (int16_t *)data;
++
++ for (i = 0; i < blockpos; i++) {
++ for (out_ch = 0; out_ch <= max_matrix_channel; out_ch++) {
++ int mat_ch = ch_assign[out_ch];
++ int32_t sample = sample_buffer[i][mat_ch]
++ << output_shift[mat_ch];
++ lossless_check_data ^= (sample & 0xffffff) << mat_ch;
++ if (is32) *data_32++ = sample << 8;
++ else *data_16++ = sample >> 8;
++ }
++ }
++ return lossless_check_data;
++}
++
+ av_cold void ff_mlpdsp_init(MLPDSPContext *c)
+ {
+ c->mlp_filter_channel = ff_mlp_filter_channel;
+ c->mlp_rematrix_channel = ff_mlp_rematrix_channel;
++ c->mlp_select_pack_output = mlp_select_pack_output;
++ c->mlp_pack_output = ff_mlp_pack_output;
+ if (ARCH_X86)
+ ff_mlpdsp_init_x86(c);
+ }
+diff --git a/lib/ffmpeg/libavcodec/mlpdsp.h b/lib/ffmpeg/libavcodec/mlpdsp.h
+index f98e9be..5bc901f 100644
+--- a/lib/ffmpeg/libavcodec/mlpdsp.h
++++ b/lib/ffmpeg/libavcodec/mlpdsp.h
+@@ -23,6 +23,7 @@
+ #define AVCODEC_MLPDSP_H
+
+ #include <stdint.h>
++#include "mlp.h"
+
+ void ff_mlp_rematrix_channel(int32_t *samples,
+ const int32_t *coeffs,
+@@ -36,6 +37,15 @@ void ff_mlp_rematrix_channel(int32_t *samples,
+ int access_unit_size_pow2,
+ int32_t mask);
+
++int32_t ff_mlp_pack_output(int32_t lossless_check_data,
++ int32_t (*sample_buffer)[MAX_CHANNELS],
++ void *data,
++ uint16_t blockpos,
++ uint8_t max_matrix_channel,
++ int is32,
++ uint8_t *ch_assign,
++ int8_t *output_shift);
++
+ typedef struct MLPDSPContext {
+ void (*mlp_filter_channel)(int32_t *state, const int32_t *coeff,
+ int firorder, int iirorder,
+@@ -52,6 +62,18 @@ typedef struct MLPDSPContext {
+ int matrix_noise_shift,
+ int access_unit_size_pow2,
+ int32_t mask);
++ int32_t (*(*mlp_select_pack_output)(uint8_t max_matrix_channel,
++ int is32,
++ uint8_t *ch_assign,
++ int8_t *output_shift))(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *);
++ int32_t (*mlp_pack_output)(int32_t lossless_check_data,
++ int32_t (*sample_buffer)[MAX_CHANNELS],
++ void *data,
++ uint16_t blockpos,
++ uint8_t max_matrix_channel,
++ int is32,
++ uint8_t *ch_assign,
++ int8_t *output_shift);
+ } MLPDSPContext;
+
+ void ff_mlpdsp_init(MLPDSPContext *c);
+--
+1.9.3
+
+
+From bdefac00779c5601816f949353d9bbeb3b199611 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 19 Mar 2014 17:54:59 +0000
+Subject: [PATCH 13/94] truehd: add hand-scheduled ARM asm version of
+ ff_mlp_pack_output.
+
+Profiling results for overall decode and the output_data function in
+particular are as follows:
+
+ Before After
+ Mean StdDev Mean StdDev Confidence Change
+6:2 total 339.6 15.1 329.3 16.0 95.8% +3.1% (insignificant)
+6:2 function 24.6 6.0 9.9 3.1 100.0% +148.5%
+8:2 total 324.5 15.5 323.6 14.3 15.2% +0.3% (insignificant)
+8:2 function 20.4 3.9 9.9 3.4 100.0% +104.7%
+6:6 total 572.8 20.6 539.9 24.2 100.0% +6.1%
+6:6 function 54.5 5.6 16.0 3.8 100.0% +240.9%
+8:8 total 741.5 21.2 702.5 18.5 100.0% +5.6%
+8:8 function 63.9 7.6 18.4 4.8 100.0% +247.3%
+
+The assembly version has also been tested with a fuzz tester to ensure that
+any combinations of inputs not exercised by my available test streams still
+generate mathematically identical results to the C version.
+---
+ lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S | 503 ++++++++++++++++++++++++++++
+ lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c | 64 ++++
+ 2 files changed, 567 insertions(+)
+
+diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+index 10008fe..338d323 100644
+--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S
+@@ -98,6 +98,26 @@ A .endif
+ .endif
+ .endm
+
++.macro loadregoffsh2 group, index, base, offgroup, offindex
++ .altmacro
++ loadregoffsh2_ \group, %(\index), \base, \offgroup, %(\offindex)
++ .noaltmacro
++.endm
++
++.macro loadregoffsh2_ group, index, base, offgroup, offindex
++ ldr \group\index, [\base, \offgroup\offindex, lsl #2]
++.endm
++
++.macro eorlslreg check, data, group, index
++ .altmacro
++ eorlslreg_ \check, \data, \group, %(\index)
++ .noaltmacro
++.endm
++
++.macro eorlslreg_ check, data, group, index
++ eor \check, \check, \data, lsl \group\index
++.endm
++
+ // A macro to update the load register number and load offsets
+
+ .macro inc howmany
+@@ -659,3 +679,486 @@ endfunc
+ .unreq LSB
+ .unreq DCH
+ .unreq MASK
++
++/********************************************************************/
++
++.macro decr_modulo var, by, modulus
++ .set \var, \var - \by
++ .if \var == 0
++ .set \var, \modulus
++ .endif
++.endm
++
++ .macro load_group1 size, channels, r0, r1, r2, r3, pointer_dead=0
++ .if \size == 2
++ ldrd \r0, \r1, [IN], #(\size + 8 - \channels) * 4
++ .else // size == 4
++ .if IDX1 > 4 || \channels==8
++ ldm IN!, {\r0, \r1, \r2, \r3}
++ .else
++ ldm IN, {\r0, \r1, \r2, \r3}
++ .if !\pointer_dead
++ add IN, IN, #(4 + 8 - \channels) * 4
++ .endif
++ .endif
++ .endif
++ decr_modulo IDX1, \size, \channels
++ .endm
++
++ .macro load_group2 size, channels, r0, r1, r2, r3, pointer_dead=0
++ .if \size == 2
++ .if IDX1 > 2
++ ldm IN!, {\r2, \r3}
++ .else
++//A .ifc \r2, ip
++//A .if \pointer_dead
++//A ldm IN, {\r2, \r3}
++//A .else
++//A ldr \r2, [IN], #4
++//A ldr \r3, [IN], #(\size - 1 + 8 - \channels) * 4
++//A .endif
++//A .else
++ ldrd \r2, \r3, [IN], #(\size + 8 - \channels) * 4
++//A .endif
++ .endif
++ .endif
++ decr_modulo IDX1, \size, \channels
++ .endm
++
++.macro implement_pack inorder, channels, shift
++.if \inorder
++.ifc \shift, mixed
++
++CHECK .req a1
++IN .req a2
++OUT .req a3
++COUNT .req a4
++DAT0 .req v1
++DAT1 .req v2
++DAT2 .req v3
++DAT3 .req v4
++SHIFT0 .req v5
++SHIFT1 .req v6
++SHIFT2 .req sl
++SHIFT3 .req fp
++SHIFT4 .req ip
++SHIFT5 .req lr
++
++ .macro output4words
++ .set SIZE_GROUP1, IDX1
++ .if SIZE_GROUP1 > 4
++ .set SIZE_GROUP1, 4
++ .endif
++ .set SIZE_GROUP2, 4 - SIZE_GROUP1
++ load_group1 SIZE_GROUP1, \channels, DAT0, DAT1, DAT2, DAT3
++ load_group2 SIZE_GROUP2, \channels, DAT0, DAT1, DAT2, DAT3
++ .if \channels == 2
++ lsl DAT0, SHIFT0
++ lsl DAT1, SHIFT1
++ lsl DAT2, SHIFT0
++ lsl DAT3, SHIFT1
++ .elseif \channels == 6
++ .if IDX2 == 6
++ lsl DAT0, SHIFT0
++ lsl DAT1, SHIFT1
++ lsl DAT2, SHIFT2
++ lsl DAT3, SHIFT3
++ .elseif IDX2 == 2
++ lsl DAT0, SHIFT4
++ lsl DAT1, SHIFT5
++ lsl DAT2, SHIFT0
++ lsl DAT3, SHIFT1
++ .else // IDX2 == 4
++ lsl DAT0, SHIFT2
++ lsl DAT1, SHIFT3
++ lsl DAT2, SHIFT4
++ lsl DAT3, SHIFT5
++ .endif
++ .elseif \channels == 8
++ .if IDX2 == 8
++ uxtb SHIFT0, SHIFT4, ror #0
++ uxtb SHIFT1, SHIFT4, ror #8
++ uxtb SHIFT2, SHIFT4, ror #16
++ uxtb SHIFT3, SHIFT4, ror #24
++ .else
++ uxtb SHIFT0, SHIFT5, ror #0
++ uxtb SHIFT1, SHIFT5, ror #8
++ uxtb SHIFT2, SHIFT5, ror #16
++ uxtb SHIFT3, SHIFT5, ror #24
++ .endif
++ lsl DAT0, SHIFT0
++ lsl DAT1, SHIFT1
++ lsl DAT2, SHIFT2
++ lsl DAT3, SHIFT3
++ .endif
++ eor CHECK, CHECK, DAT0, lsr #8 - (\channels - IDX2)
++ eor CHECK, CHECK, DAT1, lsr #7 - (\channels - IDX2)
++ decr_modulo IDX2, 2, \channels
++ eor CHECK, CHECK, DAT2, lsr #8 - (\channels - IDX2)
++ eor CHECK, CHECK, DAT3, lsr #7 - (\channels - IDX2)
++ decr_modulo IDX2, 2, \channels
++ stm OUT!, {DAT0 - DAT3}
++ .endm
++
++ .set WORDS_PER_LOOP, \channels // calculate LCM (channels, 4)
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP * 4
++ .set SAMPLES_PER_LOOP, WORDS_PER_LOOP / \channels
++
++function ff_mlp_pack_output_inorder_\channels\()ch_mixedshift_arm, export=1
++ .if SAMPLES_PER_LOOP > 1
++ tst COUNT, #SAMPLES_PER_LOOP - 1 // always seems to be in practice
++ bne X(ff_mlp_pack_output) // but just in case, branch to C implementation if not
++ .endif
++ teq COUNT, #0
++ bxeq lr
++ push {v1-v6,sl,fp,lr}
++ ldr SHIFT0, [sp, #(9+3)*4] // get output_shift from stack
++ ldr SHIFT1, =0x08080808
++ ldr SHIFT4, [SHIFT0]
++ .if \channels == 2
++ uadd8 SHIFT4, SHIFT4, SHIFT1 // increase all shifts by 8
++ uxtb SHIFT0, SHIFT4, ror #0
++ uxtb SHIFT1, SHIFT4, ror #8
++ .else
++ ldr SHIFT5, [SHIFT0, #4]
++ uadd8 SHIFT4, SHIFT4, SHIFT1 // increase all shifts by 8
++ uadd8 SHIFT5, SHIFT5, SHIFT1
++ .if \channels == 6
++ uxtb SHIFT0, SHIFT4, ror #0
++ uxtb SHIFT1, SHIFT4, ror #8
++ uxtb SHIFT2, SHIFT4, ror #16
++ uxtb SHIFT3, SHIFT4, ror #24
++ uxtb SHIFT4, SHIFT5, ror #0
++ uxtb SHIFT5, SHIFT5, ror #8
++ .endif
++ .endif
++ .set IDX1, \channels
++ .set IDX2, \channels
++0:
++ .rept WORDS_PER_LOOP / 4
++ output4words
++ .endr
++ subs COUNT, COUNT, #SAMPLES_PER_LOOP
++ bne 0b
++ pop {v1-v6,sl,fp,pc}
++ .ltorg
++endfunc
++ .purgem output4words
++
++ .unreq CHECK
++ .unreq IN
++ .unreq OUT
++ .unreq COUNT
++ .unreq DAT0
++ .unreq DAT1
++ .unreq DAT2
++ .unreq DAT3
++ .unreq SHIFT0
++ .unreq SHIFT1
++ .unreq SHIFT2
++ .unreq SHIFT3
++ .unreq SHIFT4
++ .unreq SHIFT5
++
++.else // not mixed
++
++CHECK .req a1
++IN .req a2
++OUT .req a3
++COUNT .req a4
++DAT0 .req v1
++DAT1 .req v2
++DAT2 .req v3
++DAT3 .req v4
++DAT4 .req v5
++DAT5 .req v6
++DAT6 .req sl // use these rather than the otherwise unused
++DAT7 .req fp // ip and lr so that we can load them usinf LDRD
++
++ .macro output4words tail, head, r0, r1, r2, r3, r4, r5, r6, r7, pointer_dead=0
++ .if \head
++ .set SIZE_GROUP1, IDX1
++ .if SIZE_GROUP1 > 4
++ .set SIZE_GROUP1, 4
++ .endif
++ .set SIZE_GROUP2, 4 - SIZE_GROUP1
++ load_group1 SIZE_GROUP1, \channels, \r0, \r1, \r2, \r3, \pointer_dead
++ .endif
++ .if \tail
++ eor CHECK, CHECK, \r4, lsr #8 - (\channels - IDX2)
++ eor CHECK, CHECK, \r5, lsr #7 - (\channels - IDX2)
++ decr_modulo IDX2, 2, \channels
++ .endif
++ .if \head
++ load_group2 SIZE_GROUP2, \channels, \r0, \r1, \r2, \r3, \pointer_dead
++ .endif
++ .if \tail
++ eor CHECK, CHECK, \r6, lsr #8 - (\channels - IDX2)
++ eor CHECK, CHECK, \r7, lsr #7 - (\channels - IDX2)
++ decr_modulo IDX2, 2, \channels
++ stm OUT!, {\r4, \r5, \r6, \r7}
++ .endif
++ .if \head
++ lsl \r0, #8 + \shift
++ lsl \r1, #8 + \shift
++ lsl \r2, #8 + \shift
++ lsl \r3, #8 + \shift
++ .endif
++ .endm
++
++ .set WORDS_PER_LOOP, \channels // calculate LCM (channels, 8)
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP * 8
++ .set SAMPLES_PER_LOOP, WORDS_PER_LOOP / \channels
++
++function ff_mlp_pack_output_inorder_\channels\()ch_\shift\()shift_arm, export=1
++ .if SAMPLES_PER_LOOP > 1
++ tst COUNT, #SAMPLES_PER_LOOP - 1 // always seems to be in practice
++ bne X(ff_mlp_pack_output) // but just in case, branch to C implementation if not
++ .endif
++ subs COUNT, COUNT, #SAMPLES_PER_LOOP
++ bxlo lr
++ push {v1-v6,sl,fp,lr}
++ .set IDX1, \channels
++ .set IDX2, \channels
++ output4words 0, 1, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7
++0: beq 1f
++ .rept WORDS_PER_LOOP / 8
++ output4words 1, 1, DAT4, DAT5, DAT6, DAT7, DAT0, DAT1, DAT2, DAT3
++ output4words 1, 1, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7
++ .endr
++ subs COUNT, COUNT, #SAMPLES_PER_LOOP
++ bne 0b
++1:
++ .rept WORDS_PER_LOOP / 8 - 1
++ output4words 1, 1, DAT4, DAT5, DAT6, DAT7, DAT0, DAT1, DAT2, DAT3
++ output4words 1, 1, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7
++ .endr
++ output4words 1, 1, DAT4, DAT5, DAT6, DAT7, DAT0, DAT1, DAT2, DAT3, pointer_dead=1
++ output4words 1, 0, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7
++ pop {v1-v6,sl,fp,pc}
++endfunc
++ .purgem output4words
++
++ .unreq CHECK
++ .unreq IN
++ .unreq OUT
++ .unreq COUNT
++ .unreq DAT0
++ .unreq DAT1
++ .unreq DAT2
++ .unreq DAT3
++ .unreq DAT4
++ .unreq DAT5
++ .unreq DAT6
++ .unreq DAT7
++
++.endif // mixed
++.else // not inorder
++.ifc \shift, mixed
++
++// This case not currently handled
++
++.else // not mixed
++
++CHECK .req a1
++IN .req a2
++OUT .req a3
++COUNT .req a4
++DAT0 .req v1
++DAT1 .req v2
++DAT2 .req v3
++DAT3 .req v4
++CHAN0 .req v5
++CHAN1 .req v6
++CHAN2 .req sl
++CHAN3 .req fp
++CHAN4 .req ip
++CHAN5 .req lr
++
++ .macro output4words
++ .if \channels == 8
++ .if IDX1 == 8
++ uxtb CHAN0, CHAN4, ror #0
++ uxtb CHAN1, CHAN4, ror #8
++ uxtb CHAN2, CHAN4, ror #16
++ uxtb CHAN3, CHAN4, ror #24
++ .else
++ uxtb CHAN0, CHAN5, ror #0
++ uxtb CHAN1, CHAN5, ror #8
++ uxtb CHAN2, CHAN5, ror #16
++ uxtb CHAN3, CHAN5, ror #24
++ .endif
++ ldr DAT0, [IN, CHAN0, lsl #2]
++ ldr DAT1, [IN, CHAN1, lsl #2]
++ ldr DAT2, [IN, CHAN2, lsl #2]
++ ldr DAT3, [IN, CHAN3, lsl #2]
++ .if IDX1 == 4
++ add IN, IN, #8*4
++ .endif
++ decr_modulo IDX1, 4, \channels
++ .else
++ .set SIZE_GROUP1, IDX1
++ .if SIZE_GROUP1 > 4
++ .set SIZE_GROUP1, 4
++ .endif
++ .set SIZE_GROUP2, 4 - SIZE_GROUP1
++ .if SIZE_GROUP1 == 2
++ loadregoffsh2 DAT, 0, IN, CHAN, 0 + (\channels - IDX1)
++ loadregoffsh2 DAT, 1, IN, CHAN, 1 + (\channels - IDX1)
++ add IN, IN, #8*4
++ .else // SIZE_GROUP1 == 4
++ loadregoffsh2 DAT, 0, IN, CHAN, 0 + (\channels - IDX1)
++ loadregoffsh2 DAT, 1, IN, CHAN, 1 + (\channels - IDX1)
++ loadregoffsh2 DAT, 2, IN, CHAN, 2 + (\channels - IDX1)
++ loadregoffsh2 DAT, 3, IN, CHAN, 3 + (\channels - IDX1)
++ .if IDX1 == 4
++ add IN, IN, #8*4
++ .endif
++ .endif
++ decr_modulo IDX1, SIZE_GROUP1, \channels
++ .if SIZE_GROUP2 == 2
++ loadregoffsh2 DAT, 2, IN, CHAN, 0 + (\channels - IDX1)
++ loadregoffsh2 DAT, 3, IN, CHAN, 1 + (\channels - IDX1)
++ .if IDX1 == 2
++ add IN, IN, #8*4
++ .endif
++ .endif
++ decr_modulo IDX1, SIZE_GROUP2, \channels
++ .endif
++ .if \channels == 8 // in this case we can corrupt CHAN0-3
++ rsb CHAN0, CHAN0, #8
++ rsb CHAN1, CHAN1, #8
++ rsb CHAN2, CHAN2, #8
++ rsb CHAN3, CHAN3, #8
++ lsl DAT0, #8 + \shift
++ lsl DAT1, #8 + \shift
++ lsl DAT2, #8 + \shift
++ lsl DAT3, #8 + \shift
++ eor CHECK, CHECK, DAT0, lsr CHAN0
++ eor CHECK, CHECK, DAT1, lsr CHAN1
++ eor CHECK, CHECK, DAT2, lsr CHAN2
++ eor CHECK, CHECK, DAT3, lsr CHAN3
++ .else
++ .if \shift != 0
++ lsl DAT0, #\shift
++ lsl DAT1, #\shift
++ lsl DAT2, #\shift
++ lsl DAT3, #\shift
++ .endif
++ bic DAT0, DAT0, #0xff000000
++ bic DAT1, DAT1, #0xff000000
++ bic DAT2, DAT2, #0xff000000
++ bic DAT3, DAT3, #0xff000000
++ eorlslreg CHECK, DAT0, CHAN, 0 + (\channels - IDX2)
++ eorlslreg CHECK, DAT1, CHAN, 1 + (\channels - IDX2)
++ decr_modulo IDX2, 2, \channels
++ eorlslreg CHECK, DAT2, CHAN, 0 + (\channels - IDX2)
++ eorlslreg CHECK, DAT3, CHAN, 1 + (\channels - IDX2)
++ decr_modulo IDX2, 2, \channels
++ lsl DAT0, #8
++ lsl DAT1, #8
++ lsl DAT2, #8
++ lsl DAT3, #8
++ .endif
++ stm OUT!, {DAT0 - DAT3}
++ .endm
++
++ .set WORDS_PER_LOOP, \channels // calculate LCM (channels, 4)
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .if (WORDS_PER_LOOP % 2) == 0
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2
++ .endif
++ .set WORDS_PER_LOOP, WORDS_PER_LOOP * 4
++ .set SAMPLES_PER_LOOP, WORDS_PER_LOOP / \channels
++
++function ff_mlp_pack_output_outoforder_\channels\()ch_\shift\()shift_arm, export=1
++ .if SAMPLES_PER_LOOP > 1
++ tst COUNT, #SAMPLES_PER_LOOP - 1 // always seems to be in practice
++ bne X(ff_mlp_pack_output) // but just in case, branch to C implementation if not
++ .endif
++ teq COUNT, #0
++ bxeq lr
++ push {v1-v6,sl,fp,lr}
++ ldr CHAN0, [sp, #(9+2)*4] // get ch_assign from stack
++ ldr CHAN4, [CHAN0]
++ .if \channels == 2
++ uxtb CHAN0, CHAN4, ror #0
++ uxtb CHAN1, CHAN4, ror #8
++ .else
++ ldr CHAN5, [CHAN0, #4]
++ .if \channels == 6
++ uxtb CHAN0, CHAN4, ror #0
++ uxtb CHAN1, CHAN4, ror #8
++ uxtb CHAN2, CHAN4, ror #16
++ uxtb CHAN3, CHAN4, ror #24
++ uxtb CHAN4, CHAN5, ror #0
++ uxtb CHAN5, CHAN5, ror #8
++ .endif
++ .endif
++ .set IDX1, \channels
++ .set IDX2, \channels
++0:
++ .rept WORDS_PER_LOOP / 4
++ output4words
++ .endr
++ subs COUNT, COUNT, #SAMPLES_PER_LOOP
++ bne 0b
++ pop {v1-v6,sl,fp,pc}
++ .ltorg
++endfunc
++ .purgem output4words
++
++ .unreq CHECK
++ .unreq IN
++ .unreq OUT
++ .unreq COUNT
++ .unreq DAT0
++ .unreq DAT1
++ .unreq DAT2
++ .unreq DAT3
++ .unreq CHAN0
++ .unreq CHAN1
++ .unreq CHAN2
++ .unreq CHAN3
++ .unreq CHAN4
++ .unreq CHAN5
++
++.endif // mixed
++.endif // inorder
++.endm // implement_pack
++
++.macro pack_channels inorder, channels
++ implement_pack \inorder, \channels, 0
++ implement_pack \inorder, \channels, 1
++ implement_pack \inorder, \channels, 2
++ implement_pack \inorder, \channels, 3
++ implement_pack \inorder, \channels, 4
++ implement_pack \inorder, \channels, 5
++ implement_pack \inorder, \channels, mixed
++.endm
++
++.macro pack_order inorder
++ pack_channels \inorder, 2
++ pack_channels \inorder, 6
++ pack_channels \inorder, 8
++.endm
++
++ pack_order 0
++ pack_order 1
+diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+index 268dfdd..2d8b98d 100644
+--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c
+@@ -41,8 +41,72 @@ void ff_mlp_rematrix_channel_arm(int32_t *samples,
+ int access_unit_size_pow2,
+ int32_t mask);
+
++#define DECLARE_PACK(order,channels,shift) \
++ int32_t ff_mlp_pack_output_##order##order_##channels##ch_##shift##shift_arm(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *);
++#define ENUMERATE_PACK(order,channels,shift) \
++ ff_mlp_pack_output_##order##order_##channels##ch_##shift##shift_arm,
++#define PACK_CHANNELS(macro,order,channels) \
++ macro(order,channels,0) \
++ macro(order,channels,1) \
++ macro(order,channels,2) \
++ macro(order,channels,3) \
++ macro(order,channels,4) \
++ macro(order,channels,5) \
++ macro(order,channels,mixed)
++#define PACK_ORDER(macro,order) \
++ PACK_CHANNELS(macro,order,2) \
++ PACK_CHANNELS(macro,order,6) \
++ PACK_CHANNELS(macro,order,8)
++#define PACK_ALL(macro) \
++ PACK_ORDER(macro,outof) \
++ PACK_ORDER(macro,in)
++PACK_ALL(DECLARE_PACK)
++
++#define ff_mlp_pack_output_outoforder_2ch_mixedshift_arm 0
++#define ff_mlp_pack_output_outoforder_6ch_mixedshift_arm 0
++#define ff_mlp_pack_output_outoforder_8ch_mixedshift_arm 0
++
++static int32_t (*mlp_select_pack_output_arm(uint8_t max_matrix_channel,
++ int is32,
++ uint8_t *ch_assign,
++ int8_t *output_shift))(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *)
++{
++ int ch_index;
++ int shift = output_shift[0] < 0 || output_shift[0] > 5 ? 6 : output_shift[0];
++ int inorder = 1;
++ static int32_t (*const routine[2*3*7])(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *) = {
++ PACK_ALL(ENUMERATE_PACK)
++ };
++ int i;
++
++ if (!is32) // don't support 16-bit output (it's not used by TrueHD)
++ return ff_mlp_pack_output;
++
++ switch (max_matrix_channel) {
++ case 1: ch_index = 0; break;
++ case 5: ch_index = 1; break;
++ case 7: ch_index = 2; break;
++ default: return ff_mlp_pack_output;
++ }
++
++ for (i = 0; i <= max_matrix_channel; i++) {
++ if (shift != 6 && output_shift[i] != shift)
++ shift = 6; // indicate mixed shifts
++ if (ch_assign[i] != i)
++ inorder = 0;
++ }
++ if (shift == 6 && !inorder)
++ return ff_mlp_pack_output; // can't currently handle both an order array and a shift array
++
++ return routine[(inorder*3+ch_index)*7+shift];
++}
++
+ av_cold void ff_mlpdsp_init_arm(MLPDSPContext *c)
+ {
++ int cpu_flags = av_get_cpu_flags();
++
+ c->mlp_filter_channel = ff_mlp_filter_channel_arm;
+ c->mlp_rematrix_channel = ff_mlp_rematrix_channel_arm;
++ if (cpu_flags & AV_CPU_FLAG_ARMV6)
++ c->mlp_select_pack_output = mlp_select_pack_output_arm;
+ }
+--
+1.9.3
+
+
+From 9af15bf0b7bc7940bd8bcc9ddae23178c9723bd6 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Thu, 14 Nov 2013 19:48:41 +0000
+Subject: [PATCH 14/94] More efficient infobool expression evaluator
+
+Expession infobools are evaluated at runtime from one or more single infobools
+and a combination of boolean NOT, AND and OR operators. Previously, parsing
+produced a vector of operands (leaf nodes) and operators in postfix
+(reverse-Polish) form, and evaluated all leaf nodes every time the expression
+was evaluated. But this ignores the fact that in many cases, once one operand
+of an AND or OR operation has been evaluated, there is no need to evaluate the
+other operand because its value can have no effect on the ultimate result. It
+is also worth noting that AND and OR operations are associative, meaning they
+can be rearranged at runtime to better suit the selected skin.
+
+This patch rewrites the expression parsing and evaluation code. Now the
+internal repreentation is in the form of a tree where leaf nodes represent a
+single infobool, and branch nodes represent either an AND or an OR operation
+on two or more child nodes.
+
+Expressions are rewritten at parse time into a form which favours the
+formation of groups of associative nodes. These groups are then reordered at
+evaluation time such that nodes whose value renders the evaluation of the
+remainder of the group unnecessary tend to be evaluated first (these are
+true nodes for OR subexpressions, or false nodes for AND subexpressions).
+The end effect is to minimise the number of leaf nodes that need to be
+evaluated in order to determine the value of the expression. The runtime
+adaptability has the advantage of not being customised for any particular skin.
+
+The modifications to the expression at parse time fall into two groups:
+1) Moving logical NOTs so that they are only applied to leaf nodes.
+ For example, rewriting ![A+B]|C as !A|!B|C allows reordering such that
+ any of the three leaves can be evaluated first.
+2) Combining adjacent AND or OR operations such that each path from the root
+ to a leaf encounters a strictly alternating pattern of AND and OR
+ operations. So [A|B]|[C|D+[[E|F]|G] becomes A|B|C|[D+[E|F|G]].
+
+I measured the effect while the Videos window of the default skin was open
+(but idle) on a Raspberry Pi, and this reduced the CPU usage by 2.8% from
+41.9% to 39.1%:
+
+ Before After
+ Mean StdDev Mean StdDev Confidence Change
+IdleCPU% 41.9 0.5 39.1 0.9 100.0% +7.0%
+---
+ xbmc/interfaces/info/InfoExpression.cpp | 313 +++++++++++++++++++++-----------
+ xbmc/interfaces/info/InfoExpression.h | 63 ++++++-
+ 2 files changed, 269 insertions(+), 107 deletions(-)
+
+diff --git a/xbmc/interfaces/info/InfoExpression.cpp b/xbmc/interfaces/info/InfoExpression.cpp
+index f4d32c1..db461dd 100644
+--- a/xbmc/interfaces/info/InfoExpression.cpp
++++ b/xbmc/interfaces/info/InfoExpression.cpp
+@@ -22,6 +22,9 @@
+ #include <stack>
+ #include "utils/log.h"
+ #include "GUIInfoManager.h"
++#include <list>
++#include <boost/shared_ptr.hpp>
++#include <boost/make_shared.hpp>
+
+ using namespace std;
+ using namespace INFO;
+@@ -40,21 +43,89 @@ void InfoSingle::Update(const CGUIListItem *item)
+ InfoExpression::InfoExpression(const std::string &expression, int context)
+ : InfoBool(expression, context)
+ {
+- Parse(expression);
++ if (!Parse(expression))
++ CLog::Log(LOGERROR, "Error parsing boolean expression %s", expression.c_str());
+ }
+
+ void InfoExpression::Update(const CGUIListItem *item)
+ {
+- Evaluate(item, m_value);
++ m_value = m_expression_tree->Evaluate(item);
+ }
+
+-#define OPERATOR_LB 5
+-#define OPERATOR_RB 4
+-#define OPERATOR_NOT 3
+-#define OPERATOR_AND 2
+-#define OPERATOR_OR 1
++/* Expressions are rewritten at parse time into a form which favours the
++ * formation of groups of associative nodes. These groups are then reordered at
++ * evaluation time such that nodes whose value renders the evaluation of the
++ * remainder of the group unnecessary tend to be evaluated first (these are
++ * true nodes for OR subexpressions, or false nodes for AND subexpressions).
++ * The end effect is to minimise the number of leaf nodes that need to be
++ * evaluated in order to determine the value of the expression. The runtime
++ * adaptability has the advantage of not being customised for any particular skin.
++ *
++ * The modifications to the expression at parse time fall into two groups:
++ * 1) Moving logical NOTs so that they are only applied to leaf nodes.
++ * For example, rewriting ![A+B]|C as !A|!B|C allows reordering such that
++ * any of the three leaves can be evaluated first.
++ * 2) Combining adjacent AND or OR operations such that each path from the root
++ * to a leaf encounters a strictly alternating pattern of AND and OR
++ * operations. So [A|B]|[C|D+[[E|F]|G] becomes A|B|C|[D+[E|F|G]].
++ */
++
++bool InfoExpression::InfoLeaf::Evaluate(const CGUIListItem *item)
++{
++ return m_invert ^ m_info->Get(item);
++}
+
+-short InfoExpression::GetOperator(const char ch) const
++InfoExpression::InfoAssociativeGroup::InfoAssociativeGroup(
++ bool and_not_or,
++ const InfoSubexpressionPtr &left,
++ const InfoSubexpressionPtr &right)
++ : m_and_not_or(and_not_or)
++{
++ AddChild(right);
++ AddChild(left);
++}
++
++void InfoExpression::InfoAssociativeGroup::AddChild(const InfoSubexpressionPtr &child)
++{
++ m_children.push_front(child); // largely undoes the effect of parsing right-associative
++}
++
++void InfoExpression::InfoAssociativeGroup::Merge(InfoAssociativeGroup *other)
++{
++ m_children.splice(m_children.end(), other->m_children);
++}
++
++bool InfoExpression::InfoAssociativeGroup::Evaluate(const CGUIListItem *item)
++{
++ /* Handle either AND or OR by using the relation
++ * A AND B == !(!A OR !B)
++ * to convert ANDs into ORs
++ */
++ std::list<InfoSubexpressionPtr>::iterator last = m_children.end();
++ std::list<InfoSubexpressionPtr>::iterator it = m_children.begin();
++ bool result = m_and_not_or ^ (*it)->Evaluate(item);
++ while (!result && ++it != last)
++ {
++ result = m_and_not_or ^ (*it)->Evaluate(item);
++ if (result)
++ {
++ /* Move this child to the head of the list so we evaluate faster next time */
++ InfoSubexpressionPtr p = *it;
++ m_children.erase(it);
++ m_children.push_front(p);
++ }
++ }
++ return m_and_not_or ^ result;
++}
++
++/* Expressions are parsed using the shunting-yard algorithm. Binary operators
++ * (AND/OR) are treated as right-associative so that we don't need to make a
++ * special case for the unary NOT operator. This has no effect upon the answers
++ * generated, though the initial sequence of evaluation of leaves may be
++ * different from what you might expect.
++ */
++
++InfoExpression::operator_t InfoExpression::GetOperator(char ch)
+ {
+ if (ch == '[')
+ return OPERATOR_LB;
+@@ -67,122 +138,160 @@ short InfoExpression::GetOperator(const char ch) const
+ else if (ch == '|')
+ return OPERATOR_OR;
+ else
+- return 0;
++ return OPERATOR_NONE;
+ }
+
+-void InfoExpression::Parse(const std::string &expression)
++void InfoExpression::OperatorPop(std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes)
+ {
+- stack<char> operators;
+- std::string operand;
+- for (unsigned int i = 0; i < expression.size(); i++)
++ operator_t op2 = operator_stack.top();
++ operator_stack.pop();
++ if (op2 == OPERATOR_NOT)
+ {
+- if (GetOperator(expression[i]))
++ invert = !invert;
++ }
++ else
++ {
++ // At this point, it can only be OPERATOR_AND or OPERATOR_OR
++ if (invert)
++ op2 = (operator_t) (OPERATOR_AND ^ OPERATOR_OR ^ op2);
++ node_type_t new_type = op2 == OPERATOR_AND ? NODE_AND : NODE_OR;
++
++ InfoSubexpressionPtr right = nodes.top();
++ nodes.pop();
++ InfoSubexpressionPtr left = nodes.top();
++
++ node_type_t right_type = node_types.top();
++ node_types.pop();
++ node_type_t left_type = node_types.top();
++
++ // Combine associative operations into the same node where possible
++ if (left_type == new_type && right_type == new_type)
++ (static_cast<InfoAssociativeGroup *>(left.get()))->Merge(static_cast<InfoAssociativeGroup *>(right.get()));
++ else if (left_type == new_type)
++ (static_cast<InfoAssociativeGroup *>(left.get()))->AddChild(right);
++ else
+ {
+- // cleanup any operand, translate and put into our expression list
+- if (!operand.empty())
++ nodes.pop();
++ node_types.pop();
++ if (right_type == new_type)
+ {
+- InfoPtr info = g_infoManager.Register(operand, m_context);
+- if (info)
+- {
+- m_listItemDependent |= info->ListItemDependent();
+- m_postfix.push_back(m_operands.size());
+- m_operands.push_back(info);
+- }
+- operand.clear();
++ (static_cast<InfoAssociativeGroup *>(right.get()))->AddChild(left);
++ nodes.push(right);
+ }
+- // handle closing parenthesis
+- if (expression[i] == ']')
+- {
+- while (!operators.empty())
+- {
+- char oper = operators.top();
+- operators.pop();
++ else
++ nodes.push(boost::make_shared<InfoAssociativeGroup>(new_type == NODE_AND, left, right));
++ node_types.push(new_type);
++ }
++ }
++}
++
++void InfoExpression::ProcessOperator(operator_t op, std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes)
++{
++ // Handle any higher-priority stacked operators, except when the new operator is left-bracket.
++ // For a right-bracket, this will stop with the matching left-bracket at the top of the operator stack.
++ if (op != OPERATOR_LB)
++ {
++ while (operator_stack.size() > 0 && operator_stack.top() > op)
++ OperatorPop(operator_stack, invert, node_types, nodes);
++ }
++ if (op == OPERATOR_RB)
++ operator_stack.pop(); // remove the matching left-bracket
++ else
++ operator_stack.push(op);
++ if (op == OPERATOR_NOT)
++ invert = !invert;
++}
+
+- if (oper == '[')
+- break;
++bool InfoExpression::ProcessOperand(std::string &operand, bool invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes)
++{
++ InfoPtr info = g_infoManager.Register(operand, m_context);
++ if (!info)
++ return false;
++ m_listItemDependent |= info->ListItemDependent();
++ nodes.push(boost::make_shared<InfoLeaf>(info, invert));
++ node_types.push(NODE_LEAF);
++ operand.clear();
++ return true;
++}
+
+- m_postfix.push_back(-GetOperator(oper)); // negative denotes operator
+- }
++bool InfoExpression::Parse(const std::string &expression)
++{
++ const char *s = expression.c_str();
++ std::string operand;
++ std::stack<operator_t> operator_stack;
++ bool invert = false;
++ std::stack<node_type_t> node_types;
++ std::stack<InfoSubexpressionPtr> nodes;
++ // The next two are for syntax-checking purposes
++ bool after_binaryoperator = true;
++ int bracket_count = 0;
++
++ char c;
++ // Skip leading whitespace - don't want it to count as an operand if that's all there is
++ do
++ {
++ c = *s++;
++ } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
++ s--;
++ while ((c = *s++) != '\0')
++ {
++ operator_t op;
++ if ((op = GetOperator(c)) != OPERATOR_NONE)
++ {
++ // Character is an operator
++ if ((!after_binaryoperator && (c == '!' || c == '[')) ||
++ (after_binaryoperator && (c == ']' || c == '+' || c == '|')))
++ {
++ CLog::Log(LOGERROR, "Misplaced %c", c);
++ return false;
+ }
+- else
++ if (c == '[')
++ bracket_count++;
++ else if (c == ']' && bracket_count-- == 0)
++ {
++ CLog::Log(LOGERROR, "Unmatched ]");
++ return false;
++ }
++ if (operand.size() > 0 && !ProcessOperand(operand, invert, node_types, nodes))
+ {
+- // all other operators we pop off the stack any operator
+- // that has a higher priority than the one we have.
+- while (!operators.empty() && GetOperator(operators.top()) > GetOperator(expression[i]))
+- {
+- // only handle parenthesis once they're closed.
+- if (operators.top() == '[' && expression[i] != ']')
+- break;
+-
+- m_postfix.push_back(-GetOperator(operators.top())); // negative denotes operator
+- operators.pop();
+- }
+- operators.push(expression[i]);
++ CLog::Log(LOGERROR, "Bad operand '%s'", operand.c_str());
++ return false;
+ }
++ ProcessOperator(op, operator_stack, invert, node_types, nodes);
++ if (c == '+' || c == '|')
++ after_binaryoperator = true;
++ // Skip trailing whitespace - don't want it to count as an operand if that's all there is
++ do
++ {
++ c = *s++;
++ } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
++ s--;
+ }
+ else
+ {
+- operand += expression[i];
++ // Character is part of operand
++ operand += c;
++ after_binaryoperator = false;
+ }
+ }
+-
+- if (!operand.empty())
++ if (bracket_count > 0)
+ {
+- InfoPtr info = g_infoManager.Register(operand, m_context);
+- if (info)
+- {
+- m_listItemDependent |= info->ListItemDependent();
+- m_postfix.push_back(m_operands.size());
+- m_operands.push_back(info);
+- }
++ CLog::Log(LOGERROR, "Unmatched [");
++ return false;
+ }
+-
+- // finish up by adding any operators
+- while (!operators.empty())
++ if (after_binaryoperator)
+ {
+- m_postfix.push_back(-GetOperator(operators.top())); // negative denotes operator
+- operators.pop();
++ CLog::Log(LOGERROR, "Missing operand");
++ return false;
+ }
+-
+- // test evaluate
+- bool test;
+- if (!Evaluate(NULL, test))
+- CLog::Log(LOGERROR, "Error evaluating boolean expression %s", expression.c_str());
+-}
+-
+-bool InfoExpression::Evaluate(const CGUIListItem *item, bool &result)
+-{
+- stack<bool> save;
+- for (vector<short>::const_iterator it = m_postfix.begin(); it != m_postfix.end(); ++it)
++ if (operand.size() > 0 && !ProcessOperand(operand, invert, node_types, nodes))
+ {
+- short expr = *it;
+- if (expr == -OPERATOR_NOT)
+- { // NOT the top item on the stack
+- if (save.empty()) return false;
+- bool expr = save.top();
+- save.pop();
+- save.push(!expr);
+- }
+- else if (expr == -OPERATOR_AND)
+- { // AND the top two items on the stack
+- if (save.size() < 2) return false;
+- bool right = save.top(); save.pop();
+- bool left = save.top(); save.pop();
+- save.push(left && right);
+- }
+- else if (expr == -OPERATOR_OR)
+- { // OR the top two items on the stack
+- if (save.size() < 2) return false;
+- bool right = save.top(); save.pop();
+- bool left = save.top(); save.pop();
+- save.push(left || right);
+- }
+- else if (expr >= 0) // operand
+- save.push(m_operands[expr]->Get(item));
+- }
+- if (save.size() != 1)
++ CLog::Log(LOGERROR, "Bad operand '%s'", operand.c_str());
+ return false;
+- result = save.top();
++ }
++ while (operator_stack.size() > 0)
++ OperatorPop(operator_stack, invert, node_types, nodes);
++
++ m_expression_tree = nodes.top();
+ return true;
+ }
+-
+diff --git a/xbmc/interfaces/info/InfoExpression.h b/xbmc/interfaces/info/InfoExpression.h
+index 4e0faee..0a91399 100644
+--- a/xbmc/interfaces/info/InfoExpression.h
++++ b/xbmc/interfaces/info/InfoExpression.h
+@@ -21,6 +21,8 @@
+ #pragma once
+
+ #include <vector>
++#include <list>
++#include <stack>
+ #include "InfoBool.h"
+
+ class CGUIListItem;
+@@ -50,12 +52,63 @@ class InfoExpression : public InfoBool
+
+ virtual void Update(const CGUIListItem *item);
+ private:
+- void Parse(const std::string &expression);
+- bool Evaluate(const CGUIListItem *item, bool &result);
+- short GetOperator(const char ch) const;
++ typedef enum
++ {
++ OPERATOR_NONE = 0,
++ OPERATOR_LB, // 1
++ OPERATOR_RB, // 2
++ OPERATOR_OR, // 3
++ OPERATOR_AND, // 4
++ OPERATOR_NOT, // 5
++ } operator_t;
+
+- std::vector<short> m_postfix; ///< the postfix form of the expression (operators and operand indicies)
+- std::vector<InfoPtr> m_operands; ///< the operands in the expression
++ typedef enum
++ {
++ NODE_LEAF,
++ NODE_AND,
++ NODE_OR,
++ } node_type_t;
++
++ // An abstract base class for nodes in the expression tree
++ class InfoSubexpression
++ {
++ public:
++ virtual ~InfoSubexpression(void) {}; // so we can destruct derived classes using a pointer to their base class
++ virtual bool Evaluate(const CGUIListItem *item) = 0;
++ };
++
++ typedef boost::shared_ptr<InfoSubexpression> InfoSubexpressionPtr;
++
++ // A leaf node in the expression tree
++ class InfoLeaf : public InfoSubexpression
++ {
++ public:
++ InfoLeaf(InfoPtr info, bool invert) : m_info(info), m_invert(invert) {};
++ virtual bool Evaluate(const CGUIListItem *item);
++ private:
++ InfoPtr m_info;
++ bool m_invert;
++ };
++
++ // A branch node in the expression tree
++ class InfoAssociativeGroup : public InfoSubexpression
++ {
++ public:
++ InfoAssociativeGroup(bool and_not_or, const InfoSubexpressionPtr &left, const InfoSubexpressionPtr &right);
++ void AddChild(const InfoSubexpressionPtr &child);
++ void Merge(InfoAssociativeGroup *other);
++ virtual bool Evaluate(const CGUIListItem *item);
++ private:
++ bool m_and_not_or;
++ std::list<InfoSubexpressionPtr> m_children;
++ };
++
++ static operator_t GetOperator(char ch);
++ static void OperatorPop(std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes);
++ static void ProcessOperator(operator_t op, std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes);
++ bool ProcessOperand(std::string &operand, bool invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes);
++ bool Parse(const std::string &expression);
++ InfoSubexpressionPtr m_expression_tree;
+ };
+
+ };
+--
+1.9.3
+
+
+From 36067cf823d539a00eea75d561ac78a4b1431a66 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Mon, 24 Mar 2014 22:26:21 +0000
+Subject: [PATCH 15/94] Where an infobool expression failed to parse, evaluate
+ the infobool as false. Previously, this would result in a segfault due to the
+ dereferencing of an uninitialised pointer to the head of the expression tree.
+
+---
+ xbmc/interfaces/info/InfoExpression.cpp | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/xbmc/interfaces/info/InfoExpression.cpp b/xbmc/interfaces/info/InfoExpression.cpp
+index db461dd..7c54064 100644
+--- a/xbmc/interfaces/info/InfoExpression.cpp
++++ b/xbmc/interfaces/info/InfoExpression.cpp
+@@ -44,7 +44,10 @@ InfoExpression::InfoExpression(const std::string &expression, int context)
+ : InfoBool(expression, context)
+ {
+ if (!Parse(expression))
++ {
+ CLog::Log(LOGERROR, "Error parsing boolean expression %s", expression.c_str());
++ m_expression_tree = boost::make_shared<InfoLeaf>(g_infoManager.Register("false", 0), false);
++ }
+ }
+
+ void InfoExpression::Update(const CGUIListItem *item)
+--
+1.9.3
+
+
+From 7f2870606f1e183d70b1dc2dbc07fa8bc437d0cc Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 26 Nov 2013 20:09:48 +0000
+Subject: [PATCH 16/94] Add caching of infolabels
+
+The functions CGUIInfoLabel::GetLabel and CGUIInfoLabel::GetItemLabel take
+a number of strings returned from CGUIInfoManager::GetImage or
+CGUIInfoManager::GetLabel, and combine them with various constant strings
+which were determined during CGUIInfoLabel::Parse.
+
+Rather than perform all the string operations on every call, this patch
+changes to use a two-pass process: first it queries all the GetImage/GetLabel
+strings, and then only if at least one of them has changed does it bother
+rebuilding the resultant string - otherwise it re-uses the copy built on a
+preceding call.
+
+CGUIInfoLabel::GetLabel/GetItemLabel are also changed to return string
+references, rather than forcing an additional string copy.
+
+I have measured the effect while the Videos window of the default skin was
+open (but idle) on a Raspberry Pi, and this reduced the CPU usage by 0.8%
+from 36.2% to 35.4%:
+
+ Before After
+ Mean StdDev Mean StdDev Confidence Change
+IdleCPU% 36.2 0.5 35.4 0.5 99.9% +2.2%
+---
+ xbmc/guilib/GUIInfoTypes.cpp | 102 +++++++++++++++++++++++++++++++++----------
+ xbmc/guilib/GUIInfoTypes.h | 11 ++++-
+ 2 files changed, 87 insertions(+), 26 deletions(-)
+
+diff --git a/xbmc/guilib/GUIInfoTypes.cpp b/xbmc/guilib/GUIInfoTypes.cpp
+index 6977e0f..d78c26a 100644
+--- a/xbmc/guilib/GUIInfoTypes.cpp
++++ b/xbmc/guilib/GUIInfoTypes.cpp
+@@ -136,37 +136,64 @@ void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback
+ Parse(label, context);
+ }
+
+-CStdString CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, CStdString *fallback /*= NULL*/) const
++const std::string &CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, CStdString *fallback /*= NULL*/) const
+ {
+- CStdString label;
+- for (unsigned int i = 0; i < m_info.size(); i++)
++ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
+ {
+ const CInfoPortion &portion = m_info[i];
+ if (portion.m_info)
+ {
+- CStdString infoLabel;
++ std::string infoLabel;
+ if (preferImage)
+ infoLabel = g_infoManager.GetImage(portion.m_info, contextWindow, fallback);
+ if (infoLabel.empty())
+ infoLabel = g_infoManager.GetLabel(portion.m_info, contextWindow, fallback);
+- if (!infoLabel.empty())
+- label += portion.GetLabel(infoLabel);
++ if (j == m_labelPortions.size())
++ m_labelPortions.push_back(infoLabel);
++ else if (infoLabel != m_labelPortions[j])
++ {
++ m_labelPortions[j] = infoLabel;
++ m_labelDirty = true;
++ }
++ j++;
+ }
+- else
+- { // no info, so just append the prefix
+- label += portion.m_prefix;
++ }
++ if (m_labelDirty)
++ {
++ m_label.clear();
++ for (unsigned int i = 0, j= 0; i < m_info.size(); i++)
++ {
++ const CInfoPortion &portion = m_info[i];
++ if (portion.m_info)
++ {
++ if (!m_labelPortions[j].empty())
++ m_label += portion.GetLabel(m_labelPortions[j]);
++ j++;
++ }
++ else
++ { // no info, so just append the prefix
++ m_label += portion.m_prefix;
++ }
+ }
++ if (m_label.empty()) // empty label, use the fallback
++ m_label = m_fallback;
++ m_labelDirty = false;
+ }
+- if (label.empty()) // empty label, use the fallback
+- return m_fallback;
+- return label;
++ return m_label;
+ }
+
+-CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, CStdString *fallback /*= NULL*/) const
++const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, CStdString *fallback /*= NULL*/) const
+ {
+- if (!item->IsFileItem()) return "";
+- CStdString label;
+- for (unsigned int i = 0; i < m_info.size(); i++)
++ if (!item->IsFileItem())
++ {
++ if (m_itemLabelDirty)
++ {
++ m_itemLabel = "";
++ m_itemLabelDirty = false;
++ }
++ return m_itemLabel;
++ }
++ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
+ {
+ const CInfoPortion &portion = m_info[i];
+ if (portion.m_info)
+@@ -176,17 +203,38 @@ CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImag
+ infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info, fallback);
+ else
+ infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info, fallback);
+- if (!infoLabel.empty())
+- label += portion.GetLabel(infoLabel);
++ if (j == m_itemLabelPortions.size())
++ m_itemLabelPortions.push_back(infoLabel);
++ else if (infoLabel != m_itemLabelPortions[j])
++ {
++ m_itemLabelPortions[j] = infoLabel;
++ m_itemLabelDirty = true;
++ }
++ j++;
+ }
+- else
+- { // no info, so just append the prefix
+- label += portion.m_prefix;
++ }
++ if (m_itemLabelDirty)
++ {
++ m_itemLabel.clear();
++ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
++ {
++ const CInfoPortion &portion = m_info[i];
++ if (portion.m_info)
++ {
++ if (!m_itemLabelPortions[j].empty())
++ m_itemLabel += portion.GetLabel(m_itemLabelPortions[j]);
++ j++;
++ }
++ else
++ { // no info, so just append the prefix
++ m_itemLabel += portion.m_prefix;
++ }
+ }
++ if (m_itemLabel.empty())
++ m_itemLabel = m_fallback;
++ m_itemLabelDirty = false;
+ }
+- if (label.empty())
+- return m_fallback;
+- return label;
++ return m_itemLabel;
+ }
+
+ bool CGUIInfoLabel::IsEmpty() const
+@@ -277,6 +325,12 @@ const static infoformat infoformatmap[] = {{ "$INFO[", FORMATINFO },
+ void CGUIInfoLabel::Parse(const CStdString &label, int context)
+ {
+ m_info.clear();
++ m_labelDirty = true;
++ m_label.clear();
++ m_labelPortions.clear();
++ m_itemLabelDirty = true;
++ m_itemLabel.clear();
++ m_itemLabelPortions.clear();
+ // Step 1: Replace all $LOCALIZE[number] with the real string
+ CStdString work = ReplaceLocalize(label);
+ // Step 2: Replace all $ADDON[id number] with the real string
+diff --git a/xbmc/guilib/GUIInfoTypes.h b/xbmc/guilib/GUIInfoTypes.h
+index 8c1c1dc..418b2c4 100644
+--- a/xbmc/guilib/GUIInfoTypes.h
++++ b/xbmc/guilib/GUIInfoTypes.h
+@@ -83,7 +83,7 @@ class CGUIInfoLabel
+ \param fallback if non-NULL, is set to an alternate value to use should the actual value be not appropriate. Defaults to NULL.
+ \return label (or image).
+ */
+- CStdString GetLabel(int contextWindow, bool preferImage = false, CStdString *fallback = NULL) const;
++ const std::string &GetLabel(int contextWindow, bool preferImage = false, CStdString *fallback = NULL) const;
+
+ /*!
+ \brief Gets a label (or image) for a given listitem from the info manager.
+@@ -92,7 +92,7 @@ class CGUIInfoLabel
+ \param fallback if non-NULL, is set to an alternate value to use should the actual value be not appropriate. Defaults to NULL.
+ \return label (or image).
+ */
+- CStdString GetItemLabel(const CGUIListItem *item, bool preferImage = false, CStdString *fallback = NULL) const;
++ const std::string &GetItemLabel(const CGUIListItem *item, bool preferImage = false, CStdString *fallback = NULL) const;
+
+ bool IsConstant() const;
+ bool IsEmpty() const;
+@@ -132,6 +132,13 @@ class CGUIInfoLabel
+
+ CStdString m_fallback;
+ std::vector<CInfoPortion> m_info;
++
++ mutable bool m_labelDirty;
++ mutable std::string m_label;
++ mutable std::vector<std::string> m_labelPortions;
++ mutable bool m_itemLabelDirty;
++ mutable std::string m_itemLabel;
++ mutable std::vector<std::string> m_itemLabelPortions;
+ };
+
+ #endif
+--
+1.9.3
+
+
+From 3d5a1912ffd4556ec09208fea50d2a2919775c9f Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 10 Dec 2013 01:12:31 +0000
+Subject: [PATCH 17/94] De-duplication of string cache for non-item and item
+ labels
+
+---
+ xbmc/guilib/GUIInfoTypes.cpp | 50 +++++++++++++++++++++++++-------------------
+ xbmc/guilib/GUIInfoTypes.h | 4 +---
+ 2 files changed, 29 insertions(+), 25 deletions(-)
+
+diff --git a/xbmc/guilib/GUIInfoTypes.cpp b/xbmc/guilib/GUIInfoTypes.cpp
+index d78c26a..8bd131f 100644
+--- a/xbmc/guilib/GUIInfoTypes.cpp
++++ b/xbmc/guilib/GUIInfoTypes.cpp
+@@ -121,7 +121,7 @@ void CGUIInfoColor::Parse(const CStdString &label, int context)
+ m_color = g_colorManager.GetColor(label);
+ }
+
+-CGUIInfoLabel::CGUIInfoLabel()
++CGUIInfoLabel::CGUIInfoLabel() : m_labelDirty(true)
+ {
+ }
+
+@@ -178,7 +178,10 @@ const std::string &CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage,
+ if (m_label.empty()) // empty label, use the fallback
+ m_label = m_fallback;
+ m_labelDirty = false;
++ m_isLabelOfListItem = false;
+ }
++ else
++ assert(m_isLabelOfListItem == false);
+ return m_label;
+ }
+
+@@ -186,12 +189,15 @@ const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool pr
+ {
+ if (!item->IsFileItem())
+ {
+- if (m_itemLabelDirty)
++ if (m_labelDirty)
+ {
+- m_itemLabel = "";
+- m_itemLabelDirty = false;
++ m_label = "";
++ m_labelDirty = false;
++ m_isLabelOfListItem = true;
+ }
+- return m_itemLabel;
++ else
++ assert(m_isLabelOfListItem == true);
++ return m_label;
+ }
+ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
+ {
+@@ -203,38 +209,41 @@ const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool pr
+ infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info, fallback);
+ else
+ infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info, fallback);
+- if (j == m_itemLabelPortions.size())
+- m_itemLabelPortions.push_back(infoLabel);
+- else if (infoLabel != m_itemLabelPortions[j])
++ if (j == m_labelPortions.size())
++ m_labelPortions.push_back(infoLabel);
++ else if (infoLabel != m_labelPortions[j])
+ {
+- m_itemLabelPortions[j] = infoLabel;
+- m_itemLabelDirty = true;
++ m_labelPortions[j] = infoLabel;
++ m_labelDirty = true;
+ }
+ j++;
+ }
+ }
+- if (m_itemLabelDirty)
++ if (m_labelDirty)
+ {
+- m_itemLabel.clear();
++ m_label.clear();
+ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
+ {
+ const CInfoPortion &portion = m_info[i];
+ if (portion.m_info)
+ {
+- if (!m_itemLabelPortions[j].empty())
+- m_itemLabel += portion.GetLabel(m_itemLabelPortions[j]);
++ if (!m_labelPortions[j].empty())
++ m_label += portion.GetLabel(m_labelPortions[j]);
+ j++;
+ }
+ else
+ { // no info, so just append the prefix
+- m_itemLabel += portion.m_prefix;
++ m_label += portion.m_prefix;
+ }
+ }
+- if (m_itemLabel.empty())
+- m_itemLabel = m_fallback;
+- m_itemLabelDirty = false;
++ if (m_label.empty())
++ m_label = m_fallback;
++ m_labelDirty = false;
++ m_isLabelOfListItem = true;
+ }
+- return m_itemLabel;
++ else
++ assert(m_isLabelOfListItem == true);
++ return m_label;
+ }
+
+ bool CGUIInfoLabel::IsEmpty() const
+@@ -328,9 +337,6 @@ void CGUIInfoLabel::Parse(const CStdString &label, int context)
+ m_labelDirty = true;
+ m_label.clear();
+ m_labelPortions.clear();
+- m_itemLabelDirty = true;
+- m_itemLabel.clear();
+- m_itemLabelPortions.clear();
+ // Step 1: Replace all $LOCALIZE[number] with the real string
+ CStdString work = ReplaceLocalize(label);
+ // Step 2: Replace all $ADDON[id number] with the real string
+diff --git a/xbmc/guilib/GUIInfoTypes.h b/xbmc/guilib/GUIInfoTypes.h
+index 418b2c4..6d9ebf7 100644
+--- a/xbmc/guilib/GUIInfoTypes.h
++++ b/xbmc/guilib/GUIInfoTypes.h
+@@ -133,12 +133,10 @@ class CGUIInfoLabel
+ CStdString m_fallback;
+ std::vector<CInfoPortion> m_info;
+
++ mutable bool m_isLabelOfListItem;
+ mutable bool m_labelDirty;
+ mutable std::string m_label;
+ mutable std::vector<std::string> m_labelPortions;
+- mutable bool m_itemLabelDirty;
+- mutable std::string m_itemLabel;
+- mutable std::vector<std::string> m_itemLabelPortions;
+ };
+
+ #endif
+--
+1.9.3
+
+
+From 1427baf4395b760227afbef8e17956ba251f2fbe Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Fri, 21 Feb 2014 15:16:13 +0000
+Subject: [PATCH 18/94] Faster and simpler portable implementation of
+ MathUtils::round_int().
+
+Much as I like a bit of inline assembler, I have also removed the ARM versions
+of MathUtils::truncate_int() and MathUtils::round_int(). The former was just
+how any sane compiler should have assembled a cast from double to signed int
+anyway. The latter was a much too complicated way to achieve the desired
+effect, and was switched out in most ARM builds anyway in favour of the old
+portable implementation that used floor().
+
+Verified that MathUtils::test() still passes, and that GCC is now able to
+inline MathUtils::round_int(), where it didn't previously.
+
+I tested on a Raspberry Pi with the default theme, displaying the front page
+with the RSS ticker enabled. This saturates the CPU, so I'm measuring the
+improvement using the debug window's FPS figure. This patch improves this from
+~50.8 FPS to ~52.6 FPS.
+---
+ xbmc/utils/MathUtils.h | 129 +++++++++++++++++++++++--------------------------
+ 1 file changed, 61 insertions(+), 68 deletions(-)
+
+diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h
+index 96af9f4..0dae77d 100644
+--- a/xbmc/utils/MathUtils.h
++++ b/xbmc/utils/MathUtils.h
+@@ -34,17 +34,13 @@
+
+ #if defined(__ppc__) || \
+ defined(__powerpc__) || \
+- (defined(TARGET_DARWIN_IOS) && defined(__llvm__)) || \
+- (defined(TARGET_ANDROID) && defined(__arm__)) || \
+- defined(TARGET_RASPBERRY_PI)
++ defined(__arm__)
+ #define DISABLE_MATHUTILS_ASM_ROUND_INT
+ #endif
+
+ #if defined(__ppc__) || \
+ defined(__powerpc__) || \
+- (defined(TARGET_DARWIN) && defined(__llvm__)) || \
+- (defined(TARGET_ANDROID) && defined(__arm__)) || \
+- defined(TARGET_RASPBERRY_PI)
++ defined(__arm__)
+ #define DISABLE_MATHUTILS_ASM_TRUNCATE_INT
+ #endif
+
+@@ -73,60 +69,63 @@ namespace MathUtils
+ {
+ assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
+ assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
+- const float round_to_nearest = 0.5f;
+- int i;
+
+ #if defined(DISABLE_MATHUTILS_ASM_ROUND_INT)
+- i = floor(x + round_to_nearest);
+-
+-#elif defined(__arm__)
+- // From 'ARM-v7-M Architecture Reference Manual' page A7-569:
+- // "The floating-point to integer operation (vcvt) [normally] uses the Round towards Zero rounding mode"
+- // Because of this...we must use some less-than-straightforward logic to perform this operation without
+- // changing the rounding mode flags
+-
+- /* The assembly below implements the following logic:
+- if (x < 0)
+- inc = -0.5f
+- else
+- inc = 0.5f
+- int_val = trunc(x+inc);
+- err = x - int_val;
+- if (err == 0.5f)
+- int_val++;
+- return int_val;
+- */
++ /* This implementation warrants some further explanation.
++ *
++ * First, a couple of notes on rounding:
++ * 1) C casts from float/double to integer round towards zero.
++ * 2) Float/double additions are rounded according to the normal rules,
++ * in other words: on some architectures, it's fixed at compile-time,
++ * and on others it can be set using fesetround()). The following
++ * analysis assumes round-to-nearest with ties rounding to even. This
++ * is a fairly sensible choice, and is the default with ARM VFP.
++ *
++ * What this function wants is round-to-nearest with ties rounding to
++ * +infinity. This isn't an IEEE rounding mode, even if we could guarantee
++ * that all architectures supported fesetround(), which they don't. Instead,
++ * this adds an offset of 2147483648.5 (= 0x80000000.8p0), then casts to
++ * an unsigned int (crucially, all possible inputs are now in a range where
++ * round to zero acts the same as round to -infinity) and then subtracts
++ * 0x80000000 in the integer domain. The 0.5 component of the offset
++ * converts what is effectively a round down into a round to nearest, with
++ * ties rounding up, as desired.
++ *
++ * There is a catch, that because there is a double rounding, there is a
++ * small region where the input falls just *below* a tie, where the addition
++ * of the offset causes a round *up* to an exact integer, due to the finite
++ * level of precision available in floating point. You need to be aware of
++ * this when calling this function, although at present it is not believed
++ * that XBMC ever attempts to round numbers in this window.
++ *
++ * It is worth proving the size of the affected window. Recall that double
++ * precision employs a mantissa of 52 bits.
++ * 1) For all inputs -0.5 <= x <= INT_MAX
++ * Once the offset is applied, the most significant binary digit in the
++ * floating-point representation is +2^31.
++ * At this magnitude, the smallest step representable in double precision
++ * is 2^31 / 2^52 = 0.000000476837158203125
++ * So the size of the range which is rounded up due to the addition is
++ * half the size of this step, or 0.0000002384185791015625
++ *
++ * 2) For all inputs INT_MIN/2 < x < -0.5
++ * Once the offset is applied, the most significant binary digit in the
++ * floating-point representation is +2^30.
++ * At this magnitude, the smallest step representable in double precision
++ * is 2^30 / 2^52 = 0.0000002384185791015625
++ * So the size of the range which is rounded up due to the addition is
++ * half the size of this step, or 0.00000011920928955078125
++ *
++ * 3) For all inputs INT_MIN <= x <= INT_MIN/2
++ * The representation once the offset is applied has equal or greater
++ * precision than the input, so the addition does not cause rounding.
++ */
++ return ((unsigned int) (x + 0x80000000.8p0)) - 0x80000000;
+
+- __asm__ __volatile__ (
+-#if defined(__ARM_PCS_VFP)
+- "fconstd d1,#%G[rnd_val] \n\t" // Copy round_to_nearest into a working register (d1 = 0.5)
+ #else
+- "vmov.F64 d1,%[rnd_val] \n\t"
+-#endif
+- "fcmpezd %P[value] \n\t" // Check value against zero (value == 0?)
+- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags
+- "it mi \n\t"
+- "vnegmi.F64 d1, d1 \n\t" // if N-flag is set, negate round_to_nearest (if (value < 0) d1 = -1 * d1)
+- "vadd.F64 d1,%P[value],d1 \n\t" // Add round_to_nearest to value, store result in working register (d1 += value)
+- "vcvt.S32.F64 s3,d1 \n\t" // Truncate(round towards zero) (s3 = (int)d1)
+- "vmov %[result],s3 \n\t" // Store the integer result in a general-purpose register (result = s3)
+- "vcvt.F64.S32 d1,s3 \n\t" // Convert back to floating-point (d1 = (double)s3)
+- "vsub.F64 d1,%P[value],d1 \n\t" // Calculate the error (d1 = value - d1)
+-#if defined(__ARM_PCS_VFP)
+- "fconstd d2,#%G[rnd_val] \n\t" // d2 = 0.5;
+-#else
+- "vmov.F64 d2,%[rnd_val] \n\t"
+-#endif
+- "fcmped d1, d2 \n\t" // (d1 == 0.5?)
+- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags
+- "it eq \n\t"
+- "addeq %[result],#1 \n\t" // (if (d1 == d2) result++;)
+- : [result] "=r"(i) // Outputs
+- : [rnd_val] "Dv" (round_to_nearest), [value] "w"(x) // Inputs
+- : "d1", "d2", "s3" // Clobbers
+- );
+-
+-#elif defined(__SSE2__)
++ const float round_to_nearest = 0.5f;
++ int i;
++#if defined(__SSE2__)
+ const float round_dn_to_nearest = 0.4999999f;
+ i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest));
+
+@@ -150,8 +149,8 @@ namespace MathUtils
+ );
+
+ #endif
+-
+ return i;
++#endif
+ }
+
+ /*! \brief Truncate to nearest integer.
+@@ -165,20 +164,13 @@ namespace MathUtils
+ {
+ assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
+ assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
+- int i;
+
+ #if defined(DISABLE_MATHUTILS_ASM_TRUNCATE_INT)
+- return i = (int)x;
+-
+-#elif defined(__arm__)
+- __asm__ __volatile__ (
+- "vcvt.S32.F64 %[result],%P[value] \n\t" // Truncate(round towards zero) and store the result
+- : [result] "=w"(i) // Outputs
+- : [value] "w"(x) // Inputs
+- );
+- return i;
++ return x;
+
+-#elif defined(TARGET_WINDOWS)
++#else
++ int i;
++#if defined(TARGET_WINDOWS)
+ const float round_towards_m_i = -0.5f;
+ __asm
+ {
+@@ -204,6 +196,7 @@ namespace MathUtils
+ if (x < 0)
+ i = -i;
+ return (i);
++#endif
+ }
+
+ inline int64_t abs(int64_t a)
+--
+1.9.3
+
+
+From 0ad4df440ea225cc951a65bf9553b1f00f416d85 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 11 Dec 2013 17:21:54 +0000
+Subject: [PATCH 19/94] Move the reference-counting of Begin and End calls from
+ DX and GL source files into GUIFontTTF.cpp.
+
+---
+ xbmc/guilib/GUIFontTTF.cpp | 21 ++++++++
+ xbmc/guilib/GUIFontTTF.h | 6 ++-
+ xbmc/guilib/GUIFontTTFDX.cpp | 79 +++++++++++++----------------
+ xbmc/guilib/GUIFontTTFDX.h | 4 +-
+ xbmc/guilib/GUIFontTTFGL.cpp | 118 +++++++++++++++++++------------------------
+ xbmc/guilib/GUIFontTTFGL.h | 4 +-
+ 6 files changed, 117 insertions(+), 115 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 9c8e516..90b9c4a 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -309,6 +309,27 @@ bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float as
+ return true;
+ }
+
++void CGUIFontTTFBase::Begin()
++{
++ if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin())
++ {
++ m_vertex_count = 0;
++ }
++ // Keep track of the nested begin/end calls.
++ m_nestedBeginCount++;
++}
++
++void CGUIFontTTFBase::End()
++{
++ if (m_nestedBeginCount == 0)
++ return;
++
++ if (--m_nestedBeginCount > 0)
++ return;
++
++ LastEnd();
++}
++
+ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling)
+ {
+ Begin();
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index 9723a43..c1c4507 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -77,8 +77,8 @@ class CGUIFontTTFBase
+
+ bool Load(const CStdString& strFilename, float height = 20.0f, float aspect = 1.0f, float lineSpacing = 1.0f, bool border = false);
+
+- virtual void Begin() = 0;
+- virtual void End() = 0;
++ void Begin();
++ void End();
+
+ const CStdString& GetFileName() const { return m_strFileName; };
+
+@@ -169,6 +169,8 @@ class CGUIFontTTFBase
+ CStdString m_strFileName;
+
+ private:
++ virtual bool FirstBegin() = 0;
++ virtual void LastEnd() = 0;
+ CGUIFontTTFBase(const CGUIFontTTFBase&);
+ CGUIFontTTFBase& operator=(const CGUIFontTTFBase&);
+ int m_referenceCount;
+diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp
+index e3eba24..2f90668 100644
+--- a/xbmc/guilib/GUIFontTTFDX.cpp
++++ b/xbmc/guilib/GUIFontTTFDX.cpp
+@@ -51,65 +51,56 @@ CGUIFontTTFDX::~CGUIFontTTFDX(void)
+ free(m_index);
+ }
+
+-void CGUIFontTTFDX::Begin()
++bool CGUIFontTTFDX::FirstBegin()
+ {
+ LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
+
+ if (pD3DDevice == NULL)
++ {
+ CLog::Log(LOGERROR, __FUNCTION__" - failed to get Direct3D device");
++ return false;
++ }
+
+- if (m_nestedBeginCount == 0 && pD3DDevice != NULL && m_texture != NULL)
++ int unit = 0;
++ // just have to blit from our texture.
++ m_texture->BindToUnit(unit);
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
++ unit++;
++
++ if(g_Windowing.UseLimitedColor())
+ {
+- int unit = 0;
+- // just have to blit from our texture.
+- m_texture->BindToUnit(unit);
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP , D3DTOP_ADD );
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ;
++ pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) );
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR );
+ unit++;
+-
+- if(g_Windowing.UseLimitedColor())
+- {
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP , D3DTOP_ADD );
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ;
+- pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) );
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR );
+- unit++;
+- }
+-
+- // no other texture stages needed
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE);
+- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+-
+- pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
+- pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
+- pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
+- pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
+- pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
+- pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
+- pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
+- pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
+-
+- pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
+- m_vertex_count = 0;
+ }
+
+- // Keep track of the nested begin/end calls.
+- m_nestedBeginCount++;
++ // no other texture stages needed
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE);
++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
++
++ pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
++ pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
++ pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
++ pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
++ pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
++ pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
++ pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
++ pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
++
++ pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
++ return true;
+ }
+
+-void CGUIFontTTFDX::End()
++void CGUIFontTTFDX::LastEnd()
+ {
+ LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
+
+- if (m_nestedBeginCount == 0)
+- return;
+-
+- if (--m_nestedBeginCount > 0)
+- return;
+-
+ if (m_vertex_count == 0)
+ return;
+
+diff --git a/xbmc/guilib/GUIFontTTFDX.h b/xbmc/guilib/GUIFontTTFDX.h
+index 0431085..17dfefe 100644
+--- a/xbmc/guilib/GUIFontTTFDX.h
++++ b/xbmc/guilib/GUIFontTTFDX.h
+@@ -41,8 +41,8 @@ class CGUIFontTTFDX : public CGUIFontTTFBase
+ CGUIFontTTFDX(const CStdString& strFileName);
+ virtual ~CGUIFontTTFDX(void);
+
+- virtual void Begin();
+- virtual void End();
++ virtual bool FirstBegin();
++ virtual void LastEnd();
+
+ protected:
+ virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index 3358a5a..93b7ea6 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -50,90 +50,78 @@ CGUIFontTTFGL::~CGUIFontTTFGL(void)
+ {
+ }
+
+-void CGUIFontTTFGL::Begin()
++bool CGUIFontTTFGL::FirstBegin()
+ {
+- if (m_nestedBeginCount == 0 && m_texture != NULL)
++ if (!m_bTextureLoaded)
+ {
+- if (!m_bTextureLoaded)
+- {
+- // Have OpenGL generate a texture object handle for us
+- glGenTextures(1, (GLuint*) &m_nTexture);
++ // Have OpenGL generate a texture object handle for us
++ glGenTextures(1, (GLuint*) &m_nTexture);
+
+- // Bind the texture object
+- glBindTexture(GL_TEXTURE_2D, m_nTexture);
++ // Bind the texture object
++ glBindTexture(GL_TEXTURE_2D, m_nTexture);
+ #ifdef HAS_GL
+- glEnable(GL_TEXTURE_2D);
++ glEnable(GL_TEXTURE_2D);
+ #endif
+- // Set the texture's stretching properties
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++ // Set the texture's stretching properties
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+- // Set the texture image -- THIS WORKS, so the pixels must be wrong.
+- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0,
+- GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels());
++ // Set the texture image -- THIS WORKS, so the pixels must be wrong.
++ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0,
++ GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels());
+
+- VerifyGLState();
+- m_bTextureLoaded = true;
+- }
++ VerifyGLState();
++ m_bTextureLoaded = true;
++ }
+
+- // Turn Blending On
+- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
+- glEnable(GL_BLEND);
++ // Turn Blending On
++ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
++ glEnable(GL_BLEND);
+ #ifdef HAS_GL
+- glEnable(GL_TEXTURE_2D);
++ glEnable(GL_TEXTURE_2D);
+ #endif
+- glBindTexture(GL_TEXTURE_2D, m_nTexture);
++ glBindTexture(GL_TEXTURE_2D, m_nTexture);
+
+ #ifdef HAS_GL
+- glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
+- glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
+- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
+- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0);
+- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
+- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+- VerifyGLState();
++ glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
++ glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
++ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0);
++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
++ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
++ VerifyGLState();
++
++ if(g_Windowing.UseLimitedColor())
++ {
++ glActiveTexture(GL_TEXTURE1);
++ glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind
++ glEnable(GL_TEXTURE_2D);
+
+- if(g_Windowing.UseLimitedColor())
+- {
+- glActiveTexture(GL_TEXTURE1);
+- glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind
+- glEnable(GL_TEXTURE_2D);
+-
+- const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f};
+- glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE);
+- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
+- glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB , GL_ADD);
+- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB , GL_PREVIOUS);
+- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB , GL_CONSTANT);
+- glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB , GL_SRC_COLOR);
+- glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB , GL_SRC_COLOR);
+- glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA , GL_REPLACE);
+- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA , GL_PREVIOUS);
+- VerifyGLState();
+- }
++ const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f};
++ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE);
++ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
++ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB , GL_ADD);
++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB , GL_PREVIOUS);
++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB , GL_CONSTANT);
++ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB , GL_SRC_COLOR);
++ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB , GL_SRC_COLOR);
++ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA , GL_REPLACE);
++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA , GL_PREVIOUS);
++ VerifyGLState();
++ }
+
+ #else
+- g_Windowing.EnableGUIShader(SM_FONTS);
++ g_Windowing.EnableGUIShader(SM_FONTS);
+ #endif
+-
+- m_vertex_count = 0;
+- }
+- // Keep track of the nested begin/end calls.
+- m_nestedBeginCount++;
++ return true;
+ }
+
+-void CGUIFontTTFGL::End()
++void CGUIFontTTFGL::LastEnd()
+ {
+- if (m_nestedBeginCount == 0)
+- return;
+-
+- if (--m_nestedBeginCount > 0)
+- return;
+-
+ #ifdef HAS_GL
+ glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
+
+diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h
+index a0dacba..6736cf7 100644
+--- a/xbmc/guilib/GUIFontTTFGL.h
++++ b/xbmc/guilib/GUIFontTTFGL.h
+@@ -41,8 +41,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
+ CGUIFontTTFGL(const CStdString& strFileName);
+ virtual ~CGUIFontTTFGL(void);
+
+- virtual void Begin();
+- virtual void End();
++ virtual bool FirstBegin();
++ virtual void LastEnd();
+
+ protected:
+ virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);
+--
+1.9.3
+
+
+From 427373ae5bb96489afb22529714c16e8f82c2195 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 11 Dec 2013 18:47:54 +0000
+Subject: [PATCH 20/94] Convert CGUIFontTTFBase::m_vertex to be managed as a
+ std::vector. Also retired CGUIFontTTFBase::m_vertex_count and
+ CGUIFontTTFBase::m_vertex_size because these can be derived from vector
+ member functions.
+
+---
+ xbmc/guilib/GUIFontTTF.cpp | 29 +++++------------------------
+ xbmc/guilib/GUIFontTTF.h | 4 +---
+ xbmc/guilib/GUIFontTTFDX.cpp | 12 ++++++------
+ xbmc/guilib/GUIFontTTFGL.cpp | 12 ++++++------
+ 4 files changed, 18 insertions(+), 39 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 90b9c4a..3f219d9 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -139,8 +139,7 @@ CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
+ m_nestedBeginCount = 0;
+
+ m_bTextureLoaded = false;
+- m_vertex_size = 4*1024;
+- m_vertex = (SVertex*)malloc(m_vertex_size * sizeof(SVertex));
++ m_vertex.reserve(4*1024);
+
+ m_face = NULL;
+ m_stroker = NULL;
+@@ -155,7 +154,6 @@ CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
+ m_textureScaleX = m_textureScaleY = 0.0;
+ m_ellipsesWidth = m_height = 0.0f;
+ m_color = 0;
+- m_vertex_count = 0;
+ m_nTexture = 0;
+ }
+
+@@ -216,9 +214,7 @@ void CGUIFontTTFBase::Clear()
+ g_freeTypeLibrary.ReleaseStroker(m_stroker);
+ m_stroker = NULL;
+
+- free(m_vertex);
+- m_vertex = NULL;
+- m_vertex_count = 0;
++ m_vertex.clear();
+ }
+
+ bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float aspect, float lineSpacing, bool border)
+@@ -313,7 +309,7 @@ void CGUIFontTTFBase::Begin()
+ {
+ if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin())
+ {
+- m_vertex_count = 0;
++ m_vertex.clear();
+ }
+ // Keep track of the nested begin/end calls.
+ m_nestedBeginCount++;
+@@ -746,22 +742,9 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
+ float tt = texture.y1 * m_textureScaleY;
+ float tb = texture.y2 * m_textureScaleY;
+
+- // grow the vertex buffer if required
+- if(m_vertex_count >= m_vertex_size)
+- {
+- m_vertex_size *= 2;
+- void* old = m_vertex;
+- m_vertex = (SVertex*)realloc(m_vertex, m_vertex_size * sizeof(SVertex));
+- if (!m_vertex)
+- {
+- free(old);
+- CLog::Log(LOGSEVERE, "%s: can't allocate %"PRIdS" bytes for texture", __FUNCTION__ , m_vertex_size * sizeof(SVertex));
+- return;
+- }
+- }
+-
++ m_vertex.resize(m_vertex.size() + 4);
++ SVertex* v = &m_vertex[m_vertex.size() - 4];
+ m_color = color;
+- SVertex* v = m_vertex + m_vertex_count;
+
+ unsigned char r = GET_R(color)
+ , g = GET_G(color)
+@@ -828,8 +811,6 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
+ v[3].y = y[2];
+ v[3].z = z[2];
+ #endif
+-
+- m_vertex_count+=4;
+ }
+
+ // Oblique code - original taken from freetype2 (ftsynth.c)
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index c1c4507..35e3cf9 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -157,9 +157,7 @@ class CGUIFontTTFBase
+ bool m_bTextureLoaded;
+ unsigned int m_nTexture;
+
+- SVertex* m_vertex;
+- int m_vertex_count;
+- int m_vertex_size;
++ std::vector<SVertex> m_vertex;
+
+ float m_textureScaleX;
+ float m_textureScaleY;
+diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp
+index 2f90668..6ef8984 100644
+--- a/xbmc/guilib/GUIFontTTFDX.cpp
++++ b/xbmc/guilib/GUIFontTTFDX.cpp
+@@ -101,17 +101,17 @@ void CGUIFontTTFDX::LastEnd()
+ {
+ LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
+
+- if (m_vertex_count == 0)
++ if (m_vertex.size() == 0)
+ return;
+
+- unsigned index_size = m_vertex_size * 6 / 4;
++ unsigned index_size = m_vertex.capacity() * 6 / 4;
+ if(m_index_size < index_size)
+ {
+ uint16_t* id = (uint16_t*)calloc(index_size, sizeof(uint16_t));
+ if(id == NULL)
+ return;
+
+- for(int i = 0, b = 0; i < m_vertex_size; i += 4, b += 6)
++ for(int i = 0, b = 0; i < m_vertex.capacity(); i += 4, b += 6)
+ {
+ id[b+0] = i + 0;
+ id[b+1] = i + 1;
+@@ -140,11 +140,11 @@ void CGUIFontTTFDX::LastEnd()
+
+ pD3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST
+ , 0
+- , m_vertex_count
+- , m_vertex_count / 2
++ , m_vertex.size()
++ , m_vertex.size() / 2
+ , m_index
+ , D3DFMT_INDEX16
+- , m_vertex
++ , &m_vertex[0]
+ , sizeof(SVertex));
+ pD3DDevice->SetTransform(D3DTS_WORLD, &orig);
+
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index 93b7ea6..a4e8571 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -125,13 +125,13 @@ void CGUIFontTTFGL::LastEnd()
+ #ifdef HAS_GL
+ glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
+
+- glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, r));
+- glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, x));
+- glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, u));
++ glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, r));
++ glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, x));
++ glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, u));
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+- glDrawArrays(GL_QUADS, 0, m_vertex_count);
++ glDrawArrays(GL_QUADS, 0, m_vertex.size());
+ glPopClientAttrib();
+
+ glActiveTexture(GL_TEXTURE1);
+@@ -147,10 +147,10 @@ void CGUIFontTTFGL::LastEnd()
+ GLint tex0Loc = g_Windowing.GUIShaderGetCoord0();
+
+ // stack object until VBOs will be used
+- std::vector<SVertex> vecVertices( 6 * (m_vertex_count / 4) );
++ std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
+ SVertex *vertices = &vecVertices[0];
+
+- for (int i=0; i<m_vertex_count; i+=4)
++ for (size_t i=0; i<m_vertex.size(); i+=4)
+ {
+ *vertices++ = m_vertex[i];
+ *vertices++ = m_vertex[i+1];
+--
+1.9.3
+
+
+From a3e8e7f1055726a050e04d1e4ab61a5355cdbd6a Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Mon, 16 Dec 2013 18:58:12 +0000
+Subject: [PATCH 21/94] CGUIFontTTFBase::RenderCharacter can now append to
+ arbitrary vectors of vertices rather than only CGUIFontTTFBase::m_vertex
+
+---
+ xbmc/guilib/GUIFontTTF.cpp | 12 +++++++-----
+ xbmc/guilib/GUIFontTTF.h | 2 +-
+ 2 files changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 3f219d9..1aaf68b 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -330,6 +330,8 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ {
+ Begin();
+
++ std::vector<SVertex> &vertices = m_vertex;
++
+ // save the origin, which is scaled separately
+ m_originX = x;
+ m_originY = y;
+@@ -410,7 +412,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+
+ for (int i = 0; i < 3; i++)
+ {
+- RenderCharacter(startX + cursorX, startY, period, color, !scrolling);
++ RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices);
+ cursorX += period->advance;
+ }
+ break;
+@@ -419,7 +421,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ else if (maxPixelWidth > 0 && cursorX > maxPixelWidth)
+ break; // exceeded max allowed width - stop rendering
+
+- RenderCharacter(startX + cursorX, startY, ch, color, !scrolling);
++ RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices);
+ if ( alignment & XBFONT_JUSTIFIED )
+ {
+ if ((*pos & 0xffff) == L' ')
+@@ -676,7 +678,7 @@ bool CGUIFontTTFBase::CacheCharacter(wchar_t letter, uint32_t style, Character *
+ return true;
+ }
+
+-void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX)
++void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector<SVertex> &vertices)
+ {
+ // actual image width isn't same as the character width as that is
+ // just baseline width and height should include the descent
+@@ -742,8 +744,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
+ float tt = texture.y1 * m_textureScaleY;
+ float tb = texture.y2 * m_textureScaleY;
+
+- m_vertex.resize(m_vertex.size() + 4);
+- SVertex* v = &m_vertex[m_vertex.size() - 4];
++ vertices.resize(vertices.size() + 4);
++ SVertex* v = &vertices[vertices.size() - 4];
+ m_color = color;
+
+ unsigned char r = GET_R(color)
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index 35e3cf9..4a6a696 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -109,7 +109,7 @@ class CGUIFontTTFBase
+ // Stuff for pre-rendering for speed
+ inline Character *GetCharacter(character_t letter);
+ bool CacheCharacter(wchar_t letter, uint32_t style, Character *ch);
+- void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX);
++ void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector<SVertex> &vertices);
+ void ClearCharacterCache();
+
+ virtual CBaseTexture* ReallocTexture(unsigned int& newHeight) = 0;
+--
+1.9.3
+
+
+From 79263f02e56ef10410984c98a844aaa9bf43199e Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 15 Jan 2014 17:18:38 +0000
+Subject: [PATCH 22/94] Add a cache of font glyph bounding box vertices. This
+ is implemented as a template because ultimately we will key on different
+ parameters and store values of different types, depending upon whether we
+ have a GLES or non-GLES backend, and for GLES, whether or not the currently
+ applicable transformation matrices permit the use of hardware clipping.
+
+---
+ xbmc/guilib/GUIFontCache.cpp | 105 ++++++++++++++++++++
+ xbmc/guilib/GUIFontCache.h | 217 ++++++++++++++++++++++++++++++++++++++++++
+ xbmc/guilib/GUIFontTTF.cpp | 181 +++++++++++++++++++----------------
+ xbmc/guilib/GUIFontTTF.h | 5 +
+ xbmc/guilib/GUIFontTTFGL.cpp | 1 +
+ xbmc/guilib/GraphicContext.h | 1 +
+ xbmc/guilib/Makefile.in | 1 +
+ xbmc/guilib/TransformMatrix.h | 11 +++
+ 8 files changed, 438 insertions(+), 84 deletions(-)
+ create mode 100644 xbmc/guilib/GUIFontCache.cpp
+ create mode 100644 xbmc/guilib/GUIFontCache.h
+
+diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
+new file mode 100644
+index 0000000..c029713
+--- /dev/null
++++ b/xbmc/guilib/GUIFontCache.cpp
+@@ -0,0 +1,105 @@
++/*
++ * Copyright (C) 2005-2013 Team XBMC
++ * http://xbmc.org
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with XBMC; see the file COPYING. If not, see
++ * <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <stdint.h>
++#include <vector>
++#include "utils/StdString.h" // required by GUIFontTTF.h
++#include "GUIFontTTF.h"
++#include "GraphicContext.h"
++
++template<class Position, class Value>
++void CGUIFontCacheEntry<Position, Value>::Reassign::operator()(CGUIFontCacheEntry<Position, Value> &entry)
++{
++ entry.m_key.m_pos = m_key.m_pos;
++ entry.m_key.m_colors.assign(m_key.m_colors.begin(), m_key.m_colors.end());
++ entry.m_key.m_text.assign(m_key.m_text.begin(), m_key.m_text.end());
++ entry.m_key.m_alignment = m_key.m_alignment;
++ entry.m_key.m_maxPixelWidth = m_key.m_maxPixelWidth;
++ entry.m_key.m_scrolling = m_key.m_scrolling;
++ entry.m_matrix = m_key.m_matrix;
++ entry.m_key.m_scaleX = m_key.m_scaleX;
++ entry.m_key.m_scaleY = m_key.m_scaleY;
++
++ entry.m_lastUsedMillis = m_nowMillis;
++ entry.m_value.clear();
++}
++
++template<class Position, class Value>
++CGUIFontCacheEntry<Position, Value>::~CGUIFontCacheEntry()
++{
++ delete &m_key.m_colors;
++ delete &m_key.m_text;
++ m_value.clear();
++}
++
++template<class Position, class Value>
++Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
++ const vecColors &colors, const vecText &text,
++ uint32_t alignment, float maxPixelWidth,
++ bool scrolling,
++ unsigned int nowMillis, bool &dirtyCache)
++{
++ const CGUIFontCacheKey<Position> key(pos,
++ const_cast<vecColors &>(colors), const_cast<vecText &>(text),
++ alignment, maxPixelWidth,
++ scrolling, g_graphicsContext.GetGUIMatrix(),
++ g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY());
++ EntryHashIterator i = m_list.get<Hash>().find(key);
++ if (i == m_list.get<Hash>().end())
++ {
++ /* Cache miss */
++ EntryAgeIterator oldest = m_list.get<Age>().begin();
++ if (!m_list.get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT)
++ {
++ /* The oldest existing entry is old enough to expire and reuse */
++ m_list.get<Hash>().modify(m_list.project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis));
++ m_list.get<Age>().relocate(m_list.get<Age>().end(), oldest);
++ }
++ else
++ {
++ /* We need a new entry instead */
++ /* Yes, this causes the creation an destruction of a temporary entry, but
++ * this code ought to only be used infrequently, when the cache needs to grow */
++ m_list.get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis));
++ }
++ dirtyCache = true;
++ return (--m_list.get<Age>().end())->m_value;
++ }
++ else
++ {
++ /* Cache hit */
++ /* Update time in entry and move to the back of the list */
++ i->m_lastUsedMillis = nowMillis;
++ m_list.get<Age>().relocate(m_list.get<Age>().end(), m_list.project<Age>(i));
++ dirtyCache = false;
++ return i->m_value;
++ }
++}
++
++template<class Position, class Value>
++void CGUIFontCache<Position, Value>::Flush()
++{
++ m_list.get<Age>().clear();
++}
++
++template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> &entry);
++template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry();
++template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
++template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush();
+diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h
+new file mode 100644
+index 0000000..ef65845
+--- /dev/null
++++ b/xbmc/guilib/GUIFontCache.h
+@@ -0,0 +1,217 @@
++/*!
++\file GUIFontCache.h
++\brief
++*/
++
++#ifndef CGUILIB_GUIFONTCACHE_H
++#define CGUILIB_GUIFONTCACHE_H
++#pragma once
++
++/*
++ * Copyright (C) 2005-2013 Team XBMC
++ * http://xbmc.org
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with XBMC; see the file COPYING. If not, see
++ * <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <cstddef>
++#include <cstring>
++#include <stdint.h>
++
++#include <algorithm>
++#include <vector>
++
++#include "boost/multi_index_container.hpp"
++#include "boost/multi_index/sequenced_index.hpp"
++#include "boost/multi_index/hashed_index.hpp"
++#include "boost/multi_index/member.hpp"
++
++#include "TransformMatrix.h"
++
++using namespace boost::multi_index;
++
++#define FONT_CACHE_TIME_LIMIT (1000)
++
++template<class Position, class Value> class CGUIFontCache;
++class CGUIFontTTFBase;
++
++template<class Position>
++struct CGUIFontCacheKey
++{
++ Position m_pos;
++ vecColors &m_colors;
++ vecText &m_text;
++ uint32_t m_alignment;
++ float m_maxPixelWidth;
++ bool m_scrolling;
++ const TransformMatrix &m_matrix;
++ float m_scaleX;
++ float m_scaleY;
++
++ CGUIFontCacheKey(Position pos,
++ vecColors &colors, vecText &text,
++ uint32_t alignment, float maxPixelWidth,
++ bool scrolling, const TransformMatrix &matrix,
++ float scaleX, float scaleY) :
++ m_pos(pos),
++ m_colors(colors), m_text(text),
++ m_alignment(alignment), m_maxPixelWidth(maxPixelWidth),
++ m_scrolling(scrolling), m_matrix(matrix),
++ m_scaleX(scaleX), m_scaleY(scaleY)
++ {}
++};
++
++template<class Position, class Value>
++struct CGUIFontCacheEntry
++{
++ const CGUIFontCache<Position, Value> &m_cache;
++ CGUIFontCacheKey<Position> m_key;
++ TransformMatrix m_matrix;
++
++ /* These need to be declared as mutable to get round the fact that only
++ * const iterators are available. These fields do not affect comparison or
++ * hash functors, so from the container's point of view, they are mutable. */
++ mutable unsigned int m_lastUsedMillis;
++ mutable Value m_value;
++
++ CGUIFontCacheEntry(const CGUIFontCache<Position, Value> &cache, const CGUIFontCacheKey<Position> &key, unsigned int nowMillis) :
++ m_cache(cache),
++ m_key(key.m_pos,
++ *new vecColors, *new vecText,
++ key.m_alignment, key.m_maxPixelWidth,
++ key.m_scrolling, m_matrix,
++ key.m_scaleX, key.m_scaleY),
++ m_lastUsedMillis(nowMillis)
++ {
++ m_key.m_colors.assign(key.m_colors.begin(), key.m_colors.end());
++ m_key.m_text.assign(key.m_text.begin(), key.m_text.end());
++ m_matrix = key.m_matrix;
++ }
++
++ CGUIFontCacheEntry(const CGUIFontCacheEntry &other) :
++ m_cache(other.m_cache),
++ m_key(other.m_key.m_pos,
++ *new vecColors, *new vecText,
++ other.m_key.m_alignment, other.m_key.m_maxPixelWidth,
++ other.m_key.m_scrolling, m_matrix,
++ other.m_key.m_scaleX, other.m_key.m_scaleY),
++ m_lastUsedMillis(other.m_lastUsedMillis),
++ m_value(other.m_value)
++ {
++ m_key.m_colors.assign(other.m_key.m_colors.begin(), other.m_key.m_colors.end());
++ m_key.m_text.assign(other.m_key.m_text.begin(), other.m_key.m_text.end());
++ m_matrix = other.m_key.m_matrix;
++ }
++
++ struct Reassign
++ {
++ Reassign(const CGUIFontCacheKey<Position> &key, unsigned int nowMillis) : m_key(key), m_nowMillis(nowMillis) {}
++ void operator()(CGUIFontCacheEntry &entry);
++ private:
++ const CGUIFontCacheKey<Position> &m_key;
++ unsigned int m_nowMillis;
++ };
++
++ ~CGUIFontCacheEntry();
++};
++
++template<class Position>
++struct CGUIFontCacheHash
++{
++ size_t operator()(const CGUIFontCacheKey<Position> &key) const
++ {
++ /* Not much effort has gone into choosing this hash function */
++ size_t hash = 0, i;
++ for (i = 0; i < 3 && i < key.m_text.size(); ++i)
++ hash += key.m_text[i];
++ if (key.m_colors.size())
++ hash += key.m_colors[0];
++ hash += MatrixHashContribution(key);
++ return hash;
++ }
++};
++
++template<class Position>
++struct CGUIFontCacheKeysMatch
++{
++ bool operator()(const CGUIFontCacheKey<Position> &a, const CGUIFontCacheKey<Position> &b) const
++ {
++ return a.m_text == b.m_text &&
++ a.m_colors == b.m_colors &&
++ a.m_alignment == b.m_alignment &&
++ a.m_scrolling == b.m_scrolling &&
++ a.m_maxPixelWidth == b.m_maxPixelWidth &&
++ Match(a.m_pos, a.m_matrix, b.m_pos, b.m_matrix, a.m_scrolling) &&
++ a.m_scaleX == b.m_scaleX &&
++ a.m_scaleY == b.m_scaleY;
++ }
++};
++
++template<class Position, class Value>
++class CGUIFontCache
++{
++ /* Empty structs used as tags to identify indexes */
++ struct Age {};
++ struct Hash {};
++
++ typedef multi_index_container<
++ CGUIFontCacheEntry<Position, Value>,
++ indexed_by<
++ sequenced<tag<Age> >,
++ hashed_unique<tag<Hash>, member<CGUIFontCacheEntry<Position, Value>, CGUIFontCacheKey<Position>, &CGUIFontCacheEntry<Position, Value>::m_key>, CGUIFontCacheHash<Position>, CGUIFontCacheKeysMatch<Position> >
++ >
++ > EntryList;
++
++ typedef typename EntryList::template index<Age>::type::iterator EntryAgeIterator;
++ typedef typename EntryList::template index<Hash>::type::iterator EntryHashIterator;
++
++ EntryList m_list;
++
++public:
++ const CGUIFontTTFBase &m_font;
++
++ CGUIFontCache(CGUIFontTTFBase &font) : m_font(font) {}
++ Value &Lookup(Position &pos,
++ const vecColors &colors, const vecText &text,
++ uint32_t alignment, float maxPixelWidth,
++ bool scrolling,
++ unsigned int nowMillis, bool &dirtyCache);
++ void Flush();
++};
++
++struct CGUIFontCacheStaticPosition
++{
++ float m_x;
++ float m_y;
++ CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {}
++};
++
++typedef std::vector<SVertex> CGUIFontCacheStaticValue;
++
++inline bool Match(const CGUIFontCacheStaticPosition &a, const TransformMatrix &a_m,
++ const CGUIFontCacheStaticPosition &b, const TransformMatrix &b_m,
++ bool scrolling)
++{
++ return a.m_x == b.m_x && a.m_y == b.m_y && a_m == b_m;
++}
++
++inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPosition> &a)
++{
++ /* Ensure horizontally translated versions end up in different buckets */
++ return a.m_matrix.m[0][3];
++}
++
++#endif
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 1aaf68b..288e61a 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -27,6 +27,7 @@
+ #include "utils/MathUtils.h"
+ #include "utils/log.h"
+ #include "windowing/WindowingFactory.h"
++#include "threads/SystemClock.h"
+
+ #include <math.h>
+
+@@ -131,7 +132,7 @@ class CFreeTypeLibrary
+ XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library
+ #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary)
+
+-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
++CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this)
+ {
+ m_texture = NULL;
+ m_char = NULL;
+@@ -330,108 +331,120 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ {
+ Begin();
+
+- std::vector<SVertex> &vertices = m_vertex;
+-
+- // save the origin, which is scaled separately
+- m_originX = x;
+- m_originY = y;
+-
+- // Check if we will really need to truncate or justify the text
+- if ( alignment & XBFONT_TRUNCATED )
++ bool dirtyCache;
++ CGUIFontCacheStaticPosition staticPos(x, y);
++ std::vector<SVertex> &vertices = m_staticCache.Lookup(staticPos,
++ colors, text,
++ alignment, maxPixelWidth,
++ scrolling,
++ XbmcThreads::SystemClockMillis(),
++ dirtyCache);
++ if (dirtyCache)
+ {
+- if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth)
+- alignment &= ~XBFONT_TRUNCATED;
+- }
+- else if ( alignment & XBFONT_JUSTIFIED )
+- {
+- if ( maxPixelWidth <= 0.0f )
+- alignment &= ~XBFONT_JUSTIFIED;
+- }
++ // save the origin, which is scaled separately
++ m_originX = x;
++ m_originY = y;
+
+- // calculate sizing information
+- float startX = 0;
+- float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering
++ // Check if we will really need to truncate or justify the text
++ if ( alignment & XBFONT_TRUNCATED )
++ {
++ if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth)
++ alignment &= ~XBFONT_TRUNCATED;
++ }
++ else if ( alignment & XBFONT_JUSTIFIED )
++ {
++ if ( maxPixelWidth <= 0.0f )
++ alignment &= ~XBFONT_JUSTIFIED;
++ }
+
+- if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) )
+- {
+- // Get the extent of this line
+- float w = GetTextWidthInternal( text.begin(), text.end() );
++ // calculate sizing information
++ float startX = 0;
++ float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering
+
+- if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues
+- w = maxPixelWidth;
++ if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) )
++ {
++ // Get the extent of this line
++ float w = GetTextWidthInternal( text.begin(), text.end() );
+
+- if ( alignment & XBFONT_CENTER_X)
+- w *= 0.5f;
+- // Offset this line's starting position
+- startX -= w;
+- }
++ if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues
++ w = maxPixelWidth;
+
+- float spacePerLetter = 0; // for justification effects
+- if ( alignment & XBFONT_JUSTIFIED )
+- {
+- // first compute the size of the text to render in both characters and pixels
+- unsigned int lineChars = 0;
+- float linePixels = 0;
+- for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
++ if ( alignment & XBFONT_CENTER_X)
++ w *= 0.5f;
++ // Offset this line's starting position
++ startX -= w;
++ }
++
++ float spacePerLetter = 0; // for justification effects
++ if ( alignment & XBFONT_JUSTIFIED )
+ {
+- Character *ch = GetCharacter(*pos);
+- if (ch)
+- { // spaces have multiple times the justification spacing of normal letters
+- lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1;
+- linePixels += ch->advance;
++ // first compute the size of the text to render in both characters and pixels
++ unsigned int lineChars = 0;
++ float linePixels = 0;
++ for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
++ {
++ Character *ch = GetCharacter(*pos);
++ if (ch)
++ { // spaces have multiple times the justification spacing of normal letters
++ lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1;
++ linePixels += ch->advance;
++ }
+ }
++ if (lineChars > 1)
++ spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1);
+ }
+- if (lineChars > 1)
+- spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1);
+- }
+- float cursorX = 0; // current position along the line
+-
+- for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
+- {
+- // If starting text on a new line, determine justification effects
+- // Get the current letter in the CStdString
+- color_t color = (*pos & 0xff0000) >> 16;
+- if (color >= colors.size())
+- color = 0;
+- color = colors[color];
++ float cursorX = 0; // current position along the line
+
+- // grab the next character
+- Character *ch = GetCharacter(*pos);
+- if (!ch) continue;
+-
+- if ( alignment & XBFONT_TRUNCATED )
++ for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
+ {
+- // Check if we will be exceeded the max allowed width
+- if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth )
+- {
+- // Yup. Let's draw the ellipses, then bail
+- // Perhaps we should really bail to the next line in this case??
+- Character *period = GetCharacter(L'.');
+- if (!period)
+- break;
++ // If starting text on a new line, determine justification effects
++ // Get the current letter in the CStdString
++ color_t color = (*pos & 0xff0000) >> 16;
++ if (color >= colors.size())
++ color = 0;
++ color = colors[color];
++
++ // grab the next character
++ Character *ch = GetCharacter(*pos);
++ if (!ch) continue;
+
+- for (int i = 0; i < 3; i++)
++ if ( alignment & XBFONT_TRUNCATED )
++ {
++ // Check if we will be exceeded the max allowed width
++ if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth )
+ {
+- RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices);
+- cursorX += period->advance;
++ // Yup. Let's draw the ellipses, then bail
++ // Perhaps we should really bail to the next line in this case??
++ Character *period = GetCharacter(L'.');
++ if (!period)
++ break;
++
++ for (int i = 0; i < 3; i++)
++ {
++ RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices);
++ cursorX += period->advance;
++ }
++ break;
+ }
+- break;
+ }
+- }
+- else if (maxPixelWidth > 0 && cursorX > maxPixelWidth)
+- break; // exceeded max allowed width - stop rendering
++ else if (maxPixelWidth > 0 && cursorX > maxPixelWidth)
++ break; // exceeded max allowed width - stop rendering
+
+- RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices);
+- if ( alignment & XBFONT_JUSTIFIED )
+- {
+- if ((*pos & 0xffff) == L' ')
+- cursorX += ch->advance + spacePerLetter * justification_word_weight;
++ RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices);
++ if ( alignment & XBFONT_JUSTIFIED )
++ {
++ if ((*pos & 0xffff) == L' ')
++ cursorX += ch->advance + spacePerLetter * justification_word_weight;
++ else
++ cursorX += ch->advance + spacePerLetter;
++ }
+ else
+- cursorX += ch->advance + spacePerLetter;
++ cursorX += ch->advance;
+ }
+- else
+- cursorX += ch->advance;
+ }
++ /* Append the new vertices (from the cache or otherwise) to the set collected
++ * since the first Begin() call */
++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+
+ End();
+ }
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index 4a6a696..7cb4669 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -64,6 +64,9 @@ struct SVertex
+ };
+
+
++#include "GUIFontCache.h"
++
++
+ class CGUIFontTTFBase
+ {
+ friend class CGUIFont;
+@@ -166,6 +169,8 @@ class CGUIFontTTFBase
+
+ CStdString m_strFileName;
+
++ CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> m_staticCache;
++
+ private:
+ virtual bool FirstBegin() = 0;
+ virtual void LastEnd() = 0;
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index a4e8571..cb56987 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -200,6 +200,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
+ m_textureScaleX = 1.0f / m_textureWidth;
+ if (m_textureHeight < newHeight)
+ CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight);
++ m_staticCache.Flush();
+
+ memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch());
+ if (m_texture)
+diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
+index 6c2dcd4..bab2457 100644
+--- a/xbmc/guilib/GraphicContext.h
++++ b/xbmc/guilib/GraphicContext.h
+@@ -146,6 +146,7 @@ class CGraphicContext : public CCriticalSection,
+ inline void ScaleFinalCoords(float &x, float &y, float &z) const XBMC_FORCE_INLINE { m_finalTransform.matrix.TransformPosition(x, y, z); }
+ bool RectIsAngled(float x1, float y1, float x2, float y2) const;
+
++ inline const TransformMatrix &GetGUIMatrix() const XBMC_FORCE_INLINE { return m_finalTransform.matrix; }
+ inline float GetGUIScaleX() const XBMC_FORCE_INLINE { return m_finalTransform.scaleX; }
+ inline float GetGUIScaleY() const XBMC_FORCE_INLINE { return m_finalTransform.scaleY; }
+ inline color_t MergeAlpha(color_t color) const XBMC_FORCE_INLINE
+diff --git a/xbmc/guilib/Makefile.in b/xbmc/guilib/Makefile.in
+index 086fb0d..af82979 100644
+--- a/xbmc/guilib/Makefile.in
++++ b/xbmc/guilib/Makefile.in
+@@ -23,6 +23,7 @@ SRCS += GUIEditControl.cpp
+ SRCS += GUIFadeLabelControl.cpp
+ SRCS += GUIFixedListContainer.cpp
+ SRCS += GUIFont.cpp
++SRCS += GUIFontCache.cpp
+ SRCS += GUIFontManager.cpp
+ SRCS += GUIFontTTF.cpp
+ SRCS += GUIImage.cpp
+diff --git a/xbmc/guilib/TransformMatrix.h b/xbmc/guilib/TransformMatrix.h
+index f351c99..9036ba9 100644
+--- a/xbmc/guilib/TransformMatrix.h
++++ b/xbmc/guilib/TransformMatrix.h
+@@ -245,3 +245,14 @@ class TransformMatrix
+ float alpha;
+ bool identity;
+ };
++
++inline bool operator==(const TransformMatrix &a, const TransformMatrix &b)
++{
++ return a.alpha == b.alpha && ((a.identity && b.identity) ||
++ (!a.identity && !b.identity && std::equal(&a.m[0][0], &a.m[0][0] + sizeof a.m / sizeof a.m[0][0], &b.m[0][0])));
++}
++
++inline bool operator!=(const TransformMatrix &a, const TransformMatrix &b)
++{
++ return !operator==(a, b);
++}
+--
+1.9.3
+
+
+From 65d2b7f112b400f75140de44579e6cdf98378b67 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Thu, 23 Jan 2014 22:24:17 +0000
+Subject: [PATCH 23/94] Lay the groundwork for hardware clipping.
+
+For glScissor() to replace CGraphicContext::ClipRect, a necessary condition
+is that no shear or rotation is introduced between the coordinate systems
+they use; this depends upon the settings of the GUI matrix m_finalTransform
+as well as the OpenGL model-view and projection matrices. These all remain
+unchanged between paired calls of CGUIShader::OnEnabled and
+CGUIShader::OnDisabled, so we scan the matrices in CGUIShader::OnEnabled to
+see whether hardware clipping is possible.
+
+Then, in CGUIFontTTFBase::RenderCharacter, we don't apply software clipping
+in such cases. However, because vertices arising from multiple
+CGUIFontTTFBase::DrawTextInternal calls (each of which often uses a different
+clip rectangle) get lumped into the same vector, which only at the end is
+passed to OpenGL for rendering, we need to wait a few commits before we can
+actually apply hardware clipping. In the meantime, expect to see rendering
+errors.
+---
+ xbmc/guilib/GUIFontTTF.cpp | 3 +-
+ xbmc/guilib/GUIShader.cpp | 80 +++++++++++++++++++++++++++++++-
+ xbmc/guilib/GUIShader.h | 11 +++++
+ xbmc/guilib/GraphicContext.cpp | 10 ++++
+ xbmc/guilib/GraphicContext.h | 1 +
+ xbmc/rendering/RenderSystem.h | 2 +
+ xbmc/rendering/gles/RenderSystemGLES.cpp | 22 +++++++++
+ xbmc/rendering/gles/RenderSystemGLES.h | 2 +
+ 8 files changed, 128 insertions(+), 3 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 288e61a..19c7ff4 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -710,7 +710,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
+ (posY + ch->offsetY + height) * g_graphicsContext.GetGUIScaleY());
+ vertex += CPoint(m_originX, m_originY);
+ CRect texture(ch->left, ch->top, ch->right, ch->bottom);
+- g_graphicsContext.ClipRect(vertex, texture);
++ if (!g_Windowing.ScissorsCanEffectClipping())
++ g_graphicsContext.ClipRect(vertex, texture);
+
+ // transform our positions - note, no scaling due to GUI calibration/resolution occurs
+ float x[4], y[4], z[4];
+diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp
+index 11089b8..53bce09 100644
+--- a/xbmc/guilib/GUIShader.cpp
++++ b/xbmc/guilib/GUIShader.cpp
+@@ -26,6 +26,8 @@
+ #include "GUIShader.h"
+ #include "MatrixGLES.h"
+ #include "utils/log.h"
++#include "windowing/egl/WinSystemEGL.h"
++#include "guilib/GraphicContext.h"
+
+ CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_vert.glsl", shader)
+ {
+@@ -86,8 +88,82 @@ bool CGUIShader::OnEnabled()
+ {
+ // This is called after glUseProgram()
+
+- glUniformMatrix4fv(m_hProj, 1, GL_FALSE, g_matrices.GetMatrix(MM_PROJECTION));
+- glUniformMatrix4fv(m_hModel, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
++ GLfloat *projMatrix = g_matrices.GetMatrix(MM_PROJECTION);
++ GLfloat *modelMatrix = g_matrices.GetMatrix(MM_MODELVIEW);
++ glUniformMatrix4fv(m_hProj, 1, GL_FALSE, projMatrix);
++ glUniformMatrix4fv(m_hModel, 1, GL_FALSE, modelMatrix);
++
++ const TransformMatrix &guiMatrix = g_graphicsContext.GetGUIMatrix();
++ CRect viewPort; // absolute positions of corners
++ g_Windowing.GetViewPort(viewPort);
++
++ /* glScissor operates in window coordinates. In order that we can use it to
++ * perform clipping, we must ensure that there is an independent linear
++ * transformation from the coordinate system used by CGraphicContext::ClipRect
++ * to window coordinates, separately for X and Y (in other words, no
++ * rotation or shear is introduced at any stage). To do, this, we need to
++ * check that zeros are present in the following locations:
++ *
++ * GUI matrix:
++ * / * 0 * * \
++ * | 0 * * * |
++ * \ 0 0 * * /
++ * ^ TransformMatrix::TransformX/Y/ZCoord are only ever called with
++ * input z = 0, so this column doesn't matter
++ * Model-view matrix:
++ * / * 0 0 * \
++ * | 0 * 0 * |
++ * | 0 0 * * |
++ * \ * * * * / <- eye w has no influence on window x/y (last column below
++ * is either 0 or ignored)
++ * Projection matrix:
++ * / * 0 0 0 \
++ * | 0 * 0 0 |
++ * | * * * * | <- normalised device coordinate z has no influence on window x/y
++ * \ 0 0 * 0 /
++ *
++ * Some of these zeros are not strictly required to ensure this, but they tend
++ * to be zeroed in the common case, so by checking for zeros here, we simplify
++ * the calculation of the window x/y coordinates further down the line.
++ *
++ * (Minor detail: we don't quite deal in window coordinates as defined by
++ * OpenGL, because CRenderSystemGLES::SetScissors flips the Y axis. But all
++ * that's needed to handle that is an effective negation at the stage where
++ * Y is in normalised device coordinates.)
++ */
++ m_clipPossible = guiMatrix.m[0][1] == 0 &&
++ guiMatrix.m[1][0] == 0 &&
++ guiMatrix.m[2][0] == 0 &&
++ guiMatrix.m[2][1] == 0 &&
++ modelMatrix[0+1*4] == 0 &&
++ modelMatrix[0+2*4] == 0 &&
++ modelMatrix[1+0*4] == 0 &&
++ modelMatrix[1+2*4] == 0 &&
++ modelMatrix[2+0*4] == 0 &&
++ modelMatrix[2+1*4] == 0 &&
++ projMatrix[0+1*4] == 0 &&
++ projMatrix[0+2*4] == 0 &&
++ projMatrix[0+3*4] == 0 &&
++ projMatrix[1+0*4] == 0 &&
++ projMatrix[1+2*4] == 0 &&
++ projMatrix[1+3*4] == 0 &&
++ projMatrix[3+0*4] == 0 &&
++ projMatrix[3+1*4] == 0 &&
++ projMatrix[3+3*4] == 0;
++ if (m_clipPossible)
++ {
++ m_clipXFactor = guiMatrix.m[0][0] * modelMatrix[0+0*4] * projMatrix[0+0*4];
++ m_clipXOffset = (guiMatrix.m[0][3] * modelMatrix[0+0*4] + modelMatrix[0+3*4]) * projMatrix[0+0*4];
++ m_clipYFactor = guiMatrix.m[1][1] * modelMatrix[1+1*4] * projMatrix[1+1*4];
++ m_clipYOffset = (guiMatrix.m[1][3] * modelMatrix[1+1*4] + modelMatrix[1+3*4]) * projMatrix[1+1*4];
++ float clipW = (guiMatrix.m[2][3] * modelMatrix[2+2*4] + modelMatrix[2+3*4]) * projMatrix[3+2*4];
++ float xMult = (viewPort.x2 - viewPort.x1) / (2 * clipW);
++ float yMult = (viewPort.y1 - viewPort.y2) / (2 * clipW); // correct for inverted window coordinate scheme
++ m_clipXFactor = m_clipXFactor * xMult;
++ m_clipXOffset = m_clipXOffset * xMult + (viewPort.x2 + viewPort.x1) / 2;
++ m_clipYFactor = m_clipYFactor * yMult;
++ m_clipYOffset = m_clipYOffset * yMult + (viewPort.y2 + viewPort.y1) / 2;
++ }
+
+ return true;
+ }
+diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h
+index c7e95aa..86ce4cc 100644
+--- a/xbmc/guilib/GUIShader.h
++++ b/xbmc/guilib/GUIShader.h
+@@ -41,6 +41,11 @@ class CGUIShader : public CGLSLShaderProgram
+ GLint GetCord1Loc() { return m_hCord1; }
+ GLint GetUniColLoc() { return m_hUniCol; }
+ GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; }
++ bool HardwareClipIsPossible() { return m_clipPossible; }
++ GLfloat GetClipXFactor() { return m_clipXFactor; }
++ GLfloat GetClipXOffset() { return m_clipXOffset; }
++ GLfloat GetClipYFactor() { return m_clipYFactor; }
++ GLfloat GetClipYOffset() { return m_clipYOffset; }
+
+ protected:
+ GLint m_hTex0;
+@@ -56,6 +61,12 @@ class CGUIShader : public CGLSLShaderProgram
+
+ GLfloat *m_proj;
+ GLfloat *m_model;
++
++ bool m_clipPossible;
++ GLfloat m_clipXFactor;
++ GLfloat m_clipXOffset;
++ GLfloat m_clipYFactor;
++ GLfloat m_clipYOffset;
+ };
+
+ #endif // GUI_SHADER_H
+diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
+index 38f17a7..5bffdf5 100644
+--- a/xbmc/guilib/GraphicContext.cpp
++++ b/xbmc/guilib/GraphicContext.cpp
+@@ -167,6 +167,16 @@ void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2)
+ }
+ }
+
++CRect CGraphicContext::GetClipRegion()
++{
++ if (m_clipRegions.empty())
++ return CRect(0, 0, m_iScreenWidth, m_iScreenHeight);
++ CRect clipRegion(m_clipRegions.top());
++ if (!m_origins.empty())
++ clipRegion -= m_origins.top();
++ return clipRegion;
++}
++
+ bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */)
+ {
+ // transform coordinates - we may have a rotation which changes the positioning of the
+diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
+index bab2457..0a27643 100644
+--- a/xbmc/guilib/GraphicContext.h
++++ b/xbmc/guilib/GraphicContext.h
+@@ -199,6 +199,7 @@ class CGraphicContext : public CCriticalSection,
+ void ApplyHardwareTransform();
+ void RestoreHardwareTransform();
+ void ClipRect(CRect &vertex, CRect &texture, CRect *diffuse = NULL);
++ CRect GetClipRegion();
+ inline void AddGUITransform()
+ {
+ m_transforms.push(m_finalTransform);
+diff --git a/xbmc/rendering/RenderSystem.h b/xbmc/rendering/RenderSystem.h
+index fa64eba..c1dfb93 100644
+--- a/xbmc/rendering/RenderSystem.h
++++ b/xbmc/rendering/RenderSystem.h
+@@ -110,6 +110,8 @@ class CRenderSystemBase
+ virtual void GetViewPort(CRect& viewPort) = 0;
+ virtual void RestoreViewPort() {};
+
++ virtual bool ScissorsCanEffectClipping() { return false; }
++ virtual CRect ClipRectToScissorRect(const CRect &rect) { return CRect(); }
+ virtual void SetScissors(const CRect &rect) = 0;
+ virtual void ResetScissors() = 0;
+
+diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
+index 653c9ec..deb3afc 100644
+--- a/xbmc/rendering/gles/RenderSystemGLES.cpp
++++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
+@@ -533,6 +533,28 @@ void CRenderSystemGLES::SetViewPort(CRect& viewPort)
+ m_viewPort[3] = viewPort.Height();
+ }
+
++bool CRenderSystemGLES::ScissorsCanEffectClipping()
++{
++ if (m_pGUIshader[m_method])
++ return m_pGUIshader[m_method]->HardwareClipIsPossible();
++
++ return false;
++}
++
++CRect CRenderSystemGLES::ClipRectToScissorRect(const CRect &rect)
++{
++ if (!m_pGUIshader[m_method])
++ return CRect();
++ float xFactor = m_pGUIshader[m_method]->GetClipXFactor();
++ float xOffset = m_pGUIshader[m_method]->GetClipXOffset();
++ float yFactor = m_pGUIshader[m_method]->GetClipYFactor();
++ float yOffset = m_pGUIshader[m_method]->GetClipYOffset();
++ return CRect(rect.x1 * xFactor + xOffset,
++ rect.y1 * yFactor + yOffset,
++ rect.x2 * xFactor + xOffset,
++ rect.y2 * yFactor + yOffset);
++}
++
+ void CRenderSystemGLES::SetScissors(const CRect &rect)
+ {
+ if (!m_bRenderCreated)
+diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
+index 98e398a..81ee49e 100644
+--- a/xbmc/rendering/gles/RenderSystemGLES.h
++++ b/xbmc/rendering/gles/RenderSystemGLES.h
+@@ -63,6 +63,8 @@ class CRenderSystemGLES : public CRenderSystemBase
+ virtual void SetViewPort(CRect& viewPort);
+ virtual void GetViewPort(CRect& viewPort);
+
++ virtual bool ScissorsCanEffectClipping();
++ virtual CRect ClipRectToScissorRect(const CRect &rect);
+ virtual void SetScissors(const CRect& rect);
+ virtual void ResetScissors();
+
+--
+1.9.3
+
+
+From e372121bc53da1b0353b51f5e9897011c5f54033 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Thu, 23 Jan 2014 16:42:22 +0000
+Subject: [PATCH 24/94] Increase font cache hit rate by keying on the
+ fractional part of m_originX and m_originY *after* they have been through the
+ graphics context's transformation matrix, plus the scale/rotation elements of
+ the matrix, rather than the origin in the original frame of reference plus
+ the complete transformation matrix. All vertices of individual glyph bounding
+ boxes are a constant offset from this position, and when the fractional part
+ of the translation is a match, the rounding of each vertex will be in the
+ same direction; this permits us to calculate the desired vertices from the
+ cached ones simply by adding the integer parts of the translations with no
+ additional rounding steps.
+
+Note that this requires that software clipping is *not* performed.
+---
+ xbmc/guilib/GUIFontCache.cpp | 8 +++++++
+ xbmc/guilib/GUIFontCache.h | 43 +++++++++++++++++++++++++++++++++++
+ xbmc/guilib/GUIFontTTF.cpp | 53 +++++++++++++++++++++++++++++++++++---------
+ xbmc/guilib/GUIFontTTF.h | 1 +
+ 4 files changed, 95 insertions(+), 10 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
+index c029713..b66c00b 100644
+--- a/xbmc/guilib/GUIFontCache.cpp
++++ b/xbmc/guilib/GUIFontCache.cpp
+@@ -85,6 +85,9 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
+ else
+ {
+ /* Cache hit */
++ /* Update the translation arguments so that they hold the offset to apply
++ * to the cached values (but only in the dynamic case) */
++ pos.UpdateWithOffsets(i->m_key.m_pos, scrolling);
+ /* Update time in entry and move to the back of the list */
+ i->m_lastUsedMillis = nowMillis;
+ m_list.get<Age>().relocate(m_list.get<Age>().end(), m_list.project<Age>(i));
+@@ -103,3 +106,8 @@ template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStati
+ template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry();
+ template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
+ template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush();
++
++template void CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue> &entry);
++template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry();
++template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
++template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush();
+diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h
+index ef65845..d913dee 100644
+--- a/xbmc/guilib/GUIFontCache.h
++++ b/xbmc/guilib/GUIFontCache.h
+@@ -44,6 +44,7 @@
+ using namespace boost::multi_index;
+
+ #define FONT_CACHE_TIME_LIMIT (1000)
++#define FONT_CACHE_DIST_LIMIT (0.01)
+
+ template<class Position, class Value> class CGUIFontCache;
+ class CGUIFontTTFBase;
+@@ -197,6 +198,7 @@ struct CGUIFontCacheStaticPosition
+ float m_x;
+ float m_y;
+ CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {}
++ void UpdateWithOffsets(const CGUIFontCacheStaticPosition &cached, bool scrolling) {}
+ };
+
+ typedef std::vector<SVertex> CGUIFontCacheStaticValue;
+@@ -214,4 +216,45 @@ inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPo
+ return a.m_matrix.m[0][3];
+ }
+
++struct CGUIFontCacheDynamicPosition
++{
++ float m_x;
++ float m_y;
++ float m_z;
++ CGUIFontCacheDynamicPosition() {}
++ CGUIFontCacheDynamicPosition(float x, float y, float z) : m_x(x), m_y(y), m_z(z) {}
++ void UpdateWithOffsets(const CGUIFontCacheDynamicPosition &cached, bool scrolling)
++ {
++ if (scrolling)
++ m_x = m_x - cached.m_x;
++ else
++ m_x = floorf(m_x - cached.m_x + FONT_CACHE_DIST_LIMIT);
++ m_y = floorf(m_y - cached.m_y + FONT_CACHE_DIST_LIMIT);
++ m_z = floorf(m_z - cached.m_z + FONT_CACHE_DIST_LIMIT);
++ }
++};
++
++typedef std::vector<SVertex> CGUIFontCacheDynamicValue;
++
++inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m,
++ const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m,
++ bool scrolling)
++{
++ float diffX = a.m_x - b.m_x + FONT_CACHE_DIST_LIMIT;
++ float diffY = a.m_y - b.m_y + FONT_CACHE_DIST_LIMIT;
++ float diffZ = a.m_z - b.m_z + FONT_CACHE_DIST_LIMIT;
++ return (scrolling || diffX - floorf(diffX) < 2 * FONT_CACHE_DIST_LIMIT) &&
++ diffY - floorf(diffY) < 2 * FONT_CACHE_DIST_LIMIT &&
++ diffZ - floorf(diffZ) < 2 * FONT_CACHE_DIST_LIMIT &&
++ a_m.m[0][0] == b_m.m[0][0] &&
++ a_m.m[1][1] == b_m.m[1][1] &&
++ a_m.m[2][2] == b_m.m[2][2];
++ // We already know the first 3 columns of both matrices are diagonal, so no need to check the other elements
++}
++
++inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheDynamicPosition> &a)
++{
++ return 0;
++}
++
+ #endif
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 19c7ff4..73f0e50 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -132,7 +132,7 @@ class CFreeTypeLibrary
+ XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library
+ #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary)
+
+-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this)
++CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this), m_dynamicCache(*this)
+ {
+ m_texture = NULL;
+ m_char = NULL;
+@@ -332,13 +332,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ Begin();
+
+ bool dirtyCache;
++ bool hardwareClipping = g_Windowing.ScissorsCanEffectClipping();
+ CGUIFontCacheStaticPosition staticPos(x, y);
+- std::vector<SVertex> &vertices = m_staticCache.Lookup(staticPos,
+- colors, text,
+- alignment, maxPixelWidth,
+- scrolling,
+- XbmcThreads::SystemClockMillis(),
+- dirtyCache);
++ CGUIFontCacheDynamicPosition dynamicPos;
++ if (hardwareClipping)
++ {
++ dynamicPos = CGUIFontCacheDynamicPosition(g_graphicsContext.ScaleFinalXCoord(x, y),
++ g_graphicsContext.ScaleFinalYCoord(x, y),
++ g_graphicsContext.ScaleFinalZCoord(x, y));
++ }
++ std::vector<SVertex> &vertices = hardwareClipping ?
++ m_dynamicCache.Lookup(dynamicPos,
++ colors, text,
++ alignment, maxPixelWidth,
++ scrolling,
++ XbmcThreads::SystemClockMillis(),
++ dirtyCache) :
++ m_staticCache.Lookup(staticPos,
++ colors, text,
++ alignment, maxPixelWidth,
++ scrolling,
++ XbmcThreads::SystemClockMillis(),
++ dirtyCache);
+ if (dirtyCache)
+ {
+ // save the origin, which is scaled separately
+@@ -441,10 +456,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ else
+ cursorX += ch->advance;
+ }
++ if (hardwareClipping)
++ /* Append the new vertices (which we have just constructed in the cache)
++ * to the set collected since the first Begin() call */
++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
++ }
++ else if (hardwareClipping)
++ {
++ /* Apply the translation offset to the vertices from the cache after
++ * appending them to the set collected since the first Begin() call */
++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
++ SVertex *v;
++ for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++)
++ {
++ v->x += dynamicPos.m_x;
++ v->y += dynamicPos.m_y;
++ v->z += dynamicPos.m_z;
++ }
+ }
+- /* Append the new vertices (from the cache or otherwise) to the set collected
+- * since the first Begin() call */
+- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
++ if (!hardwareClipping)
++ /* Append the new vertices (from the cache or otherwise) to the set collected
++ * since the first Begin() call */
++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+
+ End();
+ }
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index 7cb4669..78445ab 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -170,6 +170,7 @@ class CGUIFontTTFBase
+ CStdString m_strFileName;
+
+ CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> m_staticCache;
++ CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue> m_dynamicCache;
+
+ private:
+ virtual bool FirstBegin() = 0;
+--
+1.9.3
+
+
+From 10eeb73ca15798de26abd5f8846214c8938f8b42 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 8 Jan 2014 12:16:33 +0000
+Subject: [PATCH 25/94] Rewrite of scrolling text code.
+
+No longer shuffles the string round to minimise the number of characters
+before the clipping rectangle; this doesn't save much on rendering time but
+does harm the effectiveness of the cache. Now uses a pixel offset into the
+string rather than a character + pixel offset, and plots the entire string
+every time (execpt when the wrap point is visible, in which case the entire
+string is plotted twice).
+
+It also makes motion smoother, because (possibly unintentionally) the
+previous code preferred to align the scroll offset with character boundaries.
+This would lead to uneven changes of position, especially when the width of
+the character currently being scrolled off the edge was only slightly more
+than an integral multiple of the scroll increment.
+---
+ xbmc/guilib/GUIFadeLabelControl.cpp | 8 +--
+ xbmc/guilib/GUIFont.cpp | 123 +++++++++++++-----------------------
+ xbmc/guilib/GUIFont.h | 17 ++---
+ xbmc/guilib/GUIRSSControl.cpp | 6 +-
+ xbmc/utils/RssReader.cpp | 2 +-
+ xbmc/utils/RssReader.h | 2 +-
+ 6 files changed, 58 insertions(+), 100 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFadeLabelControl.cpp b/xbmc/guilib/GUIFadeLabelControl.cpp
+index d594c04..86ee73a 100644
+--- a/xbmc/guilib/GUIFadeLabelControl.cpp
++++ b/xbmc/guilib/GUIFadeLabelControl.cpp
+@@ -109,18 +109,14 @@ void CGUIFadeLabelControl::Process(unsigned int currentTime, CDirtyRegionList &d
+ bool moveToNextLabel = false;
+ if (!m_scrollOut)
+ {
+- vecText text;
+- m_textLayout.GetFirstText(text);
+- if (m_scrollInfo.characterPos && m_scrollInfo.characterPos < text.size())
+- text.erase(text.begin(), text.begin() + min((int)m_scrollInfo.characterPos - 1, (int)text.size()));
+- if (m_label.font->GetTextWidth(text) < m_width)
++ if (m_scrollInfo.pixelPos + m_width > m_scrollInfo.m_textWidth)
+ {
+ if (m_fadeAnim.GetProcess() != ANIM_PROCESS_NORMAL)
+ m_fadeAnim.QueueAnimation(ANIM_PROCESS_NORMAL);
+ moveToNextLabel = true;
+ }
+ }
+- else if (m_scrollInfo.characterPos > m_textLayout.GetTextLength())
++ else if (m_scrollInfo.pixelPos > m_scrollInfo.m_textWidth)
+ moveToNextLabel = true;
+
+ // apply the fading animation
+diff --git a/xbmc/guilib/GUIFont.cpp b/xbmc/guilib/GUIFont.cpp
+index a7ee668..eb8efdb 100644
+--- a/xbmc/guilib/GUIFont.cpp
++++ b/xbmc/guilib/GUIFont.cpp
+@@ -36,7 +36,12 @@ CScrollInfo::CScrollInfo(unsigned int wait /* = 50 */, float pos /* = 0 */,
+ initialWait = wait;
+ initialPos = pos;
+ SetSpeed(speed ? speed : defaultSpeed);
+- g_charsetConverter.utf8ToW(scrollSuffix, suffix);
++ CStdStringW wsuffix;
++ g_charsetConverter.utf8ToW(scrollSuffix, wsuffix);
++ suffix.clear();
++ suffix.reserve(wsuffix.size());
++ for (vecText::size_type i = 0; i < wsuffix.size(); i++)
++ suffix.push_back(wsuffix[i]);
+ Reset();
+ }
+
+@@ -115,11 +120,12 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo)
+ {
+ // draw at our scroll position
+ // we handle the scrolling as follows:
+- // We scroll on a per-pixel basis up until we have scrolled the first character outside
+- // of our viewport, whereby we cycle the string around, and reset the scroll position.
+- //
+- // pixelPos is the amount in pixels to move the string by.
+- // characterPos is the amount in characters to rotate the string by.
++ // We scroll on a per-pixel basis (eschewing the use of character indices
++ // which were also in use previously). The complete string, including suffix,
++ // is plotted to achieve the desired effect - normally just the one time, but
++ // if there is a wrap point within the viewport then it will be plotted twice.
++ // If the string is smaller than the viewport, then it may be plotted even
++ // more times than that.
+ //
+ if (scrollInfo.waitTime)
+ {
+@@ -135,54 +141,19 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo)
+ // move along by the appropriate scroll amount
+ float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX());
+
+- if (scrollInfo.pixelSpeed > 0)
++ if (!scrollInfo.m_widthValid)
+ {
+- // we want to move scrollAmount, grab the next character
+- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
+- if (scrollInfo.pixelPos + scrollAmount < charWidth)
+- scrollInfo.pixelPos += scrollAmount; // within the current character
+- else
+- { // past the current character, decrement scrollAmount by the charWidth and move to the next character
+- while (scrollInfo.pixelPos + scrollAmount >= charWidth)
+- {
+- scrollAmount -= (charWidth - scrollInfo.pixelPos);
+- scrollInfo.pixelPos = 0;
+- scrollInfo.characterPos++;
+- if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size())
+- {
+- scrollInfo.Reset();
+- break;
+- }
+- charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
+- }
+- }
+- }
+- else if (scrollInfo.pixelSpeed < 0)
+- { // scrolling backwards
+- // we want to move scrollAmount, grab the next character
+- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
+- if (scrollInfo.pixelPos + scrollAmount < charWidth)
+- scrollInfo.pixelPos += scrollAmount; // within the current character
+- else
+- { // past the current character, decrement scrollAmount by the charWidth and move to the next character
+- while (scrollInfo.pixelPos + scrollAmount >= charWidth)
+- {
+- scrollAmount -= (charWidth - scrollInfo.pixelPos);
+- scrollInfo.pixelPos = 0;
+- if (scrollInfo.characterPos == 0)
+- {
+- scrollInfo.Reset();
+- scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1;
+- break;
+- }
+- scrollInfo.characterPos--;
+- charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
+- }
+- }
++ /* Calculate the pixel width of the complete string */
++ scrollInfo.m_textWidth = GetTextWidth(text);
++ scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix);
++ scrollInfo.m_widthValid = true;
+ }
++ scrollInfo.pixelPos += scrollAmount;
++ assert(scrollInfo.m_totalWidth != 0);
++ while (scrollInfo.pixelPos >= scrollInfo.m_totalWidth)
++ scrollInfo.pixelPos -= scrollInfo.m_totalWidth;
+
+- if(scrollInfo.characterPos != old.characterPos
+- || scrollInfo.pixelPos != old.pixelPos)
++ if (scrollInfo.pixelPos != old.pixelPos)
+ return true;
+ else
+ return false;
+@@ -194,39 +165,27 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo
+ if (!m_font) return;
+ if (!shadowColor) shadowColor = m_shadowColor;
+
+- float spaceWidth = GetCharWidth(L' ');
+- // max chars on screen + extra margin chars
+- vecText::size_type maxChars =
+- std::min<vecText::size_type>(
+- (text.size() + (vecText::size_type)scrollInfo.suffix.size()),
+- (vecText::size_type)((maxWidth * 1.05f) / spaceWidth));
+-
+ if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment))
+ return; // nothing to render
+
+- maxWidth = ROUND((maxWidth + scrollInfo.pixelPos) / g_graphicsContext.GetGUIScaleX());
++ if (!scrollInfo.m_widthValid)
++ {
++ /* Calculate the pixel width of the complete string */
++ scrollInfo.m_textWidth = GetTextWidth(text);
++ scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix);
++ scrollInfo.m_widthValid = true;
++ }
++
++ assert(scrollInfo.m_totalWidth != 0);
++
++ float textPixelWidth = ROUND(scrollInfo.m_textWidth / g_graphicsContext.GetGUIScaleX());
++ float suffixPixelWidth = ROUND((scrollInfo.m_totalWidth - scrollInfo.m_textWidth) / g_graphicsContext.GetGUIScaleX());
+
+- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
+ float offset;
+ if(scrollInfo.pixelSpeed >= 0)
+ offset = scrollInfo.pixelPos;
+ else
+- offset = charWidth - scrollInfo.pixelPos;
+-
+- // Now rotate our string as needed, only take a slightly larger then visible part of the text.
+- unsigned int pos = scrollInfo.characterPos;
+- vecText renderText;
+- renderText.reserve(maxChars);
+- for (vecText::size_type i = 0; i < maxChars; i++)
+- {
+- if (pos >= text.size() + scrollInfo.suffix.size())
+- pos = 0;
+- if (pos < text.size())
+- renderText.push_back(text[pos]);
+- else
+- renderText.push_back(scrollInfo.suffix[pos - text.size()]);
+- pos++;
+- }
++ offset = scrollInfo.m_totalWidth - scrollInfo.pixelPos;
+
+ vecColors renderColors;
+ for (unsigned int i = 0; i < colors.size(); i++)
+@@ -239,9 +198,17 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo
+ vecColors shadowColors;
+ for (unsigned int i = 0; i < renderColors.size(); i++)
+ shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0);
+- m_font->DrawTextInternal(x - offset + 1, y + 1, shadowColors, renderText, alignment, maxWidth + m_font->GetLineHeight(2.0f), scroll);
++ for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth)
++ {
++ m_font->DrawTextInternal(x + dx + 1, y + 1, shadowColors, text, alignment, textPixelWidth, scroll);
++ m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth + 1, y + 1, shadowColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll);
++ }
++ }
++ for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth)
++ {
++ m_font->DrawTextInternal(x + dx, y, renderColors, text, alignment, textPixelWidth, scroll);
++ m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth, y, renderColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll);
+ }
+- m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + m_font->GetLineHeight(2.0f), scroll);
+
+ g_graphicsContext.RestoreClipRegion();
+ }
+diff --git a/xbmc/guilib/GUIFont.h b/xbmc/guilib/GUIFont.h
+index c55db48..09cf9b3 100644
+--- a/xbmc/guilib/GUIFont.h
++++ b/xbmc/guilib/GUIFont.h
+@@ -64,7 +64,6 @@ class CScrollInfo
+ void Reset()
+ {
+ waitTime = initialWait;
+- characterPos = 0;
+ // pixelPos is where we start the current letter, so is measured
+ // to the left of the text rendering's left edge. Thus, a negative
+ // value will mean the text starts to the right
+@@ -72,25 +71,19 @@ class CScrollInfo
+ // privates:
+ m_averageFrameTime = 1000.f / abs(defaultSpeed);
+ m_lastFrameTime = 0;
+- }
+- uint32_t GetCurrentChar(const vecText &text) const
+- {
+- assert(text.size());
+- if (characterPos < text.size())
+- return text[characterPos];
+- else if (characterPos < text.size() + suffix.size())
+- return suffix[characterPos - text.size()];
+- return text[0];
++ m_widthValid = false;
+ }
+ float GetPixelsPerFrame();
+
+ float pixelPos;
+ float pixelSpeed;
+ unsigned int waitTime;
+- unsigned int characterPos;
+ unsigned int initialWait;
+ float initialPos;
+- CStdStringW suffix;
++ vecText suffix;
++ mutable float m_textWidth;
++ mutable float m_totalWidth;
++ mutable bool m_widthValid;
+
+ static const int defaultSpeed = 60;
+ private:
+diff --git a/xbmc/guilib/GUIRSSControl.cpp b/xbmc/guilib/GUIRSSControl.cpp
+index 712e118..203c138 100644
+--- a/xbmc/guilib/GUIRSSControl.cpp
++++ b/xbmc/guilib/GUIRSSControl.cpp
+@@ -119,7 +119,9 @@ void CGUIRSSControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyre
+ dirty = true;
+
+ if (CRssManager::Get().GetReader(GetID(), GetParentID(), this, m_pReader))
+- m_scrollInfo.characterPos = m_pReader->m_SavedScrollPos;
++ {
++ m_scrollInfo.pixelPos = m_pReader->m_savedScrollPixelPos;
++ }
+ else
+ {
+ if (m_strRSSTags != "")
+@@ -177,7 +179,7 @@ void CGUIRSSControl::Render()
+ if (m_pReader)
+ {
+ m_pReader->CheckForUpdates();
+- m_pReader->m_SavedScrollPos = m_scrollInfo.characterPos;
++ m_pReader->m_savedScrollPixelPos = m_scrollInfo.pixelPos;
+ }
+ }
+ CGUIControl::Render();
+diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp
+index b1e53b7..f68597a 100644
+--- a/xbmc/utils/RssReader.cpp
++++ b/xbmc/utils/RssReader.cpp
+@@ -54,7 +54,7 @@ CRssReader::CRssReader() : CThread("RSSReader")
+ m_pObserver = NULL;
+ m_spacesBetweenFeeds = 0;
+ m_bIsRunning = false;
+- m_SavedScrollPos = 0;
++ m_savedScrollPixelPos = 0;
+ m_rtlText = false;
+ m_requestRefresh = false;
+ }
+diff --git a/xbmc/utils/RssReader.h b/xbmc/utils/RssReader.h
+index 2c6f366..b74faf2 100644
+--- a/xbmc/utils/RssReader.h
++++ b/xbmc/utils/RssReader.h
+@@ -43,7 +43,7 @@ class CRssReader : public CThread
+ void SetObserver(IRssObserver* observer);
+ void CheckForUpdates();
+ void requestRefresh();
+- unsigned int m_SavedScrollPos;
++ float m_savedScrollPixelPos;
+
+ private:
+ void Process();
+--
+1.9.3
+
+
+From 7064920379f68a7f6114813db8ad40a21c4957cc Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Mon, 27 Jan 2014 23:21:10 +0000
+Subject: [PATCH 26/94] Move the application of the translation offsets into
+ the GLES code. Still all pure software at this stage. Main change is in the
+ data types at the interface between CGUIFontTTFBase and CGUIFontTTFGL. The
+ old way (array of vertices in m_vertex) are retained in addition, for the
+ sake`of cases that need to use software clipping on GLES, as well as for DX
+ and GL support where the new scheme is not (yet?) used.
+
+---
+ xbmc/guilib/GUIFontTTF.cpp | 19 +++---------
+ xbmc/guilib/GUIFontTTF.h | 17 +++++++++++
+ xbmc/guilib/GUIFontTTFGL.cpp | 72 ++++++++++++++++++++++++++++++++------------
+ 3 files changed, 73 insertions(+), 35 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 73f0e50..ad0a53b 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -215,6 +215,7 @@ void CGUIFontTTFBase::Clear()
+ g_freeTypeLibrary.ReleaseStroker(m_stroker);
+ m_stroker = NULL;
+
++ m_vertexTrans.clear();
+ m_vertex.clear();
+ }
+
+@@ -310,6 +311,7 @@ void CGUIFontTTFBase::Begin()
+ {
+ if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin())
+ {
++ m_vertexTrans.clear();
+ m_vertex.clear();
+ }
+ // Keep track of the nested begin/end calls.
+@@ -457,23 +459,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ cursorX += ch->advance;
+ }
+ if (hardwareClipping)
+- /* Append the new vertices (which we have just constructed in the cache)
+- * to the set collected since the first Begin() call */
+- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices));
+ }
+ else if (hardwareClipping)
+- {
+- /* Apply the translation offset to the vertices from the cache after
+- * appending them to the set collected since the first Begin() call */
+- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+- SVertex *v;
+- for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++)
+- {
+- v->x += dynamicPos.m_x;
+- v->y += dynamicPos.m_y;
+- v->z += dynamicPos.m_z;
+- }
+- }
++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices));
+ if (!hardwareClipping)
+ /* Append the new vertices (from the cache or otherwise) to the set collected
+ * since the first Begin() call */
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index 78445ab..c71f90d 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -61,6 +61,14 @@ struct SVertex
+ unsigned char r, g, b, a;
+ #endif
+ float u, v;
++ struct SVertex Offset(float translate[3]) const
++ {
++ SVertex out = *this;
++ out.x += translate[0];
++ out.y += translate[1];
++ out.z += translate[2];
++ return out;
++ }
+ };
+
+
+@@ -160,6 +168,15 @@ class CGUIFontTTFBase
+ bool m_bTextureLoaded;
+ unsigned int m_nTexture;
+
++ struct CTranslatedVertices
++ {
++ float translateX;
++ float translateY;
++ float translateZ;
++ const std::vector<SVertex> *vertexBuffer;
++ CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {}
++ };
++ std::vector<CTranslatedVertices> m_vertexTrans;
+ std::vector<SVertex> m_vertex;
+
+ float m_textureScaleX;
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index cb56987..f6aa081 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -146,34 +146,65 @@ void CGUIFontTTFGL::LastEnd()
+ GLint colLoc = g_Windowing.GUIShaderGetCol();
+ GLint tex0Loc = g_Windowing.GUIShaderGetCoord0();
+
+- // stack object until VBOs will be used
+- std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
+- SVertex *vertices = &vecVertices[0];
++ // Enable the attributes used by this shader
++ glEnableVertexAttribArray(posLoc);
++ glEnableVertexAttribArray(colLoc);
++ glEnableVertexAttribArray(tex0Loc);
+
+- for (size_t i=0; i<m_vertex.size(); i+=4)
++ if (m_vertex.size() > 0)
+ {
+- *vertices++ = m_vertex[i];
+- *vertices++ = m_vertex[i+1];
+- *vertices++ = m_vertex[i+2];
++ // Deal with vertices that had to use software clipping
++ std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
++ SVertex *vertices = &vecVertices[0];
+
+- *vertices++ = m_vertex[i+1];
+- *vertices++ = m_vertex[i+3];
+- *vertices++ = m_vertex[i+2];
+- }
++ for (size_t i=0; i<m_vertex.size(); i+=4)
++ {
++ *vertices++ = m_vertex[i];
++ *vertices++ = m_vertex[i+1];
++ *vertices++ = m_vertex[i+2];
+
+- vertices = &vecVertices[0];
++ *vertices++ = m_vertex[i+1];
++ *vertices++ = m_vertex[i+3];
++ *vertices++ = m_vertex[i+2];
++ }
+
+- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
+- // Normalize color values. Does not affect Performance at all.
+- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
+- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
++ vertices = &vecVertices[0];
+
+- glEnableVertexAttribArray(posLoc);
+- glEnableVertexAttribArray(colLoc);
+- glEnableVertexAttribArray(tex0Loc);
++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
++ // Normalize color values. Does not affect Performance at all.
++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
++
++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
++ }
++ if (m_vertexTrans.size() > 0)
++ {
++ // Deal with the vertices that can be hardware clipped and therefore translated
++ std::vector<SVertex> vecVertices;
++ for (size_t i = 0; i < m_vertexTrans.size(); i++)
++ {
++ float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ };
++ for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4)
++ {
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate));
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate));
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
++ }
++ }
++ SVertex *vertices = &vecVertices[0];
+
+- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
++ // Normalize color values. Does not affect Performance at all.
++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
++
++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
++ }
+
++ // Disable the attributes used by this shader
+ glDisableVertexAttribArray(posLoc);
+ glDisableVertexAttribArray(colLoc);
+ glDisableVertexAttribArray(tex0Loc);
+@@ -201,6 +232,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
+ if (m_textureHeight < newHeight)
+ CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight);
+ m_staticCache.Flush();
++ m_dynamicCache.Flush();
+
+ memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch());
+ if (m_texture)
+--
+1.9.3
+
+
+From 476fce7bc2897e8898f4392809d934b0d5f46518 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 15 Jan 2014 15:28:06 +0000
+Subject: [PATCH 27/94] Rather than applying the translation offsets to the
+ vertices, now applies them to the model view matrix from the top of the
+ matrix stack and pushes it over to OpenGL. The vertices themselves are still
+ all held client-side.
+
+---
+ xbmc/guilib/GUIFontTTF.h | 8 -------
+ xbmc/guilib/GUIFontTTFGL.cpp | 40 +++++++++++++++++++++-----------
+ xbmc/guilib/GUIShader.h | 1 +
+ xbmc/rendering/gles/RenderSystemGLES.cpp | 8 +++++++
+ xbmc/rendering/gles/RenderSystemGLES.h | 1 +
+ 5 files changed, 36 insertions(+), 22 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index c71f90d..fde2085 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -61,14 +61,6 @@ struct SVertex
+ unsigned char r, g, b, a;
+ #endif
+ float u, v;
+- struct SVertex Offset(float translate[3]) const
+- {
+- SVertex out = *this;
+- out.x += translate[0];
+- out.y += translate[1];
+- out.z += translate[2];
+- return out;
+- }
+ };
+
+
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index f6aa081..fbffaa0 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -29,6 +29,7 @@
+ #include "utils/log.h"
+ #include "utils/GLUtils.h"
+ #include "windowing/WindowingFactory.h"
++#include "guilib/MatrixGLES.h"
+
+ // stuff for freetype
+ #include <ft2build.h>
+@@ -145,6 +146,7 @@ void CGUIFontTTFGL::LastEnd()
+ GLint posLoc = g_Windowing.GUIShaderGetPos();
+ GLint colLoc = g_Windowing.GUIShaderGetCol();
+ GLint tex0Loc = g_Windowing.GUIShaderGetCoord0();
++ GLint modelLoc = g_Windowing.GUIShaderGetModel();
+
+ // Enable the attributes used by this shader
+ glEnableVertexAttribArray(posLoc);
+@@ -183,25 +185,35 @@ void CGUIFontTTFGL::LastEnd()
+ std::vector<SVertex> vecVertices;
+ for (size_t i = 0; i < m_vertexTrans.size(); i++)
+ {
+- float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ };
++ // Apply the translation to the currently active (top-of-stack) model view matrix
++ g_matrices.MatrixMode(MM_MODELVIEW);
++ g_matrices.PushMatrix();
++ g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
++ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
++
++ vecVertices.clear();
+ for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4)
+ {
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate));
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate));
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]);
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]);
++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
+ }
+- }
+- SVertex *vertices = &vecVertices[0];
++ SVertex *vertices = &vecVertices[0];
+
+- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
+- // Normalize color values. Does not affect Performance at all.
+- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
+- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
++ // Normalize color values. Does not affect Performance at all.
++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
+
+- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
++
++ g_matrices.PopMatrix();
++ }
++ // Restore the original model view matrix
++ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+ }
+
+ // Disable the attributes used by this shader
+diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h
+index 86ce4cc..ba01956 100644
+--- a/xbmc/guilib/GUIShader.h
++++ b/xbmc/guilib/GUIShader.h
+@@ -41,6 +41,7 @@ class CGUIShader : public CGLSLShaderProgram
+ GLint GetCord1Loc() { return m_hCord1; }
+ GLint GetUniColLoc() { return m_hUniCol; }
+ GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; }
++ GLint GetModelLoc() { return m_hModel; }
+ bool HardwareClipIsPossible() { return m_clipPossible; }
+ GLfloat GetClipXFactor() { return m_clipXFactor; }
+ GLfloat GetClipXOffset() { return m_clipXOffset; }
+diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
+index deb3afc..0904d1f 100644
+--- a/xbmc/rendering/gles/RenderSystemGLES.cpp
++++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
+@@ -691,4 +691,12 @@ bool CRenderSystemGLES::SupportsStereo(RENDER_STEREO_MODE mode)
+ }
+ }
+
++GLint CRenderSystemGLES::GUIShaderGetModel()
++{
++ if (m_pGUIshader[m_method])
++ return m_pGUIshader[m_method]->GetModelLoc();
++
++ return -1;
++}
++
+ #endif
+diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
+index 81ee49e..d2f9cd1 100644
+--- a/xbmc/rendering/gles/RenderSystemGLES.h
++++ b/xbmc/rendering/gles/RenderSystemGLES.h
+@@ -91,6 +91,7 @@ class CRenderSystemGLES : public CRenderSystemBase
+ GLint GUIShaderGetCoord1();
+ GLint GUIShaderGetUniCol();
+ GLint GUIShaderGetCoord0Matrix();
++ GLint GUIShaderGetModel();
+
+ protected:
+ virtual void SetVSyncImpl(bool enable) = 0;
+--
+1.9.3
+
+
+From 473ccc4cbe616f672a72108d2ec98107780ac7da Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 29 Jan 2014 13:21:19 +0000
+Subject: [PATCH 28/94] Enable hardware clipping.
+
+---
+ xbmc/guilib/GUIFontTTF.cpp | 4 ++--
+ xbmc/guilib/GUIFontTTF.h | 5 ++++-
+ xbmc/guilib/GUIFontTTFGL.cpp | 6 ++++++
+ 3 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index ad0a53b..4dc4c8e 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -459,10 +459,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ cursorX += ch->advance;
+ }
+ if (hardwareClipping)
+- m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices));
++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion()));
+ }
+ else if (hardwareClipping)
+- m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices));
++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion()));
+ if (!hardwareClipping)
+ /* Append the new vertices (from the cache or otherwise) to the set collected
+ * since the first Begin() call */
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index fde2085..5e7c31f 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -27,6 +27,8 @@
+ *
+ */
+
++#include "Geometry.h"
++
+ // forward definition
+ class CBaseTexture;
+
+@@ -166,7 +168,8 @@ class CGUIFontTTFBase
+ float translateY;
+ float translateZ;
+ const std::vector<SVertex> *vertexBuffer;
+- CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {}
++ CRect clip;
++ CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {}
+ };
+ std::vector<CTranslatedVertices> m_vertexTrans;
+ std::vector<SVertex> m_vertex;
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index fbffaa0..b7618e1 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -185,6 +185,10 @@ void CGUIFontTTFGL::LastEnd()
+ std::vector<SVertex> vecVertices;
+ for (size_t i = 0; i < m_vertexTrans.size(); i++)
+ {
++ // Apply the clip rectangle
++ CRect clip = g_Windowing.ClipRectToScissorRect(m_vertexTrans[i].clip);
++ g_graphicsContext.SetScissors(clip);
++
+ // Apply the translation to the currently active (top-of-stack) model view matrix
+ g_matrices.MatrixMode(MM_MODELVIEW);
+ g_matrices.PushMatrix();
+@@ -212,6 +216,8 @@ void CGUIFontTTFGL::LastEnd()
+
+ g_matrices.PopMatrix();
+ }
++ // Restore the original scissor rectangle
++ g_graphicsContext.ResetScissors();
+ // Restore the original model view matrix
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+ }
+--
+1.9.3
+
+
+From f39c4523a1c05425fb94d3536a510709784f9558 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 15 Jan 2014 15:32:51 +0000
+Subject: [PATCH 29/94] Move the vertex data across to a vertex buffer object
+ just prior to drawing.
+
+---
+ xbmc/guilib/GUIFontTTFGL.cpp | 24 +++++++++++++++++++-----
+ 1 file changed, 19 insertions(+), 5 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index b7618e1..0df3749 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -207,12 +207,24 @@ void CGUIFontTTFGL::LastEnd()
+ }
+ SVertex *vertices = &vecVertices[0];
+
+- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
+- // Normalize color values. Does not affect Performance at all.
+- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
+- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
+-
++ // Generate a unique buffer object name and put it in vertexBuffer
++ GLuint vertexBuffer;
++ glGenBuffers(1, &vertexBuffer);
++ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
++ glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
++ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
++ // binding point (i.e. our buffer object) and initialise it from the
++ // specified client-side pointer
++ glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW);
++ // Set up the offsets of the various vertex attributes within the buffer
++ // object bound to GL_ARRAY_BUFFER
++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x));
++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r));
++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u));
++ // Do the actual drawing operation, using the full set of vertices in the buffer
+ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
++ // Release the buffer name for reuse
++ glDeleteBuffers(1, &vertexBuffer);
+
+ g_matrices.PopMatrix();
+ }
+@@ -220,6 +232,8 @@ void CGUIFontTTFGL::LastEnd()
+ g_graphicsContext.ResetScissors();
+ // Restore the original model view matrix
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
++ // Unbind GL_ARRAY_BUFFER
++ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ // Disable the attributes used by this shader
+--
+1.9.3
+
+
+From 3c6c1c4f9c7aec0f41fd22d7d4e7f5918a70c69a Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 15 Jan 2014 16:04:04 +0000
+Subject: [PATCH 30/94] Move vertex data into an OpenGL VBO when the font cache
+ entry is populated. The font cache now stores the "name" (handle) of the VBO,
+ rather than a vector of vertices.
+
+---
+ xbmc/guilib/GUIFontCache.cpp | 6 ++++
+ xbmc/guilib/GUIFontCache.h | 30 +++++++++++++++++-
+ xbmc/guilib/GUIFontTTF.cpp | 15 +++++++--
+ xbmc/guilib/GUIFontTTF.h | 7 +++--
+ xbmc/guilib/GUIFontTTFGL.cpp | 74 ++++++++++++++++++++++++++++++--------------
+ xbmc/guilib/GUIFontTTFGL.h | 5 +++
+ 6 files changed, 107 insertions(+), 30 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
+index b66c00b..895fa72 100644
+--- a/xbmc/guilib/GUIFontCache.cpp
++++ b/xbmc/guilib/GUIFontCache.cpp
+@@ -111,3 +111,9 @@ template void CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDyna
+ template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry();
+ template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
+ template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush();
++
++void CVertexBuffer::clear()
++{
++ if (m_font != NULL)
++ m_font->DestroyVertexBuffer(*this);
++}
+diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h
+index d913dee..ff766bf 100644
+--- a/xbmc/guilib/GUIFontCache.h
++++ b/xbmc/guilib/GUIFontCache.h
+@@ -234,7 +234,35 @@ struct CGUIFontCacheDynamicPosition
+ }
+ };
+
+-typedef std::vector<SVertex> CGUIFontCacheDynamicValue;
++struct CVertexBuffer
++{
++ void *bufferHandle;
++ size_t size;
++ CVertexBuffer() : bufferHandle(NULL), size(0), m_font(NULL) {}
++ CVertexBuffer(void *bufferHandle, size_t size, const CGUIFontTTFBase *font) : bufferHandle(bufferHandle), size(size), m_font(font) {}
++ CVertexBuffer(const CVertexBuffer &other) : bufferHandle(other.bufferHandle), size(other.size), m_font(other.m_font)
++ {
++ /* In practice, the copy constructor is only called before a vertex buffer
++ * has been attached. If this should ever change, we'll need another support
++ * function in GUIFontTTFGL/DX to duplicate a buffer, given its handle. */
++ assert(other.bufferHandle == 0);
++ }
++ CVertexBuffer &operator=(CVertexBuffer &other)
++ {
++ /* This is used with move-assignment semantics for initialising the object in the font cache */
++ assert(bufferHandle == 0);
++ bufferHandle = other.bufferHandle;
++ other.bufferHandle = 0;
++ size = other.size;
++ m_font = other.m_font;
++ return *this;
++ }
++ void clear();
++private:
++ const CGUIFontTTFBase *m_font;
++};
++
++typedef CVertexBuffer CGUIFontCacheDynamicValue;
+
+ inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m,
+ const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m,
+diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
+index 4dc4c8e..8b25306 100644
+--- a/xbmc/guilib/GUIFontTTF.cpp
++++ b/xbmc/guilib/GUIFontTTF.cpp
+@@ -343,13 +343,18 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ g_graphicsContext.ScaleFinalYCoord(x, y),
+ g_graphicsContext.ScaleFinalZCoord(x, y));
+ }
+- std::vector<SVertex> &vertices = hardwareClipping ?
++ CVertexBuffer unusedVertexBuffer;
++ CVertexBuffer &vertexBuffer = hardwareClipping ?
+ m_dynamicCache.Lookup(dynamicPos,
+ colors, text,
+ alignment, maxPixelWidth,
+ scrolling,
+ XbmcThreads::SystemClockMillis(),
+ dirtyCache) :
++ unusedVertexBuffer;
++ std::vector<SVertex> tempVertices;
++ std::vector<SVertex> &vertices = hardwareClipping ?
++ tempVertices :
+ m_staticCache.Lookup(staticPos,
+ colors, text,
+ alignment, maxPixelWidth,
+@@ -459,10 +464,14 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
+ cursorX += ch->advance;
+ }
+ if (hardwareClipping)
+- m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion()));
++ {
++ CVertexBuffer newVertexBuffer = CreateVertexBuffer(tempVertices);
++ vertexBuffer = newVertexBuffer;
++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertexBuffer, g_graphicsContext.GetClipRegion()));
++ }
+ }
+ else if (hardwareClipping)
+- m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion()));
++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertexBuffer, g_graphicsContext.GetClipRegion()));
+ if (!hardwareClipping)
+ /* Append the new vertices (from the cache or otherwise) to the set collected
+ * since the first Begin() call */
+diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
+index 5e7c31f..b1cd525 100644
+--- a/xbmc/guilib/GUIFontTTF.h
++++ b/xbmc/guilib/GUIFontTTF.h
+@@ -84,6 +84,9 @@ class CGUIFontTTFBase
+
+ void Begin();
+ void End();
++ /* The next two should only be called if we've declared we can do hardware clipping */
++ virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const { assert(false); return CVertexBuffer(); }
++ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const {}
+
+ const CStdString& GetFileName() const { return m_strFileName; };
+
+@@ -167,9 +170,9 @@ class CGUIFontTTFBase
+ float translateX;
+ float translateY;
+ float translateZ;
+- const std::vector<SVertex> *vertexBuffer;
++ const CVertexBuffer *vertexBuffer;
+ CRect clip;
+- CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {}
++ CTranslatedVertices(float translateX, float translateY, float translateZ, const CVertexBuffer *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {}
+ };
+ std::vector<CTranslatedVertices> m_vertexTrans;
+ std::vector<SVertex> m_vertex;
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index 0df3749..1cd684b7 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -49,6 +49,10 @@ CGUIFontTTFGL::CGUIFontTTFGL(const CStdString& strFileName)
+
+ CGUIFontTTFGL::~CGUIFontTTFGL(void)
+ {
++ // It's important that all the CGUIFontCacheEntry objects are
++ // destructed before the CGUIFontTTFGL goes out of scope, because
++ // our virtual methods won't be accessible after this point
++ m_dynamicCache.Flush();
+ }
+
+ bool CGUIFontTTFGL::FirstBegin()
+@@ -182,7 +186,6 @@ void CGUIFontTTFGL::LastEnd()
+ if (m_vertexTrans.size() > 0)
+ {
+ // Deal with the vertices that can be hardware clipped and therefore translated
+- std::vector<SVertex> vecVertices;
+ for (size_t i = 0; i < m_vertexTrans.size(); i++)
+ {
+ // Apply the clip rectangle
+@@ -195,36 +198,17 @@ void CGUIFontTTFGL::LastEnd()
+ g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+
+- vecVertices.clear();
+- for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4)
+- {
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]);
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]);
+- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
+- }
+- SVertex *vertices = &vecVertices[0];
+-
+- // Generate a unique buffer object name and put it in vertexBuffer
+- GLuint vertexBuffer;
+- glGenBuffers(1, &vertexBuffer);
+ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
+- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
+- // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
+- // binding point (i.e. our buffer object) and initialise it from the
+- // specified client-side pointer
+- glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW);
++ glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle);
++
+ // Set up the offsets of the various vertex attributes within the buffer
+ // object bound to GL_ARRAY_BUFFER
+ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x));
+ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r));
+ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u));
++
+ // Do the actual drawing operation, using the full set of vertices in the buffer
+- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+- // Release the buffer name for reuse
+- glDeleteBuffers(1, &vertexBuffer);
++ glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size);
+
+ g_matrices.PopMatrix();
+ }
+@@ -245,6 +229,48 @@ void CGUIFontTTFGL::LastEnd()
+ #endif
+ }
+
++#if HAS_GLES
++CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
++{
++ // Rearrange the vertices to describe triangles
++ std::vector<SVertex> triangleVertices;
++ triangleVertices.reserve(vertices.size() * 6 / 4);
++ for (size_t i = 0; i < vertices.size(); i += 4)
++ {
++ triangleVertices.push_back(vertices[i]);
++ triangleVertices.push_back(vertices[i+1]);
++ triangleVertices.push_back(vertices[i+2]);
++ triangleVertices.push_back(vertices[i+1]);
++ triangleVertices.push_back(vertices[i+3]);
++ triangleVertices.push_back(vertices[i+2]);
++ }
++
++ // Generate a unique buffer object name and put it in bufferHandle
++ GLuint bufferHandle;
++ glGenBuffers(1, &bufferHandle);
++ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
++ glBindBuffer(GL_ARRAY_BUFFER, bufferHandle);
++ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
++ // binding point (i.e. our buffer object) and initialise it from the
++ // specified client-side pointer
++ glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW);
++ // Unbind GL_ARRAY_BUFFER
++ glBindBuffer(GL_ARRAY_BUFFER, 0);
++
++ return CVertexBuffer((void *) bufferHandle, vertices.size() / 4, this);
++}
++
++void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer &buffer) const
++{
++ if (buffer.bufferHandle != 0)
++ {
++ // Release the buffer name for reuse
++ glDeleteBuffers(1, (GLuint *) &buffer.bufferHandle);
++ buffer.bufferHandle = 0;
++ }
++}
++#endif
++
+ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
+ {
+ newHeight = CBaseTexture::PadPow2(newHeight);
+diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h
+index 6736cf7..168fb21 100644
+--- a/xbmc/guilib/GUIFontTTFGL.h
++++ b/xbmc/guilib/GUIFontTTFGL.h
+@@ -29,6 +29,7 @@
+
+
+ #include "GUIFontTTF.h"
++#include "system.h"
+
+
+ /*!
+@@ -43,6 +44,10 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
+
+ virtual bool FirstBegin();
+ virtual void LastEnd();
++#if HAS_GLES
++ virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const;
++ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const;
++#endif
+
+ protected:
+ virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);
+--
+1.9.3
+
+
+From 073c09ba7de6f6b7676c83d71b6933790626874f Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Thu, 16 Jan 2014 16:29:42 +0000
+Subject: [PATCH 31/94] Switch from glDrawArrays() to glDrawElements(). This
+ involves setting up a static VBO containing the indexes necessary to convert
+ from quads to triangles on the fly in the GPU.
+
+---
+ xbmc/guilib/GUIFontTTFGL.cpp | 72 +++++++++++++++++++++++++------------
+ xbmc/guilib/GUIFontTTFGL.h | 9 +++++
+ xbmc/windowing/egl/WinSystemEGL.cpp | 17 +++++++++
+ 3 files changed, 76 insertions(+), 22 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index 1cd684b7..d476409 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -186,6 +186,10 @@ void CGUIFontTTFGL::LastEnd()
+ if (m_vertexTrans.size() > 0)
+ {
+ // Deal with the vertices that can be hardware clipped and therefore translated
++
++ // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER
++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
++
+ for (size_t i = 0; i < m_vertexTrans.size(); i++)
+ {
+ // Apply the clip rectangle
+@@ -201,14 +205,21 @@ void CGUIFontTTFGL::LastEnd()
+ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
+ glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle);
+
+- // Set up the offsets of the various vertex attributes within the buffer
+- // object bound to GL_ARRAY_BUFFER
+- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x));
+- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r));
+- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u));
++ // Do the actual drawing operation, split into groups of characters no
++ // larger than the pre-determined size of the element array
++ for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
++ {
++ size_t count = m_vertexTrans[i].vertexBuffer->size - character;
++ count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
++
++ // Set up the offsets of the various vertex attributes within the buffer
++ // object bound to GL_ARRAY_BUFFER
++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, x)));
++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, r)));
++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, u)));
+
+- // Do the actual drawing operation, using the full set of vertices in the buffer
+- glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size);
++ glDrawElements(GL_TRIANGLES, 6 * count, GL_UNSIGNED_SHORT, 0);
++ }
+
+ g_matrices.PopMatrix();
+ }
+@@ -216,8 +227,9 @@ void CGUIFontTTFGL::LastEnd()
+ g_graphicsContext.ResetScissors();
+ // Restore the original model view matrix
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+- // Unbind GL_ARRAY_BUFFER
++ // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ // Disable the attributes used by this shader
+@@ -232,19 +244,6 @@ void CGUIFontTTFGL::LastEnd()
+ #if HAS_GLES
+ CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
+ {
+- // Rearrange the vertices to describe triangles
+- std::vector<SVertex> triangleVertices;
+- triangleVertices.reserve(vertices.size() * 6 / 4);
+- for (size_t i = 0; i < vertices.size(); i += 4)
+- {
+- triangleVertices.push_back(vertices[i]);
+- triangleVertices.push_back(vertices[i+1]);
+- triangleVertices.push_back(vertices[i+2]);
+- triangleVertices.push_back(vertices[i+1]);
+- triangleVertices.push_back(vertices[i+3]);
+- triangleVertices.push_back(vertices[i+2]);
+- }
+-
+ // Generate a unique buffer object name and put it in bufferHandle
+ GLuint bufferHandle;
+ glGenBuffers(1, &bufferHandle);
+@@ -253,7 +252,7 @@ CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vert
+ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
+ // binding point (i.e. our buffer object) and initialise it from the
+ // specified client-side pointer
+- glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW);
++ glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof (SVertex), &vertices[0], GL_STATIC_DRAW);
+ // Unbind GL_ARRAY_BUFFER
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+@@ -348,4 +347,33 @@ void CGUIFontTTFGL::DeleteHardwareTexture()
+ }
+ }
+
++#if HAS_GLES
++void CGUIFontTTFGL::CreateStaticVertexBuffers(void)
++{
++ // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
++ glGenBuffers(1, &m_elementArrayHandle);
++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
++ // Create an array holding the mesh indices to convert quads to triangles
++ GLushort index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6];
++ for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++)
++ {
++ index[i][0] = 4*i;
++ index[i][1] = 4*i+1;
++ index[i][2] = 4*i+2;
++ index[i][3] = 4*i+1;
++ index[i][4] = 4*i+3;
++ index[i][5] = 4*i+2;
++ }
++ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof index, index, GL_STATIC_DRAW);
++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
++}
++
++void CGUIFontTTFGL::DestroyStaticVertexBuffers(void)
++{
++ glDeleteBuffers(1, &m_elementArrayHandle);
++}
++
++GLuint CGUIFontTTFGL::m_elementArrayHandle;
++#endif
++
+ #endif
+diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h
+index 168fb21..a14ab7a 100644
+--- a/xbmc/guilib/GUIFontTTFGL.h
++++ b/xbmc/guilib/GUIFontTTFGL.h
+@@ -30,6 +30,7 @@
+
+ #include "GUIFontTTF.h"
+ #include "system.h"
++#include "system_gl.h"
+
+
+ /*!
+@@ -47,6 +48,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
+ #if HAS_GLES
+ virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const;
+ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const;
++ static void CreateStaticVertexBuffers(void);
++ static void DestroyStaticVertexBuffers(void);
+ #endif
+
+ protected:
+@@ -54,6 +57,12 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
+ virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2);
+ virtual void DeleteHardwareTexture();
+
++#if HAS_GLES
++#define ELEMENT_ARRAY_MAX_CHAR_INDEX (1000)
++
++ static GLuint m_elementArrayHandle;
++#endif
++
+ };
+
+ #endif
+diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp
+index dfc4672..0c32947 100644
+--- a/xbmc/windowing/egl/WinSystemEGL.cpp
++++ b/xbmc/windowing/egl/WinSystemEGL.cpp
+@@ -29,6 +29,7 @@
+ #include "settings/AdvancedSettings.h"
+ #include "settings/Settings.h"
+ #include "settings/DisplaySettings.h"
++#include "guilib/GUIFontTTFGL.h"
+ #include "utils/log.h"
+ #include "EGLWrapper.h"
+ #include "EGLQuirks.h"
+@@ -192,6 +193,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
+ return false;
+ }
+
++#if HAS_GLES
++ bool newContext = false;
++#endif
+ if (m_context == EGL_NO_CONTEXT)
+ {
+ if (!m_egl->CreateContext(m_display, m_config, contextAttrs, &m_context))
+@@ -199,6 +203,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
+ CLog::Log(LOGERROR, "%s: Could not create context",__FUNCTION__);
+ return false;
+ }
++#if HAS_GLES
++ newContext = true;
++#endif
+ }
+
+ if (!m_egl->BindContext(m_display, m_surface, m_context))
+@@ -207,6 +214,11 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
+ return false;
+ }
+
++#if HAS_GLES
++ if (newContext)
++ CGUIFontTTFGL::CreateStaticVertexBuffers();
++#endif
++
+ // for the non-trivial dirty region modes, we need the EGL buffer to be preserved across updates
+ if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_COST_REDUCTION ||
+ g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_UNION)
+@@ -228,7 +240,12 @@ bool CWinSystemEGL::DestroyWindowSystem()
+ DestroyWindow();
+
+ if (m_context != EGL_NO_CONTEXT)
++ {
++#if HAS_GLES
++ CGUIFontTTFGL::DestroyStaticVertexBuffers();
++#endif
+ m_egl->DestroyContext(m_display, m_context);
++ }
+ m_context = EGL_NO_CONTEXT;
+
+ if (m_display != EGL_NO_DISPLAY)
+--
+1.9.3
+
+
+From ec39dce3628b276e3ed2fe19c95a056a1aa171b8 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 4 Feb 2014 16:17:57 +0000
+Subject: [PATCH 32/94] Update Windows project files
+
+---
+ project/VS2010Express/XBMC.vcxproj | 2 ++
+ project/VS2010Express/XBMC.vcxproj.filters | 6 ++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
+index c6de5ca..a9731de 100644
+--- a/project/VS2010Express/XBMC.vcxproj
++++ b/project/VS2010Express/XBMC.vcxproj
+@@ -540,6 +540,7 @@
+ <ClCompile Include="..\..\xbmc\guilib\GUIFadeLabelControl.cpp" />
+ <ClCompile Include="..\..\xbmc\guilib\GUIFixedListContainer.cpp" />
+ <ClCompile Include="..\..\xbmc\guilib\GUIFont.cpp" />
++ <ClCompile Include="..\..\xbmc\guilib\GUIFontCache.cpp" />
+ <ClCompile Include="..\..\xbmc\guilib\GUIFontManager.cpp" />
+ <ClCompile Include="..\..\xbmc\guilib\GUIFontTTF.cpp" />
+ <ClCompile Include="..\..\xbmc\guilib\GUIFontTTFDX.cpp" />
+@@ -2057,6 +2058,7 @@
+ <ClInclude Include="..\..\xbmc\guilib\GUIFadeLabelControl.h" />
+ <ClInclude Include="..\..\xbmc\guilib\GUIFixedListContainer.h" />
+ <ClInclude Include="..\..\xbmc\guilib\GUIFont.h" />
++ <ClInclude Include="..\..\xbmc\guilib\GUIFontCache.h" />
+ <ClInclude Include="..\..\xbmc\guilib\GUIFontManager.h" />
+ <ClInclude Include="..\..\xbmc\guilib\GUIFontTTF.h" />
+ <ClInclude Include="..\..\xbmc\guilib\GUIFontTTFDX.h" />
+diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
+index b536eb3..cb34443 100644
+--- a/project/VS2010Express/XBMC.vcxproj.filters
++++ b/project/VS2010Express/XBMC.vcxproj.filters
+@@ -1024,6 +1024,9 @@
+ <ClCompile Include="..\..\xbmc\guilib\GUIFont.cpp">
+ <Filter>guilib</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\guilib\GUIFontCache.cpp">
++ <Filter>guilib</Filter>
++ </ClCompile>
+ <ClCompile Include="..\..\xbmc\guilib\GUIFontManager.cpp">
+ <Filter>guilib</Filter>
+ </ClCompile>
+@@ -3978,6 +3981,9 @@
+ <ClInclude Include="..\..\xbmc\guilib\GUIFont.h">
+ <Filter>guilib</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\xbmc\guilib\GUIFontCache.h">
++ <Filter>guilib</Filter>
++ </ClInclude>
+ <ClInclude Include="..\..\xbmc\guilib\GUIFontManager.h">
+ <Filter>guilib</Filter>
+ </ClInclude>
+--
+1.9.3
+
+
+From e25eb385d09a5378be8616f10806610df90416db Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 4 Feb 2014 16:49:45 +0000
+Subject: [PATCH 33/94] Update XCode project file
+
+---
+ XBMC.xcodeproj/project.pbxproj | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj
+index fdd10a1..62e7e69 100644
+--- a/XBMC.xcodeproj/project.pbxproj
++++ b/XBMC.xcodeproj/project.pbxproj
+@@ -168,6 +168,9 @@
+ 1D638128161E211E003603ED /* PeripheralImon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D638126161E211E003603ED /* PeripheralImon.cpp */; };
+ 1DAFDB7C16DFDCA7007F8C68 /* PeripheralBusCEC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DAFDB7A16DFDCA7007F8C68 /* PeripheralBusCEC.cpp */; };
+ 1DE0443515828F4B005DDB4D /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; };
++ 2FD7EC5F18A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; };
++ 2FD7EC6018A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; };
++ 2FD7EC6118A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; };
+ 32C631281423A90F00F18420 /* JpegIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32C631261423A90F00F18420 /* JpegIO.cpp */; };
+ 36A9443D15821E2800727135 /* DatabaseUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443B15821E2800727135 /* DatabaseUtils.cpp */; };
+ 36A9444115821E7C00727135 /* SortUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443F15821E7C00727135 /* SortUtils.cpp */; };
+@@ -3546,6 +3549,8 @@
+ 1DAFDB7B16DFDCA7007F8C68 /* PeripheralBusCEC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeripheralBusCEC.h; sourceTree = "<group>"; };
+ 1DE0443315828F4B005DDB4D /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Exception.cpp; path = commons/Exception.cpp; sourceTree = "<group>"; };
+ 1DE0443415828F4B005DDB4D /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = commons/Exception.h; sourceTree = "<group>"; };
++ 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIFontCache.cpp; sourceTree = "<group>"; };
++ 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIFontCache.h; sourceTree = "<group>"; };
+ 32C631261423A90F00F18420 /* JpegIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JpegIO.cpp; sourceTree = "<group>"; };
+ 32C631271423A90F00F18420 /* JpegIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JpegIO.h; sourceTree = "<group>"; };
+ 36A9443B15821E2800727135 /* DatabaseUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseUtils.cpp; sourceTree = "<group>"; };
+@@ -5923,6 +5928,8 @@
+ 18B7C76A1294222E009E7A26 /* GUIFixedListContainer.cpp */,
+ 18B7C7101294222D009E7A26 /* GUIFixedListContainer.h */,
+ 18B7C76B1294222E009E7A26 /* GUIFont.cpp */,
++ 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */,
++ 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */,
+ 18B7C7111294222D009E7A26 /* GUIFont.h */,
+ 18B7C76C1294222E009E7A26 /* GUIFontManager.cpp */,
+ 18B7C7121294222D009E7A26 /* GUIFontManager.h */,
+@@ -10930,6 +10937,7 @@
+ 7C8AE850189DE3CD00C33786 /* CoreAudioHardware.cpp in Sources */,
+ 7C8AE851189DE3CD00C33786 /* CoreAudioStream.cpp in Sources */,
+ 7C8AE854189DE47F00C33786 /* CoreAudioHelpers.cpp in Sources */,
++ 2FD7EC5F18A14FE50047F86C /* GUIFontCache.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+@@ -11978,6 +11986,7 @@
+ F5CC234818150277006B5E91 /* AESinkNULL.cpp in Sources */,
+ F5CC238918150768006B5E91 /* AESinkProfiler.cpp in Sources */,
+ DF374B2518AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */,
++ 2FD7EC6118A14FE50047F86C /* GUIFontCache.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+@@ -13028,6 +13037,7 @@
+ F5CC234718150277006B5E91 /* AESinkNULL.cpp in Sources */,
+ F5CC238818150768006B5E91 /* AESinkProfiler.cpp in Sources */,
+ DF374B2418AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */,
++ 2FD7EC6018A14FE50047F86C /* GUIFontCache.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+--
+1.9.3
+
+
+From 5f4ebd2e9fd6d503220627b916e522b671d7d9ba Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 4 Feb 2014 17:44:34 +0000
+Subject: [PATCH 34/94] Clang seems to be more picky than gcc about some C++
+ template syntax
+
+---
+ xbmc/guilib/GUIFontCache.cpp | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
+index 895fa72..bd84b9a 100644
+--- a/xbmc/guilib/GUIFontCache.cpp
++++ b/xbmc/guilib/GUIFontCache.cpp
+@@ -61,26 +61,26 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
+ alignment, maxPixelWidth,
+ scrolling, g_graphicsContext.GetGUIMatrix(),
+ g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY());
+- EntryHashIterator i = m_list.get<Hash>().find(key);
+- if (i == m_list.get<Hash>().end())
++ EntryHashIterator i = m_list.template get<Hash>().find(key);
++ if (i == m_list.template get<Hash>().end())
+ {
+ /* Cache miss */
+- EntryAgeIterator oldest = m_list.get<Age>().begin();
+- if (!m_list.get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT)
++ EntryAgeIterator oldest = m_list.template get<Age>().begin();
++ if (!m_list.template get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT)
+ {
+ /* The oldest existing entry is old enough to expire and reuse */
+- m_list.get<Hash>().modify(m_list.project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis));
+- m_list.get<Age>().relocate(m_list.get<Age>().end(), oldest);
++ m_list.template get<Hash>().modify(m_list.template project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis));
++ m_list.template get<Age>().relocate(m_list.template get<Age>().end(), oldest);
+ }
+ else
+ {
+ /* We need a new entry instead */
+ /* Yes, this causes the creation an destruction of a temporary entry, but
+ * this code ought to only be used infrequently, when the cache needs to grow */
+- m_list.get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis));
++ m_list.template get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis));
+ }
+ dirtyCache = true;
+- return (--m_list.get<Age>().end())->m_value;
++ return (--m_list.template get<Age>().end())->m_value;
+ }
+ else
+ {
+@@ -90,7 +90,7 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
+ pos.UpdateWithOffsets(i->m_key.m_pos, scrolling);
+ /* Update time in entry and move to the back of the list */
+ i->m_lastUsedMillis = nowMillis;
+- m_list.get<Age>().relocate(m_list.get<Age>().end(), m_list.project<Age>(i));
++ m_list.template get<Age>().relocate(m_list.template get<Age>().end(), m_list.template project<Age>(i));
+ dirtyCache = false;
+ return i->m_value;
+ }
+@@ -99,7 +99,7 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
+ template<class Position, class Value>
+ void CGUIFontCache<Position, Value>::Flush()
+ {
+- m_list.get<Age>().clear();
++ m_list.template get<Age>().clear();
+ }
+
+ template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> &entry);
+--
+1.9.3
+
+
+From 33693dc9ff9ba7695bc0e702a41566d54a5135b9 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 4 Feb 2014 18:52:14 +0000
+Subject: [PATCH 35/94] Fix header to hopefully permit iOS builds to work
+ again. GUIShader.cpp added #include windowing/egl/WinSystemEGL.h inside a but
+ also need the header windowing/osx/WinSystemIOS.h instead. The only thing
+ GUIShader.cpp needed was g_windowing.GetViewPort, which is provided by the
+ common base class CRenderSystemGLES of g_windowing in both cases, so I think
+ it should be sufficient to use windowing/WindowingFactory.h instead, which is
+ abstracted away from the other header files.
+
+---
+ xbmc/guilib/GUIShader.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp
+index 53bce09..86330cc 100644
+--- a/xbmc/guilib/GUIShader.cpp
++++ b/xbmc/guilib/GUIShader.cpp
+@@ -26,7 +26,7 @@
+ #include "GUIShader.h"
+ #include "MatrixGLES.h"
+ #include "utils/log.h"
+-#include "windowing/egl/WinSystemEGL.h"
++#include "windowing/WindowingFactory.h"
+ #include "guilib/GraphicContext.h"
+
+ CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_vert.glsl", shader)
+--
+1.9.3
+
+
+From 9b95b3b13bd714d8320dc61c5039eee6cb3c5737 Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Tue, 8 Apr 2014 18:14:55 +0100
+Subject: [PATCH 36/94] Fix font display in stereoscopic modes
+ CGUIFontTTFGL::LastEnd was previously using the relatively high-level
+ CGraphicContext::SetScissors function to enforce hardware clipping. However,
+ the coordinates it passed in already contained the stereoscopic offset, so
+ the CGraphicContext::SetScissors effectively ended up double-applying the
+ offset, with the effect that clip rectangles were always off-screen. Changed
+ to call the low-level SetScissors call instead (using g_Windowing to select
+ the correct implementation, e.g. CRenderSystemGLES::SetScissors). This also
+ skips the intersection of the scissors with the screen limits, but that does
+ not appear to matter in practice.
+
+---
+ xbmc/guilib/GUIFontTTFGL.cpp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
+index d476409..8466a81 100644
+--- a/xbmc/guilib/GUIFontTTFGL.cpp
++++ b/xbmc/guilib/GUIFontTTFGL.cpp
+@@ -194,7 +194,7 @@ void CGUIFontTTFGL::LastEnd()
+ {
+ // Apply the clip rectangle
+ CRect clip = g_Windowing.ClipRectToScissorRect(m_vertexTrans[i].clip);
+- g_graphicsContext.SetScissors(clip);
++ g_Windowing.SetScissors(clip);
+
+ // Apply the translation to the currently active (top-of-stack) model view matrix
+ g_matrices.MatrixMode(MM_MODELVIEW);
+@@ -224,7 +224,7 @@ void CGUIFontTTFGL::LastEnd()
+ g_matrices.PopMatrix();
+ }
+ // Restore the original scissor rectangle
+- g_graphicsContext.ResetScissors();
++ g_Windowing.ResetScissors();
+ // Restore the original model view matrix
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+ // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
+--
+1.9.3
+
+
+From d51ef43b61b50de46edb2832f457af8229995cab Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Fri, 10 Jan 2014 12:10:43 +0000
+Subject: [PATCH 37/94] [rbp] Don't override dvdplayer with omxplayer.
+
+Using dvdplayer can be useful on the Pi. We can actually play sd (up to 640x480 MPEG-4 video) video in real time.
+This is useful for codec variants like DivX3 which we don't currently play.
+
+This may expose bugs where dvdplayer is incorrectly used as the default player which will need to be fixed
+---
+ xbmc/cores/playercorefactory/PlayerCoreConfig.h | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h
+index 27f0bec..fc12bb7 100644
+--- a/xbmc/cores/playercorefactory/PlayerCoreConfig.h
++++ b/xbmc/cores/playercorefactory/PlayerCoreConfig.h
+@@ -88,14 +88,7 @@ friend class CPlayerCoreFactory;
+ {
+ case EPC_MPLAYER:
+ // TODO: this hack needs removal until we have a better player selection
+-#if defined(HAS_OMXPLAYER)
+- case EPC_DVDPLAYER:
+- pPlayer = new COMXPlayer(callback);
+- CLog::Log(LOGINFO, "Created player %s for core %d / OMXPlayer forced as DVDPlayer", "OMXPlayer", m_eCore);
+- break;
+-#else
+ case EPC_DVDPLAYER: pPlayer = new CDVDPlayer(callback); break;
+-#endif
+ case EPC_PAPLAYER: pPlayer = new PAPlayer(callback); break;
+ case EPC_EXTPLAYER: pPlayer = new CExternalPlayer(callback); break;
+ #if defined(HAS_OMXPLAYER)
+--
+1.9.3
+
+
+From 3cd01da5418f1693e50ed40273cb17ca9f6d622a Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Fri, 10 Jan 2014 15:37:41 +0000
+Subject: [PATCH 38/94] [players] Use default players rather than hard coded
+ DVDPlayer/PAPlayer
+
+---
+ system/playercorefactory.xml | 23 ++++++++++++-----------
+ 1 file changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml
+index 57dfcdd..7be9799 100644
+--- a/system/playercorefactory.xml
++++ b/system/playercorefactory.xml
+@@ -11,31 +11,32 @@
+ </players>
+
+ <rules name="system rules">
+- <rule name="rtv" protocols="rtv" player="DVDPlayer" />
+- <rule name="hdhomerun/myth/mms/udp" protocols="hdhomerun|myth|cmyth|mms|mmsh|udp" player="DVDPlayer" />
+- <rule name="lastfm/shout" protocols="lastfm|shout" player="PAPlayer" />
++ <rule name="rtv" protocols="rtv" player="videodefaultplayer" />
++ <rule name="hdhomerun/myth/mms/udp" protocols="hdhomerun|myth|cmyth|mms|mmsh|udp" player="videodefaultplayer" />
++ <rule name="lastfm/shout" protocols="lastfm|shout" player="audiodefaultplayer" />
+ <rule name="rtmp" protocols="rtmp" player="videodefaultplayer" />
+
+ <!-- dvdplayer can play standard rtsp streams -->
+- <rule name="rtsp" protocols="rtsp" filetypes="!(rm|ra)" player="PAPlayer" />
++ <rule name="rtsp" protocols="rtsp" filetypes="!(rm|ra)" player="audiodefaultplayer" />
+
+ <!-- Internet streams -->
+ <rule name="streams" internetstream="true">
+- <rule name="aacp/sdp" mimetypes="audio/aacp|application/sdp" player="DVDPlayer" />
+- <rule name="mp2" mimetypes="application/octet-stream" filetypes="mp2" player="PAPlayer" />
++ <rule name="aacp/sdp" mimetypes="audio/aacp|application/sdp" player="videodefaultplayer" />
++ <rule name="mp2" mimetypes="application/octet-stream" filetypes="mp2" player="audiodefaultplayer" />
+ </rule>
+
+ <!-- DVDs -->
+- <rule name="dvd" dvd="true" player="DVDPlayer" />
+- <rule name="dvdimage" dvdimage="true" player="DVDPlayer" />
++ <rule name="dvd" dvd="true" player="videodefaultdvdplayer" />
++ <rule name="dvdfile" dvdfile="true" player="videodefaultdvdplayer" />
++ <rule name="dvdimage" dvdimage="true" player="videodefaultdvdplayer" />
+
+ <!-- Only dvdplayer can handle these normally -->
+- <rule name="sdp/asf" filetypes="sdp|asf" player="DVDPlayer" />
++ <rule name="sdp/asf" filetypes="sdp|asf" player="videodefaultplayer" />
+
+ <!-- Pass these to dvdplayer as we do not know if they are audio or video -->
+- <rule name="nsv" filetypes="nsv" player="DVDPlayer" />
++ <rule name="nsv" filetypes="nsv" player="videodefaultplayer" />
+
+ <!-- pvr radio channels should be played by dvdplayer because they need buffering -->
+- <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="DVDPlayer" />
++ <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="videodefaultplayer" />
+ </rules>
+ </playercorefactory>
+--
+1.9.3
+
+
+From 5c4de293325bba93c4b820aed6863ee8d732ce2c Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Sat, 11 Jan 2014 18:23:42 +0000
+Subject: [PATCH 39/94] [rbp] Don't force dvdplayer for airplay
+
+---
+ xbmc/network/AirPlayServer.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/xbmc/network/AirPlayServer.cpp b/xbmc/network/AirPlayServer.cpp
+index 8040d9b..182daaa 100644
+--- a/xbmc/network/AirPlayServer.cpp
++++ b/xbmc/network/AirPlayServer.cpp
+@@ -906,9 +906,11 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
+ CFileItem fileToPlay(location, false);
+ fileToPlay.SetProperty("StartPercent", position*100.0f);
+ ServerInstance->AnnounceToClients(EVENT_LOADING);
++#ifndef TARGET_RASPBERRY_PI
+ // froce to internal dvdplayer cause it is the only
+ // one who will work well with airplay
+ g_application.m_eForcedNextPlayer = EPC_DVDPLAYER;
++#endif
+ CApplicationMessenger::Get().MediaPlay(fileToPlay);
+ }
+ }
+--
+1.9.3
+
+
+From 454b77543018aac5d7e70769ef13231ae16eefb9 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 13 Jan 2014 13:11:06 +0000
+Subject: [PATCH 40/94] [rbp] Give plugins omxplayer when they request
+ dvdplayer on pi
+
+---
+ xbmc/interfaces/legacy/ModuleXbmc.cpp | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/xbmc/interfaces/legacy/ModuleXbmc.cpp b/xbmc/interfaces/legacy/ModuleXbmc.cpp
+index 16f0174..b172d47 100644
+--- a/xbmc/interfaces/legacy/ModuleXbmc.cpp
++++ b/xbmc/interfaces/legacy/ModuleXbmc.cpp
+@@ -536,7 +536,11 @@ namespace XBMCAddon
+ int getPLAYLIST_MUSIC() { return PLAYLIST_MUSIC; }
+ int getPLAYLIST_VIDEO() { return PLAYLIST_VIDEO; }
+ int getPLAYER_CORE_AUTO() { return EPC_NONE; }
++#ifdef TARGET_RASPBERRY_PI
++ int getPLAYER_CORE_DVDPLAYER() { return EPC_OMXPLAYER; }
++#else
+ int getPLAYER_CORE_DVDPLAYER() { return EPC_DVDPLAYER; }
++#endif
+ int getPLAYER_CORE_MPLAYER() { return EPC_MPLAYER; }
+ int getPLAYER_CORE_PAPLAYER() { return EPC_PAPLAYER; }
+ int getTRAY_OPEN() { return TRAY_OPEN; }
+--
+1.9.3
+
+
+From 7201ee3e0ca664518eaaf3142682b294c34c69c0 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Tue, 14 Jan 2014 18:04:07 +0000
+Subject: [PATCH 41/94] [rbp] Allow ALSA to be chosen in addition to Pi sink
+
+Needs --enable-alsa in ./configure step and alsa support on platform
+---
+ configure.in | 1 -
+ tools/depends/target/Makefile | 6 +++---
+ xbmc/cores/AudioEngine/AESinkFactory.cpp | 17 +++++++++++++++--
+ 3 files changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/configure.in b/configure.in
+index a195d00..34dd038 100644
+--- a/configure.in
++++ b/configure.in
+@@ -742,7 +742,6 @@ case $use_platform in
+ use_arch="arm"
+ use_cpu=arm1176jzf-s
+ use_hardcoded_tables="yes"
+- use_alsa="no"
+ ARCH="arm"
+ AC_DEFINE(HAS_EGLGLES, [1], [Define if supporting EGL based GLES Framebuffer])
+ USE_OMXLIB=1; AC_DEFINE([HAVE_OMXLIB],[1],["Define to 1 if OMX libs is enabled"])
+diff --git a/tools/depends/target/Makefile b/tools/depends/target/Makefile
+index 4588917..0fbd3e7 100644
+--- a/tools/depends/target/Makefile
++++ b/tools/depends/target/Makefile
+@@ -55,10 +55,10 @@ endif
+ ALSA_LIB=
+ LINUX_SYSTEM_LIBS=
+ ifeq ($(OS),linux)
+- #not for raspberry pi
++ DEPENDS += alsa-lib
++ ALSA_LIB = alsa-lib
+ ifneq ($(CPU),arm)
+- DEPENDS += alsa-lib libsdl linux-system-libs
+- ALSA_LIB = alsa-lib
++ DEPENDS += libsdl linux-system-libs
+ LINUX_SYSTEM_LIBS = linux-system-libs
+ endif
+ endif
+diff --git a/xbmc/cores/AudioEngine/AESinkFactory.cpp b/xbmc/cores/AudioEngine/AESinkFactory.cpp
+index e493123..7df6807 100644
+--- a/xbmc/cores/AudioEngine/AESinkFactory.cpp
++++ b/xbmc/cores/AudioEngine/AESinkFactory.cpp
+@@ -27,6 +27,7 @@
+ #include "Sinks/AESinkAUDIOTRACK.h"
+ #elif defined(TARGET_RASPBERRY_PI)
+ #include "Sinks/AESinkPi.h"
++ #include "Sinks/AESinkALSA.h"
+ #elif defined(TARGET_DARWIN_IOS)
+ #include "Sinks/AESinkDARWINIOS.h"
+ #elif defined(TARGET_DARWIN_OSX)
+@@ -66,6 +67,7 @@ void CAESinkFactory::ParseDevice(std::string &device, std::string &driver)
+ driver == "AUDIOTRACK" ||
+ #elif defined(TARGET_RASPBERRY_PI)
+ driver == "PI" ||
++ driver == "ALSA" ||
+ #elif defined(TARGET_DARWIN_IOS)
+ driver == "DARWINIOS" ||
+ #elif defined(TARGET_DARWIN_OSX)
+@@ -104,7 +106,12 @@ IAESink *CAESinkFactory::TrySink(std::string &driver, std::string &device, AEAud
+ #elif defined(TARGET_ANDROID)
+ sink = new CAESinkAUDIOTRACK();
+ #elif defined(TARGET_RASPBERRY_PI)
+- sink = new CAESinkPi();
++ else if (driver == "PI")
++ sink = new CAESinkPi();
++ #if defined(HAS_ALSA)
++ else if (driver == "ALSA")
++ sink = new CAESinkALSA();
++ #endif
+ #elif defined(TARGET_DARWIN_IOS)
+ sink = new CAESinkDARWINIOS();
+ #elif defined(TARGET_DARWIN_OSX)
+@@ -194,7 +201,13 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force)
+ CAESinkPi::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ if(!info.m_deviceInfoList.empty())
+ list.push_back(info);
+-
++ #if defined(HAS_ALSA)
++ info.m_deviceInfoList.clear();
++ info.m_sinkName = "ALSA";
++ CAESinkALSA::EnumerateDevicesEx(info.m_deviceInfoList, force);
++ if(!info.m_deviceInfoList.empty())
++ list.push_back(info);
++ #endif
+ #elif defined(TARGET_DARWIN_IOS)
+
+ info.m_deviceInfoList.clear();
+--
+1.9.3
+
+
+From 8ffa85ccdc4760751849d75d37924fbf6cb1b1c8 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Thu, 16 Jan 2014 01:39:29 +0000
+Subject: [PATCH 42/94] [omxcodec] Add hardware decode to dvdplayer for Pi
+
+Hijack the abandoned OpenMaxVideo codec
+---
+ configure.in | 21 +-
+ xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 12 +-
+ xbmc/cores/VideoRenderers/LinuxRendererGLES.h | 6 +-
+ xbmc/cores/VideoRenderers/RenderManager.cpp | 2 +-
+ xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp | 7 +-
+ .../dvdplayer/DVDCodecs/Video/DVDVideoCodec.h | 7 +-
+ .../DVDCodecs/Video/DVDVideoCodecOpenMax.cpp | 295 +---
+ .../DVDCodecs/Video/DVDVideoCodecOpenMax.h | 34 +-
+ xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in | 1 -
+ xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp | 269 ----
+ xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h | 116 --
+ .../dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 1418 ++++++++++----------
+ .../cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 120 +-
+ xbmc/cores/dvdplayer/DVDPlayer.cpp | 2 +
+ xbmc/cores/dvdplayer/DVDPlayerVideo.cpp | 21 +-
+ xbmc/linux/OMXCore.cpp | 45 +-
+ xbmc/linux/OMXCore.h | 2 +-
+ 17 files changed, 894 insertions(+), 1484 deletions(-)
+ delete mode 100644 xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp
+ delete mode 100644 xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h
+
+diff --git a/configure.in b/configure.in
+index 34dd038..e7550c0 100644
+--- a/configure.in
++++ b/configure.in
+@@ -1956,9 +1956,24 @@ if test "$host_vendor" = "apple" ; then
+ USE_OPENMAX=0
+ AC_MSG_NOTICE($openmax_disabled)
+ elif test "$target_platform" = "target_raspberry_pi"; then
+- use_openmax="no"
+- USE_OPENMAX=0
+- AC_MSG_NOTICE($openmax_disabled)
++ if test "$use_gles" = "yes" && test "$use_openmax" = "auto"; then
++ use_openmax="yes"
++ USE_OPENMAX=1
++ HAVE_LIBOPENMAX=1
++ AC_DEFINE([HAVE_LIBOPENMAX], [1], [Define to 1 if you have the 'LIBOPENMAX' library.])
++ AC_DEFINE([OMX_SKIP64BIT], [1], [Define to 1 if you have the 'LIBOPENMAX' library.])
++ AC_MSG_NOTICE($openmax_enabled)
++ elif test "$use_gles" = "yes" && test "$use_openmax" = "yes"; then
++ use_openmax="yes"
++ USE_OPENMAX=1
++ HAVE_LIBOPENMAX=1
++ AC_DEFINE([HAVE_LIBOPENMAX], [1], [Define to 1 if you have the 'LIBOPENMAX' library.])
++ AC_MSG_NOTICE($openmax_enabled)
++ else
++ AC_MSG_NOTICE($openmax_disabled)
++ use_openmax=no
++ USE_OPENMAX=0
++ fi
+ else
+ if test "$use_gles" = "yes" && test "$use_openmax" = "auto"; then
+ PKG_CHECK_MODULES([OPENMAX], [libomxil-bellagio],
+diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+index 30c0601..6d879e3 100644
+--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+@@ -44,7 +44,7 @@
+ #include "windowing/WindowingFactory.h"
+ #include "guilib/Texture.h"
+ #include "lib/DllSwScale.h"
+-#include "../dvdplayer/DVDCodecs/Video/OpenMaxVideo.h"
++#include "DVDCodecs/Video/OpenMaxVideo.h"
+ #include "threads/SingleLock.h"
+ #include "RenderCapture.h"
+ #include "RenderFormats.h"
+@@ -1330,6 +1330,10 @@ void CLinuxRendererGLES::RenderOpenMax(int index, int field)
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(m_textureTarget, textureId);
+
++ GLint filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR;
++ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter);
++ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter);
++
+ g_Windowing.EnableGUIShader(SM_TEXTURE_RGBA);
+
+ GLubyte idx[4] = {0, 1, 3, 2}; //determines order of triangle strip
+@@ -2676,10 +2680,12 @@ unsigned int CLinuxRendererGLES::GetProcessorSize()
+ }
+
+ #ifdef HAVE_LIBOPENMAX
+-void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index)
++void CLinuxRendererGLES::AddProcessor(COpenMaxVideoBuffer *openMaxBuffer, int index)
+ {
+ YUVBUFFER &buf = m_buffers[index];
+- buf.openMaxBuffer = picture->openMaxBuffer;
++ COpenMaxVideoBuffer *pic = openMaxBuffer->Acquire();
++ SAFE_RELEASE(buf.openMaxBuffer);
++ buf.openMaxBuffer = pic;
+ }
+ #endif
+ #ifdef HAVE_VIDEOTOOLBOXDECODER
+diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
+index 45e9c20..0ca56a2 100644
+--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
+@@ -39,7 +39,7 @@ class CRenderCapture;
+ class CBaseTexture;
+ namespace Shaders { class BaseYUV2RGBShader; }
+ namespace Shaders { class BaseVideoFilterShader; }
+-class COpenMaxVideo;
++class COpenMaxVideoBuffer;
+ class CDVDVideoCodecStageFright;
+ class CDVDMediaCodecInfo;
+ typedef std::vector<int> Features;
+@@ -161,7 +161,7 @@ class CLinuxRendererGLES : public CBaseRenderer
+ virtual std::vector<ERenderFormat> SupportedFormats() { return m_formats; }
+
+ #ifdef HAVE_LIBOPENMAX
+- virtual void AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index);
++ virtual void AddProcessor(COpenMaxVideoBuffer *openMaxVideoBuffer, int index);
+ #endif
+ #ifdef HAVE_VIDEOTOOLBOXDECODER
+ virtual void AddProcessor(struct __CVBuffer *cvBufferRef, int index);
+@@ -275,7 +275,7 @@ class CLinuxRendererGLES : public CBaseRenderer
+ unsigned flipindex; /* used to decide if this has been uploaded */
+
+ #ifdef HAVE_LIBOPENMAX
+- OpenMaxVideoBuffer *openMaxBuffer;
++ COpenMaxVideoBuffer *openMaxBuffer;
+ #endif
+ #ifdef HAVE_VIDEOTOOLBOXDECODER
+ struct __CVBuffer *cvBufferRef;
+diff --git a/xbmc/cores/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoRenderers/RenderManager.cpp
+index 6832721..3503988 100644
+--- a/xbmc/cores/VideoRenderers/RenderManager.cpp
++++ b/xbmc/cores/VideoRenderers/RenderManager.cpp
+@@ -912,7 +912,7 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
+ #endif
+ #ifdef HAVE_LIBOPENMAX
+ else if(pic.format == RENDER_FMT_OMXEGL)
+- m_pRenderer->AddProcessor(pic.openMax, &pic, index);
++ m_pRenderer->AddProcessor(pic.openMaxBuffer, index);
+ #endif
+ #ifdef TARGET_DARWIN
+ else if(pic.format == RENDER_FMT_CVBREF)
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+index 14ad038..18b8e3a 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+@@ -270,9 +270,12 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne
+ #endif
+
+ #if defined(HAVE_LIBOPENMAX)
+- if (CSettings::Get().GetBool("videoplayer.useomx") && !hint.software )
++ if (!hint.software && CSettings::Get().GetBool("videoplayer.useomx"))
+ {
+- if (hint.codec == AV_CODEC_ID_H264 || hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_VC1)
++ if (hint.codec == AV_CODEC_ID_H264 || hint.codec == AV_CODEC_ID_H263 || hint.codec == AV_CODEC_ID_MPEG4 ||
++ hint.codec == AV_CODEC_ID_MPEG1VIDEO || hint.codec == AV_CODEC_ID_MPEG2VIDEO ||
++ hint.codec == AV_CODEC_ID_VP6 || hint.codec == AV_CODEC_ID_VP6F || hint.codec == AV_CODEC_ID_VP6A || hint.codec == AV_CODEC_ID_VP8 ||
++ hint.codec == AV_CODEC_ID_THEORA || hint.codec == AV_CODEC_ID_MJPEG || hint.codec == AV_CODEC_ID_MJPEGB || hint.codec == AV_CODEC_ID_VC1 || hint.codec == AV_CODEC_ID_WMV3)
+ {
+ if ( (pCodec = OpenCodec(new CDVDVideoCodecOpenMax(), hint, options)) ) return pCodec;
+ }
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
+index f6751f4..dc047d7 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
+@@ -44,9 +44,7 @@ struct DVDCodecAvailableType
+ namespace DXVA { class CSurfaceContext; }
+ namespace VAAPI { struct CHolder; }
+ namespace VDPAU { class CVdpauRenderPicture; }
+-class COpenMax;
+-class COpenMaxVideo;
+-struct OpenMaxVideoBuffer;
++class COpenMaxVideoBuffer;
+ class CDVDVideoCodecStageFright;
+ class CDVDMediaCodecInfo;
+ typedef void* EGLImageKHR;
+@@ -75,8 +73,7 @@ struct DVDVideoPicture
+ };
+
+ struct {
+- COpenMax *openMax;
+- OpenMaxVideoBuffer *openMaxBuffer;
++ COpenMaxVideoBuffer *openMaxBuffer;
+ };
+
+ struct {
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
+index b2e7816..7d33192 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
+@@ -29,113 +29,43 @@
+ #include "DVDStreamInfo.h"
+ #include "DVDVideoCodecOpenMax.h"
+ #include "OpenMaxVideo.h"
++#include "settings/Settings.h"
+ #include "utils/log.h"
+
+-#define CLASSNAME "COpenMax"
++#define CLASSNAME "CDVDVideoCodecOpenMax"
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////
+-CDVDVideoCodecOpenMax::CDVDVideoCodecOpenMax() : CDVDVideoCodec()
++CDVDVideoCodecOpenMax::CDVDVideoCodecOpenMax()
+ {
+ m_omx_decoder = NULL;
+- m_pFormatName = "omx-xxxx";
+-
+- m_convert_bitstream = false;
+- memset(&m_videobuffer, 0, sizeof(DVDVideoPicture));
++ CLog::Log(LOGDEBUG, "%s::%s %p\n", CLASSNAME, __func__, this);
+ }
+
+ CDVDVideoCodecOpenMax::~CDVDVideoCodecOpenMax()
+ {
++ CLog::Log(LOGDEBUG, "%s::%s %p\n", CLASSNAME, __func__, this);
+ Dispose();
+ }
+
+ bool CDVDVideoCodecOpenMax::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+ {
+- // we always qualify even if DVDFactoryCodec does this too.
+- if (CSettings::Get().GetBool("videoplayer.useomx") && !hints.software)
+- {
+- m_convert_bitstream = false;
+-
+- switch (hints.codec)
+- {
+- case AV_CODEC_ID_H264:
+- {
+- m_pFormatName = "omx-h264";
+- if (hints.extrasize < 7 || hints.extradata == NULL)
+- {
+- CLog::Log(LOGNOTICE,
+- "%s::%s - avcC data too small or missing", CLASSNAME, __func__);
+- return false;
+- }
+- // valid avcC data (bitstream) always starts with the value 1 (version)
+- if ( *(char*)hints.extradata == 1 )
+- m_convert_bitstream = bitstream_convert_init(hints.extradata, hints.extrasize);
+- }
+- break;
+- case AV_CODEC_ID_MPEG4:
+- m_pFormatName = "omx-mpeg4";
+- break;
+- case AV_CODEC_ID_MPEG2VIDEO:
+- m_pFormatName = "omx-mpeg2";
+- break;
+- case AV_CODEC_ID_VC1:
+- m_pFormatName = "omx-vc1";
+- break;
+- default:
+- return false;
+- break;
+- }
+-
+- m_omx_decoder = new COpenMaxVideo;
+- if (!m_omx_decoder->Open(hints))
+- {
+- CLog::Log(LOGERROR,
+- "%s::%s - failed to open, codec(%d), profile(%d), level(%d)",
+- CLASSNAME, __func__, hints.codec, hints.profile, hints.level);
+- return false;
+- }
+-
+- // allocate a YV12 DVDVideoPicture buffer.
+- // first make sure all properties are reset.
+- memset(&m_videobuffer, 0, sizeof(DVDVideoPicture));
+-
+- m_videobuffer.dts = DVD_NOPTS_VALUE;
+- m_videobuffer.pts = DVD_NOPTS_VALUE;
+- //m_videobuffer.format = RENDER_FMT_YUV420P;
+- m_videobuffer.format = RENDER_FMT_OMXEGL;
+- m_videobuffer.color_range = 0;
+- m_videobuffer.color_matrix = 4;
+- m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
+- m_videobuffer.iWidth = hints.width;
+- m_videobuffer.iHeight = hints.height;
+- m_videobuffer.iDisplayWidth = hints.width;
+- m_videobuffer.iDisplayHeight = hints.height;
+-
+- return true;
+- }
++ m_omx_decoder = new COpenMaxVideo;
++ return m_omx_decoder->Open(hints, options);
++}
+
+- return false;
++const char* CDVDVideoCodecOpenMax::GetName(void)
++{
++ return m_omx_decoder ? m_omx_decoder->GetName() : "omx-xxx";
+ }
+
+ void CDVDVideoCodecOpenMax::Dispose()
+ {
+ if (m_omx_decoder)
+ {
+- m_omx_decoder->Close();
++ m_omx_decoder->Dispose();
+ delete m_omx_decoder;
+ m_omx_decoder = NULL;
+ }
+- if (m_videobuffer.iFlags & DVP_FLAG_ALLOCATED)
+- {
+- m_videobuffer.iFlags = 0;
+- }
+- 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;
+- }
+- }
+ }
+
+ void CDVDVideoCodecOpenMax::SetDropState(bool bDrop)
+@@ -145,37 +75,12 @@ void CDVDVideoCodecOpenMax::SetDropState(bool bDrop)
+
+ int CDVDVideoCodecOpenMax::Decode(uint8_t* pData, int iSize, double dts, double pts)
+ {
+- if (pData)
+- {
+- int rtn;
+- int demuxer_bytes = iSize;
+- uint8_t *demuxer_content = pData;
+- bool bitstream_convered = false;
+-
+- if (m_convert_bitstream)
+- {
+- // convert demuxer packet from bitstream to bytestream (AnnexB)
+- int bytestream_size = 0;
+- uint8_t *bytestream_buff = NULL;
+-
+- bitstream_convert(demuxer_content, demuxer_bytes, &bytestream_buff, &bytestream_size);
+- if (bytestream_buff && (bytestream_size > 0))
+- {
+- bitstream_convered = true;
+- demuxer_bytes = bytestream_size;
+- demuxer_content = bytestream_buff;
+- }
+- }
+-
+- rtn = m_omx_decoder->Decode(demuxer_content, demuxer_bytes, dts, pts);
+-
+- if (bitstream_convered)
+- free(demuxer_content);
++ return m_omx_decoder->Decode(pData, iSize, dts, pts);
++}
+
+- return rtn;
+- }
+-
+- return VC_BUFFER;
++unsigned CDVDVideoCodecOpenMax::GetAllowedReferences()
++{
++ return m_omx_decoder->GetAllowedReferences();
+ }
+
+ void CDVDVideoCodecOpenMax::Reset(void)
+@@ -185,172 +90,12 @@ void CDVDVideoCodecOpenMax::Reset(void)
+
+ bool CDVDVideoCodecOpenMax::GetPicture(DVDVideoPicture* pDvdVideoPicture)
+ {
+- m_omx_decoder->GetPicture(&m_videobuffer);
+- *pDvdVideoPicture = m_videobuffer;
+-
+- return VC_PICTURE | VC_BUFFER;
+-}
+-
+-////////////////////////////////////////////////////////////////////////////////////////////
+-bool CDVDVideoCodecOpenMax::bitstream_convert_init(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;
+- }
+- uint8_t* new_out = (uint8_t*)realloc(out, total_size);
+- if (new_out)
+- {
+- out = new_out;
+- }
+- else
+- {
+- CLog::Log(LOGERROR, "bitstream_convert_init failed - %s : could not realloc the buffer out", __FUNCTION__);
+- free(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 CDVDVideoCodecOpenMax::bitstream_convert(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)
+- {
+- bitstream_alloc_and_copy(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
+- {
+- bitstream_alloc_and_copy(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;
++ return m_omx_decoder->GetPicture(pDvdVideoPicture);
+ }
+
+-void CDVDVideoCodecOpenMax::bitstream_alloc_and_copy(
+- uint8_t **poutbuf, int *poutbuf_size,
+- const uint8_t *sps_pps, uint32_t sps_pps_size,
+- const uint8_t *in, uint32_t in_size)
++bool CDVDVideoCodecOpenMax::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
+ {
+- // 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;
+- }
++ return m_omx_decoder->ClearPicture(pDvdVideoPicture);
+ }
+
+ #endif
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
+index fb80d02..67cc235 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
+@@ -23,7 +23,7 @@
+
+ #include "DVDVideoCodec.h"
+
+-class COpenVideoMax;
++class COpenMaxVideo;
+ class CDVDVideoCodecOpenMax : public CDVDVideoCodec
+ {
+ public:
+@@ -36,39 +36,13 @@ class CDVDVideoCodecOpenMax : public CDVDVideoCodec
+ virtual int Decode(uint8_t *pData, int iSize, double dts, double pts);
+ virtual void Reset(void);
+ virtual bool GetPicture(DVDVideoPicture *pDvdVideoPicture);
++ virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture);
++ virtual unsigned GetAllowedReferences();
+ virtual void SetDropState(bool bDrop);
+- virtual const char* GetName(void) { return (const char*)m_pFormatName; }
++ virtual const char* GetName(void);
+
+ protected:
+- const char *m_pFormatName;
+ COpenMaxVideo *m_omx_decoder;
+- DVDVideoPicture m_videobuffer;
+-
+- // bitstream to bytestream (Annex B) conversion support.
+- bool bitstream_convert_init(void *in_extradata, int in_extrasize);
+- bool bitstream_convert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size);
+- static void bitstream_alloc_and_copy( 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()
+- {
+- length_size = 0;
+- first_idr = 0;
+- sps_pps_data = NULL;
+- size = 0;
+- }
+-
+- } omx_bitstream_ctx;
+-
+- uint32_t m_sps_pps_size;
+- omx_bitstream_ctx m_sps_pps_context;
+- bool m_convert_bitstream;
+ };
+
+ #endif
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
+index 8a97889..ebf7123 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
+@@ -20,7 +20,6 @@ SRCS += DVDVideoCodecVDA.cpp
+ SRCS += VDA.cpp
+ endif
+ ifeq (@USE_OPENMAX@,1)
+-SRCS += OpenMax.cpp
+ SRCS += OpenMaxVideo.cpp
+ SRCS += DVDVideoCodecOpenMax.cpp
+ endif
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp
+deleted file mode 100644
+index 7b0a0c2ef..0000000
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp
++++ /dev/null
+@@ -1,269 +0,0 @@
+-/*
+- * Copyright (C) 2010-2013 Team XBMC
+- * http://xbmc.org
+- *
+- * This Program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This Program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with XBMC; see the file COPYING. If not, see
+- * <http://www.gnu.org/licenses/>.
+- *
+- */
+-
+-#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+- #include "config.h"
+-#elif defined(TARGET_WINDOWS)
+-#include "system.h"
+-#endif
+-
+-#if defined(HAVE_LIBOPENMAX)
+-#include "OpenMax.h"
+-#include "DynamicDll.h"
+-#include "DVDClock.h"
+-#include "DVDStreamInfo.h"
+-#include "windowing/WindowingFactory.h"
+-#include "DVDVideoCodec.h"
+-#include "utils/log.h"
+-#include "utils/TimeUtils.h"
+-#include "ApplicationMessenger.h"
+-#include "Application.h"
+-
+-#include <OMX_Core.h>
+-#include <OMX_Component.h>
+-#include <OMX_Index.h>
+-#include <OMX_Image.h>
+-
+-#define CLASSNAME "COpenMax"
+-
+-
+-////////////////////////////////////////////////////////////////////////////////////////////
+-class DllLibOpenMaxInterface
+-{
+-public:
+- virtual ~DllLibOpenMaxInterface() {}
+-
+- 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;
+-};
+-
+-class DllLibOpenMax : public DllDynamic, DllLibOpenMaxInterface
+-{
+- DECLARE_DLL_WRAPPER(DllLibOpenMax, "/usr/lib/libnvomx.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))
+- 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)
+- END_METHOD_RESOLVE()
+-};
+-
+-////////////////////////////////////////////////////////////////////////////////////////////
+-#define OMX_INIT_STRUCTURE(a) \
+- memset(&(a), 0, sizeof(a)); \
+- (a).nSize = sizeof(a); \
+- (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \
+- (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \
+- (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \
+- (a).nVersion.s.nStep = OMX_VERSION_STEP
+-
+-////////////////////////////////////////////////////////////////////////////////////////////
+-////////////////////////////////////////////////////////////////////////////////////////////
+-COpenMax::COpenMax()
+-{
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
+- #endif
+- m_dll = new DllLibOpenMax;
+- m_dll->Load();
+- m_is_open = false;
+-
+- m_omx_decoder = NULL;
+- m_omx_client_state = DEAD;
+- m_omx_decoder_state = 0;
+- sem_init(m_omx_decoder_state_change, 0, 0);
+- /*
+- m_omx_flush_input = (sem_t*)malloc(sizeof(sem_t));
+- sem_init(m_omx_flush_input, 0, 0);
+- m_omx_flush_output = (sem_t*)malloc(sizeof(sem_t));
+- sem_init(m_omx_flush_output, 0, 0);
+- */
+-}
+-
+-COpenMax::~COpenMax()
+-{
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
+- #endif
+- /*
+- sem_destroy(m_omx_flush_input);
+- free(m_omx_flush_input);
+- sem_destroy(m_omx_flush_output);
+- free(m_omx_flush_output);
+- */
+- delete m_dll;
+-}
+-
+-
+-
+-
+-////////////////////////////////////////////////////////////////////////////////////////////
+-// DecoderEventHandler -- OMX event callback
+-OMX_ERRORTYPE COpenMax::DecoderEventHandlerCallback(
+- OMX_HANDLETYPE hComponent,
+- OMX_PTR pAppData,
+- OMX_EVENTTYPE eEvent,
+- OMX_U32 nData1,
+- OMX_U32 nData2,
+- OMX_PTR pEventData)
+-{
+- COpenMax *ctx = (COpenMax*)pAppData;
+- return ctx->DecoderEventHandler(hComponent, pAppData, eEvent, nData1, nData2, pEventData);
+-}
+-
+-// DecoderEmptyBufferDone -- OpenMax input buffer has been emptied
+-OMX_ERRORTYPE COpenMax::DecoderEmptyBufferDoneCallback(
+- OMX_HANDLETYPE hComponent,
+- OMX_PTR pAppData,
+- OMX_BUFFERHEADERTYPE* pBuffer)
+-{
+- COpenMax *ctx = (COpenMax*)pAppData;
+- return ctx->DecoderEmptyBufferDone( hComponent, pAppData, pBuffer);
+-}
+-
+-// DecoderFillBufferDone -- OpenMax output buffer has been filled
+-OMX_ERRORTYPE COpenMax::DecoderFillBufferDoneCallback(
+- OMX_HANDLETYPE hComponent,
+- OMX_PTR pAppData,
+- OMX_BUFFERHEADERTYPE* pBuffer)
+-{
+- COpenMax *ctx = (COpenMax*)pAppData;
+- return ctx->DecoderFillBufferDone(hComponent, pAppData, pBuffer);
+-}
+-
+-
+-
+-// Wait for a component to transition to the specified state
+-OMX_ERRORTYPE COpenMax::WaitForState(OMX_STATETYPE state)
+-{
+- OMX_STATETYPE test_state;
+- int tries = 0;
+- struct timespec timeout;
+- OMX_ERRORTYPE omx_error = OMX_GetState(m_omx_decoder, &test_state);
+-
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s - waiting for state(%d)\n", CLASSNAME, __func__, state);
+- #endif
+- while ((omx_error == OMX_ErrorNone) && (test_state != state))
+- {
+- clock_gettime(CLOCK_REALTIME, &timeout);
+- timeout.tv_sec += 1;
+- sem_timedwait(m_omx_decoder_state_change, &timeout);
+- if (errno == ETIMEDOUT)
+- tries++;
+- if (tries > 5)
+- return OMX_ErrorUndefined;
+-
+- omx_error = OMX_GetState(m_omx_decoder, &test_state);
+- }
+-
+- return omx_error;
+-}
+-
+-// SetStateForAllComponents
+-// Blocks until all state changes have completed
+-OMX_ERRORTYPE COpenMax::SetStateForComponent(OMX_STATETYPE state)
+-{
+- OMX_ERRORTYPE omx_err;
+-
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s - state(%d)\n", CLASSNAME, __func__, state);
+- #endif
+- omx_err = OMX_SendCommand(m_omx_decoder, OMX_CommandStateSet, state, 0);
+- if (omx_err)
+- CLog::Log(LOGERROR, "%s::%s - OMX_CommandStateSet failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- else
+- omx_err = WaitForState(state);
+-
+- return omx_err;
+-}
+-
+-bool COpenMax::Initialize( const CStdString &decoder_name)
+-{
+- OMX_ERRORTYPE omx_err = m_dll->OMX_Init();
+- if (omx_err)
+- {
+- CLog::Log(LOGERROR,
+- "%s::%s - OpenMax failed to init, status(%d), ", // codec(%d), profile(%d), level(%d)
+- CLASSNAME, __func__, omx_err );//, hints.codec, hints.profile, hints.level);
+- return false;
+- }
+-
+- // Get video decoder handle setting up callbacks, component is in loaded state on return.
+- static OMX_CALLBACKTYPE decoder_callbacks = {
+- &DecoderEventHandlerCallback, &DecoderEmptyBufferDoneCallback, &DecoderFillBufferDoneCallback };
+- omx_err = m_dll->OMX_GetHandle(&m_omx_decoder, (char*)decoder_name.c_str(), this, &decoder_callbacks);
+- if (omx_err)
+- {
+- CLog::Log(LOGERROR,
+- "%s::%s - could not get decoder handle\n", CLASSNAME, __func__);
+- m_dll->OMX_Deinit();
+- return false;
+- }
+-
+- return true;
+-}
+-
+-void COpenMax::Deinitialize()
+-{
+- CLog::Log(LOGERROR,
+- "%s::%s - failed to get component port parameter\n", CLASSNAME, __func__);
+- m_dll->OMX_FreeHandle(m_omx_decoder);
+- m_omx_decoder = NULL;
+- m_dll->OMX_Deinit();
+-}
+-
+-// OpenMax decoder callback routines.
+-OMX_ERRORTYPE COpenMax::DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData)
+-{
+- return OMX_ErrorNone;
+-}
+-
+-OMX_ERRORTYPE COpenMax::DecoderEmptyBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer)
+-{
+- return OMX_ErrorNone;
+-}
+-
+-OMX_ERRORTYPE COpenMax::DecoderFillBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader)
+-{
+- return OMX_ErrorNone;
+-}
+-
+-#endif
+-
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h
+deleted file mode 100644
+index 0d9ff18..0000000
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h
++++ /dev/null
+@@ -1,116 +0,0 @@
+-#pragma once
+-/*
+- * Copyright (C) 2010-2013 Team XBMC
+- * http://xbmc.org
+- *
+- * This Program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This Program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with XBMC; see the file COPYING. If not, see
+- * <http://www.gnu.org/licenses/>.
+- *
+- */
+-
+-#if defined(HAVE_LIBOPENMAX)
+-
+-#include "cores/dvdplayer/DVDStreamInfo.h"
+-#include "DVDVideoCodec.h"
+-#include "threads/Event.h"
+-
+-#include <queue>
+-#include <semaphore.h>
+-#include <OMX_Core.h>
+-
+-////////////////////////////////////////////////////////////////////////////////////////////
+-// debug spew defines
+-#if 0
+-#define OMX_DEBUG_VERBOSE
+-#define OMX_DEBUG_EVENTHANDLER
+-#define OMX_DEBUG_FILLBUFFERDONE
+-#define OMX_DEBUG_EMPTYBUFFERDONE
+-#endif
+-
+-typedef struct omx_codec_capability {
+- // level is OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE,
+- // or OMX_VIDEO_MPEG4PROFILETYPE depending on context.
+- OMX_U32 level;
+- // level is OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE,
+- // or OMX_VIDEO_MPEG4PROFILETYPE depending on context.
+- OMX_U32 profile;
+-} omx_codec_capability;
+-
+-typedef struct omx_demux_packet {
+- OMX_U8 *buff;
+- int size;
+- double dts;
+- double pts;
+-} omx_demux_packet;
+-
+-class DllLibOpenMax;
+-class COpenMax
+-{
+-public:
+- COpenMax();
+- virtual ~COpenMax();
+-
+-protected:
+- enum OMX_CLIENT_STATE {
+- DEAD,
+- LOADED,
+- LOADED_TO_IDLE,
+- IDLE_TO_EXECUTING,
+- EXECUTING,
+- EXECUTING_TO_IDLE,
+- IDLE_TO_LOADED,
+- RECONFIGURING,
+- ERROR
+- };
+-
+- // initialize OpenMax and get decoder component
+- bool Initialize( const CStdString &decoder_name);
+- void Deinitialize();
+-
+- // OpenMax Decoder delegate callback routines.
+- static OMX_ERRORTYPE DecoderEventHandlerCallback(OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData);
+- static OMX_ERRORTYPE DecoderEmptyBufferDoneCallback(
+- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer);
+- static OMX_ERRORTYPE DecoderFillBufferDoneCallback(
+- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader);
+-
+- // OpenMax decoder callback routines.
+- virtual OMX_ERRORTYPE DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData);
+- virtual OMX_ERRORTYPE DecoderEmptyBufferDone(
+- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer);
+- virtual OMX_ERRORTYPE DecoderFillBufferDone(
+- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader);
+-
+- // OpenMax helper routines
+- OMX_ERRORTYPE WaitForState(OMX_STATETYPE state);
+- OMX_ERRORTYPE SetStateForComponent(OMX_STATETYPE state);
+-
+- DllLibOpenMax *m_dll;
+- bool m_is_open;
+- OMX_HANDLETYPE m_omx_decoder; // openmax decoder component reference
+-
+- // OpenMax state tracking
+- OMX_CLIENT_STATE m_omx_client_state;
+- volatile int m_omx_decoder_state;
+- sem_t *m_omx_decoder_state_change;
+- std::vector<omx_codec_capability> m_omx_decoder_capabilities;
+-
+-private:
+- COpenMax(const COpenMax& other);
+- COpenMax& operator=(const COpenMax&);
+-};
+-
+-#endif
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+index dcbdb1e..aca2e0d 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+@@ -32,673 +32,891 @@
+ #include "DVDVideoCodec.h"
+ #include "utils/log.h"
+ #include "utils/TimeUtils.h"
++#include "settings/Settings.h"
+ #include "ApplicationMessenger.h"
+ #include "Application.h"
++#include "threads/Atomics.h"
+
+-#include <OMX_Core.h>
+-#include <OMX_Component.h>
+-#include <OMX_Index.h>
+-#include <OMX_Image.h>
++#include <IL/OMX_Core.h>
++#include <IL/OMX_Component.h>
++#include <IL/OMX_Index.h>
++#include <IL/OMX_Image.h>
+
++#include "cores/omxplayer/OMXImage.h"
++
++#define DTS_QUEUE
++
++#define DEFAULT_TIMEOUT 1000
++#ifdef _DEBUG
++#define OMX_DEBUG_VERBOSE
++#endif
+
+ #define CLASSNAME "COpenMaxVideo"
+
+-// 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"
+-
+-// EGL extension functions
+-static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
+-static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
+-#define GETEXTENSION(type, ext) \
+-do \
+-{ \
+- ext = (type) eglGetProcAddress(#ext); \
+- if (!ext) \
+- { \
+- CLog::Log(LOGERROR, "%s::%s - ERROR getting proc addr of " #ext "\n", CLASSNAME, __func__); \
+- } \
+-} while (0);
+-
+-#define OMX_INIT_STRUCTURE(a) \
+- memset(&(a), 0, sizeof(a)); \
+- (a).nSize = sizeof(a); \
+- (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \
+- (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \
+- (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \
+- (a).nVersion.s.nStep = OMX_VERSION_STEP
++COpenMaxVideoBuffer::COpenMaxVideoBuffer(COpenMaxVideo *omv)
++ : m_omv(omv), m_refs(0)
++{
++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this);
++ omx_buffer = NULL;
++ width = 0;
++ height = 0;
++ index = 0;
++ egl_image = 0;
++ texture_id = 0;
++}
++
++COpenMaxVideoBuffer::~COpenMaxVideoBuffer()
++{
++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this);
++}
+
+
+-COpenMaxVideo::COpenMaxVideo()
++// DecoderFillBufferDone -- OpenMax output buffer has been filled
++static OMX_ERRORTYPE DecoderFillBufferDoneCallback(
++ OMX_HANDLETYPE hComponent,
++ OMX_PTR pAppData,
++ OMX_BUFFERHEADERTYPE* pBuffer)
++{
++ COpenMaxVideoBuffer *pic = static_cast<COpenMaxVideoBuffer*>(pBuffer->pAppPrivate);
++ COpenMaxVideo *ctx = pic->m_omv;
++ return ctx->DecoderFillBufferDone(hComponent, pBuffer);
++}
++
++
++COpenMaxVideoBuffer* COpenMaxVideoBuffer::Acquire()
++{
++ long count = AtomicIncrement(&m_refs);
++ #if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s %p ref:%ld", CLASSNAME, __func__, this, count);
++ #endif
++ (void)count;
++ return this;
++}
++
++long COpenMaxVideoBuffer::Release()
+ {
+- m_portChanging = false;
++ long count = AtomicDecrement(&m_refs);
++ if (count == 0)
++ {
++ m_omv->ReleaseOpenMaxBuffer(this);
++ }
++
++ #if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s %p ref:%ld", CLASSNAME, __func__, this, count);
++ #endif
++ return count;
++}
+
+- pthread_mutex_init(&m_omx_input_mutex, NULL);
++void COpenMaxVideoBuffer::Sync()
++{
++ #if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s %p ref:%ld", CLASSNAME, __func__, this, m_refs);
++ #endif
++ Release();
++}
++
++COpenMaxVideo::COpenMaxVideo()
++{
++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this);
+ pthread_mutex_init(&m_omx_output_mutex, NULL);
+
+- m_omx_decoder_state_change = (sem_t*)malloc(sizeof(sem_t));
+- sem_init(m_omx_decoder_state_change, 0, 0);
+- memset(&m_videobuffer, 0, sizeof(DVDVideoPicture));
+ m_drop_state = false;
+ m_decoded_width = 0;
+ m_decoded_height = 0;
+- m_omx_input_eos = false;
+- m_omx_input_port = 0;
+- m_omx_output_eos = false;
+- m_omx_output_port = 0;
+- m_videoplayback_done = false;
++ m_egl_buffer_count = 0;
++
++ m_port_settings_changed = false;
++ m_pFormatName = "omx-xxxx";
+ }
+
+ COpenMaxVideo::~COpenMaxVideo()
+ {
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this);
+ #endif
+- if (m_is_open)
+- Close();
+- pthread_mutex_destroy(&m_omx_input_mutex);
++ if (m_omx_decoder.IsInitialized())
++ {
++ if (m_omx_tunnel.IsInitialized())
++ m_omx_tunnel.Deestablish();
++
++ StopDecoder();
++
++ if (m_omx_egl_render.IsInitialized())
++ m_omx_egl_render.Deinitialize();
++ if (m_omx_decoder.IsInitialized())
++ m_omx_decoder.Deinitialize();
++ }
+ pthread_mutex_destroy(&m_omx_output_mutex);
+- sem_destroy(m_omx_decoder_state_change);
+- free(m_omx_decoder_state_change);
+ }
+
+-bool COpenMaxVideo::Open(CDVDStreamInfo &hints)
++bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+ {
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
+
++ // we always qualify even if DVDFactoryCodec does this too.
++ if (!CSettings::Get().GetBool("videoplayer.useomx") || hints.software)
++ return false;
++
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+- std::string decoder_name;
+
+ m_decoded_width = hints.width;
+ m_decoded_height = hints.height;
+
++ m_egl_display = g_Windowing.GetEGLDisplay();
++ m_egl_context = g_Windowing.GetEGLContext();
++ m_egl_buffer_count = 4;
++
++ m_codingType = OMX_VIDEO_CodingUnused;
++
+ switch (hints.codec)
+ {
+ case AV_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;
+- break;
+- case FF_PROFILE_H264_MAIN:
+- // (role name) video_decoder.avc
+- // H.264 Main profile
+- decoder_name = OMX_H264MAIN_DECODER;
+- break;
+- case FF_PROFILE_H264_HIGH:
+- // (role name) video_decoder.avc
+- // H.264 Main profile
+- decoder_name = OMX_H264HIGH_DECODER;
+- break;
+- default:
+- return false;
+- break;
+- }
+- }
++ // H.264
++ m_codingType = OMX_VIDEO_CodingAVC;
++ m_pFormatName = "omx-h264";
+ break;
++ case AV_CODEC_ID_H263:
+ case AV_CODEC_ID_MPEG4:
+- // (role name) video_decoder.mpeg4
+- // MPEG-4, DivX 4/5 and Xvid compatible
+- decoder_name = OMX_MPEG4_DECODER;
+- break;
+- /*
+- TODO: what mpeg4 formats are "ext" ????
+- case NvxStreamType_MPEG4Ext:
+- // (role name) video_decoder.mpeg4
+ // MPEG-4, DivX 4/5 and Xvid compatible
+- decoder_name = OMX_MPEG4EXT_DECODER;
++ m_codingType = OMX_VIDEO_CodingMPEG4;
+ m_pFormatName = "omx-mpeg4";
+ break;
+- */
++ case AV_CODEC_ID_MPEG1VIDEO:
+ case AV_CODEC_ID_MPEG2VIDEO:
+- // (role name) video_decoder.mpeg2
+ // MPEG-2
+- decoder_name = OMX_MPEG2V_DECODER;
++ m_codingType = OMX_VIDEO_CodingMPEG2;
++ m_pFormatName = "omx-mpeg2";
++ break;
++ case AV_CODEC_ID_VP6:
++ // this form is encoded upside down
++ // fall through
++ case AV_CODEC_ID_VP6F:
++ case AV_CODEC_ID_VP6A:
++ // VP6
++ m_codingType = OMX_VIDEO_CodingVP6;
++ m_pFormatName = "omx-vp6";
++ break;
++ case AV_CODEC_ID_VP8:
++ // VP8
++ m_codingType = OMX_VIDEO_CodingVP8;
++ m_pFormatName = "omx-vp8";
++ break;
++ case AV_CODEC_ID_THEORA:
++ // theora
++ m_codingType = OMX_VIDEO_CodingTheora;
++ m_pFormatName = "omx-theora";
++ break;
++ case AV_CODEC_ID_MJPEG:
++ case AV_CODEC_ID_MJPEGB:
++ // mjpg
++ m_codingType = OMX_VIDEO_CodingMJPEG;
++ m_pFormatName = "omx-mjpg";
+ break;
+ case AV_CODEC_ID_VC1:
+- // (role name) video_decoder.vc1
++ case AV_CODEC_ID_WMV3:
+ // VC-1, WMV9
+- decoder_name = OMX_VC1_DECODER;
+- break;
++ m_codingType = OMX_VIDEO_CodingWMV;
++ m_pFormatName = "omx-vc1";
++ break;
+ default:
++ CLog::Log(LOGERROR, "%s::%s : Video codec unknown: %x", CLASSNAME, __func__, hints.codec);
+ return false;
+ break;
+ }
+
+ // initialize OpenMAX.
+- if (!Initialize(decoder_name))
++ if (!m_omx_decoder.Initialize("OMX.broadcom.video_decode", OMX_IndexParamVideoInit))
+ {
++ CLog::Log(LOGERROR, "%s::%s error m_omx_decoder.Initialize", CLASSNAME, __func__);
+ return false;
+ }
+
+- // TODO: Find component from role name.
+- // Get the port information. This will obtain information about the
+- // number of ports and index of the first port.
+- OMX_PORT_PARAM_TYPE port_param;
+- OMX_INIT_STRUCTURE(port_param);
+- omx_err = OMX_GetParameter(m_omx_decoder, OMX_IndexParamVideoInit, &port_param);
+- if (omx_err)
++ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateIdle);
++ if (omx_err != OMX_ErrorNone)
+ {
+- Deinitialize();
++ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetStateForComponent omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+- m_omx_input_port = port_param.nStartPortNumber;
+- m_omx_output_port = m_omx_input_port + 1;
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - decoder_component(0x%p), input_port(0x%x), output_port(0x%x)\n",
+- CLASSNAME, __func__, m_omx_decoder, m_omx_input_port, m_omx_output_port);
+- #endif
+
+- // TODO: Set role for the component because components could have multiple roles.
+- //QueryCodec();
++ OMX_VIDEO_PARAM_PORTFORMATTYPE formatType;
++ OMX_INIT_STRUCTURE(formatType);
++ formatType.nPortIndex = m_omx_decoder.GetInputPort();
++ formatType.eCompressionFormat = m_codingType;
++
++ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamVideoPortFormat, &formatType);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ 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, "%s::%s error OMX_IndexParamPortDefinition omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ portParam.nPortIndex = m_omx_decoder.GetInputPort();
++ portParam.nBufferCountActual = 20;
++ portParam.format.video.nFrameWidth = m_decoded_width;
++ portParam.format.video.nFrameHeight = m_decoded_height;
++
++ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &portParam);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s error OMX_IndexParamPortDefinition omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ // request portsettingschanged on aspect ratio change
++ OMX_CONFIG_REQUESTCALLBACKTYPE notifications;
++ OMX_INIT_STRUCTURE(notifications);
++ notifications.nPortIndex = m_omx_decoder.GetOutputPort();
++ notifications.nIndex = OMX_IndexParamBrcmPixelAspectRatio;
++ notifications.bEnable = OMX_TRUE;
++
++ omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexConfigRequestCallback, &notifications);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s OMX_IndexConfigRequestCallback error (0%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ if (NaluFormatStartCodes(hints.codec, (uint8_t *)hints.extradata, hints.extrasize))
++ {
++ OMX_NALSTREAMFORMATTYPE nalStreamFormat;
++ OMX_INIT_STRUCTURE(nalStreamFormat);
++ nalStreamFormat.nPortIndex = m_omx_decoder.GetInputPort();
++ nalStreamFormat.eNaluFormat = OMX_NaluFormatStartCodes;
++
++ omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexParamNalStreamFormatSelect, &nalStreamFormat);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s OMX_IndexParamNalStreamFormatSelect error (0%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++ }
+
+- // Component will be in OMX_StateLoaded now so we can alloc omx input/output buffers.
+- // we can only alloc them in OMX_StateLoaded state or if the component port is disabled
+ // Alloc buffers for the omx input port.
+- omx_err = AllocOMXInputBuffers();
+- if (omx_err)
++ omx_err = m_omx_decoder.AllocInputBuffers();
++ if (omx_err != OMX_ErrorNone)
+ {
+- Deinitialize();
++ CLog::Log(LOGERROR, "%s::%s AllocInputBuffers error (0%08x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+- // Alloc buffers for the omx output port.
+- m_egl_display = g_Windowing.GetEGLDisplay();
+- m_egl_context = g_Windowing.GetEGLContext();
+- omx_err = AllocOMXOutputBuffers();
+- if (omx_err)
++
++ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting);
++ if (omx_err != OMX_ErrorNone)
+ {
+- FreeOMXInputBuffers(false);
+- Deinitialize();
++ CLog::Log(LOGERROR, "%s::%s error m_omx_decoder.SetStateForComponent error (0%08x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+- m_is_open = true;
+- m_drop_state = false;
+- m_videoplayback_done = false;
++ if (!SendDecoderConfig((uint8_t *)hints.extradata, hints.extrasize))
++ return false;
+
+- // crank it up.
+- StartDecoder();
++ m_drop_state = false;
+
+ return true;
+ }
+
+-void COpenMaxVideo::Close()
++void COpenMaxVideo::Dispose()
+ {
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
+- if (m_omx_decoder)
+- {
+- if (m_omx_decoder_state != OMX_StateLoaded)
+- StopDecoder();
+- Deinitialize();
+- }
+- m_is_open = false;
+ }
+
+ void COpenMaxVideo::SetDropState(bool bDrop)
+ {
++#if defined(OMX_DEBUG_VERBOSE)
++ if (m_drop_state != bDrop)
++ CLog::Log(LOGDEBUG, "%s::%s - m_drop_state(%d)",
++ CLASSNAME, __func__, bDrop);
++#endif
+ m_drop_state = bDrop;
++}
+
+- if (m_drop_state)
++bool COpenMaxVideo::SendDecoderConfig(uint8_t *extradata, int extrasize)
++{
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
++
++ /* send decoder config */
++ if (extrasize > 0 && extradata != NULL)
+ {
+- OMX_ERRORTYPE omx_err;
++ OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer();
+
+- // blow all but the last ready video frame
+- pthread_mutex_lock(&m_omx_output_mutex);
+- while (m_omx_output_ready.size() > 1)
++ if (omx_buffer == NULL)
+ {
+- m_dts_queue.pop();
+- OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_output_ready.front()->omx_buffer;
+- m_omx_output_ready.pop();
+- // return the omx buffer back to OpenMax to fill.
+- omx_err = OMX_FillThisBuffer(m_omx_decoder, omx_buffer);
+- if (omx_err)
+- CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
++ CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err);
++ return false;
+ }
+- pthread_mutex_unlock(&m_omx_output_mutex);
+
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s - m_drop_state(%d)\n",
+- CLASSNAME, __func__, m_drop_state);
+- #endif
++ omx_buffer->nOffset = 0;
++ omx_buffer->nFilledLen = extrasize;
++ if (omx_buffer->nFilledLen > omx_buffer->nAllocLen)
++ {
++ CLog::Log(LOGERROR, "%s::%s - omx_buffer->nFilledLen > omx_buffer->nAllocLen", CLASSNAME, __func__);
++ return false;
++ }
++
++ memcpy((unsigned char *)omx_buffer->pBuffer, extradata, omx_buffer->nFilledLen);
++ omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;
++
++//CLog::Log(LOGINFO, "%s::%s - Empty(%d,%x)", CLASSNAME, __func__, omx_buffer->nFilledLen, omx_buffer->nFlags); CLog::MemDump((char *)omx_buffer->pBuffer, std::min(64U, omx_buffer->nFilledLen));
++ 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)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
+ }
++ return true;
+ }
+
+-int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
++bool COpenMaxVideo::NaluFormatStartCodes(enum AVCodecID codec, uint8_t *extradata, int extrasize)
++{
++ switch(codec)
++ {
++ case AV_CODEC_ID_H264:
++ if (extrasize < 7 || extradata == NULL)
++ return true;
++ // valid avcC atom data always starts with the value 1 (version), otherwise annexb
++ else if ( *extradata != 1 )
++ return true;
++ default: break;
++ }
++ return false;
++}
++
++bool COpenMaxVideo::PortSettingsChanged()
+ {
+- if (pData)
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
++
++ if (m_port_settings_changed)
++ {
++ m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), true);
++ }
++
++ OMX_PARAM_PORTDEFINITIONTYPE port_def;
++ OMX_INIT_STRUCTURE(port_def);
++ port_def.nPortIndex = m_omx_decoder.GetOutputPort();
++ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - error m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ OMX_CONFIG_POINTTYPE pixel_aspect;
++ OMX_INIT_STRUCTURE(pixel_aspect);
++ pixel_aspect.nPortIndex = m_omx_decoder.GetOutputPort();
++ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamBrcmPixelAspectRatio, &pixel_aspect);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - error m_omx_decoder.GetParameter(OMX_IndexParamBrcmPixelAspectRatio) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ if (m_port_settings_changed)
+ {
+- int demuxer_bytes = iSize;
+- uint8_t *demuxer_content = pData;
++ m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), true);
++ return true;
++ }
++
++ // convert in stripes
++ port_def.format.video.nSliceHeight = 16;
++ port_def.format.video.nStride = 0;
++
++ 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)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ OMX_CALLBACKTYPE callbacks = { NULL, NULL, DecoderFillBufferDoneCallback };
++ if (!m_omx_egl_render.Initialize("OMX.broadcom.egl_render", OMX_IndexParamVideoInit, &callbacks))
++ {
++ CLog::Log(LOGERROR, "%s::%s error m_omx_egl_render.Initialize", CLASSNAME, __func__);
++ return false;
++ }
++
++ OMX_CONFIG_PORTBOOLEANTYPE discardMode;
++ OMX_INIT_STRUCTURE(discardMode);
++ discardMode.nPortIndex = m_omx_egl_render.GetInputPort();
++ discardMode.bEnabled = OMX_FALSE;
++ omx_err = m_omx_egl_render.SetParameter(OMX_IndexParamBrcmVideoEGLRenderDiscardMode, &discardMode);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - error m_omx_egl_render.SetParameter(OMX_IndexParamBrcmVideoEGLRenderDiscardMode) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ m_omx_egl_render.ResetEos();
++
++ CLog::Log(LOGDEBUG, "%s::%s - %dx%d@%.2f interlace:%d deinterlace:%d", CLASSNAME, __func__,
++ port_def.format.video.nFrameWidth, port_def.format.video.nFrameHeight,
++ port_def.format.video.xFramerate / (float)(1<<16), 0,0);
++
++ m_omx_tunnel.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_egl_render, m_omx_egl_render.GetInputPort());
++
++ omx_err = m_omx_tunnel.Establish();
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_tunnel.Establish omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ // Obtain the information about the output port.
++ OMX_PARAM_PORTDEFINITIONTYPE port_format;
++ OMX_INIT_STRUCTURE(port_format);
++ port_format.nPortIndex = m_omx_egl_render.GetOutputPort();
++ omx_err = m_omx_egl_render.GetParameter(OMX_IndexParamPortDefinition, &port_format);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s - m_omx_egl_render.GetParameter OMX_IndexParamPortDefinition omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ port_format.nBufferCountActual = m_egl_buffer_count;
++ omx_err = m_omx_egl_render.SetParameter(OMX_IndexParamPortDefinition, &port_format);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s - m_omx_egl_render.SetParameter OMX_IndexParamPortDefinition omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ #if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG,
++ "%s::%s (1) - oport(%d), nFrameWidth(%u), nFrameHeight(%u), nStride(%x), nBufferCountMin(%u), nBufferCountActual(%u), nBufferSize(%u)",
++ CLASSNAME, __func__, m_omx_egl_render.GetOutputPort(),
++ port_format.format.video.nFrameWidth, port_format.format.video.nFrameHeight,port_format.format.video.nStride,
++ port_format.nBufferCountMin, port_format.nBufferCountActual, port_format.nBufferSize);
++ #endif
++
+
+- // we need to queue then de-queue the demux packet, seems silly but
+- // omx might not have a omx input buffer avaliable when we are called
+- // and we must store the demuxer packet and try again later.
++ omx_err = m_omx_egl_render.EnablePort(m_omx_egl_render.GetOutputPort(), false);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_egl_render.EnablePort omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ if (!AllocOMXOutputBuffers())
++ {
++ CLog::Log(LOGERROR, "%s::%s - AllocOMXOutputBuffers failed", CLASSNAME, __func__);
++ return false;
++ }
++
++ omx_err = m_omx_egl_render.WaitForCommand(OMX_CommandPortEnable, m_omx_egl_render.GetOutputPort());
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_egl_render.WaitForCommand(OMX_CommandPortEnable) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ assert(m_omx_output_busy.empty());
++ assert(m_omx_output_ready.empty());
++
++ omx_err = m_omx_egl_render.SetStateForComponent(OMX_StateExecuting);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_egl_render.SetStateForComponent omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ omx_err = PrimeFillBuffers();
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_egl_render.PrimeFillBuffers omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ m_port_settings_changed = true;
++ return true;
++}
++
++
++int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
++{
++ #if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s - %-8p %-6d dts:%.3f pts:%.3f demux_queue(%d) dts_queue(%d) ready_queue(%d) busy_queue(%d)",
++ CLASSNAME, __func__, pData, iSize, dts == DVD_NOPTS_VALUE ? 0.0 : dts*1e-6, pts == DVD_NOPTS_VALUE ? 0.0 : pts*1e-6, m_demux_queue.size(), m_dts_queue.size(), m_omx_output_ready.size(), m_omx_output_busy.size());
++ #endif
++
++ OMX_ERRORTYPE omx_err;
++ unsigned int demuxer_bytes = 0;
++ uint8_t *demuxer_content = NULL;
++
++ // we need to queue then de-queue the demux packet, seems silly but
++ // omx might not have a omx input buffer available when we are called
++ // and we must store the demuxer packet and try again later.
++ if (pData && m_demux_queue.empty() && m_omx_decoder.GetInputBufferSpace() >= (unsigned int)iSize)
++ {
++ demuxer_bytes = iSize;
++ demuxer_content = pData;
++ }
++ else if (pData && iSize)
++ {
+ omx_demux_packet demux_packet;
+ demux_packet.dts = dts;
+ demux_packet.pts = pts;
+-
+- demux_packet.size = demuxer_bytes;
+- demux_packet.buff = new OMX_U8[demuxer_bytes];
+- memcpy(demux_packet.buff, demuxer_content, demuxer_bytes);
++ demux_packet.size = iSize;
++ demux_packet.buff = new OMX_U8[iSize];
++ memcpy(demux_packet.buff, pData, iSize);
+ m_demux_queue.push(demux_packet);
++ }
+
+- // we can look at m_omx_input_avaliable.empty without needing to lock/unlock
++ OMX_U8 *buffer_to_free = NULL;
++ while (1)
++ {
+ // try to send any/all demux packets to omx decoder.
+- while (!m_omx_input_avaliable.empty() && !m_demux_queue.empty() )
++ if (!demuxer_bytes && !m_demux_queue.empty())
+ {
+- OMX_ERRORTYPE omx_err;
+- OMX_BUFFERHEADERTYPE* omx_buffer;
+-
+- demux_packet = m_demux_queue.front();
+- m_demux_queue.pop();
+- // need to lock here to retreve an input buffer and pop the queue
+- pthread_mutex_lock(&m_omx_input_mutex);
+- omx_buffer = m_omx_input_avaliable.front();
+- m_omx_input_avaliable.pop();
+- pthread_mutex_unlock(&m_omx_input_mutex);
+-
+- // delete the previous demuxer buffer
+- delete [] omx_buffer->pBuffer;
+- // setup a new omx_buffer.
+- omx_buffer->nFlags = m_omx_input_eos ? OMX_BUFFERFLAG_EOS : 0;
++ omx_demux_packet &demux_packet = m_demux_queue.front();
++ if (m_omx_decoder.GetInputBufferSpace() >= (unsigned int)demux_packet.size)
++ {
++ // need to lock here to retrieve an input buffer and pop the queue
++ m_demux_queue.pop();
++ demuxer_bytes = (unsigned int)demux_packet.size;
++ demuxer_content = demux_packet.buff;
++ buffer_to_free = demux_packet.buff;
++ dts = demux_packet.dts;
++ pts = demux_packet.pts;
++ }
++ }
++
++ if (demuxer_content)
++ {
++ // 500ms timeout
++ OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(500);
++ if (omx_buffer == NULL)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_decoder.GetInputBuffer timeout", CLASSNAME, __func__);
++ return VC_ERROR;
++ }
++ #if defined(OMX_DEBUG_VERBOSE)
++ //CLog::Log(LOGDEBUG, "%s::%s - omx_buffer=%p", CLASSNAME, __func__, omx_buffer);
++ #endif
++ omx_buffer->nFlags = 0;
+ omx_buffer->nOffset = 0;
+- omx_buffer->pBuffer = demux_packet.buff;
+- omx_buffer->nAllocLen = demux_packet.size;
+- omx_buffer->nFilledLen = demux_packet.size;
+- omx_buffer->nTimeStamp = (demux_packet.pts == DVD_NOPTS_VALUE) ? 0 : demux_packet.pts * 1000.0; // in microseconds;
++
++ omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes;
++ omx_buffer->nTimeStamp = ToOMXTime((uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts);
+ omx_buffer->pAppPrivate = omx_buffer;
+- omx_buffer->nInputPortIndex = m_omx_input_port;
++ memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen);
+
+- #if defined(OMX_DEBUG_EMPTYBUFFERDONE)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - feeding decoder, omx_buffer->pBuffer(0x%p), demuxer_bytes(%d)\n",
+- CLASSNAME, __func__, omx_buffer->pBuffer, demuxer_bytes);
+- #endif
+- // Give this omx_buffer to OpenMax to be decoded.
+- omx_err = OMX_EmptyThisBuffer(m_omx_decoder, omx_buffer);
+- if (omx_err)
++ demuxer_bytes -= omx_buffer->nFilledLen;
++ demuxer_content += omx_buffer->nFilledLen;
++
++ if (demuxer_bytes == 0)
++ omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
++ if (pts == DVD_NOPTS_VALUE)
++ omx_buffer->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN;
++ if (m_drop_state) // hijack an omx flag to signal this frame to be dropped - it will be returned with the picture (but otherwise ignored)
++ omx_buffer->nFlags |= OMX_BUFFERFLAG_DATACORRUPT;
++
++#if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s - %-6d dts:%.3f pts:%.3f flags:%x",
++ CLASSNAME, __func__, omx_buffer->nFilledLen, dts == DVD_NOPTS_VALUE ? 0.0 : dts*1e-6, pts == DVD_NOPTS_VALUE ? 0.0 : pts*1e-6, omx_buffer->nFlags);
++#endif
++
++ omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
++ if (omx_err != OMX_ErrorNone)
+ {
+- CLog::Log(LOGDEBUG,
+- "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
++ CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)", CLASSNAME, __func__, omx_err);
+ return VC_ERROR;
+ }
+- // only push if we are successful with feeding OMX_EmptyThisBuffer
+- m_dts_queue.push(demux_packet.dts);
+-
+- // if m_omx_input_avaliable and/or demux_queue are now empty,
+- // wait up to 20ms for OpenMax to consume a demux packet
+- if (m_omx_input_avaliable.empty() || m_demux_queue.empty())
+- m_input_consumed_event.WaitMSec(1);
++ if (demuxer_bytes == 0)
++ {
++#ifdef DTS_QUEUE
++ // only push if we are successful with feeding OMX_EmptyThisBuffer
++ m_dts_queue.push(dts);
++ assert(m_dts_queue.size() < 32);
++#endif
++ if (buffer_to_free)
++ {
++ delete [] buffer_to_free;
++ buffer_to_free = NULL;
++ demuxer_content = NULL;
++ continue;
++ }
++ }
++ }
++ omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged, 0);
++ if (omx_err == OMX_ErrorNone)
++ {
++ if (!PortSettingsChanged())
++ {
++ CLog::Log(LOGERROR, "%s::%s - error PortSettingsChanged omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return VC_ERROR;
++ }
++ }
++ else if (omx_err != OMX_ErrorTimeout)
++ {
++ CLog::Log(LOGERROR, "%s::%s - video not supported omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return VC_ERROR;
+ }
+- if (m_omx_input_avaliable.empty() && !m_demux_queue.empty())
+- m_input_consumed_event.WaitMSec(1);
+-
+- #if defined(OMX_DEBUG_VERBOSE)
+- if (m_omx_input_avaliable.empty())
+- CLog::Log(LOGDEBUG,
+- "%s::%s - buffering demux, m_demux_queue_size(%d), demuxer_bytes(%d)\n",
+- CLASSNAME, __func__, m_demux_queue.size(), demuxer_bytes);
+- #endif
++ omx_err = m_omx_decoder.WaitForEvent(OMX_EventParamOrConfigChanged, 0);
++ if (omx_err == OMX_ErrorNone)
++ {
++ if (!PortSettingsChanged())
++ {
++ CLog::Log(LOGERROR, "%s::%s - error PortSettingsChanged (EventParamOrConfigChanged) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return VC_ERROR;
++ }
++ }
++ else if (omx_err == OMX_ErrorStreamCorrupt)
++ {
++ CLog::Log(LOGERROR, "%s::%s - video not supported 2 omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return VC_ERROR;
++ }
++ if (!demuxer_bytes)
++ break;
+ }
+
++#if defined(OMX_DEBUG_VERBOSE)
++ if (!m_omx_decoder.GetInputBufferSpace())
++ CLog::Log(LOGDEBUG,
++ "%s::%s - buffering demux, m_demux_queue_size(%d), demuxer_bytes(%d) m_dts_queue.size(%d)",
++ CLASSNAME, __func__, m_demux_queue.size(), demuxer_bytes, m_dts_queue.size());
++ #endif
++
+ if (m_omx_output_ready.empty())
++ {
++ //CLog::Log(LOGDEBUG, "%s::%s - empty: buffers:%d", CLASSNAME, __func__, m_omx_output_ready.size());
+ return VC_BUFFER;
++ }
+
+- return VC_PICTURE | VC_BUFFER;
++ //CLog::Log(LOGDEBUG, "%s::%s - full: buffers:%d", CLASSNAME, __func__, m_omx_output_ready.size());
++ return VC_PICTURE;
+ }
+
+ void COpenMaxVideo::Reset(void)
+ {
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
+-/*
+- // only reset OpenMax decoder if it's running
+- if (m_omx_decoder_state == OMX_StateExecuting)
++ m_omx_egl_render.FlushAll();
++ m_omx_decoder.FlushAll();
++ // blow all ready video frames
++ while (!m_omx_output_ready.empty())
+ {
+- OMX_ERRORTYPE omx_err;
+-
+- omx_err = StopDecoder();
+- // Alloc OpenMax input buffers.
+- omx_err = AllocOMXInputBuffers();
+- // Alloc OpenMax output buffers.
+- omx_err = AllocOMXOutputBuffers();
+-
+- omx_err = StartDecoder();
++ pthread_mutex_lock(&m_omx_output_mutex);
++ COpenMaxVideoBuffer *pic = m_omx_output_ready.front();
++ m_omx_output_ready.pop();
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ // return the omx buffer back to OpenMax to fill.
++ ReturnOpenMaxBuffer(pic);
+ }
+-*/
+- ::Sleep(100);
++#ifdef DTS_QUEUE
++ while (!m_dts_queue.empty())
++ m_dts_queue.pop();
++#endif
++
++ while (!m_demux_queue.empty())
++ m_demux_queue.pop();
+ }
+
+-bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
++
++OMX_ERRORTYPE COpenMaxVideo::ReturnOpenMaxBuffer(COpenMaxVideoBuffer *buffer)
+ {
+- while (m_omx_output_busy.size() > 1)
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
++#if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s %p (%d)", CLASSNAME, __func__, buffer, m_omx_output_busy.size());
++#endif
++ bool done = buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_EOS;
++ if (!done)
+ {
+- // fetch a output buffer and pop it off the busy list
+- pthread_mutex_lock(&m_omx_output_mutex);
+- OpenMaxVideoBuffer *buffer = m_omx_output_busy.front();
+- m_omx_output_busy.pop();
+- pthread_mutex_unlock(&m_omx_output_mutex);
++ // return the omx buffer back to OpenMax to fill.
++ buffer->omx_buffer->nFlags = 0;
++ buffer->omx_buffer->nFilledLen = 0;
+
+- bool done = buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_EOS;
+- if (!done)
+- {
+- // return the omx buffer back to OpenMax to fill.
+- OMX_ERRORTYPE omx_err = OMX_FillThisBuffer(m_omx_decoder, buffer->omx_buffer);
+- if (omx_err)
+- CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- }
++ assert(buffer->omx_buffer->nOutputPortIndex == m_omx_egl_render.GetOutputPort());
++#if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s FillThisBuffer(%p) %p->%ld", CLASSNAME, __func__, buffer, buffer->omx_buffer, buffer->m_refs);
++#endif
++ OMX_ERRORTYPE omx_err = m_omx_egl_render.FillThisBuffer(buffer->omx_buffer);
++
++ if (omx_err)
++ CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)", CLASSNAME, __func__, omx_err);
+ }
++ return omx_err;
++}
++
++void COpenMaxVideo::ReleaseOpenMaxBuffer(COpenMaxVideoBuffer *buffer)
++{
++ // remove from busy list
++ pthread_mutex_lock(&m_omx_output_mutex);
++ m_omx_output_busy.erase(std::remove(m_omx_output_busy.begin(), m_omx_output_busy.end(), buffer), m_omx_output_busy.end());
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ ReturnOpenMaxBuffer(buffer);
++}
++
++bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
++{
++ //CLog::Log(LOGDEBUG, "%s::%s - m_omx_output_busy.size()=%d m_omx_output_ready.size()=%d", CLASSNAME, __func__, m_omx_output_busy.size(), m_omx_output_ready.size());
++ //CLog::Log(LOGDEBUG, "%s::%s - full: buffers:%d", CLASSNAME, __func__, m_omx_output_ready.size());
+
+ if (!m_omx_output_ready.empty())
+ {
+- OpenMaxVideoBuffer *buffer;
++ COpenMaxVideoBuffer *buffer;
+ // fetch a output buffer and pop it off the ready list
+ pthread_mutex_lock(&m_omx_output_mutex);
+ buffer = m_omx_output_ready.front();
+ m_omx_output_ready.pop();
+- m_omx_output_busy.push(buffer);
++ m_omx_output_busy.push_back(buffer);
+ pthread_mutex_unlock(&m_omx_output_mutex);
+
++ memset(pDvdVideoPicture, 0, sizeof *pDvdVideoPicture);
+ pDvdVideoPicture->dts = DVD_NOPTS_VALUE;
+ pDvdVideoPicture->pts = DVD_NOPTS_VALUE;
+ pDvdVideoPicture->format = RENDER_FMT_OMXEGL;
+- pDvdVideoPicture->openMax = this;
+ pDvdVideoPicture->openMaxBuffer = buffer;
+-
++ pDvdVideoPicture->color_range = 0;
++ pDvdVideoPicture->color_matrix = 4;
++ pDvdVideoPicture->iWidth = m_decoded_width;
++ pDvdVideoPicture->iHeight = m_decoded_height;
++ pDvdVideoPicture->iDisplayWidth = m_decoded_width;
++ pDvdVideoPicture->iDisplayHeight = m_decoded_height;
++
++#ifdef DTS_QUEUE
+ if (!m_dts_queue.empty())
+ {
+ pDvdVideoPicture->dts = m_dts_queue.front();
+ m_dts_queue.pop();
+ }
++#endif
+ // nTimeStamp is in microseconds
+- pDvdVideoPicture->pts = (buffer->omx_buffer->nTimeStamp == 0) ? DVD_NOPTS_VALUE : (double)buffer->omx_buffer->nTimeStamp / 1000.0;
++ double ts = FromOMXTime(buffer->omx_buffer->nTimeStamp);
++ pDvdVideoPicture->pts = (ts == 0) ? DVD_NOPTS_VALUE : ts;
++ pDvdVideoPicture->openMaxBuffer->Acquire();
++ pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED;
++ if (buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_DATACORRUPT)
++ pDvdVideoPicture->iFlags |= DVP_FLAG_DROPPED;
++#if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGINFO, "%s::%s dts:%.3f pts:%.3f flags:%x:%x openMaxBuffer:%p omx_buffer:%p egl_image:%p texture_id:%x", CLASSNAME, __func__,
++ pDvdVideoPicture->dts == DVD_NOPTS_VALUE ? 0.0 : pDvdVideoPicture->dts*1e-6, pDvdVideoPicture->pts == DVD_NOPTS_VALUE ? 0.0 : pDvdVideoPicture->pts*1e-6,
++ pDvdVideoPicture->iFlags, buffer->omx_buffer->nFlags, pDvdVideoPicture->openMaxBuffer, pDvdVideoPicture->openMaxBuffer->omx_buffer, pDvdVideoPicture->openMaxBuffer->egl_image, pDvdVideoPicture->openMaxBuffer->texture_id);
++#endif
+ }
+- #if defined(OMX_DEBUG_VERBOSE)
+ else
+ {
+- CLog::Log(LOGDEBUG, "%s::%s - called but m_omx_output_ready is empty\n",
+- CLASSNAME, __func__);
++ CLog::Log(LOGERROR, "%s::%s - called but m_omx_output_ready is empty", CLASSNAME, __func__);
++ return false;
+ }
+- #endif
+-
+- pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED;
+- pDvdVideoPicture->iFlags |= m_drop_state ? DVP_FLAG_DROPPED : 0;
+-
+ return true;
+ }
+
+-
+-// DecoderEmptyBufferDone -- OpenMax input buffer has been emptied
+-OMX_ERRORTYPE COpenMaxVideo::DecoderEmptyBufferDone(
+- OMX_HANDLETYPE hComponent,
+- OMX_PTR pAppData,
+- OMX_BUFFERHEADERTYPE* pBuffer)
++bool COpenMaxVideo::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
+ {
+- COpenMaxVideo *ctx = static_cast<COpenMaxVideo*>(pAppData);
+-/*
+- #if defined(OMX_DEBUG_EMPTYBUFFERDONE)
+- CLog::Log(LOGDEBUG, "%s::%s - buffer_size(%lu), timestamp(%f)\n",
+- CLASSNAME, __func__, pBuffer->nFilledLen, (double)pBuffer->nTimeStamp / 1000.0);
+- #endif
+-*/
+- // queue free input buffer to avaliable list.
+- pthread_mutex_lock(&ctx->m_omx_input_mutex);
+- ctx->m_omx_input_avaliable.push(pBuffer);
+- ctx->m_input_consumed_event.Set();
+- pthread_mutex_unlock(&ctx->m_omx_input_mutex);
+-
+- return OMX_ErrorNone;
++#if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s - %p", CLASSNAME, __func__, pDvdVideoPicture->openMaxBuffer);
++#endif
++ if (pDvdVideoPicture->format == RENDER_FMT_OMXEGL)
++ pDvdVideoPicture->openMaxBuffer->Release();
++ memset(pDvdVideoPicture, 0, sizeof *pDvdVideoPicture);
++ return true;
+ }
+
+-// DecoderFillBufferDone -- OpenMax output buffer has been filled
++ // DecoderFillBufferDone -- OpenMax output buffer has been filled
+ OMX_ERRORTYPE COpenMaxVideo::DecoderFillBufferDone(
+ OMX_HANDLETYPE hComponent,
+- OMX_PTR pAppData,
+ OMX_BUFFERHEADERTYPE* pBuffer)
+ {
+- COpenMaxVideo *ctx = static_cast<COpenMaxVideo*>(pAppData);
+- OpenMaxVideoBuffer *buffer = (OpenMaxVideoBuffer*)pBuffer->pAppPrivate;
++ COpenMaxVideoBuffer *buffer = (COpenMaxVideoBuffer*)pBuffer->pAppPrivate;
+
+- #if defined(OMX_DEBUG_FILLBUFFERDONE)
+- CLog::Log(LOGDEBUG, "%s::%s - buffer_size(%lu), timestamp(%f)\n",
+- CLASSNAME, __func__, pBuffer->nFilledLen, (double)pBuffer->nTimeStamp / 1000.0);
++ #if defined(OMX_DEBUG_VERBOSE)
++ CLog::Log(LOGDEBUG, "%s::%s - %p (%p,%p) buffer_size(%u), pts:%.3f",
++ CLASSNAME, __func__, buffer, pBuffer, buffer->omx_buffer, pBuffer->nFilledLen, (double)FromOMXTime(buffer->omx_buffer->nTimeStamp)*1e-6);
+ #endif
+
+- if (!ctx->m_portChanging)
+- {
+- // queue output omx buffer to ready list.
+- pthread_mutex_lock(&ctx->m_omx_output_mutex);
+- ctx->m_omx_output_ready.push(buffer);
+- pthread_mutex_unlock(&ctx->m_omx_output_mutex);
+- }
++ // queue output omx buffer to ready list.
++ pthread_mutex_lock(&m_omx_output_mutex);
++ m_omx_output_ready.push(buffer);
++ pthread_mutex_unlock(&m_omx_output_mutex);
+
+ return OMX_ErrorNone;
+ }
+
+-void COpenMaxVideo::QueryCodec(void)
+-{
+- OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+- OMX_VIDEO_PARAM_PROFILELEVELTYPE port_param;
+- OMX_INIT_STRUCTURE(port_param);
+-
+- port_param.nPortIndex = m_omx_input_port;
+-
+- for (port_param.nProfileIndex = 0;; port_param.nProfileIndex++)
+- {
+- omx_err = OMX_GetParameter(m_omx_decoder,
+- OMX_IndexParamVideoProfileLevelQuerySupported, &port_param);
+- if (omx_err)
+- break;
+-
+- omx_codec_capability omx_capability;
+- omx_capability.level = port_param.eLevel;
+- omx_capability.profile = port_param.eProfile;
+- m_omx_decoder_capabilities.push_back(omx_capability);
+- }
+-}
+-
+ OMX_ERRORTYPE COpenMaxVideo::PrimeFillBuffers(void)
+ {
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+- OpenMaxVideoBuffer *buffer;
++ COpenMaxVideoBuffer *buffer;
+
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
+ // tell OpenMax to start filling output buffers
+ for (size_t i = 0; i < m_omx_output_buffers.size(); i++)
+ {
+ buffer = m_omx_output_buffers[i];
+ // always set the port index.
+- buffer->omx_buffer->nOutputPortIndex = m_omx_output_port;
+- // Need to clear the EOS flag.
+- buffer->omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
++ buffer->omx_buffer->nOutputPortIndex = m_omx_egl_render.GetOutputPort();
+ buffer->omx_buffer->pAppPrivate = buffer;
+-
+- omx_err = OMX_FillThisBuffer(m_omx_decoder, buffer->omx_buffer);
+- if (omx_err)
+- CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
++ omx_err = ReturnOpenMaxBuffer(buffer);
+ }
+-
+ return omx_err;
+ }
+
+-OMX_ERRORTYPE COpenMaxVideo::AllocOMXInputBuffers(void)
+-{
+- OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+-
+- // Obtain the information about the decoder input port.
+- OMX_PARAM_PORTDEFINITIONTYPE port_format;
+- OMX_INIT_STRUCTURE(port_format);
+- port_format.nPortIndex = m_omx_input_port;
+- OMX_GetParameter(m_omx_decoder, OMX_IndexParamPortDefinition, &port_format);
+-
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - iport(%d), nBufferCountMin(%lu), nBufferSize(%lu)\n",
+- CLASSNAME, __func__, m_omx_input_port, port_format.nBufferCountMin, port_format.nBufferSize);
+- #endif
+- for (size_t i = 0; i < port_format.nBufferCountMin; i++)
+- {
+- OMX_BUFFERHEADERTYPE *buffer = NULL;
+- // use an external buffer that's sized according to actual demux
+- // packet size, start at internal's buffer size, will get deleted when
+- // we start pulling demuxer packets and using demux packet sized buffers.
+- OMX_U8* data = new OMX_U8[port_format.nBufferSize];
+- omx_err = OMX_UseBuffer(m_omx_decoder, &buffer, m_omx_input_port, NULL, port_format.nBufferSize, data);
+- if (omx_err)
+- {
+- CLog::Log(LOGERROR, "%s::%s - OMX_UseBuffer failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- return(omx_err);
+- }
+- m_omx_input_buffers.push_back(buffer);
+- // don't have to lock/unlock here, we are not decoding
+- m_omx_input_avaliable.push(buffer);
+- }
+- m_omx_input_eos = false;
+-
+- return(omx_err);
+-}
+-OMX_ERRORTYPE COpenMaxVideo::FreeOMXInputBuffers(bool wait)
++OMX_ERRORTYPE COpenMaxVideo::FreeOMXInputBuffers(void)
+ {
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
+- /*
+- omx_err = OMX_SendCommand(m_omx_decoder, OMX_CommandFlush, m_omx_input_port, 0);
+- if (omx_err)
+- CLog::Log(LOGERROR, "%s::%s - OMX_CommandFlush failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- else if (wait)
+- sem_wait(m_omx_flush_input);
+- */
+-
+- // free omx input port buffers.
+- for (size_t i = 0; i < m_omx_input_buffers.size(); i++)
+- {
+- // using external buffers (OMX_UseBuffer), free our external buffers
+- // before calling OMX_FreeBuffer which frees the omx buffer.
+- delete [] m_omx_input_buffers[i]->pBuffer;
+- m_omx_input_buffers[i]->pBuffer = NULL;
+- omx_err = OMX_FreeBuffer(m_omx_decoder, m_omx_input_port, m_omx_input_buffers[i]);
+- }
+- m_omx_input_buffers.clear();
+-
+ // empty input buffer queue. not decoding so don't need lock/unlock.
+- while (!m_omx_input_avaliable.empty())
+- m_omx_input_avaliable.pop();
+ while (!m_demux_queue.empty())
+ m_demux_queue.pop();
++#ifdef DTS_QUEUE
+ while (!m_dts_queue.empty())
+ m_dts_queue.pop();
+-
++#endif
+ return(omx_err);
+ }
+
+-void COpenMaxVideo::CallbackAllocOMXEGLTextures(void *userdata)
++bool COpenMaxVideo::CallbackAllocOMXEGLTextures(void *userdata)
+ {
+ COpenMaxVideo *omx = static_cast<COpenMaxVideo*>(userdata);
+- omx->AllocOMXOutputEGLTextures();
++ return omx->AllocOMXOutputEGLTextures() == OMX_ErrorNone;
+ }
+
+-void COpenMaxVideo::CallbackFreeOMXEGLTextures(void *userdata)
++bool COpenMaxVideo::CallbackFreeOMXEGLTextures(void *userdata)
+ {
+ COpenMaxVideo *omx = static_cast<COpenMaxVideo*>(userdata);
+- omx->FreeOMXOutputEGLTextures(true);
++ return omx->FreeOMXOutputEGLTextures() == OMX_ErrorNone;
+ }
+
+-OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputBuffers(void)
++bool COpenMaxVideo::AllocOMXOutputBuffers(void)
+ {
+- OMX_ERRORTYPE omx_err;
+-
+- if ( g_application.IsCurrentThread() )
+- {
+- omx_err = AllocOMXOutputEGLTextures();
+- }
+- else
+- {
+- ThreadMessageCallback callbackData;
+- callbackData.callback = &CallbackAllocOMXEGLTextures;
+- callbackData.userptr = (void *)this;
+-
+- ThreadMessage tMsg;
+- tMsg.dwMessage = TMSG_CALLBACK;
+- tMsg.lpVoid = (void*)&callbackData;
+-
+- g_application.getApplicationMessenger().SendMessage(tMsg, true);
+-
+- omx_err = OMX_ErrorNone;
+- }
+-
+- return omx_err;
++ return g_OMXImage.SendMessage(CallbackAllocOMXEGLTextures, (void *)this);
+ }
+
+-OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputBuffers(bool wait)
++bool COpenMaxVideo::FreeOMXOutputBuffers(void)
+ {
+- OMX_ERRORTYPE omx_err = FreeOMXOutputEGLTextures(wait);
+-
+- return omx_err;
++ return g_OMXImage.SendMessage(CallbackFreeOMXEGLTextures, (void *)this);
+ }
+
+ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+ {
+- OMX_ERRORTYPE omx_err;
+-
+- if (!eglCreateImageKHR)
+- {
+- GETEXTENSION(PFNEGLCREATEIMAGEKHRPROC, eglCreateImageKHR);
+- }
+-
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ EGLint attrib = EGL_NONE;
+- OpenMaxVideoBuffer *egl_buffer;
+-
+- // Obtain the information about the output port.
+- OMX_PARAM_PORTDEFINITIONTYPE port_format;
+- OMX_INIT_STRUCTURE(port_format);
+- port_format.nPortIndex = m_omx_output_port;
+- omx_err = OMX_GetParameter(m_omx_decoder, OMX_IndexParamPortDefinition, &port_format);
+-
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG,
+- "%s::%s (1) - oport(%d), nFrameWidth(%lu), nFrameHeight(%lu), nStride(%lx), nBufferCountMin(%lu), nBufferSize(%lu)\n",
+- CLASSNAME, __func__, m_omx_output_port,
+- port_format.format.video.nFrameWidth, port_format.format.video.nFrameHeight,port_format.format.video.nStride,
+- port_format.nBufferCountMin, port_format.nBufferSize);
+- #endif
++ COpenMaxVideoBuffer *egl_buffer;
+
+ glActiveTexture(GL_TEXTURE0);
+
+- for (size_t i = 0; i < port_format.nBufferCountMin; i++)
++ for (size_t i = 0; i < m_egl_buffer_count; i++)
+ {
+- egl_buffer = new OpenMaxVideoBuffer;
+- memset(egl_buffer, 0, sizeof(*egl_buffer));
++ egl_buffer = new COpenMaxVideoBuffer(this);
+ egl_buffer->width = m_decoded_width;
+ egl_buffer->height = m_decoded_height;
+
+ glGenTextures(1, &egl_buffer->texture_id);
+ glBindTexture(GL_TEXTURE_2D, egl_buffer->texture_id);
+
++ // no mipmaps
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++
+ // create space for buffer with a texture
+ glTexImage2D(
+ GL_TEXTURE_2D, // target
+@@ -710,8 +928,6 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+ GL_RGBA, // format
+ GL_UNSIGNED_BYTE, // type
+ NULL); // pixels -- will be provided later
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // create EGLImage from texture
+ egl_buffer->egl_image = eglCreateImageKHR(
+@@ -722,49 +938,40 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+ &attrib);
+ if (!egl_buffer->egl_image)
+ {
+- CLog::Log(LOGERROR, "%s::%s - ERROR creating EglImage\n", CLASSNAME, __func__);
++ CLog::Log(LOGERROR, "%s::%s - ERROR creating EglImage", CLASSNAME, __func__);
+ return(OMX_ErrorUndefined);
+ }
+ egl_buffer->index = i;
+
+ // tell decoder output port that it will be using EGLImage
+- omx_err = OMX_UseEGLImage(
+- m_omx_decoder, &egl_buffer->omx_buffer, m_omx_output_port, egl_buffer, egl_buffer->egl_image);
++ omx_err = m_omx_egl_render.UseEGLImage(
++ &egl_buffer->omx_buffer, m_omx_egl_render.GetOutputPort(), egl_buffer, egl_buffer->egl_image);
+ if (omx_err)
+ {
+- CLog::Log(LOGERROR, "%s::%s - OMX_UseEGLImage failed with omx_err(0x%x)\n",
++ CLog::Log(LOGERROR, "%s::%s - OMX_UseEGLImage failed with omx_err(0x%x)",
+ CLASSNAME, __func__, omx_err);
+ return(omx_err);
+ }
+ m_omx_output_buffers.push_back(egl_buffer);
+
+- CLog::Log(LOGDEBUG, "%s::%s - Texture %p Width %d Height %d\n",
++ CLog::Log(LOGDEBUG, "%s::%s - Texture %p Width %d Height %d",
+ CLASSNAME, __func__, egl_buffer->egl_image, egl_buffer->width, egl_buffer->height);
+ }
+- m_omx_output_eos = false;
+- while (!m_omx_output_busy.empty())
+- m_omx_output_busy.pop();
+- while (!m_omx_output_ready.empty())
+- m_omx_output_ready.pop();
+-
+ return omx_err;
+ }
+
+-OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputEGLTextures(bool wait)
++OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputEGLTextures(void)
+ {
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+- OpenMaxVideoBuffer *egl_buffer;
+-
+- if (!eglDestroyImageKHR)
+- {
+- GETEXTENSION(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR);
+- }
++ COpenMaxVideoBuffer *egl_buffer;
+
+ for (size_t i = 0; i < m_omx_output_buffers.size(); i++)
+ {
+ egl_buffer = m_omx_output_buffers[i];
+ // tell decoder output port to stop using the EGLImage
+- omx_err = OMX_FreeBuffer(m_omx_decoder, m_omx_output_port, egl_buffer->omx_buffer);
++ omx_err = m_omx_egl_render.FreeOutputBuffer(egl_buffer->omx_buffer);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s m_omx_egl_render.FreeOutputBuffer(%p) omx_err(0x%08x)", CLASSNAME, __func__, egl_buffer->omx_buffer, omx_err);
+ // destroy egl_image
+ eglDestroyImageKHR(m_egl_display, egl_buffer->egl_image);
+ // free texture
+@@ -777,274 +984,45 @@ OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputEGLTextures(bool wait)
+ }
+
+
+-////////////////////////////////////////////////////////////////////////////////////////////
+-// DecoderEventHandler -- OMX event callback
+-OMX_ERRORTYPE COpenMaxVideo::DecoderEventHandler(
+- OMX_HANDLETYPE hComponent,
+- OMX_PTR pAppData,
+- OMX_EVENTTYPE eEvent,
+- OMX_U32 nData1,
+- OMX_U32 nData2,
+- OMX_PTR pEventData)
+-{
+- OMX_ERRORTYPE omx_err;
+- COpenMaxVideo *ctx = static_cast<COpenMaxVideo*>(pAppData);
+-
+-/*
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG,
+- "COpenMax::%s - hComponent(0x%p), eEvent(0x%x), nData1(0x%lx), nData2(0x%lx), pEventData(0x%p)\n",
+- __func__, hComponent, eEvent, nData1, nData2, pEventData);
+- #endif
+-*/
+-
+- switch (eEvent)
+- {
+- case OMX_EventCmdComplete:
+- switch(nData1)
+- {
+- case OMX_CommandStateSet:
+- ctx->m_omx_decoder_state = (int)nData2;
+- switch (ctx->m_omx_decoder_state)
+- {
+- case OMX_StateInvalid:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_StateInvalid\n", CLASSNAME, __func__);
+- break;
+- case OMX_StateLoaded:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_StateLoaded\n", CLASSNAME, __func__);
+- break;
+- case OMX_StateIdle:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_StateIdle\n", CLASSNAME, __func__);
+- break;
+- case OMX_StateExecuting:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_StateExecuting\n", CLASSNAME, __func__);
+- break;
+- case OMX_StatePause:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_StatePause\n", CLASSNAME, __func__);
+- break;
+- case OMX_StateWaitForResources:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_StateWaitForResources\n", CLASSNAME, __func__);
+- break;
+- default:
+- CLog::Log(LOGDEBUG,
+- "%s::%s - Unknown OMX_Statexxxxx, state(%d)\n",
+- CLASSNAME, __func__, ctx->m_omx_decoder_state);
+- break;
+- }
+- sem_post(ctx->m_omx_decoder_state_change);
+- break;
+- case OMX_CommandFlush:
+- /*
+- if (OMX_ALL == (int)nData2)
+- {
+- sem_post(ctx->m_omx_flush_input);
+- sem_post(ctx->m_omx_flush_output);
+- CLog::Log(LOGDEBUG, "COpenMax::%s - OMX_CommandFlush input/output\n",__func__);
+- }
+- else if (ctx->m_omx_input_port == (int)nData2)
+- {
+- sem_post(ctx->m_omx_flush_input);
+- CLog::Log(LOGDEBUG, "COpenMax::%s - OMX_CommandFlush input\n",__func__);
+- }
+- else if (ctx->m_omx_output_port == (int)nData2)
+- {
+- sem_post(ctx->m_omx_flush_output);
+- CLog::Log(LOGDEBUG, "COpenMax::%s - OMX_CommandFlush ouput\n",__func__);
+- }
+- else
+- */
+- {
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - OMX_CommandFlush, nData2(0x%lx)\n",
+- CLASSNAME, __func__, nData2);
+- #endif
+- }
+- break;
+- case OMX_CommandPortDisable:
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - OMX_CommandPortDisable, nData1(0x%lx), nData2(0x%lx)\n",
+- CLASSNAME, __func__, nData1, nData2);
+- #endif
+- if (ctx->m_omx_output_port == (int)nData2)
+- {
+- // Got OMX_CommandPortDisable event, alloc new buffers for the output port.
+- ctx->AllocOMXOutputBuffers();
+- omx_err = OMX_SendCommand(ctx->m_omx_decoder, OMX_CommandPortEnable, ctx->m_omx_output_port, NULL);
+- }
+- break;
+- case OMX_CommandPortEnable:
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - OMX_CommandPortEnable, nData1(0x%lx), nData2(0x%lx)\n",
+- CLASSNAME, __func__, nData1, nData2);
+- #endif
+- if (ctx->m_omx_output_port == (int)nData2)
+- {
+- // Got OMX_CommandPortEnable event.
+- // OMX_CommandPortDisable will have re-alloced new ones so re-prime
+- ctx->PrimeFillBuffers();
+- }
+- ctx->m_portChanging = false;
+- break;
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- case OMX_CommandMarkBuffer:
+- CLog::Log(LOGDEBUG,
+- "%s::%s - OMX_CommandMarkBuffer, nData1(0x%lx), nData2(0x%lx)\n",
+- CLASSNAME, __func__, nData1, nData2);
+- break;
+- #endif
+- }
+- break;
+- case OMX_EventBufferFlag:
+- if (ctx->m_omx_decoder == hComponent && (nData2 & OMX_BUFFERFLAG_EOS)) {
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- if(ctx->m_omx_input_port == (int)nData1)
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_EventBufferFlag(input)\n",
+- CLASSNAME, __func__);
+- #endif
+- if(ctx->m_omx_output_port == (int)nData1)
+- {
+- ctx->m_videoplayback_done = true;
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_EventBufferFlag(output)\n",
+- CLASSNAME, __func__);
+- #endif
+- }
+- }
+- break;
+- case OMX_EventPortSettingsChanged:
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- CLog::Log(LOGDEBUG,
+- "%s::%s - OMX_EventPortSettingsChanged(output)\n", CLASSNAME, __func__);
+- #endif
+- // not sure nData2 is the input/output ports in this call, docs don't say
+- if (ctx->m_omx_output_port == (int)nData2)
+- {
+- // free the current OpenMax output buffers, you must do this before sending
+- // OMX_CommandPortDisable to component as it expects output buffers
+- // to be freed before it will issue a OMX_CommandPortDisable event.
+- ctx->m_portChanging = true;
+- OMX_SendCommand(ctx->m_omx_decoder, OMX_CommandPortDisable, ctx->m_omx_output_port, NULL);
+- omx_err = ctx->FreeOMXOutputBuffers(false);
+- }
+- break;
+- #if defined(OMX_DEBUG_EVENTHANDLER)
+- case OMX_EventMark:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_EventMark\n", CLASSNAME, __func__);
+- break;
+- case OMX_EventResourcesAcquired:
+- CLog::Log(LOGDEBUG, "%s::%s - OMX_EventResourcesAcquired\n", CLASSNAME, __func__);
+- break;
+- #endif
+- case OMX_EventError:
+- switch((OMX_S32)nData1)
+- {
+- case OMX_ErrorInsufficientResources:
+- CLog::Log(LOGERROR, "%s::%s - OMX_EventError, insufficient resources\n",
+- CLASSNAME, __func__);
+- // we are so frack'ed
+- //exit(0);
+- break;
+- case OMX_ErrorFormatNotDetected:
+- CLog::Log(LOGERROR, "%s::%s - OMX_EventError, cannot parse input stream\n",
+- CLASSNAME, __func__);
+- break;
+- case OMX_ErrorPortUnpopulated:
+- // silently ignore these. We can get them when setting OMX_CommandPortDisable
+- // on the output port and the component flushes the output buffers.
+- break;
+- case OMX_ErrorStreamCorrupt:
+- CLog::Log(LOGERROR, "%s::%s - OMX_EventError, Bitstream corrupt\n",
+- CLASSNAME, __func__);
+- ctx->m_videoplayback_done = true;
+- break;
+- default:
+- CLog::Log(LOGERROR, "%s::%s - OMX_EventError detected, nData1(0x%lx), nData2(0x%lx)\n",
+- CLASSNAME, __func__, nData1, nData2);
+- break;
+- }
+- // do this so we don't hang on errors
+- /*
+- sem_post(ctx->m_omx_flush_input);
+- sem_post(ctx->m_omx_flush_output);
+- */
+- sem_post(ctx->m_omx_decoder_state_change);
+- break;
+- default:
+- CLog::Log(LOGWARNING,
+- "%s::%s - Unknown eEvent(0x%x), nData1(0x%lx), nData2(0x%lx)\n",
+- CLASSNAME, __func__, eEvent, nData1, nData2);
+- break;
+- }
+-
+- return OMX_ErrorNone;
+-}
+-
+-// StartPlayback -- Kick off video playback.
+-OMX_ERRORTYPE COpenMaxVideo::StartDecoder(void)
+-{
+- OMX_ERRORTYPE omx_err;
+-
+- #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
+- #endif
+-
+- // transition decoder component to IDLE state
+- omx_err = SetStateForComponent(OMX_StateIdle);
+- if (omx_err)
+- {
+- CLog::Log(LOGERROR, "%s::%s - setting OMX_StateIdle failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- return omx_err;
+- }
+-
+- // transition decoder component to executing state
+- omx_err = SetStateForComponent(OMX_StateExecuting);
+- if (omx_err)
+- {
+- CLog::Log(LOGERROR, "%s::%s - setting OMX_StateExecuting failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- return omx_err;
+- }
+-
+- //prime the omx output buffers.
+- PrimeFillBuffers();
+-
+- return omx_err;
+-}
+-
+ // StopPlayback -- Stop video playback
+ OMX_ERRORTYPE COpenMaxVideo::StopDecoder(void)
+ {
+- OMX_ERRORTYPE omx_err;
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
++
+ // transition decoder component from executing to idle
+- omx_err = SetStateForComponent(OMX_StateIdle);
+- if (omx_err)
++ if (m_omx_decoder.IsInitialized())
+ {
+- CLog::Log(LOGERROR, "%s::%s - setting OMX_StateIdle failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
+- return omx_err;
++ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateIdle);
++ if (omx_err)
++ CLog::Log(LOGERROR, "%s::%s - setting OMX_StateIdle failed with omx_err(0x%x)",
++ CLASSNAME, __func__, omx_err);
+ }
+
+ // we can free our allocated port buffers in OMX_StateIdle state.
+ // free OpenMax input buffers.
+- FreeOMXInputBuffers(true);
+- // free OpenMax output buffers.
+- FreeOMXOutputBuffers(true);
++ FreeOMXInputBuffers();
++
++ if (m_omx_egl_render.IsInitialized())
++ {
++ omx_err = m_omx_egl_render.SetStateForComponent(OMX_StateIdle);
++ if (omx_err)
++ CLog::Log(LOGERROR, "%s::%s - setting egl OMX_StateIdle failed with omx_err(0x%x)",
++ CLASSNAME, __func__, omx_err);
++ // free OpenMax output buffers.
++ omx_err = m_omx_egl_render.DisablePort(m_omx_egl_render.GetOutputPort(), false);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s m_omx_egl_render.DisablePort(%d) omx_err(0x%08x)", CLASSNAME, __func__, m_omx_egl_render.GetOutputPort(), omx_err);
+
+- // transition decoder component from idle to loaded
+- omx_err = SetStateForComponent(OMX_StateLoaded);
+- if (omx_err)
+- CLog::Log(LOGERROR,
+- "%s::%s - setting OMX_StateLoaded failed with omx_err(0x%x)\n",
+- CLASSNAME, __func__, omx_err);
++ FreeOMXOutputBuffers();
+
++ omx_err = m_omx_egl_render.WaitForCommand(OMX_CommandPortDisable, m_omx_egl_render.GetOutputPort());
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s WaitForCommand:OMX_CommandPortDisable omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ }
+ return omx_err;
+ }
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+index e06c41d..9079c13 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+@@ -21,12 +21,35 @@
+
+ #if defined(HAVE_LIBOPENMAX)
+
+-#include "OpenMax.h"
++#include "system_gl.h"
+ #include <EGL/egl.h>
+ #include <EGL/eglext.h>
+
++#include "linux/OMXCore.h"
++#include "linux/OMXClock.h"
++
++#include "cores/dvdplayer/DVDStreamInfo.h"
++#include "DVDVideoCodec.h"
++#include "threads/Event.h"
++
++#include <queue>
++#include <semaphore.h>
++
++typedef struct omx_demux_packet {
++ OMX_U8 *buff;
++ int size;
++ double dts;
++ double pts;
++} omx_demux_packet;
++
++class COpenMaxVideo;
+ // an omx egl video frame
+-typedef struct OpenMaxVideoBuffer {
++class COpenMaxVideoBuffer
++{
++public:
++ COpenMaxVideoBuffer(COpenMaxVideo *omv);
++ virtual ~COpenMaxVideoBuffer();
++
+ OMX_BUFFERHEADERTYPE *omx_buffer;
+ int width;
+ int height;
+@@ -35,79 +58,86 @@ typedef struct OpenMaxVideoBuffer {
+ // used for egl based rendering if active
+ EGLImageKHR egl_image;
+ GLuint texture_id;
+-} OpenMaxVideoBuffer;
++ // reference counting
++ COpenMaxVideoBuffer* Acquire();
++ long Release();
++ void Sync();
++
++ COpenMaxVideo *m_omv;
++ long m_refs;
++private:
++};
+
+-class COpenMaxVideo : public COpenMax
++class COpenMaxVideo
+ {
+ public:
+ COpenMaxVideo();
+ virtual ~COpenMaxVideo();
+
+ // Required overrides
+- bool Open(CDVDStreamInfo &hints);
+- void Close(void);
+- int Decode(uint8_t *pData, int iSize, double dts, double pts);
+- void Reset(void);
+- bool GetPicture(DVDVideoPicture *pDvdVideoPicture);
+- void SetDropState(bool bDrop);
++ virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
++ virtual void Dispose(void);
++ virtual int Decode(uint8_t *pData, int iSize, double dts, double pts);
++ virtual void Reset(void);
++ virtual bool GetPicture(DVDVideoPicture *pDvdVideoPicture);
++ virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture);
++ virtual unsigned GetAllowedReferences() { return 2; }
++ virtual void SetDropState(bool bDrop);
++ virtual const char* GetName(void) { return (const char*)m_pFormatName; }
++
++ // OpenMax decoder callback routines.
++ OMX_ERRORTYPE DecoderFillBufferDone(OMX_HANDLETYPE hComponent, OMX_BUFFERHEADERTYPE* pBuffer);
++ void ReleaseOpenMaxBuffer(COpenMaxVideoBuffer *buffer);
++
+ protected:
+ void QueryCodec(void);
+ OMX_ERRORTYPE PrimeFillBuffers(void);
+ OMX_ERRORTYPE AllocOMXInputBuffers(void);
+- OMX_ERRORTYPE FreeOMXInputBuffers(bool wait);
+- OMX_ERRORTYPE AllocOMXOutputBuffers(void);
+- OMX_ERRORTYPE FreeOMXOutputBuffers(bool wait);
+- static void CallbackAllocOMXEGLTextures(void*);
++ OMX_ERRORTYPE FreeOMXInputBuffers(void);
++ bool AllocOMXOutputBuffers(void);
++ bool FreeOMXOutputBuffers(void);
++ static bool CallbackAllocOMXEGLTextures(void*);
+ OMX_ERRORTYPE AllocOMXOutputEGLTextures(void);
+- static void CallbackFreeOMXEGLTextures(void*);
+- OMX_ERRORTYPE FreeOMXOutputEGLTextures(bool wait);
+-
+- // TODO Those should move into the base class. After start actions can be executed by callbacks.
+- OMX_ERRORTYPE StartDecoder(void);
++ static bool CallbackFreeOMXEGLTextures(void*);
++ OMX_ERRORTYPE FreeOMXOutputEGLTextures(void);
+ OMX_ERRORTYPE StopDecoder(void);
+-
+- // OpenMax decoder callback routines.
+- virtual OMX_ERRORTYPE DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData);
+- virtual OMX_ERRORTYPE DecoderEmptyBufferDone(
+- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer);
+- virtual OMX_ERRORTYPE DecoderFillBufferDone(
+- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader);
++ OMX_ERRORTYPE ReturnOpenMaxBuffer(COpenMaxVideoBuffer *buffer);
+
+ // EGL Resources
+ EGLDisplay m_egl_display;
+ EGLContext m_egl_context;
+
+ // Video format
+- DVDVideoPicture m_videobuffer;
+ bool m_drop_state;
+ int m_decoded_width;
+ int m_decoded_height;
++ unsigned int m_egl_buffer_count;
++
++ bool m_port_settings_changed;
++ const char *m_pFormatName;
+
+ std::queue<double> m_dts_queue;
+ std::queue<omx_demux_packet> m_demux_queue;
+
+- // OpenMax input buffers (demuxer packets)
+- pthread_mutex_t m_omx_input_mutex;
+- std::queue<OMX_BUFFERHEADERTYPE*> m_omx_input_avaliable;
+- std::vector<OMX_BUFFERHEADERTYPE*> m_omx_input_buffers;
+- bool m_omx_input_eos;
+- int m_omx_input_port;
+- //sem_t *m_omx_flush_input;
+- CEvent m_input_consumed_event;
+-
+ // OpenMax output buffers (video frames)
+ pthread_mutex_t m_omx_output_mutex;
+- std::queue<OpenMaxVideoBuffer*> m_omx_output_busy;
+- std::queue<OpenMaxVideoBuffer*> m_omx_output_ready;
+- std::vector<OpenMaxVideoBuffer*> m_omx_output_buffers;
+- bool m_omx_output_eos;
+- int m_omx_output_port;
+- //sem_t *m_omx_flush_output;
++ std::vector<COpenMaxVideoBuffer*> m_omx_output_busy;
++ std::queue<COpenMaxVideoBuffer*> m_omx_output_ready;
++ std::vector<COpenMaxVideoBuffer*> m_omx_output_buffers;
++
++ // initialize OpenMax and get decoder component
++ bool Initialize( const CStdString &decoder_name);
++
++ // Components
++ COMXCoreComponent m_omx_decoder;
++ COMXCoreComponent m_omx_egl_render;
+
+- bool m_portChanging;
++ COMXCoreTunel m_omx_tunnel;
++ OMX_VIDEO_CODINGTYPE m_codingType;
+
+- volatile bool m_videoplayback_done;
++ bool PortSettingsChanged();
++ bool SendDecoderConfig(uint8_t *extradata, int extrasize);
++ bool NaluFormatStartCodes(enum AVCodecID codec, uint8_t *extradata, int extrasize);
+ };
+
+ // defined(HAVE_LIBOPENMAX)
+diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
+index a485275..d607f55 100644
+--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
++++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
+@@ -2988,7 +2988,9 @@ bool CDVDPlayer::OpenVideoStream(int iStream, int source, bool reset)
+ hint.aspect = aspect;
+ hint.forced_aspect = true;
+ }
++#ifndef TARGET_RASPBERRY_PI
+ hint.software = true;
++#endif
+ }
+
+ boost::shared_ptr<CPVRClient> client;
+diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
+index 99b3155..fddb7f7 100644
+--- a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
+@@ -325,6 +325,9 @@ void CDVDPlayerVideo::Process()
+
+ while (!m_bStop)
+ {
++ DemuxPacket staticPacket = {};
++ DemuxPacket* pPacket = NULL;
++ bool bPacketDrop = false;
+ int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000;
+ int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
+
+@@ -361,8 +364,10 @@ void CDVDPlayerVideo::Process()
+ OutputPicture(&picture, pts);
+ pts+= frametime;
+ }
+-
+- continue;
++ pPacket = &staticPacket;
++ bPacketDrop = false;
++ goto submit_empty_packet;
++ //continue;
+ }
+
+ if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
+@@ -489,9 +494,12 @@ void CDVDPlayerVideo::Process()
+
+ if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
+ {
+- DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
+- bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
+-
++ pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
++ bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
++ }
++submit_empty_packet:
++ if (ret == MSGQ_TIMEOUT || pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
++ {
+ if (m_stalled)
+ {
+ CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe left, switching to normal playback");
+@@ -749,7 +757,8 @@ void CDVDPlayerVideo::Process()
+ }
+
+ // all data is used by the decoder, we can safely free it now
+- pMsg->Release();
++ if (ret != MSGQ_TIMEOUT)
++ pMsg->Release();
+ }
+
+ // we need to let decoder release any picture retained resources.
+diff --git a/xbmc/linux/OMXCore.cpp b/xbmc/linux/OMXCore.cpp
+index 6e7d9a9..99e407a 100644
+--- a/xbmc/linux/OMXCore.cpp
++++ b/xbmc/linux/OMXCore.cpp
+@@ -460,7 +460,12 @@ void COMXCoreComponent::FlushInput()
+ CLog::Log(LOGERROR, "COMXCoreComponent::FlushInput - Error on component %s omx_err(0x%08x)",
+ m_componentName.c_str(), (int)omx_err);
+ }
+- WaitForCommand(OMX_CommandFlush, m_input_port);
++ omx_err = WaitForCommand(OMX_CommandFlush, m_input_port);
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "COMXCoreComponent::FlushInput - %s WaitForCommand omx_err(0x%08x)",
++ m_componentName.c_str(), (int)omx_err);
++ }
+ }
+
+ void COMXCoreComponent::FlushOutput()
+@@ -477,7 +482,12 @@ void COMXCoreComponent::FlushOutput()
+ CLog::Log(LOGERROR, "COMXCoreComponent::FlushOutput - Error on component %s omx_err(0x%08x)",
+ m_componentName.c_str(), (int)omx_err);
+ }
+- WaitForCommand(OMX_CommandFlush, m_output_port);
++ omx_err = WaitForCommand(OMX_CommandFlush, m_output_port);
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "COMXCoreComponent::FlushOutput - %s WaitForCommand omx_err(0x%08x)",
++ m_componentName.c_str(), (int)omx_err);
++ }
+ }
+
+ // timeout in milliseconds
+@@ -1149,7 +1159,12 @@ OMX_STATETYPE COMXCoreComponent::GetState()
+
+ OMX_STATETYPE state;
+
+- OMX_GetState(m_handle, &state);
++ OMX_ERRORTYPE omx_err = OMX_GetState(m_handle, &state);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "COMXCoreComponent::GetState - %s failed with omx_err(0x%x)\n",
++ m_componentName.c_str(), omx_err);
++ }
+ return state;
+ }
+
+@@ -1307,6 +1322,8 @@ OMX_ERRORTYPE COMXCoreComponent::DisablePort(unsigned int port, bool wait)
+
+ OMX_ERRORTYPE COMXCoreComponent::UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage)
+ {
++if (m_callbacks.FillBufferDone == &COMXCoreComponent::DecoderFillBufferDoneCallback)
++{
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
+ if(!m_handle)
+@@ -1383,8 +1400,21 @@ OMX_ERRORTYPE COMXCoreComponent::UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr,
+
+ return omx_err;
+ }
++else
++{
++ OMX_ERRORTYPE omx_err;
++ omx_err = OMX_UseEGLImage(m_handle, ppBufferHdr, nPortIndex, pAppPrivate, eglImage);
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - %s failed with omx_err(0x%x)\n",
++ CLASSNAME, __func__, m_componentName.c_str(), omx_err);
++ return omx_err;
++ }
++ return omx_err;
++}
++}
+
+-bool COMXCoreComponent::Initialize( const std::string &component_name, OMX_INDEXTYPE index)
++bool COMXCoreComponent::Initialize( const std::string &component_name, OMX_INDEXTYPE index, OMX_CALLBACKTYPE *callbacks)
+ {
+ OMX_ERRORTYPE omx_err;
+
+@@ -1419,6 +1449,13 @@ bool COMXCoreComponent::Initialize( const std::string &component_name, OMX_INDEX
+ m_callbacks.EmptyBufferDone = &COMXCoreComponent::DecoderEmptyBufferDoneCallback;
+ m_callbacks.FillBufferDone = &COMXCoreComponent::DecoderFillBufferDoneCallback;
+
++ if (callbacks && callbacks->EventHandler)
++ m_callbacks.EventHandler = callbacks->EventHandler;
++ if (callbacks && callbacks->EmptyBufferDone)
++ m_callbacks.EmptyBufferDone = callbacks->EmptyBufferDone;
++ if (callbacks && callbacks->FillBufferDone)
++ m_callbacks.FillBufferDone = callbacks->FillBufferDone;
++
+ // Get video component handle setting up callbacks, component is in loaded state on return.
+ if(!m_handle)
+ {
+diff --git a/xbmc/linux/OMXCore.h b/xbmc/linux/OMXCore.h
+index 54d35aa..5b9c2f9 100644
+--- a/xbmc/linux/OMXCore.h
++++ b/xbmc/linux/OMXCore.h
+@@ -109,7 +109,7 @@ class COMXCoreComponent
+ OMX_ERRORTYPE DisablePort(unsigned int port, bool wait = true);
+ OMX_ERRORTYPE UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage);
+
+- bool Initialize( const std::string &component_name, OMX_INDEXTYPE index);
++ bool Initialize( const std::string &component_name, OMX_INDEXTYPE index, OMX_CALLBACKTYPE *callbacks = NULL);
+ bool IsInitialized();
+ bool Deinitialize();
+
+--
+1.9.3
+
+
+From e9b71fb1ee80896444d3301f919bf315a96530a3 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Sat, 3 May 2014 11:57:25 +0100
+Subject: [PATCH 43/94] [omxcodec] Enable for dvd menus
+
+---
+ xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+index 18b8e3a..c85c8d2 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+@@ -194,6 +194,10 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne
+
+ CLog::Log(LOGDEBUG, "CDVDFactoryCodec: compiled in hardware support: %s", hwSupport.c_str());
+
++#if defined(HAVE_LIBOPENMAX)
++ // libopenmax can handle dvd playback including stills
++ if (!CSettings::Get().GetBool("videoplayer.useomx"))
++#endif
+ if (hint.stills && (hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_MPEG1VIDEO))
+ {
+ // If dvd is an mpeg2 and hint.stills
+--
+1.9.3
+
+
+From a7a4ccc26d85d1362a77b718564ddb1f08ca1246 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 3 Feb 2014 22:27:44 +0000
+Subject: [PATCH 44/94] [omxcodec] Add omx specific texture
+ create/upload/delete functions
+
+---
+ xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 33 +++++++++++++++++++++++++
+ xbmc/cores/VideoRenderers/LinuxRendererGLES.h | 4 +++
+ 2 files changed, 37 insertions(+)
+
+diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+index 6d879e3..279aa90 100644
+--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+@@ -805,6 +805,12 @@ void CLinuxRendererGLES::LoadShaders(int field)
+ m_textureCreate = &CLinuxRendererGLES::CreateNV12Texture;
+ m_textureDelete = &CLinuxRendererGLES::DeleteNV12Texture;
+ }
++ else if (m_format == RENDER_FMT_OMXEGL)
++ {
++ m_textureUpload = &CLinuxRendererGLES::UploadOMXEGLTexture;
++ m_textureCreate = &CLinuxRendererGLES::CreateOMXEGLTexture;
++ m_textureDelete = &CLinuxRendererGLES::DeleteOMXEGLTexture;
++ }
+ else
+ {
+ // default to YV12 texture handlers
+@@ -2487,6 +2493,33 @@ bool CLinuxRendererGLES::CreateSurfaceTexture(int index)
+ return true;
+ }
+
++//********************************************************************************************************
++// SurfaceTexture creation, deletion, copying + clearing
++//********************************************************************************************************
++void CLinuxRendererGLES::UploadOMXEGLTexture(int index)
++{
++#ifdef HAVE_LIBOPENMAX
++ YUVBUFFER &buf = m_buffers[index];
++ if (buf.openMaxBuffer)
++ {
++ //buf.openMaxBuffer->Sync();
++ }
++#endif
++}
++void CLinuxRendererGLES::DeleteOMXEGLTexture(int index)
++{
++#ifdef HAVE_LIBOPENMAX
++ YUVBUFFER &buf = m_buffers[index];
++ if (buf.openMaxBuffer)
++ SAFE_RELEASE(buf.openMaxBuffer);
++#endif
++}
++bool CLinuxRendererGLES::CreateOMXEGLTexture(int index)
++{
++ DeleteOMXEGLTexture(index);
++ return true;
++}
++
+ void CLinuxRendererGLES::SetTextureFilter(GLenum method)
+ {
+ for (int i = 0 ; i<m_NumYV12Buffers ; i++)
+diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
+index 0ca56a2..f3dd3d3 100644
+--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
+@@ -212,6 +212,10 @@ class CLinuxRendererGLES : public CBaseRenderer
+ void DeleteSurfaceTexture(int index);
+ bool CreateSurfaceTexture(int index);
+
++ void UploadOMXEGLTexture(int index);
++ void DeleteOMXEGLTexture(int index);
++ bool CreateOMXEGLTexture(int index);
++
+ void CalculateTextureSourceRects(int source, int num_planes);
+
+ // renderers
+--
+1.9.3
+
+
+From 6a8bd5509ada85a05ad4672a33b76418573382ea Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 3 Feb 2014 22:50:43 +0000
+Subject: [PATCH 45/94] [omxcodec] Add shared pointer to delay shutdown of
+ codec until buffers are returned
+
+---
+ .../DVDCodecs/Video/DVDVideoCodecOpenMax.cpp | 13 +++----------
+ .../DVDCodecs/Video/DVDVideoCodecOpenMax.h | 3 ++-
+ .../dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 21 ++++++++++++++++++++-
+ xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 5 ++++-
+ 4 files changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
+index 7d33192..ef10555 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
+@@ -28,7 +28,6 @@
+ #include "DVDClock.h"
+ #include "DVDStreamInfo.h"
+ #include "DVDVideoCodecOpenMax.h"
+-#include "OpenMaxVideo.h"
+ #include "settings/Settings.h"
+ #include "utils/log.h"
+
+@@ -36,8 +35,8 @@
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ CDVDVideoCodecOpenMax::CDVDVideoCodecOpenMax()
++ : m_omx_decoder( new COpenMaxVideo )
+ {
+- m_omx_decoder = NULL;
+ CLog::Log(LOGDEBUG, "%s::%s %p\n", CLASSNAME, __func__, this);
+ }
+
+@@ -49,8 +48,7 @@ CDVDVideoCodecOpenMax::~CDVDVideoCodecOpenMax()
+
+ bool CDVDVideoCodecOpenMax::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+ {
+- m_omx_decoder = new COpenMaxVideo;
+- return m_omx_decoder->Open(hints, options);
++ return m_omx_decoder->Open(hints, options, m_omx_decoder);
+ }
+
+ const char* CDVDVideoCodecOpenMax::GetName(void)
+@@ -60,12 +58,7 @@ const char* CDVDVideoCodecOpenMax::GetName(void)
+
+ void CDVDVideoCodecOpenMax::Dispose()
+ {
+- if (m_omx_decoder)
+- {
+- m_omx_decoder->Dispose();
+- delete m_omx_decoder;
+- m_omx_decoder = NULL;
+- }
++ m_omx_decoder->Dispose();
+ }
+
+ void CDVDVideoCodecOpenMax::SetDropState(bool bDrop)
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
+index 67cc235..b7c0c1b 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
+@@ -22,6 +22,7 @@
+ #if defined(HAVE_LIBOPENMAX)
+
+ #include "DVDVideoCodec.h"
++#include "OpenMaxVideo.h"
+
+ class COpenMaxVideo;
+ class CDVDVideoCodecOpenMax : public CDVDVideoCodec
+@@ -42,7 +43,7 @@ class CDVDVideoCodecOpenMax : public CDVDVideoCodec
+ virtual const char* GetName(void);
+
+ protected:
+- COpenMaxVideo *m_omx_decoder;
++ OpenMaxVideoPtr m_omx_decoder;
+ };
+
+ #endif
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+index aca2e0d..29b5bb9 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+@@ -126,6 +126,7 @@ COpenMaxVideo::COpenMaxVideo()
+ m_egl_buffer_count = 0;
+
+ m_port_settings_changed = false;
++ m_finished = false;
+ m_pFormatName = "omx-xxxx";
+ }
+
+@@ -134,6 +135,7 @@ COpenMaxVideo::~COpenMaxVideo()
+ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this);
+ #endif
++ assert(m_finished);
+ if (m_omx_decoder.IsInitialized())
+ {
+ if (m_omx_tunnel.IsInitialized())
+@@ -149,7 +151,7 @@ COpenMaxVideo::~COpenMaxVideo()
+ pthread_mutex_destroy(&m_omx_output_mutex);
+ }
+
+-bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
++bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenMaxVideoPtr myself)
+ {
+ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+@@ -161,6 +163,7 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
++ m_myself = myself;
+ m_decoded_width = hints.width;
+ m_decoded_height = hints.height;
+
+@@ -331,6 +334,15 @@ void COpenMaxVideo::Dispose()
+ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
++ // we are happy to exit, but let last shared pointer being deleted trigger the destructor
++ bool done = false;
++ pthread_mutex_lock(&m_omx_output_mutex);
++ if (m_omx_output_busy.empty())
++ done = true;
++ m_finished = true;
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ if (done)
++ m_myself.reset();
+ }
+
+ void COpenMaxVideo::SetDropState(bool bDrop)
+@@ -752,6 +764,13 @@ void COpenMaxVideo::ReleaseOpenMaxBuffer(COpenMaxVideoBuffer *buffer)
+ m_omx_output_busy.erase(std::remove(m_omx_output_busy.begin(), m_omx_output_busy.end(), buffer), m_omx_output_busy.end());
+ pthread_mutex_unlock(&m_omx_output_mutex);
+ ReturnOpenMaxBuffer(buffer);
++ bool done = false;
++ pthread_mutex_lock(&m_omx_output_mutex);
++ if (m_finished && m_omx_output_busy.empty())
++ done = true;
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ if (done)
++ m_myself.reset();
+ }
+
+ bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+index 9079c13..0975e8a 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+@@ -43,6 +43,7 @@ typedef struct omx_demux_packet {
+ } omx_demux_packet;
+
+ class COpenMaxVideo;
++typedef boost::shared_ptr<COpenMaxVideo> OpenMaxVideoPtr;
+ // an omx egl video frame
+ class COpenMaxVideoBuffer
+ {
+@@ -75,7 +76,7 @@ class COpenMaxVideo
+ virtual ~COpenMaxVideo();
+
+ // Required overrides
+- virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
++ virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenMaxVideoPtr myself);
+ virtual void Dispose(void);
+ virtual int Decode(uint8_t *pData, int iSize, double dts, double pts);
+ virtual void Reset(void);
+@@ -112,9 +113,11 @@ class COpenMaxVideo
+ int m_decoded_width;
+ int m_decoded_height;
+ unsigned int m_egl_buffer_count;
++ bool m_finished;
+
+ bool m_port_settings_changed;
+ const char *m_pFormatName;
++ OpenMaxVideoPtr m_myself;
+
+ std::queue<double> m_dts_queue;
+ std::queue<omx_demux_packet> m_demux_queue;
+--
+1.9.3
+
+
+From e8f40e625203fe4113e2687d3730c38770cc0857 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 3 Feb 2014 23:11:31 +0000
+Subject: [PATCH 46/94] [omxcodec] Fix for aspect ratio in non-square pixel
+ modes
+
+---
+ xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 17 +++++++++++++++++
+ xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 3 +++
+ 2 files changed, 20 insertions(+)
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+index 29b5bb9..7e23c87 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+@@ -63,6 +63,7 @@ COpenMaxVideoBuffer::COpenMaxVideoBuffer(COpenMaxVideo *omv)
+ index = 0;
+ egl_image = 0;
+ texture_id = 0;
++ m_aspect_ratio = 0.0f;
+ }
+
+ COpenMaxVideoBuffer::~COpenMaxVideoBuffer()
+@@ -166,6 +167,8 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenM
+ m_myself = myself;
+ m_decoded_width = hints.width;
+ m_decoded_height = hints.height;
++ m_forced_aspect_ratio = hints.forced_aspect;
++ m_aspect_ratio = hints.aspect;
+
+ m_egl_display = g_Windowing.GetEGLDisplay();
+ m_egl_context = g_Windowing.GetEGLContext();
+@@ -435,6 +438,9 @@ bool COpenMaxVideo::PortSettingsChanged()
+ CLog::Log(LOGERROR, "%s::%s - error m_omx_decoder.GetParameter(OMX_IndexParamBrcmPixelAspectRatio) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
++ if (!m_forced_aspect_ratio && pixel_aspect.nX && pixel_aspect.nY)
++ m_aspect_ratio = (float)pixel_aspect.nX * port_def.format.video.nFrameWidth /
++ ((float)pixel_aspect.nY * port_def.format.video.nFrameHeight);
+
+ if (m_port_settings_changed)
+ {
+@@ -800,6 +806,16 @@ bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
+ pDvdVideoPicture->iDisplayWidth = m_decoded_width;
+ pDvdVideoPicture->iDisplayHeight = m_decoded_height;
+
++ if (buffer->m_aspect_ratio > 0.0 && !m_forced_aspect_ratio)
++ {
++ pDvdVideoPicture->iDisplayWidth = ((int)lrint(pDvdVideoPicture->iHeight * buffer->m_aspect_ratio)) & -3;
++ if (pDvdVideoPicture->iDisplayWidth > pDvdVideoPicture->iWidth)
++ {
++ pDvdVideoPicture->iDisplayWidth = pDvdVideoPicture->iWidth;
++ pDvdVideoPicture->iDisplayHeight = ((int)lrint(pDvdVideoPicture->iWidth / buffer->m_aspect_ratio)) & -3;
++ }
++ }
++
+ #ifdef DTS_QUEUE
+ if (!m_dts_queue.empty())
+ {
+@@ -853,6 +869,7 @@ OMX_ERRORTYPE COpenMaxVideo::DecoderFillBufferDone(
+
+ // queue output omx buffer to ready list.
+ pthread_mutex_lock(&m_omx_output_mutex);
++ buffer->m_aspect_ratio = m_aspect_ratio;
+ m_omx_output_ready.push(buffer);
+ pthread_mutex_unlock(&m_omx_output_mutex);
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+index 0975e8a..9138a20 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+@@ -54,6 +54,7 @@ class COpenMaxVideoBuffer
+ OMX_BUFFERHEADERTYPE *omx_buffer;
+ int width;
+ int height;
++ float m_aspect_ratio;
+ int index;
+
+ // used for egl based rendering if active
+@@ -114,6 +115,8 @@ class COpenMaxVideo
+ int m_decoded_height;
+ unsigned int m_egl_buffer_count;
+ bool m_finished;
++ float m_aspect_ratio;
++ bool m_forced_aspect_ratio;
+
+ bool m_port_settings_changed;
+ const char *m_pFormatName;
+--
+1.9.3
+
+
+From e42ca92b464ad88dbe0f8b0d86080d64d52e08a8 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 3 Feb 2014 23:19:22 +0000
+Subject: [PATCH 47/94] [omxcodec] Report error when codec not enabled
+
+---
+ xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+index 7e23c87..2ae722b 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+@@ -43,6 +43,7 @@
+ #include <IL/OMX_Image.h>
+
+ #include "cores/omxplayer/OMXImage.h"
++#include "linux/RBP.h"
+
+ #define DTS_QUEUE
+
+@@ -155,7 +156,7 @@ COpenMaxVideo::~COpenMaxVideo()
+ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenMaxVideoPtr myself)
+ {
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
++ CLog::Log(LOGDEBUG, "%s::%s useomx:%d software:%d", CLASSNAME, __func__, CSettings::Get().GetBool("videoplayer.useomx"), hints.software);
+ #endif
+
+ // we always qualify even if DVDFactoryCodec does this too.
+@@ -232,6 +233,13 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenM
+ break;
+ }
+
++ if ( (m_codingType == OMX_VIDEO_CodingMPEG2 && !g_RBP.GetCodecMpg2() ) ||
++ (m_codingType == OMX_VIDEO_CodingWMV && !g_RBP.GetCodecWvc1() ) )
++ {
++ CLog::Log(LOGWARNING, "%s::%s Codec %s is not supported\n", CLASSNAME, __func__, m_pFormatName);
++ return false;
++ }
++
+ // initialize OpenMAX.
+ if (!m_omx_decoder.Initialize("OMX.broadcom.video_decode", OMX_IndexParamVideoInit))
+ {
+--
+1.9.3
+
+
+From 55b0b157ba32d03ca0ec854b7935aee5601810d8 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Tue, 4 Feb 2014 17:29:37 +0000
+Subject: [PATCH 48/94] [omxcodec] Add deinterlace support
+
+---
+ xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 2 +-
+ .../dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 106 ++++++++++++++++++---
+ .../cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 9 +-
+ 3 files changed, 103 insertions(+), 14 deletions(-)
+
+diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+index 279aa90..a57abe4 100644
+--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+@@ -2607,7 +2607,7 @@ bool CLinuxRendererGLES::Supports(EDEINTERLACEMODE mode)
+ return true;
+
+ if(m_renderMethod & RENDER_OMXEGL)
+- return false;
++ return true;
+
+ if(m_renderMethod & RENDER_EGLIMG)
+ return false;
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+index 2ae722b..fbf1458 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+@@ -33,6 +33,7 @@
+ #include "utils/log.h"
+ #include "utils/TimeUtils.h"
+ #include "settings/Settings.h"
++#include "settings/MediaSettings.h"
+ #include "ApplicationMessenger.h"
+ #include "Application.h"
+ #include "threads/Atomics.h"
+@@ -130,6 +131,10 @@ COpenMaxVideo::COpenMaxVideo()
+ m_port_settings_changed = false;
+ m_finished = false;
+ m_pFormatName = "omx-xxxx";
++
++ m_deinterlace = false;
++ m_deinterlace_request = VS_DEINTERLACEMODE_OFF;
++ m_deinterlace_second_field = false;
+ }
+
+ COpenMaxVideo::~COpenMaxVideo()
+@@ -140,13 +145,17 @@ COpenMaxVideo::~COpenMaxVideo()
+ assert(m_finished);
+ if (m_omx_decoder.IsInitialized())
+ {
+- if (m_omx_tunnel.IsInitialized())
+- m_omx_tunnel.Deestablish();
++ if (m_omx_tunnel_decoder.IsInitialized())
++ m_omx_tunnel_decoder.Deestablish();
++ if (m_omx_tunnel_image_fx.IsInitialized())
++ m_omx_tunnel_image_fx.Deestablish();
+
+ StopDecoder();
+
+ if (m_omx_egl_render.IsInitialized())
+ m_omx_egl_render.Deinitialize();
++ if (m_omx_image_fx.IsInitialized())
++ m_omx_image_fx.Deinitialize();
+ if (m_omx_decoder.IsInitialized())
+ m_omx_decoder.Deinitialize();
+ }
+@@ -165,6 +174,8 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenM
+
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
++ m_deinterlace_request = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
++
+ m_myself = myself;
+ m_decoded_width = hints.width;
+ m_decoded_height = hints.height;
+@@ -467,6 +478,49 @@ bool COpenMaxVideo::PortSettingsChanged()
+ return false;
+ }
+
++ OMX_CONFIG_INTERLACETYPE interlace;
++ OMX_INIT_STRUCTURE(interlace);
++ interlace.nPortIndex = m_omx_decoder.GetOutputPort();
++ omx_err = m_omx_decoder.GetConfig(OMX_IndexConfigCommonInterlace, &interlace);
++
++ if (m_deinterlace_request == VS_DEINTERLACEMODE_FORCE)
++ m_deinterlace = true;
++ else if (m_deinterlace_request == VS_DEINTERLACEMODE_OFF)
++ m_deinterlace = false;
++ else
++ m_deinterlace = interlace.eMode != OMX_InterlaceProgressive;
++
++ CLog::Log(LOGDEBUG, "%s::%s - %dx%d@%.2f interlace:%d deinterlace:%d",
++ CLASSNAME, __func__, port_def.format.video.nFrameWidth, port_def.format.video.nFrameHeight, port_def.format.video.xFramerate / (float) (1 << 16),
++ interlace.eMode, m_deinterlace);
++
++ if (m_deinterlace)
++ {
++ if (!m_omx_image_fx.Initialize("OMX.broadcom.image_fx", OMX_IndexParamImageInit))
++ {
++ CLog::Log(LOGERROR, "%s::%s error m_omx_image_fx.Initialize", CLASSNAME, __func__);
++ 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, "%s::%s - OMX_IndexConfigCommonImageFilterParameters omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++ }
++
+ OMX_CALLBACKTYPE callbacks = { NULL, NULL, DecoderFillBufferDoneCallback };
+ if (!m_omx_egl_render.Initialize("OMX.broadcom.egl_render", OMX_IndexParamVideoInit, &callbacks))
+ {
+@@ -487,19 +541,40 @@ bool COpenMaxVideo::PortSettingsChanged()
+
+ m_omx_egl_render.ResetEos();
+
+- CLog::Log(LOGDEBUG, "%s::%s - %dx%d@%.2f interlace:%d deinterlace:%d", CLASSNAME, __func__,
+- port_def.format.video.nFrameWidth, port_def.format.video.nFrameHeight,
+- port_def.format.video.xFramerate / (float)(1<<16), 0,0);
+-
+- m_omx_tunnel.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_egl_render, m_omx_egl_render.GetInputPort());
++ 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_egl_render, m_omx_egl_render.GetInputPort());
++ }
++ else
++ {
++ m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_egl_render, m_omx_egl_render.GetInputPort());
++ }
+
+- omx_err = m_omx_tunnel.Establish();
++ omx_err = m_omx_tunnel_decoder.Establish();
+ if (omx_err != OMX_ErrorNone)
+ {
+- CLog::Log(LOGERROR, "%s::%s - m_omx_tunnel.Establish omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ CLog::Log(LOGERROR, "%s::%s - m_omx_tunnel_decoder.Establish omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
++ if (m_deinterlace)
++ {
++ omx_err = m_omx_tunnel_image_fx.Establish();
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_tunnel_image_fx.Establish omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++ omx_err = m_omx_image_fx.SetStateForComponent(OMX_StateExecuting);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - m_omx_image_fx.SetStateForComponent omx_err(0x%08x)",
++ CLASSNAME, __func__, omx_err);
++ return false;
++ }
++ }
++
+ // Obtain the information about the output port.
+ OMX_PARAM_PORTDEFINITIONTYPE port_format;
+ OMX_INIT_STRUCTURE(port_format);
+@@ -724,8 +799,12 @@ void COpenMaxVideo::Reset(void)
+ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__);
+ #endif
+- m_omx_egl_render.FlushAll();
+- m_omx_decoder.FlushAll();
++ if (m_omx_egl_render.IsInitialized())
++ m_omx_egl_render.FlushAll();
++ if (m_omx_image_fx.IsInitialized())
++ m_omx_image_fx.FlushAll();
++ if (m_omx_decoder.IsInitialized())
++ m_omx_decoder.FlushAll();
+ // blow all ready video frames
+ while (!m_omx_output_ready.empty())
+ {
+@@ -825,11 +904,14 @@ bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
+ }
+
+ #ifdef DTS_QUEUE
+- if (!m_dts_queue.empty())
++ if (!m_deinterlace_second_field)
+ {
++ assert(!m_dts_queue.empty());
+ pDvdVideoPicture->dts = m_dts_queue.front();
+ m_dts_queue.pop();
+ }
++ if (m_deinterlace)
++ m_deinterlace_second_field = !m_deinterlace_second_field;
+ #endif
+ // nTimeStamp is in microseconds
+ double ts = FromOMXTime(buffer->omx_buffer->nTimeStamp);
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+index 9138a20..c8ad4d8 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+@@ -31,6 +31,7 @@
+ #include "cores/dvdplayer/DVDStreamInfo.h"
+ #include "DVDVideoCodec.h"
+ #include "threads/Event.h"
++#include "xbmc/settings/VideoSettings.h"
+
+ #include <queue>
+ #include <semaphore.h>
+@@ -136,11 +137,17 @@ class COpenMaxVideo
+
+ // Components
+ COMXCoreComponent m_omx_decoder;
++ COMXCoreComponent m_omx_image_fx;
+ COMXCoreComponent m_omx_egl_render;
+
+- COMXCoreTunel m_omx_tunnel;
++ COMXCoreTunel m_omx_tunnel_decoder;
++ COMXCoreTunel m_omx_tunnel_image_fx;
+ OMX_VIDEO_CODINGTYPE m_codingType;
+
++ bool m_deinterlace;
++ EDEINTERLACEMODE m_deinterlace_request;
++ bool m_deinterlace_second_field;
++
+ bool PortSettingsChanged();
+ bool SendDecoderConfig(uint8_t *extradata, int extrasize);
+ bool NaluFormatStartCodes(enum AVCodecID codec, uint8_t *extradata, int extrasize);
+--
+1.9.3
+
+
+From 0701b41709e6a18567b9f6bfd0d491285546eedc Mon Sep 17 00:00:00 2001
+From: Ben Avison <bavison@riscosopen.org>
+Date: Wed, 12 Feb 2014 18:43:14 +0000
+Subject: [PATCH 49/94] Improved file buffering in CArchive.
+
+CArchive already did some file buffering, but only on writes. Added the
+equivalent code for reads. Also improved the write buffer case so that it
+only ever issues sector-aligned writes (the read code does this from the
+start). Shuffled various bits of code into the header file to squeeze a bit
+more performance out of it.
+
+Profiled the effect on CFileItemList::Archive(), which is one of the slow
+parts of the process of reopening a media library, on a non-overclocked
+Raspberry Pi. Times are in seconds:
+
+TV shows (253 items)
+
+Before After
+Mean StdDev Mean StdDev Confidence Change
+0.394 0.005 0.151 0.005 100.0% +159.8%
+
+Songs (4115 items)
+
+Before After
+Mean StdDev Mean StdDev Confidence Change
+2.931 0.045 0.690 0.019 100.0% +324.4%
+---
+ xbmc/utils/Archive.cpp | 158 ++++++++++++++++++-------------------------------
+ xbmc/utils/Archive.h | 130 ++++++++++++++++++++++++++++++++++------
+ 2 files changed, 172 insertions(+), 116 deletions(-)
+
+diff --git a/xbmc/utils/Archive.cpp b/xbmc/utils/Archive.cpp
+index 4519e19..b2ad273 100644
+--- a/xbmc/utils/Archive.cpp
++++ b/xbmc/utils/Archive.cpp
+@@ -30,24 +30,29 @@
+
+ using namespace XFILE;
+
+-#define BUFFER_MAX 4096
+-
+ CArchive::CArchive(CFile* pFile, int mode)
+ {
+ m_pFile = pFile;
+ m_iMode = mode;
+
+- m_pBuffer = new uint8_t[BUFFER_MAX];
+- memset(m_pBuffer, 0, BUFFER_MAX);
+-
+- m_BufferPos = 0;
++ m_pBuffer = new uint8_t[CARCHIVE_BUFFER_MAX];
++ memset(m_pBuffer, 0, CARCHIVE_BUFFER_MAX);
++ if (mode == load)
++ {
++ m_BufferPos = m_pBuffer + CARCHIVE_BUFFER_MAX;
++ m_BufferRemain = 0;
++ }
++ else
++ {
++ m_BufferPos = m_pBuffer;
++ m_BufferRemain = CARCHIVE_BUFFER_MAX;
++ }
+ }
+
+ CArchive::~CArchive()
+ {
+ FlushBuffer();
+ delete[] m_pBuffer;
+- m_BufferPos = 0;
+ }
+
+ void CArchive::Close()
+@@ -214,89 +219,6 @@ CArchive& CArchive::operator<<(const std::vector<int>& iArray)
+ return *this;
+ }
+
+-inline CArchive& CArchive::streamout(const void* dataPtr, size_t size)
+-{
+- const uint8_t* ptr = (const uint8_t*)dataPtr;
+-
+- if (size + m_BufferPos >= BUFFER_MAX)
+- {
+- FlushBuffer();
+- while (size >= BUFFER_MAX)
+- {
+- memcpy(m_pBuffer, ptr, BUFFER_MAX);
+- m_BufferPos = BUFFER_MAX;
+- ptr += BUFFER_MAX;
+- size -= BUFFER_MAX;
+- FlushBuffer();
+- }
+- }
+-
+- memcpy(m_pBuffer + m_BufferPos, ptr, size);
+- m_BufferPos += size;
+-
+- return *this;
+-}
+-
+-CArchive& CArchive::operator>>(float& f)
+-{
+- return streamin(&f, sizeof(f));
+-}
+-
+-CArchive& CArchive::operator>>(double& d)
+-{
+- return streamin(&d, sizeof(d));
+-}
+-
+-CArchive& CArchive::operator>>(short int& s)
+-{
+- return streamin(&s, sizeof(s));
+-}
+-
+-CArchive& CArchive::operator>>(unsigned short int& us)
+-{
+- return streamin(&us, sizeof(us));
+-}
+-
+-CArchive& CArchive::operator>>(int& i)
+-{
+- return streamin(&i, sizeof(i));
+-}
+-
+-CArchive& CArchive::operator>>(unsigned int& ui)
+-{
+- return streamin(&ui, sizeof(ui));
+-}
+-
+-CArchive& CArchive::operator>>(long int& l)
+-{
+- return streamin(&l, sizeof(l));
+-}
+-
+-CArchive& CArchive::operator>>(unsigned long int& ul)
+-{
+- return streamin(&ul, sizeof(ul));
+-}
+-
+-CArchive& CArchive::operator>>(long long int& ll)
+-{
+- return streamin(&ll, sizeof(ll));
+-}
+-
+-CArchive& CArchive::operator>>(unsigned long long int& ull)
+-{
+- return streamin(&ull, sizeof(ull));
+-}
+-
+-CArchive& CArchive::operator>>(bool& b)
+-{
+- return streamin(&b, sizeof(b));
+-}
+-
+-CArchive& CArchive::operator>>(char& c)
+-{
+- return streamin(&c, sizeof(c));
+-}
+-
+ CArchive& CArchive::operator>>(std::string& str)
+ {
+ size_t iLength = 0;
+@@ -450,23 +372,61 @@ CArchive& CArchive::operator>>(std::vector<int>& iArray)
+ return *this;
+ }
+
+-inline CArchive& CArchive::streamin(void* dataPtr, const size_t size)
++void CArchive::FlushBuffer()
+ {
+- size_t read = m_pFile->Read(dataPtr, size);
+- if (read < size)
++ if (m_iMode == store && m_BufferPos != m_pBuffer)
+ {
+- CLog::Log(LOGERROR, "%s: can't stream out: requested %lu bytes, was read %lu bytes", __FUNCTION__, (unsigned long)size, (unsigned long)read);
+- memset(dataPtr, 0, size);
++ m_pFile->Write(m_pBuffer, m_BufferPos - m_pBuffer);
++ m_BufferPos = m_pBuffer;
++ m_BufferRemain = CARCHIVE_BUFFER_MAX;
+ }
++}
+
++CArchive &CArchive::streamout_bufferwrap(const uint8_t *ptr, size_t size)
++{
++ do
++ {
++ size_t chunkSize = std::min(size, m_BufferRemain);
++ m_BufferPos = std::copy(ptr, ptr + chunkSize, m_BufferPos);
++ ptr += chunkSize;
++ size -= chunkSize;
++ m_BufferRemain -= chunkSize;
++ if (m_BufferRemain == 0)
++ FlushBuffer();
++ } while (size > 0);
+ return *this;
+ }
+
+-void CArchive::FlushBuffer()
++void CArchive::FillBuffer()
+ {
+- if (m_BufferPos > 0)
++ if (m_iMode == load && m_BufferRemain == 0)
+ {
+- m_pFile->Write(m_pBuffer, m_BufferPos);
+- m_BufferPos = 0;
++ m_BufferRemain = m_pFile->Read(m_pBuffer, CARCHIVE_BUFFER_MAX);
++ m_BufferPos = m_pBuffer;
+ }
+ }
++
++CArchive &CArchive::streamin_bufferwrap(uint8_t *ptr, size_t size)
++{
++ uint8_t *orig_ptr = ptr;
++ size_t orig_size = size;
++ do
++ {
++ if (m_BufferRemain == 0)
++ {
++ FillBuffer();
++ if (m_BufferRemain < CARCHIVE_BUFFER_MAX && m_BufferRemain < size)
++ {
++ CLog::Log(LOGERROR, "%s: can't stream in: requested %lu bytes, was read %lu bytes", __FUNCTION__, (unsigned long) orig_size, (unsigned long) (ptr - orig_ptr + m_BufferRemain));
++ memset(orig_ptr, 0, orig_size);
++ return *this;
++ }
++ }
++ size_t chunkSize = std::min(size, m_BufferRemain);
++ ptr = std::copy(m_BufferPos, m_BufferPos + chunkSize, ptr);
++ m_BufferPos += chunkSize;
++ m_BufferRemain -= chunkSize;
++ size -= chunkSize;
++ } while (size > 0);
++ return *this;
++}
+diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h
+index 0148fcb..5b25be5 100644
+--- a/xbmc/utils/Archive.h
++++ b/xbmc/utils/Archive.h
+@@ -24,6 +24,8 @@
+ #include <vector>
+ #include "PlatformDefs.h" // for SYSTEMTIME
+
++#define CARCHIVE_BUFFER_MAX 4096
++
+ namespace XFILE
+ {
+ class CFile;
+@@ -77,18 +79,66 @@ class CArchive
+ CArchive& operator<<(const std::vector<int>& iArray);
+
+ // loading
+- CArchive& operator>>(float& f);
+- CArchive& operator>>(double& d);
+- CArchive& operator>>(short int& s);
+- CArchive& operator>>(unsigned short int& us);
+- CArchive& operator>>(int& i);
+- CArchive& operator>>(unsigned int& ui);
+- CArchive& operator>>(long int& l);
+- CArchive& operator>>(unsigned long int& ul);
+- CArchive& operator>>(long long int& ll);
+- CArchive& operator>>(unsigned long long int& ull);
+- CArchive& operator>>(bool& b);
+- CArchive& operator>>(char& c);
++ inline CArchive& operator>>(float& f)
++ {
++ return streamin(&f, sizeof(f));
++ }
++
++ inline CArchive& operator>>(double& d)
++ {
++ return streamin(&d, sizeof(d));
++ }
++
++ inline CArchive& operator>>(short int& s)
++ {
++ return streamin(&s, sizeof(s));
++ }
++
++ inline CArchive& operator>>(unsigned short int& us)
++ {
++ return streamin(&us, sizeof(us));
++ }
++
++ inline CArchive& operator>>(int& i)
++ {
++ return streamin(&i, sizeof(i));
++ }
++
++ inline CArchive& operator>>(unsigned int& ui)
++ {
++ return streamin(&ui, sizeof(ui));
++ }
++
++ inline CArchive& operator>>(long int& l)
++ {
++ return streamin(&l, sizeof(l));
++ }
++
++ inline CArchive& operator>>(unsigned long int& ul)
++ {
++ return streamin(&ul, sizeof(ul));
++ }
++
++ inline CArchive& operator>>(long long int& ll)
++ {
++ return streamin(&ll, sizeof(ll));
++ }
++
++ inline CArchive& operator>>(unsigned long long int& ull)
++ {
++ return streamin(&ull, sizeof(ull));
++ }
++
++ inline CArchive& operator>>(bool& b)
++ {
++ return streamin(&b, sizeof(b));
++ }
++
++ inline CArchive& operator>>(char& c)
++ {
++ return streamin(&c, sizeof(c));
++ }
++
+ CArchive& operator>>(std::string &str);
+ CArchive& operator>>(std::wstring& wstr);
+ CArchive& operator>>(SYSTEMTIME& time);
+@@ -105,12 +155,58 @@ class CArchive
+ enum Mode {load = 0, store};
+
+ protected:
+- CArchive& streamout(const void* dataPtr, size_t size);
+- CArchive& streamin(void* dataPtr, const size_t size);
+- void FlushBuffer();
++ inline CArchive &streamout(const void *dataPtr, size_t size)
++ {
++ const uint8_t *ptr = (const uint8_t *) dataPtr;
++ /* Note, the buffer is flushed as soon as it is full (m_BufferRemain == size) rather
++ * than waiting until we attempt to put more data into an already full buffer */
++ if (m_BufferRemain > size)
++ {
++ switch (size)
++ {
++ case 1: *m_BufferPos++ = *ptr; m_BufferRemain--; break;
++ case 2: *(uint16_t *) m_BufferPos = *(const uint16_t *) ptr; m_BufferPos += 2; m_BufferRemain -= 2; break;
++ case 4: *(uint32_t *) m_BufferPos = *(const uint32_t *) ptr; m_BufferPos += 4; m_BufferRemain -= 4; break;
++ default: m_BufferPos = std::copy(ptr, ptr + size, m_BufferPos); m_BufferRemain -= size; break;
++ }
++ return *this;
++ }
++ else
++ {
++ return streamout_bufferwrap(ptr, size);
++ }
++ }
++
++ inline CArchive &streamin(void *dataPtr, size_t size)
++ {
++ uint8_t *ptr = (uint8_t *) dataPtr;
++ /* Note, refilling the buffer is deferred until we know we need to read more from it */
++ if (m_BufferRemain >= size)
++ {
++ switch (size)
++ {
++ case 1: *ptr = *m_BufferPos++; m_BufferRemain--; break;
++ case 2: *(uint16_t *) ptr = *(const uint16_t *) m_BufferPos; m_BufferPos += 2; m_BufferRemain -= 2; break;
++ case 4: *(uint32_t *) ptr = *(const uint32_t *) m_BufferPos; m_BufferPos += 4; m_BufferRemain -= 4; break;
++ default: std::copy(m_BufferPos, m_BufferPos + size, ptr); m_BufferPos += size; m_BufferRemain -= size; break;
++ }
++ return *this;
++ }
++ else
++ {
++ return streamin_bufferwrap(ptr, size);
++ }
++ }
++
+ XFILE::CFile* m_pFile;
+ int m_iMode;
+ uint8_t *m_pBuffer;
+- int m_BufferPos;
+-};
++ uint8_t *m_BufferPos;
++ size_t m_BufferRemain;
+
++private:
++ void FlushBuffer();
++ CArchive &streamout_bufferwrap(const uint8_t *ptr, size_t size);
++ void FillBuffer();
++ CArchive &streamin_bufferwrap(uint8_t *ptr, size_t size);
++};
+--
+1.9.3
+
+
+From 2ae9667952e98ab91b0056094231e923570fa64b Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Wed, 5 Feb 2014 11:46:33 +0000
+Subject: [PATCH 50/94] [rbp/settings] Allow av sync type to be enabled
+
+It works for dvdplayer
+---
+ system/settings/rbp.xml | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
+index 2b7d0a6..1429256 100644
+--- a/system/settings/rbp.xml
++++ b/system/settings/rbp.xml
+@@ -1,13 +1,6 @@
+ <?xml version="1.0" encoding="utf-8" ?>
+ <settings>
+ <section id="videos">
+- <category id="videoplayer">
+- <group id="2">
+- <setting id="videoplayer.synctype">
+- <visible>false</visible>
+- </setting>
+- </group>
+- </category>
+ <category id="videoacceleration">
+ <group id="1">
+ <visible>false</visible>
+--
+1.9.3
+
+
+From 4fb1419b986a36f2e53a5bca71caf90bd13443ba Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Sun, 16 Feb 2014 17:38:05 +0000
+Subject: [PATCH 51/94] [omxcodec] Only do essential calls in texture thread
+ [omxcodec] Fix for files with no valid pts values. [omxcodec] Fix stall on
+ seek/trickplay - need to reset start flag [omxcodec] Make sure we have a
+ valid context when video decode starts before first fanart is decoded
+ [omxcodec] More care with dropping frames quickly
+
+---
+ .../dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 127 ++++++++++++++-------
+ .../cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 14 +--
+ 2 files changed, 89 insertions(+), 52 deletions(-)
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+index fbf1458..71d19af 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+@@ -55,6 +55,9 @@
+
+ #define CLASSNAME "COpenMaxVideo"
+
++#define OMX_BUFFERFLAG_PTS_INVALID (1<<28)
++#define OMX_BUFFERFLAG_DROPPED (1<<29)
++
+ COpenMaxVideoBuffer::COpenMaxVideoBuffer(COpenMaxVideo *omv)
+ : m_omv(omv), m_refs(0)
+ {
+@@ -120,7 +123,9 @@ void COpenMaxVideoBuffer::Sync()
+
+ COpenMaxVideo::COpenMaxVideo()
+ {
++ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this);
++ #endif
+ pthread_mutex_init(&m_omx_output_mutex, NULL);
+
+ m_drop_state = false;
+@@ -135,6 +140,7 @@ COpenMaxVideo::COpenMaxVideo()
+ m_deinterlace = false;
+ m_deinterlace_request = VS_DEINTERLACEMODE_OFF;
+ m_deinterlace_second_field = false;
++ m_startframe = false;
+ }
+
+ COpenMaxVideo::~COpenMaxVideo()
+@@ -182,8 +188,6 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenM
+ m_forced_aspect_ratio = hints.forced_aspect;
+ m_aspect_ratio = hints.aspect;
+
+- m_egl_display = g_Windowing.GetEGLDisplay();
+- m_egl_context = g_Windowing.GetEGLContext();
+ m_egl_buffer_count = 4;
+
+ m_codingType = OMX_VIDEO_CodingUnused;
+@@ -347,6 +351,7 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenM
+ return false;
+
+ m_drop_state = false;
++ m_startframe = false;
+
+ return true;
+ }
+@@ -375,6 +380,25 @@ void COpenMaxVideo::SetDropState(bool bDrop)
+ CLASSNAME, __func__, bDrop);
+ #endif
+ m_drop_state = bDrop;
++ if (m_drop_state)
++ {
++ while (1)
++ {
++ COpenMaxVideoBuffer *buffer = NULL;
++ pthread_mutex_lock(&m_omx_output_mutex);
++ // fetch a output buffer and pop it off the ready list
++ if (!m_omx_output_ready.empty())
++ {
++ buffer = m_omx_output_ready.front();
++ m_omx_output_ready.pop();
++ }
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ if (buffer)
++ ReturnOpenMaxBuffer(buffer);
++ else
++ break;
++ }
++ }
+ }
+
+ bool COpenMaxVideo::SendDecoderConfig(uint8_t *extradata, int extrasize)
+@@ -713,10 +737,13 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
+
+ if (demuxer_bytes == 0)
+ omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
+- if (pts == DVD_NOPTS_VALUE)
++ // openmax doesn't like an unknown timestamp as first frame
++ if (pts == DVD_NOPTS_VALUE && m_startframe)
+ omx_buffer->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN;
++ if (pts == DVD_NOPTS_VALUE) // hijack an omx flag to indicate there wasn't a real timestamp - it will be returned with the picture (but otherwise ignored)
++ omx_buffer->nFlags |= OMX_BUFFERFLAG_PTS_INVALID;
+ if (m_drop_state) // hijack an omx flag to signal this frame to be dropped - it will be returned with the picture (but otherwise ignored)
+- omx_buffer->nFlags |= OMX_BUFFERFLAG_DATACORRUPT;
++ omx_buffer->nFlags |= OMX_BUFFERFLAG_DECODEONLY | OMX_BUFFERFLAG_DROPPED;
+
+ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGDEBUG, "%s::%s - %-6d dts:%.3f pts:%.3f flags:%x",
+@@ -731,10 +758,14 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
+ }
+ if (demuxer_bytes == 0)
+ {
++ m_startframe = true;
+ #ifdef DTS_QUEUE
+- // only push if we are successful with feeding OMX_EmptyThisBuffer
+- m_dts_queue.push(dts);
+- assert(m_dts_queue.size() < 32);
++ if (!m_drop_state)
++ {
++ // only push if we are successful with feeding OMX_EmptyThisBuffer
++ m_dts_queue.push(dts);
++ assert(m_dts_queue.size() < 32);
++ }
+ #endif
+ if (buffer_to_free)
+ {
+@@ -806,15 +837,8 @@ void COpenMaxVideo::Reset(void)
+ if (m_omx_decoder.IsInitialized())
+ m_omx_decoder.FlushAll();
+ // blow all ready video frames
+- while (!m_omx_output_ready.empty())
+- {
+- pthread_mutex_lock(&m_omx_output_mutex);
+- COpenMaxVideoBuffer *pic = m_omx_output_ready.front();
+- m_omx_output_ready.pop();
+- pthread_mutex_unlock(&m_omx_output_mutex);
+- // return the omx buffer back to OpenMax to fill.
+- ReturnOpenMaxBuffer(pic);
+- }
++ SetDropState(true);
++ SetDropState(false);
+ #ifdef DTS_QUEUE
+ while (!m_dts_queue.empty())
+ m_dts_queue.pop();
+@@ -822,6 +846,7 @@ void COpenMaxVideo::Reset(void)
+
+ while (!m_demux_queue.empty())
+ m_demux_queue.pop();
++ m_startframe = false;
+ }
+
+
+@@ -914,17 +939,17 @@ bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
+ m_deinterlace_second_field = !m_deinterlace_second_field;
+ #endif
+ // nTimeStamp is in microseconds
+- double ts = FromOMXTime(buffer->omx_buffer->nTimeStamp);
+- pDvdVideoPicture->pts = (ts == 0) ? DVD_NOPTS_VALUE : ts;
++ pDvdVideoPicture->pts = FromOMXTime(buffer->omx_buffer->nTimeStamp);
+ pDvdVideoPicture->openMaxBuffer->Acquire();
+ pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED;
+- if (buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_DATACORRUPT)
+- pDvdVideoPicture->iFlags |= DVP_FLAG_DROPPED;
++ if (buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_PTS_INVALID)
++ pDvdVideoPicture->pts = DVD_NOPTS_VALUE;
+ #if defined(OMX_DEBUG_VERBOSE)
+ CLog::Log(LOGINFO, "%s::%s dts:%.3f pts:%.3f flags:%x:%x openMaxBuffer:%p omx_buffer:%p egl_image:%p texture_id:%x", CLASSNAME, __func__,
+ pDvdVideoPicture->dts == DVD_NOPTS_VALUE ? 0.0 : pDvdVideoPicture->dts*1e-6, pDvdVideoPicture->pts == DVD_NOPTS_VALUE ? 0.0 : pDvdVideoPicture->pts*1e-6,
+ pDvdVideoPicture->iFlags, buffer->omx_buffer->nFlags, pDvdVideoPicture->openMaxBuffer, pDvdVideoPicture->openMaxBuffer->omx_buffer, pDvdVideoPicture->openMaxBuffer->egl_image, pDvdVideoPicture->openMaxBuffer->texture_id);
+ #endif
++ assert(!(buffer->omx_buffer->nFlags & (OMX_BUFFERFLAG_DECODEONLY | OMX_BUFFERFLAG_DROPPED)));
+ }
+ else
+ {
+@@ -953,10 +978,11 @@ OMX_ERRORTYPE COpenMaxVideo::DecoderFillBufferDone(
+ COpenMaxVideoBuffer *buffer = (COpenMaxVideoBuffer*)pBuffer->pAppPrivate;
+
+ #if defined(OMX_DEBUG_VERBOSE)
+- CLog::Log(LOGDEBUG, "%s::%s - %p (%p,%p) buffer_size(%u), pts:%.3f",
+- CLASSNAME, __func__, buffer, pBuffer, buffer->omx_buffer, pBuffer->nFilledLen, (double)FromOMXTime(buffer->omx_buffer->nTimeStamp)*1e-6);
++ CLog::Log(LOGDEBUG, "%s::%s - %p (%p,%p) buffer_size(%u), pts:%.3f flags:%x",
++ CLASSNAME, __func__, buffer, pBuffer, buffer->omx_buffer, pBuffer->nFilledLen, (double)FromOMXTime(buffer->omx_buffer->nTimeStamp)*1e-6, buffer->omx_buffer->nFlags);
+ #endif
+
++ assert(!(buffer->omx_buffer->nFlags & (OMX_BUFFERFLAG_DECODEONLY | OMX_BUFFERFLAG_DROPPED)));
+ // queue output omx buffer to ready list.
+ pthread_mutex_lock(&m_omx_output_mutex);
+ buffer->m_aspect_ratio = m_aspect_ratio;
+@@ -1000,41 +1026,60 @@ OMX_ERRORTYPE COpenMaxVideo::FreeOMXInputBuffers(void)
+ return(omx_err);
+ }
+
+-bool COpenMaxVideo::CallbackAllocOMXEGLTextures(void *userdata)
++bool COpenMaxVideo::CallbackAllocOMXEGLTextures(EGLDisplay egl_display, EGLContext egl_context, void *userdata)
+ {
+ COpenMaxVideo *omx = static_cast<COpenMaxVideo*>(userdata);
+- return omx->AllocOMXOutputEGLTextures() == OMX_ErrorNone;
++ return omx->AllocOMXOutputEGLTextures(egl_display, egl_context) == OMX_ErrorNone;
+ }
+
+-bool COpenMaxVideo::CallbackFreeOMXEGLTextures(void *userdata)
++bool COpenMaxVideo::CallbackFreeOMXEGLTextures(EGLDisplay egl_display, EGLContext egl_context, void *userdata)
+ {
+ COpenMaxVideo *omx = static_cast<COpenMaxVideo*>(userdata);
+- return omx->FreeOMXOutputEGLTextures() == OMX_ErrorNone;
++ return omx->FreeOMXOutputEGLTextures(egl_display, egl_context) == OMX_ErrorNone;
+ }
+
+ bool COpenMaxVideo::AllocOMXOutputBuffers(void)
+ {
+- return g_OMXImage.SendMessage(CallbackAllocOMXEGLTextures, (void *)this);
++ pthread_mutex_lock(&m_omx_output_mutex);
++ for (size_t i = 0; i < m_egl_buffer_count; i++)
++ {
++ COpenMaxVideoBuffer *egl_buffer = new COpenMaxVideoBuffer(this);
++ egl_buffer->width = m_decoded_width;
++ egl_buffer->height = m_decoded_height;
++ egl_buffer->index = i;
++ m_omx_output_buffers.push_back(egl_buffer);
++ }
++ bool ret = g_OMXImage.SendMessage(CallbackAllocOMXEGLTextures, (void *)this);
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ return ret;
+ }
+
+ bool COpenMaxVideo::FreeOMXOutputBuffers(void)
+ {
+- return g_OMXImage.SendMessage(CallbackFreeOMXEGLTextures, (void *)this);
++ pthread_mutex_lock(&m_omx_output_mutex);
++ bool ret = g_OMXImage.SendMessage(CallbackFreeOMXEGLTextures, (void *)this);
++
++ for (size_t i = 0; i < m_omx_output_buffers.size(); i++)
++ {
++ COpenMaxVideoBuffer *egl_buffer = m_omx_output_buffers[i];
++ delete egl_buffer;
++ }
++
++ m_omx_output_buffers.clear();
++ pthread_mutex_unlock(&m_omx_output_mutex);
++ return ret;
+ }
+
+-OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
++OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(EGLDisplay egl_display, EGLContext egl_context)
+ {
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ EGLint attrib = EGL_NONE;
+- COpenMaxVideoBuffer *egl_buffer;
+
+ glActiveTexture(GL_TEXTURE0);
+
+ for (size_t i = 0; i < m_egl_buffer_count; i++)
+ {
+- egl_buffer = new COpenMaxVideoBuffer(this);
+- egl_buffer->width = m_decoded_width;
+- egl_buffer->height = m_decoded_height;
++ COpenMaxVideoBuffer *egl_buffer = m_omx_output_buffers[i];
+
+ glGenTextures(1, &egl_buffer->texture_id);
+ glBindTexture(GL_TEXTURE_2D, egl_buffer->texture_id);
+@@ -1057,8 +1102,8 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+
+ // create EGLImage from texture
+ egl_buffer->egl_image = eglCreateImageKHR(
+- m_egl_display,
+- m_egl_context,
++ egl_display,
++ egl_context,
+ EGL_GL_TEXTURE_2D_KHR,
+ (EGLClientBuffer)(egl_buffer->texture_id),
+ &attrib);
+@@ -1067,7 +1112,6 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+ CLog::Log(LOGERROR, "%s::%s - ERROR creating EglImage", CLASSNAME, __func__);
+ return(OMX_ErrorUndefined);
+ }
+- egl_buffer->index = i;
+
+ // tell decoder output port that it will be using EGLImage
+ omx_err = m_omx_egl_render.UseEGLImage(
+@@ -1078,7 +1122,6 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+ CLASSNAME, __func__, omx_err);
+ return(omx_err);
+ }
+- m_omx_output_buffers.push_back(egl_buffer);
+
+ CLog::Log(LOGDEBUG, "%s::%s - Texture %p Width %d Height %d",
+ CLASSNAME, __func__, egl_buffer->egl_image, egl_buffer->width, egl_buffer->height);
+@@ -1086,26 +1129,22 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void)
+ return omx_err;
+ }
+
+-OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputEGLTextures(void)
++OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputEGLTextures(EGLDisplay egl_display, EGLContext egl_context)
+ {
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+- COpenMaxVideoBuffer *egl_buffer;
+
+ for (size_t i = 0; i < m_omx_output_buffers.size(); i++)
+ {
+- egl_buffer = m_omx_output_buffers[i];
++ COpenMaxVideoBuffer *egl_buffer = m_omx_output_buffers[i];
+ // tell decoder output port to stop using the EGLImage
+ omx_err = m_omx_egl_render.FreeOutputBuffer(egl_buffer->omx_buffer);
+ if (omx_err != OMX_ErrorNone)
+ CLog::Log(LOGERROR, "%s::%s m_omx_egl_render.FreeOutputBuffer(%p) omx_err(0x%08x)", CLASSNAME, __func__, egl_buffer->omx_buffer, omx_err);
+ // destroy egl_image
+- eglDestroyImageKHR(m_egl_display, egl_buffer->egl_image);
++ eglDestroyImageKHR(egl_display, egl_buffer->egl_image);
+ // free texture
+ glDeleteTextures(1, &egl_buffer->texture_id);
+- delete egl_buffer;
+ }
+- m_omx_output_buffers.clear();
+-
+ return omx_err;
+ }
+
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+index c8ad4d8..f234f6d 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+@@ -99,17 +99,13 @@ class COpenMaxVideo
+ OMX_ERRORTYPE FreeOMXInputBuffers(void);
+ bool AllocOMXOutputBuffers(void);
+ bool FreeOMXOutputBuffers(void);
+- static bool CallbackAllocOMXEGLTextures(void*);
+- OMX_ERRORTYPE AllocOMXOutputEGLTextures(void);
+- static bool CallbackFreeOMXEGLTextures(void*);
+- OMX_ERRORTYPE FreeOMXOutputEGLTextures(void);
++ static bool CallbackAllocOMXEGLTextures(EGLDisplay egl_display, EGLContext egl_context, void*);
++ OMX_ERRORTYPE AllocOMXOutputEGLTextures(EGLDisplay egl_display, EGLContext egl_context);
++ static bool CallbackFreeOMXEGLTextures(EGLDisplay egl_display, EGLContext egl_context, void*);
++ OMX_ERRORTYPE FreeOMXOutputEGLTextures(EGLDisplay egl_display, EGLContext egl_context);
+ OMX_ERRORTYPE StopDecoder(void);
+ OMX_ERRORTYPE ReturnOpenMaxBuffer(COpenMaxVideoBuffer *buffer);
+
+- // EGL Resources
+- EGLDisplay m_egl_display;
+- EGLContext m_egl_context;
+-
+ // Video format
+ bool m_drop_state;
+ int m_decoded_width;
+@@ -148,6 +144,8 @@ class COpenMaxVideo
+ EDEINTERLACEMODE m_deinterlace_request;
+ bool m_deinterlace_second_field;
+
++ bool m_startframe;
++
+ bool PortSettingsChanged();
+ bool SendDecoderConfig(uint8_t *extradata, int extrasize);
+ bool NaluFormatStartCodes(enum AVCodecID codec, uint8_t *extradata, int extrasize);
+--
+1.9.3
+
+
+From 182b137323347482bfca46dbb857813e4f984298 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Sat, 1 Mar 2014 14:24:08 +0000
+Subject: [PATCH 52/94] [omxplayer] Allow small audio packets to be
+ concatenated to make better use of audio fifo
+
+Some audio codecs produce small packets which causes a high overhead when submitting to GPU, and doesn't make full use of GPU side buffering.
+TrueHD in particular can produce packets with 40 samples (so 1200 packets per second) which causes very high overhead.
+
+What this aims to do is to concatenate audio packets until they approach the ideal audio packet size,
+and then deal with the awkardness of concatenated planar formats.
+---
+ xbmc/cores/omxplayer/OMXAudio.cpp | 67 +++++++++++++++++++---------
+ xbmc/cores/omxplayer/OMXAudio.h | 3 +-
+ xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp | 72 +++++++++++++++++++++----------
+ xbmc/cores/omxplayer/OMXAudioCodecOMX.h | 9 +++-
+ xbmc/cores/omxplayer/OMXPlayerAudio.cpp | 9 ++--
+ 5 files changed, 110 insertions(+), 50 deletions(-)
+
+diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
+index dd80412..e67dc94 100644
+--- a/xbmc/cores/omxplayer/OMXAudio.cpp
++++ b/xbmc/cores/omxplayer/OMXAudio.cpp
+@@ -43,6 +43,10 @@
+
+ using namespace std;
+
++// the size of the audio_render output port buffers
++#define AUDIO_DECODE_OUTPUT_BUFFER (32*1024)
++static const char rounded_up_channels_shift[] = {0,0,1,2,2,3,3,3,3};
++
+ 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};
+
+@@ -61,6 +65,7 @@ COMXAudio::COMXAudio() :
+ m_Passthrough (false ),
+ m_HWDecode (false ),
+ m_BytesPerSec (0 ),
++ m_InputBytesPerSec(0 ),
+ m_BufferLen (0 ),
+ m_ChunkLen (0 ),
+ m_InputChannels (0 ),
+@@ -490,11 +495,15 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
+
+ m_SampleRate = m_format.m_sampleRate;
+ m_BitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+- m_BufferLen = m_BytesPerSec = m_format.m_sampleRate * (16 >> 3) * m_InputChannels;
+- m_BufferLen *= AUDIO_BUFFER_SECONDS;
++ m_BytesPerSec = m_SampleRate * 2 << rounded_up_channels_shift[m_InputChannels];
++ m_BufferLen = m_BytesPerSec * AUDIO_BUFFER_SECONDS;
++ m_InputBytesPerSec = m_SampleRate * m_BitsPerSample * m_InputChannels >> 3;
++
++ // should be big enough that common formats (e.g. 6 channel DTS) fit in a single packet.
++ // we don't mind less common formats being split (e.g. ape/wma output large frames)
+ // the audio_decode output buffer size is 32K, and typically we convert from
+- // 6 channel 32bpp float to 8 channel 16bpp in, so a full 48K input buffer will fit the outbut buffer
+- m_ChunkLen = 48*1024;
++ // 6 channel 32bpp float to 8 channel 16bpp in, so a full 48K input buffer will fit the output buffer
++ m_ChunkLen = AUDIO_DECODE_OUTPUT_BUFFER * (m_InputChannels * m_BitsPerSample) >> (rounded_up_channels_shift[m_InputChannels] + 4);
+
+ m_wave_header.Samples.wSamplesPerBlock = 0;
+ m_wave_header.Format.nChannels = m_InputChannels;
+@@ -682,7 +691,7 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
+ m_maxLevel = 0.0f;
+
+ 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);
++ (int)m_pcm_input.nBitPerSample, (int)m_pcm_input.nSamplingRate, (int)m_pcm_input.nChannels, m_BufferLen, m_InputBytesPerSec);
+ PrintPCM(&m_pcm_input, std::string("input"));
+ CLog::Log(LOGDEBUG, "COMXAudio::Initialize device passthrough %d hwdecode %d",
+ m_Passthrough, m_HWDecode);
+@@ -865,11 +874,11 @@ bool COMXAudio::ApplyVolume(void)
+ //***********************************************************************************************
+ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len)
+ {
+- return AddPackets(data, len, 0, 0);
++ return AddPackets(data, len, 0, 0, 0);
+ }
+
+ //***********************************************************************************************
+-unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dts, double pts)
++unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dts, double pts, unsigned int frame_size)
+ {
+ CSingleLock lock (m_critSection);
+
+@@ -916,24 +925,40 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
+ omx_buffer->nOffset = 0;
+ omx_buffer->nFlags = 0;
+
++ // we want audio_decode output buffer size to be no more than AUDIO_DECODE_OUTPUT_BUFFER.
++ // it will be 16-bit and rounded up to next power of 2 in channels
++ unsigned int max_buffer = AUDIO_DECODE_OUTPUT_BUFFER * (m_InputChannels * m_BitsPerSample) >> (rounded_up_channels_shift[m_InputChannels] + 4);
++
+ unsigned int remaining = demuxer_samples-demuxer_samples_sent;
+- unsigned int samples_space = omx_buffer->nAllocLen/pitch;
++ unsigned int samples_space = std::min(max_buffer, omx_buffer->nAllocLen)/pitch;
+ unsigned int samples = std::min(remaining, samples_space);
+
+ omx_buffer->nFilledLen = samples * pitch;
+
+- if (samples < demuxer_samples && m_BitsPerSample==32 && !(m_Passthrough || m_HWDecode))
++ unsigned int frames = frame_size ? len/frame_size:0;
++ if ((samples < demuxer_samples || frames > 1) && m_BitsPerSample==32 && !(m_Passthrough || m_HWDecode))
+ {
+- uint8_t *dst = omx_buffer->pBuffer;
+- uint8_t *src = demuxer_content + demuxer_samples_sent * (m_BitsPerSample >> 3);
+- // we need to extract samples from planar audio, so the copying needs to be done per plane
+- for (int i=0; i<(int)m_InputChannels; i++)
+- {
+- memcpy(dst, src, omx_buffer->nFilledLen / m_InputChannels);
+- dst += omx_buffer->nFilledLen / m_InputChannels;
+- src += demuxer_samples * (m_BitsPerSample >> 3);
+- }
+- assert(dst <= omx_buffer->pBuffer + m_ChunkLen);
++ const unsigned int sample_pitch = m_BitsPerSample >> 3;
++ const unsigned int frame_samples = frame_size / pitch;
++ const unsigned int plane_size = frame_samples * sample_pitch;
++ const unsigned int out_plane_size = samples * sample_pitch;
++ //CLog::Log(LOGDEBUG, "%s::%s samples:%d/%d ps:%d ops:%d fs:%d pitch:%d filled:%d frames=%d", CLASSNAME, __func__, samples, demuxer_samples, plane_size, out_plane_size, frame_size, pitch, omx_buffer->nFilledLen, frames);
++ for (unsigned int sample = 0; sample < samples; )
++ {
++ unsigned int frame = (demuxer_samples_sent + sample) / frame_samples;
++ unsigned int sample_in_frame = (demuxer_samples_sent + sample) - frame * frame_samples;
++ int out_remaining = std::min(std::min(frame_samples - sample_in_frame, samples), samples-sample);
++ uint8_t *src = demuxer_content + frame*frame_size + sample_in_frame * sample_pitch;
++ uint8_t *dst = (uint8_t *)omx_buffer->pBuffer + sample * sample_pitch;
++ for (unsigned int channel = 0; channel < m_InputChannels; channel++)
++ {
++ //CLog::Log(LOGDEBUG, "%s::%s copy(%d,%d,%d) (s:%d f:%d sin:%d c:%d)", CLASSNAME, __func__, dst-(uint8_t *)omx_buffer->pBuffer, src-demuxer_content, out_remaining, sample, frame, sample_in_frame, channel);
++ memcpy(dst, src, out_remaining * sample_pitch);
++ src += plane_size;
++ dst += out_plane_size;
++ }
++ sample += out_remaining;
++ }
+ }
+ else
+ {
+@@ -1114,7 +1139,9 @@ float COMXAudio::GetCacheTime()
+
+ float COMXAudio::GetCacheTotal()
+ {
+- return m_BytesPerSec ? (float)m_BufferLen / (float)m_BytesPerSec : 0.0f;
++ float audioplus_buffer = m_SampleRate ? 0.0f : 32.0f * 512.0f / m_SampleRate;
++ float input_buffer = (float)m_omx_decoder.GetInputBufferSize() / (float)m_InputBytesPerSec;
++ return AUDIO_BUFFER_SECONDS + input_buffer + audioplus_buffer;
+ }
+
+ //***********************************************************************************************
+diff --git a/xbmc/cores/omxplayer/OMXAudio.h b/xbmc/cores/omxplayer/OMXAudio.h
+index 804bd2a..b0264d8 100644
+--- a/xbmc/cores/omxplayer/OMXAudio.h
++++ b/xbmc/cores/omxplayer/OMXAudio.h
+@@ -66,7 +66,7 @@ class COMXAudio
+ ~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 AddPackets(const void* data, unsigned int len, double dts, double pts, unsigned int frame_size);
+ unsigned int GetSpace();
+ bool Deinitialize();
+
+@@ -114,6 +114,7 @@ class COMXAudio
+ bool m_Passthrough;
+ bool m_HWDecode;
+ unsigned int m_BytesPerSec;
++ unsigned int m_InputBytesPerSec;
+ unsigned int m_BufferLen;
+ unsigned int m_ChunkLen;
+ unsigned int m_InputChannels;
+diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
+index 5503a0e..557e847 100644
+--- a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
++++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
+@@ -26,10 +26,15 @@
+
+ #include "cores/AudioEngine/Utils/AEUtil.h"
+
++// the size of the audio_render output port buffers
++#define AUDIO_DECODE_OUTPUT_BUFFER (32*1024)
++static const char rounded_up_channels_shift[] = {0,0,1,2,2,3,3,3,3};
++
+ COMXAudioCodecOMX::COMXAudioCodecOMX()
+ {
+ m_pBufferOutput = NULL;
+ m_iBufferOutputAlloced = 0;
++ m_iBufferOutputUsed = 0;
+
+ m_pCodecContext = NULL;
+ m_pConvert = NULL;
+@@ -37,7 +42,10 @@ COMXAudioCodecOMX::COMXAudioCodecOMX()
+
+ m_channels = 0;
+ m_pFrame1 = NULL;
++ m_frameSize = 0;
+ m_bGotFrame = false;
++ m_bNoConcatenate = false;
++
+ m_iSampleFormat = AV_SAMPLE_FMT_NONE;
+ m_desiredSampleFormat = AV_SAMPLE_FMT_NONE;
+ }
+@@ -47,6 +55,7 @@ COMXAudioCodecOMX::~COMXAudioCodecOMX()
+ m_dllAvUtil.av_free(m_pBufferOutput);
+ m_pBufferOutput = NULL;
+ m_iBufferOutputAlloced = 0;
++ m_iBufferOutputUsed = 0;
+ Dispose();
+ }
+
+@@ -83,6 +92,10 @@ bool COMXAudioCodecOMX::Open(CDVDStreamInfo &hints)
+ m_pCodecContext->bit_rate = hints.bitrate;
+ m_pCodecContext->bits_per_coded_sample = hints.bitspersample;
+
++ // vorbis has variable sized planar output, so skip concatenation
++ if (hints.codec == AV_CODEC_ID_VORBIS)
++ m_bNoConcatenate = true;
++
+ if(m_pCodecContext->bits_per_coded_sample == 0)
+ m_pCodecContext->bits_per_coded_sample = 16;
+
+@@ -132,7 +145,7 @@ void COMXAudioCodecOMX::Dispose()
+ m_bGotFrame = false;
+ }
+
+-int COMXAudioCodecOMX::Decode(BYTE* pData, int iSize)
++int COMXAudioCodecOMX::Decode(BYTE* pData, int iSize, double dts, double pts)
+ {
+ int iBytesUsed, got_frame;
+ if (!m_pCodecContext) return -1;
+@@ -167,10 +180,15 @@ int COMXAudioCodecOMX::Decode(BYTE* pData, int iSize)
+ }
+
+ m_bGotFrame = true;
++ if (!m_iBufferOutputUsed)
++ {
++ m_dts = dts;
++ m_pts = pts;
++ }
+ return iBytesUsed;
+ }
+
+-int COMXAudioCodecOMX::GetData(BYTE** dst)
++int COMXAudioCodecOMX::GetData(BYTE** dst, double &dts, double &pts)
+ {
+ if (!m_bGotFrame)
+ return 0;
+@@ -179,13 +197,11 @@ int COMXAudioCodecOMX::GetData(BYTE** dst)
+ int inputSize = m_dllAvUtil.av_samples_get_buffer_size(&inLineSize, m_pCodecContext->channels, m_pFrame1->nb_samples, m_pCodecContext->sample_fmt, 0);
+ /* output audio will be packed */
+ int outputSize = m_dllAvUtil.av_samples_get_buffer_size(&outLineSize, m_pCodecContext->channels, m_pFrame1->nb_samples, m_desiredSampleFormat, 1);
+- bool cont = !m_pFrame1->data[1] || (m_pFrame1->data[1] == m_pFrame1->data[0] + inLineSize && inLineSize == outLineSize && inLineSize * m_pCodecContext->channels == inputSize);
+
+- if (m_iBufferOutputAlloced < outputSize)
++ if (m_iBufferOutputAlloced < m_iBufferOutputUsed + outputSize)
+ {
+- m_dllAvUtil.av_free(m_pBufferOutput);
+- m_pBufferOutput = (BYTE*)m_dllAvUtil.av_malloc(outputSize + FF_INPUT_BUFFER_PADDING_SIZE);
+- m_iBufferOutputAlloced = outputSize;
++ m_pBufferOutput = (BYTE*)m_dllAvUtil.av_realloc(m_pBufferOutput, m_iBufferOutputUsed + outputSize + FF_INPUT_BUFFER_PADDING_SIZE);
++ m_iBufferOutputAlloced = m_iBufferOutputUsed + outputSize;
+ }
+ *dst = m_pBufferOutput;
+
+@@ -217,7 +233,7 @@ int COMXAudioCodecOMX::GetData(BYTE** dst)
+
+ /* use unaligned flag to keep output packed */
+ uint8_t *out_planes[m_pCodecContext->channels];
+- if(m_dllAvUtil.av_samples_fill_arrays(out_planes, NULL, m_pBufferOutput, m_pCodecContext->channels, m_pFrame1->nb_samples, m_desiredSampleFormat, 1) < 0 ||
++ if(m_dllAvUtil.av_samples_fill_arrays(out_planes, NULL, m_pBufferOutput + m_iBufferOutputUsed, m_pCodecContext->channels, m_pFrame1->nb_samples, m_desiredSampleFormat, 1) < 0 ||
+ m_dllSwResample.swr_convert(m_pConvert, out_planes, m_pFrame1->nb_samples, (const uint8_t **)m_pFrame1->data, m_pFrame1->nb_samples) < 0)
+ {
+ CLog::Log(LOGERROR, "COMXAudioCodecOMX::Decode - Unable to convert format %d to %d", (int)m_pCodecContext->sample_fmt, m_desiredSampleFormat);
+@@ -226,35 +242,45 @@ int COMXAudioCodecOMX::GetData(BYTE** dst)
+ }
+ else
+ {
+- /* if it is already contiguous, just return decoded frame */
+- if (cont)
+- {
+- *dst = m_pFrame1->data[0];
+- }
+- else
++ /* copy to a contiguous buffer */
++ uint8_t *out_planes[m_pCodecContext->channels];
++ if (m_dllAvUtil.av_samples_fill_arrays(out_planes, NULL, m_pBufferOutput + m_iBufferOutputUsed, m_pCodecContext->channels, m_pFrame1->nb_samples, m_desiredSampleFormat, 1) < 0 ||
++ m_dllAvUtil.av_samples_copy(out_planes, m_pFrame1->data, 0, 0, m_pFrame1->nb_samples, m_pCodecContext->channels, m_desiredSampleFormat) < 0 )
+ {
+- /* copy to a contiguous buffer */
+- uint8_t *out_planes[m_pCodecContext->channels];
+- if (m_dllAvUtil.av_samples_fill_arrays(out_planes, NULL, m_pBufferOutput, m_pCodecContext->channels, m_pFrame1->nb_samples, m_desiredSampleFormat, 1) < 0 ||
+- m_dllAvUtil.av_samples_copy(out_planes, m_pFrame1->data, 0, 0, m_pFrame1->nb_samples, m_pCodecContext->channels, m_desiredSampleFormat) < 0 )
+- {
+- outputSize = 0;
+- }
++ outputSize = 0;
+ }
+ }
++ int desired_size = AUDIO_DECODE_OUTPUT_BUFFER * (m_pCodecContext->channels * GetBitsPerSample()) >> (rounded_up_channels_shift[m_pCodecContext->channels] + 4);
+
+ if (m_bFirstFrame)
+ {
+- CLog::Log(LOGDEBUG, "COMXAudioCodecOMX::GetData size=%d/%d line=%d/%d cont=%d buf=%p", inputSize, outputSize, inLineSize, outLineSize, cont, *dst);
++ CLog::Log(LOGDEBUG, "COMXAudioCodecOMX::GetData size=%d/%d line=%d/%d buf=%p, desired=%d", inputSize, outputSize, inLineSize, outLineSize, *dst, desired_size);
+ m_bFirstFrame = false;
+ }
+- return outputSize;
++ m_iBufferOutputUsed += outputSize;
++
++ if (!m_bNoConcatenate && m_pCodecContext->sample_fmt == AV_SAMPLE_FMT_FLTP && m_frameSize && (int)m_frameSize != outputSize)
++ CLog::Log(LOGERROR, "COMXAudioCodecOMX::GetData Unexpected change of size (%d->%d)", m_frameSize, outputSize);
++ m_frameSize = outputSize;
++
++ // if next buffer submitted won't fit then flush it out
++ if (m_iBufferOutputUsed + outputSize > desired_size || m_bNoConcatenate)
++ {
++ int ret = m_iBufferOutputUsed;
++ m_bGotFrame = false;
++ m_iBufferOutputUsed = 0;
++ dts = m_dts;
++ pts = m_pts;
++ return ret;
++ }
++ return 0;
+ }
+
+ void COMXAudioCodecOMX::Reset()
+ {
+ if (m_pCodecContext) m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);
+ m_bGotFrame = false;
++ m_iBufferOutputUsed = 0;
+ }
+
+ int COMXAudioCodecOMX::GetChannels()
+diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.h b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
+index 343465c..66e5b4a 100644
+--- a/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
++++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
+@@ -36,8 +36,8 @@ class COMXAudioCodecOMX
+ virtual ~COMXAudioCodecOMX();
+ bool Open(CDVDStreamInfo &hints);
+ void Dispose();
+- int Decode(BYTE* pData, int iSize);
+- int GetData(BYTE** dst);
++ int Decode(BYTE* pData, int iSize, double dts, double pts);
++ int GetData(BYTE** dst, double &dts, double &pts);
+ void Reset();
+ int GetChannels();
+ uint64_t GetChannelMap();
+@@ -45,6 +45,7 @@ class COMXAudioCodecOMX
+ int GetBitsPerSample();
+ static const char* GetName() { return "FFmpeg"; }
+ int GetBitRate();
++ unsigned int GetFrameSize() { return m_frameSize; }
+
+ protected:
+ AVCodecContext* m_pCodecContext;
+@@ -55,6 +56,7 @@ class COMXAudioCodecOMX
+ AVFrame* m_pFrame1;
+
+ BYTE *m_pBufferOutput;
++ int m_iBufferOutputUsed;
+ int m_iBufferOutputAlloced;
+
+ bool m_bOpenedCodec;
+@@ -63,6 +65,9 @@ class COMXAudioCodecOMX
+
+ bool m_bFirstFrame;
+ bool m_bGotFrame;
++ bool m_bNoConcatenate;
++ unsigned int m_frameSize;
++ double m_dts, m_pts;
+ DllAvCodec m_dllAvCodec;
+ DllAvUtil m_dllAvUtil;
+ DllSwResample m_dllSwResample;
+diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
+index 8219015..a4c11777 100644
+--- a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
++++ b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
+@@ -227,9 +227,10 @@ bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
+
+ if(!OMX_IS_RAW(m_format.m_dataFormat) && !bDropPacket)
+ {
++ double dts = pkt->dts, pts=pkt->pts;
+ while(!m_bStop && data_len > 0)
+ {
+- int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len);
++ int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len, dts, pts);
+ if( (len < 0) || (len > data_len) )
+ {
+ m_pAudioCodec->Reset();
+@@ -240,7 +241,7 @@ bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
+ data_len -= len;
+
+ uint8_t *decoded;
+- int decoded_size = m_pAudioCodec->GetData(&decoded);
++ int decoded_size = m_pAudioCodec->GetData(&decoded, dts, pts);
+
+ if(decoded_size <=0)
+ continue;
+@@ -274,7 +275,7 @@ bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
+ if(m_silence)
+ memset(decoded, 0x0, decoded_size);
+
+- ret = m_omxAudio.AddPackets(decoded, decoded_size, m_audioClock, m_audioClock);
++ ret = m_omxAudio.AddPackets(decoded, decoded_size, dts, pts, m_pAudioCodec->GetFrameSize());
+
+ if(ret != decoded_size)
+ {
+@@ -312,7 +313,7 @@ bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
+ if(m_silence)
+ memset(pkt->pData, 0x0, pkt->iSize);
+
+- m_omxAudio.AddPackets(pkt->pData, pkt->iSize, m_audioClock, m_audioClock);
++ m_omxAudio.AddPackets(pkt->pData, pkt->iSize, m_audioClock, m_audioClock, 0);
+ }
+
+ m_audioStats.AddSampleBytes(pkt->iSize);
+--
+1.9.3
+
+
+From 10a9d6134a624bf59096831851ee12191f658da1 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Wed, 5 Mar 2014 22:10:01 +0000
+Subject: [PATCH 53/94] [omxplayer] Use media for determing audio delay and
+ cache time
+
+I've also added caching to the call to OMXMediaTime as the GPU round trip is expensive when called too frequently
+---
+ xbmc/cores/omxplayer/OMXAudio.cpp | 33 ++++++++++++++-------
+ xbmc/linux/OMXClock.cpp | 62 +++++++++++++++++++++++++++------------
+ xbmc/linux/OMXClock.h | 2 ++
+ 3 files changed, 68 insertions(+), 29 deletions(-)
+
+diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
+index e67dc94..3e64de0 100644
+--- a/xbmc/cores/omxplayer/OMXAudio.cpp
++++ b/xbmc/cores/omxplayer/OMXAudio.cpp
+@@ -1118,29 +1118,40 @@ void COMXAudio::UpdateAttenuation()
+ //***********************************************************************************************
+ unsigned int COMXAudio::GetSpace()
+ {
+- int free = m_omx_decoder.GetInputBufferSpace();
+- return free;
++ return m_omx_decoder.GetInputBufferSpace();
+ }
+
+ float COMXAudio::GetDelay()
+ {
+- unsigned int free = m_omx_decoder.GetInputBufferSize() - m_omx_decoder.GetInputBufferSpace();
+- return m_BytesPerSec ? (float)free / (float)m_BytesPerSec : 0.0f;
++ CSingleLock lock (m_critSection);
++ double stamp = DVD_NOPTS_VALUE;
++ double ret = 0.0;
++ if (m_last_pts != DVD_NOPTS_VALUE && m_av_clock)
++ stamp = m_av_clock->OMXMediaTime();
++ // if possible the delay is current media time - time of last submitted packet
++ if (stamp != DVD_NOPTS_VALUE)
++ {
++ ret = (m_last_pts - stamp) * (1.0 / DVD_TIME_BASE);
++ //CLog::Log(LOGINFO, "%s::%s - %.2f %.0f %.0f", CLASSNAME, __func__, ret, stamp, m_last_pts);
++ }
++ else // just measure the input fifo
++ {
++ unsigned int used = m_omx_decoder.GetInputBufferSize() - m_omx_decoder.GetInputBufferSpace();
++ float ret = m_InputBytesPerSec ? (float)used / (float)m_InputBytesPerSec : 0.0f;
++ //CLog::Log(LOGINFO, "%s::%s - %.2f %d, %d, %d", CLASSNAME, __func__, ret, used, m_omx_decoder.GetInputBufferSize(), m_omx_decoder.GetInputBufferSpace());
++ }
++ return ret;
+ }
+
+ float COMXAudio::GetCacheTime()
+ {
+- float fBufferLenFull = (float)m_BufferLen - (float)GetSpace();
+- if(fBufferLenFull < 0)
+- fBufferLenFull = 0;
+- float ret = m_BytesPerSec ? fBufferLenFull / (float)m_BytesPerSec : 0.0f;
+- return ret;
++ return GetDelay();
+ }
+
+ float COMXAudio::GetCacheTotal()
+ {
+- float audioplus_buffer = m_SampleRate ? 0.0f : 32.0f * 512.0f / m_SampleRate;
+- float input_buffer = (float)m_omx_decoder.GetInputBufferSize() / (float)m_InputBytesPerSec;
++ float audioplus_buffer = m_SampleRate ? 32.0f * 512.0f / m_SampleRate : 0.0f;
++ float input_buffer = m_InputBytesPerSec ? (float)m_omx_decoder.GetInputBufferSize() / (float)m_InputBytesPerSec : 0;
+ return AUDIO_BUFFER_SECONDS + input_buffer + audioplus_buffer;
+ }
+
+diff --git a/xbmc/linux/OMXClock.cpp b/xbmc/linux/OMXClock.cpp
+index 241657b..bee7bac 100644
+--- a/xbmc/linux/OMXClock.cpp
++++ b/xbmc/linux/OMXClock.cpp
+@@ -45,6 +45,8 @@ OMXClock::OMXClock()
+ m_eState = OMX_TIME_ClockStateStopped;
+ m_eClock = OMX_TIME_RefClockNone;
+ m_clock = NULL;
++ m_last_media_time = 0.0f;
++ m_last_media_time_read = 0.0f;
+
+ pthread_mutex_init(&m_lock, NULL);
+ }
+@@ -113,6 +115,7 @@ bool OMXClock::OMXSetReferenceClock(bool has_audio, bool lock /* = true */)
+ }
+ m_eClock = refClock.eClock;
+ }
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -142,6 +145,7 @@ void OMXClock::OMXDeinitialize()
+ m_omx_clock.Deinitialize();
+
+ m_omx_speed = DVD_PLAYSPEED_NORMAL;
++ m_last_media_time = 0.0f;
+ }
+
+ bool OMXClock::OMXStateExecute(bool lock /* = true */)
+@@ -169,6 +173,7 @@ bool OMXClock::OMXStateExecute(bool lock /* = true */)
+ }
+ }
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -186,6 +191,7 @@ void OMXClock::OMXStateIdle(bool lock /* = true */)
+ if(m_omx_clock.GetState() != OMX_StateIdle)
+ m_omx_clock.SetStateForComponent(OMX_StateIdle);
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+ }
+@@ -222,6 +228,7 @@ bool OMXClock::OMXStop(bool lock /* = true */)
+ }
+ m_eState = clock.eState;
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -252,6 +259,7 @@ bool OMXClock::OMXStep(int steps /* = 1 */, bool lock /* = true */)
+ return false;
+ }
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -302,6 +310,7 @@ bool OMXClock::OMXReset(bool has_video, bool has_audio, bool lock /* = true */)
+ }
+ }
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -310,33 +319,45 @@ bool OMXClock::OMXReset(bool has_video, bool has_audio, bool lock /* = true */)
+
+ double OMXClock::OMXMediaTime(bool lock /* = true */)
+ {
++ double pts = 0.0;
+ if(m_omx_clock.GetComponent() == NULL)
+ return 0;
+
+- if(lock)
+- Lock();
++ double now = GetAbsoluteClock();
++ if (now - m_last_media_time_read > DVD_MSEC_TO_TIME(100) || m_last_media_time == 0.0)
++ {
++ if(lock)
++ Lock();
+
+- OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+- double pts = 0;
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
+- OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
+- OMX_INIT_STRUCTURE(timeStamp);
+- timeStamp.nPortIndex = m_omx_clock.GetInputPort();
++ OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
++ OMX_INIT_STRUCTURE(timeStamp);
++ timeStamp.nPortIndex = m_omx_clock.GetInputPort();
++
++ omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentMediaTime, &timeStamp);
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigTimeCurrentMediaTime\n");
++ if(lock)
++ UnLock();
++ return 0;
++ }
++
++ pts = FromOMXTime(timeStamp.nTimestamp);
++ //CLog::Log(LOGINFO, "OMXClock::MediaTime %.2f (%.2f, %.2f)", pts, m_last_media_time, now - m_last_media_time_read);
++ m_last_media_time = pts;
++ m_last_media_time_read = now;
+
+- omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentMediaTime, &timeStamp);
+- if(omx_err != OMX_ErrorNone)
+- {
+- CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigTimeCurrentMediaTime\n");
+ if(lock)
+ UnLock();
+- return 0;
+ }
+-
+- pts = FromOMXTime(timeStamp.nTimestamp);
+-
+- if(lock)
+- UnLock();
+-
++ else
++ {
++ double speed = m_pause ? 0.0 : (double)m_omx_speed / DVD_PLAYSPEED_NORMAL;
++ pts = m_last_media_time + (now - m_last_media_time_read) * speed;
++ //CLog::Log(LOGINFO, "OMXClock::MediaTime cached %.2f (%.2f, %.2f)", pts, m_last_media_time, now - m_last_media_time_read);
++ }
+ return pts;
+ }
+
+@@ -409,6 +430,7 @@ bool OMXClock::OMXMediaTime(double pts, bool lock /* = true*/)
+ CLog::Log(LOGDEBUG, "OMXClock::OMXMediaTime set config %s = %.2f", index == OMX_IndexConfigTimeCurrentAudioReference ?
+ "OMX_IndexConfigTimeCurrentAudioReference":"OMX_IndexConfigTimeCurrentVideoReference", pts);
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -428,6 +450,7 @@ bool OMXClock::OMXPause(bool lock /* = true */)
+ if (OMXSetSpeed(0, false, true))
+ m_pause = true;
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+ }
+@@ -447,6 +470,7 @@ bool OMXClock::OMXResume(bool lock /* = true */)
+ if (OMXSetSpeed(m_omx_speed, false, true))
+ m_pause = false;
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+ }
+@@ -485,6 +509,7 @@ bool OMXClock::OMXSetSpeed(int speed, bool lock /* = true */, bool pause_resume
+ if (!pause_resume)
+ m_omx_speed = speed;
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+@@ -521,6 +546,7 @@ bool OMXClock::HDMIClockSync(bool lock /* = true */)
+ return false;
+ }
+
++ m_last_media_time = 0.0f;
+ if(lock)
+ UnLock();
+
+diff --git a/xbmc/linux/OMXClock.h b/xbmc/linux/OMXClock.h
+index d7d06fe..f83074a 100644
+--- a/xbmc/linux/OMXClock.h
++++ b/xbmc/linux/OMXClock.h
+@@ -58,6 +58,8 @@ class OMXClock
+ CDVDClock *m_clock;
+ private:
+ COMXCoreComponent m_omx_clock;
++ double m_last_media_time;
++ double m_last_media_time_read;
+ public:
+ OMXClock();
+ ~OMXClock();
+--
+1.9.3
+
+
+From 29c5c42b2f5be546c242bc8ef02dc06a8dd0fd17 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 3 Mar 2014 22:24:19 +0000
+Subject: [PATCH 54/94] [omx] Skip the resize when not needed when decoding
+ jpegs
+
+The decode to texture path almost always uses cached jpegs that are the correct size, so the resize is rarely needed.
+The re-enc path usually needs resizing, but may not where the source is small.
+
+Skipping the resize stage saves a little time and memory on GPU and also saves some setup time.
+---
+ xbmc/cores/omxplayer/OMXImage.cpp | 262 ++++++++++++++++++++++----------------
+ 1 file changed, 152 insertions(+), 110 deletions(-)
+
+diff --git a/xbmc/cores/omxplayer/OMXImage.cpp b/xbmc/cores/omxplayer/OMXImage.cpp
+index 4456fdb..262a004 100644
+--- a/xbmc/cores/omxplayer/OMXImage.cpp
++++ b/xbmc/cores/omxplayer/OMXImage.cpp
+@@ -1477,9 +1477,22 @@ bool COMXImageReEnc::HandlePortSettingChange(unsigned int resize_width, unsigned
+ return false;
+ }
+
++ if (resize_width != port_def.format.image.nFrameWidth || resize_height != port_def.format.image.nFrameHeight || (orientation & 4))
++ {
++ if(!m_omx_resize.Initialize("OMX.broadcom.resize", OMX_IndexParamImageInit))
++ {
++ CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize\n", CLASSNAME, __func__);
++ return false;
++ }
++ }
++
+ // TODO: jpeg decoder can decimate by factors of 2
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+- port_def.format.image.nSliceHeight = 16;//(port_def.format.image.nFrameHeight+15) & ~15;
++ if (m_omx_resize.IsInitialized())
++ port_def.format.image.nSliceHeight = 16;
++ else
++ port_def.format.image.nSliceHeight = (resize_height+15) & ~15;
++
+ port_def.format.image.nStride = 0;
+
+ m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+@@ -1489,38 +1502,35 @@ bool COMXImageReEnc::HandlePortSettingChange(unsigned int resize_width, unsigned
+ return false;
+ }
+
+- if(!m_omx_resize.Initialize("OMX.broadcom.resize", OMX_IndexParamImageInit))
++ if (m_omx_resize.IsInitialized())
+ {
+- CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize\n", CLASSNAME, __func__);
+- return false;
+- }
+-
+- port_def.nPortIndex = m_omx_resize.GetInputPort();
++ port_def.nPortIndex = m_omx_resize.GetInputPort();
+
+- 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;
+- }
++ 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;
++ }
+
+- port_def.nPortIndex = m_omx_resize.GetOutputPort();
+- 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.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+- port_def.format.image.nFrameWidth = resize_width;
+- port_def.format.image.nFrameHeight = resize_height;
+- port_def.format.image.nSliceHeight = (resize_height+15) & ~15;
+- port_def.format.image.nStride = 0;
+- 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;
++ port_def.nPortIndex = m_omx_resize.GetOutputPort();
++ 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.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
++ port_def.format.image.nFrameWidth = resize_width;
++ port_def.format.image.nFrameHeight = resize_height;
++ port_def.format.image.nSliceHeight = (resize_height+15) & ~15;
++ port_def.format.image.nStride = 0;
++ 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;
++ }
+ }
+
+ if(!m_omx_encoder.Initialize("OMX.broadcom.image_encode", OMX_IndexParamImageInit))
+@@ -1621,31 +1631,44 @@ bool COMXImageReEnc::HandlePortSettingChange(unsigned int resize_width, unsigned
+ return false;
+ }
+
+- 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();
+- if(omx_err != OMX_ErrorNone)
++ if (m_omx_resize.IsInitialized())
+ {
+- CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish\n", CLASSNAME, __func__);
+- return false;
+- }
++ m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_resize, m_omx_resize.GetInputPort());
+
+- m_omx_tunnel_resize.Initialize(&m_omx_resize, m_omx_resize.GetOutputPort(), &m_omx_encoder, m_omx_encoder.GetInputPort());
++ omx_err = m_omx_tunnel_decode.Establish();
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish\n", CLASSNAME, __func__);
++ return false;
++ }
+
+- omx_err = m_omx_tunnel_resize.Establish();
+- if(omx_err != OMX_ErrorNone)
+- {
+- CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_resize.Establish\n", CLASSNAME, __func__);
+- return false;
+- }
++ m_omx_tunnel_resize.Initialize(&m_omx_resize, m_omx_resize.GetOutputPort(), &m_omx_encoder, m_omx_encoder.GetInputPort());
+
+- 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;
++ omx_err = m_omx_tunnel_resize.Establish();
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_resize.Establish\n", CLASSNAME, __func__);
++ 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;
++ }
+ }
++ else
++ {
++ m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_encoder, m_omx_encoder.GetInputPort());
+
++ omx_err = m_omx_tunnel_decode.Establish();
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish\n", CLASSNAME, __func__);
++ return false;
++ }
++ }
+ omx_err = m_omx_encoder.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+@@ -1662,24 +1685,27 @@ bool COMXImageReEnc::HandlePortSettingChange(unsigned int resize_width, unsigned
+ // a little surprising, make a note
+ CLog::Log(LOGDEBUG, "%s::%s m_omx_resize second port changed event\n", CLASSNAME, __func__);
+ m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), true);
+- m_omx_resize.DisablePort(m_omx_resize.GetInputPort(), true);
++ if (m_omx_resize.IsInitialized())
++ {
++ m_omx_resize.DisablePort(m_omx_resize.GetInputPort(), true);
+
+- OMX_PARAM_PORTDEFINITIONTYPE port_def;
+- OMX_INIT_STRUCTURE(port_def);
++ OMX_PARAM_PORTDEFINITIONTYPE port_def;
++ OMX_INIT_STRUCTURE(port_def);
+
+- port_def.nPortIndex = m_omx_decoder.GetOutputPort();
+- m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+- port_def.nPortIndex = m_omx_resize.GetInputPort();
+- m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
++ port_def.nPortIndex = m_omx_decoder.GetOutputPort();
++ m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
++ port_def.nPortIndex = m_omx_resize.GetInputPort();
++ m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+
+- omx_err = m_omx_resize.WaitForEvent(OMX_EventPortSettingsChanged);
+- if(omx_err != OMX_ErrorNone)
+- {
+- CLog::Log(LOGERROR, "%s::%s m_omx_resize.WaitForEvent=%x\n", CLASSNAME, __func__, omx_err);
+- return false;
++ omx_err = m_omx_resize.WaitForEvent(OMX_EventPortSettingsChanged);
++ if(omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_resize.WaitForEvent=%x\n", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++ m_omx_resize.EnablePort(m_omx_resize.GetInputPort(), true);
+ }
+ m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), true);
+- m_omx_resize.EnablePort(m_omx_resize.GetInputPort(), true);
+ }
+ return true;
+ }
+@@ -1918,42 +1944,45 @@ bool COMXTexture::HandlePortSettingChange(unsigned int resize_width, unsigned in
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+-
+- if (!m_omx_resize.Initialize("OMX.broadcom.resize", OMX_IndexParamImageInit))
++ if (resize_width != port_def.format.image.nFrameWidth || resize_height != port_def.format.image.nFrameHeight)
+ {
+- CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize", CLASSNAME, __func__);
+- return false;
++ if (!m_omx_resize.Initialize("OMX.broadcom.resize", OMX_IndexParamImageInit))
++ {
++ CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize", CLASSNAME, __func__);
++ return false;
++ }
+ }
+-
+- port_def.nPortIndex = m_omx_resize.GetInputPort();
+-
+- omx_err = m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+- if (omx_err != OMX_ErrorNone)
++ if (m_omx_resize.IsInitialized())
+ {
+- CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+- return false;
+- }
++ port_def.nPortIndex = m_omx_resize.GetInputPort();
+
+- 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;
+- }
++ 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;
++ }
+
+- port_def.format.image.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+- port_def.format.image.nFrameWidth = resize_width;
+- port_def.format.image.nFrameHeight = resize_height;
+- port_def.format.image.nSliceHeight = 16;
+- port_def.format.image.nStride = 0;
+- 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;
+- }
++ 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.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
++ port_def.format.image.nFrameWidth = resize_width;
++ port_def.format.image.nFrameHeight = resize_height;
++ port_def.format.image.nSliceHeight = 16;
++ port_def.format.image.nStride = 0;
++ 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;
++ }
++ }
+ if (!m_omx_egl_render.Initialize("OMX.broadcom.egl_render", OMX_IndexParamVideoInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s error m_omx_egl_render.Initialize", CLASSNAME, __func__);
+@@ -1983,30 +2012,43 @@ bool COMXTexture::HandlePortSettingChange(unsigned int resize_width, unsigned in
+ CLog::Log(LOGERROR, "%s::%s error m_omx_egl_render.UseEGLImage (%x)", CLASSNAME, __func__, omx_err);
+ return false;
+ }
++ if (m_omx_resize.IsInitialized())
++ {
++ m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_resize, m_omx_resize.GetInputPort());
+
+- 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();
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish (%x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
+
+- omx_err = m_omx_tunnel_decode.Establish();
+- if (omx_err != OMX_ErrorNone)
+- {
+- CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish (%x)", CLASSNAME, __func__, omx_err);
+- return false;
+- }
++ m_omx_tunnel_egl.Initialize(&m_omx_resize, m_omx_resize.GetOutputPort(), &m_omx_egl_render, m_omx_egl_render.GetInputPort());
+
+- m_omx_tunnel_egl.Initialize(&m_omx_resize, m_omx_resize.GetOutputPort(), &m_omx_egl_render, m_omx_egl_render.GetInputPort());
++ omx_err = m_omx_tunnel_egl.Establish();
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_egl.Establish (%x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
+
+- omx_err = m_omx_tunnel_egl.Establish();
+- if (omx_err != OMX_ErrorNone)
+- {
+- CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_egl.Establish (%x)", CLASSNAME, __func__, omx_err);
+- return false;
++ omx_err = m_omx_resize.SetStateForComponent(OMX_StateExecuting);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s error m_omx_egl_render.GetParameter (%x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
+ }
+-
+- omx_err = m_omx_resize.SetStateForComponent(OMX_StateExecuting);
+- if (omx_err != OMX_ErrorNone)
++ else
+ {
+- CLog::Log(LOGERROR, "%s::%s error m_omx_egl_render.GetParameter (%x)", CLASSNAME, __func__, omx_err);
+- return false;
++ m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_egl_render, m_omx_egl_render.GetInputPort());
++
++ omx_err = m_omx_tunnel_decode.Establish();
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish (%x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
+ }
+
+ omx_err = m_omx_egl_render.SetStateForComponent(OMX_StateExecuting);
+--
+1.9.3
+
+
+From 1156d9abfac43de458d4ba66e5494c1d027e0f17 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Sat, 8 Mar 2014 15:36:06 +0000
+Subject: [PATCH 55/94] [hifiberry] Hack: force it to be recognised as IEC958
+ capable to enable passthrough options
+
+---
+ xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
+index b48a4fc..d9897e5 100644
+--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
++++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
+@@ -932,6 +932,10 @@ void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &dev
+ if (snd_card_get_name(cardNr, &cardName) == 0)
+ info.m_displayName = cardName;
+
++ // hack: hifiberry digi doesn't correctly report as iec958 device. Needs fixing in kernel driver
++ if (info.m_displayName == "snd_rpi_hifiberry_digi")
++ info.m_deviceType = AE_DEVTYPE_IEC958;
++
+ if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 &&
+ info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI")
+ {
+--
+1.9.3
+
+
+From 03aa9ebf0993f06d24a34f8dd7d86a183ebd2dcd Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Tue, 11 Mar 2014 18:50:23 +0000
+Subject: [PATCH 56/94] [dvdplayer] Use inexact seeking like omxplayer
+
+---
+ xbmc/cores/dvdplayer/DVDPlayer.cpp | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
+index d607f55..1d4ba52 100644
+--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
++++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
+@@ -1935,7 +1935,11 @@ void CDVDPlayer::CheckAutoSceneSkip()
+ /*
+ * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
+ */
++#ifdef TARGET_RASPBERRY_PI
++ m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, true, true, false, true));
++#else
+ m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false, true));
++#endif
+ /*
+ * 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
+@@ -1953,7 +1957,11 @@ void CDVDPlayer::CheckAutoSceneSkip()
+ /*
+ * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
+ */
++#ifdef TARGET_RASPBERRY_PI
++ m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
++#else
+ m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
++#endif
+ /*
+ * 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
+@@ -3293,9 +3301,12 @@ bool CDVDPlayer::CloseTeletextStream(bool bWaitForBuffers)
+ void CDVDPlayer::FlushBuffers(bool queued, double pts, bool accurate)
+ {
+ double startpts;
++#ifndef TARGET_RASPBERRY_PI
++ /* for now, ignore accurate flag as it discards keyframes and causes corrupt frames */
+ if(accurate)
+ startpts = pts;
+ else
++#endif
+ startpts = DVD_NOPTS_VALUE;
+
+ /* call with demuxer pts */
+--
+1.9.3
+
+
+From d4487b87819003a44f59a607074a41108f644915 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Thu, 13 Mar 2014 16:08:46 +0000
+Subject: [PATCH 57/94] [omxplayer] Make use of TrueHD fastpath when downmixing
+
+The TrueHD codec actually works in 3 stages. It decodes the downmixed stereo. It then decodes the differences required to produce 5.1.
+It then decodes the differences required to produce 7.1.
+
+Many users end up downmixing this 7.1 stream back to 2.0.
+Much better to tell the codec we only need the 2.0 stream. It saves about 50% of the CPU required
+---
+ xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
+index 557e847..7f6ef6e 100644
+--- a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
++++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
+@@ -25,6 +25,8 @@
+ #include "utils/log.h"
+
+ #include "cores/AudioEngine/Utils/AEUtil.h"
++#include "settings/Settings.h"
++#include "PCMRemap.h"
+
+ // the size of the audio_render output port buffers
+ #define AUDIO_DECODE_OUTPUT_BUFFER (32*1024)
+@@ -91,6 +93,16 @@ bool COMXAudioCodecOMX::Open(CDVDStreamInfo &hints)
+ m_pCodecContext->block_align = hints.blockalign;
+ m_pCodecContext->bit_rate = hints.bitrate;
+ m_pCodecContext->bits_per_coded_sample = hints.bitspersample;
++ enum PCMLayout layout = (enum PCMLayout)std::max(0, CSettings::Get().GetInt("audiooutput.channels")-1);
++ if (hints.codec == AV_CODEC_ID_TRUEHD)
++ {
++ if (layout == PCM_LAYOUT_2_0)
++ m_pCodecContext->request_channel_layout = AV_CH_LAYOUT_STEREO;
++ else if (layout <= PCM_LAYOUT_5_1)
++ m_pCodecContext->request_channel_layout = AV_CH_LAYOUT_5POINT1;
++ }
++ if (m_pCodecContext->request_channel_layout)
++ CLog::Log(LOGNOTICE,"COMXAudioCodecOMX::Open() Requesting channel layout of %d", (unsigned)m_pCodecContext->request_channel_layout);
+
+ // vorbis has variable sized planar output, so skip concatenation
+ if (hints.codec == AV_CODEC_ID_VORBIS)
+--
+1.9.3
+
+
+From 8dedad4307cbca1416262af9e2ac2404c7490713 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Sat, 15 Mar 2014 19:38:38 +0000
+Subject: [PATCH 58/94] [omxplayer] When in dual audio mode, make one output
+ the slave
+
+May help audio sync between the two outputs
+---
+ xbmc/cores/omxplayer/OMXAudio.cpp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
+index 3e64de0..72e42ec 100644
+--- a/xbmc/cores/omxplayer/OMXAudio.cpp
++++ b/xbmc/cores/omxplayer/OMXAudio.cpp
+@@ -245,7 +245,7 @@ bool COMXAudio::PortSettingsChanged()
+ {
+ // By default audio_render is the clock master, and if output samples don't fit the timestamps, it will speed up/slow down the clock.
+ // This tends to be better for maintaining audio sync and avoiding audio glitches, but can affect video/display sync
+- if(CSettings::Get().GetBool("videoplayer.usedisplayasclock"))
++ if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") || (CSettings::Get().GetBool("audiooutput.dualaudio") && CSettings::Get().GetString("audiooutput.audiodevice") != "PI:Analogue"))
+ {
+ OMX_CONFIG_BOOLEANTYPE configBool;
+ OMX_INIT_STRUCTURE(configBool);
+@@ -271,7 +271,7 @@ bool COMXAudio::PortSettingsChanged()
+ {
+ // By default audio_render is the clock master, and if output samples don't fit the timestamps, it will speed up/slow down the clock.
+ // This tends to be better for maintaining audio sync and avoiding audio glitches, but can affect video/display sync
+- if(CSettings::Get().GetBool("videoplayer.usedisplayasclock"))
++ if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") || (CSettings::Get().GetBool("audiooutput.dualaudio") && CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue"))
+ {
+ OMX_CONFIG_BOOLEANTYPE configBool;
+ OMX_INIT_STRUCTURE(configBool);
+--
+1.9.3
+
+
+From 96842193cb39ac3625a1dcbdd67388141733a5ee Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 30 Dec 2013 12:02:14 +0000
+Subject: [PATCH 59/94] [rbp] Hardware accelerated resampling
+
+This replaces the format conversion, up/down mixing and resampling code from ActiveAE with a GPU accelerated version.
+Should significantly reduce CPU when using paplayer or dvdplayer.
+
+Requires updated firmware
+---
+ .../Engines/ActiveAE/ActiveAEResample.cpp | 5 +
+ .../Engines/ActiveAE/ActiveAEResample.h | 9 +
+ .../Engines/ActiveAE/ActiveAEResamplePi.cpp | 592 +++++++++++++++++++++
+ .../Engines/ActiveAE/ActiveAEResamplePi.h | 65 +++
+ xbmc/cores/AudioEngine/Makefile.in | 1 +
+ xbmc/linux/OMXCore.cpp | 4 +-
+ 6 files changed, 674 insertions(+), 2 deletions(-)
+ create mode 100644 xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+ create mode 100644 xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.h
+
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp
+index e131f16..94b69a0 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp
+@@ -18,6 +18,10 @@
+ *
+ */
+
++#include "system.h"
++
++#if !defined(TARGET_RASPBERRY_PI)
++
+ #include "ActiveAEResample.h"
+
+ using namespace ActiveAE;
+@@ -344,3 +348,4 @@ int CActiveAEResample::GetAVChannelIndex(enum AEChannel aechannel, uint64_t layo
+ {
+ return m_dllAvUtil.av_get_channel_layout_channel_index(layout, GetAVChannel(aechannel));
+ }
++#endif
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h
+index 1e0e342..6a8949b 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h
+@@ -21,11 +21,18 @@
+
+ #include "DllAvUtil.h"
+ #include "DllSwResample.h"
++
++#include "system.h"
++
+ #include "cores/AudioEngine/Utils/AEChannelInfo.h"
+ #include "cores/AudioEngine/Utils/AEAudioFormat.h"
+ #include "cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.h"
+ #include "cores/AudioEngine/Interfaces/AE.h"
+
++#if defined(TARGET_RASPBERRY_PI)
++#include "ActiveAEResamplePi.h"
++#else
++
+ namespace ActiveAE
+ {
+
+@@ -62,3 +69,5 @@ class CActiveAEResample
+ };
+
+ }
++
++#endif
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+new file mode 100644
+index 0000000..1d7b425
+--- /dev/null
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+@@ -0,0 +1,592 @@
++/*
++ * Copyright (C) 2010-2013 Team XBMC
++ * http://xbmc.org
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with XBMC; see the file COPYING. If not, see
++ * <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "system.h"
++
++#if defined(TARGET_RASPBERRY_PI)
++
++#include "ActiveAEResample.h"
++#include "linux/RBP.h"
++#include "cores/omxplayer/PCMRemap.h"
++#include "settings/Settings.h"
++#include "utils/log.h"
++
++//#define DEBUG_VERBOSE
++
++#define CLASSNAME "CActiveAEResamplePi"
++
++#define BUFFERSIZE (32*1024*2*8)
++
++//#define BENCHMARKING
++#ifdef BENCHMARKING
++#define LOGTIMEINIT(f) \
++ struct timespec now; \
++ uint64_t Start, End; \
++ clock_gettime(CLOCK_MONOTONIC, &now); \
++ Start = ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec; \
++ const char *_filename = f;
++
++#define LOGTIME(n) \
++ clock_gettime(CLOCK_MONOTONIC, &now); \
++ End = ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec; \
++ CLog::Log(LOGNOTICE, "ActiveAE::%s %d - resample %s took %.0fms", __FUNCTION__, n, _filename, (End-Start)*1e-6); \
++ Start=End;
++#else
++#define LOGTIMEINIT(f)
++#define LOGTIME(n)
++#endif
++
++using namespace ActiveAE;
++
++CActiveAEResample::CActiveAEResample()
++{
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ m_loaded = false;
++
++ if (m_dllAvUtil.Load() && m_dllSwResample.Load())
++ m_loaded = true;
++
++ m_Initialized = false;
++ m_last_src_fmt = AV_SAMPLE_FMT_NONE;
++ m_last_dst_fmt = AV_SAMPLE_FMT_NONE;
++ m_last_src_channels = 0;
++ m_last_dst_channels = 0;
++}
++
++CActiveAEResample::~CActiveAEResample()
++{
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ DeInit();
++ m_dllAvUtil.Unload();
++ m_dllSwResample.Unload();
++}
++
++void CActiveAEResample::DeInit()
++{
++ CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
++ if (m_Initialized)
++ {
++ m_omx_mixer.FlushAll();
++ m_omx_mixer.Deinitialize();
++ m_Initialized = false;
++ }
++}
++
++bool CActiveAEResample::Init(uint64_t dst_chan_layout, int dst_channels, int dst_rate, AVSampleFormat dst_fmt, int dst_bits, uint64_t src_chan_layout, int src_channels, int src_rate, AVSampleFormat src_fmt, int src_bits, bool upmix, bool normalize, CAEChannelInfo *remapLayout, AEQuality quality)
++{
++ LOGTIMEINIT("x");
++
++ CLog::Log(LOGINFO, "%s::%s remap:%p chan:%d->%d rate:%d->%d format:%d->%d bits:%d->%d norm:%d upmix:%d", CLASSNAME, __func__, remapLayout, src_channels, dst_channels, src_rate, dst_rate, src_fmt, dst_fmt, src_bits, dst_bits, normalize, upmix);
++ if (!m_loaded)
++ return false;
++
++ m_dst_chan_layout = dst_chan_layout;
++ m_dst_channels = dst_channels;
++ m_dst_rate = dst_rate;
++ m_dst_fmt = dst_fmt;
++ m_dst_bits = dst_bits ? dst_bits : 8;
++ m_src_chan_layout = src_chan_layout;
++ m_src_channels = src_channels;
++ m_src_rate = src_rate;
++ m_src_fmt = src_fmt;
++ m_src_bits = src_bits ? src_bits : 8;
++
++ if (m_dst_chan_layout == 0)
++ m_dst_chan_layout = m_dllAvUtil.av_get_default_channel_layout(m_dst_channels);
++ if (m_src_chan_layout == 0)
++ m_src_chan_layout = m_dllAvUtil.av_get_default_channel_layout(m_src_channels);
++
++ OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS8x8 mix;
++ OMX_INIT_STRUCTURE(mix);
++
++ assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 64);
++
++ LOGTIME(1);
++// this code is just uses ffmpeg to produce the 8x8 mixing matrix
++{
++ // dummy sample rate and format, as we only care about channel mapping
++ SwrContext *m_pContext = m_dllSwResample.swr_alloc_set_opts(NULL, m_dst_chan_layout, AV_SAMPLE_FMT_FLT, 48000,
++ m_src_chan_layout, AV_SAMPLE_FMT_FLT, 48000, 0, NULL);
++ if (!m_pContext)
++ {
++ CLog::Log(LOGERROR, "CActiveAEResample::Init - create context failed");
++ return false;
++ }
++ // tell resampler to clamp float values
++ // not required for sink stage (remapLayout == true)
++ if (!remapLayout && normalize)
++ {
++ m_dllAvUtil.av_opt_set_double(m_pContext, "rematrix_maxval", 1.0, 0);
++ }
++
++ if (remapLayout)
++ {
++ // one-to-one mapping of channels
++ // remapLayout is the layout of the sink, if the channel is in our src layout
++ // the channel is mapped by setting coef 1.0
++ double m_rematrix[AE_CH_MAX][AE_CH_MAX];
++ memset(m_rematrix, 0, sizeof(m_rematrix));
++ m_dst_chan_layout = 0;
++ for (unsigned int out=0; out<remapLayout->Count(); out++)
++ {
++ m_dst_chan_layout += (uint64_t) (1 << out);
++ int idx = GetAVChannelIndex((*remapLayout)[out], m_src_chan_layout);
++ if (idx >= 0)
++ {
++ m_rematrix[out][idx] = 1.0;
++ }
++ }
++
++ m_dllAvUtil.av_opt_set_int(m_pContext, "out_channel_count", m_dst_channels, 0);
++ m_dllAvUtil.av_opt_set_int(m_pContext, "out_channel_layout", m_dst_chan_layout, 0);
++
++ if (m_dllSwResample.swr_set_matrix(m_pContext, (const double*)m_rematrix, AE_CH_MAX) < 0)
++ {
++ CLog::Log(LOGERROR, "CActiveAEResample::Init - setting channel matrix failed");
++ return false;
++ }
++ }
++ // stereo upmix
++ else if (upmix && m_src_channels == 2 && m_dst_channels > 2)
++ {
++ double m_rematrix[AE_CH_MAX][AE_CH_MAX];
++ memset(m_rematrix, 0, sizeof(m_rematrix));
++ for (int out=0; out<m_dst_channels; out++)
++ {
++ uint64_t out_chan = m_dllAvUtil.av_channel_layout_extract_channel(m_dst_chan_layout, out);
++ switch(out_chan)
++ {
++ case AV_CH_FRONT_LEFT:
++ case AV_CH_BACK_LEFT:
++ case AV_CH_SIDE_LEFT:
++ m_rematrix[out][0] = 1.0;
++ break;
++ case AV_CH_FRONT_RIGHT:
++ case AV_CH_BACK_RIGHT:
++ case AV_CH_SIDE_RIGHT:
++ m_rematrix[out][1] = 1.0;
++ break;
++ case AV_CH_FRONT_CENTER:
++ m_rematrix[out][0] = 0.5;
++ m_rematrix[out][1] = 0.5;
++ break;
++ case AV_CH_LOW_FREQUENCY:
++ m_rematrix[out][0] = 0.5;
++ m_rematrix[out][1] = 0.5;
++ break;
++ default:
++ break;
++ }
++ }
++
++ if (m_dllSwResample.swr_set_matrix(m_pContext, (const double*)m_rematrix, AE_CH_MAX) < 0)
++ {
++ CLog::Log(LOGERROR, "CActiveAEResample::Init - setting channel matrix failed");
++ return false;
++ }
++ }
++
++ if (m_dllSwResample.swr_init(m_pContext) < 0)
++ {
++ CLog::Log(LOGERROR, "CActiveAEResample::Init - init resampler failed");
++ return false;
++ }
++
++ const int samples = 8;
++ uint8_t *output, *input;
++ av_samples_alloc(&output, NULL, m_dst_channels, samples, AV_SAMPLE_FMT_FLT, 1);
++ av_samples_alloc(&input , NULL, m_src_channels, samples, AV_SAMPLE_FMT_FLT, 1);
++
++ // Produce "identity" samples
++ float *f = (float *)input;
++ for (int j=0; j < samples; j++)
++ for (int i=0; i < m_src_channels; i++)
++ *f++ = i == j ? 1.0f : 0.0f;
++
++ int ret = m_dllSwResample.swr_convert(m_pContext, &output, samples, (const uint8_t **)&input, samples);
++ if (ret < 0)
++ CLog::Log(LOGERROR, "CActiveAEResample::Resample - resample failed");
++
++ f = (float *)output;
++ for (int j=0; j < samples; j++)
++ for (int i=0; i < m_dst_channels; i++)
++ mix.coeff[8*i+j] = *f++ * (1<<16);
++
++ for (int j=0; j < 8; j++)
++ {
++ char s[128] = {}, *t=s;
++ for (int i=0; i < 8; i++)
++ t += sprintf(t, "% 6.2f ", mix.coeff[j*8+i] * (1.0/0x10000));
++ CLog::Log(LOGINFO, "%s::%s %s", CLASSNAME, __func__, s);
++ }
++ av_freep(&input);
++ av_freep(&output);
++ m_dllSwResample.swr_free(&m_pContext);
++}
++ LOGTIME(2);
++
++ // This may be called before Application calls g_RBP.Initialise, so call it here too
++ g_RBP.Initialize();
++
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
++
++ if (!m_omx_mixer.Initialize("OMX.broadcom.audio_mixer", OMX_IndexParamAudioInit))
++ CLog::Log(LOGERROR, "%s::%s - m_omx_mixer.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(3);
++
++ OMX_INIT_STRUCTURE(m_pcm_input);
++ m_pcm_input.nPortIndex = m_omx_mixer.GetInputPort();
++ m_pcm_input.eNumData = OMX_NumericalDataSigned;
++ m_pcm_input.eEndian = OMX_EndianLittle;
++ m_pcm_input.bInterleaved = OMX_TRUE;
++ m_pcm_input.nBitPerSample = m_src_bits;
++ m_pcm_input.ePCMMode = m_src_fmt == AV_SAMPLE_FMT_FLT ? (OMX_AUDIO_PCMMODETYPE)0x8000 : OMX_AUDIO_PCMModeLinear;
++ m_pcm_input.nChannels = src_channels;
++ m_pcm_input.nSamplingRate = src_rate;
++
++ omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s - error m_omx_mixer in SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ OMX_INIT_STRUCTURE(m_pcm_output);
++ m_pcm_output.nPortIndex = m_omx_mixer.GetOutputPort();
++ m_pcm_output.eNumData = OMX_NumericalDataSigned;
++ m_pcm_output.eEndian = OMX_EndianLittle;
++ m_pcm_output.bInterleaved = OMX_TRUE;
++ m_pcm_output.nBitPerSample = m_dst_bits;
++ m_pcm_output.ePCMMode = m_dst_fmt == AV_SAMPLE_FMT_FLT ? (OMX_AUDIO_PCMMODETYPE)0x8000 : OMX_AUDIO_PCMModeLinear;
++ m_pcm_output.nChannels = dst_channels;
++ m_pcm_output.nSamplingRate = dst_rate;
++
++ omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s::%s - error m_omx_mixer out SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(4);
++
++ mix.nPortIndex = m_omx_mixer.GetInputPort();
++ omx_err = m_omx_mixer.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients8x8, &mix);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s - error setting mixer OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
++ CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ // set up the number/size of buffers for decoder input
++ OMX_PARAM_PORTDEFINITIONTYPE port_param;
++ OMX_INIT_STRUCTURE(port_param);
++ port_param.nPortIndex = m_omx_mixer.GetInputPort();
++
++ omx_err = m_omx_mixer.GetParameter(OMX_IndexParamPortDefinition, &port_param);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)1);
++ port_param.nBufferSize = BUFFERSIZE;
++
++ omx_err = m_omx_mixer.SetParameter(OMX_IndexParamPortDefinition, &port_param);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(5);
++
++ omx_err = m_omx_mixer.AllocInputBuffers();
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(6);
++
++ // set up the number/size of buffers for decoder output
++ OMX_INIT_STRUCTURE(port_param);
++ port_param.nPortIndex = m_omx_mixer.GetOutputPort();
++
++ omx_err = m_omx_mixer.GetParameter(OMX_IndexParamPortDefinition, &port_param);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)1);
++ port_param.nBufferSize = BUFFERSIZE;
++
++ omx_err = m_omx_mixer.SetParameter(OMX_IndexParamPortDefinition, &port_param);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(7);
++
++ omx_err = m_omx_mixer.AllocOutputBuffers();
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(8);
++
++ omx_err = m_omx_mixer.SetStateForComponent(OMX_StateExecuting);
++ if (omx_err != OMX_ErrorNone)
++ CLog::Log(LOGERROR, "%s:%s - m_omx_mixer OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
++
++ LOGTIME(9);
++
++ m_Initialized = true;
++
++ return true;
++}
++
++int CActiveAEResample::Resample(uint8_t **dst_buffer, int dst_samples, uint8_t **src_buffer, int src_samples, double ratio)
++{
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s samples:%d->%d (%.2f)", CLASSNAME, __func__, src_samples, dst_samples, ratio);
++ #endif
++ if (!m_Initialized)
++ return 0;
++ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
++
++ const int s_pitch = m_pcm_input.nChannels * m_src_bits >> 3;
++ const int d_pitch = m_pcm_output.nChannels * m_dst_bits >> 3;
++ int sent = 0;
++ int received = 0;
++ while (sent < src_samples)
++ {
++ OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
++ OMX_BUFFERHEADERTYPE *m_encoded_buffer = NULL;
++
++ omx_buffer = m_omx_mixer.GetInputBuffer(1000);
++ if (omx_buffer == NULL)
++ return false;
++
++ const int max_src_samples = BUFFERSIZE / s_pitch;
++ const int max_dst_samples = (long long)(BUFFERSIZE/d_pitch) * m_src_rate / (m_dst_rate + m_src_rate-1);
++ int send = std::min(std::min(max_dst_samples, max_src_samples), src_samples - sent);
++
++ omx_buffer->nOffset = 0;
++ omx_buffer->nFlags = OMX_BUFFERFLAG_EOS;
++ omx_buffer->nFilledLen = send * s_pitch;
++
++ assert(omx_buffer->nFilledLen > 0 && omx_buffer->nFilledLen <= omx_buffer->nAllocLen);
++
++ if (omx_buffer->nFilledLen)
++ {
++ memcpy(omx_buffer->pBuffer, src_buffer[0] + sent * s_pitch, omx_buffer->nFilledLen);
++ sent += send;
++ }
++
++ omx_err = m_omx_mixer.EmptyThisBuffer(omx_buffer);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s OMX_EmptyThisBuffer() failed with result(0x%x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++
++ m_encoded_buffer = m_omx_mixer.GetOutputBuffer();
++
++ if (!m_encoded_buffer)
++ {
++ CLog::Log(LOGERROR, "%s::%s no output buffer", CLASSNAME, __func__);
++ return false;
++ }
++
++ omx_err = m_omx_mixer.FillThisBuffer(m_encoded_buffer);
++ if (omx_err != OMX_ErrorNone)
++ return false;
++
++ omx_err = m_omx_mixer.WaitForOutputDone(1000);
++ if (omx_err != OMX_ErrorNone)
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_mixer.WaitForOutputDone result(0x%x)", CLASSNAME, __func__, omx_err);
++ return false;
++ }
++ assert(m_encoded_buffer->nFilledLen > 0 && m_encoded_buffer->nFilledLen <= m_encoded_buffer->nAllocLen);
++
++ if (m_omx_mixer.BadState())
++ {
++ CLog::Log(LOGERROR, "%s::%s m_omx_mixer.BadState", CLASSNAME, __func__);
++ return false;
++ }
++
++ if (m_encoded_buffer->nFilledLen)
++ {
++ memcpy(dst_buffer[0] + received * d_pitch, m_encoded_buffer->pBuffer, m_encoded_buffer->nFilledLen);
++ received += m_encoded_buffer->nFilledLen / d_pitch;
++ }
++ }
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s format:%d->%d rate:%d->%d chan:%d->%d samples %d->%d (%f) %d =%d", CLASSNAME, __func__,
++ (int)m_src_fmt, (int)m_dst_fmt, m_src_rate, m_dst_rate, m_src_channels, m_dst_channels, src_samples, dst_samples, ratio, m_Initialized, received);
++ #endif
++ return received;
++}
++
++int64_t CActiveAEResample::GetDelay(int64_t base)
++{
++ int ret = 0;
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s = %d", CLASSNAME, __func__, ret);
++ #endif
++ return ret;
++}
++
++int CActiveAEResample::GetBufferedSamples()
++{
++ int ret = 0;
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s = %d", CLASSNAME, __func__, ret);
++ #endif
++ return ret;
++}
++
++int CActiveAEResample::CalcDstSampleCount(int src_samples, int dst_rate, int src_rate)
++{
++ int ret = ((long long)src_samples * dst_rate + src_rate-1) / src_rate;
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s = %d", CLASSNAME, __func__, ret);
++ #endif
++ return ret;
++}
++
++int CActiveAEResample::GetSrcBufferSize(int samples)
++{
++ int ret = 0;
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s = %d", CLASSNAME, __func__, ret);
++ #endif
++ return ret;
++}
++
++int CActiveAEResample::GetDstBufferSize(int samples)
++{
++ int ret = CalcDstSampleCount(samples, m_dst_rate, m_src_rate);
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s = %d", CLASSNAME, __func__, ret);
++ #endif
++ return ret;
++}
++
++uint64_t CActiveAEResample::GetAVChannelLayout(CAEChannelInfo &info)
++{
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ #endif
++ uint64_t channelLayout = 0;
++ if (info.HasChannel(AE_CH_FL)) channelLayout |= AV_CH_FRONT_LEFT;
++ if (info.HasChannel(AE_CH_FR)) channelLayout |= AV_CH_FRONT_RIGHT;
++ if (info.HasChannel(AE_CH_FC)) channelLayout |= AV_CH_FRONT_CENTER;
++ if (info.HasChannel(AE_CH_LFE)) channelLayout |= AV_CH_LOW_FREQUENCY;
++ if (info.HasChannel(AE_CH_BL)) channelLayout |= AV_CH_BACK_LEFT;
++ if (info.HasChannel(AE_CH_BR)) channelLayout |= AV_CH_BACK_RIGHT;
++ if (info.HasChannel(AE_CH_FLOC)) channelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
++ if (info.HasChannel(AE_CH_FROC)) channelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
++ if (info.HasChannel(AE_CH_BC)) channelLayout |= AV_CH_BACK_CENTER;
++ if (info.HasChannel(AE_CH_SL)) channelLayout |= AV_CH_SIDE_LEFT;
++ if (info.HasChannel(AE_CH_SR)) channelLayout |= AV_CH_SIDE_RIGHT;
++ if (info.HasChannel(AE_CH_TC)) channelLayout |= AV_CH_TOP_CENTER;
++ if (info.HasChannel(AE_CH_TFL)) channelLayout |= AV_CH_TOP_FRONT_LEFT;
++ if (info.HasChannel(AE_CH_TFC)) channelLayout |= AV_CH_TOP_FRONT_CENTER;
++ if (info.HasChannel(AE_CH_TFR)) channelLayout |= AV_CH_TOP_FRONT_RIGHT;
++ if (info.HasChannel(AE_CH_TBL)) channelLayout |= AV_CH_TOP_BACK_LEFT;
++ if (info.HasChannel(AE_CH_TBC)) channelLayout |= AV_CH_TOP_BACK_CENTER;
++ if (info.HasChannel(AE_CH_TBR)) channelLayout |= AV_CH_TOP_BACK_RIGHT;
++
++ return channelLayout;
++}
++
++AVSampleFormat CActiveAEResample::GetAVSampleFormat(AEDataFormat format)
++{
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ #endif
++ if (format == AE_FMT_U8) return AV_SAMPLE_FMT_U8;
++ else if (format == AE_FMT_S16NE) return AV_SAMPLE_FMT_S16;
++ else if (format == AE_FMT_S32NE) return AV_SAMPLE_FMT_S32;
++ else if (format == AE_FMT_S24NE4) return AV_SAMPLE_FMT_S32;
++ else if (format == AE_FMT_FLOAT) return AV_SAMPLE_FMT_FLT;
++ else if (format == AE_FMT_DOUBLE) return AV_SAMPLE_FMT_DBL;
++
++ else if (format == AE_FMT_U8P) return AV_SAMPLE_FMT_U8P;
++ else if (format == AE_FMT_S16NEP) return AV_SAMPLE_FMT_S16P;
++ else if (format == AE_FMT_S32NEP) return AV_SAMPLE_FMT_S32P;
++ else if (format == AE_FMT_S24NE4P) return AV_SAMPLE_FMT_S32P;
++ else if (format == AE_FMT_FLOATP) return AV_SAMPLE_FMT_FLTP;
++ else if (format == AE_FMT_DOUBLEP) return AV_SAMPLE_FMT_DBLP;
++
++ return AV_SAMPLE_FMT_FLT;
++}
++
++AEDataFormat CActiveAEResample::GetAESampleFormat(AVSampleFormat format, int bits)
++{
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ #endif
++ if (format == AV_SAMPLE_FMT_U8) return AE_FMT_U8;
++ else if (format == AV_SAMPLE_FMT_S16) return AE_FMT_S16NE;
++ else if (format == AV_SAMPLE_FMT_S32 && bits == 32) return AE_FMT_S32NE;
++ else if (format == AV_SAMPLE_FMT_S32 && bits == 24) return AE_FMT_S24NE4;
++ else if (format == AV_SAMPLE_FMT_FLT) return AE_FMT_FLOAT;
++ else if (format == AV_SAMPLE_FMT_DBL) return AE_FMT_DOUBLE;
++
++ else if (format == AV_SAMPLE_FMT_U8P) return AE_FMT_U8P;
++ else if (format == AV_SAMPLE_FMT_S16P) return AE_FMT_S16NEP;
++ else if (format == AV_SAMPLE_FMT_S32P && bits == 32) return AE_FMT_S32NEP;
++ else if (format == AV_SAMPLE_FMT_S32P && bits == 24) return AE_FMT_S24NE4P;
++ else if (format == AV_SAMPLE_FMT_FLTP) return AE_FMT_FLOATP;
++ else if (format == AV_SAMPLE_FMT_DBLP) return AE_FMT_DOUBLEP;
++
++ CLog::Log(LOGERROR, "CActiveAEResample::GetAESampleFormat - format not supported");
++ return AE_FMT_INVALID;
++}
++
++uint64_t CActiveAEResample::GetAVChannel(enum AEChannel aechannel)
++{
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ #endif
++ switch (aechannel)
++ {
++ case AE_CH_FL: return AV_CH_FRONT_LEFT;
++ case AE_CH_FR: return AV_CH_FRONT_RIGHT;
++ case AE_CH_FC: return AV_CH_FRONT_CENTER;
++ case AE_CH_LFE: return AV_CH_LOW_FREQUENCY;
++ case AE_CH_BL: return AV_CH_BACK_LEFT;
++ case AE_CH_BR: return AV_CH_BACK_RIGHT;
++ case AE_CH_FLOC: return AV_CH_FRONT_LEFT_OF_CENTER;
++ case AE_CH_FROC: return AV_CH_FRONT_RIGHT_OF_CENTER;
++ case AE_CH_BC: return AV_CH_BACK_CENTER;
++ case AE_CH_SL: return AV_CH_SIDE_LEFT;
++ case AE_CH_SR: return AV_CH_SIDE_RIGHT;
++ case AE_CH_TC: return AV_CH_TOP_CENTER;
++ case AE_CH_TFL: return AV_CH_TOP_FRONT_LEFT;
++ case AE_CH_TFC: return AV_CH_TOP_FRONT_CENTER;
++ case AE_CH_TFR: return AV_CH_TOP_FRONT_RIGHT;
++ case AE_CH_TBL: return AV_CH_TOP_BACK_LEFT;
++ case AE_CH_TBC: return AV_CH_TOP_BACK_CENTER;
++ case AE_CH_TBR: return AV_CH_TOP_BACK_RIGHT;
++ default:
++ return 0;
++ }
++}
++
++int CActiveAEResample::GetAVChannelIndex(enum AEChannel aechannel, uint64_t layout)
++{
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ #endif
++ return m_dllAvUtil.av_get_channel_layout_channel_index(layout, GetAVChannel(aechannel));
++}
++
++#endif
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.h
+new file mode 100644
+index 0000000..8371c33
+--- /dev/null
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.h
+@@ -0,0 +1,65 @@
++#pragma once
++/*
++ * Copyright (C) 2010-2013 Team XBMC
++ * http://xbmc.org
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with XBMC; see the file COPYING. If not, see
++ * <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "linux/OMXCore.h"
++
++namespace ActiveAE
++{
++
++class CActiveAEResample
++{
++public:
++ CActiveAEResample();
++ virtual ~CActiveAEResample();
++ bool Init(uint64_t dst_chan_layout, int dst_channels, int dst_rate, AVSampleFormat dst_fmt, int dst_bits, uint64_t src_chan_layout, int src_channels, int src_rate, AVSampleFormat src_fmt, int src_bits, bool upmix, bool normalize, CAEChannelInfo *remapLayout, AEQuality quality);
++ int Resample(uint8_t **dst_buffer, int dst_samples, uint8_t **src_buffer, int src_samples, double ratio);
++ int64_t GetDelay(int64_t base);
++ int GetBufferedSamples();
++ int CalcDstSampleCount(int src_samples, int dst_rate, int src_rate);
++ int GetSrcBufferSize(int samples);
++ int GetDstBufferSize(int samples);
++ static uint64_t GetAVChannelLayout(CAEChannelInfo &info);
++// static CAEChannelInfo GetAEChannelLayout(uint64_t layout);
++ static AVSampleFormat GetAVSampleFormat(AEDataFormat format);
++ static AEDataFormat GetAESampleFormat(AVSampleFormat format, int bits);
++ static uint64_t GetAVChannel(enum AEChannel aechannel);
++ int GetAVChannelIndex(enum AEChannel aechannel, uint64_t layout);
++
++protected:
++ void DeInit();
++ DllAvUtil m_dllAvUtil;
++ DllSwResample m_dllSwResample;
++ bool m_loaded;
++ uint64_t m_src_chan_layout, m_dst_chan_layout;
++ int m_src_rate, m_dst_rate;
++ int m_src_channels, m_dst_channels;
++ AVSampleFormat m_src_fmt, m_dst_fmt;
++ int m_src_bits, m_dst_bits;
++
++ OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_input;
++ OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_output;
++ COMXCoreComponent m_omx_mixer;
++ bool m_Initialized;
++ AVSampleFormat m_last_src_fmt, m_last_dst_fmt;
++ int m_last_src_channels, m_last_dst_channels;
++};
++
++}
+diff --git a/xbmc/cores/AudioEngine/Makefile.in b/xbmc/cores/AudioEngine/Makefile.in
+index b49c3cc..2ba50f9 100644
+--- a/xbmc/cores/AudioEngine/Makefile.in
++++ b/xbmc/cores/AudioEngine/Makefile.in
+@@ -31,6 +31,7 @@ SRCS += Engines/ActiveAE/ActiveAESink.cpp
+ SRCS += Engines/ActiveAE/ActiveAEStream.cpp
+ SRCS += Engines/ActiveAE/ActiveAESound.cpp
+ SRCS += Engines/ActiveAE/ActiveAEResample.cpp
++SRCS += Engines/ActiveAE/ActiveAEResamplePi.cpp
+ SRCS += Engines/ActiveAE/ActiveAEBuffer.cpp
+
+ ifeq (@USE_ANDROID@,1)
+diff --git a/xbmc/linux/OMXCore.cpp b/xbmc/linux/OMXCore.cpp
+index 99e407a..8d3c86a 100644
+--- a/xbmc/linux/OMXCore.cpp
++++ b/xbmc/linux/OMXCore.cpp
+@@ -448,7 +448,7 @@ void COMXCoreComponent::FlushAll()
+
+ void COMXCoreComponent::FlushInput()
+ {
+- if(!m_handle)
++ if(!m_handle || m_resource_error)
+ return;
+
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+@@ -470,7 +470,7 @@ void COMXCoreComponent::FlushInput()
+
+ void COMXCoreComponent::FlushOutput()
+ {
+- if(!m_handle)
++ if(!m_handle || m_resource_error)
+ return;
+
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+--
+1.9.3
+
+
+From 07042c916d780bf4acb742e174005aa1e38f3a13 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Thu, 27 Mar 2014 00:22:05 +0000
+Subject: [PATCH 60/94] [PiResample] Work around AE not providing correct
+ src_bits
+
+---
+ .../AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+index 1d7b425..a91e208 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+@@ -97,16 +97,25 @@ bool CActiveAEResample::Init(uint64_t dst_chan_layout, int dst_channels, int dst
+ if (!m_loaded)
+ return false;
+
++ if (src_bits == 0)
++ {
++ if (src_fmt == AV_SAMPLE_FMT_U8) src_bits = 8;
++ else if (src_fmt == AV_SAMPLE_FMT_S16) src_bits = 16;
++ else if (src_fmt == AV_SAMPLE_FMT_S32) src_bits = 32;
++ else if (src_fmt == AV_SAMPLE_FMT_FLT) src_bits = 32;
++ }
++ assert(src_bits && dst_bits);
++
+ m_dst_chan_layout = dst_chan_layout;
+ m_dst_channels = dst_channels;
+ m_dst_rate = dst_rate;
+ m_dst_fmt = dst_fmt;
+- m_dst_bits = dst_bits ? dst_bits : 8;
++ m_dst_bits = dst_bits;
+ m_src_chan_layout = src_chan_layout;
+ m_src_channels = src_channels;
+ m_src_rate = src_rate;
+ m_src_fmt = src_fmt;
+- m_src_bits = src_bits ? src_bits : 8;
++ m_src_bits = src_bits;
+
+ if (m_dst_chan_layout == 0)
+ m_dst_chan_layout = m_dllAvUtil.av_get_default_channel_layout(m_dst_channels);
+--
+1.9.3
+
+
+From 16e8c9550b1afc0a4f5c050cc10c2c19ec9cea38 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Tue, 25 Mar 2014 19:43:07 +0000
+Subject: [PATCH 61/94] [ffmpeg] Speed up wtv index creation
+
+The index creation is O(N^2) with number of entries (typically thousands).
+On a Pi this can take more than 60 seconds to execute for a recording of a few hours.
+
+By replacing with an O(N) loop, this takes virtually zero time
+---
+ lib/ffmpeg/libavformat/wtvdec.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/lib/ffmpeg/libavformat/wtvdec.c b/lib/ffmpeg/libavformat/wtvdec.c
+index e423370..70898bd 100644
+--- a/lib/ffmpeg/libavformat/wtvdec.c
++++ b/lib/ffmpeg/libavformat/wtvdec.c
+@@ -980,21 +980,23 @@ static int read_header(AVFormatContext *s)
+ pb = wtvfile_open(s, root, root_size, ff_timeline_table_0_entries_Events_le16);
+ if (pb) {
+ int i;
++ AVIndexEntry *e = wtv->index_entries;
++ AVIndexEntry *e_end = wtv->index_entries + wtv->nb_index_entries - 1;
++ uint64_t last_position = 0;
+ while (1) {
+ uint64_t frame_nb = avio_rl64(pb);
+ uint64_t position = avio_rl64(pb);
++ while (frame_nb > e->size && e <= e_end) {
++ e->pos = last_position;
++ e++;
++ }
+ if (url_feof(pb))
+ break;
+- for (i = wtv->nb_index_entries - 1; i >= 0; i--) {
+- AVIndexEntry *e = wtv->index_entries + i;
+- if (frame_nb > e->size)
+- break;
+- if (position > e->pos)
+- e->pos = position;
+- }
++ last_position = position;
+ }
++ e_end->pos = last_position;
+ wtvfile_close(pb);
+- st->duration = wtv->index_entries[wtv->nb_index_entries - 1].timestamp;
++ st->duration = e_end->timestamp;
+ }
+ }
+ }
+--
+1.9.3
+
+
+From c95293a4cfd39a5296b434c330c3e6e24831bb6e Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 7 Apr 2014 18:19:32 +0100
+Subject: [PATCH 62/94] [rbp/omxplayer] When opening a stream don't try to
+ update gui so often
+
+---
+ xbmc/dialogs/GUIDialogBusy.cpp | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/xbmc/dialogs/GUIDialogBusy.cpp b/xbmc/dialogs/GUIDialogBusy.cpp
+index e9ba7d3..0fdc3c2 100644
+--- a/xbmc/dialogs/GUIDialogBusy.cpp
++++ b/xbmc/dialogs/GUIDialogBusy.cpp
+@@ -64,7 +64,11 @@ bool CGUIDialogBusy::WaitOnEvent(CEvent &event, unsigned int displaytime /* = 10
+ if (dialog)
+ {
+ dialog->Show();
++#ifdef TARGET_RASPBERRY_PI
++ while(!event.WaitMSec(100))
++#else
+ while(!event.WaitMSec(1))
++#endif
+ {
+ g_windowManager.ProcessRenderLoop(false);
+ if (allowCancel && dialog->IsCanceled())
+--
+1.9.3
+
+
+From 9420d803f17902c7d9f1e6734ab42a78c5d67572 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 7 Apr 2014 15:28:57 +0100
+Subject: [PATCH 63/94] [omxcodec] Clamp video texture at edges to avoid image
+ wrapping
+
+---
+ xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+index a57abe4..e22a153 100644
+--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+@@ -1339,6 +1339,8 @@ void CLinuxRendererGLES::RenderOpenMax(int index, int field)
+ GLint filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR;
+ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter);
++ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
++ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ g_Windowing.EnableGUIShader(SM_TEXTURE_RGBA);
+
+--
+1.9.3
+
+
+From 49c65c8d083952be840d8730f3fa40cfd5e0211c Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 7 Apr 2014 17:36:19 +0100
+Subject: [PATCH 64/94] [PiSink] Remove unneeded header and use CAEChannelInfo
+ directly
+
+---
+ .../AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp | 1 -
+ xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 14 +++++++-------
+ 2 files changed, 7 insertions(+), 8 deletions(-)
+
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+index a91e208..60c5e04 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+@@ -24,7 +24,6 @@
+
+ #include "ActiveAEResample.h"
+ #include "linux/RBP.h"
+-#include "cores/omxplayer/PCMRemap.h"
+ #include "settings/Settings.h"
+ #include "utils/log.h"
+
+diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
+index 9ce00e3..070e6eb 100644
+--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
++++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
+@@ -78,9 +78,9 @@ static void SetAudioProps(bool stream_channels, uint32_t channel_map)
+ CLog::Log(LOGDEBUG, "%s:%s hdmi_stream_channels %d hdmi_channel_map %08x", CLASSNAME, __func__, stream_channels, channel_map);
+ }
+
+-static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
++static uint32_t GetChannelMap(const CAEChannelInfo &channelLayout, bool passthrough)
+ {
+- unsigned int channels = format.m_channelLayout.Count();
++ unsigned int channels = channelLayout.Count();
+ uint32_t channel_map = 0;
+ if (passthrough)
+ return 0;
+@@ -119,12 +119,12 @@ static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
+ // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
+ // but no BR BL channels, we use the wide map in order to open only the num of channels really
+ // needed.
+- if (format.m_channelLayout.HasChannel(AE_CH_BL) && !format.m_channelLayout.HasChannel(AE_CH_SL))
++ if (channelLayout.HasChannel(AE_CH_BL) && !channelLayout.HasChannel(AE_CH_SL))
+ map = map_back;
+
+ for (unsigned int i = 0; i < channels; ++i)
+ {
+- AEChannel c = format.m_channelLayout[i];
++ AEChannel c = channelLayout[i];
+ unsigned int chan = 0;
+ if ((unsigned int)c < sizeof map_normal / sizeof *map_normal)
+ chan = map[(unsigned int)c];
+@@ -155,9 +155,9 @@ static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
+ 0xff, // 7
+ 0x13, // 7.1
+ };
+- uint8_t cea = format.m_channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
++ uint8_t cea = channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
+ if (cea == 0xff)
+- CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, format.m_channelLayout.HasChannel(AE_CH_LFE), channels);
++ CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, channelLayout.HasChannel(AE_CH_LFE), channels);
+
+ channel_map |= cea << 24;
+
+@@ -189,7 +189,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
+ format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER;
+ format.m_frameSamples = format.m_frames * channels;
+
+- SetAudioProps(m_passthrough, GetChannelMap(format, m_passthrough));
++ SetAudioProps(m_passthrough, GetChannelMap(format.m_channelLayout, m_passthrough));
+
+ m_format = format;
+ m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
+--
+1.9.3
+
+
+From 6ce35e828b39e0a8f2d81cb6fb6162edd53468a8 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 7 Apr 2014 17:37:41 +0100
+Subject: [PATCH 65/94] [omxplayer] Remove PCMRemap and handle multichannel
+ mixing like ActiveAE does
+
+---
+ tools/buildsteps/rbpi/config-xbmc-makefile | 4 +-
+ tools/depends/target/alsa-lib/Makefile | 2 +-
+ tools/depends/target/libcec/Makefile | 2 +-
+ xbmc/cores/omxplayer/Makefile.in | 1 -
+ xbmc/cores/omxplayer/OMXAudio.cpp | 395 +++++++++-----
+ xbmc/cores/omxplayer/OMXAudio.h | 9 +-
+ xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp | 39 +-
+ xbmc/cores/omxplayer/OMXAudioCodecOMX.h | 5 +-
+ xbmc/cores/omxplayer/OMXPlayerAudio.cpp | 4 +-
+ xbmc/cores/omxplayer/PCMRemap.cpp | 813 -----------------------------
+ xbmc/cores/omxplayer/PCMRemap.h | 151 ------
+ 11 files changed, 294 insertions(+), 1131 deletions(-)
+ delete mode 100644 xbmc/cores/omxplayer/PCMRemap.cpp
+ delete mode 100644 xbmc/cores/omxplayer/PCMRemap.h
+
+diff --git a/tools/buildsteps/rbpi/config-xbmc-makefile b/tools/buildsteps/rbpi/config-xbmc-makefile
+index 761a3b4..18600e2 100644
+--- a/tools/buildsteps/rbpi/config-xbmc-makefile
++++ b/tools/buildsteps/rbpi/config-xbmc-makefile
+@@ -12,10 +12,10 @@ CONFIGURE = cp -f $(CONFIG_SUB) $(CONFIG_GUESS) build-aux/ ;\
+ --disable-gl --enable-gles --enable-airplay \
+ --enable-airtunes --enable-libcec --enable-player=omxplayer \
+ --disable-sdl --disable-x11 --disable-xrandr --disable-openmax \
+- --disable-optical-drive --disable-dvdcss --disable-joystick \
++ --disable-optical-drive --enable-dvdcss --enable-openmax --disable-joystick \
+ --disable-crystalhd --disable-vtbdecoder --disable-vaapi \
+ --disable-vdpau --disable-projectm --disable-rsxs --disable-fishbmc \
+- --disable-alsa
++ --enable-alsa
+
+ all: $(SOURCE)/libxbmc.so
+
+diff --git a/tools/depends/target/alsa-lib/Makefile b/tools/depends/target/alsa-lib/Makefile
+index b03fc19..a04d933 100644
+--- a/tools/depends/target/alsa-lib/Makefile
++++ b/tools/depends/target/alsa-lib/Makefile
+@@ -19,7 +19,7 @@ CONFIGURE=cp -f $(CONFIG_SUB) $(CONFIG_GUESS) .; \
+ --with-ctl-plugins=ext \
+ --with-pcm-plugins="copy,linear,route,mulaw,alaw,adpcm,rate,plug,multi,file,null,empty,share,meter,hooks,lfloat,ladspa,asym,iec958,softvol,extplug,ioplug,mmap_emul" \
+ --disable-resmgr --enable-aload --enable-mixer --enable-pcm --disable-rawmidi --enable-hwdep --disable-seq --disable-alisp --disable-old-symbols --disable-python \
+- --with-softfloat=yes --with-libdl=yes --with-pthread=yes --with-librt=no --disable-shared \
++ --with-softfloat=yes --with-libdl=yes --with-pthread=yes --with-librt=no --enable-shared \
+
+ LIBDYLIB=$(PLATFORM)/src/.libs/$(LIBNAME).a
+
+diff --git a/tools/depends/target/libcec/Makefile b/tools/depends/target/libcec/Makefile
+index 16fec1b..d18f1c8 100644
+--- a/tools/depends/target/libcec/Makefile
++++ b/tools/depends/target/libcec/Makefile
+@@ -8,7 +8,7 @@ SOURCE=$(LIBNAME)-$(VERSION)-2
+ ARCHIVE=$(SOURCE).tar.gz
+
+ # configuration settings
+-CONFIGURE=./configure --prefix=$(PREFIX) --disable-rpi \
++CONFIGURE=./configure --prefix=$(PREFIX) --enable-rpi \
+
+ LIBDYLIB=$(PLATFORM)/src/lib/.libs/libcec.la
+
+diff --git a/xbmc/cores/omxplayer/Makefile.in b/xbmc/cores/omxplayer/Makefile.in
+index 3163282..e5cad70 100644
+--- a/xbmc/cores/omxplayer/Makefile.in
++++ b/xbmc/cores/omxplayer/Makefile.in
+@@ -7,7 +7,6 @@ SRCS += OMXAudioCodecOMX.cpp
+ SRCS += OMXPlayerAudio.cpp
+ SRCS += OMXPlayerVideo.cpp
+ SRCS += OMXImage.cpp
+-SRCS += PCMRemap.cpp
+
+ LIB = omxplayer.a
+
+diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
+index 72e42ec..d9beb68 100644
+--- a/xbmc/cores/omxplayer/OMXAudio.cpp
++++ b/xbmc/cores/omxplayer/OMXAudio.cpp
+@@ -29,6 +29,7 @@
+ #include "OMXAudio.h"
+ #include "Application.h"
+ #include "utils/log.h"
++#include "linux/RBP.h"
+
+ #define CLASSNAME "COMXAudio"
+
+@@ -40,6 +41,7 @@
+ #include "guilib/LocalizeStrings.h"
+ #include "cores/AudioEngine/Utils/AEConvert.h"
+ #include "cores/AudioEngine/AEFactory.h"
++#include "DllSwResample.h"
+
+ using namespace std;
+
+@@ -404,28 +406,147 @@ bool COMXAudio::PortSettingsChanged()
+ return true;
+ }
+
+-static unsigned count_bits(int64_t value)
++static uint64_t GetAVChannelLayout(CAEChannelInfo &info)
+ {
+- unsigned bits = 0;
+- for(;value;++bits)
+- value &= value - 1;
+- return bits;
++ #ifdef DEBUG_VERBOSE
++ CLog::Log(LOGINFO, "%s::%s", CLASSNAME, __func__);
++ #endif
++ uint64_t channelLayout = 0;
++ if (info.HasChannel(AE_CH_FL)) channelLayout |= AV_CH_FRONT_LEFT;
++ if (info.HasChannel(AE_CH_FR)) channelLayout |= AV_CH_FRONT_RIGHT;
++ if (info.HasChannel(AE_CH_FC)) channelLayout |= AV_CH_FRONT_CENTER;
++ if (info.HasChannel(AE_CH_LFE)) channelLayout |= AV_CH_LOW_FREQUENCY;
++ if (info.HasChannel(AE_CH_BL)) channelLayout |= AV_CH_BACK_LEFT;
++ if (info.HasChannel(AE_CH_BR)) channelLayout |= AV_CH_BACK_RIGHT;
++ if (info.HasChannel(AE_CH_FLOC)) channelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
++ if (info.HasChannel(AE_CH_FROC)) channelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
++ if (info.HasChannel(AE_CH_BC)) channelLayout |= AV_CH_BACK_CENTER;
++ if (info.HasChannel(AE_CH_SL)) channelLayout |= AV_CH_SIDE_LEFT;
++ if (info.HasChannel(AE_CH_SR)) channelLayout |= AV_CH_SIDE_RIGHT;
++ if (info.HasChannel(AE_CH_TC)) channelLayout |= AV_CH_TOP_CENTER;
++ if (info.HasChannel(AE_CH_TFL)) channelLayout |= AV_CH_TOP_FRONT_LEFT;
++ if (info.HasChannel(AE_CH_TFC)) channelLayout |= AV_CH_TOP_FRONT_CENTER;
++ if (info.HasChannel(AE_CH_TFR)) channelLayout |= AV_CH_TOP_FRONT_RIGHT;
++ if (info.HasChannel(AE_CH_TBL)) channelLayout |= AV_CH_TOP_BACK_LEFT;
++ if (info.HasChannel(AE_CH_TBC)) channelLayout |= AV_CH_TOP_BACK_CENTER;
++ if (info.HasChannel(AE_CH_TBR)) channelLayout |= AV_CH_TOP_BACK_RIGHT;
++
++ return channelLayout;
++}
++
++static void SetAudioProps(bool stream_channels, uint32_t channel_map)
++{
++ char command[80], response[80];
++
++ sprintf(command, "hdmi_stream_channels %d", stream_channels ? 1 : 0);
++ vc_gencmd(response, sizeof response, command);
++
++ sprintf(command, "hdmi_channel_map 0x%08x", channel_map);
++ vc_gencmd(response, sizeof response, command);
++
++ CLog::Log(LOGDEBUG, "%s:%s hdmi_stream_channels %d hdmi_channel_map %08x", CLASSNAME, __func__, stream_channels, channel_map);
++}
++
++static uint32_t GetChannelMap(const CAEChannelInfo &channelLayout, bool passthrough)
++{
++ unsigned int channels = channelLayout.Count();
++ uint32_t channel_map = 0;
++ if (passthrough)
++ return 0;
++
++ static const unsigned char map_normal[] =
++ {
++ 0, //AE_CH_RAW ,
++ 1, //AE_CH_FL
++ 2, //AE_CH_FR
++ 4, //AE_CH_FC
++ 3, //AE_CH_LFE
++ 7, //AE_CH_BL
++ 8, //AE_CH_BR
++ 1, //AE_CH_FLOC,
++ 2, //AE_CH_FROC,
++ 4, //AE_CH_BC,
++ 5, //AE_CH_SL
++ 6, //AE_CH_SR
++ };
++ static const unsigned char map_back[] =
++ {
++ 0, //AE_CH_RAW ,
++ 1, //AE_CH_FL
++ 2, //AE_CH_FR
++ 4, //AE_CH_FC
++ 3, //AE_CH_LFE
++ 5, //AE_CH_BL
++ 6, //AE_CH_BR
++ 1, //AE_CH_FLOC,
++ 2, //AE_CH_FROC,
++ 4, //AE_CH_BC,
++ 5, //AE_CH_SL
++ 6, //AE_CH_SR
++ };
++ const unsigned char *map = map_normal;
++ // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
++ // but no BR BL channels, we use the wide map in order to open only the num of channels really
++ // needed.
++ if (channelLayout.HasChannel(AE_CH_BL) && !channelLayout.HasChannel(AE_CH_SL))
++ map = map_back;
++
++ for (unsigned int i = 0; i < channels; ++i)
++ {
++ AEChannel c = channelLayout[i];
++ unsigned int chan = 0;
++ if ((unsigned int)c < sizeof map_normal / sizeof *map_normal)
++ chan = map[(unsigned int)c];
++ if (chan > 0)
++ channel_map |= (chan-1) << (3*i);
++ }
++ // These numbers are from Table 28 Audio InfoFrame Data byte 4 of CEA 861
++ // and describe the speaker layout
++ static const uint8_t cea_map[] = {
++ 0xff, // 0
++ 0xff, // 1
++ 0x00, // 2.0
++ 0x02, // 3.0
++ 0x08, // 4.0
++ 0x0a, // 5.0
++ 0xff, // 6
++ 0x12, // 7.0
++ 0xff, // 8
++ };
++ static const uint8_t cea_map_lfe[] = {
++ 0xff, // 0
++ 0xff, // 1
++ 0xff, // 2
++ 0x01, // 2.1
++ 0x03, // 3.1
++ 0x09, // 4.1
++ 0x0b, // 5.1
++ 0xff, // 7
++ 0x13, // 7.1
++ };
++ uint8_t cea = channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
++ if (cea == 0xff)
++ CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, channelLayout.HasChannel(AE_CH_LFE), channels);
++
++ channel_map |= cea << 24;
++
++ return channel_map;
+ }
+
+-bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, uint64_t channelMap, bool bUsePassthrough, bool bUseHWDecode)
++bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, CAEChannelInfo channelMap, bool bUsePassthrough, bool bUseHWDecode)
+ {
+ CSingleLock lock (m_critSection);
+ OMX_ERRORTYPE omx_err;
+
+ Deinitialize();
+
+- if(!m_dllAvUtil.Load())
++ if (!m_dllAvUtil.Load())
+ return false;
+
+ m_HWDecode = bUseHWDecode;
+ m_Passthrough = bUsePassthrough;
+
+- m_InputChannels = count_bits(channelMap);
++ m_InputChannels = channelMap.Count();
+ m_format = format;
+
+ if(m_InputChannels == 0)
+@@ -471,26 +592,133 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
+
+ if (!m_Passthrough)
+ {
+- enum PCMChannels inLayout[OMX_AUDIO_MAXCHANNELS];
+- enum PCMChannels outLayout[OMX_AUDIO_MAXCHANNELS];
+- enum PCMLayout layout = (enum PCMLayout)std::max(0, CSettings::Get().GetInt("audiooutput.channels")-1);
++ bool upmix = CSettings::Get().GetBool("audiooutput.stereoupmix");
++ bool normalize = CSettings::Get().GetBool("audiooutput.normalizelevels");
++ void *remapLayout = NULL;
++
++ CAEChannelInfo stdLayout = (enum AEStdChLayout)CSettings::Get().GetInt("audiooutput.channels");
++
+ // ignore layout setting for analogue
+ if (CSettings::Get().GetBool("audiooutput.dualaudio") || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
+- layout = PCM_LAYOUT_2_0;
++ stdLayout = AE_CH_LAYOUT_2_0;
+
+ // force out layout to stereo if input is not multichannel - it gives the receiver a chance to upmix
+- if (channelMap == (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT) || channelMap == AV_CH_FRONT_CENTER)
+- layout = PCM_LAYOUT_2_0;
+- BuildChannelMap(inLayout, channelMap);
+- m_OutputChannels = BuildChannelMapCEA(outLayout, GetChannelLayout(layout));
+- CPCMRemap m_remap;
+- m_remap.Reset();
+- /*outLayout = */m_remap.SetInputFormat (m_InputChannels, inLayout, CAEUtil::DataFormatToBits(m_format.m_dataFormat) / 8, m_format.m_sampleRate, layout);
+- m_remap.SetOutputFormat(m_OutputChannels, outLayout);
+- m_remap.GetDownmixMatrix(m_downmix_matrix);
+- m_wave_header.dwChannelMask = channelMap;
+- BuildChannelMapOMX(m_input_channels, channelMap);
+- BuildChannelMapOMX(m_output_channels, GetChannelLayout(layout));
++ if (m_InputChannels <= 2)
++ stdLayout = AE_CH_LAYOUT_2_0;
++
++ uint64_t m_dst_chan_layout = GetAVChannelLayout(stdLayout);
++ uint64_t m_src_chan_layout = GetAVChannelLayout(channelMap);
++ m_OutputChannels = stdLayout.Count();
++
++ int m_dst_channels = m_OutputChannels;
++ int m_src_channels = m_InputChannels;
++ SetAudioProps(m_Passthrough, GetChannelMap(stdLayout, m_Passthrough));
++
++ CLog::Log(LOGINFO, "%s::%s remap:%p chan:%d->%d norm:%d upmix:%d %llx:%llx", CLASSNAME, __func__, remapLayout, m_src_channels, m_dst_channels, normalize, upmix, m_src_chan_layout, m_dst_chan_layout);
++
++ // this code is just uses ffmpeg to produce the 8x8 mixing matrix
++ // dummy sample rate and format, as we only care about channel mapping
++ DllSwResample m_dllSwResample;
++ if (!m_dllSwResample.Load())
++ return false;
++
++ SwrContext *m_pContext = m_dllSwResample.swr_alloc_set_opts(NULL, m_dst_chan_layout, AV_SAMPLE_FMT_FLT, 48000,
++ m_src_chan_layout, AV_SAMPLE_FMT_FLT, 48000, 0, NULL);
++ if(!m_pContext)
++ {
++ CLog::Log(LOGERROR, "COMXAudio::Init - create context failed");
++ return false;
++ }
++ // tell resampler to clamp float values
++ // not required for sink stage (remapLayout == true)
++ if (!remapLayout && normalize)
++ {
++ av_opt_set_double(m_pContext, "rematrix_maxval", 1.0, 0);
++ }
++
++ // stereo upmix
++ if (upmix && m_src_channels == 2 && m_dst_channels > 2)
++ {
++ double m_rematrix[AE_CH_MAX][AE_CH_MAX];
++ memset(m_rematrix, 0, sizeof(m_rematrix));
++ for (int out=0; out<m_dst_channels; out++)
++ {
++ uint64_t out_chan = m_dllAvUtil.av_channel_layout_extract_channel(m_dst_chan_layout, out);
++ switch(out_chan)
++ {
++ case AV_CH_FRONT_LEFT:
++ case AV_CH_BACK_LEFT:
++ case AV_CH_SIDE_LEFT:
++ m_rematrix[out][0] = 1.0;
++ break;
++ case AV_CH_FRONT_RIGHT:
++ case AV_CH_BACK_RIGHT:
++ case AV_CH_SIDE_RIGHT:
++ m_rematrix[out][1] = 1.0;
++ break;
++ case AV_CH_FRONT_CENTER:
++ m_rematrix[out][0] = 0.5;
++ m_rematrix[out][1] = 0.5;
++ break;
++ case AV_CH_LOW_FREQUENCY:
++ m_rematrix[out][0] = 0.5;
++ m_rematrix[out][1] = 0.5;
++ break;
++ default:
++ break;
++ }
++ }
++
++ if (m_dllSwResample.swr_set_matrix(m_pContext, (const double*)m_rematrix, AE_CH_MAX) < 0)
++ {
++ CLog::Log(LOGERROR, "COMXAudio::Init - setting channel matrix failed");
++ return false;
++ }
++ }
++
++ if (m_dllSwResample.swr_init(m_pContext) < 0)
++ {
++ CLog::Log(LOGERROR, "COMXAudio::Init - init resampler failed");
++ return false;
++ }
++
++ const int samples = 8;
++ uint8_t *output, *input;
++ av_samples_alloc(&output, NULL, m_dst_channels, samples, AV_SAMPLE_FMT_FLT, 1);
++ av_samples_alloc(&input , NULL, m_src_channels, samples, AV_SAMPLE_FMT_FLT, 1);
++
++ // Produce "identity" samples
++ float *f = (float *)input;
++ for (int j=0; j < samples; j++)
++ for (int i=0; i < m_src_channels; i++)
++ *f++ = i == j ? 1.0f : 0.0f;
++
++ int ret = m_dllSwResample.swr_convert(m_pContext, &output, samples, (const uint8_t **)&input, samples);
++ if (ret < 0)
++ CLog::Log(LOGERROR, "COMXAudio::Resample - resample failed");
++
++ f = (float *)output;
++ for (int j=0; j < 8; j++)
++ {
++ for (int i=0; i < m_dst_channels; i++)
++ m_downmix_matrix[8*i+j] = *f++;
++ for (int i=m_dst_channels; i < 8; i++)
++ m_downmix_matrix[8*i+j] = 0.0f;
++ }
++
++ for (int j=0; j < 8; j++)
++ {
++ char s[128] = {}, *t=s;
++ for (int i=0; i < 8; i++)
++ t += sprintf(t, "% 6.2f ", m_downmix_matrix[j*8+i]);
++ CLog::Log(LOGINFO, "%s::%s %s", CLASSNAME, __func__, s);
++ }
++ av_freep(&input);
++ av_freep(&output);
++ m_dllSwResample.swr_free(&m_pContext);
++ m_dllSwResample.Unload();
++
++ m_wave_header.dwChannelMask = m_src_chan_layout;
+ }
+
+ m_SampleRate = m_format.m_sampleRate;
+@@ -1605,122 +1833,3 @@ void COMXAudio::CheckOutputBufferSize(void **buffer, int *oldSize, int newSize)
+ }
+ memset(*buffer, 0x0, *oldSize);
+ }
+-
+-void COMXAudio::BuildChannelMap(enum PCMChannels *channelMap, uint64_t layout)
+-{
+- int index = 0;
+- if (layout & AV_CH_FRONT_LEFT ) channelMap[index++] = PCM_FRONT_LEFT ;
+- if (layout & AV_CH_FRONT_RIGHT ) channelMap[index++] = PCM_FRONT_RIGHT ;
+- if (layout & AV_CH_FRONT_CENTER ) channelMap[index++] = PCM_FRONT_CENTER ;
+- if (layout & AV_CH_LOW_FREQUENCY ) channelMap[index++] = PCM_LOW_FREQUENCY ;
+- if (layout & AV_CH_BACK_LEFT ) channelMap[index++] = PCM_BACK_LEFT ;
+- if (layout & AV_CH_BACK_RIGHT ) channelMap[index++] = PCM_BACK_RIGHT ;
+- if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) channelMap[index++] = PCM_FRONT_LEFT_OF_CENTER ;
+- if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) channelMap[index++] = PCM_FRONT_RIGHT_OF_CENTER;
+- if (layout & AV_CH_BACK_CENTER ) channelMap[index++] = PCM_BACK_CENTER ;
+- if (layout & AV_CH_SIDE_LEFT ) channelMap[index++] = PCM_SIDE_LEFT ;
+- if (layout & AV_CH_SIDE_RIGHT ) channelMap[index++] = PCM_SIDE_RIGHT ;
+- if (layout & AV_CH_TOP_CENTER ) channelMap[index++] = PCM_TOP_CENTER ;
+- if (layout & AV_CH_TOP_FRONT_LEFT ) channelMap[index++] = PCM_TOP_FRONT_LEFT ;
+- if (layout & AV_CH_TOP_FRONT_CENTER ) channelMap[index++] = PCM_TOP_FRONT_CENTER ;
+- if (layout & AV_CH_TOP_FRONT_RIGHT ) channelMap[index++] = PCM_TOP_FRONT_RIGHT ;
+- if (layout & AV_CH_TOP_BACK_LEFT ) channelMap[index++] = PCM_TOP_BACK_LEFT ;
+- if (layout & AV_CH_TOP_BACK_CENTER ) channelMap[index++] = PCM_TOP_BACK_CENTER ;
+- if (layout & AV_CH_TOP_BACK_RIGHT ) channelMap[index++] = PCM_TOP_BACK_RIGHT ;
+- while (index<OMX_AUDIO_MAXCHANNELS)
+- channelMap[index++] = PCM_INVALID;
+-}
+-
+-// See CEA spec: Table 20, Audio InfoFrame data byte 4 for the ordering here
+-int COMXAudio::BuildChannelMapCEA(enum PCMChannels *channelMap, uint64_t layout)
+-{
+- int index = 0;
+- if (layout & AV_CH_FRONT_LEFT ) channelMap[index++] = PCM_FRONT_LEFT;
+- if (layout & AV_CH_FRONT_RIGHT ) channelMap[index++] = PCM_FRONT_RIGHT;
+- if (layout & AV_CH_LOW_FREQUENCY ) channelMap[index++] = PCM_LOW_FREQUENCY;
+- if (layout & AV_CH_FRONT_CENTER ) channelMap[index++] = PCM_FRONT_CENTER;
+- if (layout & AV_CH_BACK_LEFT ) channelMap[index++] = PCM_BACK_LEFT;
+- if (layout & AV_CH_BACK_RIGHT ) channelMap[index++] = PCM_BACK_RIGHT;
+- if (layout & AV_CH_SIDE_LEFT ) channelMap[index++] = PCM_SIDE_LEFT;
+- if (layout & AV_CH_SIDE_RIGHT ) channelMap[index++] = PCM_SIDE_RIGHT;
+-
+- while (index<OMX_AUDIO_MAXCHANNELS)
+- channelMap[index++] = PCM_INVALID;
+-
+- int num_channels = 0;
+- for (index=0; index<OMX_AUDIO_MAXCHANNELS; index++)
+- if (channelMap[index] != PCM_INVALID)
+- num_channels = index+1;
+- return num_channels;
+-}
+-
+-void COMXAudio::BuildChannelMapOMX(enum OMX_AUDIO_CHANNELTYPE * channelMap, uint64_t layout)
+-{
+- int index = 0;
+-
+- if (layout & AV_CH_FRONT_LEFT ) channelMap[index++] = OMX_AUDIO_ChannelLF;
+- if (layout & AV_CH_FRONT_RIGHT ) channelMap[index++] = OMX_AUDIO_ChannelRF;
+- if (layout & AV_CH_FRONT_CENTER ) channelMap[index++] = OMX_AUDIO_ChannelCF;
+- if (layout & AV_CH_LOW_FREQUENCY ) channelMap[index++] = OMX_AUDIO_ChannelLFE;
+- if (layout & AV_CH_BACK_LEFT ) channelMap[index++] = OMX_AUDIO_ChannelLR;
+- if (layout & AV_CH_BACK_RIGHT ) channelMap[index++] = OMX_AUDIO_ChannelRR;
+- if (layout & AV_CH_SIDE_LEFT ) channelMap[index++] = OMX_AUDIO_ChannelLS;
+- if (layout & AV_CH_SIDE_RIGHT ) channelMap[index++] = OMX_AUDIO_ChannelRS;
+- if (layout & AV_CH_BACK_CENTER ) channelMap[index++] = OMX_AUDIO_ChannelCS;
+- // following are not in openmax spec, but gpu does accept them
+- if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)10;
+- if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)11;
+- if (layout & AV_CH_TOP_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)12;
+- if (layout & AV_CH_TOP_FRONT_LEFT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)13;
+- if (layout & AV_CH_TOP_FRONT_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)14;
+- if (layout & AV_CH_TOP_FRONT_RIGHT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)15;
+- if (layout & AV_CH_TOP_BACK_LEFT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)16;
+- if (layout & AV_CH_TOP_BACK_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)17;
+- if (layout & AV_CH_TOP_BACK_RIGHT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)18;
+-
+- while (index<OMX_AUDIO_MAXCHANNELS)
+- channelMap[index++] = OMX_AUDIO_ChannelNone;
+-}
+-
+-uint64_t COMXAudio::GetChannelLayout(enum PCMLayout layout)
+-{
+- uint64_t layouts[] = {
+- /* 2.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT,
+- /* 2.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_LOW_FREQUENCY,
+- /* 3.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER,
+- /* 3.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_LOW_FREQUENCY,
+- /* 4.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT,
+- /* 4.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT | 1<<PCM_LOW_FREQUENCY,
+- /* 5.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT,
+- /* 5.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT | 1<<PCM_LOW_FREQUENCY,
+- /* 7.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_SIDE_LEFT | 1<<PCM_SIDE_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT,
+- /* 7.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_SIDE_LEFT | 1<<PCM_SIDE_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT | 1<<PCM_LOW_FREQUENCY
+- };
+- return (int)layout < 10 ? layouts[(int)layout] : 0;
+-}
+-
+-CAEChannelInfo COMXAudio::GetAEChannelLayout(uint64_t layout)
+-{
+- CAEChannelInfo m_channelLayout;
+- 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 ;
+- return m_channelLayout;
+-}
+diff --git a/xbmc/cores/omxplayer/OMXAudio.h b/xbmc/cores/omxplayer/OMXAudio.h
+index b0264d8..a1c59da 100644
+--- a/xbmc/cores/omxplayer/OMXAudio.h
++++ b/xbmc/cores/omxplayer/OMXAudio.h
+@@ -38,7 +38,6 @@
+ #include "OMXCore.h"
+ #include "DllAvCodec.h"
+ #include "DllAvUtil.h"
+-#include "PCMRemap.h"
+
+ #include "threads/CriticalSection.h"
+
+@@ -61,7 +60,7 @@ class COMXAudio
+ float GetCacheTime();
+ float GetCacheTotal();
+ COMXAudio();
+- bool Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, uint64_t channelMap, bool bUsePassthrough, bool bUseHWDecode);
++ bool Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, CAEChannelInfo channelMap, bool bUsePassthrough, bool bUseHWDecode);
+ bool PortSettingsChanged();
+ ~COMXAudio();
+
+@@ -99,12 +98,6 @@ class COMXAudio
+ unsigned int GetAudioRenderingLatency();
+ float GetMaxLevel(double &pts);
+
+- void BuildChannelMap(enum PCMChannels *channelMap, uint64_t layout);
+- int BuildChannelMapCEA(enum PCMChannels *channelMap, uint64_t layout);
+- void BuildChannelMapOMX(enum OMX_AUDIO_CHANNELTYPE *channelMap, uint64_t layout);
+- uint64_t GetChannelLayout(enum PCMLayout layout);
+- CAEChannelInfo GetAEChannelLayout(uint64_t layout);
+-
+ private:
+ IAudioCallback* m_pCallback;
+ bool m_Initialized;
+diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
+index 7f6ef6e..5e47317 100644
+--- a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
++++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp
+@@ -26,7 +26,6 @@
+
+ #include "cores/AudioEngine/Utils/AEUtil.h"
+ #include "settings/Settings.h"
+-#include "PCMRemap.h"
+
+ // the size of the audio_render output port buffers
+ #define AUDIO_DECODE_OUTPUT_BUFFER (32*1024)
+@@ -93,12 +92,11 @@ bool COMXAudioCodecOMX::Open(CDVDStreamInfo &hints)
+ m_pCodecContext->block_align = hints.blockalign;
+ m_pCodecContext->bit_rate = hints.bitrate;
+ m_pCodecContext->bits_per_coded_sample = hints.bitspersample;
+- enum PCMLayout layout = (enum PCMLayout)std::max(0, CSettings::Get().GetInt("audiooutput.channels")-1);
+ if (hints.codec == AV_CODEC_ID_TRUEHD)
+ {
+- if (layout == PCM_LAYOUT_2_0)
++ if (CSettings::Get().GetInt("audiooutput.channels") == AE_CH_LAYOUT_2_0)
+ m_pCodecContext->request_channel_layout = AV_CH_LAYOUT_STEREO;
+- else if (layout <= PCM_LAYOUT_5_1)
++ else if (CSettings::Get().GetInt("audiooutput.channels") == AE_CH_LAYOUT_5_1)
+ m_pCodecContext->request_channel_layout = AV_CH_LAYOUT_5POINT1;
+ }
+ if (m_pCodecContext->request_channel_layout)
+@@ -323,7 +321,7 @@ int COMXAudioCodecOMX::GetBitRate()
+ return m_pCodecContext->bit_rate;
+ }
+
+-static unsigned count_bits(int64_t value)
++static unsigned count_bits(uint64_t value)
+ {
+ unsigned bits = 0;
+ for(;value;++bits)
+@@ -331,9 +329,10 @@ static unsigned count_bits(int64_t value)
+ return bits;
+ }
+
+-uint64_t COMXAudioCodecOMX::GetChannelMap()
++void COMXAudioCodecOMX::BuildChannelMap()
+ {
+ uint64_t layout;
++
+ int bits = count_bits(m_pCodecContext->channel_layout);
+ if (bits == m_pCodecContext->channels)
+ layout = m_pCodecContext->channel_layout;
+@@ -342,5 +341,31 @@ uint64_t COMXAudioCodecOMX::GetChannelMap()
+ 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);
+ }
+- return layout;
++
++ 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 ;
++}
++
++CAEChannelInfo COMXAudioCodecOMX::GetChannelMap()
++{
++ BuildChannelMap();
++ return m_channelLayout;
+ }
+diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.h b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
+index 66e5b4a..6e6b226 100644
+--- a/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
++++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h
+@@ -40,7 +40,8 @@ class COMXAudioCodecOMX
+ int GetData(BYTE** dst, double &dts, double &pts);
+ void Reset();
+ int GetChannels();
+- uint64_t GetChannelMap();
++ void BuildChannelMap();
++ CAEChannelInfo GetChannelMap();
+ int GetSampleRate();
+ int GetBitsPerSample();
+ static const char* GetName() { return "FFmpeg"; }
+@@ -62,7 +63,7 @@ class COMXAudioCodecOMX
+ bool m_bOpenedCodec;
+
+ int m_channels;
+-
++ CAEChannelInfo m_channelLayout;
+ bool m_bFirstFrame;
+ bool m_bGotFrame;
+ bool m_bNoConcatenate;
+diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
+index a4c11777..d3348ec 100644
+--- a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
++++ b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
+@@ -567,13 +567,13 @@ bool OMXPlayerAudio::OpenDecoder()
+ /* GetDataFormat is setting up evrything */
+ m_format.m_dataFormat = GetDataFormat(m_hints);
+
+- uint64_t channelMap = 0;
++ CAEChannelInfo channelMap;
+ if (m_pAudioCodec && !m_passthrough)
+ channelMap = m_pAudioCodec->GetChannelMap();
+ else if (m_passthrough)
+ // we just want to get the channel count right to stop OMXAudio.cpp rejecting stream
+ // the actual layout is not used
+- channelMap = (1<<m_nChannels)-1;
++ channelMap = AE_CH_LAYOUT_5_1;
+ bool bAudioRenderOpen = m_omxAudio.Initialize(m_format, m_av_clock, m_hints, channelMap, m_passthrough, m_hw_decode);
+
+ m_codec_name = "";
+diff --git a/xbmc/cores/omxplayer/PCMRemap.cpp b/xbmc/cores/omxplayer/PCMRemap.cpp
+deleted file mode 100644
+index f8acfcc..0000000
+--- a/xbmc/cores/omxplayer/PCMRemap.cpp
++++ /dev/null
+@@ -1,813 +0,0 @@
+-/*
+- * Copyright (C) 2005-2010 Team XBMC
+- * http://xbmc.org
+- *
+- * This Program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This Program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with XBMC; see the file COPYING. If not, write to
+- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+- * http://www.gnu.org/copyleft/gpl.html
+- *
+- */
+-
+-#ifndef __STDC_LIMIT_MACROS
+-#define __STDC_LIMIT_MACROS
+-#endif
+-
+-#include <cstdlib>
+-#include <string.h>
+-#include <stdio.h>
+-#include <math.h>
+-
+-//#include "MathUtils.h"
+-#include "PCMRemap.h"
+-#include "utils/log.h"
+-#include "utils/StringUtils.h"
+-#include "settings/Settings.h"
+-#include "settings/AdvancedSettings.h"
+-#ifdef _WIN32
+-#include "../win32/PlatformDefs.h"
+-#endif
+-
+-static enum PCMChannels PCMLayoutMap[PCM_MAX_LAYOUT][PCM_MAX_CH + 1] =
+-{
+- /* 2.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_INVALID},
+- /* 2.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
+- /* 3.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_INVALID},
+- /* 3.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_INVALID},
+- /* 4.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
+- /* 4.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
+- /* 5.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
+- /* 5.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
+- /* 7.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
+- /* 7.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID}
+-};
+-
+-/*
+- map missing output into channel @ volume level
+- the order of this table is important, mix tables can not depend on channels that have not been defined yet
+- eg, FC can only be mixed into FL, FR as they are the only channels that have been defined
+-*/
+-#define PCM_MAX_MIX 3
+-static struct PCMMapInfo PCMDownmixTable[PCM_MAX_CH][PCM_MAX_MIX] =
+-{
+- /* PCM_FRONT_LEFT */
+- {
+- {PCM_INVALID}
+- },
+- /* PCM_FRONT_RIGHT */
+- {
+- {PCM_INVALID}
+- },
+- /* PCM_FRONT_CENTER */
+- {
+- {PCM_FRONT_LEFT_OF_CENTER , 1.0},
+- {PCM_FRONT_RIGHT_OF_CENTER, 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_LOW_FREQUENCY */
+- {
+- /*
+- A/52B 7.8 paragraph 2 recomends +10db
+- but due to horrible clipping when normalize
+- is disabled we set this to 1.0
+- */
+- {PCM_FRONT_LEFT , 1.0},//3.5},
+- {PCM_FRONT_RIGHT , 1.0},//3.5},
+- {PCM_INVALID}
+- },
+- /* PCM_BACK_LEFT */
+- {
+- {PCM_FRONT_LEFT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_BACK_RIGHT */
+- {
+- {PCM_FRONT_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_FRONT_LEFT_OF_CENTER */
+- {
+- {PCM_FRONT_LEFT , 1.0},
+- {PCM_FRONT_CENTER , 1.0, true},
+- {PCM_INVALID}
+- },
+- /* PCM_FRONT_RIGHT_OF_CENTER */
+- {
+- {PCM_FRONT_RIGHT , 1.0},
+- {PCM_FRONT_CENTER , 1.0, true},
+- {PCM_INVALID}
+- },
+- /* PCM_BACK_CENTER */
+- {
+- {PCM_BACK_LEFT , 1.0},
+- {PCM_BACK_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_SIDE_LEFT */
+- {
+- {PCM_FRONT_LEFT , 1.0},
+- {PCM_BACK_LEFT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_SIDE_RIGHT */
+- {
+- {PCM_FRONT_RIGHT , 1.0},
+- {PCM_BACK_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_FRONT_LEFT */
+- {
+- {PCM_FRONT_LEFT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_FRONT_RIGHT */
+- {
+- {PCM_FRONT_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_FRONT_CENTER */
+- {
+- {PCM_TOP_FRONT_LEFT , 1.0},
+- {PCM_TOP_FRONT_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_CENTER */
+- {
+- {PCM_TOP_FRONT_LEFT , 1.0},
+- {PCM_TOP_FRONT_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_BACK_LEFT */
+- {
+- {PCM_BACK_LEFT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_BACK_RIGHT */
+- {
+- {PCM_BACK_RIGHT , 1.0},
+- {PCM_INVALID}
+- },
+- /* PCM_TOP_BACK_CENTER */
+- {
+- {PCM_TOP_BACK_LEFT , 1.0},
+- {PCM_TOP_BACK_RIGHT , 1.0},
+- {PCM_INVALID}
+- }
+-};
+-
+-CPCMRemap::CPCMRemap() :
+- m_inSet (false),
+- m_outSet (false),
+- m_inChannels (0),
+- m_outChannels (0),
+- m_inSampleSize(0),
+- m_ignoreLayout(false),
+- m_buf(NULL),
+- m_bufsize(0),
+- m_attenuation (1.0),
+- m_attenuationInc(0.0),
+- m_attenuationMin(1.0),
+- m_sampleRate (48000.0), //safe default
+- m_holdCounter (0),
+- m_limiterEnabled(false)
+-{
+- Dispose();
+-}
+-
+-CPCMRemap::~CPCMRemap()
+-{
+- Dispose();
+-}
+-
+-void CPCMRemap::Dispose()
+-{
+- free(m_buf);
+- m_buf = NULL;
+- m_bufsize = 0;
+-}
+-
+-/* resolves the channels recursively and returns the new index of tablePtr */
+-struct PCMMapInfo* CPCMRemap::ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr)
+-{
+- if (channel == PCM_INVALID) return tablePtr;
+-
+- /* if its a 1 to 1 mapping, return */
+- if (m_useable[channel])
+- {
+- tablePtr->channel = channel;
+- tablePtr->level = level;
+-
+- ++tablePtr;
+- tablePtr->channel = PCM_INVALID;
+- return tablePtr;
+- } else
+- if (ifExists)
+- level /= 2;
+-
+- struct PCMMapInfo *info;
+- std::vector<enum PCMChannels>::iterator itt;
+-
+- for(info = PCMDownmixTable[channel]; info->channel != PCM_INVALID; ++info)
+- {
+- /* make sure we are not about to recurse into ourself */
+- bool found = false;
+- for(itt = path.begin(); itt != path.end(); ++itt)
+- if (*itt == info->channel)
+- {
+- found = true;
+- break;
+- }
+-
+- if (found)
+- continue;
+-
+- path.push_back(channel);
+- float l = (info->level * (level / 100)) * 100;
+- tablePtr = ResolveChannel(info->channel, l, info->ifExists, path, tablePtr);
+- path.pop_back();
+- }
+-
+- return tablePtr;
+-}
+-
+-/*
+- Builds a lookup table without extra adjustments, useful if we simply
+- want to find out which channels are active.
+- For final adjustments, BuildMap() is used.
+-*/
+-void CPCMRemap::ResolveChannels()
+-{
+- unsigned int in_ch, out_ch;
+- bool hasSide = false;
+- bool hasBack = false;
+-
+- memset(m_useable, 0, sizeof(m_useable));
+-
+- if (!m_outSet)
+- {
+- /* Output format is not known yet, assume the full configured map.
+- * Note that m_ignoreLayout-using callers normally ignore the result of
+- * this function when !m_outSet, when it is called only for an advice for
+- * the caller of SetInputFormat about the best possible output map, and
+- * they can still set their output format arbitrarily in their call to
+- * SetOutputFormat. */
+- for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
+- m_useable[*chan] = true;
+- }
+- else if (m_ignoreLayout)
+- {
+- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+- m_useable[m_outMap[out_ch]] = true;
+- }
+- else
+- {
+- /* figure out what channels we have and can use */
+- for(enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
+- {
+- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+- if (m_outMap[out_ch] == *chan)
+- {
+- m_useable[*chan] = true;
+- break;
+- }
+- }
+- }
+-
+- /* force mono audio to front left and front right */
+- if (!m_ignoreLayout && m_inChannels == 1 && m_inMap[0] == PCM_FRONT_CENTER
+- && m_useable[PCM_FRONT_LEFT] && m_useable[PCM_FRONT_RIGHT])
+- {
+- CLog::Log(LOGDEBUG, "CPCMRemap: Mapping mono audio to front left and front right");
+- m_useable[PCM_FRONT_CENTER] = false;
+- m_useable[PCM_FRONT_LEFT_OF_CENTER] = false;
+- m_useable[PCM_FRONT_RIGHT_OF_CENTER] = false;
+- }
+-
+- /* see if our input has side/back channels */
+- for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
+- switch(m_inMap[in_ch])
+- {
+- case PCM_SIDE_LEFT:
+- case PCM_SIDE_RIGHT:
+- hasSide = true;
+- break;
+-
+- case PCM_BACK_LEFT:
+- case PCM_BACK_RIGHT:
+- hasBack = true;
+- break;
+-
+- default:;
+- }
+-
+- /* if our input has side, and not back channels, and our output doesnt have side channels */
+- if (hasSide && !hasBack && (!m_useable[PCM_SIDE_LEFT] || !m_useable[PCM_SIDE_RIGHT]))
+- {
+- CLog::Log(LOGDEBUG, "CPCMRemap: Forcing side channel map to back channels");
+- for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
+- if (m_inMap[in_ch] == PCM_SIDE_LEFT ) m_inMap[in_ch] = PCM_BACK_LEFT;
+- else if (m_inMap[in_ch] == PCM_SIDE_RIGHT) m_inMap[in_ch] = PCM_BACK_RIGHT;
+- }
+-
+- /* resolve all the channels */
+- struct PCMMapInfo table[PCM_MAX_CH + 1], *info, *dst;
+- std::vector<enum PCMChannels> path;
+-
+- for (int i = 0; i < PCM_MAX_CH + 1; i++)
+- {
+- for (int j = 0; j < PCM_MAX_CH + 1; j++)
+- m_lookupMap[i][j].channel = PCM_INVALID;
+- }
+-
+- memset(m_counts, 0, sizeof(m_counts));
+- for(in_ch = 0; in_ch < m_inChannels; ++in_ch) {
+-
+- for (int i = 0; i < PCM_MAX_CH + 1; i++)
+- table[i].channel = PCM_INVALID;
+-
+- ResolveChannel(m_inMap[in_ch], 1.0f, false, path, table);
+- for(info = table; info->channel != PCM_INVALID; ++info)
+- {
+- /* find the end of the table */
+- for(dst = m_lookupMap[info->channel]; dst->channel != PCM_INVALID; ++dst);
+-
+- /* append it to the table and set its input offset */
+- dst->channel = m_inMap[in_ch];
+- dst->in_offset = in_ch * 2;
+- dst->level = info->level;
+- m_counts[dst->channel]++;
+- }
+- }
+-}
+-
+-/*
+- builds a lookup table to convert from the input mapping to the output
+- mapping, this decreases the amount of work per sample to remap it.
+-*/
+-void CPCMRemap::BuildMap()
+-{
+- struct PCMMapInfo *dst;
+- unsigned int out_ch;
+-
+- if (!m_inSet || !m_outSet) return;
+-
+- m_inStride = m_inSampleSize * m_inChannels ;
+- m_outStride = m_inSampleSize * m_outChannels;
+-
+- /* see if we need to normalize the levels */
+- bool dontnormalize = !CSettings::Get().GetBool("audiooutput.normalizelevels");
+- CLog::Log(LOGDEBUG, "CPCMRemap: Downmix normalization is %s", (dontnormalize ? "disabled" : "enabled"));
+-
+- ResolveChannels();
+-
+- /* convert the levels into RMS values */
+- float loudest = 0.0;
+- bool hasLoudest = false;
+-
+- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+- {
+- float scale = 0;
+- int count = 0;
+- for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
+- {
+- dst->copy = false;
+- dst->level = dst->level / sqrt((float)m_counts[dst->channel]);
+- scale += dst->level;
+- ++count;
+- }
+-
+- /* if there is only 1 channel to mix, and the level is 1.0, then just copy the channel */
+- dst = m_lookupMap[m_outMap[out_ch]];
+- if (count == 1 && dst->level > 0.99 && dst->level < 1.01)
+- dst->copy = true;
+-
+- /* normalize the levels if it is turned on */
+- if (!dontnormalize)
+- for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
+- {
+- dst->level /= scale;
+- /* find the loudest output level we have that is not 1-1 */
+- if (dst->level < 1.0 && loudest < dst->level)
+- {
+- loudest = dst->level;
+- hasLoudest = true;
+- }
+- }
+- }
+-
+- /* adjust the channels that are too loud */
+- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+- {
+- CStdString s = "", f;
+- for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
+- {
+- if (hasLoudest && dst->copy)
+- {
+- dst->level = loudest;
+- dst->copy = false;
+- }
+-
+- f = StringUtils::Format("%s(%f%s) ", PCMChannelStr(dst->channel).c_str(), dst->level, dst->copy ? "*" : "");
+- s += f;
+- }
+- CLog::Log(LOGDEBUG, "CPCMRemap: %s = %s\n", PCMChannelStr(m_outMap[out_ch]).c_str(), s.c_str());
+- }
+-}
+-
+-void CPCMRemap::DumpMap(CStdString info, unsigned int channels, enum PCMChannels *channelMap)
+-{
+- if (channelMap == NULL)
+- {
+- CLog::Log(LOGINFO, "CPCMRemap: %s channel map: NULL", info.c_str());
+- return;
+- }
+-
+- CStdString mapping;
+- for(unsigned int i = 0; i < channels; ++i)
+- mapping += ((i == 0) ? "" : ",") + PCMChannelStr(channelMap[i]);
+-
+- CLog::Log(LOGINFO, "CPCMRemap: %s channel map: %s\n", info.c_str(), mapping.c_str());
+-}
+-
+-void CPCMRemap::Reset()
+-{
+- m_inSet = false;
+- m_outSet = false;
+- Dispose();
+-}
+-
+-/* sets the input format, and returns the requested channel layout */
+-enum PCMChannels *CPCMRemap::SetInputFormat(unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize, unsigned int sampleRate, PCMLayout layout)
+-{
+- m_inChannels = channels;
+- m_inSampleSize = sampleSize;
+- m_sampleRate = sampleRate;
+- m_inSet = channelMap != NULL;
+- if (channelMap)
+- memcpy(m_inMap, channelMap, sizeof(enum PCMChannels) * channels);
+-
+- /* get the audio layout, and count the channels in it */
+- m_channelLayout = layout;
+- if (m_channelLayout >= PCM_MAX_LAYOUT) m_channelLayout = PCM_LAYOUT_2_0;
+-
+-
+- DumpMap("I", channels, channelMap);
+- BuildMap();
+-
+- /* now remove the empty channels from PCMLayoutMap;
+- * we don't perform upmixing so we want the minimum amount of those */
+- if (channelMap) {
+- if (!m_outSet)
+- ResolveChannels(); /* Do basic channel resolving to find out the empty channels;
+- * If m_outSet == true, this was done already by BuildMap() above */
+- int i = 0;
+- for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
+- if (m_lookupMap[*chan][0].channel != PCM_INVALID) {
+- /* something is mapped here, so add the channel */
+- m_layoutMap[i++] = *chan;
+- }
+- m_layoutMap[i] = PCM_INVALID;
+- } else
+- memcpy(m_layoutMap, PCMLayoutMap[m_channelLayout], sizeof(PCMLayoutMap[m_channelLayout]));
+-
+- m_attenuation = 1.0;
+- m_attenuationInc = 1.0;
+- m_holdCounter = 0;
+-
+- return m_layoutMap;
+-}
+-
+-/* sets the output format supported by the audio renderer */
+-void CPCMRemap::SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout/* = false */)
+-{
+- m_outChannels = channels;
+- m_outSet = channelMap != NULL;
+- m_ignoreLayout = ignoreLayout;
+- if (channelMap)
+- memcpy(m_outMap, channelMap, sizeof(enum PCMChannels) * channels);
+-
+- DumpMap("O", channels, channelMap);
+- BuildMap();
+-
+- m_attenuation = 1.0;
+- m_attenuationInc = 1.0;
+- m_holdCounter = 0;
+-}
+-
+-#if 0
+-void CPCMRemap::Remap(void *data, void *out, unsigned int samples, long drc)
+-{
+- float gain = 1.0f;
+- if (drc > 0)
+- gain = pow(10.0f, (float)drc / 2000.0f);
+-
+- Remap(data, out, samples, gain);
+-}
+-
+-/* remap the supplied data into out, which must be pre-allocated */
+-void CPCMRemap::Remap(void *data, void *out, unsigned int samples, float gain /*= 1.0f*/)
+-{
+- CheckBufferSize(samples * m_outChannels * sizeof(float));
+-
+- //set output buffer to 0
+- memset(out, 0, samples * m_outChannels * m_inSampleSize);
+-
+- //set intermediate buffer to 0
+- memset(m_buf, 0, m_bufsize);
+-
+- ProcessInput(data, out, samples, gain);
+- AddGain(m_buf, samples * m_outChannels, gain);
+- ProcessLimiter(samples, gain);
+- ProcessOutput(out, samples, gain);
+-}
+-
+-void CPCMRemap::CheckBufferSize(int size)
+-{
+- if (m_bufsize < size)
+- {
+- m_bufsize = size;
+- m_buf = (float*)realloc(m_buf, m_bufsize);
+- }
+-}
+-
+-void CPCMRemap::ProcessInput(void* data, void* out, unsigned int samples, float gain)
+-{
+- for (unsigned int ch = 0; ch < m_outChannels; ch++)
+- {
+- struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+- if (info->channel == PCM_INVALID)
+- continue;
+-
+- if (info->copy && gain == 1.0f) //do direct copy
+- {
+- uint8_t* src = (uint8_t*)data + info->in_offset;
+- uint8_t* dst = (uint8_t*)out + ch * m_inSampleSize;
+- uint8_t* dstend = dst + samples * m_outStride;
+- while (dst != dstend)
+- {
+- *(int16_t*)dst = *(int16_t*)src;
+- src += m_inStride;
+- dst += m_outStride;
+- }
+- }
+- else //needs some volume change or mixing, put into intermediate buffer
+- {
+- for(; info->channel != PCM_INVALID; info++)
+- {
+- uint8_t* src = (uint8_t*)data + info->in_offset;
+- float* dst = m_buf + ch;
+- float* dstend = dst + samples * m_outChannels;
+- while (dst != dstend)
+- {
+- *dst += (float)(*(int16_t*)src) * info->level;
+- src += m_inStride;
+- dst += m_outChannels;
+- }
+- }
+- }
+- }
+-}
+-
+-void CPCMRemap::AddGain(float* buf, unsigned int samples, float gain)
+-{
+- if (gain != 1.0f) //needs a gain change
+- {
+- float* ptr = m_buf;
+- float* end = m_buf + samples;
+- while (ptr != end)
+- *(ptr++) *= gain;
+- }
+-}
+-
+-void CPCMRemap::ProcessLimiter(unsigned int samples, float gain)
+-{
+- //check total gain for each output channel
+- float highestgain = 1.0f;
+- for (unsigned int ch = 0; ch < m_outChannels; ch++)
+- {
+- struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+- if (info->channel == PCM_INVALID)
+- continue;
+-
+- float chgain = 0.0f;
+- for(; info->channel != PCM_INVALID; info++)
+- chgain += info->level * gain;
+-
+- if (chgain > highestgain)
+- highestgain = chgain;
+- }
+-
+- m_attenuationMin = 1.0f;
+-
+- //if one of the channels can clip, enable a limiter
+- if (highestgain > 1.0001f)
+- {
+- m_attenuationMin = m_attenuation;
+-
+- if (!m_limiterEnabled)
+- {
+- CLog::Log(LOGDEBUG, "CPCMRemap:: max gain: %f, enabling limiter", highestgain);
+- m_limiterEnabled = true;
+- }
+-
+- for (unsigned int i = 0; i < samples; i++)
+- {
+- //for each collection of samples, get the highest absolute value
+- float maxAbs = 0.0f;
+- for (unsigned int outch = 0; outch < m_outChannels; outch++)
+- {
+- float absval = fabs(m_buf[i * m_outChannels + outch]) / 32768.0f;
+- if (maxAbs < absval)
+- maxAbs = absval;
+- }
+-
+- //if attenuatedAbs is higher than 1.0f, audio is clipping
+- float attenuatedAbs = maxAbs * m_attenuation;
+- if (attenuatedAbs > 1.0f)
+- {
+- //set m_attenuation so that m_attenuation * sample is the maximum output value
+- m_attenuation = 1.0f / maxAbs;
+- if (m_attenuation < m_attenuationMin)
+- m_attenuationMin = m_attenuation;
+- //value to add to m_attenuation to make it 1.0f
+- m_attenuationInc = 1.0f - m_attenuation;
+- //amount of samples to hold m_attenuation
+- m_holdCounter = MathUtils::round_int(m_sampleRate * g_advancedSettings.m_limiterHold);
+- }
+- else if (m_attenuation < 1.0f && attenuatedAbs > 0.95f)
+- {
+- //if we're attenuating and we get within 5% of clipping, hold m_attenuation
+- m_attenuationInc = 1.0f - m_attenuation;
+- m_holdCounter = MathUtils::round_int(m_sampleRate * g_advancedSettings.m_limiterHold);
+- }
+-
+- //apply attenuation
+- for (unsigned int outch = 0; outch < m_outChannels; outch++)
+- m_buf[i * m_outChannels + outch] *= m_attenuation;
+-
+- if (m_holdCounter)
+- {
+- //hold m_attenuation
+- m_holdCounter--;
+- }
+- else if (m_attenuationInc > 0.0f)
+- {
+- //move m_attenuation to 1.0 in g_advancedSettings.m_limiterRelease seconds
+- m_attenuation += m_attenuationInc / m_sampleRate / g_advancedSettings.m_limiterRelease;
+- if (m_attenuation > 1.0f)
+- {
+- m_attenuation = 1.0f;
+- m_attenuationInc = 0.0f;
+- }
+- }
+- }
+- }
+- else
+- {
+- if (m_limiterEnabled)
+- {
+- CLog::Log(LOGDEBUG, "CPCMRemap:: max gain: %f, disabling limiter", highestgain);
+- m_limiterEnabled = false;
+- }
+-
+- //reset the limiter
+- m_attenuation = 1.0f;
+- m_attenuationInc = 0.0f;
+- m_holdCounter = 0;
+- }
+-}
+-
+-void CPCMRemap::ProcessOutput(void* out, unsigned int samples, float gain)
+-{
+- //copy from intermediate buffer to output
+- for (unsigned int ch = 0; ch < m_outChannels; ch++)
+- {
+- struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+- if (info->channel == PCM_INVALID)
+- continue;
+-
+- if (!info->copy || gain != 1.0f)
+- {
+- float* src = m_buf + ch;
+- uint8_t* dst = (uint8_t*)out + ch * m_inSampleSize;
+- uint8_t* dstend = dst + samples * m_outStride;
+-
+- while(dst != dstend)
+- {
+- *(int16_t*)dst = MathUtils::round_int(std::min(std::max(*src, (float)INT16_MIN), (float)INT16_MAX));
+- src += m_outChannels;
+- dst += m_outStride;
+- }
+- }
+- }
+-}
+-
+-bool CPCMRemap::CanRemap()
+-{
+- return (m_inSet && m_outSet);
+-}
+-
+-int CPCMRema