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 CPCMRemap::InBytesToFrames(int bytes)
-{
-  return bytes / m_inSampleSize / m_inChannels;
-}
-
-int CPCMRemap::FramesToOutBytes(int frames)
-{
-  return frames * m_inSampleSize * m_outChannels;
-}
-
-int CPCMRemap::FramesToInBytes(int frames)
-{
-  return frames * m_inSampleSize * m_inChannels;
-}
-#endif
-CStdString CPCMRemap::PCMChannelStr(enum PCMChannels ename)
-{
-  const char* PCMChannelName[] =
-  {
-    "FL",
-    "FR",
-    "CE",
-    "LFE",
-    "BL",
-    "BR",
-    "FLOC",
-    "FROC",
-    "BC",
-    "SL",
-    "SR",
-    "TFL",
-    "TFR",
-    "TFC",
-    "TC",
-    "TBL",
-    "TBR",
-    "TBC"
-  };
-
-  int namepos = (int)ename;
-  CStdString namestr;
-
-  if (namepos < 0 || namepos >= (int)(sizeof(PCMChannelName) / sizeof(const char*)))
-    namestr = StringUtils::Format("UNKNOWN CHANNEL:%i", namepos);
-  else
-    namestr = PCMChannelName[namepos];
-
-  return namestr;
-}
-#if 0
-CStdString CPCMRemap::PCMLayoutStr(enum PCMLayout ename)
-{
-  const char* PCMLayoutName[] =
-  {
-    "2.0",
-    "2.1",
-    "3.0",
-    "3.1",
-    "4.0",
-    "4.1",
-    "5.0",
-    "5.1",
-    "7.0",
-    "7.1"
-  };
-
-  int namepos = (int)ename;
-  CStdString namestr;
-
-  if (namepos < 0 || namepos >= (int)(sizeof(PCMLayoutName) / sizeof(const char*)))
-    namestr.Format("UNKNOWN LAYOUT:%i", namepos);
-  else
-    namestr = PCMLayoutName[namepos];
-
-  return namestr;
-}
-#endif
-
-
-void CPCMRemap::GetDownmixMatrix(float *downmix)
-{
-  for (int i=0; i<8*8; i++)
-    downmix[i] = 0.0f;
-
-  for (unsigned int ch = 0; ch < m_outChannels; ch++)
-  {
-    struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
-    if (info->channel == PCM_INVALID)
-      continue;
-
-    for(; info->channel != PCM_INVALID; info++)
-      downmix[8*ch + (info->in_offset>>1)] = info->level;
-  }
-}
diff --git a/xbmc/cores/omxplayer/PCMRemap.h b/xbmc/cores/omxplayer/PCMRemap.h
deleted file mode 100644
index a273cd1..0000000
--- a/xbmc/cores/omxplayer/PCMRemap.h
+++ /dev/null
@@ -1,151 +0,0 @@
-#ifndef __PCM_REMAP__H__
-#define __PCM_REMAP__H__
-
-/*
- *      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
- *
- */
-
-#include <stdint.h>
-#include <vector>
-#include "utils/StdString.h"
-
-#define PCM_MAX_CH 18
-enum PCMChannels
-{
-  PCM_INVALID = -1,
-  PCM_FRONT_LEFT,
-  PCM_FRONT_RIGHT,
-  PCM_FRONT_CENTER,
-  PCM_LOW_FREQUENCY,
-  PCM_BACK_LEFT,
-  PCM_BACK_RIGHT,
-  PCM_FRONT_LEFT_OF_CENTER,
-  PCM_FRONT_RIGHT_OF_CENTER,
-  PCM_BACK_CENTER,
-  PCM_SIDE_LEFT,
-  PCM_SIDE_RIGHT,
-  PCM_TOP_FRONT_LEFT,
-  PCM_TOP_FRONT_RIGHT,
-  PCM_TOP_FRONT_CENTER,
-  PCM_TOP_CENTER,
-  PCM_TOP_BACK_LEFT,
-  PCM_TOP_BACK_RIGHT,
-  PCM_TOP_BACK_CENTER
-};
-
-#define PCM_MAX_LAYOUT 10
-enum PCMLayout
-{
-  PCM_LAYOUT_2_0 = 0,
-  PCM_LAYOUT_2_1,
-  PCM_LAYOUT_3_0,
-  PCM_LAYOUT_3_1,
-  PCM_LAYOUT_4_0,
-  PCM_LAYOUT_4_1,
-  PCM_LAYOUT_5_0,
-  PCM_LAYOUT_5_1,
-  PCM_LAYOUT_7_0,
-  PCM_LAYOUT_7_1
-};
-
-struct PCMMapInfo
-{
-  enum  PCMChannels channel;
-  float level;
-  bool  ifExists;
-  int   in_offset;
-  bool  copy;
-};
-
-//!  Channels remapper class
-/*!
-   The usual set-up process:
-   - user calls SetInputFormat with the input channels information
-   - SetInputFormat responds with a channelmap corresponding to the speaker
-     layout that the user has configured, with empty (according to information
-     calculated from the input channelmap) channels removed
-   - user uses this information to create the desired output channelmap,
-     and calls SetOutputFormat to set it (if the channelmap contains channels
-     that do not exist in the configured speaker layout, they will contain
-     only silence unless ignoreLayout is true)
- */
-
-class CPCMRemap
-{
-protected:
-  bool               m_inSet, m_outSet;
-  enum PCMLayout     m_channelLayout;
-  unsigned int       m_inChannels, m_outChannels;
-  unsigned int       m_inSampleSize;
-  enum PCMChannels   m_inMap [PCM_MAX_CH];
-  enum PCMChannels   m_outMap[PCM_MAX_CH];
-  enum PCMChannels   m_layoutMap[PCM_MAX_CH + 1];
-
-  bool               m_ignoreLayout;
-  bool               m_useable  [PCM_MAX_CH];
-  int                m_inStride, m_outStride;
-  struct PCMMapInfo  m_lookupMap[PCM_MAX_CH + 1][PCM_MAX_CH + 1];
-  int                m_counts[PCM_MAX_CH];
-
-  float*             m_buf;
-  int                m_bufsize;
-  float              m_attenuation;
-  float              m_attenuationInc;
-  float              m_attenuationMin; //lowest attenuation value during a call of Remap(), used for the codec info
-  float              m_sampleRate;
-  unsigned int       m_holdCounter;
-  bool               m_limiterEnabled;
-  bool               m_dontnormalize;
-
-  struct PCMMapInfo* ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr);
-  void               ResolveChannels(); //!< Partial BuildMap(), just enough to see which output channels are active
-  void               BuildMap();
-  void               DumpMap(CStdString info, int unsigned channels, enum PCMChannels *channelMap);
-  void               Dispose();
-  CStdString         PCMChannelStr(enum PCMChannels ename);
-  CStdString         PCMLayoutStr(enum PCMLayout ename);
-
-  void               CheckBufferSize(int size);
-  void               ProcessInput(void* data, void* out, unsigned int samples, float gain);
-  void               AddGain(float* buf, unsigned int samples, float gain);
-  void               ProcessLimiter(unsigned int samples, float gain);
-  void               ProcessOutput(void* out, unsigned int samples, float gain);
-
-public:
-
-  CPCMRemap();
-  ~CPCMRemap();
-
-  void Reset();
-  enum PCMChannels *SetInputFormat (unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize, unsigned int sampleRate, PCMLayout layout);
-  void SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout = false);
-#if 0
-  void Remap(void *data, void *out, unsigned int samples, long drc);
-  void Remap(void *data, void *out, unsigned int samples, float gain = 1.0f);
-  bool CanRemap();
-  int  InBytesToFrames (int bytes );
-  int  FramesToOutBytes(int frames);
-  int  FramesToInBytes (int frames);
-#endif
-  float GetCurrentAttenuation() { return m_attenuationMin; }
-  void               GetDownmixMatrix(float *downmix);
-};
-
-#endif
-- 
1.9.3


From f642e8eac4fb16039ce662a8a170908efd43ebf8 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 10 Apr 2014 17:19:18 +0100
Subject: [PATCH 66/94] [omxplayer] Remove unused framerate functions

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp |  4 +---
 xbmc/linux/OMXClock.cpp                 | 35 ---------------------------------
 xbmc/linux/OMXClock.h                   |  4 ----
 3 files changed, 1 insertion(+), 42 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index a5620da..019f4b2 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -527,7 +527,7 @@ bool OMXPlayerVideo::OpenDecoder()
     return false;
 
   if (m_hints.fpsrate && m_hints.fpsscale)
-    m_fFrameRate = DVD_TIME_BASE / OMXClock::NormalizeFrameduration((double)DVD_TIME_BASE * m_hints.fpsscale / m_hints.fpsrate);
+    m_fFrameRate = DVD_TIME_BASE / CDVDCodecUtils::NormalizeFrameduration((double)DVD_TIME_BASE * m_hints.fpsscale / m_hints.fpsrate);
   else
     m_fFrameRate = 25;
 
@@ -564,8 +564,6 @@ bool OMXPlayerVideo::OpenDecoder()
     sprintf(command, "hdmi_ntsc_freqs %d", bNtscFreq);
     CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder fps: %f %s\n", m_fFrameRate, command);
     m_DllBcmHost.vc_gencmd(response, sizeof response, command);
-
-    m_av_clock->SetRefreshRate(m_fFrameRate);
   }
 
   // start from assuming all recent frames had valid pts
diff --git a/xbmc/linux/OMXClock.cpp b/xbmc/linux/OMXClock.cpp
index bee7bac..c631ab6 100644
--- a/xbmc/linux/OMXClock.cpp
+++ b/xbmc/linux/OMXClock.cpp
@@ -39,7 +39,6 @@ OMXClock::OMXClock()
 {
   m_pause       = false;
 
-  m_fps = 25.0f;
   m_omx_speed = DVD_PLAYSPEED_NORMAL;
   m_WaitMask = 0;
   m_eState = OMX_TIME_ClockStateStopped;
@@ -565,38 +564,4 @@ int64_t OMXClock::CurrentHostFrequency(void)
   return( (int64_t)1000000000L );
 }
 
-int OMXClock::GetRefreshRate(double* interval)
-{
-  if(!interval)
-    return false;
-
-  *interval = m_fps;
-  return true;
-}
-
-double OMXClock::NormalizeFrameduration(double frameduration)
-{
-  //if the duration is within 20 microseconds of a common duration, use that
-  const double durations[] = {DVD_TIME_BASE * 1.001 / 24.0, DVD_TIME_BASE / 24.0, DVD_TIME_BASE / 25.0,
-                              DVD_TIME_BASE * 1.001 / 30.0, DVD_TIME_BASE / 30.0, DVD_TIME_BASE / 50.0,
-                              DVD_TIME_BASE * 1.001 / 60.0, DVD_TIME_BASE / 60.0};
-
-  double lowestdiff = DVD_TIME_BASE;
-  int    selected   = -1;
-  for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); i++)
-  {
-    double diff = fabs(frameduration - durations[i]);
-    if (diff < DVD_MSEC_TO_TIME(0.02) && diff < lowestdiff)
-    {
-      selected = i;
-      lowestdiff = diff;
-    }
-  }
-
-  if (selected != -1)
-    return durations[selected];
-  else
-    return frameduration;
-}
-
 #endif
diff --git a/xbmc/linux/OMXClock.h b/xbmc/linux/OMXClock.h
index f83074a..7bb6d4d 100644
--- a/xbmc/linux/OMXClock.h
+++ b/xbmc/linux/OMXClock.h
@@ -50,7 +50,6 @@ class OMXClock
 protected:
   bool              m_pause;
   pthread_mutex_t   m_lock;
-  double            m_fps;
   int               m_omx_speed;
   OMX_U32           m_WaitMask;
   OMX_TIME_CLOCKSTATE   m_eState;
@@ -91,9 +90,6 @@ class OMXClock
   static int64_t CurrentHostCounter(void);
   static int64_t CurrentHostFrequency(void);
 
-  int     GetRefreshRate(double* interval = NULL);
-  void    SetRefreshRate(double fps) { m_fps = fps; };
-
   static double NormalizeFrameduration(double frameduration);
 };
 
-- 
1.9.3


From 652d09aff0c0ed4cab08ab7ebbe0da37118c53ba Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 30 Mar 2014 15:54:34 +0100
Subject: [PATCH 67/94] [omxplayer] Make the sharpness control act as a
 sharpness control.

This fixes scaling kernel as Mitchell Netravali, and varies sharpness over range B=[5/3,0] C=[-1/3,1/2]
---
 xbmc/cores/omxplayer/OMXPlayer.cpp | 338 +++++++++++++++++++++++++++++++++++++
 1 file changed, 338 insertions(+)

diff --git a/xbmc/cores/omxplayer/OMXPlayer.cpp b/xbmc/cores/omxplayer/OMXPlayer.cpp
index 2515fa1..b09e8e0 100644
--- a/xbmc/cores/omxplayer/OMXPlayer.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayer.cpp
@@ -1051,6 +1051,334 @@ bool COMXPlayer::IsBetterStream(COMXCurrentStream& current, CDemuxStream* stream
   return false;
 }
 
+static void SetSharpness(float sharpness)
+{
+  const int16_t mitchells[][32] =
+  {
+      { // B=1.66667 C=-0.33333
+         0,  3,  8, 15, 24, 35, 49, 55, 70, 92,100,107,109,113,113,114,114,113,113,109,107,100, 92, 70, 55, 49, 35, 24, 15,  8,  3,  0,
+      },
+      { // B=1.64000 C=-0.32000
+         0,  3,  7, 14, 24, 34, 48, 54, 69, 91,100,107,111,114,116,116,116,116,114,111,107,100, 91, 69, 54, 48, 34, 24, 14,  7,  3,  0,
+      },
+      { // B=1.61333 C=-0.30667
+         0,  3,  7, 14, 23, 34, 47, 53, 68, 90, 99,107,112,115,118,118,118,118,115,112,107, 99, 90, 68, 53, 47, 34, 23, 14,  7,  3,  0,
+      },
+      { // B=1.58667 C=-0.29333
+         0,  2,  7, 14, 22, 33, 46, 52, 67, 89, 99,107,113,117,119,121,121,119,117,113,107, 99, 89, 67, 52, 46, 33, 22, 14,  7,  2,  0,
+      },
+      { // B=1.56000 C=-0.28000
+         0,  2,  7, 13, 22, 32, 45, 51, 66, 88, 98,107,114,119,121,123,123,121,119,114,107, 98, 88, 66, 51, 45, 32, 22, 13,  7,  2,  0,
+      },
+      { // B=1.53333 C=-0.26667
+         0,  2,  7, 12, 21, 31, 44, 50, 65, 87, 98,108,114,120,123,125,125,123,120,114,108, 98, 87, 65, 50, 44, 31, 21, 12,  7,  2,  0,
+      },
+      { // B=1.50667 C=-0.25333
+         0,  2,  6, 12, 20, 30, 43, 49, 64, 86, 98,108,116,122,125,127,127,125,122,116,108, 98, 86, 64, 49, 43, 30, 20, 12,  6,  2,  0,
+      },
+      { // B=1.48000 C=-0.24000
+         0,  2,  6, 12, 19, 29, 42, 47, 63, 85, 98,108,117,123,127,130,130,127,123,117,108, 98, 85, 63, 47, 42, 29, 19, 12,  6,  2,  0,
+      },
+      { // B=1.45333 C=-0.22667
+         0,  2,  6, 11, 19, 28, 41, 46, 62, 85, 97,108,118,125,129,132,132,129,125,118,108, 97, 85, 62, 46, 41, 28, 19, 11,  6,  2,  0,
+      },
+      { // B=1.42667 C=-0.21333
+         0,  2,  5, 11, 18, 28, 40, 45, 61, 84, 97,108,119,126,131,134,134,131,126,119,108, 97, 84, 61, 45, 40, 28, 18, 11,  5,  2,  0,
+      },
+      { // B=1.40000 C=-0.20000
+         0,  2,  5, 10, 18, 27, 39, 44, 60, 84, 96,109,119,127,134,136,136,134,127,119,109, 96, 84, 60, 44, 39, 27, 18, 10,  5,  2,  0,
+      },
+      { // B=1.37333 C=-0.18667
+         0,  1,  5, 10, 17, 26, 38, 43, 58, 82, 96,109,120,129,135,139,139,135,129,120,109, 96, 82, 58, 43, 38, 26, 17, 10,  5,  1,  0,
+      },
+      { // B=1.34667 C=-0.17333
+         0,  2,  4, 10, 16, 25, 37, 42, 57, 81, 96,109,121,131,137,141,141,137,131,121,109, 96, 81, 57, 42, 37, 25, 16, 10,  4,  2,  0,
+      },
+      { // B=1.32000 C=-0.16000
+         0,  1,  4,  9, 15, 24, 36, 41, 56, 81, 95,110,122,132,139,143,143,139,132,122,110, 95, 81, 56, 41, 36, 24, 15,  9,  4,  1,  0,
+      },
+      { // B=1.29333 C=-0.14667
+         0,  1,  4,  8, 15, 23, 35, 40, 55, 80, 95,110,123,133,141,146,146,141,133,123,110, 95, 80, 55, 40, 35, 23, 15,  8,  4,  1,  0,
+      },
+      { // B=1.26667 C=-0.13333
+         0,  1,  4,  8, 14, 22, 33, 38, 54, 79, 95,110,124,135,143,148,148,143,135,124,110, 95, 79, 54, 38, 33, 22, 14,  8,  4,  1,  0,
+      },
+      { // B=1.24000 C=-0.12000
+         0,  1,  4,  7, 14, 21, 33, 37, 53, 78, 94,110,125,136,145,150,150,145,136,125,110, 94, 78, 53, 37, 33, 21, 14,  7,  4,  1,  0,
+      },
+      { // B=1.21333 C=-0.10667
+         0,  1,  3,  7, 13, 20, 32, 36, 52, 77, 94,110,127,138,147,152,152,147,138,127,110, 94, 77, 52, 36, 32, 20, 13,  7,  3,  1,  0,
+      },
+      { // B=1.18667 C=-0.09333
+         0,  1,  3,  7, 12, 20, 30, 35, 51, 77, 93,111,125,140,149,155,155,149,140,125,111, 93, 77, 51, 35, 30, 20, 12,  7,  3,  1,  0,
+      },
+      { // B=1.16000 C=-0.08000
+         0,  1,  3,  6, 11, 19, 29, 34, 50, 76, 93,111,128,141,151,157,157,151,141,128,111, 93, 76, 50, 34, 29, 19, 11,  6,  3,  1,  0,
+      },
+      { // B=1.13333 C=-0.06667
+         0,  1,  3,  5, 11, 18, 28, 33, 49, 75, 93,111,129,143,153,159,159,153,143,129,111, 93, 75, 49, 33, 28, 18, 11,  5,  3,  1,  0,
+      },
+      { // B=1.10667 C=-0.05333
+         0,  1,  2,  5, 10, 17, 27, 32, 48, 74, 93,111,130,144,155,161,161,155,144,130,111, 93, 74, 48, 32, 27, 17, 10,  5,  2,  1,  0,
+      },
+      { // B=1.08000 C=-0.04000
+         0,  1,  2,  5,  9, 16, 26, 31, 46, 73, 92,112,130,145,157,164,164,157,145,130,112, 92, 73, 46, 31, 26, 16,  9,  5,  2,  1,  0,
+      },
+      { // B=1.05333 C=-0.02667
+         0,  0,  2,  4,  9, 15, 25, 29, 45, 72, 92,112,131,147,159,166,166,159,147,131,112, 92, 72, 45, 29, 25, 15,  9,  4,  2,  0,  0,
+      },
+      { // B=1.02667 C=-0.01333
+         0,  0,  1,  4,  8, 14, 24, 28, 44, 72, 92,112,132,148,161,168,168,161,148,132,112, 92, 72, 44, 28, 24, 14,  8,  4,  1,  0,  0,
+      },
+      { // B=1.00000 C=0.00000
+         0,  0,  1,  4,  7, 14, 23, 27, 43, 71, 91,112,133,150,163,170,170,163,150,133,112, 91, 71, 43, 27, 23, 14,  7,  4,  1,  0,  0,
+      },
+      { // B=0.97333 C=0.01333
+         0,  0,  1,  3,  7, 12, 22, 26, 42, 70, 91,113,133,152,165,173,173,165,152,133,113, 91, 70, 42, 26, 22, 12,  7,  3,  1,  0,  0,
+      },
+      { // B=0.94667 C=0.02667
+         0,  0,  1,  2,  6, 12, 21, 25, 41, 69, 90,113,135,153,167,175,175,167,153,135,113, 90, 69, 41, 25, 21, 12,  6,  2,  1,  0,  0,
+      },
+      { // B=0.92000 C=0.04000
+         0,  0,  0,  2,  5, 11, 20, 24, 40, 68, 90,113,136,154,169,177,177,169,154,136,113, 90, 68, 40, 24, 20, 11,  5,  2,  0,  0,  0,
+      },
+      { // B=0.89333 C=0.05333
+         0,  0,  0,  1,  5, 10, 19, 23, 39, 67, 90,114,136,156,171,179,179,171,156,136,114, 90, 67, 39, 23, 19, 10,  5,  1,  0,  0,  0,
+      },
+      { // B=0.86667 C=0.06667
+         0,  0,  0,  1,  4,  9, 18, 22, 38, 66, 89,114,137,157,173,182,182,173,157,137,114, 89, 66, 38, 22, 18,  9,  4,  1,  0,  0,  0,
+      },
+      { // B=0.84000 C=0.08000
+         0,  0, -1,  1,  3,  9, 17, 21, 37, 65, 89,114,138,159,175,184,184,175,159,138,114, 89, 65, 37, 21, 17,  9,  3,  1, -1,  0,  0,
+      },
+      { // B=0.81333 C=0.09333
+         0,  0, -1,  0,  3,  7, 16, 19, 36, 65, 89,114,139,160,177,186,186,177,160,139,114, 89, 65, 36, 19, 16,  7,  3,  0, -1,  0,  0,
+      },
+      { // B=0.78667 C=0.10667
+         0, -1, -1,  0,  2,  6, 15, 18, 35, 64, 88,115,139,162,179,188,188,179,162,139,115, 88, 64, 35, 18, 15,  6,  2,  0, -1, -1,  0,
+      },
+      { // B=0.76000 C=0.12000
+         0, -1, -1, -1,  1,  6, 14, 17, 33, 63, 88,115,141,163,181,191,191,181,163,141,115, 88, 63, 33, 17, 14,  6,  1, -1, -1, -1,  0,
+      },
+      { // B=0.73333 C=0.13333
+         0, -1, -1, -1,  0,  5, 13, 16, 32, 62, 87,115,142,165,183,193,193,183,165,142,115, 87, 62, 32, 16, 13,  5,  0, -1, -1, -1,  0,
+      },
+      { // B=0.70667 C=0.14667
+         0, -1, -1, -2,  0,  4, 12, 15, 31, 61, 87,115,143,166,185,195,195,185,166,143,115, 87, 61, 31, 15, 12,  4,  0, -2, -1, -1,  0,
+      },
+      { // B=0.68000 C=0.16000
+         0, -1, -2, -2, -1,  3, 11, 14, 30, 61, 87,116,142,168,187,197,197,187,168,142,116, 87, 61, 30, 14, 11,  3, -1, -2, -2, -1,  0,
+      },
+      { // B=0.65333 C=0.17333
+         0, -1, -2, -3, -1,  2, 10, 13, 29, 60, 86,116,144,169,189,200,200,189,169,144,116, 86, 60, 29, 13, 10,  2, -1, -3, -2, -1,  0,
+      },
+      { // B=0.62667 C=0.18667
+         0, -1, -3, -3, -2,  1,  9, 12, 28, 59, 86,116,145,171,191,202,202,191,171,145,116, 86, 59, 28, 12,  9,  1, -2, -3, -3, -1,  0,
+      },
+      { // B=0.60000 C=0.20000
+         0, -1, -3, -3, -3,  0,  8, 10, 27, 58, 86,116,146,172,193,204,204,193,172,146,116, 86, 58, 27, 10,  8,  0, -3, -3, -3, -1,  0,
+      },
+      { // B=0.57333 C=0.21333
+         0, -1, -3, -4, -3, -1,  7,  9, 26, 57, 86,116,147,174,194,207,207,194,174,147,116, 86, 57, 26,  9,  7, -1, -3, -4, -3, -1,  0,
+      },
+      { // B=0.54667 C=0.22667
+         0, -2, -3, -5, -4, -1,  5,  8, 25, 57, 85,117,148,176,196,209,209,196,176,148,117, 85, 57, 25,  8,  5, -1, -4, -5, -3, -2,  0,
+      },
+      { // B=0.52000 C=0.24000
+         0, -1, -4, -5, -5, -2,  4,  7, 24, 55, 85,117,149,177,199,211,211,199,177,149,117, 85, 55, 24,  7,  4, -2, -5, -5, -4, -1,  0,
+      },
+      { // B=0.49333 C=0.25333
+         0, -2, -4, -5, -6, -3,  3,  6, 23, 55, 84,117,150,178,200,214,214,200,178,150,117, 84, 55, 23,  6,  3, -3, -6, -5, -4, -2,  0,
+      },
+      { // B=0.46667 C=0.26667
+         0, -2, -4, -6, -6, -4,  2,  6, 22, 54, 84,118,150,180,202,216,216,202,180,150,118, 84, 54, 22,  6,  2, -4, -6, -6, -4, -2,  0,
+      },
+      { // B=0.44000 C=0.28000
+         0, -2, -4, -6, -7, -5,  2,  5, 21, 53, 83,118,150,181,205,218,218,205,181,150,118, 83, 53, 21,  5,  2, -5, -7, -6, -4, -2,  0,
+      },
+      { // B=0.41333 C=0.29333
+         0, -2, -4, -7, -7, -6,  0,  5, 20, 53, 83,118,152,183,207,220,220,207,183,152,118, 83, 53, 20,  5,  0, -6, -7, -7, -4, -2,  0,
+      },
+      { // B=0.38667 C=0.30667
+         0, -2, -5, -7, -8, -7, -1,  4, 19, 52, 83,118,153,185,208,223,223,208,185,153,118, 83, 52, 19,  4, -1, -7, -8, -7, -5, -2,  0,
+      },
+      { // B=0.36000 C=0.32000
+         0, -2, -5, -8, -8, -8, -2,  3, 19, 51, 83,118,155,186,210,225,225,210,186,155,118, 83, 51, 19,  3, -2, -8, -8, -8, -5, -2,  0,
+      },
+      { // B=0.33333 C=0.33333
+         0, -2, -6, -8,-10, -8, -3,  2, 18, 50, 82,119,155,187,213,227,227,213,187,155,119, 82, 50, 18,  2, -3, -8,-10, -8, -6, -2,  0,
+      },
+      { // B=0.32667 C=0.33667
+         0, -2, -6, -8,-10, -8, -3,  2, 18, 49, 82,119,155,188,213,228,228,213,188,155,119, 82, 49, 18,  2, -3, -8,-10, -8, -6, -2,  0,
+      },
+      { // B=0.32000 C=0.34000
+         0, -2, -6, -8,-10, -9, -3,  2, 18, 49, 82,119,155,188,214,228,228,214,188,155,119, 82, 49, 18,  2, -3, -9,-10, -8, -6, -2,  0,
+      },
+      { // B=0.31333 C=0.34333
+         0, -2, -6, -8,-10, -9, -4,  1, 18, 49, 82,119,155,188,214,229,229,214,188,155,119, 82, 49, 18,  1, -4, -9,-10, -8, -6, -2,  0,
+      },
+      { // B=0.30667 C=0.34667
+         0, -2, -6, -9,-10, -9, -4,  1, 18, 49, 82,119,156,189,214,229,229,214,189,156,119, 82, 49, 18,  1, -4, -9,-10, -9, -6, -2,  0,
+      },
+      { // B=0.30000 C=0.35000
+         0, -3, -5, -9,-10,-10, -4,  1, 18, 49, 82,119,156,189,215,230,230,215,189,156,119, 82, 49, 18,  1, -4,-10,-10, -9, -5, -3,  0,
+      },
+      { // B=0.29333 C=0.35333
+         0, -2, -6, -9,-10,-10, -4,  1, 17, 48, 82,119,156,190,215,231,231,215,190,156,119, 82, 48, 17,  1, -4,-10,-10, -9, -6, -2,  0,
+      },
+      { // B=0.28667 C=0.35667
+         0, -2, -6, -9,-11,-10, -5,  1, 17, 48, 82,119,157,190,216,231,231,216,190,157,119, 82, 48, 17,  1, -5,-10,-11, -9, -6, -2,  0,
+      },
+      { // B=0.28000 C=0.36000
+         0, -3, -6, -9,-11,-10, -5,  0, 17, 48, 82,119,157,190,217,231,231,217,190,157,119, 82, 48, 17,  0, -5,-10,-11, -9, -6, -3,  0,
+      },
+      { // B=0.27333 C=0.36333
+         0, -3, -6, -9,-11,-11, -5,  0, 17, 48, 82,119,157,191,217,232,232,217,191,157,119, 82, 48, 17,  0, -5,-11,-11, -9, -6, -3,  0,
+      },
+      { // B=0.26667 C=0.36667
+         0, -3, -6, -9,-11,-11, -5,  0, 17, 48, 81,119,157,191,217,233,233,217,191,157,119, 81, 48, 17,  0, -5,-11,-11, -9, -6, -3,  0,
+      },
+      { // B=0.26000 C=0.37000
+         0, -3, -6,-10,-11,-11, -5,  0, 16, 47, 81,120,156,191,218,233,233,218,191,156,120, 81, 47, 16,  0, -5,-11,-11,-10, -6, -3,  0,
+      },
+      { // B=0.25333 C=0.37333
+         0, -3, -6, -9,-12,-11, -6,  0, 16, 47, 81,119,158,192,218,234,234,218,192,158,119, 81, 47, 16,  0, -6,-11,-12, -9, -6, -3,  0,
+      },
+      { // B=0.24667 C=0.37667
+         0, -3, -6,-10,-12,-11, -6,  0, 16, 47, 81,120,157,192,219,234,234,219,192,157,120, 81, 47, 16,  0, -6,-11,-12,-10, -6, -3,  0,
+      },
+      { // B=0.24000 C=0.38000
+         0, -3, -6,-10,-12,-12, -6, -1, 16, 47, 81,120,158,193,219,235,235,219,193,158,120, 81, 47, 16, -1, -6,-12,-12,-10, -6, -3,  0,
+      },
+      { // B=0.23333 C=0.38333
+         0, -3, -6,-10,-12,-12, -6, -1, 16, 46, 81,120,158,193,220,236,236,220,193,158,120, 81, 46, 16, -1, -6,-12,-12,-10, -6, -3,  0,
+      },
+      { // B=0.22667 C=0.38667
+         0, -3, -6,-10,-12,-12, -7, -1, 15, 47, 80,120,158,194,220,236,236,220,194,158,120, 80, 47, 15, -1, -7,-12,-12,-10, -6, -3,  0,
+      },
+      { // B=0.22000 C=0.39000
+         0, -3, -6,-10,-13,-12, -7, -1, 15, 46, 80,120,159,194,221,237,237,221,194,159,120, 80, 46, 15, -1, -7,-12,-13,-10, -6, -3,  0,
+      },
+      { // B=0.21333 C=0.39333
+         0, -3, -6,-10,-13,-12, -8, -1, 15, 46, 80,120,159,194,221,237,237,221,194,159,120, 80, 46, 15, -1, -8,-12,-13,-10, -6, -3,  0,
+      },
+      { // B=0.20667 C=0.39667
+         0, -3, -7,-10,-13,-12, -8, -2, 15, 46, 80,120,159,194,222,238,238,222,194,159,120, 80, 46, 15, -2, -8,-12,-13,-10, -7, -3,  0,
+      },
+      { // B=0.20000 C=0.40000
+         0, -3, -7,-10,-13,-13, -8, -2, 15, 45, 81,120,159,195,222,238,238,222,195,159,120, 81, 45, 15, -2, -8,-13,-13,-10, -7, -3,  0,
+      },
+      { // B=0.19333 C=0.40333
+         0, -3, -7,-11,-13,-13, -8, -2, 15, 45, 81,120,160,195,223,239,239,223,195,160,120, 81, 45, 15, -2, -8,-13,-13,-11, -7, -3,  0,
+      },
+      { // B=0.18667 C=0.40667
+         0, -3, -7,-10,-14,-13, -9, -2, 14, 45, 80,120,160,196,223,240,240,223,196,160,120, 80, 45, 14, -2, -9,-13,-14,-10, -7, -3,  0,
+      },
+      { // B=0.18000 C=0.41000
+         0, -3, -7,-11,-13,-13, -9, -2, 14, 45, 80,120,160,196,224,240,240,224,196,160,120, 80, 45, 14, -2, -9,-13,-13,-11, -7, -3,  0,
+      },
+      { // B=0.17333 C=0.41333
+         0, -3, -7,-11,-13,-14, -9, -3, 14, 45, 80,120,160,196,225,240,240,225,196,160,120, 80, 45, 14, -3, -9,-14,-13,-11, -7, -3,  0,
+      },
+      { // B=0.16667 C=0.41667
+         0, -3, -7,-11,-14,-14, -9, -3, 14, 44, 80,120,161,197,225,241,241,225,197,161,120, 80, 44, 14, -3, -9,-14,-14,-11, -7, -3,  0,
+      },
+      { // B=0.16000 C=0.42000
+         0, -3, -7,-11,-14,-14,-10, -3, 14, 44, 80,120,161,197,225,242,242,225,197,161,120, 80, 44, 14, -3,-10,-14,-14,-11, -7, -3,  0,
+      },
+      { // B=0.15333 C=0.42333
+         0, -3, -7,-11,-14,-14,-10, -3, 13, 44, 80,120,161,197,226,242,242,226,197,161,120, 80, 44, 13, -3,-10,-14,-14,-11, -7, -3,  0,
+      },
+      { // B=0.14667 C=0.42667
+         0, -3, -7,-11,-15,-14,-10, -4, 14, 43, 80,120,163,198,226,243,243,226,198,163,120, 80, 43, 14, -4,-10,-14,-15,-11, -7, -3,  0,
+      },
+      { // B=0.14000 C=0.43000
+         0, -3, -7,-12,-14,-15,-10, -4, 14, 43, 80,120,163,198,227,243,243,227,198,163,120, 80, 43, 14, -4,-10,-15,-14,-12, -7, -3,  0,
+      },
+      { // B=0.13333 C=0.43333
+         0, -3, -7,-12,-14,-15,-11, -4, 13, 43, 79,121,161,199,227,244,244,227,199,161,121, 79, 43, 13, -4,-11,-15,-14,-12, -7, -3,  0,
+      },
+      { // B=0.12667 C=0.43667
+         0, -3, -7,-12,-14,-15,-11, -4, 13, 43, 79,120,163,199,228,245,245,228,199,163,120, 79, 43, 13, -4,-11,-15,-14,-12, -7, -3,  0,
+      },
+      { // B=0.12000 C=0.44000
+         0, -3, -7,-12,-15,-15,-12, -5, 13, 43, 79,121,162,199,228,245,245,228,199,162,121, 79, 43, 13, -5,-12,-15,-15,-12, -7, -3,  0,
+      },
+      { // B=0.11333 C=0.44333
+         0, -3, -7,-12,-15,-16,-11, -5, 13, 42, 79,121,162,200,229,246,246,229,200,162,121, 79, 42, 13, -5,-11,-16,-15,-12, -7, -3,  0,
+      },
+      { // B=0.10667 C=0.44667
+         0, -3, -8,-12,-15,-16,-12, -5, 13, 42, 79,121,162,200,229,246,246,229,200,162,121, 79, 42, 13, -5,-12,-16,-15,-12, -8, -3,  0,
+      },
+      { // B=0.10000 C=0.45000
+         0, -3, -8,-12,-16,-16,-12, -6, 13, 42, 79,121,163,200,230,247,247,230,200,163,121, 79, 42, 13, -6,-12,-16,-16,-12, -8, -3,  0,
+      },
+      { // B=0.09333 C=0.45333
+         0, -3, -8,-12,-16,-16,-13, -5, 12, 42, 79,121,163,201,230,248,248,230,201,163,121, 79, 42, 12, -5,-13,-16,-16,-12, -8, -3,  0,
+      },
+      { // B=0.08667 C=0.45667
+         0, -3, -8,-12,-16,-16,-13, -5, 12, 41, 79,121,163,201,231,248,248,231,201,163,121, 79, 41, 12, -5,-13,-16,-16,-12, -8, -3,  0,
+      },
+      { // B=0.08000 C=0.46000
+         0, -3, -8,-12,-16,-17,-13, -6, 12, 41, 79,121,163,201,232,248,248,232,201,163,121, 79, 41, 12, -6,-13,-17,-16,-12, -8, -3,  0,
+      },
+      { // B=0.07333 C=0.46333
+         0, -3, -8,-13,-16,-17,-13, -6, 12, 41, 79,121,164,202,232,249,249,232,202,164,121, 79, 41, 12, -6,-13,-17,-16,-13, -8, -3,  0,
+      },
+      { // B=0.06667 C=0.46667
+         0, -3, -8,-13,-16,-17,-14, -6, 11, 41, 79,121,164,202,233,249,249,233,202,164,121, 79, 41, 11, -6,-14,-17,-16,-13, -8, -3,  0,
+      },
+      { // B=0.06000 C=0.47000
+         0, -3, -8,-13,-16,-18,-14, -6, 11, 40, 79,121,164,203,233,250,250,233,203,164,121, 79, 40, 11, -6,-14,-18,-16,-13, -8, -3,  0,
+      },
+      { // B=0.05333 C=0.47333
+         0, -3, -8,-13,-17,-18,-14, -6, 11, 40, 79,121,165,203,233,251,251,233,203,165,121, 79, 40, 11, -6,-14,-18,-17,-13, -8, -3,  0,
+      },
+      { // B=0.04667 C=0.47667
+         0, -4, -8,-13,-17,-18,-14, -7, 11, 40, 79,121,166,203,234,251,251,234,203,166,121, 79, 40, 11, -7,-14,-18,-17,-13, -8, -4,  0,
+      },
+      { // B=0.04000 C=0.48000
+         0, -4, -8,-13,-17,-18,-14, -7, 11, 40, 78,121,166,204,234,251,251,234,204,166,121, 78, 40, 11, -7,-14,-18,-17,-13, -8, -4,  0,
+      },
+      { // B=0.03333 C=0.48333
+         0, -4, -8,-14,-17,-18,-15, -7, 11, 40, 78,122,164,204,235,252,252,235,204,164,122, 78, 40, 11, -7,-15,-18,-17,-14, -8, -4,  0,
+      },
+      { // B=0.02667 C=0.48667
+         0, -4, -8,-14,-17,-19,-15, -7, 10, 40, 78,122,164,205,235,253,253,235,205,164,122, 78, 40, 10, -7,-15,-19,-17,-14, -8, -4,  0,
+      },
+      { // B=0.02000 C=0.49000
+         0, -4, -8,-14,-17,-19,-15, -7, 10, 39, 78,122,164,205,236,253,253,236,205,164,122, 78, 39, 10, -7,-15,-19,-17,-14, -8, -4,  0,
+      },
+      { // B=0.01333 C=0.49333
+         0, -4, -8,-14,-18,-19,-16, -7,  9, 40, 78,122,165,205,236,254,254,236,205,165,122, 78, 40,  9, -7,-16,-19,-18,-14, -8, -4,  0,
+      },
+      { // B=0.00667 C=0.49667
+         0, -4, -9,-14,-18,-19,-15, -8, 10, 39, 78,122,166,206,236,254,254,236,206,166,122, 78, 39, 10, -8,-15,-19,-18,-14, -9, -4,  0,
+      },
+      { // B=0.00000 C=0.50000
+         0, -4, -8,-14,-18,-19,-16, -8,  9, 39, 77,122,166,206,237,255,255,237,206,166,122, 77, 39,  9, -8,-16,-19,-18,-14, -8, -4,  0,
+      },
+  };
+  int index = (sharpness + 1.0f) * 50.0f + 0.5f;
+  if (index >=0 && index <= 100)
+  {
+    const int16_t *coef = mitchells[index];
+
+    char command[33*12];
+    char response[33*12];
+    unsigned int len = sprintf(command, "scaling_kernel ");
+    for (int i=0; i < 32; i++) {
+       if (len + 12 < sizeof command)
+         len += sprintf(command+len, "%d ", coef[i]);
+    }
+    // no interpolate flag
+    if (len + 12 < sizeof command)
+      len += sprintf(command+len, " %d", 0);
+    //printf("%i: %s\n", index, command);
+    vc_gencmd(response, sizeof response, command);
+  }
+}
+
 void COMXPlayer::Process()
 {
   bool bOmxWaitVideo = false;
@@ -1183,6 +1511,8 @@ void COMXPlayer::Process()
     SetCaching(CACHESTATE_FLUSH);
 
   EDEINTERLACEMODE current_deinterlace = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+  float current_sharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
+  SetSharpness(current_sharpness);
 
   while (!m_bAbortRequest)
   {
@@ -1214,6 +1544,13 @@ void COMXPlayer::Process()
         current_deinterlace = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
       }
 
+      // if sharpness setting has changed, we should update it
+      if (current_sharpness != CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness)
+      {
+        current_sharpness = CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness;
+        SetSharpness(current_sharpness);
+      }
+
       m_video_fifo = (int)(100.0*(m_omxPlayerVideo.GetDecoderBufferSize()-m_omxPlayerVideo.GetDecoderFreeSpace())/m_omxPlayerVideo.GetDecoderBufferSize());
       m_audio_fifo = (int)(100.0*audio_fifo/m_omxPlayerAudio.GetCacheTotal());
 
@@ -4577,6 +4914,7 @@ void COMXPlayer::GetRenderFeatures(std::vector<int> &renderFeatures)
   renderFeatures.push_back(RENDERFEATURE_CROP);
   renderFeatures.push_back(RENDERFEATURE_PIXEL_RATIO);
   renderFeatures.push_back(RENDERFEATURE_ZOOM);
+  renderFeatures.push_back(RENDERFEATURE_SHARPNESS);
 }
 
 void COMXPlayer::GetDeinterlaceMethods(std::vector<int> &deinterlaceMethods)
-- 
1.9.3


From 97a3f2347e45eee8f960a2190891d4bd9de3dda0 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 10 Apr 2014 17:24:51 +0100
Subject: [PATCH 68/94] [rpi] Include ntsc frequencies in list of supported
 resolutions

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp         |  8 ------
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp | 35 ++++++++++++++++++++++++-
 xbmc/windowing/egl/WinSystemEGL.cpp             |  3 ++-
 3 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index 019f4b2..b8a3871 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -556,14 +556,6 @@ bool OMXPlayerVideo::OpenDecoder()
         m_omxVideo.GetDecoderName().c_str() , m_hints.width, m_hints.height, m_hints.profile, m_fFrameRate);
 
     m_codecname = m_omxVideo.GetDecoderName();
-
-    // if we are closer to ntsc version of framerate, let gpu know
-    int   iFrameRate  = (int)(m_fFrameRate + 0.5f);
-    bool  bNtscFreq  = fabs(m_fFrameRate * 1001.0f / 1000.0f - iFrameRate) < fabs(m_fFrameRate - iFrameRate);
-    char  response[80], command[80];
-    sprintf(command, "hdmi_ntsc_freqs %d", bNtscFreq);
-    CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder fps: %f %s\n", m_fFrameRate, command);
-    m_DllBcmHost.vc_gencmd(response, sizeof response, command);
   }
 
   // start from assuming all recent frames had valid pts
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index dc47095..21b8cc4 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -20,6 +20,7 @@
 #include "system.h"
 
 #include <EGL/egl.h>
+#include <math.h>
 #include "EGLNativeTypeRaspberryPI.h"
 #include "utils/log.h"
 #include "guilib/gui3d.h"
@@ -236,6 +237,18 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
       property.param2 = 0;
       vc_tv_hdmi_set_property(&property);
     }
+
+    HDMI_PROPERTY_PARAM_T property;
+    property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
+    // if we are closer to ntsc version of framerate, let gpu know
+    int   iFrameRate  = (int)(res.fRefreshRate + 0.5f);
+    if (fabsf(res.fRefreshRate * (1001.0f / 1000.0f) - iFrameRate) < fabsf(res.fRefreshRate - iFrameRate))
+      property.param1 = HDMI_PIXEL_CLOCK_TYPE_NTSC;
+    else
+      property.param1 = HDMI_PIXEL_CLOCK_TYPE_PAL;
+    property.param2 = 0;
+    vc_tv_hdmi_set_property(&property);
+
     int success = m_DllBcmHost->vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags));
 
     if (success == 0)
@@ -442,7 +455,10 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
         m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
         m_desktopRes.fPixelRatio *= 0.5;
       }
-      m_desktopRes.fRefreshRate = (float)tv_state.display.hdmi.frame_rate;
+      HDMI_PROPERTY_PARAM_T property;
+      property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
+      vc_tv_hdmi_get_property(&property);
+      m_desktopRes.fRefreshRate = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tv_state.display.hdmi.frame_rate * (1000.0f/1001.0f) : tv_state.display.hdmi.frame_rate;
     }
     else // sdtv
     {
@@ -576,6 +592,12 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
       res.iSubtitles    = (int)(0.965 * res.iHeight);
 
       AddUniqueResolution(res, resolutions);
+      if (tv->frame_rate == 24 || tv->frame_rate == 30 || tv->frame_rate == 60)
+      {
+        RESOLUTION_INFO res2 = res;
+        res2.fRefreshRate  = (float)tv->frame_rate * (1000.0f/1001.0f);
+        AddUniqueResolution(res2, resolutions);
+      }
 
       // Also add 3D versions of modes
       if (tv->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)
@@ -590,6 +612,11 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
 
         AddUniqueResolution(res2, resolutions);
+        if (tv->frame_rate == 24 || tv->frame_rate == 30 || tv->frame_rate == 60)
+        {
+          res2.fRefreshRate  = (float)tv->frame_rate * (1000.0f/1001.0f);
+          AddUniqueResolution(res2, resolutions);
+        }
       }
       if (tv->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM)
       {
@@ -603,6 +630,12 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
 
         AddUniqueResolution(res2, resolutions);
+        if (tv->frame_rate == 24 || tv->frame_rate == 30 || tv->frame_rate == 60)
+        {
+          res2.fRefreshRate  = (float)tv->frame_rate * (1000.0f/1001.0f);
+          AddUniqueResolution(res2, resolutions);
+        }
+
       }
     }
   }
diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp
index 0c32947..258a293 100644
--- a/xbmc/windowing/egl/WinSystemEGL.cpp
+++ b/xbmc/windowing/egl/WinSystemEGL.cpp
@@ -34,6 +34,7 @@
 #include "EGLWrapper.h"
 #include "EGLQuirks.h"
 #include <vector>
+#include <float.h>
 ////////////////////////////////////////////////////////////////////////////////////////////
 CWinSystemEGL::CWinSystemEGL() : CWinSystemBase()
 {
@@ -399,7 +400,7 @@ void CWinSystemEGL::UpdateResolutions()
        resDesktop.iScreenWidth == resolutions[i].iScreenWidth &&
        resDesktop.iScreenHeight == resolutions[i].iScreenHeight &&
        (resDesktop.dwFlags & D3DPRESENTFLAG_MODEMASK) == (resolutions[i].dwFlags & D3DPRESENTFLAG_MODEMASK) &&
-       resDesktop.fRefreshRate == resolutions[i].fRefreshRate)
+       fabs(resDesktop.fRefreshRate - resolutions[i].fRefreshRate) < FLT_EPSILON)
     {
       ResDesktop = res_index;
     }
-- 
1.9.3


From 6e000e35a42863a9db644692246c43300e4ab948 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 10 Apr 2014 17:26:20 +0100
Subject: [PATCH 69/94] [omxplayer] Allow a framerate callback from GPU to
 trigger a hdmi mode change

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp |  8 +++++---
 xbmc/cores/omxplayer/OMXPlayerVideo.h   |  4 ++--
 xbmc/cores/omxplayer/OMXVideo.cpp       | 35 +++++++++++++++++----------------
 xbmc/cores/omxplayer/OMXVideo.h         |  2 +-
 4 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index b8a3871..e9010b1 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -694,7 +694,7 @@ void OMXPlayerVideo::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect,
   player->SetVideoRect(SrcRect, DestRect);
 }
 
-void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, float display_aspect)
+void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, float framerate, float display_aspect)
 {
   RESOLUTION res  = g_graphicsContext.GetVideoResolution();
   uint32_t video_width   = CDisplaySettings::Get().GetResolutionInfo(res).iScreenWidth;
@@ -743,6 +743,8 @@ void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, f
   else if( display_aspect != 0.0f )
     iDisplayWidth = (int) (iDisplayHeight * display_aspect);
 
+  m_fFrameRate = DVD_TIME_BASE / CDVDCodecUtils::NormalizeFrameduration((double)DVD_TIME_BASE / framerate);
+
   CLog::Log(LOGDEBUG,"%s - change configuration. video:%dx%d. framerate: %4.2f. %dx%d format: BYPASS",
       __FUNCTION__, video_width, video_height, m_fFrameRate, iDisplayWidth, iDisplayHeight);
 
@@ -757,9 +759,9 @@ void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, f
   g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack);
 }
 
-void OMXPlayerVideo::ResolutionUpdateCallBack(void *ctx, uint32_t width, uint32_t height, float display_aspect)
+void OMXPlayerVideo::ResolutionUpdateCallBack(void *ctx, uint32_t width, uint32_t height, float framerate, float display_aspect)
 {
   OMXPlayerVideo *player = static_cast<OMXPlayerVideo*>(ctx);
-  player->ResolutionUpdateCallBack(width, height, display_aspect);
+  player->ResolutionUpdateCallBack(width, height, framerate, display_aspect);
 }
 
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.h b/xbmc/cores/omxplayer/OMXPlayerVideo.h
index b49748f..33564dd 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.h
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.h
@@ -125,7 +125,7 @@ class OMXPlayerVideo : public CThread
   int GetFreeSpace();
   void  SetVideoRect(const CRect &SrcRect, const CRect &DestRect);
   static void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect);
-  void ResolutionUpdateCallBack(uint32_t width, uint32_t height, float pixel_aspect);
-  static void ResolutionUpdateCallBack(void *ctx, uint32_t width, uint32_t height, float pixel_aspect);
+  void ResolutionUpdateCallBack(uint32_t width, uint32_t height, float framerate, float pixel_aspect);
+  static void ResolutionUpdateCallBack(void *ctx, uint32_t width, uint32_t height, float framerate, float pixel_aspect);
 };
 #endif
diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp
index 66a351d..10a7530 100644
--- a/xbmc/cores/omxplayer/OMXVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXVideo.cpp
@@ -171,6 +171,22 @@ bool COMXVideo::PortSettingsChanged()
     CLog::Log(LOGERROR, "%s::%s - error m_omx_decoder.GetParameter(OMX_IndexParamBrcmPixelAspectRatio) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
   }
 
+  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_image.format.video.nFrameWidth, port_image.format.video.nFrameHeight,
+      port_image.format.video.xFramerate / (float)(1<<16), interlace.eMode, m_deinterlace);
+
   // let OMXPlayerVideo know about resolution so it can inform RenderManager
   if (m_res_callback)
   {
@@ -178,7 +194,8 @@ bool COMXVideo::PortSettingsChanged()
     if (pixel_aspect.nX && pixel_aspect.nY)
       display_aspect = (float)pixel_aspect.nX * port_image.format.video.nFrameWidth /
         ((float)pixel_aspect.nY * port_image.format.video.nFrameHeight);
-    m_res_callback(m_res_ctx, port_image.format.video.nFrameWidth, port_image.format.video.nFrameHeight, display_aspect);
+    m_res_callback(m_res_ctx, port_image.format.video.nFrameWidth, port_image.format.video.nFrameHeight,
+        port_image.format.video.xFramerate / (float)(1<<16), display_aspect);
   }
 
   if (m_settings_changed)
@@ -187,27 +204,11 @@ bool COMXVideo::PortSettingsChanged()
     return true;
   }
 
-  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;
-
   if(!m_omx_render.Initialize("OMX.broadcom.video_render", OMX_IndexParamVideoInit))
     return false;
 
   m_omx_render.ResetEos();
 
-  CLog::Log(LOGDEBUG, "%s::%s - %dx%d@%.2f interlace:%d deinterlace:%d", CLASSNAME, __func__,
-      port_image.format.video.nFrameWidth, port_image.format.video.nFrameHeight,
-      port_image.format.video.xFramerate / (float)(1<<16), interlace.eMode, m_deinterlace);
-
   if(!m_omx_sched.Initialize("OMX.broadcom.video_scheduler", OMX_IndexParamVideoInit))
     return false;
 
diff --git a/xbmc/cores/omxplayer/OMXVideo.h b/xbmc/cores/omxplayer/OMXVideo.h
index d69f854..fd23e70 100644
--- a/xbmc/cores/omxplayer/OMXVideo.h
+++ b/xbmc/cores/omxplayer/OMXVideo.h
@@ -38,7 +38,7 @@
 
 #define CLASSNAME "COMXVideo"
 
-typedef void (*ResolutionUpdateCallBackFn)(void *ctx, uint32_t width, uint32_t height, float display_aspect);
+typedef void (*ResolutionUpdateCallBackFn)(void *ctx, uint32_t width, uint32_t height, float framerate, float display_aspect);
 
 class COMXVideo
 {
-- 
1.9.3


From 7d8653f297a2aeea7b10bf36732892878ae89334 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 11 Apr 2014 19:01:26 +0100
Subject: [PATCH 70/94] [omxplayer] Request to be notified about framerate
 changes

---
 xbmc/cores/omxplayer/OMXVideo.cpp | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp
index 10a7530..148b16f 100644
--- a/xbmc/cores/omxplayer/OMXVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXVideo.cpp
@@ -183,7 +183,7 @@ bool COMXVideo::PortSettingsChanged()
   else
     m_deinterlace = interlace.eMode != OMX_InterlaceProgressive;
 
-    CLog::Log(LOGDEBUG, "%s::%s - %dx%d@%.2f interlace:%d deinterlace:%d", CLASSNAME, __func__,
+  CLog::Log(LOGDEBUG, "%s::%s - %dx%d@%.2f interlace:%d deinterlace:%d", CLASSNAME, __func__,
       port_image.format.video.nFrameWidth, port_image.format.video.nFrameHeight,
       port_image.format.video.xFramerate / (float)(1<<16), interlace.eMode, m_deinterlace);
 
@@ -558,7 +558,17 @@ bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, EDEINTERLACEMODE de
     CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexConfigRequestCallback error (0%08x)\n", omx_err);
     return false;
   }
-
+  // request portsettingschanged on refresh rate change
+  if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") == ADJUST_REFRESHRATE_ALWAYS)
+  {
+    notifications.nIndex = OMX_IndexParamPortDefinition;
+    omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexConfigRequestCallback, &notifications);
+    if (omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexConfigRequestCallback error (0%08x)\n", omx_err);
+      //return false;
+    }
+  }
   OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE concanParam;
   OMX_INIT_STRUCTURE(concanParam);
   if(g_advancedSettings.m_omxDecodeStartWithValidFrame)
-- 
1.9.3


From 12a54cc9ca0c0500f1d86173df0c8466270bd766 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 14 Apr 2014 17:04:57 +0100
Subject: [PATCH 71/94] [omxplayer] Support stereo view modes with scaling

The Pi only supported a single view rectangle, which is sufficient for all mono view modes,
but only supports a subset of stereo modes.

To work around this, the scaling rectangles were ignored in 3D modes and 3D video was treated as unscaled.
While this worked or square pixel, 16:9 content on a 16:9 display, it went wrong is various other conditions.

@sraue reported that mono view of SBS/TAB content wasn't scaled correctly, and other forum reports
aspect ratio errors in widescreen 3D videos.

As it wasn't trivial to work around these bug reports, I've added a new stereo flags to the firmware
that allows a second display region to be created for the second eye, allowing scaling to work.

I've been through the video stereo modes (none, sbs, tab) and the display stereo modes (mono, sbs, tab)
and all the zoom modes, and compared the scaling to xbmc on windows and all seem to match

Requires udpated firmware.
---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 103 +++++++++++++++++++++-----------
 xbmc/cores/omxplayer/OMXPlayerVideo.h   |   4 +-
 xbmc/cores/omxplayer/OMXVideo.cpp       |  45 ++++++++------
 xbmc/cores/omxplayer/OMXVideo.h         |   3 +-
 4 files changed, 100 insertions(+), 55 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index e9010b1..d5f3bec 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -97,6 +97,9 @@ OMXPlayerVideo::OMXPlayerVideo(OMXClock *av_clock,
 
   m_src_rect.SetRect(0, 0, 0, 0);
   m_dst_rect.SetRect(0, 0, 0, 0);
+  m_video_stereo_mode = RENDER_STEREO_MODE_OFF;
+  m_display_stereo_mode = RENDER_STEREO_MODE_OFF;
+  m_StereoInvert = false;
   m_started = false;
   m_iCurrentPts = DVD_NOPTS_VALUE;
   m_nextOverlay = DVD_NOPTS_VALUE;
@@ -120,6 +123,9 @@ bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints)
   // force SetVideoRect to be called initially
   m_src_rect.SetRect(0, 0, 0, 0);
   m_dst_rect.SetRect(0, 0, 0, 0);
+  m_video_stereo_mode = RENDER_STEREO_MODE_OFF;
+  m_display_stereo_mode = RENDER_STEREO_MODE_OFF;
+  m_StereoInvert = false;
 
   if (!m_DllBcmHost.Load())
     return false;
@@ -634,58 +640,85 @@ int OMXPlayerVideo::GetFreeSpace()
 
 void OMXPlayerVideo::SetVideoRect(const CRect &InSrcRect, const CRect &InDestRect)
 {
-  CRect SrcRect = InSrcRect, DestRect = InDestRect;
+  // we get called twice a frame for left/right. Can ignore the rights.
+  if (g_graphicsContext.GetStereoView() == RENDER_STEREO_VIEW_RIGHT)
+    return;
 
-  // in 3d modes skip this - we get called as the gui switches from left eye to right eye
+  CRect SrcRect = InSrcRect, DestRect = InDestRect;
   unsigned flags = GetStereoModeFlags(GetStereoMode());
-
-  if (CONF_FLAGS_STEREO_MODE_MASK(flags))
-  {
-    if (g_graphicsContext.GetStereoMode() == RENDER_STEREO_MODE_MONO)
-    {
-       if (GetStereoMode() == "left_right")
-         SrcRect.SetRect(0, 0, m_hints.width>>1, m_hints.height);
-       else if (GetStereoMode() == "right_left")
-         SrcRect.SetRect(m_hints.width>>1, 0, m_hints.width, m_hints.height);
-       else if (GetStereoMode() == "top_bottom")
-         SrcRect.SetRect(0, 0, m_hints.width, m_hints.height>>1);
-       else if (GetStereoMode() == "bottom_top")
-         SrcRect.SetRect(0, m_hints.height>>1, m_hints.width, m_hints.height);
-    }
-    else
-      SrcRect.SetRect(0, 0, m_hints.width, m_hints.height);
-    // interpreted as fullscreen
-    DestRect.SetRect(0, 0, 0, 0);
-  }
+  RENDER_STEREO_MODE video_stereo_mode = (flags & CONF_FLAGS_STEREO_MODE_SBS) ? RENDER_STEREO_MODE_SPLIT_VERTICAL :
+                                         (flags & CONF_FLAGS_STEREO_MODE_TAB) ? RENDER_STEREO_MODE_SPLIT_HORIZONTAL : RENDER_STEREO_MODE_OFF;
+  bool stereo_invert                   = (flags & CONF_FLAGS_STEREO_CADANCE_RIGHT_LEFT) ? true : false;
+  RENDER_STEREO_MODE display_stereo_mode = g_graphicsContext.GetStereoMode();
 
   // check if destination rect or video view mode has changed
-  if (m_dst_rect != DestRect || m_src_rect != SrcRect)
-  {
-    m_src_rect  = SrcRect;
-    m_dst_rect  = DestRect;
-  }
-  else
-  {
+  if (!(m_dst_rect != DestRect) && !(m_src_rect != SrcRect) && m_video_stereo_mode == video_stereo_mode && m_display_stereo_mode == display_stereo_mode && m_StereoInvert == stereo_invert)
     return;
-  }
+
+  CLog::Log(LOGDEBUG, "OMXPlayerVideo::%s %d,%d,%d,%d -> %d,%d,%d,%d (%d,%d,%d,%d,%s)", __func__,
+      (int)SrcRect.x1, (int)SrcRect.y1, (int)SrcRect.x2, (int)SrcRect.y2,
+      (int)DestRect.x1, (int)DestRect.y1, (int)DestRect.x2, (int)DestRect.y2,
+      video_stereo_mode, display_stereo_mode, CMediaSettings::Get().GetCurrentVideoSettings().m_StereoInvert, g_graphicsContext.GetStereoView(), OMXPlayerVideo::GetStereoMode().c_str());
+
+  m_src_rect = SrcRect;
+  m_dst_rect = DestRect;
+  m_video_stereo_mode = video_stereo_mode;
+  m_display_stereo_mode = display_stereo_mode;
+  m_StereoInvert = stereo_invert;
 
   // might need to scale up m_dst_rect to display size as video decodes
   // to separate video plane that is at display size.
   RESOLUTION res = g_graphicsContext.GetVideoResolution();
   CRect gui(0, 0, CDisplaySettings::Get().GetResolutionInfo(res).iWidth, CDisplaySettings::Get().GetResolutionInfo(res).iHeight);
   CRect display(0, 0, CDisplaySettings::Get().GetResolutionInfo(res).iScreenWidth, CDisplaySettings::Get().GetResolutionInfo(res).iScreenHeight);
-  CRect dst_rect(m_dst_rect);
+
+  switch (video_stereo_mode)
+  {
+  case RENDER_STEREO_MODE_SPLIT_VERTICAL:
+    // optimisation - use simpler display mode in common case of unscaled 3d with same display mode
+    if (video_stereo_mode == display_stereo_mode && DestRect.x1 == 0.0f && DestRect.x2 * 2.0f == gui.Width() && !stereo_invert)
+    {
+      SrcRect.x2 *= 2.0f;
+      DestRect.x2 *= 2.0f;
+      video_stereo_mode = RENDER_STEREO_MODE_OFF;
+      display_stereo_mode = RENDER_STEREO_MODE_OFF;
+    }
+    else if (stereo_invert)
+    {
+      SrcRect.x1 += m_hints.width / 2;
+      SrcRect.x2 += m_hints.width / 2;
+    }
+    break;
+
+  case RENDER_STEREO_MODE_SPLIT_HORIZONTAL:
+    // optimisation - use simpler display mode in common case of unscaled 3d with same display mode
+    if (video_stereo_mode == display_stereo_mode && DestRect.y1 == 0.0f && DestRect.y2 * 2.0f == gui.Height() && !stereo_invert)
+    {
+      SrcRect.y2 *= 2.0f;
+      DestRect.y2 *= 2.0f;
+      video_stereo_mode = RENDER_STEREO_MODE_OFF;
+      display_stereo_mode = RENDER_STEREO_MODE_OFF;
+    }
+    else if (stereo_invert)
+    {
+      SrcRect.y1 += m_hints.height / 2;
+      SrcRect.y2 += m_hints.height / 2;
+    }
+    break;
+
+  default: break;
+  }
 
   if (gui != display)
   {
     float xscale = display.Width()  / gui.Width();
     float yscale = display.Height() / gui.Height();
-    dst_rect.x1 *= xscale;
-    dst_rect.x2 *= xscale;
-    dst_rect.y1 *= yscale;
-    dst_rect.y2 *= yscale;
+    DestRect.x1 *= xscale;
+    DestRect.x2 *= xscale;
+    DestRect.y1 *= yscale;
+    DestRect.y2 *= yscale;
   }
-  m_omxVideo.SetVideoRect(SrcRect, dst_rect);
+  m_omxVideo.SetVideoRect(SrcRect, DestRect, video_stereo_mode, display_stereo_mode);
 }
 
 void OMXPlayerVideo::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect)
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.h b/xbmc/cores/omxplayer/OMXPlayerVideo.h
index 33564dd..6f19395 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.h
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.h
@@ -69,7 +69,9 @@ class OMXPlayerVideo : public CThread
 
   CRect                     m_src_rect;
   CRect                     m_dst_rect;
-
+  RENDER_STEREO_MODE        m_video_stereo_mode;
+  RENDER_STEREO_MODE        m_display_stereo_mode;
+  bool                      m_StereoInvert;
   uint32_t                  m_history_valid_pts;
   DllBcmHost                m_DllBcmHost;
 
diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp
index 148b16f..dceb8bf 100644
--- a/xbmc/cores/omxplayer/OMXVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXVideo.cpp
@@ -836,7 +836,7 @@ void COMXVideo::Reset(void)
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////
-void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect)
+void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect, RENDER_STEREO_MODE video_mode, RENDER_STEREO_MODE display_mode)
 {
   CSingleLock lock (m_critSection);
   if(!m_is_open)
@@ -846,27 +846,36 @@ void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect)
 
   OMX_INIT_STRUCTURE(configDisplay);
   configDisplay.nPortIndex = m_omx_render.GetInputPort();
-  configDisplay.set                 = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_DEST_RECT|OMX_DISPLAY_SET_SRC_RECT|OMX_DISPLAY_SET_FULLSCREEN|OMX_DISPLAY_SET_NOASPECT);
-  configDisplay.dest_rect.x_offset  = (int)(DestRect.x1+0.5f);
-  configDisplay.dest_rect.y_offset  = (int)(DestRect.y1+0.5f);
-  configDisplay.dest_rect.width     = (int)(DestRect.Width()+0.5f);
-  configDisplay.dest_rect.height    = (int)(DestRect.Height()+0.5f);
-
-  configDisplay.src_rect.x_offset   = (int)(SrcRect.x1+0.5f);
-  configDisplay.src_rect.y_offset   = (int)(SrcRect.y1+0.5f);
-  configDisplay.src_rect.width      = (int)(SrcRect.Width()+0.5f);
-  configDisplay.src_rect.height     = (int)(SrcRect.Height()+0.5f);
-
-  if (configDisplay.dest_rect.width == 0 || configDisplay.dest_rect.height == 0)
-    configDisplay.fullscreen = OMX_TRUE;
+  configDisplay.set                 = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_DEST_RECT|OMX_DISPLAY_SET_SRC_RECT|OMX_DISPLAY_SET_FULLSCREEN|OMX_DISPLAY_SET_NOASPECT|OMX_DISPLAY_SET_MODE);
+  configDisplay.dest_rect.x_offset  = lrintf(DestRect.x1);
+  configDisplay.dest_rect.y_offset  = lrintf(DestRect.y1);
+  configDisplay.dest_rect.width     = lrintf(DestRect.Width());
+  configDisplay.dest_rect.height    = lrintf(DestRect.Height());
+
+  configDisplay.src_rect.x_offset   = lrintf(SrcRect.x1);
+  configDisplay.src_rect.y_offset   = lrintf(SrcRect.y1);
+  configDisplay.src_rect.width      = lrintf(SrcRect.Width());
+  configDisplay.src_rect.height     = lrintf(SrcRect.Height());
+
+  configDisplay.fullscreen = OMX_FALSE;
+  configDisplay.noaspect = OMX_TRUE;
+
+  if (video_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL && display_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+    configDisplay.mode = OMX_DISPLAY_MODE_STEREO_TOP_TO_TOP;
+  else if (video_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL && display_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+    configDisplay.mode = OMX_DISPLAY_MODE_STEREO_TOP_TO_LEFT;
+  else if (video_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL && display_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+    configDisplay.mode = OMX_DISPLAY_MODE_STEREO_LEFT_TO_TOP;
+  else if (video_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL && display_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+    configDisplay.mode = OMX_DISPLAY_MODE_STEREO_LEFT_TO_LEFT;
   else
-    configDisplay.noaspect = OMX_TRUE;
+    configDisplay.mode = OMX_DISPLAY_MODE_LETTERBOX;
 
   m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
 
-  CLog::Log(LOGDEBUG, "dest_rect.x_offset %d dest_rect.y_offset %d dest_rect.width %d dest_rect.height %d\n",
-      configDisplay.dest_rect.x_offset, configDisplay.dest_rect.y_offset, 
-      configDisplay.dest_rect.width, configDisplay.dest_rect.height);
+  CLog::Log(LOGDEBUG, "%s::%s %d,%d,%d,%d -> %d,%d,%d,%d mode:%d", CLASSNAME, __func__,
+      configDisplay.src_rect.x_offset, configDisplay.src_rect.y_offset, configDisplay.src_rect.width, configDisplay.src_rect.height,
+      configDisplay.dest_rect.x_offset, configDisplay.dest_rect.y_offset, configDisplay.dest_rect.width, configDisplay.dest_rect.height, configDisplay.mode);
 }
 
 int COMXVideo::GetInputBufferSize()
diff --git a/xbmc/cores/omxplayer/OMXVideo.h b/xbmc/cores/omxplayer/OMXVideo.h
index fd23e70..226000e 100644
--- a/xbmc/cores/omxplayer/OMXVideo.h
+++ b/xbmc/cores/omxplayer/OMXVideo.h
@@ -32,6 +32,7 @@
 #include "DVDDemuxers/DVDDemux.h"
 #include "xbmc/settings/VideoSettings.h"
 #include "threads/CriticalSection.h"
+#include "xbmc/rendering/RenderSystem.h"
 #include <string>
 
 #define VIDEO_BUFFERS 60
@@ -58,7 +59,7 @@ class COMXVideo
   void Reset(void);
   void SetDropState(bool bDrop);
   std::string GetDecoderName() { return m_video_codec_name; };
-  void SetVideoRect(const CRect& SrcRect, const CRect& DestRect);
+  void SetVideoRect(const CRect& SrcRect, const CRect& DestRect, RENDER_STEREO_MODE video_mode, RENDER_STEREO_MODE display_mode);
   int GetInputBufferSize();
   void SubmitEOS();
   bool IsEOS();
-- 
1.9.3


From 716400b7b3cf13c6546bba6a0d0a2f47825f9117 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 16 Apr 2014 21:18:06 +0100
Subject: [PATCH 72/94] [omxplayer] Don't propagate 3d flags based on supported
 3d modes

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 29 ++++-------------------------
 1 file changed, 4 insertions(+), 25 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index d5f3bec..e9f86f3 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -736,36 +736,15 @@ void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, f
   unsigned flags = 0;
   ERenderFormat format = RENDER_FMT_BYPASS;
 
+  /* figure out steremode expected based on user settings and hints */
+  unsigned int stereo_flags = GetStereoModeFlags(GetStereoMode());
+
   if(m_bAllowFullscreen)
   {
     flags |= CONF_FLAGS_FULLSCREEN;
     m_bAllowFullscreen = false; // only allow on first configure
   }
-
-  flags |= GetStereoModeFlags(GetStereoMode());
-
-  if(flags & CONF_FLAGS_STEREO_MODE_SBS)
-  {
-    if(g_Windowing.Support3D(video_width, video_height, D3DPRESENTFLAG_MODE3DSBS))
-      CLog::Log(LOGNOTICE, "3DSBS movie found");
-    else
-    {
-      flags &= ~CONF_FLAGS_STEREO_MODE_MASK(~0);
-      CLog::Log(LOGNOTICE, "3DSBS movie found but not supported");
-    }
-  }
-  else if(flags & CONF_FLAGS_STEREO_MODE_TAB)
-  {
-    if(g_Windowing.Support3D(video_width, video_height, D3DPRESENTFLAG_MODE3DTB))
-      CLog::Log(LOGNOTICE, "3DTB movie found");
-    else
-    {
-      flags &= ~CONF_FLAGS_STEREO_MODE_MASK(~0);
-      CLog::Log(LOGNOTICE, "3DTB movie found but not supported");
-    }
-  }
-  else
-    CLog::Log(LOGNOTICE, "not a 3D movie");
+  flags |= stereo_flags;
 
   unsigned int iDisplayWidth  = width;
   unsigned int iDisplayHeight = height;
-- 
1.9.3


From 7f2220746d9efeb3e8cfc4524479a615db5b710c Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 17 Apr 2014 13:00:52 +0100
Subject: [PATCH 73/94] [graphics] Don't set stereo mode based on resolution

The resolution change should follow stereo mode
---
 xbmc/guilib/GraphicContext.cpp | 22 ----------------------
 1 file changed, 22 deletions(-)

diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index 5bffdf5..7e4fdd4 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -418,28 +418,6 @@ void CGraphicContext::SetVideoResolution(RESOLUTION res, bool forceUpdate)
   Lock();
 
   RESOLUTION_INFO info_org  = CDisplaySettings::Get().GetResolutionInfo(res);
-  RESOLUTION_INFO info_last = CDisplaySettings::Get().GetResolutionInfo(lastRes);
-
-  RENDER_STEREO_MODE stereo_mode = m_stereoMode;
-
-  // if the new mode is an actual stereo mode, switch to that
-  // if the old mode was an actual stereo mode, switch to no 3d mode
-  if (info_org.dwFlags & D3DPRESENTFLAG_MODE3DTB)
-    stereo_mode = RENDER_STEREO_MODE_SPLIT_HORIZONTAL;
-  else if (info_org.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
-    stereo_mode = RENDER_STEREO_MODE_SPLIT_VERTICAL;
-  else if ((info_last.dwFlags & D3DPRESENTFLAG_MODE3DSBS) != 0
-        || (info_last.dwFlags & D3DPRESENTFLAG_MODE3DTB)  != 0)
-    stereo_mode = RENDER_STEREO_MODE_OFF;
-
-  if(stereo_mode != m_stereoMode)
-  {
-    m_stereoView     = RENDER_STEREO_VIEW_OFF;
-    m_stereoMode     = stereo_mode;
-    m_nextStereoMode = stereo_mode;
-    CSettings::Get().SetInt("videoscreen.stereoscopicmode", (int)m_stereoMode);
-  }
-
   RESOLUTION_INFO info_mod = GetResInfo(res);
 
   m_iScreenWidth  = info_mod.iWidth;
-- 
1.9.3


From 7885d90283809ccbcdb9c39809682dfc099049c4 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 17 Apr 2014 13:01:51 +0100
Subject: [PATCH 74/94] [graphics] Allow switching to a more suitable 3D
 resolution

---
 xbmc/guilib/GraphicContext.cpp | 41 ++++++++++++++++++++++++++++++++++++++++-
 xbmc/guilib/GraphicContext.h   |  1 +
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index 7e4fdd4..886b612 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -35,6 +35,7 @@
 #include "utils/JobManager.h"
 #include "video/VideoReferenceClock.h"
 #include "cores/IPlayer.h"
+#include <float.h>
 
 using namespace std;
 
@@ -459,6 +460,44 @@ RESOLUTION CGraphicContext::GetVideoResolution() const
   return m_Resolution;
 }
 
+RESOLUTION CGraphicContext::Get3DVideoResolution(RENDER_STEREO_MODE mode) const
+{
+  RESOLUTION best = m_Resolution;
+  RESOLUTION_INFO curr = CDisplaySettings::Get().GetResolutionInfo(best);
+
+  // Find closest refresh rate
+  for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
+  {
+    const RESOLUTION_INFO info = CDisplaySettings::Get().GetResolutionInfo((RESOLUTION)i);
+
+    //discard resolutions that are not the same width and height (and interlaced/3D flags)
+    //or have a too low refreshrate
+    if (info.iScreenWidth  != curr.iScreenWidth
+    ||  info.iScreenHeight != curr.iScreenHeight
+    ||  info.iScreen       != curr.iScreen
+    ||  (info.dwFlags & D3DPRESENTFLAG_INTERLACED) != (curr.dwFlags & D3DPRESENTFLAG_INTERLACED)
+    ||  fabs(info.fRefreshRate - curr.fRefreshRate) >= FLT_EPSILON)
+      continue;
+
+    if (mode == RENDER_STEREO_MODE_SPLIT_VERTICAL && info.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+    {
+      best = (RESOLUTION)i;
+      break;
+    }
+    else if (mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL && info.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+    {
+      best = (RESOLUTION)i;
+      break;
+    }
+    else if ((mode == RENDER_STEREO_MODE_OFF || mode == RENDER_STEREO_MODE_MONO) && !(info.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB)))
+    {
+      best = (RESOLUTION)i;
+      break;
+    }
+  }
+  return best;
+}
+
 void CGraphicContext::ResetOverscan(RESOLUTION_INFO &res)
 {
   res.Overscan.left = 0;
@@ -996,7 +1035,7 @@ void CGraphicContext::Flip(const CDirtyRegionList& dirty)
   if(m_stereoMode != m_nextStereoMode)
   {
     m_stereoMode = m_nextStereoMode;
-    SetVideoResolution(GetVideoResolution(), true);
+    SetVideoResolution(Get3DVideoResolution(m_stereoMode), true);
     g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
   }
 }
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index 0a27643..df55e92 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -108,6 +108,7 @@ class CGraphicContext : public CCriticalSection,
   bool IsValidResolution(RESOLUTION res);
   void SetVideoResolution(RESOLUTION res, bool forceUpdate = false);
   RESOLUTION GetVideoResolution() const;
+  RESOLUTION Get3DVideoResolution(RENDER_STEREO_MODE mode) const;
   void ResetOverscan(RESOLUTION res, OVERSCAN &overscan);
   void ResetOverscan(RESOLUTION_INFO &resinfo);
   void ResetScreenParameters(RESOLUTION res);
-- 
1.9.3


From 49438256c57d11b415f6a90f653857e7fae16a1d Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 17 Apr 2014 13:38:55 +0100
Subject: [PATCH 75/94] [3D] Support switching to 3D resolutions

Include matching 3D flags (SBS/TAB) in the score of a resolution to switch to, to enable switching to 3d modes.
Also remove the old code that treated 3D modes differently when assigning a score.
---
 xbmc/cores/VideoRenderers/BaseRenderer.cpp | 47 +++++++++++-------------------
 1 file changed, 17 insertions(+), 30 deletions(-)

diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
index 970b822..9ca1be1 100644
--- a/xbmc/cores/VideoRenderers/BaseRenderer.cpp
+++ b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
@@ -222,10 +222,14 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
 RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RESOLUTION current, float& weight)
 {
   RESOLUTION_INFO curr = g_graphicsContext.GetResInfo(current);
+  RENDER_STEREO_MODE stereo_mode = g_graphicsContext.GetStereoMode();
 
   float fRefreshRate = fps;
 
-  float last_diff = fRefreshRate;
+  int c_weight = MathUtils::round_int(RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
+  if (!(stereo_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL) != !(curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ||
+      !(stereo_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL) != !(curr.dwFlags & D3DPRESENTFLAG_MODE3DTB))
+    c_weight += 1000;
 
   // Find closest refresh rate
   for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
@@ -241,40 +245,23 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
     ||  info.fRefreshRate < (fRefreshRate * multiplier / 1.001) - 0.001)
       continue;
 
-    // For 3D choose the closest refresh rate 
-    if(CONF_FLAGS_STEREO_MODE_MASK(m_iFlags))
-    {
-      float diff = (info.fRefreshRate - fRefreshRate);
-      if(diff < 0)
-        diff *= -1.0f;
+    int i_weight = MathUtils::round_int(RefreshWeight(info.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
 
-      if(diff < last_diff)
-      {
-        last_diff = diff;
-        current = (RESOLUTION)i;
-        curr = info;
-      }
-    }
-    else
-    {
-      int c_weight = MathUtils::round_int(RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
-      int i_weight = MathUtils::round_int(RefreshWeight(info.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
+    if (!(stereo_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL) != !(info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ||
+        !(stereo_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL) != !(info.dwFlags & D3DPRESENTFLAG_MODE3DTB))
+      i_weight += 1000;
 
-      // Closer the better, prefer higher refresh rate if the same
-      if ((i_weight <  c_weight)
-      ||  (i_weight == c_weight && info.fRefreshRate > curr.fRefreshRate))
-      {
-        current = (RESOLUTION)i;
-        curr    = info;
-      }
+    // Closer the better, prefer higher refresh rate if the same
+    if ((i_weight <  c_weight)
+    ||  (i_weight == c_weight && info.fRefreshRate > curr.fRefreshRate))
+    {
+      current  = (RESOLUTION)i;
+      curr     = info;
+      c_weight = i_weight;
     }
   }
 
-  // For 3D overwrite weight
-  if(CONF_FLAGS_STEREO_MODE_MASK(m_iFlags))
-    weight = 0;
-  else
-    weight = RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier);
+  weight = RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier);
 
   return current;
 }
-- 
1.9.3


From bd077947037288550a8e68efed630a3477a16564 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 23 Apr 2014 00:05:07 +0100
Subject: [PATCH 76/94] [graphics] Make pixel ratio for 3d modes consistent

Note: Use the stored stereo flags from lists of resolutions.
Use current stereo mode for current resolution.
---
 xbmc/cores/VideoRenderers/BaseRenderer.cpp      | 10 ++++----
 xbmc/guilib/GraphicContext.cpp                  | 32 ++++++++++---------------
 xbmc/guilib/GraphicContext.h                    |  4 ++--
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  8 -------
 4 files changed, 19 insertions(+), 35 deletions(-)

diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
index 9ca1be1..6bf42bf 100644
--- a/xbmc/cores/VideoRenderers/BaseRenderer.cpp
+++ b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
@@ -119,7 +119,7 @@ bool CBaseRenderer::FindResolutionFromOverride(float fps, float& weight, bool fa
 
     for (size_t j = (int)RES_DESKTOP; j < CDisplaySettings::Get().ResolutionInfoSize(); j++)
     {
-      RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)j);
+      RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)j, false);
 
       if (info.iScreenWidth  == curr.iScreenWidth
        && info.iScreenHeight == curr.iScreenHeight
@@ -179,7 +179,7 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
       //get the resolution with the refreshrate closest to 60 hertz
       for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
       {
-        RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i);
+        RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i, false);
 
         if (MathUtils::round_int(info.fRefreshRate) == 60
          && info.iScreenWidth  == curr.iScreenWidth
@@ -200,7 +200,7 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
         CLog::Log(LOGDEBUG, "60 hertz refreshrate not available, choosing highest");
         for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
         {
-          RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i);
+          RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i, false);
 
           if (info.fRefreshRate  >  curr.fRefreshRate
            && info.iScreenWidth  == curr.iScreenWidth
@@ -234,14 +234,14 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
   // Find closest refresh rate
   for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
   {
-    const RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i);
+    const RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i, false);
 
     //discard resolutions that are not the same width and height (and interlaced/3D flags)
     //or have a too low refreshrate
     if (info.iScreenWidth  != curr.iScreenWidth
     ||  info.iScreenHeight != curr.iScreenHeight
     ||  info.iScreen       != curr.iScreen
-    ||  (info.dwFlags & D3DPRESENTFLAG_MODEMASK) != (curr.dwFlags & D3DPRESENTFLAG_MODEMASK)
+    ||  (info.dwFlags & D3DPRESENTFLAG_INTERLACED) != (curr.dwFlags & D3DPRESENTFLAG_INTERLACED)
     ||  info.fRefreshRate < (fRefreshRate * multiplier / 1.001) - 0.001)
       continue;
 
diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index 886b612..40a6362 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -707,32 +707,26 @@ void CGraphicContext::ApplyStateBlock()
   g_Windowing.ApplyStateBlock();
 }
 
-const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res) const
+const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res, bool use_current_3d /*= true*/) const
 {
   RESOLUTION_INFO info = CDisplaySettings::Get().GetResolutionInfo(res);
 
-  if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+  if(use_current_3d ? m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL : (info.dwFlags & D3DPRESENTFLAG_MODE3DTB))
   {
-    if((info.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
-    {
-      info.fPixelRatio     /= 2;
-      info.iBlanking        = 0;
-      info.dwFlags         |= D3DPRESENTFLAG_MODE3DTB;
-    }
+    info.fPixelRatio     /= 2;
+    info.iBlanking        = 0;
+    info.dwFlags         |= D3DPRESENTFLAG_MODE3DTB;
     info.iHeight          = (info.iHeight         - info.iBlanking) / 2;
     info.Overscan.top    /= 2;
     info.Overscan.bottom  = (info.Overscan.bottom - info.iBlanking) / 2;
     info.iSubtitles       = (info.iSubtitles      - info.iBlanking) / 2;
   }
 
-  if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+  if(use_current_3d ? m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL : (info.dwFlags & D3DPRESENTFLAG_MODE3DSBS))
   {
-    if((info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
-    {
-      info.fPixelRatio     *= 2;
-      info.iBlanking        = 0;
-      info.dwFlags         |= D3DPRESENTFLAG_MODE3DSBS;
-    }
+    info.fPixelRatio     *= 2;
+    info.iBlanking        = 0;
+    info.dwFlags         |= D3DPRESENTFLAG_MODE3DSBS;
     info.iWidth           = (info.iWidth         - info.iBlanking) / 2;
     info.Overscan.left   /= 2;
     info.Overscan.right   = (info.Overscan.right - info.iBlanking) / 2;
@@ -740,7 +734,7 @@ const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res) const
   return info;
 }
 
-void CGraphicContext::SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info)
+void CGraphicContext::SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info, bool use_current_3d /*= true*/)
 {
   RESOLUTION_INFO& curr = CDisplaySettings::Get().GetResolutionInfo(res);
   curr.Overscan   = info.Overscan;
@@ -750,16 +744,14 @@ void CGraphicContext::SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info)
   if(info.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
   {
     curr.Overscan.right  = info.Overscan.right  * 2 + info.iBlanking;
-    if((curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
-      curr.fPixelRatio  /= 2.0;
+    curr.fPixelRatio  /= 2.0;
   }
 
   if(info.dwFlags & D3DPRESENTFLAG_MODE3DTB)
   {
     curr.Overscan.bottom = info.Overscan.bottom * 2 + info.iBlanking;
     curr.iSubtitles      = info.iSubtitles      * 2 + info.iBlanking;
-    if((curr.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
-      curr.fPixelRatio  *= 2.0;
+    curr.fPixelRatio  *= 2.0;
   }
 }
 
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index df55e92..c77f2ff 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -124,8 +124,8 @@ class CGraphicContext : public CCriticalSection,
   {
     return GetResInfo(m_Resolution);
   }
-  const RESOLUTION_INFO GetResInfo(RESOLUTION res) const;
-  void SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info);
+  const RESOLUTION_INFO GetResInfo(RESOLUTION res, bool use_current_3d = true) const;
+  void SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info, bool use_current_3d = true);
 
   /* \brief Get UI scaling information from a given resolution to the screen resolution.
    Takes account of overscan and UI zooming.
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index 21b8cc4..f57b22b 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -446,15 +446,9 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
       m_desktopRes.fPixelRatio  = get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
       // Also add 3D flags
       if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_SBS_HALF)
-      {
         m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
-        m_desktopRes.fPixelRatio *= 2.0;
-      }
       else if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_TB_HALF)
-      {
         m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
-        m_desktopRes.fPixelRatio *= 0.5;
-      }
       HDMI_PROPERTY_PARAM_T property;
       property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
       vc_tv_hdmi_get_property(&property);
@@ -605,7 +599,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        res2.fPixelRatio   *= 2.0f;
         SetResolutionString(res2);
         CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
 
@@ -623,7 +616,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        res2.fPixelRatio   *= 0.5f;
         SetResolutionString(res2);
         CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
 
-- 
1.9.3


From 7464b116a5db0be0c2b50314fcf703529d6f646e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 23 Apr 2014 21:07:51 +0100
Subject: [PATCH 77/94] [PiSink] More attempts to reduce underrun audio
 glitches with multichannl and high samplerate

---
 xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 79 +++++++++++--------------------
 1 file changed, 27 insertions(+), 52 deletions(-)

diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
index 070e6eb..133b9f6 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
@@ -186,7 +186,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
   format.m_frameSize     = sample_size * channels;
   format.m_sampleRate    = std::max(8000U, std::min(192000U, format.m_sampleRate));
-  format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER;
+  format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER / NUM_OMX_BUFFERS;
   format.m_frameSamples  = format.m_frames * channels;
 
   SetAudioProps(m_passthrough, GetChannelMap(format.m_channelLayout, m_passthrough));
@@ -232,7 +232,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
     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)NUM_OMX_BUFFERS);
-  port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames / port_param.nBufferCountActual;
+  port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames;
 
   omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
   if (omx_err != OMX_ErrorNone)
@@ -308,63 +308,38 @@ double CAESinkPi::GetCacheTotal()
 
 unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
 {
-  unsigned int sent = 0;
-
-  if (!m_Initialized)
+  if (!m_Initialized || !frames)
     return frames;
 
   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
   OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
-  while (sent < frames)
+
+  double delay = GetDelay();
+  if (delay <= 0.0 && m_submitted)
+    CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
+
+  omx_buffer = m_omx_render.GetInputBuffer(1000);
+  if (omx_buffer == NULL)
   {
-    double delay = GetDelay();
-    double ideal_submission_time = AUDIO_PLAYBUFFER - delay;
-    // ideal amount of audio we'd like submit (to make delay match AUDIO_PLAYBUFFER)
-    int timeout = blocking ? 1000 : 0;
-    int ideal_submission_samples = ideal_submission_time / (m_sinkbuffer_sec_per_byte * m_format.m_frameSize);
-    // if we are almost full then sleep (to avoid repeatedly sending a few samples)
-    bool too_laggy = ideal_submission_time < 0.25 * AUDIO_PLAYBUFFER;
-    int sleeptime = (int)(AUDIO_PLAYBUFFER * 0.25 * 1000.0);
-    if (too_laggy)
-    {
-      if (blocking)
-      {
-        Sleep(sleeptime);
-        continue;
-      }
-      break;
-    }
-    omx_buffer = m_omx_render.GetInputBuffer(timeout);
-    if (omx_buffer == NULL)
-    {
-      if (blocking)
-        CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
-      break;
-    }
-
-    unsigned int space = omx_buffer->nAllocLen / m_format.m_frameSize;
-    unsigned int samples = std::min(std::min(space, (unsigned int)ideal_submission_samples), frames - sent);
-
-    omx_buffer->nFilledLen = samples * m_format.m_frameSize;
-    omx_buffer->nTimeStamp = ToOMXTime(0);
-    omx_buffer->nFlags = 0;
-    memcpy(omx_buffer->pBuffer, (uint8_t *)data + sent * m_format.m_frameSize, omx_buffer->nFilledLen);
-
-    sent += samples;
-
-    if (sent == frames)
-      omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
-
-    if (delay <= 0.0 && m_submitted)
-      CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
-
-    omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
-    if (omx_err != OMX_ErrorNone)
-      CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
-    m_submitted++;
+    CLog::Log(LOGERROR, "CAESinkPi::AddPackets timeout");
+    return 0;
   }
 
-  return sent;
+  omx_buffer->nFilledLen = frames * m_format.m_frameSize;
+  // must be true
+  assert(omx_buffer->nFilledLen <= omx_buffer->nAllocLen);
+  omx_buffer->nTimeStamp = ToOMXTime(0);
+  omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
+  memcpy(omx_buffer->pBuffer, data, omx_buffer->nFilledLen);
+
+  omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
+  if (omx_err != OMX_ErrorNone)
+    CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
+  m_submitted++;
+  delay = GetDelay();
+  if (delay > AUDIO_PLAYBUFFER)
+    Sleep((int)(1000.0f * (delay - AUDIO_PLAYBUFFER)));
+  return frames;
 }
 
 void CAESinkPi::Drain()
-- 
1.9.3


From 508c32de290d09c429d73b2497408b930550f1a3 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 23 Apr 2014 22:36:01 +0100
Subject: [PATCH 78/94] [omxplayer] Fix for aspect ratio of portrait videos

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index e9f86f3..7e2c644 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -651,6 +651,16 @@ void OMXPlayerVideo::SetVideoRect(const CRect &InSrcRect, const CRect &InDestRec
   bool stereo_invert                   = (flags & CONF_FLAGS_STEREO_CADANCE_RIGHT_LEFT) ? true : false;
   RENDER_STEREO_MODE display_stereo_mode = g_graphicsContext.GetStereoMode();
 
+  // fix up transposed video
+  if (m_hints.orientation == 90 || m_hints.orientation == 270)
+  {
+    float diff = (DestRect.Height() - DestRect.Width()) * 0.5f;
+    DestRect.x1 -= diff;
+    DestRect.x2 += diff;
+    DestRect.y1 += diff;
+    DestRect.y2 -= diff;
+  }
+
   // check if destination rect or video view mode has changed
   if (!(m_dst_rect != DestRect) && !(m_src_rect != SrcRect) && m_video_stereo_mode == video_stereo_mode && m_display_stereo_mode == display_stereo_mode && m_StereoInvert == stereo_invert)
     return;
-- 
1.9.3


From fe9fea7a1aac545aa601b71aae01651bc42a5376 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 22 Apr 2014 12:23:23 +0100
Subject: [PATCH 79/94] [omxplayer] Make dvdplayer the default for dvd images

---
 xbmc/cores/omxplayer/omxplayer_advancedsettings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml
index 77c6a15..51c0daf 100644
--- a/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml
+++ b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml
@@ -2,6 +2,6 @@
 <advancedsettings>
   <video>
     <defaultplayer>omxplayer</defaultplayer>
-    <defaultdvdplayer>omxplayer</defaultdvdplayer>
+    <defaultdvdplayer>dvdplayer</defaultdvdplayer>
   </video>
 </advancedsettings>
-- 
1.9.3


From f417d7d303eaa5113edba8ba562cb61ed8a6c59a Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 26 Apr 2014 17:27:52 +0100
Subject: [PATCH 80/94] [cec] Don't suspend pi on tv switch off - it can't wake
 up

---
 system/peripherals.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/system/peripherals.xml b/system/peripherals.xml
index a906628..9b5271a 100644
--- a/system/peripherals.xml
+++ b/system/peripherals.xml
@@ -16,7 +16,7 @@
     <setting key="send_inactive_source" type="bool" value="1" label="36025" order="5" />
     <setting key="cec_standby_screensaver" type="bool" value="0" label="36009" order="6" />
     <setting key="cec_wake_screensaver" type="bool" value="1" label="36010" order="7" />
-    <setting key="standby_pc_on_tv_standby" type="enum" value="13011" label="36029" order="8" lvalues="36028|13005|13011" />
+    <setting key="standby_pc_on_tv_standby" type="enum" value="36028" label="36029" order="8" lvalues="36028|13005|13011" />
     <setting key="standby_tv_on_pc_standby" type="bool" value="1" label="36026" order="9" />
     <setting key="use_tv_menu_language" type="bool" value="1" label="36018" order="10" />
     <setting key="pause_playback_on_deactivate" type="bool" value="1" label="36033" order="11" />
-- 
1.9.3


From 0563f1df1295ac5600fd330fb201e854ad900e02 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 12 Apr 2014 17:57:19 +0100
Subject: [PATCH 81/94] [omxplayer] Ignore occasionally valid pts values, they
 cause live tv stutter

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index 7e2c644..b3786f6 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -490,7 +490,7 @@ void OMXPlayerVideo::Process()
         // if a stream has had more than 4 valid pts values in the last 16, the use UNKNOWN, otherwise use dts
         m_history_valid_pts = (m_history_valid_pts << 1) | (pPacket->pts != DVD_NOPTS_VALUE);
         double pts = pPacket->pts;
-        if(pPacket->pts == DVD_NOPTS_VALUE && count_bits(m_history_valid_pts & 0xffff) < 4)
+        if(count_bits(m_history_valid_pts & 0xffff) < 4)
           pts = pPacket->dts;
 
         if (pts != DVD_NOPTS_VALUE)
-- 
1.9.3


From cddc5f27f9aa11dfb65e16ec1f84a809cf79c68b Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 7 May 2014 14:54:41 +0100
Subject: [PATCH 82/94] [Pi] Fix naming of refresh rates to avoid lost
 calibration settings

---
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp | 13 ++++---------
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h   |  2 +-
 2 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index f57b22b..5b26b20 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -58,6 +58,7 @@
 # define DLOG(fmt, args...)
 #endif
 
+static void SetResolutionString(RESOLUTION_INFO &res);
 
 CEGLNativeTypeRaspberryPI::CEGLNativeTypeRaspberryPI()
 {
@@ -194,8 +195,9 @@ int CEGLNativeTypeRaspberryPI::FindMatchingResolution(const RESOLUTION_INFO &res
 #endif
 
 #if defined(TARGET_RASPBERRY_PI)
-int CEGLNativeTypeRaspberryPI::AddUniqueResolution(const RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions)
+int CEGLNativeTypeRaspberryPI::AddUniqueResolution(RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions)
 {
+  SetResolutionString(res);
   int i = FindMatchingResolution(res, resolutions);
   if (i>=0)
   {  // don't replace a progressive resolution with an interlaced one of same resolution
@@ -467,10 +469,7 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
       m_desktopRes.fPixelRatio  = get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
     }
 
-    m_desktopRes.strMode = StringUtils::Format("%dx%d", m_desktopRes.iScreenWidth, m_desktopRes.iScreenHeight);
-
-    if((int)m_desktopRes.fRefreshRate > 1)
-      SetResolutionString(m_desktopRes);
+    SetResolutionString(m_desktopRes);
 
     m_initDesktopRes = false;
 
@@ -578,8 +577,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
       res.iScreenHeight = tv->height;
       res.fPixelRatio   = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res.iScreenWidth / (float)res.iScreenHeight);
 
-      SetResolutionString(res);
-
       CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) %s%s:%x\n", i, res.strMode.c_str(), res.fPixelRatio,
           tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
 
@@ -599,7 +596,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        SetResolutionString(res2);
         CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
 
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
@@ -616,7 +612,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        SetResolutionString(res2);
         CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
 
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
index d1ebb81..59401f5 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
@@ -71,6 +71,6 @@ class CEGLNativeTypeRaspberryPI : public CEGLNativeType
 
   void DestroyDispmaxWindow();
   int FindMatchingResolution(const RESOLUTION_INFO &res, const std::vector<RESOLUTION_INFO> &resolutions);
-  int AddUniqueResolution(const RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions);
+  int AddUniqueResolution(RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions);
 #endif
 };
-- 
1.9.3


From a4c36a4925e780b63d9821fb04504453ac982205 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 10 May 2014 11:40:41 +0100
Subject: [PATCH 83/94] [omxplayer] Skip out of submit loop when closing.

Avoids a permanent hang at EOF when using IPTV streams
---
 xbmc/cores/omxplayer/OMXPlayerAudio.cpp | 7 +++++--
 xbmc/cores/omxplayer/OMXPlayerAudio.h   | 1 +
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 7 +++++--
 xbmc/cores/omxplayer/OMXPlayerVideo.h   | 1 +
 4 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
index d3348ec..4435d22 100644
--- a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
@@ -89,6 +89,7 @@ OMXPlayerAudio::OMXPlayerAudio(OMXClock *av_clock, CDVDMessageQueue& parent)
   m_hw_decode = false;
   m_silence = false;
   m_flush = false;  
+  m_bClose = false;
 }
 
 
@@ -145,11 +146,13 @@ void OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec)
   m_format.m_dataFormat    = GetDataFormat(m_hints);
   m_format.m_sampleRate    = 0;
   m_format.m_channelLayout = 0;
+  m_bClose = false;
 }
 
 bool OMXPlayerAudio::CloseStream(bool bWaitForBuffers)
 {
   // wait until buffers are empty
+  m_bClose = true;
   if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
 
   m_messageQueue.Abort();
@@ -259,8 +262,8 @@ bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
 
       while(!m_bStop)
       {
-        // discard if flushing as clocks may be stopped and we'll never submit it
-        if(m_flush)
+        // discard if flushing or closing as clocks may be stopped and we'll never submit it
+        if(m_flush || m_bClose)
           break;
 
         if(m_omxAudio.GetSpace() < (unsigned int)decoded_size)
diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.h b/xbmc/cores/omxplayer/OMXPlayerAudio.h
index 685a686..7b55e48 100644
--- a/xbmc/cores/omxplayer/OMXPlayerAudio.h
+++ b/xbmc/cores/omxplayer/OMXPlayerAudio.h
@@ -70,6 +70,7 @@ class OMXPlayerAudio : public CThread
   bool                      m_DecoderOpen;
 
   bool                      m_bad_state;
+  bool                      m_bClose;
 
   virtual void OnStartup();
   virtual void OnExit();
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index b3786f6..61b884e 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -104,6 +104,7 @@ OMXPlayerVideo::OMXPlayerVideo(OMXClock *av_clock,
   m_iCurrentPts = DVD_NOPTS_VALUE;
   m_nextOverlay = DVD_NOPTS_VALUE;
   m_flush = false;
+  m_bClose = false;
   m_history_valid_pts = 0;
 }
 
@@ -118,6 +119,7 @@ bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints)
   m_hdmi_clock_sync = (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF);
   m_started     = false;
   m_flush       = false;
+  m_bClose      = false;
   m_stalled     = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
   m_nextOverlay = DVD_NOPTS_VALUE;
   // force SetVideoRect to be called initially
@@ -161,6 +163,7 @@ bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints, COMXVideo *codec)
 bool OMXPlayerVideo::CloseStream(bool bWaitForBuffers)
 {
   // wait until buffers are empty
+  m_bClose = true;
   if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
 
   m_messageQueue.Abort();
@@ -469,8 +472,8 @@ void OMXPlayerVideo::Process()
 
       while (!m_bStop)
       {
-        // discard if flushing as clocks may be stopped and we'll never submit it
-        if (m_flush)
+        // discard if flushing or closing as clocks may be stopped and we'll never submit it
+        if (m_flush || m_bClose)
            break;
 
         if((int)m_omxVideo.GetFreeSpace() < pPacket->iSize)
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.h b/xbmc/cores/omxplayer/OMXPlayerVideo.h
index 6f19395..8eff32f 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.h
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.h
@@ -66,6 +66,7 @@ class OMXPlayerVideo : public CThread
 
   float                     m_fForcedAspectRatio;
   unsigned                  m_flags;
+  bool                      m_bClose;
 
   CRect                     m_src_rect;
   CRect                     m_dst_rect;
-- 
1.9.3


From b90562b8ffc29c1d9e037e94b0c92b3b0b67413b Mon Sep 17 00:00:00 2001
From: xbmc <fernetmenta@online.de>
Date: Mon, 28 May 2012 10:34:39 +0200
Subject: [PATCH 84/94] videoplayer: adapt lateness detection and dropping to
 buffering

---
 xbmc/cores/VideoRenderers/RenderManager.cpp        |  16 +-
 xbmc/cores/VideoRenderers/RenderManager.h          |  12 +-
 .../dvdplayer/DVDCodecs/Video/DVDVideoCodec.h      |  38 +++-
 .../DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp        |  41 +++++
 .../DVDCodecs/Video/DVDVideoCodecFFmpeg.h          |   7 +
 xbmc/cores/dvdplayer/DVDPlayerVideo.cpp            | 197 +++++++++++++++++----
 xbmc/cores/dvdplayer/DVDPlayerVideo.h              |  23 +++
 7 files changed, 296 insertions(+), 38 deletions(-)

diff --git a/xbmc/cores/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoRenderers/RenderManager.cpp
index 3503988..4ca74f8 100644
--- a/xbmc/cores/VideoRenderers/RenderManager.cpp
+++ b/xbmc/cores/VideoRenderers/RenderManager.cpp
@@ -286,6 +286,8 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi
     m_bIsStarted = true;
     m_bReconfigured = true;
     m_presentstep = PRESENT_IDLE;
+    m_presentpts = DVD_NOPTS_VALUE;
+    m_sleeptime = 1.0;
     m_presentevent.notifyAll();
 
     m_firstFlipPage = false;  // tempfix
@@ -629,7 +631,7 @@ void CXBMCRenderManager::SetViewMode(int iViewMode)
     m_pRenderer->SetViewMode(iViewMode);
 }
 
-void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
+void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, double pts /* = 0 */, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
 {
   { CSharedLock lock(m_sharedSection);
 
@@ -697,6 +699,7 @@ void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0L
     m.timestamp     = timestamp;
     m.presentfield  = sync;
     m.presentmethod = presentmethod;
+    m.pts           = pts;
     requeue(m_queued, m_free);
 
     /* signal to any waiters to check state */
@@ -1065,6 +1068,8 @@ void CXBMCRenderManager::PrepareNextRender()
     m_discard.push_back(m_presentsource);
     m_presentsource = idx;
     m_queued.pop_front();
+    m_sleeptime = m_Queue[idx].timestamp - clocktime;
+    m_presentpts = m_Queue[idx].pts;
     m_presentevent.notifyAll();
   }
 }
@@ -1081,3 +1086,12 @@ void CXBMCRenderManager::DiscardBuffer()
     m_presentstep   = PRESENT_IDLE;
   m_presentevent.notifyAll();
 }
+
+bool CXBMCRenderManager::GetStats(double &sleeptime, double &pts, int &bufferLevel)
+{
+  CSingleLock lock(m_presentlock);
+  sleeptime = m_sleeptime;
+  pts = m_presentpts;
+  bufferLevel = m_queued.size() + m_discard.size();
+  return true;
+}
diff --git a/xbmc/cores/VideoRenderers/RenderManager.h b/xbmc/cores/VideoRenderers/RenderManager.h
index c469795..949c652b 100644
--- a/xbmc/cores/VideoRenderers/RenderManager.h
+++ b/xbmc/cores/VideoRenderers/RenderManager.h
@@ -98,10 +98,11 @@ class CXBMCRenderManager
    *
    * @param bStop reference to stop flag of calling thread
    * @param timestamp of frame delivered with AddVideoPicture
+   * @param pts used for lateness detection
    * @param source depreciated
    * @param sync signals frame, top, or bottom field
    */
-  void FlipPage(volatile bool& bStop, double timestamp = 0.0, int source = -1, EFIELDSYNC sync = FS_NONE);
+  void FlipPage(volatile bool& bStop, double timestamp = 0.0, double pts = 0.0, int source = -1, EFIELDSYNC sync = FS_NONE);
   unsigned int PreInit();
   void UnInit();
   bool Flush();
@@ -176,6 +177,12 @@ class CXBMCRenderManager
   int WaitForBuffer(volatile bool& bStop, int timeout = 100);
 
   /**
+   * Can be called by player for lateness detection. This is done best by
+   * looking at the end of the queue.
+   */
+  bool GetStats(double &sleeptime, double &pts, int &bufferLevel);
+
+  /**
    * Video player call this on flush in oder to discard any queued frames
    */
   void DiscardBuffer();
@@ -222,6 +229,7 @@ class CXBMCRenderManager
 
   struct SPresent
   {
+    double         pts;
     double         timestamp;
     EFIELDSYNC     presentfield;
     EPRESENTMETHOD presentmethod;
@@ -233,6 +241,8 @@ class CXBMCRenderManager
 
   ERenderFormat   m_format;
 
+  double     m_sleeptime;
+  double     m_presentpts;
   double     m_presentcorr;
   double     m_presenterr;
   double     m_errorbuff[ERRORBUFFSIZE];
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
index dc047d7..c09939c 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
@@ -129,6 +129,10 @@ struct DVDVideoUserData
 #define DVP_FLAG_NOSKIP             0x00000010 // indicate this picture should never be dropped
 #define DVP_FLAG_DROPPED            0x00000020 // indicate that this picture has been dropped in decoder stage, will have no data
 
+#define DVP_FLAG_DROPDEINT          0x00000040 // indicate that this picture was requested to have been dropped in deint stage
+#define DVP_FLAG_NO_POSTPROC        0x00000100 // see GetCodecStats
+#define DVP_FLAG_DRAIN              0x00000200 // see GetCodecStats
+
 // DVP_FLAG 0x00000100 - 0x00000f00 is in use by libmpeg2!
 
 #define DVP_QSCALE_UNKNOWN          0
@@ -146,6 +150,8 @@ class CDVDCodecOptions;
 #define VC_PICTURE  0x00000004  // the decoder got a picture, call Decode(NULL, 0) again to parse the rest of the data
 #define VC_USERDATA 0x00000008  // the decoder found some userdata,  call Decode(NULL, 0) again to parse the rest of the data
 #define VC_FLUSHED  0x00000010  // the decoder lost it's state, we need to restart decoding again
+#define VC_DROPPED  0x00000020  // needed to identify if a picture was dropped
+
 class CDVDVideoCodec
 {
 public:
@@ -263,7 +269,6 @@ class CDVDVideoCodec
     return 0;
   }
 
-
   /**
    * Number of references to old pictures that are allowed to
    * be retained when calling decode on the next demux packet
@@ -280,4 +285,35 @@ class CDVDVideoCodec
   * Interact with user settings so that user disabled codecs are disabled
   */
   static bool IsCodecDisabled(DVDCodecAvailableType* map, unsigned int size, AVCodecID id);
+
+   /* For calculation of dropping requirements player asks for some information.
+   *
+   * - pts : right after decoder, used to detect gaps (dropped frames in decoder)
+   * - droppedPics : indicates if decoder has dropped a picture
+   *                 -1 means that decoder has no info on this.
+   *
+   * If codec does not implement this method, pts of decoded frame at input
+   * video player is used. In case decoder does post-proc and de-interlacing there
+   * may be quite some frames queued up between exit decoder and entry player.
+   */
+  virtual bool GetCodecStats(double &pts, int &droppedPics)
+  {
+    droppedPics= -1;
+    return false;
+  }
+
+  /**
+   * Codec can be informed by player with the following flags:
+   *
+   * DVP_FLAG_NO_POSTPROC : if speed is not normal the codec can switch off
+   *                        postprocessing and de-interlacing
+   *
+   * DVP_FLAG_DRAIN : codecs may do postprocessing and de-interlacing.
+   *                  If video buffers in RenderManager are about to run dry,
+   *                  this is signaled to codec. Codec can wait for post-proc
+   *                  to be finished instead of returning empty and getting another
+   *                  packet.
+   *
+   */
+  virtual void SetCodecControl(int flags) {}
 };
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
index 9b6a34d..81fe0cf 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
@@ -164,6 +164,7 @@ CDVDVideoCodecFFmpeg::CDVDVideoCodecFFmpeg() : CDVDVideoCodec()
   m_iLastKeyframe = 0;
   m_dts = DVD_NOPTS_VALUE;
   m_started = false;
+  m_decoderPts = DVD_NOPTS_VALUE;
 }
 
 CDVDVideoCodecFFmpeg::~CDVDVideoCodecFFmpeg()
@@ -355,6 +356,14 @@ void CDVDVideoCodecFFmpeg::SetDropState(bool bDrop)
 {
   if( m_pCodecContext )
   {
+    if (bDrop && m_pHardware && m_pHardware->CanSkipDeint())
+    {
+      m_requestSkipDeint = true;
+      bDrop = false;
+    }
+    else
+      m_requestSkipDeint = false;
+
     // i don't know exactly how high this should be set
     // couldn't find any good docs on it. think it varies
     // from codec to codec on what it does
@@ -556,6 +565,7 @@ int CDVDVideoCodecFFmpeg::Decode(uint8_t* pData, int iSize, double dts, double p
 void CDVDVideoCodecFFmpeg::Reset()
 {
   m_started = false;
+  m_decoderPts = DVD_NOPTS_VALUE;
   m_iLastKeyframe = m_pCodecContext->has_b_frames;
   m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);
 
@@ -665,6 +675,22 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(DVDVideoPicture* pDvdVideoPicture)
   else
     pDvdVideoPicture->pts = DVD_NOPTS_VALUE;
 
+  if (pDvdVideoPicture->pts != DVD_NOPTS_VALUE)
+    m_decoderPts = pDvdVideoPicture->pts;
+  else
+    m_decoderPts = m_dts;
+
+  if (m_requestSkipDeint)
+  {
+    pDvdVideoPicture->iFlags |= DVP_FLAG_DROPDEINT;
+    m_skippedDeint = 1;
+  }
+  else
+    m_skippedDeint = 0;
+
+  m_requestSkipDeint = false;
+  pDvdVideoPicture->iFlags |= m_codecControlFlags;
+
   if(!m_started)
     pDvdVideoPicture->iFlags |= DVP_FLAG_DROPPED;
 
@@ -924,3 +950,18 @@ unsigned CDVDVideoCodecFFmpeg::GetAllowedReferences()
   else
     return 0;
 }
+
+bool CDVDVideoCodecFFmpeg::GetCodecStats(double &pts, int &droppedPics)
+{
+  pts = m_decoderPts;
+  if (m_skippedDeint)
+    droppedPics = m_skippedDeint;
+  else
+    droppedPics = -1;
+  return true;
+}
+
+void CDVDVideoCodecFFmpeg::SetCodecControl(int flags)
+{
+  m_codecControlFlags = flags;
+}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h
index 2287031..827c9507 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h
@@ -45,6 +45,7 @@ class CDVDVideoCodecFFmpeg : public CDVDVideoCodec
     virtual int  Check     (AVCodecContext* avctx) = 0;
     virtual void Reset     () {}
     virtual unsigned GetAllowedReferences() { return 0; }
+    virtual bool CanSkipDeint() {return false; }
     virtual const std::string Name() = 0;
     virtual CCriticalSection* Section() { return NULL; }
   };
@@ -62,6 +63,8 @@ class CDVDVideoCodecFFmpeg : public CDVDVideoCodec
   virtual const char* GetName() { return m_name.c_str(); }; // m_name is never changed after open
   virtual unsigned GetConvergeCount();
   virtual unsigned GetAllowedReferences();
+  virtual bool GetCodecStats(double &pts, int &droppedPics);
+  virtual void SetCodecControl(int flags);
 
   bool               IsHardwareAllowed()                     { return !m_bSoftware; }
   IHardwareDecoder * GetHardware()                           { return m_pHardware; };
@@ -127,4 +130,8 @@ class CDVDVideoCodecFFmpeg : public CDVDVideoCodec
   double m_dts;
   bool   m_started;
   std::vector<PixelFormat> m_formats;
+  double m_decoderPts, m_decoderInterval;
+  int    m_skippedDeint;
+  bool   m_requestSkipDeint;
+  int    m_codecControlFlags;
 };
diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
index fddb7f7..181ff74 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
@@ -38,6 +38,7 @@
 #include "DVDCodecs/DVDCodecs.h"
 #include "DVDCodecs/Overlay/DVDOverlayCodecCC.h"
 #include "DVDCodecs/Overlay/DVDOverlaySSA.h"
+#include "guilib/GraphicContext.h"
 #include <sstream>
 #include <iomanip>
 #include <numeric>
@@ -320,8 +321,10 @@ void CDVDPlayerVideo::Process()
 
   int iDropped = 0; //frames dropped in a row
   bool bRequestDrop = false;
+  int iDropDirective;
 
   m_videoStats.Start();
+  m_droppingStats.Reset();
 
   while (!m_bStop)
   {
@@ -436,6 +439,7 @@ void CDVDPlayerVideo::Process()
       picture.iFlags &= ~DVP_FLAG_ALLOCATED;
       m_packets.clear();
       m_started = false;
+      m_droppingStats.Reset();
     }
     else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush())
     {
@@ -448,6 +452,7 @@ void CDVDPlayerVideo::Process()
       //we need to recalculate the framerate
       //TODO: this needs to be set on a streamchange instead
       ResetFrameRateCalc();
+      m_droppingStats.Reset();
 
       m_stalled = true;
       m_started = false;
@@ -467,6 +472,7 @@ void CDVDPlayerVideo::Process()
         m_iNrOfPicturesNotToSkip = 0;
       if (m_pVideoCodec)
         m_pVideoCodec->SetSpeed(m_speed);
+      m_droppingStats.Reset();
     }
     else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
     {
@@ -515,6 +521,28 @@ void CDVDPlayerVideo::Process()
         m_iNrOfPicturesNotToSkip = 1;
       }
 
+      bRequestDrop = false;
+      iDropDirective = CalcDropRequirement(pts);
+      if (iDropDirective & EOS_VERYLATE)
+      {
+        if (m_bAllowDrop)
+        {
+          m_pullupCorrection.Flush();
+          bRequestDrop = true;
+        }
+      }
+      int codecControl = 0;
+      if (iDropDirective & EOS_BUFFER_LEVEL)
+        codecControl |= DVP_FLAG_DRAIN;
+      if (m_speed > DVD_PLAYSPEED_NORMAL)
+        codecControl |= DVP_FLAG_NO_POSTPROC;
+      m_pVideoCodec->SetCodecControl(codecControl);
+      if (iDropDirective & EOS_DROPPED)
+      {
+        m_iDroppedFrames++;
+        iDropped++;
+      }
+
       if (m_messageQueue.GetDataSize() == 0
       ||  m_speed < 0)
       {
@@ -567,15 +595,7 @@ void CDVDPlayerVideo::Process()
       }
 
       m_videoStats.AddSampleBytes(pPacket->iSize);
-      // assume decoder dropped a picture if it didn't give us any
-      // picture from a demux packet, this should be reasonable
-      // for libavformat as a demuxer as it normally packetizes
-      // pictures when they come from demuxer
-      if(bRequestDrop && !bPacketDrop && (iDecoderState & VC_BUFFER) && !(iDecoderState & VC_PICTURE))
-      {
-        m_iDroppedFrames++;
-        iDropped++;
-      }
+
       // reset the request, the following while loop may break before
       // setting the flag to a new value
       bRequestDrop = false;
@@ -1185,33 +1205,12 @@ int CDVDPlayerVideo::OutputPicture(const DVDVideoPicture* src, double pts)
   m_FlipTimeStamp += max(0.0, iSleepTime);
   m_FlipTimeStamp += iFrameDuration;
 
-  if (iSleepTime <= 0 && m_speed)
-    m_iLateFrames++;
-  else
-    m_iLateFrames = 0;
-
-  // ask decoder to drop frames next round, as we are very late
-  if(m_iLateFrames > 10)
+  if ((pPicture->iFlags & DVP_FLAG_DROPPED))
   {
-    if (!(pPicture->iFlags & DVP_FLAG_NOSKIP))
-    {
-      //if we're calculating the framerate,
-      //don't drop frames until we've calculated a stable framerate
-      if (m_bAllowDrop || m_speed != DVD_PLAYSPEED_NORMAL)
-      {
-        result |= EOS_VERYLATE;
-        m_pullupCorrection.Flush(); //dropped frames mess up the pattern, so just flush it
-      }
-      m_iDroppedRequest++;
-    }
-  }
-  else
-  {
-    m_iDroppedRequest = 0;
-  }
-
-  if( (pPicture->iFlags & DVP_FLAG_DROPPED) )
+    m_droppingStats.AddOutputDropGain(pts, 1/m_fFrameRate);
+    CLog::Log(LOGDEBUG,"%s - dropped in output", __FUNCTION__);
     return result | EOS_DROPPED;
+  }
 
   // set fieldsync if picture is interlaced
   EFIELDSYNC mDisplayField = FS_NONE;
@@ -1244,7 +1243,7 @@ int CDVDPlayerVideo::OutputPicture(const DVDVideoPicture* src, double pts)
   if (index < 0)
     return EOS_DROPPED;
 
-  g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, mDisplayField);
+  g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, pts, -1, mDisplayField);
 
   return result;
 #else
@@ -1544,3 +1543,131 @@ void CDVDPlayerVideo::CalcFrameRate()
     m_iFrameRateCount = 0;
   }
 }
+
+int CDVDPlayerVideo::CalcDropRequirement(double pts)
+{
+  int result = 0;
+  double iSleepTime;
+  double iDecoderPts, iRenderPts;
+  double iInterval;
+  double iGain;
+  double iLateness;
+  bool   bNewFrame;
+  int    iDroppedPics = -1;
+  int    iBufferLevel;
+
+  // get decoder stats
+  if (!m_pVideoCodec->GetCodecStats(iDecoderPts, iDroppedPics))
+    iDecoderPts = pts;
+  if (iDecoderPts == DVD_NOPTS_VALUE)
+    iDecoderPts = pts;
+
+  // get render stats
+  g_renderManager.GetStats(iSleepTime, iRenderPts, iBufferLevel);
+
+  if (iBufferLevel < 0)
+    result |= EOS_BUFFER_LEVEL;
+  else if (iBufferLevel < 2)
+  {
+    result |= EOS_BUFFER_LEVEL;
+    CLog::Log(LOGDEBUG,"CDVDPlayerVideo::CalcDropRequirement - hurry: %d", iBufferLevel);
+  }
+
+  bNewFrame = iDecoderPts != m_droppingStats.m_lastDecoderPts;
+
+  iInterval = 1/m_fFrameRate*(double)DVD_TIME_BASE;
+
+  m_FlipTimeStamp = m_pClock->GetAbsoluteClock() + max(0.0, iSleepTime) + iInterval;
+
+  if (m_stalled)
+    m_iCurrentPts = DVD_NOPTS_VALUE;
+  else
+    m_iCurrentPts = iRenderPts - max(0.0, iSleepTime);
+
+  if (m_droppingStats.m_lastDecoderPts > 0
+      && bNewFrame
+      && m_bAllowDrop)
+  {
+    iGain = (iDecoderPts - m_droppingStats.m_lastDecoderPts - iInterval)/(double)DVD_TIME_BASE;
+    if (iDroppedPics > 0)
+    {
+      CDroppingStats::CGain gain;
+      gain.gain = iDroppedPics * 1/m_fFrameRate;
+      gain.pts = iDecoderPts;
+      m_droppingStats.m_gain.push_back(gain);
+      m_droppingStats.m_totalGain += gain.gain;
+      result |= EOS_DROPPED;
+      m_droppingStats.m_dropRequests = 0;
+      CLog::Log(LOGDEBUG,"CDVDPlayerVideo::CalcDropRequirement - dropped pictures, Sleeptime: %f, Bufferlevel: %d, Gain: %f", iSleepTime, iBufferLevel, iGain);
+    }
+    else if (iDroppedPics < 0 && iGain > 1/m_fFrameRate)
+    {
+      CDroppingStats::CGain gain;
+      gain.gain = iGain;
+      gain.pts = iDecoderPts;
+      m_droppingStats.m_gain.push_back(gain);
+      m_droppingStats.m_totalGain += iGain;
+      result |= EOS_DROPPED;
+      m_droppingStats.m_dropRequests = 0;
+      CLog::Log(LOGDEBUG,"CDVDPlayerVideo::CalcDropRequirement - dropped in decoder, Sleeptime: %f, Bufferlevel: %d, Gain: %f", iSleepTime, iBufferLevel, iGain);
+    }
+  }
+  m_droppingStats.m_lastDecoderPts = iDecoderPts;
+
+  // subtract gains
+  while (!m_droppingStats.m_gain.empty() &&
+         iRenderPts >= m_droppingStats.m_gain.front().pts)
+  {
+    m_droppingStats.m_totalGain -= m_droppingStats.m_gain.front().gain;
+    m_droppingStats.m_gain.pop_front();
+  }
+
+  // calculate lateness
+  iLateness = iSleepTime + m_droppingStats.m_totalGain;
+  if (iLateness < 0 && m_speed)
+  {
+    if (bNewFrame)
+      m_droppingStats.m_lateFrames++;
+
+    // if lateness is smaller than frametime, we observe this state
+    // for 10 cycles
+    if (m_droppingStats.m_lateFrames > 10 || iLateness < -2/m_fFrameRate)
+    {
+      // is frame allowed to skip
+      if (m_iNrOfPicturesNotToSkip <= 0)
+      {
+        if (bNewFrame || m_droppingStats.m_dropRequests < 5)
+        {
+          result |= EOS_VERYLATE;
+        }
+        m_droppingStats.m_dropRequests++;
+      }
+    }
+  }
+  else
+  {
+    m_droppingStats.m_dropRequests = 0;
+    m_droppingStats.m_lateFrames = 0;
+  }
+  m_droppingStats.m_lastRenderPts = iRenderPts;
+  return result;
+}
+
+void CDroppingStats::Reset()
+{
+  m_gain.clear();
+  m_totalGain = 0;
+  m_lastDecoderPts = 0;
+  m_lastRenderPts = 0;
+  m_lateFrames = 0;
+  m_dropRequests = 0;
+}
+
+void CDroppingStats::AddOutputDropGain(double pts, double frametime)
+{
+  CDroppingStats::CGain gain;
+  gain.gain = frametime;
+  gain.pts = pts;
+  m_gain.push_back(gain);
+  m_totalGain += frametime;
+}
diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.h b/xbmc/cores/dvdplayer/DVDPlayerVideo.h
index f8ad541..186e271 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerVideo.h
+++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.h
@@ -36,6 +36,25 @@ class CDVDOverlayCodecCC;
 
 #define VIDEO_PICTURE_QUEUE_SIZE 1
 
+class CDroppingStats
+{
+public:
+  void Reset();
+  void AddOutputDropGain(double pts, double frametime);
+  struct CGain
+  {
+    double gain;
+    double pts;
+  };
+  std::deque<CGain> m_gain;
+  double m_totalGain;
+  double m_lastDecoderPts;
+  double m_lastRenderPts;
+  unsigned int m_lateFrames;
+  unsigned int m_dropRequests;
+};
+
+
 class CDVDPlayerVideo : public CThread
 {
 public:
@@ -104,6 +123,7 @@ class CDVDPlayerVideo : public CThread
 #define EOS_ABORT 1
 #define EOS_DROPPED 2
 #define EOS_VERYLATE 4
+#define EOS_BUFFER_LEVEL 8
 
   void AutoCrop(DVDVideoPicture* pPicture);
   void AutoCrop(DVDVideoPicture *pPicture, RECT &crop);
@@ -129,6 +149,7 @@ class CDVDPlayerVideo : public CThread
 
   void   ResetFrameRateCalc();
   void   CalcFrameRate();
+  int    CalcDropRequirement(double pts);
 
   double m_fFrameRate;       //framerate of the video currently playing
   bool   m_bCalcFrameRate;  //if we should calculate the framerate from the timestamps
@@ -182,5 +203,7 @@ class CDVDPlayerVideo : public CThread
   CPullupCorrection m_pullupCorrection;
 
   std::list<DVDMessageListItem> m_packets;
+
+  CDroppingStats m_droppingStats;
 };
 
-- 
1.9.3


From 16d2c2ee305eb2cd3ec42fd0aad474dbf356d75d Mon Sep 17 00:00:00 2001
From: xbmc <fernetmenta@online.de>
Date: Sun, 2 Sep 2012 16:05:21 +0200
Subject: [PATCH 85/94] video player: present correct pts to user for a/v sync
 (after buffering in renderer)

---
 xbmc/cores/dvdplayer/DVDPlayerVideo.cpp | 16 ++++++++++++++++
 xbmc/cores/dvdplayer/DVDPlayerVideo.h   |  2 +-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
index 181ff74..01757cc 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
@@ -1463,6 +1463,22 @@ void CDVDPlayerVideo::ResetFrameRateCalc()
                         g_advancedSettings.m_videoFpsDetect == 0;
 }
 
+double CDVDPlayerVideo::GetCurrentPts()
+{
+  double iSleepTime, iRenderPts;
+  int iBufferLevel;
+
+  // get render stats
+  g_renderManager.GetStats(iSleepTime, iRenderPts, iBufferLevel);
+
+  if( m_stalled )
+    iRenderPts = DVD_NOPTS_VALUE;
+  else
+    iRenderPts = iRenderPts - max(0.0, iSleepTime);
+
+  return iRenderPts;
+}
+
 #define MAXFRAMERATEDIFF   0.01
 #define MAXFRAMESERR    1000
 
diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.h b/xbmc/cores/dvdplayer/DVDPlayerVideo.h
index 186e271..59c7f09 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerVideo.h
+++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.h
@@ -100,7 +100,7 @@ class CDVDPlayerVideo : public CThread
 
   bool InitializedOutputDevice();
 
-  double GetCurrentPts()                           { return m_iCurrentPts; }
+  double GetCurrentPts();
   int    GetPullupCorrection()                     { return m_pullupCorrection.GetPatternLength(); }
 
   double GetOutputDelay(); /* returns the expected delay, from that a packet is put in queue */
-- 
1.9.3


From afa38b57afee02720263e2db79d20e1411461433 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 12 May 2014 23:06:43 +0100
Subject: [PATCH 86/94] [omxcodec] Updates to work better with dropping and
 lateness detection

---
 .../DVDCodecs/Video/DVDVideoCodecOpenMax.cpp       |  5 ++
 .../DVDCodecs/Video/DVDVideoCodecOpenMax.h         |  1 +
 .../dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp     | 95 ++++++++++++++++------
 .../cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h |  9 +-
 4 files changed, 84 insertions(+), 26 deletions(-)

diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
index ef10555..8323497 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp
@@ -91,4 +91,9 @@ bool CDVDVideoCodecOpenMax::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
   return m_omx_decoder->ClearPicture(pDvdVideoPicture);
 }
 
+bool CDVDVideoCodecOpenMax::GetCodecStats(double &pts, int &droppedPics)
+{
+  return m_omx_decoder->GetCodecStats(pts, droppedPics);
+}
+
 #endif
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
index b7c0c1b..4f243df 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h
@@ -41,6 +41,7 @@ class CDVDVideoCodecOpenMax : public CDVDVideoCodec
   virtual unsigned GetAllowedReferences();
   virtual void SetDropState(bool bDrop);
   virtual const char* GetName(void);
+  virtual bool GetCodecStats(double &pts, int &droppedPics);
   
 protected:
   OpenMaxVideoPtr m_omx_decoder;
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
index 71d19af..93cf521 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
@@ -37,6 +37,7 @@
 #include "ApplicationMessenger.h"
 #include "Application.h"
 #include "threads/Atomics.h"
+#include "guilib/GUIWindowManager.h"
 
 #include <IL/OMX_Core.h>
 #include <IL/OMX_Component.h>
@@ -57,6 +58,7 @@
 
 #define OMX_BUFFERFLAG_PTS_INVALID (1<<28)
 #define OMX_BUFFERFLAG_DROPPED     (1<<29)
+#define OMX_BUFFERFLAG_FIRST_FIELD (1<<30)
 
 COpenMaxVideoBuffer::COpenMaxVideoBuffer(COpenMaxVideo *omv)
     : m_omv(omv), m_refs(0)
@@ -139,8 +141,11 @@ COpenMaxVideo::COpenMaxVideo()
 
   m_deinterlace = false;
   m_deinterlace_request = VS_DEINTERLACEMODE_OFF;
-  m_deinterlace_second_field = false;
   m_startframe = false;
+  m_decoderPts = DVD_NOPTS_VALUE;
+  m_droppedPics = 0;
+  m_decode_frame_number = 1;
+  m_skipDeinterlaceFields = false;
 }
 
 COpenMaxVideo::~COpenMaxVideo()
@@ -369,7 +374,10 @@ void COpenMaxVideo::Dispose()
   m_finished = true;
   pthread_mutex_unlock(&m_omx_output_mutex);
   if (done)
+  {
+    assert(m_dts_queue.empty());
     m_myself.reset();
+  }
 }
 
 void COpenMaxVideo::SetDropState(bool bDrop)
@@ -730,6 +738,7 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
       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->pMarkData = (OMX_PTR)m_decode_frame_number;
       memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen);
 
       demuxer_bytes -= omx_buffer->nFilledLen;
@@ -742,12 +751,18 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
         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)
+      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_DECODEONLY | OMX_BUFFERFLAG_DROPPED;
+        m_droppedPics += m_deinterlace ? 2:1;
+      }
+      // always set this flag on input. It won't be set on second field of interlaced video.
+      omx_buffer->nFlags |= OMX_BUFFERFLAG_FIRST_FIELD;
 
 #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);
+      CLog::Log(LOGDEBUG, "%s::%s - %-6d dts:%.3f pts:%.3f flags:%x frame:%d",
+        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, (int)omx_buffer->pMarkData);
 #endif
 
       omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
@@ -758,13 +773,16 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
       }
       if (demuxer_bytes == 0)
       {
+        m_decode_frame_number++;
         m_startframe = true;
 #ifdef DTS_QUEUE
         if (!m_drop_state)
         {
           // only push if we are successful with feeding OMX_EmptyThisBuffer
+          pthread_mutex_lock(&m_omx_output_mutex);
           m_dts_queue.push(dts);
           assert(m_dts_queue.size() < 32);
+          pthread_mutex_unlock(&m_omx_output_mutex);
         }
 #endif
         if (buffer_to_free)
@@ -840,13 +858,18 @@ void COpenMaxVideo::Reset(void)
   SetDropState(true);
   SetDropState(false);
 #ifdef DTS_QUEUE
+  pthread_mutex_lock(&m_omx_output_mutex);
   while (!m_dts_queue.empty())
     m_dts_queue.pop();
+  pthread_mutex_unlock(&m_omx_output_mutex);
 #endif
 
   while (!m_demux_queue.empty())
     m_demux_queue.pop();
   m_startframe = false;
+  m_decoderPts = DVD_NOPTS_VALUE;
+  m_droppedPics = 0;
+  m_decode_frame_number = 1;
 }
 
 
@@ -928,26 +951,17 @@ bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
       }
     }
 
-#ifdef DTS_QUEUE
-    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
+    pDvdVideoPicture->dts = buffer->dts;
     pDvdVideoPicture->pts = FromOMXTime(buffer->omx_buffer->nTimeStamp);
     pDvdVideoPicture->openMaxBuffer->Acquire();
     pDvdVideoPicture->iFlags  = DVP_FLAG_ALLOCATED;
     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__,
+    CLog::Log(LOGINFO, "%s::%s dts:%.3f pts:%.3f flags:%x:%x frame:%d 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);
+        pDvdVideoPicture->iFlags, buffer->omx_buffer->nFlags, (int)buffer->omx_buffer->pMarkData, 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)));
   }
@@ -956,6 +970,12 @@ bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture)
     CLog::Log(LOGERROR, "%s::%s - called but m_omx_output_ready is empty", CLASSNAME, __func__);
     return false;
   }
+
+  if (pDvdVideoPicture->pts != DVD_NOPTS_VALUE)
+    m_decoderPts = pDvdVideoPicture->pts;
+  else
+    m_decoderPts = pDvdVideoPicture->dts; // xxx is DVD_NOPTS_VALUE better?
+
   return true;
 }
 
@@ -970,25 +990,54 @@ bool COpenMaxVideo::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
   return true;
 }
 
+bool COpenMaxVideo::GetCodecStats(double &pts, int &droppedPics)
+{
+  pts = m_decoderPts;
+  droppedPics = m_droppedPics;
+  m_droppedPics = 0;
+  CLog::Log(LOGDEBUG, "%s::%s - pts:%.0f droppedPics:%d", CLASSNAME, __func__, pts, droppedPics);
+    return true;
+}
+
   // DecoderFillBufferDone -- OpenMax output buffer has been filled
 OMX_ERRORTYPE COpenMaxVideo::DecoderFillBufferDone(
   OMX_HANDLETYPE hComponent,
   OMX_BUFFERHEADERTYPE* pBuffer)
 {
   COpenMaxVideoBuffer *buffer = (COpenMaxVideoBuffer*)pBuffer->pAppPrivate;
+  bool skipDeinterlaceFields = m_skipDeinterlaceFields || g_windowManager.HasDialogOnScreen();
 
   #if defined(OMX_DEBUG_VERBOSE)
-  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);
+  CLog::Log(LOGDEBUG, "%s::%s - %p (%p,%p) buffer_size(%u), pts:%.3f flags:%x frame:%d win:%x",
+    CLASSNAME, __func__, buffer, pBuffer, buffer->omx_buffer, pBuffer->nFilledLen, (double)FromOMXTime(buffer->omx_buffer->nTimeStamp)*1e-6, buffer->omx_buffer->nFlags, (int)buffer->omx_buffer->pMarkData, skipDeinterlaceFields);
   #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;
-  m_omx_output_ready.push(buffer);
-  pthread_mutex_unlock(&m_omx_output_mutex);
 
+
+  // flags have OMX_BUFFERFLAG_FIRST_FIELD set if this is a direct result of a submitted frame,
+  // clear for second field of deinterlaced frame. They are zero when frame is returned due to a flush.
+#ifdef DTS_QUEUE
+  if ((!m_deinterlace || (buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_FIRST_FIELD)) && buffer->omx_buffer->nFlags)
+  {
+    pthread_mutex_lock(&m_omx_output_mutex);
+    assert(!m_dts_queue.empty());
+    buffer->dts = m_dts_queue.front();
+    m_dts_queue.pop();
+    pthread_mutex_unlock(&m_omx_output_mutex);
+  }
+#endif
+  if (m_drop_state || (m_deinterlace && skipDeinterlaceFields && !(buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_FIRST_FIELD)))
+  {
+    ReturnOpenMaxBuffer(buffer);
+  }
+  else
+  {
+    buffer->m_aspect_ratio = m_aspect_ratio;
+    pthread_mutex_lock(&m_omx_output_mutex);
+    m_omx_output_ready.push(buffer);
+    pthread_mutex_unlock(&m_omx_output_mutex);
+  }
   return OMX_ErrorNone;
 }
 
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
index f234f6d..adf53b5 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h
@@ -57,6 +57,7 @@ class COpenMaxVideoBuffer
   int height;
   float m_aspect_ratio;
   int index;
+  double dts;
 
   // used for egl based rendering if active
   EGLImageKHR egl_image;
@@ -87,6 +88,7 @@ class COpenMaxVideo
   virtual unsigned GetAllowedReferences() { return 2; }
   virtual void SetDropState(bool bDrop);
   virtual const char* GetName(void) { return (const char*)m_pFormatName; }
+  virtual bool GetCodecStats(double &pts, int &droppedPics);
 
   // OpenMax decoder callback routines.
   OMX_ERRORTYPE DecoderFillBufferDone(OMX_HANDLETYPE hComponent, OMX_BUFFERHEADERTYPE* pBuffer);
@@ -142,10 +144,11 @@ class COpenMaxVideo
 
   bool              m_deinterlace;
   EDEINTERLACEMODE  m_deinterlace_request;
-  bool              m_deinterlace_second_field;
-
   bool              m_startframe;
-
+  unsigned int      m_decode_frame_number;
+  double            m_decoderPts;
+  unsigned int      m_droppedPics;
+  bool              m_skipDeinterlaceFields;
   bool PortSettingsChanged();
   bool SendDecoderConfig(uint8_t *extradata, int extrasize);
   bool NaluFormatStartCodes(enum AVCodecID codec, uint8_t *extradata, int extrasize);
-- 
1.9.3


From 4dd2fcf0f479b6b18dac9a496ddf1788b82388f2 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 11 May 2014 16:13:45 +0100
Subject: [PATCH 87/94] [rbp] Add config.txt settings to log file

---
 xbmc/linux/RBP.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 49dcbb8..9a5e9cb 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -79,11 +79,17 @@ bool CRBP::Initialize()
 
 void CRBP::LogFirmwareVerison()
 {
-  char  response[160];
+  char  response[1024];
   m_DllBcmHost->vc_gencmd(response, sizeof response, "version");
   response[sizeof(response) - 1] = '\0';
   CLog::Log(LOGNOTICE, "Raspberry PI firmware version: %s", response);
   CLog::Log(LOGNOTICE, "ARM mem: %dMB GPU mem: %dMB MPG2:%d WVC1:%d", m_arm_mem, m_gpu_mem, m_codec_mpg2_enabled, m_codec_wvc1_enabled);
+  m_DllBcmHost->vc_gencmd(response, sizeof response, "get_config int");
+  response[sizeof(response) - 1] = '\0';
+  CLog::Log(LOGNOTICE, "Config:\n%s", response);
+  m_DllBcmHost->vc_gencmd(response, sizeof response, "get_config str");
+  response[sizeof(response) - 1] = '\0';
+  CLog::Log(LOGNOTICE, "Config:\n%s", response);
 }
 
 void CRBP::GetDisplaySize(int &width, int &height)
-- 
1.9.3


From eb2cba833f1399befcbd60901f3d97a08e3a2781 Mon Sep 17 00:00:00 2001
From: Alex Deryskyba <alex@codesnake.com>
Date: Thu, 8 May 2014 18:54:54 +0300
Subject: [PATCH 88/94] Reset display region when video stream properties
 change

Currently when video stream properties change, e.g. when user switches to next or previous Live TV channel, and has the same frame width and height, the COMXVideo::SetVideoRect() method is not called, causing the video to play back with default source and destination rectangles, not respecting the user-defined zoom settings.

This commit fixes the issue.
---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index 61b884e..eaa1e34 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -781,6 +781,9 @@ void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, f
     return;
   }
 
+  m_src_rect.SetRect(0, 0, 0, 0);
+  m_dst_rect.SetRect(0, 0, 0, 0);
+
   g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack);
 }
 
-- 
1.9.3


From 7c369ba5ec4e700f5c06e1caa20095bc26e61195 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 28 May 2014 23:44:11 +0100
Subject: [PATCH 89/94] [omxplayer] Fix for mapping of multichannel PCM audio

---
 xbmc/cores/omxplayer/OMXAudio.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index d9beb68..75eff26 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -606,13 +606,18 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
     if (m_InputChannels <= 2)
       stdLayout = AE_CH_LAYOUT_2_0;
 
-    uint64_t m_dst_chan_layout = GetAVChannelLayout(stdLayout);
+
+    CAEChannelInfo resolvedMap = channelMap;
+    resolvedMap.ResolveChannels(stdLayout);
+    uint64_t m_dst_chan_layout = GetAVChannelLayout(resolvedMap);
     uint64_t m_src_chan_layout = GetAVChannelLayout(channelMap);
-    m_OutputChannels = stdLayout.Count();
+
+    m_InputChannels = channelMap.Count();
+    m_OutputChannels = resolvedMap.Count();
 
     int m_dst_channels = m_OutputChannels;
     int m_src_channels = m_InputChannels;
-    SetAudioProps(m_Passthrough, GetChannelMap(stdLayout, m_Passthrough));
+    SetAudioProps(m_Passthrough, GetChannelMap(resolvedMap, 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);
 
-- 
1.9.3


From 458ec80741e4aa1ae95fe616f5953e1268a4802e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 30 May 2014 14:15:10 +0100
Subject: [PATCH 90/94] [pi] Fix for logged resolutions

---
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index 5b26b20..a3edf0e 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -483,10 +483,8 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
 
   if(resolutions.size() == 0)
   {
-    RESOLUTION_INFO res;
-    CLog::Log(LOGDEBUG, "EGL probe resolution %s:%x\n", m_desktopRes.strMode.c_str(), m_desktopRes.dwFlags);
-
     AddUniqueResolution(m_desktopRes, resolutions);
+    CLog::Log(LOGDEBUG, "EGL probe resolution %s:%x\n", m_desktopRes.strMode.c_str(), m_desktopRes.dwFlags);
   }
 
   if(resolutions.size() < 2)
@@ -576,13 +574,12 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
       res.iScreenWidth  = tv->width;
       res.iScreenHeight = tv->height;
       res.fPixelRatio   = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res.iScreenWidth / (float)res.iScreenHeight);
+      res.iSubtitles    = (int)(0.965 * res.iHeight);
 
+      AddUniqueResolution(res, resolutions);
       CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) %s%s:%x\n", i, res.strMode.c_str(), res.fPixelRatio,
           tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
 
-      res.iSubtitles    = (int)(0.965 * res.iHeight);
-
-      AddUniqueResolution(res, resolutions);
       if (tv->frame_rate == 24 || tv->frame_rate == 30 || tv->frame_rate == 60)
       {
         RESOLUTION_INFO res2 = res;
@@ -596,11 +593,10 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
-
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
 
         AddUniqueResolution(res2, resolutions);
+        CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
         if (tv->frame_rate == 24 || tv->frame_rate == 30 || tv->frame_rate == 60)
         {
           res2.fRefreshRate  = (float)tv->frame_rate * (1000.0f/1001.0f);
@@ -612,11 +608,10 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
-
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
 
         AddUniqueResolution(res2, resolutions);
+        CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f)\n", i, res2.strMode.c_str(), res2.fPixelRatio);
         if (tv->frame_rate == 24 || tv->frame_rate == 30 || tv->frame_rate == 60)
         {
           res2.fRefreshRate  = (float)tv->frame_rate * (1000.0f/1001.0f);
-- 
1.9.3


From 9eb0d69eb1f319421780025cefe6df3ade40c4dc Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 7 Jun 2014 16:55:41 +0100
Subject: [PATCH 91/94] [omx] Remove logging for texture jobs

This causes a lot of log spam which hasn't proved useful so far.
---
 xbmc/cores/omxplayer/OMXImage.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXImage.cpp b/xbmc/cores/omxplayer/OMXImage.cpp
index 262a004..d529b20 100644
--- a/xbmc/cores/omxplayer/OMXImage.cpp
+++ b/xbmc/cores/omxplayer/OMXImage.cpp
@@ -210,13 +210,11 @@ bool COMXImage::SendMessage(bool (*callback)(EGLDisplay egl_display, EGLContext
   mess.sync.Reset();
   {
     CSingleLock lock(m_texqueue_lock);
-    CLog::Log(LOGDEBUG, "%s: texture job: %p:%p", __func__, &mess, mess.callback);
     m_texqueue.push(&mess);
     m_texqueue_cond.notifyAll();
   }
   // wait for function to have finished (in texture thread)
   mess.sync.Wait();
-  CLog::Log(LOGDEBUG, "%s: texture job done: %p:%p = %d", __func__, &mess, mess.callback, mess.result);
   // need to ensure texture thread has returned from mess.sync.Set() before we exit and free tex
   CSingleLock lock(m_texqueue_lock);
   return mess.result;
@@ -429,15 +427,12 @@ void COMXImage::Process()
       struct callbackinfo *mess = m_texqueue.front();
       m_texqueue.pop();
       lock.Leave();
-      CLog::Log(LOGDEBUG, "%s: texture job: %p:%p:%p", __func__, mess, mess->callback, mess->cookie);
 
       mess->result = mess->callback(g_Windowing.GetEGLDisplay(), GetEGLContext(), mess->cookie);
-      CLog::Log(LOGDEBUG, "%s: texture job about to Set: %p:%p:%p", __func__, mess, mess->callback, mess->cookie);
       {
         CSingleLock lock(m_texqueue_lock);
         mess->sync.Set();
       }
-      CLog::Log(LOGDEBUG, "%s: texture job: %p done", __func__, mess);
     }
   }
 }
-- 
1.9.3


From 4c7a42273416f4053a5bb90755ea45cc0a5f7a0b Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 15 Jun 2014 13:20:53 +0100
Subject: [PATCH 92/94] gles: Avoid crash when capturing snapshot when using
 dvdplayer

Note: snapshot will be blank, but that's better than crashing
---
 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 e22a153..0cff2c5 100644
--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
@@ -1600,7 +1600,9 @@ bool CLinuxRendererGLES::RenderCapture(CRenderCapture* capture)
     return false;
 
   // If rendered directly by the hardware
+#ifndef TARGET_RASPBERRY_PI
   if (m_renderMethod & RENDER_BYPASS)
+#endif
   {
     capture->BeginRender();
     capture->EndRender();
-- 
1.9.3


From 9805b1c9b218f8ba15c41752cc88f6e8bc3223ad Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 28 May 2014 18:30:51 +0100
Subject: [PATCH 93/94] [omxcodec] Reduce GPU memory use by 2 video frames

---
 xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
index 93cf521..cc45570 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
@@ -308,6 +308,20 @@ bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, OpenM
     return false;
   }
 
+  {
+    // as we aren't tunnelled to display, we can save memory by setting extrabuffers to 0
+    OMX_PARAM_U32TYPE extra_buffers;
+    OMX_INIT_STRUCTURE(extra_buffers);
+    extra_buffers.nU32 = 0;
+
+    omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmExtraBuffers, &extra_buffers);
+    if(omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamBrcmExtraBuffers omx_err(0x%08x)\n", omx_err);
+      return false;
+    }
+  }
+
   // request portsettingschanged on aspect ratio change
   OMX_CONFIG_REQUESTCALLBACKTYPE notifications;
   OMX_INIT_STRUCTURE(notifications);
-- 
1.9.3


From 1b49a6f5b1655918e26d84ca4260fc249c00022f Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 18 Jun 2014 23:11:28 +0100
Subject: [PATCH 94/94] [rbp] Reduce GPU memory use when limited

Switching from default triple buffered output to double buffered saves 8M with 1080p GUI.
This may slightly reduce framerate, but is likely to be minimal.

Assume if gpu_mem is set below the default 128M that this memory reduction is wanted
---
 xbmc/linux/RBP.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 9a5e9cb..50d5186 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -72,6 +72,9 @@ bool CRBP::Initialize()
   if (vc_gencmd(response, sizeof response, "codec_enabled WVC1") == 0)
     m_codec_wvc1_enabled = strcmp("WVC1=enabled", response) == 0;
 
+  if (m_gpu_mem < 128)
+    setenv("V3D_DOUBLE_BUFFER", "1", 1);
+
   g_OMXImage.Initialize();
   m_omx_image_init = true;
   return true;
-- 
1.9.3

