diff options
Diffstat (limited to 'package/xbmc/patches')
-rw-r--r-- | package/xbmc/patches/xbmc-gotham_rbp_backports.patch | 20665 |
1 files changed, 20665 insertions, 0 deletions
diff --git a/package/xbmc/patches/xbmc-gotham_rbp_backports.patch b/package/xbmc/patches/xbmc-gotham_rbp_backports.patch new file mode 100644 index 000000000..9a4772437 --- /dev/null +++ b/package/xbmc/patches/xbmc-gotham_rbp_backports.patch @@ -0,0 +1,20665 @@ +From 1353d8feca19f2f84019797942d70864054db1b0 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Mon, 5 Aug 2013 13:12:46 +0100 +Subject: [PATCH 01/94] h264_parser: Initialize the h264dsp context in the + parser as well +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Each AVStream struct for an H.264 elementary stream actually has two +copies of the H264DSPContext struct (and in fact all the other members +of H264Context as well): + +((H264Context *) ((AVStream *)st)->codec->priv_data)->h264dsp +((H264Context *) ((AVStream *)st)->parser->priv_data)->h264dsp + +but only the first of these was actually being initialised. This +prevented the addition of platform-specific implementations of +parser-related functions. + +Signed-off-by: Martin Storsjö <martin@martin.st> +--- + lib/ffmpeg/libavcodec/h264_parser.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/ffmpeg/libavcodec/h264_parser.c b/lib/ffmpeg/libavcodec/h264_parser.c +index aff9ba1..a732f79 100644 +--- a/lib/ffmpeg/libavcodec/h264_parser.c ++++ b/lib/ffmpeg/libavcodec/h264_parser.c +@@ -386,6 +386,7 @@ static int init(AVCodecParserContext *s) + H264Context *h = s->priv_data; + h->thread_context[0] = h; + h->slice_context_count = 1; ++ ff_h264dsp_init(&h->h264dsp, 8, 1); + return 0; + } + +-- +1.9.3 + + +From 7ea2cb68f6fb1149fce70854e36ed6357a267238 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Mon, 5 Aug 2013 13:12:47 +0100 +Subject: [PATCH 02/94] h264dsp: Factorize code into a new function, + h264_find_start_code_candidate +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This performs the start code search which was previously part of +h264_find_frame_end() - the most CPU intensive part of the function. + +By itself, this results in a performance regression: + Before After + Mean StdDev Mean StdDev Change +Overall time 2925.6 26.2 3068.5 31.7 -4.7% + +but this can more than be made up for by platform-optimised +implementations of the function. + +Signed-off-by: Martin Storsjö <martin@martin.st> +--- + lib/ffmpeg/libavcodec/h264_parser.c | 20 +++----------------- + lib/ffmpeg/libavcodec/h264dsp.c | 29 +++++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/h264dsp.h | 9 +++++++++ + 3 files changed, 41 insertions(+), 17 deletions(-) + +diff --git a/lib/ffmpeg/libavcodec/h264_parser.c b/lib/ffmpeg/libavcodec/h264_parser.c +index a732f79..972aace 100644 +--- a/lib/ffmpeg/libavcodec/h264_parser.c ++++ b/lib/ffmpeg/libavcodec/h264_parser.c +@@ -62,23 +62,9 @@ static int ff_h264_find_frame_end(H264Context *h, const uint8_t *buf, int buf_si + } + + if(state==7){ +-#if HAVE_FAST_UNALIGNED +- /* we check i<buf_size instead of i+3/7 because its simpler +- * and there should be FF_INPUT_BUFFER_PADDING_SIZE bytes at the end +- */ +-# if HAVE_FAST_64BIT +- while(i<next_avc && !((~*(const uint64_t*)(buf+i) & (*(const uint64_t*)(buf+i) - 0x0101010101010101ULL)) & 0x8080808080808080ULL)) +- i+=8; +-# else +- while(i<next_avc && !((~*(const uint32_t*)(buf+i) & (*(const uint32_t*)(buf+i) - 0x01010101U)) & 0x80808080U)) +- i+=4; +-# endif +-#endif +- for(; i<next_avc; i++){ +- if(!buf[i]){ +- state=2; +- break; +- } ++ i += h->h264dsp.h264_find_start_code_candidate(buf + i, buf_size - i); ++ if (i < buf_size) ++ state = 2; + } + }else if(state<=2){ + if(buf[i]==1) state^= 5; //2->7, 1->4, 0->5 +diff --git a/lib/ffmpeg/libavcodec/h264dsp.c b/lib/ffmpeg/libavcodec/h264dsp.c +index da9e417..b7d61cd 100644 +--- a/lib/ffmpeg/libavcodec/h264dsp.c ++++ b/lib/ffmpeg/libavcodec/h264dsp.c +@@ -60,6 +60,34 @@ + #include "h264addpx_template.c" + #undef BIT_DEPTH + ++static int h264_find_start_code_candidate_c(const uint8_t *buf, int size) ++{ ++ int i = 0; ++#if HAVE_FAST_UNALIGNED ++ /* we check i < size instead of i + 3 / 7 because it is ++ * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE ++ * bytes at the end. ++ */ ++#if HAVE_FAST_64BIT ++ while (i < size && ++ !((~*(const uint64_t *)(buf + i) & ++ (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) & ++ 0x8080808080808080ULL)) ++ i += 8; ++#else ++ while (i < size && ++ !((~*(const uint32_t *)(buf + i) & ++ (*(const uint32_t *)(buf + i) - 0x01010101U)) & ++ 0x80808080U)) ++ i += 4; ++#endif ++#endif ++ for (; i < size; i++) ++ if (!buf[i]) ++ break; ++ return i; ++} ++ + void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_format_idc) + { + #undef FUNC +@@ -146,6 +174,7 @@ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_fo + H264_DSP(8); + break; + } ++ c->h264_find_start_code_candidate = h264_find_start_code_candidate_c; + + if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc); + if (HAVE_ALTIVEC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc); +diff --git a/lib/ffmpeg/libavcodec/h264dsp.h b/lib/ffmpeg/libavcodec/h264dsp.h +index 98ea15c..1be4804 100644 +--- a/lib/ffmpeg/libavcodec/h264dsp.h ++++ b/lib/ffmpeg/libavcodec/h264dsp.h +@@ -105,6 +105,15 @@ typedef struct H264DSPContext { + /* bypass-transform */ + void (*h264_add_pixels8_clear)(uint8_t *dst, int16_t *block, int stride); + void (*h264_add_pixels4_clear)(uint8_t *dst, int16_t *block, int stride); ++ ++ /** ++ * Search buf from the start for up to size bytes. Return the index ++ * of a zero byte, or >= size if not found. Ideally, use lookahead ++ * to filter out any zero bytes that are known to not be followed by ++ * one or more further zero bytes and a one byte. Better still, filter ++ * out any bytes that form the trailing_zero_8bits syntax element too. ++ */ ++ int (*h264_find_start_code_candidate)(const uint8_t *buf, int size); + } H264DSPContext; + + void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, +-- +1.9.3 + + +From 458ff4b6c1855c529f563dbbd15e35aaab50adae Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Mon, 5 Aug 2013 13:12:48 +0100 +Subject: [PATCH 03/94] arm: Add assembly version of + h264_find_start_code_candidate +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + + Before After + Mean StdDev Mean StdDev Change +This function 508.8 23.4 185.4 9.0 +174.4% +Overall 3068.5 31.7 2752.1 29.4 +11.5% + +In combination with the preceding patch: + Before After + Mean StdDev Mean StdDev Change +Overall 2925.6 26.2 2752.1 29.4 +6.3% + +Signed-off-by: Martin Storsjö <martin@martin.st> +--- + lib/ffmpeg/libavcodec/arm/Makefile | 1 + + lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S | 253 +++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c | 4 + + lib/ffmpeg/libavcodec/h264_parser.c | 1 - + 4 files changed, 258 insertions(+), 1 deletion(-) + create mode 100644 lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S + +diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile +index 7390a8b..480000b71 100644 +--- a/lib/ffmpeg/libavcodec/arm/Makefile ++++ b/lib/ffmpeg/libavcodec/arm/Makefile +@@ -9,6 +9,7 @@ OBJS-$(CONFIG_AAC_DECODER) += arm/sbrdsp_init_arm.o \ + OBJS-$(CONFIG_DCA_DECODER) += arm/dcadsp_init_arm.o \ + + ARMV6-OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_armv6.o ++ARMV6-OBJS-$(CONFIG_H264DSP) += arm/h264dsp_armv6.o + + OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \ + arm/flacdsp_arm.o \ +diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S b/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S +new file mode 100644 +index 0000000..c4f12a6 +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S +@@ -0,0 +1,253 @@ ++/* ++ * Copyright (c) 2013 RISC OS Open Ltd ++ * Author: Ben Avison <bavison@riscosopen.org> ++ * ++ * This file is part of Libav. ++ * ++ * Libav is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * Libav is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with Libav; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "libavutil/arm/asm.S" ++ ++RESULT .req a1 ++BUF .req a1 ++SIZE .req a2 ++PATTERN .req a3 ++PTR .req a4 ++DAT0 .req v1 ++DAT1 .req v2 ++DAT2 .req v3 ++DAT3 .req v4 ++TMP0 .req v5 ++TMP1 .req v6 ++TMP2 .req ip ++TMP3 .req lr ++ ++#define PRELOAD_DISTANCE 4 ++ ++.macro innerloop4 ++ ldr DAT0, [PTR], #4 ++ subs SIZE, SIZE, #4 @ C flag survives rest of macro ++ sub TMP0, DAT0, PATTERN, lsr #14 ++ bic TMP0, TMP0, DAT0 ++ ands TMP0, TMP0, PATTERN ++.endm ++ ++.macro innerloop16 decrement, do_preload ++ ldmia PTR!, {DAT0,DAT1,DAT2,DAT3} ++ .ifnc "\do_preload","" ++ pld [PTR, #PRELOAD_DISTANCE*32] ++ .endif ++ .ifnc "\decrement","" ++ subs SIZE, SIZE, #\decrement @ C flag survives rest of macro ++ .endif ++ sub TMP0, DAT0, PATTERN, lsr #14 ++ sub TMP1, DAT1, PATTERN, lsr #14 ++ bic TMP0, TMP0, DAT0 ++ bic TMP1, TMP1, DAT1 ++ sub TMP2, DAT2, PATTERN, lsr #14 ++ sub TMP3, DAT3, PATTERN, lsr #14 ++ ands TMP0, TMP0, PATTERN ++ bic TMP2, TMP2, DAT2 ++ it eq ++ andseq TMP1, TMP1, PATTERN ++ bic TMP3, TMP3, DAT3 ++ itt eq ++ andseq TMP2, TMP2, PATTERN ++ andseq TMP3, TMP3, PATTERN ++.endm ++ ++/* int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size) */ ++function ff_h264_find_start_code_candidate_armv6, export=1 ++ push {v1-v6,lr} ++ mov PTR, BUF ++ @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go ++ @ before using code that does preloads ++ cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1 ++ blo 60f ++ ++ @ Get to word-alignment, 1 byte at a time ++ tst PTR, #3 ++ beq 2f ++1: ldrb DAT0, [PTR], #1 ++ sub SIZE, SIZE, #1 ++ teq DAT0, #0 ++ beq 90f ++ tst PTR, #3 ++ bne 1b ++2: @ Get to 4-word alignment, 1 word at a time ++ ldr PATTERN, =0x80008000 ++ setend be ++ tst PTR, #12 ++ beq 4f ++3: innerloop4 ++ bne 91f ++ tst PTR, #12 ++ bne 3b ++4: @ Get to cacheline (8-word) alignment ++ tst PTR, #16 ++ beq 5f ++ innerloop16 16 ++ bne 93f ++5: @ Check complete cachelines, with preloading ++ @ We need to stop when there are still (PRELOAD_DISTANCE+1) ++ @ complete cachelines to go ++ sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 ++6: innerloop16 , do_preload ++ bne 93f ++ innerloop16 32 ++ bne 93f ++ bcs 6b ++ @ Preload trailing part-cacheline, if any ++ tst SIZE, #31 ++ beq 7f ++ pld [PTR, #(PRELOAD_DISTANCE+1)*32] ++ @ Check remaining data without doing any more preloads. First ++ @ do in chunks of 4 words: ++7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16 ++ bmi 9f ++8: innerloop16 16 ++ bne 93f ++ bcs 8b ++ @ Then in words: ++9: adds SIZE, SIZE, #16 - 4 ++ bmi 11f ++10: innerloop4 ++ bne 91f ++ bcs 10b ++11: setend le ++ @ Check second byte of final halfword ++ ldrb DAT0, [PTR, #-1] ++ teq DAT0, #0 ++ beq 90f ++ @ Check any remaining bytes ++ tst SIZE, #3 ++ beq 13f ++12: ldrb DAT0, [PTR], #1 ++ sub SIZE, SIZE, #1 ++ teq DAT0, #0 ++ beq 90f ++ tst SIZE, #3 ++ bne 12b ++ @ No candidate found ++13: sub RESULT, PTR, BUF ++ b 99f ++ ++60: @ Small buffer - simply check by looping over bytes ++ subs SIZE, SIZE, #1 ++ bcc 99f ++61: ldrb DAT0, [PTR], #1 ++ subs SIZE, SIZE, #1 ++ teq DAT0, #0 ++ beq 90f ++ bcs 61b ++ @ No candidate found ++ sub RESULT, PTR, BUF ++ b 99f ++ ++90: @ Found a candidate at the preceding byte ++ sub RESULT, PTR, BUF ++ sub RESULT, RESULT, #1 ++ b 99f ++ ++91: @ Found a candidate somewhere in the preceding 4 bytes ++ sub RESULT, PTR, BUF ++ sub RESULT, RESULT, #4 ++ sub TMP0, DAT0, #0x20000 ++ bics TMP0, TMP0, DAT0 ++ itt pl ++ ldrbpl DAT0, [PTR, #-3] ++ addpl RESULT, RESULT, #2 ++ bpl 92f ++ teq RESULT, #0 ++ beq 98f @ don't look back a byte if found at first byte in buffer ++ ldrb DAT0, [PTR, #-5] ++92: teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++ ++93: @ Found a candidate somewhere in the preceding 16 bytes ++ sub RESULT, PTR, BUF ++ sub RESULT, RESULT, #16 ++ teq TMP0, #0 ++ beq 95f @ not in first 4 bytes ++ sub TMP0, DAT0, #0x20000 ++ bics TMP0, TMP0, DAT0 ++ itt pl ++ ldrbpl DAT0, [PTR, #-15] ++ addpl RESULT, RESULT, #2 ++ bpl 94f ++ teq RESULT, #0 ++ beq 98f @ don't look back a byte if found at first byte in buffer ++ ldrb DAT0, [PTR, #-17] ++94: teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++95: add RESULT, RESULT, #4 ++ teq TMP1, #0 ++ beq 96f @ not in next 4 bytes ++ sub TMP1, DAT1, #0x20000 ++ bics TMP1, TMP1, DAT1 ++ itee mi ++ ldrbmi DAT0, [PTR, #-13] ++ ldrbpl DAT0, [PTR, #-11] ++ addpl RESULT, RESULT, #2 ++ teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++96: add RESULT, RESULT, #4 ++ teq TMP2, #0 ++ beq 97f @ not in next 4 bytes ++ sub TMP2, DAT2, #0x20000 ++ bics TMP2, TMP2, DAT2 ++ itee mi ++ ldrbmi DAT0, [PTR, #-9] ++ ldrbpl DAT0, [PTR, #-7] ++ addpl RESULT, RESULT, #2 ++ teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++97: add RESULT, RESULT, #4 ++ sub TMP3, DAT3, #0x20000 ++ bics TMP3, TMP3, DAT3 ++ itee mi ++ ldrbmi DAT0, [PTR, #-5] ++ ldrbpl DAT0, [PTR, #-3] ++ addpl RESULT, RESULT, #2 ++ teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ @ drop through to 98f ++98: setend le ++99: pop {v1-v6,pc} ++.endfunc ++ ++ .unreq RESULT ++ .unreq BUF ++ .unreq SIZE ++ .unreq PATTERN ++ .unreq PTR ++ .unreq DAT0 ++ .unreq DAT1 ++ .unreq DAT2 ++ .unreq DAT3 ++ .unreq TMP0 ++ .unreq TMP1 ++ .unreq TMP2 ++ .unreq TMP3 +diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c +index 785b604..2804e56 100644 +--- a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c ++++ b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c +@@ -24,6 +24,8 @@ + #include "libavutil/arm/cpu.h" + #include "libavcodec/h264dsp.h" + ++int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size); ++ + void ff_h264_v_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha, + int beta, int8_t *tc0); + void ff_h264_h_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha, +@@ -106,6 +108,8 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth, + { + int cpu_flags = av_get_cpu_flags(); + ++ if (have_armv6(cpu_flags)) ++ c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6; + if (have_neon(cpu_flags)) + ff_h264dsp_init_neon(c, bit_depth, chroma_format_idc); + } +diff --git a/lib/ffmpeg/libavcodec/h264_parser.c b/lib/ffmpeg/libavcodec/h264_parser.c +index 972aace..363843c 100644 +--- a/lib/ffmpeg/libavcodec/h264_parser.c ++++ b/lib/ffmpeg/libavcodec/h264_parser.c +@@ -65,7 +65,6 @@ static int ff_h264_find_frame_end(H264Context *h, const uint8_t *buf, int buf_si + i += h->h264dsp.h264_find_start_code_candidate(buf + i, buf_size - i); + if (i < buf_size) + state = 2; +- } + }else if(state<=2){ + if(buf[i]==1) state^= 5; //2->7, 1->4, 0->5 + else if(buf[i]) state = 7; +-- +1.9.3 + + +From 5841d5b69f0df2f286c0a8e419deb16d927e864e Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Mon, 19 Aug 2013 22:48:05 +0100 +Subject: [PATCH 04/94] [ffmpeg] Backport of h264_find_start_code_candidate + optimisation + +--- + ...-Initialize-the-h264dsp-context-in-the-pa.patch | 39 +++ + ...torize-code-into-a-new-function-h264_find.patch | 134 +++++++++ + ...embly-version-of-h264_find_start_code_can.patch | 322 +++++++++++++++++++++ + 3 files changed, 495 insertions(+) + create mode 100644 lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch + create mode 100644 lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch + create mode 100644 lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch + +diff --git a/lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch b/lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch +new file mode 100644 +index 0000000..263578d +--- /dev/null ++++ b/lib/ffmpeg/patches/0056-h264_parser-Initialize-the-h264dsp-context-in-the-pa.patch +@@ -0,0 +1,39 @@ ++From 7a82022ee2f9b1fad991ace0936901e7419444be Mon Sep 17 00:00:00 2001 ++From: Ben Avison <bavison@riscosopen.org> ++Date: Mon, 5 Aug 2013 13:12:46 +0100 ++Subject: [PATCH 1/3] h264_parser: Initialize the h264dsp context in the ++ parser as well ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++Each AVStream struct for an H.264 elementary stream actually has two ++copies of the H264DSPContext struct (and in fact all the other members ++of H264Context as well): ++ ++((H264Context *) ((AVStream *)st)->codec->priv_data)->h264dsp ++((H264Context *) ((AVStream *)st)->parser->priv_data)->h264dsp ++ ++but only the first of these was actually being initialised. This ++prevented the addition of platform-specific implementations of ++parser-related functions. ++ ++Signed-off-by: Martin Storsjö <martin@martin.st> ++--- ++ libavcodec/h264_parser.c | 1 + ++ 1 file changed, 1 insertion(+) ++ ++diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c ++index 2ed155c..da2a5f9 100644 ++--- a/libavcodec/h264_parser.c +++++ b/libavcodec/h264_parser.c ++@@ -417,6 +417,7 @@ static av_cold int init(AVCodecParserContext *s) ++ H264Context *h = s->priv_data; ++ h->thread_context[0] = h; ++ h->slice_context_count = 1; +++ ff_h264dsp_init(&h->h264dsp, 8, 1); ++ return 0; ++ } ++ ++-- ++1.7.9.5 +diff --git a/lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch b/lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch +new file mode 100644 +index 0000000..0151d85 +--- /dev/null ++++ b/lib/ffmpeg/patches/0057-h264dsp-Factorize-code-into-a-new-function-h264_find.patch +@@ -0,0 +1,134 @@ ++From 218d6844b37d339ffbf2044ad07d8be7767e2734 Mon Sep 17 00:00:00 2001 ++From: Ben Avison <bavison@riscosopen.org> ++Date: Mon, 5 Aug 2013 13:12:47 +0100 ++Subject: [PATCH 2/3] h264dsp: Factorize code into a new function, ++ h264_find_start_code_candidate ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++This performs the start code search which was previously part of ++h264_find_frame_end() - the most CPU intensive part of the function. ++ ++By itself, this results in a performance regression: ++ Before After ++ Mean StdDev Mean StdDev Change ++Overall time 2925.6 26.2 3068.5 31.7 -4.7% ++ ++but this can more than be made up for by platform-optimised ++implementations of the function. ++ ++Signed-off-by: Martin Storsjö <martin@martin.st> ++--- ++ libavcodec/h264_parser.c | 27 +++------------------------ ++ libavcodec/h264dsp.c | 29 +++++++++++++++++++++++++++++ ++ libavcodec/h264dsp.h | 9 +++++++++ ++ 3 files changed, 41 insertions(+), 24 deletions(-) ++ ++diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c ++index da2a5f9..ef5da98 100644 ++--- a/libavcodec/h264_parser.c +++++ b/libavcodec/h264_parser.c ++@@ -47,30 +47,9 @@ static int h264_find_frame_end(H264Context *h, const uint8_t *buf, ++ ++ for (i = 0; i < buf_size; i++) { ++ if (state == 7) { ++-#if HAVE_FAST_UNALIGNED ++- /* we check i < buf_size instead of i + 3 / 7 because it is ++- * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE ++- * bytes at the end. ++- */ ++-#if HAVE_FAST_64BIT ++- while (i < buf_size && ++- !((~*(const uint64_t *)(buf + i) & ++- (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) & ++- 0x8080808080808080ULL)) ++- i += 8; ++-#else ++- while (i < buf_size && ++- !((~*(const uint32_t *)(buf + i) & ++- (*(const uint32_t *)(buf + i) - 0x01010101U)) & ++- 0x80808080U)) ++- i += 4; ++-#endif ++-#endif ++- for (; i < buf_size; i++) ++- if (!buf[i]) { ++- state = 2; ++- break; ++- } +++ i += h->h264dsp.h264_find_start_code_candidate(buf + i, buf_size - i); +++ if (i < buf_size) +++ state = 2; ++ } else if (state <= 2) { ++ if (buf[i] == 1) ++ state ^= 5; // 2->7, 1->4, 0->5 ++diff --git a/libavcodec/h264dsp.c b/libavcodec/h264dsp.c ++index 3ca6abe..a901dbb 100644 ++--- a/libavcodec/h264dsp.c +++++ b/libavcodec/h264dsp.c ++@@ -53,6 +53,34 @@ ++ #include "h264addpx_template.c" ++ #undef BIT_DEPTH ++ +++static int h264_find_start_code_candidate_c(const uint8_t *buf, int size) +++{ +++ int i = 0; +++#if HAVE_FAST_UNALIGNED +++ /* we check i < size instead of i + 3 / 7 because it is +++ * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE +++ * bytes at the end. +++ */ +++#if HAVE_FAST_64BIT +++ while (i < size && +++ !((~*(const uint64_t *)(buf + i) & +++ (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) & +++ 0x8080808080808080ULL)) +++ i += 8; +++#else +++ while (i < size && +++ !((~*(const uint32_t *)(buf + i) & +++ (*(const uint32_t *)(buf + i) - 0x01010101U)) & +++ 0x80808080U)) +++ i += 4; +++#endif +++#endif +++ for (; i < size; i++) +++ if (!buf[i]) +++ break; +++ return i; +++} +++ ++ av_cold void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, ++ const int chroma_format_idc) ++ { ++@@ -133,6 +161,7 @@ av_cold void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, ++ H264_DSP(8); ++ break; ++ } +++ c->h264_find_start_code_candidate = h264_find_start_code_candidate_c; ++ ++ if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc); ++ if (ARCH_PPC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc); ++diff --git a/libavcodec/h264dsp.h b/libavcodec/h264dsp.h ++index 1f9f8fe..6249ba7 100644 ++--- a/libavcodec/h264dsp.h +++++ b/libavcodec/h264dsp.h ++@@ -105,6 +105,15 @@ typedef struct H264DSPContext { ++ /* bypass-transform */ ++ void (*h264_add_pixels8_clear)(uint8_t *dst, int16_t *block, int stride); ++ void (*h264_add_pixels4_clear)(uint8_t *dst, int16_t *block, int stride); +++ +++ /** +++ * Search buf from the start for up to size bytes. Return the index +++ * of a zero byte, or >= size if not found. Ideally, use lookahead +++ * to filter out any zero bytes that are known to not be followed by +++ * one or more further zero bytes and a one byte. Better still, filter +++ * out any bytes that form the trailing_zero_8bits syntax element too. +++ */ +++ int (*h264_find_start_code_candidate)(const uint8_t *buf, int size); ++ } H264DSPContext; ++ ++ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, ++-- ++1.7.9.5 +diff --git a/lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch b/lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch +new file mode 100644 +index 0000000..cdc2d1e +--- /dev/null ++++ b/lib/ffmpeg/patches/0058-arm-Add-assembly-version-of-h264_find_start_code_can.patch +@@ -0,0 +1,322 @@ ++From 45e10e5c8d3df09c80a4d80483bff2712367f3fa Mon Sep 17 00:00:00 2001 ++From: Ben Avison <bavison@riscosopen.org> ++Date: Mon, 5 Aug 2013 13:12:48 +0100 ++Subject: [PATCH 3/3] arm: Add assembly version of ++ h264_find_start_code_candidate ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++ Before After ++ Mean StdDev Mean StdDev Change ++This function 508.8 23.4 185.4 9.0 +174.4% ++Overall 3068.5 31.7 2752.1 29.4 +11.5% ++ ++In combination with the preceding patch: ++ Before After ++ Mean StdDev Mean StdDev Change ++Overall 2925.6 26.2 2752.1 29.4 +6.3% ++ ++Signed-off-by: Martin Storsjö <martin@martin.st> ++--- ++ libavcodec/arm/Makefile | 1 + ++ libavcodec/arm/h264dsp_armv6.S | 253 +++++++++++++++++++++++++++++++++++++ ++ libavcodec/arm/h264dsp_init_arm.c | 4 + ++ 3 files changed, 258 insertions(+) ++ create mode 100644 libavcodec/arm/h264dsp_armv6.S ++ ++diff --git a/libavcodec/arm/Makefile b/libavcodec/arm/Makefile ++index e941aaa..9c64b36 100644 ++--- a/libavcodec/arm/Makefile +++++ b/libavcodec/arm/Makefile ++@@ -45,6 +45,7 @@ ARMV6-OBJS-$(CONFIG_DSPUTIL) += arm/dsputil_init_armv6.o \ ++ arm/simple_idct_armv6.o \ ++ ++ ARMV6-OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_armv6.o +++ARMV6-OBJS-$(CONFIG_H264DSP) += arm/h264dsp_armv6.o ++ ARMV6-OBJS-$(CONFIG_HPELDSP) += arm/hpeldsp_init_armv6.o \ ++ arm/hpeldsp_armv6.o ++ ARMV6-OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_fixed_armv6.o ++diff --git a/libavcodec/arm/h264dsp_armv6.S b/libavcodec/arm/h264dsp_armv6.S ++new file mode 100644 ++index 0000000..c4f12a6 ++--- /dev/null +++++ b/libavcodec/arm/h264dsp_armv6.S ++@@ -0,0 +1,253 @@ +++/* +++ * Copyright (c) 2013 RISC OS Open Ltd +++ * Author: Ben Avison <bavison@riscosopen.org> +++ * +++ * This file is part of Libav. +++ * +++ * Libav is free software; you can redistribute it and/or +++ * modify it under the terms of the GNU Lesser General Public +++ * License as published by the Free Software Foundation; either +++ * version 2.1 of the License, or (at your option) any later version. +++ * +++ * Libav is distributed in the hope that it will be useful, +++ * but WITHOUT ANY WARRANTY; without even the implied warranty of +++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +++ * Lesser General Public License for more details. +++ * +++ * You should have received a copy of the GNU Lesser General Public +++ * License along with Libav; if not, write to the Free Software +++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +++ */ +++ +++#include "libavutil/arm/asm.S" +++ +++RESULT .req a1 +++BUF .req a1 +++SIZE .req a2 +++PATTERN .req a3 +++PTR .req a4 +++DAT0 .req v1 +++DAT1 .req v2 +++DAT2 .req v3 +++DAT3 .req v4 +++TMP0 .req v5 +++TMP1 .req v6 +++TMP2 .req ip +++TMP3 .req lr +++ +++#define PRELOAD_DISTANCE 4 +++ +++.macro innerloop4 +++ ldr DAT0, [PTR], #4 +++ subs SIZE, SIZE, #4 @ C flag survives rest of macro +++ sub TMP0, DAT0, PATTERN, lsr #14 +++ bic TMP0, TMP0, DAT0 +++ ands TMP0, TMP0, PATTERN +++.endm +++ +++.macro innerloop16 decrement, do_preload +++ ldmia PTR!, {DAT0,DAT1,DAT2,DAT3} +++ .ifnc "\do_preload","" +++ pld [PTR, #PRELOAD_DISTANCE*32] +++ .endif +++ .ifnc "\decrement","" +++ subs SIZE, SIZE, #\decrement @ C flag survives rest of macro +++ .endif +++ sub TMP0, DAT0, PATTERN, lsr #14 +++ sub TMP1, DAT1, PATTERN, lsr #14 +++ bic TMP0, TMP0, DAT0 +++ bic TMP1, TMP1, DAT1 +++ sub TMP2, DAT2, PATTERN, lsr #14 +++ sub TMP3, DAT3, PATTERN, lsr #14 +++ ands TMP0, TMP0, PATTERN +++ bic TMP2, TMP2, DAT2 +++ it eq +++ andseq TMP1, TMP1, PATTERN +++ bic TMP3, TMP3, DAT3 +++ itt eq +++ andseq TMP2, TMP2, PATTERN +++ andseq TMP3, TMP3, PATTERN +++.endm +++ +++/* int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size) */ +++function ff_h264_find_start_code_candidate_armv6, export=1 +++ push {v1-v6,lr} +++ mov PTR, BUF +++ @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go +++ @ before using code that does preloads +++ cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1 +++ blo 60f +++ +++ @ Get to word-alignment, 1 byte at a time +++ tst PTR, #3 +++ beq 2f +++1: ldrb DAT0, [PTR], #1 +++ sub SIZE, SIZE, #1 +++ teq DAT0, #0 +++ beq 90f +++ tst PTR, #3 +++ bne 1b +++2: @ Get to 4-word alignment, 1 word at a time +++ ldr PATTERN, =0x80008000 +++ setend be +++ tst PTR, #12 +++ beq 4f +++3: innerloop4 +++ bne 91f +++ tst PTR, #12 +++ bne 3b +++4: @ Get to cacheline (8-word) alignment +++ tst PTR, #16 +++ beq 5f +++ innerloop16 16 +++ bne 93f +++5: @ Check complete cachelines, with preloading +++ @ We need to stop when there are still (PRELOAD_DISTANCE+1) +++ @ complete cachelines to go +++ sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 +++6: innerloop16 , do_preload +++ bne 93f +++ innerloop16 32 +++ bne 93f +++ bcs 6b +++ @ Preload trailing part-cacheline, if any +++ tst SIZE, #31 +++ beq 7f +++ pld [PTR, #(PRELOAD_DISTANCE+1)*32] +++ @ Check remaining data without doing any more preloads. First +++ @ do in chunks of 4 words: +++7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16 +++ bmi 9f +++8: innerloop16 16 +++ bne 93f +++ bcs 8b +++ @ Then in words: +++9: adds SIZE, SIZE, #16 - 4 +++ bmi 11f +++10: innerloop4 +++ bne 91f +++ bcs 10b +++11: setend le +++ @ Check second byte of final halfword +++ ldrb DAT0, [PTR, #-1] +++ teq DAT0, #0 +++ beq 90f +++ @ Check any remaining bytes +++ tst SIZE, #3 +++ beq 13f +++12: ldrb DAT0, [PTR], #1 +++ sub SIZE, SIZE, #1 +++ teq DAT0, #0 +++ beq 90f +++ tst SIZE, #3 +++ bne 12b +++ @ No candidate found +++13: sub RESULT, PTR, BUF +++ b 99f +++ +++60: @ Small buffer - simply check by looping over bytes +++ subs SIZE, SIZE, #1 +++ bcc 99f +++61: ldrb DAT0, [PTR], #1 +++ subs SIZE, SIZE, #1 +++ teq DAT0, #0 +++ beq 90f +++ bcs 61b +++ @ No candidate found +++ sub RESULT, PTR, BUF +++ b 99f +++ +++90: @ Found a candidate at the preceding byte +++ sub RESULT, PTR, BUF +++ sub RESULT, RESULT, #1 +++ b 99f +++ +++91: @ Found a candidate somewhere in the preceding 4 bytes +++ sub RESULT, PTR, BUF +++ sub RESULT, RESULT, #4 +++ sub TMP0, DAT0, #0x20000 +++ bics TMP0, TMP0, DAT0 +++ itt pl +++ ldrbpl DAT0, [PTR, #-3] +++ addpl RESULT, RESULT, #2 +++ bpl 92f +++ teq RESULT, #0 +++ beq 98f @ don't look back a byte if found at first byte in buffer +++ ldrb DAT0, [PTR, #-5] +++92: teq DAT0, #0 +++ it eq +++ subeq RESULT, RESULT, #1 +++ b 98f +++ +++93: @ Found a candidate somewhere in the preceding 16 bytes +++ sub RESULT, PTR, BUF +++ sub RESULT, RESULT, #16 +++ teq TMP0, #0 +++ beq 95f @ not in first 4 bytes +++ sub TMP0, DAT0, #0x20000 +++ bics TMP0, TMP0, DAT0 +++ itt pl +++ ldrbpl DAT0, [PTR, #-15] +++ addpl RESULT, RESULT, #2 +++ bpl 94f +++ teq RESULT, #0 +++ beq 98f @ don't look back a byte if found at first byte in buffer +++ ldrb DAT0, [PTR, #-17] +++94: teq DAT0, #0 +++ it eq +++ subeq RESULT, RESULT, #1 +++ b 98f +++95: add RESULT, RESULT, #4 +++ teq TMP1, #0 +++ beq 96f @ not in next 4 bytes +++ sub TMP1, DAT1, #0x20000 +++ bics TMP1, TMP1, DAT1 +++ itee mi +++ ldrbmi DAT0, [PTR, #-13] +++ ldrbpl DAT0, [PTR, #-11] +++ addpl RESULT, RESULT, #2 +++ teq DAT0, #0 +++ it eq +++ subeq RESULT, RESULT, #1 +++ b 98f +++96: add RESULT, RESULT, #4 +++ teq TMP2, #0 +++ beq 97f @ not in next 4 bytes +++ sub TMP2, DAT2, #0x20000 +++ bics TMP2, TMP2, DAT2 +++ itee mi +++ ldrbmi DAT0, [PTR, #-9] +++ ldrbpl DAT0, [PTR, #-7] +++ addpl RESULT, RESULT, #2 +++ teq DAT0, #0 +++ it eq +++ subeq RESULT, RESULT, #1 +++ b 98f +++97: add RESULT, RESULT, #4 +++ sub TMP3, DAT3, #0x20000 +++ bics TMP3, TMP3, DAT3 +++ itee mi +++ ldrbmi DAT0, [PTR, #-5] +++ ldrbpl DAT0, [PTR, #-3] +++ addpl RESULT, RESULT, #2 +++ teq DAT0, #0 +++ it eq +++ subeq RESULT, RESULT, #1 +++ @ drop through to 98f +++98: setend le +++99: pop {v1-v6,pc} +++.endfunc +++ +++ .unreq RESULT +++ .unreq BUF +++ .unreq SIZE +++ .unreq PATTERN +++ .unreq PTR +++ .unreq DAT0 +++ .unreq DAT1 +++ .unreq DAT2 +++ .unreq DAT3 +++ .unreq TMP0 +++ .unreq TMP1 +++ .unreq TMP2 +++ .unreq TMP3 ++diff --git a/libavcodec/arm/h264dsp_init_arm.c b/libavcodec/arm/h264dsp_init_arm.c ++index bb8b3b9..b206a1b 100644 ++--- a/libavcodec/arm/h264dsp_init_arm.c +++++ b/libavcodec/arm/h264dsp_init_arm.c ++@@ -24,6 +24,8 @@ ++ #include "libavutil/arm/cpu.h" ++ #include "libavcodec/h264dsp.h" ++ +++int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size); +++ ++ void ff_h264_v_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha, ++ int beta, int8_t *tc0); ++ void ff_h264_h_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha, ++@@ -102,6 +104,8 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth, ++ { ++ int cpu_flags = av_get_cpu_flags(); ++ +++ if (have_armv6(cpu_flags)) +++ c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6; ++ if (have_neon(cpu_flags)) ++ h264dsp_init_neon(c, bit_depth, chroma_format_idc); ++ } ++-- ++1.7.9.5 +-- +1.9.3 + + +From db098a580259625bb7b7385a48cb0756aea5cafe Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 16 Apr 2014 01:51:31 +0100 +Subject: [PATCH 05/94] h264: Move search code search functions into separate + source files. + +This permits re-use with parsers for codecs which use similar start codes. + +Signed-off-by: Michael Niedermayer <michaelni@gmx.at> +--- + lib/ffmpeg/libavcodec/Makefile | 2 +- + lib/ffmpeg/libavcodec/arm/Makefile | 2 +- + lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S | 253 --------------------------- + lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c | 4 +- + lib/ffmpeg/libavcodec/arm/startcode_armv6.S | 253 +++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/h264dsp.c | 31 +--- + lib/ffmpeg/libavcodec/startcode.c | 57 ++++++ + lib/ffmpeg/libavcodec/startcode.h | 35 ++++ + 8 files changed, 351 insertions(+), 286 deletions(-) + delete mode 100644 lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S + create mode 100644 lib/ffmpeg/libavcodec/arm/startcode_armv6.S + create mode 100644 lib/ffmpeg/libavcodec/startcode.c + create mode 100644 lib/ffmpeg/libavcodec/startcode.h + +diff --git a/lib/ffmpeg/libavcodec/Makefile b/lib/ffmpeg/libavcodec/Makefile +index dc065a5..460f42c 100644 +--- a/lib/ffmpeg/libavcodec/Makefile ++++ b/lib/ffmpeg/libavcodec/Makefile +@@ -49,7 +49,7 @@ OBJS-$(CONFIG_FFT) += avfft.o fft_fixed.o fft_float.o \ + $(FFT-OBJS-yes) + OBJS-$(CONFIG_GOLOMB) += golomb.o + OBJS-$(CONFIG_H264CHROMA) += h264chroma.o +-OBJS-$(CONFIG_H264DSP) += h264dsp.o h264idct.o ++OBJS-$(CONFIG_H264DSP) += h264dsp.o h264idct.o startcode.o + OBJS-$(CONFIG_H264PRED) += h264pred.o + OBJS-$(CONFIG_H264QPEL) += h264qpel.o + OBJS-$(CONFIG_HUFFMAN) += huffman.o +diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile +index 480000b71..0b432e3 100644 +--- a/lib/ffmpeg/libavcodec/arm/Makefile ++++ b/lib/ffmpeg/libavcodec/arm/Makefile +@@ -9,7 +9,7 @@ OBJS-$(CONFIG_AAC_DECODER) += arm/sbrdsp_init_arm.o \ + OBJS-$(CONFIG_DCA_DECODER) += arm/dcadsp_init_arm.o \ + + ARMV6-OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_armv6.o +-ARMV6-OBJS-$(CONFIG_H264DSP) += arm/h264dsp_armv6.o ++ARMV6-OBJS-$(CONFIG_H264DSP) += arm/startcode_armv6.o + + OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \ + arm/flacdsp_arm.o \ +diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S b/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S +deleted file mode 100644 +index c4f12a6..0000000 +--- a/lib/ffmpeg/libavcodec/arm/h264dsp_armv6.S ++++ /dev/null +@@ -1,253 +0,0 @@ +-/* +- * Copyright (c) 2013 RISC OS Open Ltd +- * Author: Ben Avison <bavison@riscosopen.org> +- * +- * This file is part of Libav. +- * +- * Libav is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Lesser General Public +- * License as published by the Free Software Foundation; either +- * version 2.1 of the License, or (at your option) any later version. +- * +- * Libav is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with Libav; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +- */ +- +-#include "libavutil/arm/asm.S" +- +-RESULT .req a1 +-BUF .req a1 +-SIZE .req a2 +-PATTERN .req a3 +-PTR .req a4 +-DAT0 .req v1 +-DAT1 .req v2 +-DAT2 .req v3 +-DAT3 .req v4 +-TMP0 .req v5 +-TMP1 .req v6 +-TMP2 .req ip +-TMP3 .req lr +- +-#define PRELOAD_DISTANCE 4 +- +-.macro innerloop4 +- ldr DAT0, [PTR], #4 +- subs SIZE, SIZE, #4 @ C flag survives rest of macro +- sub TMP0, DAT0, PATTERN, lsr #14 +- bic TMP0, TMP0, DAT0 +- ands TMP0, TMP0, PATTERN +-.endm +- +-.macro innerloop16 decrement, do_preload +- ldmia PTR!, {DAT0,DAT1,DAT2,DAT3} +- .ifnc "\do_preload","" +- pld [PTR, #PRELOAD_DISTANCE*32] +- .endif +- .ifnc "\decrement","" +- subs SIZE, SIZE, #\decrement @ C flag survives rest of macro +- .endif +- sub TMP0, DAT0, PATTERN, lsr #14 +- sub TMP1, DAT1, PATTERN, lsr #14 +- bic TMP0, TMP0, DAT0 +- bic TMP1, TMP1, DAT1 +- sub TMP2, DAT2, PATTERN, lsr #14 +- sub TMP3, DAT3, PATTERN, lsr #14 +- ands TMP0, TMP0, PATTERN +- bic TMP2, TMP2, DAT2 +- it eq +- andseq TMP1, TMP1, PATTERN +- bic TMP3, TMP3, DAT3 +- itt eq +- andseq TMP2, TMP2, PATTERN +- andseq TMP3, TMP3, PATTERN +-.endm +- +-/* int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size) */ +-function ff_h264_find_start_code_candidate_armv6, export=1 +- push {v1-v6,lr} +- mov PTR, BUF +- @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go +- @ before using code that does preloads +- cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1 +- blo 60f +- +- @ Get to word-alignment, 1 byte at a time +- tst PTR, #3 +- beq 2f +-1: ldrb DAT0, [PTR], #1 +- sub SIZE, SIZE, #1 +- teq DAT0, #0 +- beq 90f +- tst PTR, #3 +- bne 1b +-2: @ Get to 4-word alignment, 1 word at a time +- ldr PATTERN, =0x80008000 +- setend be +- tst PTR, #12 +- beq 4f +-3: innerloop4 +- bne 91f +- tst PTR, #12 +- bne 3b +-4: @ Get to cacheline (8-word) alignment +- tst PTR, #16 +- beq 5f +- innerloop16 16 +- bne 93f +-5: @ Check complete cachelines, with preloading +- @ We need to stop when there are still (PRELOAD_DISTANCE+1) +- @ complete cachelines to go +- sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 +-6: innerloop16 , do_preload +- bne 93f +- innerloop16 32 +- bne 93f +- bcs 6b +- @ Preload trailing part-cacheline, if any +- tst SIZE, #31 +- beq 7f +- pld [PTR, #(PRELOAD_DISTANCE+1)*32] +- @ Check remaining data without doing any more preloads. First +- @ do in chunks of 4 words: +-7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16 +- bmi 9f +-8: innerloop16 16 +- bne 93f +- bcs 8b +- @ Then in words: +-9: adds SIZE, SIZE, #16 - 4 +- bmi 11f +-10: innerloop4 +- bne 91f +- bcs 10b +-11: setend le +- @ Check second byte of final halfword +- ldrb DAT0, [PTR, #-1] +- teq DAT0, #0 +- beq 90f +- @ Check any remaining bytes +- tst SIZE, #3 +- beq 13f +-12: ldrb DAT0, [PTR], #1 +- sub SIZE, SIZE, #1 +- teq DAT0, #0 +- beq 90f +- tst SIZE, #3 +- bne 12b +- @ No candidate found +-13: sub RESULT, PTR, BUF +- b 99f +- +-60: @ Small buffer - simply check by looping over bytes +- subs SIZE, SIZE, #1 +- bcc 99f +-61: ldrb DAT0, [PTR], #1 +- subs SIZE, SIZE, #1 +- teq DAT0, #0 +- beq 90f +- bcs 61b +- @ No candidate found +- sub RESULT, PTR, BUF +- b 99f +- +-90: @ Found a candidate at the preceding byte +- sub RESULT, PTR, BUF +- sub RESULT, RESULT, #1 +- b 99f +- +-91: @ Found a candidate somewhere in the preceding 4 bytes +- sub RESULT, PTR, BUF +- sub RESULT, RESULT, #4 +- sub TMP0, DAT0, #0x20000 +- bics TMP0, TMP0, DAT0 +- itt pl +- ldrbpl DAT0, [PTR, #-3] +- addpl RESULT, RESULT, #2 +- bpl 92f +- teq RESULT, #0 +- beq 98f @ don't look back a byte if found at first byte in buffer +- ldrb DAT0, [PTR, #-5] +-92: teq DAT0, #0 +- it eq +- subeq RESULT, RESULT, #1 +- b 98f +- +-93: @ Found a candidate somewhere in the preceding 16 bytes +- sub RESULT, PTR, BUF +- sub RESULT, RESULT, #16 +- teq TMP0, #0 +- beq 95f @ not in first 4 bytes +- sub TMP0, DAT0, #0x20000 +- bics TMP0, TMP0, DAT0 +- itt pl +- ldrbpl DAT0, [PTR, #-15] +- addpl RESULT, RESULT, #2 +- bpl 94f +- teq RESULT, #0 +- beq 98f @ don't look back a byte if found at first byte in buffer +- ldrb DAT0, [PTR, #-17] +-94: teq DAT0, #0 +- it eq +- subeq RESULT, RESULT, #1 +- b 98f +-95: add RESULT, RESULT, #4 +- teq TMP1, #0 +- beq 96f @ not in next 4 bytes +- sub TMP1, DAT1, #0x20000 +- bics TMP1, TMP1, DAT1 +- itee mi +- ldrbmi DAT0, [PTR, #-13] +- ldrbpl DAT0, [PTR, #-11] +- addpl RESULT, RESULT, #2 +- teq DAT0, #0 +- it eq +- subeq RESULT, RESULT, #1 +- b 98f +-96: add RESULT, RESULT, #4 +- teq TMP2, #0 +- beq 97f @ not in next 4 bytes +- sub TMP2, DAT2, #0x20000 +- bics TMP2, TMP2, DAT2 +- itee mi +- ldrbmi DAT0, [PTR, #-9] +- ldrbpl DAT0, [PTR, #-7] +- addpl RESULT, RESULT, #2 +- teq DAT0, #0 +- it eq +- subeq RESULT, RESULT, #1 +- b 98f +-97: add RESULT, RESULT, #4 +- sub TMP3, DAT3, #0x20000 +- bics TMP3, TMP3, DAT3 +- itee mi +- ldrbmi DAT0, [PTR, #-5] +- ldrbpl DAT0, [PTR, #-3] +- addpl RESULT, RESULT, #2 +- teq DAT0, #0 +- it eq +- subeq RESULT, RESULT, #1 +- @ drop through to 98f +-98: setend le +-99: pop {v1-v6,pc} +-.endfunc +- +- .unreq RESULT +- .unreq BUF +- .unreq SIZE +- .unreq PATTERN +- .unreq PTR +- .unreq DAT0 +- .unreq DAT1 +- .unreq DAT2 +- .unreq DAT3 +- .unreq TMP0 +- .unreq TMP1 +- .unreq TMP2 +- .unreq TMP3 +diff --git a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c +index 2804e56..842fb9f 100644 +--- a/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c ++++ b/lib/ffmpeg/libavcodec/arm/h264dsp_init_arm.c +@@ -24,7 +24,7 @@ + #include "libavutil/arm/cpu.h" + #include "libavcodec/h264dsp.h" + +-int ff_h264_find_start_code_candidate_armv6(const uint8_t *buf, int size); ++int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size); + + void ff_h264_v_loop_filter_luma_neon(uint8_t *pix, int stride, int alpha, + int beta, int8_t *tc0); +@@ -109,7 +109,7 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth, + int cpu_flags = av_get_cpu_flags(); + + if (have_armv6(cpu_flags)) +- c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6; ++ c->h264_find_start_code_candidate = ff_startcode_find_candidate_armv6; + if (have_neon(cpu_flags)) + ff_h264dsp_init_neon(c, bit_depth, chroma_format_idc); + } +diff --git a/lib/ffmpeg/libavcodec/arm/startcode_armv6.S b/lib/ffmpeg/libavcodec/arm/startcode_armv6.S +new file mode 100644 +index 0000000..a46f009 +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/arm/startcode_armv6.S +@@ -0,0 +1,253 @@ ++/* ++ * Copyright (c) 2013 RISC OS Open Ltd ++ * Author: Ben Avison <bavison@riscosopen.org> ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "libavutil/arm/asm.S" ++ ++RESULT .req a1 ++BUF .req a1 ++SIZE .req a2 ++PATTERN .req a3 ++PTR .req a4 ++DAT0 .req v1 ++DAT1 .req v2 ++DAT2 .req v3 ++DAT3 .req v4 ++TMP0 .req v5 ++TMP1 .req v6 ++TMP2 .req ip ++TMP3 .req lr ++ ++#define PRELOAD_DISTANCE 4 ++ ++.macro innerloop4 ++ ldr DAT0, [PTR], #4 ++ subs SIZE, SIZE, #4 @ C flag survives rest of macro ++ sub TMP0, DAT0, PATTERN, lsr #14 ++ bic TMP0, TMP0, DAT0 ++ ands TMP0, TMP0, PATTERN ++.endm ++ ++.macro innerloop16 decrement, do_preload ++ ldmia PTR!, {DAT0,DAT1,DAT2,DAT3} ++ .ifnc "\do_preload","" ++ pld [PTR, #PRELOAD_DISTANCE*32] ++ .endif ++ .ifnc "\decrement","" ++ subs SIZE, SIZE, #\decrement @ C flag survives rest of macro ++ .endif ++ sub TMP0, DAT0, PATTERN, lsr #14 ++ sub TMP1, DAT1, PATTERN, lsr #14 ++ bic TMP0, TMP0, DAT0 ++ bic TMP1, TMP1, DAT1 ++ sub TMP2, DAT2, PATTERN, lsr #14 ++ sub TMP3, DAT3, PATTERN, lsr #14 ++ ands TMP0, TMP0, PATTERN ++ bic TMP2, TMP2, DAT2 ++ it eq ++ andseq TMP1, TMP1, PATTERN ++ bic TMP3, TMP3, DAT3 ++ itt eq ++ andseq TMP2, TMP2, PATTERN ++ andseq TMP3, TMP3, PATTERN ++.endm ++ ++/* int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size) */ ++function ff_startcode_find_candidate_armv6, export=1 ++ push {v1-v6,lr} ++ mov PTR, BUF ++ @ Ensure there are at least (PRELOAD_DISTANCE+2) complete cachelines to go ++ @ before using code that does preloads ++ cmp SIZE, #(PRELOAD_DISTANCE+3)*32 - 1 ++ blo 60f ++ ++ @ Get to word-alignment, 1 byte at a time ++ tst PTR, #3 ++ beq 2f ++1: ldrb DAT0, [PTR], #1 ++ sub SIZE, SIZE, #1 ++ teq DAT0, #0 ++ beq 90f ++ tst PTR, #3 ++ bne 1b ++2: @ Get to 4-word alignment, 1 word at a time ++ ldr PATTERN, =0x80008000 ++ setend be ++ tst PTR, #12 ++ beq 4f ++3: innerloop4 ++ bne 91f ++ tst PTR, #12 ++ bne 3b ++4: @ Get to cacheline (8-word) alignment ++ tst PTR, #16 ++ beq 5f ++ innerloop16 16 ++ bne 93f ++5: @ Check complete cachelines, with preloading ++ @ We need to stop when there are still (PRELOAD_DISTANCE+1) ++ @ complete cachelines to go ++ sub SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 ++6: innerloop16 , do_preload ++ bne 93f ++ innerloop16 32 ++ bne 93f ++ bcs 6b ++ @ Preload trailing part-cacheline, if any ++ tst SIZE, #31 ++ beq 7f ++ pld [PTR, #(PRELOAD_DISTANCE+1)*32] ++ @ Check remaining data without doing any more preloads. First ++ @ do in chunks of 4 words: ++7: adds SIZE, SIZE, #(PRELOAD_DISTANCE+2)*32 - 16 ++ bmi 9f ++8: innerloop16 16 ++ bne 93f ++ bcs 8b ++ @ Then in words: ++9: adds SIZE, SIZE, #16 - 4 ++ bmi 11f ++10: innerloop4 ++ bne 91f ++ bcs 10b ++11: setend le ++ @ Check second byte of final halfword ++ ldrb DAT0, [PTR, #-1] ++ teq DAT0, #0 ++ beq 90f ++ @ Check any remaining bytes ++ tst SIZE, #3 ++ beq 13f ++12: ldrb DAT0, [PTR], #1 ++ sub SIZE, SIZE, #1 ++ teq DAT0, #0 ++ beq 90f ++ tst SIZE, #3 ++ bne 12b ++ @ No candidate found ++13: sub RESULT, PTR, BUF ++ b 99f ++ ++60: @ Small buffer - simply check by looping over bytes ++ subs SIZE, SIZE, #1 ++ bcc 99f ++61: ldrb DAT0, [PTR], #1 ++ subs SIZE, SIZE, #1 ++ teq DAT0, #0 ++ beq 90f ++ bcs 61b ++ @ No candidate found ++ sub RESULT, PTR, BUF ++ b 99f ++ ++90: @ Found a candidate at the preceding byte ++ sub RESULT, PTR, BUF ++ sub RESULT, RESULT, #1 ++ b 99f ++ ++91: @ Found a candidate somewhere in the preceding 4 bytes ++ sub RESULT, PTR, BUF ++ sub RESULT, RESULT, #4 ++ sub TMP0, DAT0, #0x20000 ++ bics TMP0, TMP0, DAT0 ++ itt pl ++ ldrbpl DAT0, [PTR, #-3] ++ addpl RESULT, RESULT, #2 ++ bpl 92f ++ teq RESULT, #0 ++ beq 98f @ don't look back a byte if found at first byte in buffer ++ ldrb DAT0, [PTR, #-5] ++92: teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++ ++93: @ Found a candidate somewhere in the preceding 16 bytes ++ sub RESULT, PTR, BUF ++ sub RESULT, RESULT, #16 ++ teq TMP0, #0 ++ beq 95f @ not in first 4 bytes ++ sub TMP0, DAT0, #0x20000 ++ bics TMP0, TMP0, DAT0 ++ itt pl ++ ldrbpl DAT0, [PTR, #-15] ++ addpl RESULT, RESULT, #2 ++ bpl 94f ++ teq RESULT, #0 ++ beq 98f @ don't look back a byte if found at first byte in buffer ++ ldrb DAT0, [PTR, #-17] ++94: teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++95: add RESULT, RESULT, #4 ++ teq TMP1, #0 ++ beq 96f @ not in next 4 bytes ++ sub TMP1, DAT1, #0x20000 ++ bics TMP1, TMP1, DAT1 ++ itee mi ++ ldrbmi DAT0, [PTR, #-13] ++ ldrbpl DAT0, [PTR, #-11] ++ addpl RESULT, RESULT, #2 ++ teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++96: add RESULT, RESULT, #4 ++ teq TMP2, #0 ++ beq 97f @ not in next 4 bytes ++ sub TMP2, DAT2, #0x20000 ++ bics TMP2, TMP2, DAT2 ++ itee mi ++ ldrbmi DAT0, [PTR, #-9] ++ ldrbpl DAT0, [PTR, #-7] ++ addpl RESULT, RESULT, #2 ++ teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ b 98f ++97: add RESULT, RESULT, #4 ++ sub TMP3, DAT3, #0x20000 ++ bics TMP3, TMP3, DAT3 ++ itee mi ++ ldrbmi DAT0, [PTR, #-5] ++ ldrbpl DAT0, [PTR, #-3] ++ addpl RESULT, RESULT, #2 ++ teq DAT0, #0 ++ it eq ++ subeq RESULT, RESULT, #1 ++ @ drop through to 98f ++98: setend le ++99: pop {v1-v6,pc} ++endfunc ++ ++ .unreq RESULT ++ .unreq BUF ++ .unreq SIZE ++ .unreq PATTERN ++ .unreq PTR ++ .unreq DAT0 ++ .unreq DAT1 ++ .unreq DAT2 ++ .unreq DAT3 ++ .unreq TMP0 ++ .unreq TMP1 ++ .unreq TMP2 ++ .unreq TMP3 +diff --git a/lib/ffmpeg/libavcodec/h264dsp.c b/lib/ffmpeg/libavcodec/h264dsp.c +index b7d61cd..a84ae59 100644 +--- a/lib/ffmpeg/libavcodec/h264dsp.c ++++ b/lib/ffmpeg/libavcodec/h264dsp.c +@@ -30,6 +30,7 @@ + #include "avcodec.h" + #include "h264dsp.h" + #include "h264idct.h" ++#include "startcode.h" + #include "libavutil/common.h" + + #define BIT_DEPTH 8 +@@ -60,34 +61,6 @@ + #include "h264addpx_template.c" + #undef BIT_DEPTH + +-static int h264_find_start_code_candidate_c(const uint8_t *buf, int size) +-{ +- int i = 0; +-#if HAVE_FAST_UNALIGNED +- /* we check i < size instead of i + 3 / 7 because it is +- * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE +- * bytes at the end. +- */ +-#if HAVE_FAST_64BIT +- while (i < size && +- !((~*(const uint64_t *)(buf + i) & +- (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) & +- 0x8080808080808080ULL)) +- i += 8; +-#else +- while (i < size && +- !((~*(const uint32_t *)(buf + i) & +- (*(const uint32_t *)(buf + i) - 0x01010101U)) & +- 0x80808080U)) +- i += 4; +-#endif +-#endif +- for (; i < size; i++) +- if (!buf[i]) +- break; +- return i; +-} +- + void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_format_idc) + { + #undef FUNC +@@ -174,7 +147,7 @@ void ff_h264dsp_init(H264DSPContext *c, const int bit_depth, const int chroma_fo + H264_DSP(8); + break; + } +- c->h264_find_start_code_candidate = h264_find_start_code_candidate_c; ++ c->h264_find_start_code_candidate = ff_startcode_find_candidate_c; + + if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc); + if (HAVE_ALTIVEC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc); +diff --git a/lib/ffmpeg/libavcodec/startcode.c b/lib/ffmpeg/libavcodec/startcode.c +new file mode 100644 +index 0000000..5df7695 +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/startcode.c +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (c) 2003-2010 Michael Niedermayer <michaelni@gmx.at> ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * Accelerated start code search function for start codes common to ++ * MPEG-1/2/4 video, VC-1, H.264/5 ++ * @author Michael Niedermayer <michaelni@gmx.at> ++ */ ++ ++#include "startcode.h" ++#include "config.h" ++ ++int ff_startcode_find_candidate_c(const uint8_t *buf, int size) ++{ ++ int i = 0; ++#if HAVE_FAST_UNALIGNED ++ /* we check i < size instead of i + 3 / 7 because it is ++ * simpler and there must be FF_INPUT_BUFFER_PADDING_SIZE ++ * bytes at the end. ++ */ ++# if HAVE_FAST_64BIT ++ while (i < size && ++ !((~*(const uint64_t *)(buf + i) & ++ (*(const uint64_t *)(buf + i) - 0x0101010101010101ULL)) & ++ 0x8080808080808080ULL)) ++ i += 8; ++# else ++ while (i < size && ++ !((~*(const uint32_t *)(buf + i) & ++ (*(const uint32_t *)(buf + i) - 0x01010101U)) & ++ 0x80808080U)) ++ i += 4; ++# endif ++#endif ++ for (; i < size; i++) ++ if (!buf[i]) ++ break; ++ return i; ++} +diff --git a/lib/ffmpeg/libavcodec/startcode.h b/lib/ffmpeg/libavcodec/startcode.h +new file mode 100644 +index 0000000..cc55d5f +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/startcode.h +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2003-2010 Michael Niedermayer <michaelni@gmx.at> ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * Accelerated start code search function for start codes common to ++ * MPEG-1/2/4 video, VC-1, H.264/5 ++ * @author Michael Niedermayer <michaelni@gmx.at> ++ */ ++ ++#ifndef AVCODEC_STARTCODE_H ++#define AVCODEC_STARTCODE_H ++ ++#include <stdint.h> ++ ++int ff_startcode_find_candidate_c(const uint8_t *buf, int size); ++ ++#endif /* AVCODEC_STARTCODE_H */ +-- +1.9.3 + + +From 7d95eb8e026582e5446e7e11d75ba999286a34d0 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 16 Apr 2014 01:51:32 +0100 +Subject: [PATCH 06/94] vc-1: Add platform-specific start code search routine + to VC1DSPContext. + +Initialise VC1DSPContext for parser as well as for decoder. +Note, the VC-1 code doesn't actually use the function pointer yet. + +Signed-off-by: Michael Niedermayer <michaelni@gmx.at> +--- + lib/ffmpeg/libavcodec/Makefile | 7 +++--- + lib/ffmpeg/libavcodec/arm/Makefile | 3 +++ + lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c | 33 +++++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/vc1.c | 2 ++ + lib/ffmpeg/libavcodec/vc1dec.c | 1 - + lib/ffmpeg/libavcodec/vc1dsp.c | 5 +++++ + lib/ffmpeg/libavcodec/vc1dsp.h | 9 ++++++++ + 7 files changed, 56 insertions(+), 4 deletions(-) + create mode 100644 lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c + +diff --git a/lib/ffmpeg/libavcodec/Makefile b/lib/ffmpeg/libavcodec/Makefile +index 460f42c..8d8a548 100644 +--- a/lib/ffmpeg/libavcodec/Makefile ++++ b/lib/ffmpeg/libavcodec/Makefile +@@ -455,7 +455,7 @@ OBJS-$(CONFIG_VB_DECODER) += vb.o + OBJS-$(CONFIG_VBLE_DECODER) += vble.o + OBJS-$(CONFIG_VC1_DECODER) += vc1dec.o vc1.o vc1data.o vc1dsp.o \ + msmpeg4.o msmpeg4data.o \ +- intrax8.o intrax8dsp.o ++ intrax8.o intrax8dsp.o startcode.o + OBJS-$(CONFIG_VC1_DXVA2_HWACCEL) += dxva2_vc1.o + OBJS-$(CONFIG_VC1_VAAPI_HWACCEL) += vaapi_vc1.o + OBJS-$(CONFIG_VC1_VDPAU_HWACCEL) += vdpau_vc1.o +@@ -487,6 +487,7 @@ OBJS-$(CONFIG_WMAVOICE_DECODER) += wmavoice.o \ + celp_filters.o \ + acelp_vectors.o acelp_filters.o + OBJS-$(CONFIG_WMV1_DECODER) += msmpeg4.o msmpeg4data.o ++ + OBJS-$(CONFIG_WMV2_DECODER) += wmv2dec.o wmv2.o wmv2dsp.o \ + msmpeg4.o msmpeg4data.o \ + intrax8.o intrax8dsp.o +@@ -746,9 +747,9 @@ OBJS-$(CONFIG_PNM_PARSER) += pnm_parser.o pnm.o + OBJS-$(CONFIG_RV30_PARSER) += rv34_parser.o + OBJS-$(CONFIG_RV40_PARSER) += rv34_parser.o + OBJS-$(CONFIG_TAK_PARSER) += tak_parser.o tak.o +-OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.o \ ++OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.o vc1dsp.o \ + msmpeg4.o msmpeg4data.o mpeg4video.o \ +- h263.o ++ h263.o startcode.o + OBJS-$(CONFIG_VORBIS_PARSER) += vorbis_parser.o xiph.o + OBJS-$(CONFIG_VP3_PARSER) += vp3_parser.o + OBJS-$(CONFIG_VP8_PARSER) += vp8_parser.o +diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile +index 0b432e3..715eed7 100644 +--- a/lib/ffmpeg/libavcodec/arm/Makefile ++++ b/lib/ffmpeg/libavcodec/arm/Makefile +@@ -16,6 +16,9 @@ OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \ + + OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_init_arm.o + ARMV6-OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_fixed_armv6.o ++ARMV6-OBJS-$(CONFIG_VC1_DECODER) += arm/startcode_armv6.o ++OBJS-$(CONFIG_VC1_DECODER) += arm/vc1dsp_init_arm.o ++ARMV6-OBJS-$(CONFIG_VC1_PARSER) += arm/startcode_armv6.o + + OBJS-$(CONFIG_MPEGVIDEO) += arm/mpegvideo_arm.o + OBJS-$(CONFIG_VORBIS_DECODER) += arm/vorbisdsp_init_arm.o +diff --git a/lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c +new file mode 100644 +index 0000000..fec5e78 +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/arm/vc1dsp_init_arm.c +@@ -0,0 +1,33 @@ ++/* ++ * This file is part of Libav. ++ * ++ * Libav is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * Libav is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with Libav; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include <stdint.h> ++ ++#include "libavutil/attributes.h" ++#include "libavutil/arm/cpu.h" ++#include "libavcodec/vc1dsp.h" ++ ++int ff_startcode_find_candidate_armv6(const uint8_t *buf, int size); ++ ++av_cold void ff_vc1dsp_init_arm(VC1DSPContext *dsp) ++{ ++ int cpu_flags = av_get_cpu_flags(); ++ ++ if (have_armv6(cpu_flags)) ++ dsp->vc1_find_start_code_candidate = ff_startcode_find_candidate_armv6; ++} +diff --git a/lib/ffmpeg/libavcodec/vc1.c b/lib/ffmpeg/libavcodec/vc1.c +index e2e90a8..9b15809 100644 +--- a/lib/ffmpeg/libavcodec/vc1.c ++++ b/lib/ffmpeg/libavcodec/vc1.c +@@ -1663,5 +1663,7 @@ int ff_vc1_init_common(VC1Context *v) + v->pq = -1; + v->mvrange = 0; /* 7.1.1.18, p80 */ + ++ ff_vc1dsp_init(&v->vc1dsp); ++ + return 0; + } +diff --git a/lib/ffmpeg/libavcodec/vc1dec.c b/lib/ffmpeg/libavcodec/vc1dec.c +index 2130c74..9fd3cae 100644 +--- a/lib/ffmpeg/libavcodec/vc1dec.c ++++ b/lib/ffmpeg/libavcodec/vc1dec.c +@@ -5193,7 +5193,6 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) + ff_vc1_decode_end(avctx); + + ff_h264chroma_init(&v->h264chroma, 8); +- ff_vc1dsp_init(&v->vc1dsp); + + if (avctx->codec_id == AV_CODEC_ID_WMV3 || avctx->codec_id == AV_CODEC_ID_WMV3IMAGE) { + int count = 0; +diff --git a/lib/ffmpeg/libavcodec/vc1dsp.c b/lib/ffmpeg/libavcodec/vc1dsp.c +index 260eda4..3e3f00e 100644 +--- a/lib/ffmpeg/libavcodec/vc1dsp.c ++++ b/lib/ffmpeg/libavcodec/vc1dsp.c +@@ -30,6 +30,7 @@ + #include "h264chroma.h" + #include "rnd_avg.h" + #include "vc1dsp.h" ++#include "startcode.h" + + + /** Apply overlap transform to horizontal edge +@@ -861,8 +862,12 @@ av_cold void ff_vc1dsp_init(VC1DSPContext* dsp) { + dsp->sprite_v_double_twoscale = sprite_v_double_twoscale_c; + #endif + ++ dsp->vc1_find_start_code_candidate = ff_startcode_find_candidate_c; ++ + if (HAVE_ALTIVEC) + ff_vc1dsp_init_altivec(dsp); ++ if (ARCH_ARM) ++ ff_vc1dsp_init_arm(dsp); + if (ARCH_X86) + ff_vc1dsp_init_x86(dsp); + } +diff --git a/lib/ffmpeg/libavcodec/vc1dsp.h b/lib/ffmpeg/libavcodec/vc1dsp.h +index 6540eff..302e4a8 100644 +--- a/lib/ffmpeg/libavcodec/vc1dsp.h ++++ b/lib/ffmpeg/libavcodec/vc1dsp.h +@@ -73,10 +73,19 @@ typedef struct VC1DSPContext { + void (*sprite_v_double_twoscale)(uint8_t *dst, const uint8_t *src1a, const uint8_t *src1b, int offset1, + const uint8_t *src2a, const uint8_t *src2b, int offset2, + int alpha, int width); ++ ++ /** ++ * Search buf from the start for up to size bytes. Return the index ++ * of a zero byte, or >= size if not found. Ideally, use lookahead ++ * to filter out any zero bytes that are known to not be followed by ++ * one or more further zero bytes and a one byte. ++ */ ++ int (*vc1_find_start_code_candidate)(const uint8_t *buf, int size); + } VC1DSPContext; + + void ff_vc1dsp_init(VC1DSPContext* c); + void ff_vc1dsp_init_altivec(VC1DSPContext* c); ++void ff_vc1dsp_init_arm(VC1DSPContext* dsp); + void ff_vc1dsp_init_x86(VC1DSPContext* dsp); + + #endif /* AVCODEC_VC1DSP_H */ +-- +1.9.3 + + +From 9b459c3c4130299099b2e5aca5bff3d6f8d60e72 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 23 Apr 2014 01:41:04 +0100 +Subject: [PATCH 07/94] vc-1: Optimise parser (with special attention to ARM) + +The previous implementation of the parser made four passes over each input +buffer (reduced to two if the container format already guaranteed the input +buffer corresponded to frames, such as with MKV). But these buffers are +often 200K in size, certainly enough to flush the data out of L1 cache, and +for many CPUs, all the way out to main memory. The passes were: + +1) locate frame boundaries (not needed for MKV etc) +2) copy the data into a contiguous block (not needed for MKV etc) +3) locate the start codes within each frame +4) unescape the data between start codes + +After this, the unescaped data was parsed to extract certain header fields, +but because the unescape operation was so large, this was usually also +effectively operating on uncached memory. Most of the unescaped data was +simply thrown away and never processed further. Only step 2 - because it +used memcpy - was using prefetch, making things even worse. + +This patch reorganises these steps so that, aside from the copying, the +operations are performed in parallel, maximising cache utilisation. No more +than the worst-case number of bytes needed for header parsing is unescaped. +Most of the data is, in practice, only read in order to search for a start +code, for which optimised implementations already existed in the H264 codec +(notably the ARM version uses prefetch, so we end up doing both remaining +passes at maximum speed). For MKV files, we know when we've found the last +start code of interest in a given frame, so we are able to avoid doing even +that one remaining pass for most of the buffer. + +In some use-cases (such as the Raspberry Pi) video decode is handled by the +GPU, but the entire elementary stream is still fed through the parser to +pick out certain elements of the header which are necessary to manage the +decode process. As you might expect, in these cases, the performance of the +parser is significant. + +To measure parser performance, I used the same VC-1 elementary stream in +either an MPEG-2 transport stream or a MKV file, and fed it through ffmpeg +with -c:v copy -c:a copy -f null. These are the gperftools counts for +those streams, both filtered to only include vc1_parse() and its callees, +and unfiltered (to include the whole binary). Lower numbers are better: + + Before After +File Filtered Mean StdDev Mean StdDev Confidence Change +M2TS No 861.7 8.2 650.5 8.1 100.0% +32.5% +MKV No 868.9 7.4 731.7 9.0 100.0% +18.8% +M2TS Yes 250.0 11.2 27.2 3.4 100.0% +817.9% +MKV Yes 149.0 12.8 1.7 0.8 100.0% +8526.3% + +Yes, that last case shows vc1_parse() running 86 times faster! The M2TS +case does show a larger absolute improvement though, since it was worse +to begin with. + +This patch has been tested with the FATE suite (albeit on x86 for speed). + +Signed-off-by: Michael Niedermayer <michaelni@gmx.at> +--- + lib/ffmpeg/libavcodec/vc1_parser.c | 269 ++++++++++++++++++++++++------------- + 1 file changed, 175 insertions(+), 94 deletions(-) + +diff --git a/lib/ffmpeg/libavcodec/vc1_parser.c b/lib/ffmpeg/libavcodec/vc1_parser.c +index 53af61c..af601ad 100644 +--- a/lib/ffmpeg/libavcodec/vc1_parser.c ++++ b/lib/ffmpeg/libavcodec/vc1_parser.c +@@ -29,112 +29,83 @@ + #include "vc1.h" + #include "get_bits.h" + ++/** The maximum number of bytes of a sequence, entry point or ++ * frame header whose values we pay any attention to */ ++#define UNESCAPED_THRESHOLD 37 ++ ++/** The maximum number of bytes of a sequence, entry point or ++ * frame header which must be valid memory (because they are ++ * used to update the bitstream cache in skip_bits() calls) ++ */ ++#define UNESCAPED_LIMIT 144 ++ ++typedef enum { ++ NO_MATCH, ++ ONE_ZERO, ++ TWO_ZEROS, ++ ONE ++} VC1ParseSearchState; ++ + typedef struct { + ParseContext pc; + VC1Context v; ++ uint8_t prev_start_code; ++ size_t bytes_to_skip; ++ uint8_t unesc_buffer[UNESCAPED_LIMIT]; ++ size_t unesc_index; ++ VC1ParseSearchState search_state; + } VC1ParseContext; + +-static void vc1_extract_headers(AVCodecParserContext *s, AVCodecContext *avctx, +- const uint8_t *buf, int buf_size) ++static void vc1_extract_header(AVCodecParserContext *s, AVCodecContext *avctx, ++ const uint8_t *buf, int buf_size) + { ++ /* Parse the header we just finished unescaping */ + VC1ParseContext *vpc = s->priv_data; + GetBitContext gb; +- const uint8_t *start, *end, *next; +- uint8_t *buf2 = av_mallocz(buf_size + FF_INPUT_BUFFER_PADDING_SIZE); +- ++ int ret; + vpc->v.s.avctx = avctx; + vpc->v.parse_only = 1; +- next = buf; +- s->repeat_pict = 0; +- +- for(start = buf, end = buf + buf_size; next < end; start = next){ +- int buf2_size, size; +- +- next = find_next_marker(start + 4, end); +- size = next - start - 4; +- buf2_size = vc1_unescape_buffer(start + 4, size, buf2); +- init_get_bits(&gb, buf2, buf2_size * 8); +- if(size <= 0) continue; +- switch(AV_RB32(start)){ +- case VC1_CODE_SEQHDR: +- ff_vc1_decode_sequence_header(avctx, &vpc->v, &gb); +- break; +- case VC1_CODE_ENTRYPOINT: +- ff_vc1_decode_entry_point(avctx, &vpc->v, &gb); +- break; +- case VC1_CODE_FRAME: +- if(vpc->v.profile < PROFILE_ADVANCED) +- ff_vc1_parse_frame_header (&vpc->v, &gb); +- else +- ff_vc1_parse_frame_header_adv(&vpc->v, &gb); +- +- /* keep AV_PICTURE_TYPE_BI internal to VC1 */ +- if (vpc->v.s.pict_type == AV_PICTURE_TYPE_BI) +- s->pict_type = AV_PICTURE_TYPE_B; +- else +- s->pict_type = vpc->v.s.pict_type; +- +- if (avctx->ticks_per_frame > 1){ +- // process pulldown flags +- s->repeat_pict = 1; +- // Pulldown flags are only valid when 'broadcast' has been set. +- // So ticks_per_frame will be 2 +- if (vpc->v.rff){ +- // repeat field +- s->repeat_pict = 2; +- }else if (vpc->v.rptfrm){ +- // repeat frames +- s->repeat_pict = vpc->v.rptfrm * 2 + 1; +- } +- } ++ init_get_bits(&gb, buf, buf_size * 8); ++ switch (vpc->prev_start_code) { ++ case VC1_CODE_SEQHDR & 0xFF: ++ ff_vc1_decode_sequence_header(avctx, &vpc->v, &gb); ++ break; ++ case VC1_CODE_ENTRYPOINT & 0xFF: ++ ff_vc1_decode_entry_point(avctx, &vpc->v, &gb); ++ break; ++ case VC1_CODE_FRAME & 0xFF: ++ if(vpc->v.profile < PROFILE_ADVANCED) ++ ret = ff_vc1_parse_frame_header (&vpc->v, &gb); ++ else ++ ret = ff_vc1_parse_frame_header_adv(&vpc->v, &gb); + ++ if (ret < 0) + break; +- } +- } + +- av_free(buf2); +-} ++ /* keep AV_PICTURE_TYPE_BI internal to VC1 */ ++ if (vpc->v.s.pict_type == AV_PICTURE_TYPE_BI) ++ s->pict_type = AV_PICTURE_TYPE_B; ++ else ++ s->pict_type = vpc->v.s.pict_type; + +-/** +- * Find the end of the current frame in the bitstream. +- * @return the position of the first byte of the next frame, or -1 +- */ +-static int vc1_find_frame_end(ParseContext *pc, const uint8_t *buf, +- int buf_size) { +- int pic_found, i; +- uint32_t state; +- +- pic_found= pc->frame_start_found; +- state= pc->state; +- +- i=0; +- if(!pic_found){ +- for(i=0; i<buf_size; i++){ +- state= (state<<8) | buf[i]; +- if(state == VC1_CODE_FRAME || state == VC1_CODE_FIELD){ +- i++; +- pic_found=1; +- break; ++ if (avctx->ticks_per_frame > 1){ ++ // process pulldown flags ++ s->repeat_pict = 1; ++ // Pulldown flags are only valid when 'broadcast' has been set. ++ // So ticks_per_frame will be 2 ++ if (vpc->v.rff){ ++ // repeat field ++ s->repeat_pict = 2; ++ }else if (vpc->v.rptfrm){ ++ // repeat frames ++ s->repeat_pict = vpc->v.rptfrm * 2 + 1; + } ++ }else{ ++ s->repeat_pict = 0; + } +- } + +- if(pic_found){ +- /* EOF considered as end of frame */ +- if (buf_size == 0) +- return 0; +- for(; i<buf_size; i++){ +- state= (state<<8) | buf[i]; +- if(IS_MARKER(state) && state != VC1_CODE_FIELD && state != VC1_CODE_SLICE){ +- pc->frame_start_found=0; +- pc->state=-1; +- return i-3; +- } +- } ++ break; + } +- pc->frame_start_found= pic_found; +- pc->state= state; +- return END_NOT_FOUND; + } + + static int vc1_parse(AVCodecParserContext *s, +@@ -142,22 +113,127 @@ static int vc1_parse(AVCodecParserContext *s, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) + { ++ /* Here we do the searching for frame boundaries and headers at ++ * the same time. Only a minimal amount at the start of each ++ * header is unescaped. */ + VC1ParseContext *vpc = s->priv_data; +- int next; ++ int pic_found = vpc->pc.frame_start_found; ++ uint8_t *unesc_buffer = vpc->unesc_buffer; ++ size_t unesc_index = vpc->unesc_index; ++ VC1ParseSearchState search_state = vpc->search_state; ++ int next = END_NOT_FOUND; ++ int i = vpc->bytes_to_skip; + +- if(s->flags & PARSER_FLAG_COMPLETE_FRAMES){ +- next= buf_size; +- }else{ +- next= vc1_find_frame_end(&vpc->pc, buf, buf_size); ++ if (pic_found && buf_size == 0) { ++ /* EOF considered as end of frame */ ++ memset(unesc_buffer + unesc_index, 0, UNESCAPED_THRESHOLD - unesc_index); ++ vc1_extract_header(s, avctx, unesc_buffer, unesc_index); ++ next = 0; ++ } ++ while (i < buf_size) { ++ int start_code_found = 0; ++ uint8_t b; ++ while (i < buf_size && unesc_index < UNESCAPED_THRESHOLD) { ++ b = buf[i++]; ++ unesc_buffer[unesc_index++] = b; ++ if (search_state <= ONE_ZERO) ++ search_state = b ? NO_MATCH : search_state + 1; ++ else if (search_state == TWO_ZEROS) { ++ if (b == 1) ++ search_state = ONE; ++ else if (b > 1) { ++ if (b == 3) ++ unesc_index--; // swallow emulation prevention byte ++ search_state = NO_MATCH; ++ } ++ } ++ else { // search_state == ONE ++ // Header unescaping terminates early due to detection of next start code ++ search_state = NO_MATCH; ++ start_code_found = 1; ++ break; ++ } ++ } ++ if ((s->flags & PARSER_FLAG_COMPLETE_FRAMES) && ++ unesc_index >= UNESCAPED_THRESHOLD && ++ vpc->prev_start_code == (VC1_CODE_FRAME & 0xFF)) ++ { ++ // No need to keep scanning the rest of the buffer for ++ // start codes if we know it contains a complete frame and ++ // we've already unescaped all we need of the frame header ++ vc1_extract_header(s, avctx, unesc_buffer, unesc_index); ++ break; ++ } ++ if (unesc_index >= UNESCAPED_THRESHOLD && !start_code_found) { ++ while (i < buf_size) { ++ if (search_state == NO_MATCH) { ++ i += vpc->v.vc1dsp.vc1_find_start_code_candidate(buf + i, buf_size - i); ++ if (i < buf_size) { ++ search_state = ONE_ZERO; ++ } ++ i++; ++ } else { ++ b = buf[i++]; ++ if (search_state == ONE_ZERO) ++ search_state = b ? NO_MATCH : TWO_ZEROS; ++ else if (search_state == TWO_ZEROS) { ++ if (b >= 1) ++ search_state = b == 1 ? ONE : NO_MATCH; ++ } ++ else { // search_state == ONE ++ search_state = NO_MATCH; ++ start_code_found = 1; ++ break; ++ } ++ } ++ } ++ } ++ if (start_code_found) { ++ vc1_extract_header(s, avctx, unesc_buffer, unesc_index); ++ ++ vpc->prev_start_code = b; ++ unesc_index = 0; ++ ++ if (!(s->flags & PARSER_FLAG_COMPLETE_FRAMES)) { ++ if (!pic_found && (b == (VC1_CODE_FRAME & 0xFF) || b == (VC1_CODE_FIELD & 0xFF))) { ++ pic_found = 1; ++ } ++ else if (pic_found && b != (VC1_CODE_FIELD & 0xFF) && b != (VC1_CODE_SLICE & 0xFF)) { ++ next = i - 4; ++ pic_found = b == (VC1_CODE_FRAME & 0xFF); ++ break; ++ } ++ } ++ } ++ } + ++ vpc->pc.frame_start_found = pic_found; ++ vpc->unesc_index = unesc_index; ++ vpc->search_state = search_state; ++ ++ if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { ++ next = buf_size; ++ } else { + if (ff_combine_frame(&vpc->pc, next, &buf, &buf_size) < 0) { ++ vpc->bytes_to_skip = 0; + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + } + +- vc1_extract_headers(s, avctx, buf, buf_size); ++ vpc->v.first_pic_header_flag = 1; ++ ++ /* If we return with a valid pointer to a combined frame buffer ++ * then on the next call then we'll have been unhelpfully rewound ++ * by up to 4 bytes (depending upon whether the start code ++ * overlapped the input buffer, and if so by how much). We don't ++ * want this: it will either cause spurious second detections of ++ * the start code we've already seen, or cause extra bytes to be ++ * inserted at the start of the unescaped buffer. */ ++ vpc->bytes_to_skip = 4; ++ if (next < 0) ++ vpc->bytes_to_skip += next; + + *poutbuf = buf; + *poutbuf_size = buf_size; +@@ -188,6 +264,11 @@ static int vc1_parse_init(AVCodecParserContext *s) + { + VC1ParseContext *vpc = s->priv_data; + vpc->v.s.slice_context_count = 1; ++ vpc->v.first_pic_header_flag = 1; ++ vpc->prev_start_code = 0; ++ vpc->bytes_to_skip = 0; ++ vpc->unesc_index = 0; ++ vpc->search_state = NO_MATCH; + return ff_vc1_init_common(&vpc->v); + } + +-- +1.9.3 + + +From c2ebe54fe1d7c7a6cee7282bcf2668a826006ade Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 19 Mar 2014 17:44:59 +0000 +Subject: [PATCH 08/94] truehd: add hand-scheduled ARM asm version of + mlp_filter_channel. + +Profiling results for overall audio decode and the mlp_filter_channel(_arm) +function in particular are as follows: + + Before After + Mean StdDev Mean StdDev Confidence Change +6:2 total 380.4 22.0 370.8 17.0 87.4% +2.6% (insignificant) +6:2 function 60.7 7.2 36.6 8.1 100.0% +65.8% +8:2 total 357.0 17.5 343.2 19.0 97.8% +4.0% (insignificant) +8:2 function 60.3 8.8 37.3 3.8 100.0% +61.8% +6:6 total 717.2 23.2 658.4 15.7 100.0% +8.9% +6:6 function 140.4 12.9 81.5 9.2 100.0% +72.4% +8:8 total 981.9 16.2 896.2 24.5 100.0% +9.6% +8:8 function 193.4 15.0 103.3 11.5 100.0% +87.2% + +Experiments with adding preload instructions to this function yielded no +useful benefit, so these have not been included. + +The assembly version has also been tested with a fuzz tester to ensure that +any combinations of inputs not exercised by my available test streams still +generate mathematically identical results to the C version. +--- + lib/ffmpeg/libavcodec/arm/Makefile | 5 +- + lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S | 430 ++++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c | 36 +++ + lib/ffmpeg/libavcodec/mlpdsp.h | 1 + + 4 files changed, 471 insertions(+), 1 deletion(-) + create mode 100644 lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S + create mode 100644 lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c + +diff --git a/lib/ffmpeg/libavcodec/arm/Makefile b/lib/ffmpeg/libavcodec/arm/Makefile +index 715eed7..5b0edf0 100644 +--- a/lib/ffmpeg/libavcodec/arm/Makefile ++++ b/lib/ffmpeg/libavcodec/arm/Makefile +@@ -14,6 +14,8 @@ ARMV6-OBJS-$(CONFIG_H264DSP) += arm/startcode_armv6.o + OBJS-$(CONFIG_FLAC_DECODER) += arm/flacdsp_init_arm.o \ + arm/flacdsp_arm.o \ + ++OBJS-$(CONFIG_MLP_DECODER) += arm/mlpdsp_init_arm.o \ ++ arm/mlpdsp_arm.o + OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_init_arm.o + ARMV6-OBJS-$(CONFIG_MPEGAUDIODSP) += arm/mpegaudiodsp_fixed_armv6.o + ARMV6-OBJS-$(CONFIG_VC1_DECODER) += arm/startcode_armv6.o +@@ -21,6 +23,8 @@ OBJS-$(CONFIG_VC1_DECODER) += arm/vc1dsp_init_arm.o + ARMV6-OBJS-$(CONFIG_VC1_PARSER) += arm/startcode_armv6.o + + OBJS-$(CONFIG_MPEGVIDEO) += arm/mpegvideo_arm.o ++OBJS-$(CONFIG_TRUEHD_DECODER) += arm/mlpdsp_init_arm.o \ ++ arm/mlpdsp_arm.o + OBJS-$(CONFIG_VORBIS_DECODER) += arm/vorbisdsp_init_arm.o + OBJS-$(CONFIG_VP3DSP) += arm/vp3dsp_init_arm.o + OBJS-$(CONFIG_VP5_DECODER) += arm/vp56dsp_init_arm.o +@@ -34,7 +38,6 @@ OBJS-$(CONFIG_H264CHROMA) += arm/h264chroma_init_arm.o + OBJS-$(CONFIG_H264DSP) += arm/h264dsp_init_arm.o + OBJS-$(CONFIG_H264PRED) += arm/h264pred_init_arm.o + OBJS-$(CONFIG_H264QPEL) += arm/h264qpel_init_arm.o +- + OBJS-$(CONFIG_RV30_DECODER) += arm/rv34dsp_init_arm.o + OBJS-$(CONFIG_RV40_DECODER) += arm/rv34dsp_init_arm.o \ + arm/rv40dsp_init_arm.o \ +diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S +new file mode 100644 +index 0000000..114496f +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S +@@ -0,0 +1,430 @@ ++/* ++ * Copyright (c) 2014 RISC OS Open Ltd ++ * Author: Ben Avison <bavison@riscosopen.org> ++ * ++ * This file is part of Libav. ++ * ++ * Libav is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * Libav is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with Libav; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++// This code uses too many ARM-only tricks to easily assemble as Thumb ++.arm ++#undef CONFIG_THUMB ++#define CONFIG_THUMB 0 ++ ++#include "libavutil/arm/asm.S" ++ ++#define MAX_CHANNELS 8 ++#define MAX_FIR_ORDER 8 ++#define MAX_IIR_ORDER 4 ++#define MAX_RATEFACTOR 4 ++#define MAX_BLOCKSIZE (40 * MAX_RATEFACTOR) ++ ++PST .req a1 ++PCO .req a2 ++AC0 .req a3 ++AC1 .req a4 ++CO0 .req v1 ++CO1 .req v2 ++CO2 .req v3 ++CO3 .req v4 ++ST0 .req v5 ++ST1 .req v6 ++ST2 .req sl ++ST3 .req fp ++I .req ip ++PSAMP .req lr ++ ++ ++// Some macros that do loads/multiplies where the register number is determined ++// from an assembly-time expression. Boy is GNU assembler's syntax ugly... ++ ++.macro load group, index, base, offset ++ .altmacro ++ load_ \group, %(\index), \base, \offset ++ .noaltmacro ++.endm ++ ++.macro load_ group, index, base, offset ++ ldr \group\index, [\base, #\offset] ++.endm ++ ++.macro loadd group, index, base, offset ++ .altmacro ++ loadd_ \group, %(\index), %(\index+1), \base, \offset ++ .noaltmacro ++.endm ++ ++.macro loadd_ group, index0, index1, base, offset ++A .if offset >= 256 ++A ldr \group\index0, [\base, #\offset] ++A ldr \group\index1, [\base, #(\offset) + 4] ++A .else ++ ldrd \group\index0, \group\index1, [\base, #\offset] ++A .endif ++.endm ++ ++.macro multiply index, accumulate, long ++ .altmacro ++ multiply_ %(\index), \accumulate, \long ++ .noaltmacro ++.endm ++ ++.macro multiply_ index, accumulate, long ++ .if \long ++ .if \accumulate ++ smlal AC0, AC1, CO\index, ST\index ++ .else ++ smull AC0, AC1, CO\index, ST\index ++ .endif ++ .else ++ .if \accumulate ++ mla AC0, CO\index, ST\index, AC0 ++ .else ++ mul AC0, CO\index, ST\index ++ .endif ++ .endif ++.endm ++ ++// A macro to update the load register number and load offsets ++ ++.macro inc howmany ++ .set LOAD_REG, (LOAD_REG + \howmany) & 3 ++ .set OFFSET_CO, OFFSET_CO + 4 * \howmany ++ .set OFFSET_ST, OFFSET_ST + 4 * \howmany ++ .if FIR_REMAIN > 0 ++ .set FIR_REMAIN, FIR_REMAIN - \howmany ++ .if FIR_REMAIN == 0 ++ .set OFFSET_CO, 4 * MAX_FIR_ORDER ++ .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER) ++ .endif ++ .elseif IIR_REMAIN > 0 ++ .set IIR_REMAIN, IIR_REMAIN - \howmany ++ .endif ++.endm ++ ++// Macro to implement the inner loop for one specific combination of parameters ++ ++.macro implement_filter mask_minus1, shift_0, shift_8, iir_taps, fir_taps ++ .set TOTAL_TAPS, \iir_taps + \fir_taps ++ ++ // Deal with register allocation... ++ .set DEFINED_SHIFT, 0 ++ .set DEFINED_MASK, 0 ++ .set SHUFFLE_SHIFT, 0 ++ .set SHUFFLE_MASK, 0 ++ .set SPILL_SHIFT, 0 ++ .set SPILL_MASK, 0 ++ .if TOTAL_TAPS == 0 ++ // Little register pressure in this case - just keep MASK where it was ++ .if !\mask_minus1 ++ MASK .req ST1 ++ .set DEFINED_MASK, 1 ++ .endif ++ .else ++ .if \shift_0 ++ .if !\mask_minus1 ++ // AC1 is unused with shift 0 ++ MASK .req AC1 ++ .set DEFINED_MASK, 1 ++ .set SHUFFLE_MASK, 1 ++ .endif ++ .elseif \shift_8 ++ .if !\mask_minus1 ++ .if TOTAL_TAPS <= 4 ++ // All coefficients are preloaded (so pointer not needed) ++ MASK .req PCO ++ .set DEFINED_MASK, 1 ++ .set SHUFFLE_MASK, 1 ++ .else ++ .set SPILL_MASK, 1 ++ .endif ++ .endif ++ .else // shift not 0 or 8 ++ .if TOTAL_TAPS <= 3 ++ // All coefficients are preloaded, and at least one CO register is unused ++ .if \fir_taps & 1 ++ SHIFT .req CO0 ++ .set DEFINED_SHIFT, 1 ++ .set SHUFFLE_SHIFT, 1 ++ .else ++ SHIFT .req CO3 ++ .set DEFINED_SHIFT, 1 ++ .set SHUFFLE_SHIFT, 1 ++ .endif ++ .if !\mask_minus1 ++ MASK .req PCO ++ .set DEFINED_MASK, 1 ++ .set SHUFFLE_MASK, 1 ++ .endif ++ .elseif TOTAL_TAPS == 4 ++ // All coefficients are preloaded ++ SHIFT .req PCO ++ .set DEFINED_SHIFT, 1 ++ .set SHUFFLE_SHIFT, 1 ++ .if !\mask_minus1 ++ .set SPILL_MASK, 1 ++ .endif ++ .else ++ .set SPILL_SHIFT, 1 ++ .if !\mask_minus1 ++ .set SPILL_MASK, 1 ++ .endif ++ .endif ++ .endif ++ .endif ++ .if SPILL_SHIFT ++ SHIFT .req ST0 ++ .set DEFINED_SHIFT, 1 ++ .endif ++ .if SPILL_MASK ++ MASK .req ST1 ++ .set DEFINED_MASK, 1 ++ .endif ++ ++ // Preload coefficients if possible ++ .if TOTAL_TAPS <= 4 ++ .set OFFSET_CO, 0 ++ .if \fir_taps & 1 ++ .set LOAD_REG, 1 ++ .else ++ .set LOAD_REG, 0 ++ .endif ++ .rept \fir_taps ++ load CO, LOAD_REG, PCO, OFFSET_CO ++ .set LOAD_REG, (LOAD_REG + 1) & 3 ++ .set OFFSET_CO, OFFSET_CO + 4 ++ .endr ++ .set OFFSET_CO, 4 * MAX_FIR_ORDER ++ .rept \iir_taps ++ load CO, LOAD_REG, PCO, OFFSET_CO ++ .set LOAD_REG, (LOAD_REG + 1) & 3 ++ .set OFFSET_CO, OFFSET_CO + 4 ++ .endr ++ .endif ++ ++ // Move mask/shift to final positions if necessary ++ // Need to do this after preloading, because in some cases we ++ // reuse the coefficient pointer register ++ .if SHUFFLE_SHIFT ++ mov SHIFT, ST0 ++ .endif ++ .if SHUFFLE_MASK ++ mov MASK, ST1 ++ .endif ++ ++ // Begin loop ++01: ++ .if TOTAL_TAPS == 0 ++ // Things simplify a lot in this case ++ // In fact this could be pipelined further if it's worth it... ++ ldr ST0, [PSAMP] ++ subs I, I, #1 ++ .if !\mask_minus1 ++ and ST0, ST0, MASK ++ .endif ++ str ST0, [PST, #-4]! ++ str ST0, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)] ++ str ST0, [PSAMP], #4 * MAX_CHANNELS ++ bne 01b ++ .else ++ .if \fir_taps & 1 ++ .set LOAD_REG, 1 ++ .else ++ .set LOAD_REG, 0 ++ .endif ++ .set LOAD_BANK, 0 ++ .set FIR_REMAIN, \fir_taps ++ .set IIR_REMAIN, \iir_taps ++ .if FIR_REMAIN == 0 // only IIR terms ++ .set OFFSET_CO, 4 * MAX_FIR_ORDER ++ .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER) ++ .else ++ .set OFFSET_CO, 0 ++ .set OFFSET_ST, 0 ++ .endif ++ .set MUL_REG, LOAD_REG ++ .set COUNTER, 0 ++ .rept TOTAL_TAPS + 2 ++ // Do load(s) ++ .if FIR_REMAIN != 0 || IIR_REMAIN != 0 ++ .if COUNTER == 0 ++ .if TOTAL_TAPS > 4 ++ load CO, LOAD_REG, PCO, OFFSET_CO ++ .endif ++ load ST, LOAD_REG, PST, OFFSET_ST ++ inc 1 ++ .elseif COUNTER == 1 && (\fir_taps & 1) == 0 ++ .if TOTAL_TAPS > 4 ++ load CO, LOAD_REG, PCO, OFFSET_CO ++ .endif ++ load ST, LOAD_REG, PST, OFFSET_ST ++ inc 1 ++ .elseif LOAD_BANK == 0 ++ .if TOTAL_TAPS > 4 ++ .if FIR_REMAIN == 0 && IIR_REMAIN == 1 ++ load CO, LOAD_REG, PCO, OFFSET_CO ++ .else ++ loadd CO, LOAD_REG, PCO, OFFSET_CO ++ .endif ++ .endif ++ .set LOAD_BANK, 1 ++ .else ++ .if FIR_REMAIN == 0 && IIR_REMAIN == 1 ++ load ST, LOAD_REG, PST, OFFSET_ST ++ inc 1 ++ .else ++ loadd ST, LOAD_REG, PST, OFFSET_ST ++ inc 2 ++ .endif ++ .set LOAD_BANK, 0 ++ .endif ++ .endif ++ ++ // Do interleaved multiplies, slightly delayed ++ .if COUNTER >= 2 ++ multiply MUL_REG, COUNTER > 2, !\shift_0 ++ .set MUL_REG, (MUL_REG + 1) & 3 ++ .endif ++ .set COUNTER, COUNTER + 1 ++ .endr ++ ++ // Post-process the result of the multiplies ++ .if SPILL_SHIFT ++ ldr SHIFT, [sp, #9*4 + 0*4] ++ .endif ++ .if SPILL_MASK ++ ldr MASK, [sp, #9*4 + 1*4] ++ .endif ++ ldr ST2, [PSAMP] ++ subs I, I, #1 ++ .if \shift_8 ++ mov AC0, AC0, lsr #8 ++ orr AC0, AC0, AC1, lsl #24 ++ .elseif !\shift_0 ++ rsb ST3, SHIFT, #32 ++ mov AC0, AC0, lsr SHIFT ++ orr AC0, AC0, AC1, lsl ST3 ++ .endif ++ .if \mask_minus1 ++ add ST3, ST2, AC0 ++ .else ++ add ST2, ST2, AC0 ++ and ST3, ST2, MASK ++ sub ST2, ST3, AC0 ++ .endif ++ str ST3, [PST, #-4]! ++ str ST2, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)] ++ str ST3, [PSAMP], #4 * MAX_CHANNELS ++ bne 01b ++ .endif ++ b 99f ++ ++ .if DEFINED_SHIFT ++ .unreq SHIFT ++ .endif ++ .if DEFINED_MASK ++ .unreq MASK ++ .endif ++.endm ++ ++.macro switch_on_fir_taps mask_minus1, shift_0, shift_8, iir_taps ++ ldr pc, [pc, a3, LSL #2] // firorder is in range 0-(8-iir_taps) ++ .word 0 ++ .word 70f ++ .word 71f ++ .word 72f ++ .word 73f ++ .word 74f ++ .word 75f ++ .if \iir_taps <= 2 ++ .word 76f ++ .if \iir_taps <= 1 ++ .word 77f ++ .if \iir_taps == 0 ++ .word 78f ++ .endif ++ .endif ++ .endif ++70: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 0 ++71: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 1 ++72: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 2 ++73: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 3 ++74: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 4 ++75: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 5 ++ .if \iir_taps <= 2 ++76: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 6 ++ .if \iir_taps <= 1 ++77: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 7 ++ .if \iir_taps == 0 ++78: implement_filter \mask_minus1, \shift_0, \shift_8, \iir_taps, 8 ++ .endif ++ .endif ++ .endif ++.endm ++ ++.macro switch_on_iir_taps mask_minus1, shift_0, shift_8 ++ ldr pc, [pc, a4, LSL #2] // irorder is in range 0-3 ++ .word 0 ++ .word 60f ++ .word 61f ++ .word 62f ++ .word 63f ++60: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 0 ++61: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 1 ++62: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 2 ++63: switch_on_fir_taps \mask_minus1, \shift_0, \shift_8, 3 ++.endm ++ ++/* void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff, ++ * int firorder, int iirorder, ++ * unsigned int filter_shift, int32_t mask, ++ * int blocksize, int32_t *sample_buffer); ++ */ ++function ff_mlp_filter_channel_arm, export=1 ++ push {v1-fp,lr} ++ add v1, sp, #9*4 // point at arguments on stack ++ ldm v1, {ST0,ST1,I,PSAMP} ++ cmp ST1, #-1 ++ bne 30f ++ movs ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8 ++ bne 20f ++ bcs 10f ++ switch_on_iir_taps 1, 1, 0 ++10: switch_on_iir_taps 1, 0, 1 ++20: switch_on_iir_taps 1, 0, 0 ++30: movs ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8 ++ bne 50f ++ bcs 40f ++ switch_on_iir_taps 0, 1, 0 ++40: switch_on_iir_taps 0, 0, 1 ++50: switch_on_iir_taps 0, 0, 0 ++99: pop {v1-fp,pc} ++endfunc ++ ++ .unreq PST ++ .unreq PCO ++ .unreq AC0 ++ .unreq AC1 ++ .unreq CO0 ++ .unreq CO1 ++ .unreq CO2 ++ .unreq CO3 ++ .unreq ST0 ++ .unreq ST1 ++ .unreq ST2 ++ .unreq ST3 ++ .unreq I ++ .unreq PSAMP +diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c +new file mode 100644 +index 0000000..f0ea285 +--- /dev/null ++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2014 RISC OS Open Ltd ++ * Author: Ben Avison <bavison@riscosopen.org> ++ * ++ * This file is part of Libav. ++ * ++ * Libav is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * Libav is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with Libav; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include <stdint.h> ++ ++#include "libavutil/arm/cpu.h" ++#include "libavutil/attributes.h" ++#include "libavcodec/mlpdsp.h" ++ ++void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff, ++ int firorder, int iirorder, ++ unsigned int filter_shift, int32_t mask, ++ int blocksize, int32_t *sample_buffer); ++ ++av_cold void ff_mlpdsp_init_arm(MLPDSPContext *c) ++{ ++ c->mlp_filter_channel = ff_mlp_filter_channel_arm; ++} +diff --git a/lib/ffmpeg/libavcodec/mlpdsp.h b/lib/ffmpeg/libavcodec/mlpdsp.h +index 84a8aa3..129bcfe 100644 +--- a/lib/ffmpeg/libavcodec/mlpdsp.h ++++ b/lib/ffmpeg/libavcodec/mlpdsp.h +@@ -32,6 +32,7 @@ typedef struct MLPDSPContext { + } MLPDSPContext; + + void ff_mlpdsp_init(MLPDSPContext *c); ++void ff_mlpdsp_init_arm(MLPDSPContext *c); + void ff_mlpdsp_init_x86(MLPDSPContext *c); + + #endif /* AVCODEC_MLPDSP_H */ +-- +1.9.3 + + +From 904cb11e58484c5d0bca17b8c209916d106d2079 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 19 Mar 2014 17:48:54 +0000 +Subject: [PATCH 09/94] truehd: break out part of rematrix_channels into + platform-specific callback. + +Verified with profiling that this doesn't have a measurable effect upon +overall performance. +--- + lib/ffmpeg/libavcodec/mlpdec.c | 37 ++++++++++++------------------------- + lib/ffmpeg/libavcodec/mlpdsp.c | 35 ++++++++++++++++++++++++++++++++++- + lib/ffmpeg/libavcodec/mlpdsp.h | 23 +++++++++++++++++++++++ + 3 files changed, 69 insertions(+), 26 deletions(-) + +diff --git a/lib/ffmpeg/libavcodec/mlpdec.c b/lib/ffmpeg/libavcodec/mlpdec.c +index c763624..e9343a5 100644 +--- a/lib/ffmpeg/libavcodec/mlpdec.c ++++ b/lib/ffmpeg/libavcodec/mlpdec.c +@@ -958,7 +958,7 @@ static void fill_noise_buffer(MLPDecodeContext *m, unsigned int substr) + static void rematrix_channels(MLPDecodeContext *m, unsigned int substr) + { + SubStream *s = &m->substream[substr]; +- unsigned int mat, src_ch, i; ++ unsigned int mat; + unsigned int maxchan; + + maxchan = s->max_matrix_channel; +@@ -970,31 +970,18 @@ static void rematrix_channels(MLPDecodeContext *m, unsigned int substr) + } + + for (mat = 0; mat < s->num_primitive_matrices; mat++) { +- int matrix_noise_shift = s->matrix_noise_shift[mat]; + unsigned int dest_ch = s->matrix_out_ch[mat]; +- int32_t mask = MSB_MASK(s->quant_step_size[dest_ch]); +- int32_t *coeffs = s->matrix_coeff[mat]; +- int index = s->num_primitive_matrices - mat; +- int index2 = 2 * index + 1; +- +- /* TODO: DSPContext? */ +- +- for (i = 0; i < s->blockpos; i++) { +- int32_t bypassed_lsb = m->bypassed_lsbs[i][mat]; +- int32_t *samples = m->sample_buffer[i]; +- int64_t accum = 0; +- +- for (src_ch = 0; src_ch <= maxchan; src_ch++) +- accum += (int64_t) samples[src_ch] * coeffs[src_ch]; +- +- if (matrix_noise_shift) { +- index &= m->access_unit_size_pow2 - 1; +- accum += m->noise_buffer[index] << (matrix_noise_shift + 7); +- index += index2; +- } +- +- samples[dest_ch] = ((accum >> 14) & mask) + bypassed_lsb; +- } ++ m->dsp.mlp_rematrix_channel(&m->sample_buffer[0][0], ++ s->matrix_coeff[mat], ++ &m->bypassed_lsbs[0][mat], ++ m->noise_buffer, ++ s->num_primitive_matrices - mat, ++ dest_ch, ++ s->blockpos, ++ maxchan, ++ s->matrix_noise_shift[mat], ++ m->access_unit_size_pow2, ++ MSB_MASK(s->quant_step_size[dest_ch])); + } + } + +diff --git a/lib/ffmpeg/libavcodec/mlpdsp.c b/lib/ffmpeg/libavcodec/mlpdsp.c +index 9a376e2..1f912fb 100644 +--- a/lib/ffmpeg/libavcodec/mlpdsp.c ++++ b/lib/ffmpeg/libavcodec/mlpdsp.c +@@ -56,9 +56,42 @@ static void ff_mlp_filter_channel(int32_t *state, const int32_t *coeff, + } + } + +-void ff_mlpdsp_init(MLPDSPContext *c) ++void ff_mlp_rematrix_channel(int32_t *samples, ++ const int32_t *coeffs, ++ const uint8_t *bypassed_lsbs, ++ const int8_t *noise_buffer, ++ int index, ++ unsigned int dest_ch, ++ uint16_t blockpos, ++ unsigned int maxchan, ++ int matrix_noise_shift, ++ int access_unit_size_pow2, ++ int32_t mask) ++{ ++ unsigned int src_ch, i; ++ int index2 = 2 * index + 1; ++ for (i = 0; i < blockpos; i++) { ++ int64_t accum = 0; ++ ++ for (src_ch = 0; src_ch <= maxchan; src_ch++) ++ accum += (int64_t) samples[src_ch] * coeffs[src_ch]; ++ ++ if (matrix_noise_shift) { ++ index &= access_unit_size_pow2 - 1; ++ accum += noise_buffer[index] << (matrix_noise_shift + 7); ++ index += index2; ++ } ++ ++ samples[dest_ch] = ((accum >> 14) & mask) + *bypassed_lsbs; ++ bypassed_lsbs += MAX_CHANNELS; ++ samples += MAX_CHANNELS; ++ } ++} ++ ++av_cold void ff_mlpdsp_init(MLPDSPContext *c) + { + c->mlp_filter_channel = ff_mlp_filter_channel; ++ c->mlp_rematrix_channel = ff_mlp_rematrix_channel; + if (ARCH_X86) + ff_mlpdsp_init_x86(c); + } +diff --git a/lib/ffmpeg/libavcodec/mlpdsp.h b/lib/ffmpeg/libavcodec/mlpdsp.h +index 129bcfe..f98e9be 100644 +--- a/lib/ffmpeg/libavcodec/mlpdsp.h ++++ b/lib/ffmpeg/libavcodec/mlpdsp.h +@@ -24,11 +24,34 @@ + + #include <stdint.h> + ++void ff_mlp_rematrix_channel(int32_t *samples, ++ const int32_t *coeffs, ++ const uint8_t *bypassed_lsbs, ++ const int8_t *noise_buffer, ++ int index, ++ unsigned int dest_ch, ++ uint16_t blockpos, ++ unsigned int maxchan, ++ int matrix_noise_shift, ++ int access_unit_size_pow2, ++ int32_t mask); ++ + typedef struct MLPDSPContext { + void (*mlp_filter_channel)(int32_t *state, const int32_t *coeff, + int firorder, int iirorder, + unsigned int filter_shift, int32_t mask, + int blocksize, int32_t *sample_buffer); ++ void (*mlp_rematrix_channel)(int32_t *samples, ++ const int32_t *coeffs, ++ const uint8_t *bypassed_lsbs, ++ const int8_t *noise_buffer, ++ int index, ++ unsigned int dest_ch, ++ uint16_t blockpos, ++ unsigned int maxchan, ++ int matrix_noise_shift, ++ int access_unit_size_pow2, ++ int32_t mask); + } MLPDSPContext; + + void ff_mlpdsp_init(MLPDSPContext *c); +-- +1.9.3 + + +From 0bb8daacca4b35d716addbc591fec43fd4fe6467 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 19 Mar 2014 17:49:48 +0000 +Subject: [PATCH 10/94] truehd: add hand-scheduled ARM asm version of + ff_mlp_rematrix_channel. + +Profiling results for overall audio decode and the rematrix_channels function +in particular are as follows: + + Before After + Mean StdDev Mean StdDev Confidence Change +6:2 total 370.8 17.0 348.8 20.1 99.9% +6.3% +6:2 function 46.4 8.4 45.8 6.6 18.0% +1.2% (insignificant) +8:2 total 343.2 19.0 339.1 15.4 54.7% +1.2% (insignificant) +8:2 function 38.9 3.9 40.2 6.9 52.4% -3.2% (insignificant) +6:6 total 658.4 15.7 604.6 20.8 100.0% +8.9% +6:6 function 109.0 8.7 59.5 5.4 100.0% +83.3% +8:8 total 896.2 24.5 766.4 17.6 100.0% +16.9% +8:8 function 223.4 12.8 93.8 5.0 100.0% +138.3% + +The assembly version has also been tested with a fuzz tester to ensure that +any combinations of inputs not exercised by my available test streams still +generate mathematically identical results to the C version. +--- + lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S | 231 ++++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c | 12 ++ + 2 files changed, 243 insertions(+) + +diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S +index 114496f..10008fe 100644 +--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S ++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S +@@ -428,3 +428,234 @@ endfunc + .unreq ST3 + .unreq I + .unreq PSAMP ++ ++/********************************************************************/ ++ ++PSA .req a1 // samples ++PCO .req a2 // coeffs ++PBL .req a3 // bypassed_lsbs ++INDEX .req a4 ++CO0 .req v1 ++CO1 .req v2 ++CO2 .req v3 ++CO3 .req v4 ++SA0 .req v5 ++SA1 .req v6 ++SA2 .req sl ++SA3 .req fp ++AC0 .req ip ++AC1 .req lr ++NOISE .req SA0 ++LSB .req SA1 ++DCH .req SA2 // dest_ch ++MASK .req SA3 ++ ++ // INDEX is used as follows: ++ // bits 0..6 index2 (values up to 17, but wider so that we can ++ // add to index field without needing to mask) ++ // bits 7..14 i (values up to 160) ++ // bit 15 underflow detect for i ++ // bits 25..31 (if access_unit_size_pow2 == 128) \ index ++ // bits 26..31 (if access_unit_size_pow2 == 64) / ++ ++.macro implement_rematrix shift, index_mask, mask_minus1, maxchan ++ .if \maxchan == 1 ++ // We can just leave the coefficients in registers in this case ++ ldrd CO0, CO1, [PCO] ++ .endif ++1: ++ .if \maxchan == 1 ++ ldrd SA0, SA1, [PSA] ++ smull AC0, AC1, CO0, SA0 ++ .elseif \maxchan == 5 ++ ldr CO0, [PCO, #0] ++ ldr SA0, [PSA, #0] ++ ldr CO1, [PCO, #4] ++ ldr SA1, [PSA, #4] ++ ldrd CO2, CO3, [PCO, #8] ++ smull AC0, AC1, CO0, SA0 ++ ldrd SA2, SA3, [PSA, #8] ++ smlal AC0, AC1, CO1, SA1 ++ ldrd CO0, CO1, [PCO, #16] ++ smlal AC0, AC1, CO2, SA2 ++ ldrd SA0, SA1, [PSA, #16] ++ smlal AC0, AC1, CO3, SA3 ++ smlal AC0, AC1, CO0, SA0 ++ .else // \maxchan == 7 ++ ldr CO2, [PCO, #0] ++ ldr SA2, [PSA, #0] ++ ldr CO3, [PCO, #4] ++ ldr SA3, [PSA, #4] ++ ldrd CO0, CO1, [PCO, #8] ++ smull AC0, AC1, CO2, SA2 ++ ldrd SA0, SA1, [PSA, #8] ++ smlal AC0, AC1, CO3, SA3 ++ ldrd CO2, CO3, [PCO, #16] ++ smlal AC0, AC1, CO0, SA0 ++ ldrd SA2, SA3, [PSA, #16] ++ smlal AC0, AC1, CO1, SA1 ++ ldrd CO0, CO1, [PCO, #24] ++ smlal AC0, AC1, CO2, SA2 ++ ldrd SA0, SA1, [PSA, #24] ++ smlal AC0, AC1, CO3, SA3 ++ smlal AC0, AC1, CO0, SA0 ++ .endif ++ ldm sp, {NOISE, DCH, MASK} ++ smlal AC0, AC1, CO1, SA1 ++ .if \shift != 0 ++ .if \index_mask == 63 ++ add NOISE, NOISE, INDEX, lsr #32-6 ++ ldrb LSB, [PBL], #MAX_CHANNELS ++ ldrsb NOISE, [NOISE] ++ add INDEX, INDEX, INDEX, lsl #32-6 ++ .else // \index_mask == 127 ++ add NOISE, NOISE, INDEX, lsr #32-7 ++ ldrb LSB, [PBL], #MAX_CHANNELS ++ ldrsb NOISE, [NOISE] ++ add INDEX, INDEX, INDEX, lsl #32-7 ++ .endif ++ sub INDEX, INDEX, #1<<7 ++ adds AC0, AC0, NOISE, lsl #\shift + 7 ++ adc AC1, AC1, NOISE, asr #31 ++ .else ++ ldrb LSB, [PBL], #MAX_CHANNELS ++ sub INDEX, INDEX, #1<<7 ++ .endif ++ add PSA, PSA, #MAX_CHANNELS*4 ++ mov AC0, AC0, lsr #14 ++ orr AC0, AC0, AC1, lsl #18 ++ .if !\mask_minus1 ++ and AC0, AC0, MASK ++ .endif ++ add AC0, AC0, LSB ++ tst INDEX, #1<<15 ++ str AC0, [PSA, DCH, lsl #2] // DCH is precompensated for the early increment of PSA ++ beq 1b ++ b 98f ++.endm ++ ++.macro switch_on_maxchan shift, index_mask, mask_minus1 ++ cmp v4, #5 ++ blo 51f ++ beq 50f ++ implement_rematrix \shift, \index_mask, \mask_minus1, 7 ++50: implement_rematrix \shift, \index_mask, \mask_minus1, 5 ++51: implement_rematrix \shift, \index_mask, \mask_minus1, 1 ++.endm ++ ++.macro switch_on_mask shift, index_mask ++ cmp sl, #-1 ++ bne 40f ++ switch_on_maxchan \shift, \index_mask, 1 ++40: switch_on_maxchan \shift, \index_mask, 0 ++.endm ++ ++.macro switch_on_au_size shift ++ .if \shift == 0 ++ switch_on_mask \shift, undefined ++ .else ++ teq v6, #64 ++ bne 30f ++ orr INDEX, INDEX, v1, lsl #32-6 ++ switch_on_mask \shift, 63 ++30: orr INDEX, INDEX, v1, lsl #32-7 ++ switch_on_mask \shift, 127 ++ .endif ++.endm ++ ++/* void ff_mlp_rematrix_channel_arm(int32_t *samples, ++ * const int32_t *coeffs, ++ * const uint8_t *bypassed_lsbs, ++ * const int8_t *noise_buffer, ++ * int index, ++ * unsigned int dest_ch, ++ * uint16_t blockpos, ++ * unsigned int maxchan, ++ * int matrix_noise_shift, ++ * int access_unit_size_pow2, ++ * int32_t mask); ++ */ ++function ff_mlp_rematrix_channel_arm, export=1 ++ push {v1-fp,lr} ++ add v1, sp, #9*4 // point at arguments on stack ++ ldm v1, {v1-sl} ++ teq v4, #1 ++ teqne v4, #5 ++ teqne v4, #7 ++ bne 99f ++ teq v6, #64 ++ teqne v6, #128 ++ bne 99f ++ sub v2, v2, #MAX_CHANNELS ++ push {a4,v2,sl} // initialise NOISE,DCH,MASK; make sp dword-aligned ++ movs INDEX, v3, lsl #7 ++ beq 98f // just in case, do nothing if blockpos = 0 ++ subs INDEX, INDEX, #1<<7 // offset by 1 so we borrow at the right time ++ adc lr, v1, v1 // calculate index2 (C was set by preceding subs) ++ orr INDEX, INDEX, lr ++ // Switch on matrix_noise_shift: values 0 and 1 are ++ // disproportionately common so do those in a form the branch ++ // predictor can accelerate. Values can only go up to 15. ++ cmp v5, #1 ++ beq 11f ++ blo 10f ++ ldr pc, [pc, v5, lsl #2] ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 12f ++ .word 13f ++ .word 14f ++ .word 15f ++ .word 16f ++ .word 17f ++ .word 18f ++ .word 19f ++ .word 20f ++ .word 21f ++ .word 22f ++ .word 23f ++ .word 24f ++ .word 25f ++10: switch_on_au_size 0 ++11: switch_on_au_size 1 ++12: switch_on_au_size 2 ++13: switch_on_au_size 3 ++14: switch_on_au_size 4 ++15: switch_on_au_size 5 ++16: switch_on_au_size 6 ++17: switch_on_au_size 7 ++18: switch_on_au_size 8 ++19: switch_on_au_size 9 ++20: switch_on_au_size 10 ++21: switch_on_au_size 11 ++22: switch_on_au_size 12 ++23: switch_on_au_size 13 ++24: switch_on_au_size 14 ++25: switch_on_au_size 15 ++ ++98: add sp, sp, #3*4 ++ pop {v1-fp,pc} ++99: // Can't handle these parameters, drop back to C ++ pop {v1-fp,lr} ++ b X(ff_mlp_rematrix_channel) ++endfunc ++ ++ .unreq PSA ++ .unreq PCO ++ .unreq PBL ++ .unreq INDEX ++ .unreq CO0 ++ .unreq CO1 ++ .unreq CO2 ++ .unreq CO3 ++ .unreq SA0 ++ .unreq SA1 ++ .unreq SA2 ++ .unreq SA3 ++ .unreq AC0 ++ .unreq AC1 ++ .unreq NOISE ++ .unreq LSB ++ .unreq DCH ++ .unreq MASK +diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c +index f0ea285..268dfdd 100644 +--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c ++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c +@@ -29,8 +29,20 @@ void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff, + int firorder, int iirorder, + unsigned int filter_shift, int32_t mask, + int blocksize, int32_t *sample_buffer); ++void ff_mlp_rematrix_channel_arm(int32_t *samples, ++ const int32_t *coeffs, ++ const uint8_t *bypassed_lsbs, ++ const int8_t *noise_buffer, ++ int index, ++ unsigned int dest_ch, ++ uint16_t blockpos, ++ unsigned int maxchan, ++ int matrix_noise_shift, ++ int access_unit_size_pow2, ++ int32_t mask); + + av_cold void ff_mlpdsp_init_arm(MLPDSPContext *c) + { + c->mlp_filter_channel = ff_mlp_filter_channel_arm; ++ c->mlp_rematrix_channel = ff_mlp_rematrix_channel_arm; + } +-- +1.9.3 + + +From 034e1a8920aec0fa36ffc7da8f63e48c68364e15 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 19 Mar 2014 17:50:36 +0000 +Subject: [PATCH 11/94] truehd: tune VLC decoding for ARM. + +Profiling on a Raspberry Pi revealed the best performance to correspond +with VLC_BITS = 5. Results for overall audio decode and the get_vlc2 function +in particular are as follows: + + Before After + Mean StdDev Mean StdDev Confidence Change +6:2 total 348.8 20.1 339.6 15.1 88.8% +2.7% (insignificant) +6:2 function 38.1 8.1 26.4 4.1 100.0% +44.5% +8:2 total 339.1 15.4 324.5 15.5 99.4% +4.5% +8:2 function 33.8 7.0 27.3 5.6 99.7% +23.6% +6:6 total 604.6 20.8 572.8 20.6 100.0% +5.6% +6:6 function 95.8 8.4 68.9 8.2 100.0% +39.1% +8:8 total 766.4 17.6 741.5 21.2 100.0% +3.4% +8:8 function 106.0 11.4 86.1 9.9 100.0% +23.1% +--- + lib/ffmpeg/libavcodec/mlpdec.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/lib/ffmpeg/libavcodec/mlpdec.c b/lib/ffmpeg/libavcodec/mlpdec.c +index e9343a5..a998dac 100644 +--- a/lib/ffmpeg/libavcodec/mlpdec.c ++++ b/lib/ffmpeg/libavcodec/mlpdec.c +@@ -36,9 +36,16 @@ + #include "mlp_parser.h" + #include "mlpdsp.h" + #include "mlp.h" ++#include "config.h" + + /** number of bits used for VLC lookup - longest Huffman code is 9 */ ++#if ARCH_ARM == 1 ++#define VLC_BITS 5 ++#define VLC_STATIC_SIZE 64 ++#else + #define VLC_BITS 9 ++#define VLC_STATIC_SIZE 512 ++#endif + + typedef struct SubStream { + /// Set if a valid restart header has been read. Otherwise the substream cannot be decoded. +@@ -190,13 +197,13 @@ static av_cold void init_static(void) + if (!huff_vlc[0].bits) { + INIT_VLC_STATIC(&huff_vlc[0], VLC_BITS, 18, + &ff_mlp_huffman_tables[0][0][1], 2, 1, +- &ff_mlp_huffman_tables[0][0][0], 2, 1, 512); ++ &ff_mlp_huffman_tables[0][0][0], 2, 1, VLC_STATIC_SIZE); + INIT_VLC_STATIC(&huff_vlc[1], VLC_BITS, 16, + &ff_mlp_huffman_tables[1][0][1], 2, 1, +- &ff_mlp_huffman_tables[1][0][0], 2, 1, 512); ++ &ff_mlp_huffman_tables[1][0][0], 2, 1, VLC_STATIC_SIZE); + INIT_VLC_STATIC(&huff_vlc[2], VLC_BITS, 15, + &ff_mlp_huffman_tables[2][0][1], 2, 1, +- &ff_mlp_huffman_tables[2][0][0], 2, 1, 512); ++ &ff_mlp_huffman_tables[2][0][0], 2, 1, VLC_STATIC_SIZE); + } + + ff_mlp_init_crc(); +-- +1.9.3 + + +From 25ab0401ebb7f035bcf7291452e6772a9c7b233a Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 19 Mar 2014 17:54:07 +0000 +Subject: [PATCH 12/94] truehd: break out part of output_data into + platform-specific callback. + +Verified with profiling that this doesn't have a measurable effect upon +overall performance. +--- + lib/ffmpeg/libavcodec/mlpdec.c | 40 +++++++++++++++++++++++----------------- + lib/ffmpeg/libavcodec/mlpdsp.c | 36 ++++++++++++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/mlpdsp.h | 22 ++++++++++++++++++++++ + 3 files changed, 81 insertions(+), 17 deletions(-) + +diff --git a/lib/ffmpeg/libavcodec/mlpdec.c b/lib/ffmpeg/libavcodec/mlpdec.c +index a998dac..6d7c803 100644 +--- a/lib/ffmpeg/libavcodec/mlpdec.c ++++ b/lib/ffmpeg/libavcodec/mlpdec.c +@@ -359,6 +359,10 @@ static int read_major_sync(MLPDecodeContext *m, GetBitContext *gb) + m->avctx->sample_fmt = AV_SAMPLE_FMT_S32; + else + m->avctx->sample_fmt = AV_SAMPLE_FMT_S16; ++ m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(m->substream[m->max_decoded_substream].max_matrix_channel, ++ m->avctx->sample_fmt == AV_SAMPLE_FMT_S32, ++ m->substream[m->max_decoded_substream].ch_assign, ++ m->substream[m->max_decoded_substream].output_shift); + + m->params_valid = 1; + for (substr = 0; substr < MAX_SUBSTREAMS; substr++) +@@ -553,6 +557,10 @@ static int read_restart_header(MLPDecodeContext *m, GetBitContext *gbp, + if (substr == m->max_decoded_substream) { + m->avctx->channels = s->max_matrix_channel + 1; + m->avctx->channel_layout = s->ch_layout; ++ m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(s->max_matrix_channel, ++ m->avctx->sample_fmt == AV_SAMPLE_FMT_S32, ++ s->ch_assign, ++ s->output_shift); + + if (m->avctx->codec_id == AV_CODEC_ID_MLP && m->needs_reordering) { + if (m->avctx->channel_layout == (AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY) || +@@ -798,9 +806,15 @@ static int read_decoding_params(MLPDecodeContext *m, GetBitContext *gbp, + return ret; + + if (s->param_presence_flags & PARAM_OUTSHIFT) +- if (get_bits1(gbp)) ++ if (get_bits1(gbp)) { + for (ch = 0; ch <= s->max_matrix_channel; ch++) + s->output_shift[ch] = get_sbits(gbp, 4); ++ if (substr == m->max_decoded_substream) ++ m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(s->max_matrix_channel, ++ m->avctx->sample_fmt == AV_SAMPLE_FMT_S32, ++ s->ch_assign, ++ s->output_shift); ++ } + + if (s->param_presence_flags & PARAM_QUANTSTEP) + if (get_bits1(gbp)) +@@ -999,9 +1013,6 @@ static int output_data(MLPDecodeContext *m, unsigned int substr, + { + AVCodecContext *avctx = m->avctx; + SubStream *s = &m->substream[substr]; +- unsigned int i, out_ch = 0; +- int32_t *data_32; +- int16_t *data_16; + int ret; + int is32 = (m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); + +@@ -1021,19 +1032,14 @@ static int output_data(MLPDecodeContext *m, unsigned int substr, + av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + return ret; + } +- data_32 = (int32_t *)frame->data[0]; +- data_16 = (int16_t *)frame->data[0]; +- +- for (i = 0; i < s->blockpos; i++) { +- for (out_ch = 0; out_ch <= s->max_matrix_channel; out_ch++) { +- int mat_ch = s->ch_assign[out_ch]; +- int32_t sample = m->sample_buffer[i][mat_ch] +- << s->output_shift[mat_ch]; +- s->lossless_check_data ^= (sample & 0xffffff) << mat_ch; +- if (is32) *data_32++ = sample << 8; +- else *data_16++ = sample >> 8; +- } +- } ++ s->lossless_check_data = m->dsp.mlp_pack_output(s->lossless_check_data, ++ m->sample_buffer, ++ frame->data[0], ++ s->blockpos, ++ s->max_matrix_channel, ++ is32, ++ s->ch_assign, ++ s->output_shift); + + *got_frame_ptr = 1; + +diff --git a/lib/ffmpeg/libavcodec/mlpdsp.c b/lib/ffmpeg/libavcodec/mlpdsp.c +index 1f912fb..2bb5cec 100644 +--- a/lib/ffmpeg/libavcodec/mlpdsp.c ++++ b/lib/ffmpeg/libavcodec/mlpdsp.c +@@ -88,10 +88,46 @@ void ff_mlp_rematrix_channel(int32_t *samples, + } + } + ++static int32_t (*mlp_select_pack_output(uint8_t max_matrix_channel, ++ int is32, ++ uint8_t *ch_assign, ++ int8_t *output_shift))(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *) ++{ ++ return ff_mlp_pack_output; ++} ++ ++int32_t ff_mlp_pack_output(int32_t lossless_check_data, ++ int32_t (*sample_buffer)[MAX_CHANNELS], ++ void *data, ++ uint16_t blockpos, ++ uint8_t max_matrix_channel, ++ int is32, ++ uint8_t *ch_assign, ++ int8_t *output_shift) ++{ ++ unsigned int i, out_ch = 0; ++ int32_t *data_32 = (int32_t *)data; ++ int16_t *data_16 = (int16_t *)data; ++ ++ for (i = 0; i < blockpos; i++) { ++ for (out_ch = 0; out_ch <= max_matrix_channel; out_ch++) { ++ int mat_ch = ch_assign[out_ch]; ++ int32_t sample = sample_buffer[i][mat_ch] ++ << output_shift[mat_ch]; ++ lossless_check_data ^= (sample & 0xffffff) << mat_ch; ++ if (is32) *data_32++ = sample << 8; ++ else *data_16++ = sample >> 8; ++ } ++ } ++ return lossless_check_data; ++} ++ + av_cold void ff_mlpdsp_init(MLPDSPContext *c) + { + c->mlp_filter_channel = ff_mlp_filter_channel; + c->mlp_rematrix_channel = ff_mlp_rematrix_channel; ++ c->mlp_select_pack_output = mlp_select_pack_output; ++ c->mlp_pack_output = ff_mlp_pack_output; + if (ARCH_X86) + ff_mlpdsp_init_x86(c); + } +diff --git a/lib/ffmpeg/libavcodec/mlpdsp.h b/lib/ffmpeg/libavcodec/mlpdsp.h +index f98e9be..5bc901f 100644 +--- a/lib/ffmpeg/libavcodec/mlpdsp.h ++++ b/lib/ffmpeg/libavcodec/mlpdsp.h +@@ -23,6 +23,7 @@ + #define AVCODEC_MLPDSP_H + + #include <stdint.h> ++#include "mlp.h" + + void ff_mlp_rematrix_channel(int32_t *samples, + const int32_t *coeffs, +@@ -36,6 +37,15 @@ void ff_mlp_rematrix_channel(int32_t *samples, + int access_unit_size_pow2, + int32_t mask); + ++int32_t ff_mlp_pack_output(int32_t lossless_check_data, ++ int32_t (*sample_buffer)[MAX_CHANNELS], ++ void *data, ++ uint16_t blockpos, ++ uint8_t max_matrix_channel, ++ int is32, ++ uint8_t *ch_assign, ++ int8_t *output_shift); ++ + typedef struct MLPDSPContext { + void (*mlp_filter_channel)(int32_t *state, const int32_t *coeff, + int firorder, int iirorder, +@@ -52,6 +62,18 @@ typedef struct MLPDSPContext { + int matrix_noise_shift, + int access_unit_size_pow2, + int32_t mask); ++ int32_t (*(*mlp_select_pack_output)(uint8_t max_matrix_channel, ++ int is32, ++ uint8_t *ch_assign, ++ int8_t *output_shift))(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *); ++ int32_t (*mlp_pack_output)(int32_t lossless_check_data, ++ int32_t (*sample_buffer)[MAX_CHANNELS], ++ void *data, ++ uint16_t blockpos, ++ uint8_t max_matrix_channel, ++ int is32, ++ uint8_t *ch_assign, ++ int8_t *output_shift); + } MLPDSPContext; + + void ff_mlpdsp_init(MLPDSPContext *c); +-- +1.9.3 + + +From bdefac00779c5601816f949353d9bbeb3b199611 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 19 Mar 2014 17:54:59 +0000 +Subject: [PATCH 13/94] truehd: add hand-scheduled ARM asm version of + ff_mlp_pack_output. + +Profiling results for overall decode and the output_data function in +particular are as follows: + + Before After + Mean StdDev Mean StdDev Confidence Change +6:2 total 339.6 15.1 329.3 16.0 95.8% +3.1% (insignificant) +6:2 function 24.6 6.0 9.9 3.1 100.0% +148.5% +8:2 total 324.5 15.5 323.6 14.3 15.2% +0.3% (insignificant) +8:2 function 20.4 3.9 9.9 3.4 100.0% +104.7% +6:6 total 572.8 20.6 539.9 24.2 100.0% +6.1% +6:6 function 54.5 5.6 16.0 3.8 100.0% +240.9% +8:8 total 741.5 21.2 702.5 18.5 100.0% +5.6% +8:8 function 63.9 7.6 18.4 4.8 100.0% +247.3% + +The assembly version has also been tested with a fuzz tester to ensure that +any combinations of inputs not exercised by my available test streams still +generate mathematically identical results to the C version. +--- + lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S | 503 ++++++++++++++++++++++++++++ + lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c | 64 ++++ + 2 files changed, 567 insertions(+) + +diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S +index 10008fe..338d323 100644 +--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S ++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_arm.S +@@ -98,6 +98,26 @@ A .endif + .endif + .endm + ++.macro loadregoffsh2 group, index, base, offgroup, offindex ++ .altmacro ++ loadregoffsh2_ \group, %(\index), \base, \offgroup, %(\offindex) ++ .noaltmacro ++.endm ++ ++.macro loadregoffsh2_ group, index, base, offgroup, offindex ++ ldr \group\index, [\base, \offgroup\offindex, lsl #2] ++.endm ++ ++.macro eorlslreg check, data, group, index ++ .altmacro ++ eorlslreg_ \check, \data, \group, %(\index) ++ .noaltmacro ++.endm ++ ++.macro eorlslreg_ check, data, group, index ++ eor \check, \check, \data, lsl \group\index ++.endm ++ + // A macro to update the load register number and load offsets + + .macro inc howmany +@@ -659,3 +679,486 @@ endfunc + .unreq LSB + .unreq DCH + .unreq MASK ++ ++/********************************************************************/ ++ ++.macro decr_modulo var, by, modulus ++ .set \var, \var - \by ++ .if \var == 0 ++ .set \var, \modulus ++ .endif ++.endm ++ ++ .macro load_group1 size, channels, r0, r1, r2, r3, pointer_dead=0 ++ .if \size == 2 ++ ldrd \r0, \r1, [IN], #(\size + 8 - \channels) * 4 ++ .else // size == 4 ++ .if IDX1 > 4 || \channels==8 ++ ldm IN!, {\r0, \r1, \r2, \r3} ++ .else ++ ldm IN, {\r0, \r1, \r2, \r3} ++ .if !\pointer_dead ++ add IN, IN, #(4 + 8 - \channels) * 4 ++ .endif ++ .endif ++ .endif ++ decr_modulo IDX1, \size, \channels ++ .endm ++ ++ .macro load_group2 size, channels, r0, r1, r2, r3, pointer_dead=0 ++ .if \size == 2 ++ .if IDX1 > 2 ++ ldm IN!, {\r2, \r3} ++ .else ++//A .ifc \r2, ip ++//A .if \pointer_dead ++//A ldm IN, {\r2, \r3} ++//A .else ++//A ldr \r2, [IN], #4 ++//A ldr \r3, [IN], #(\size - 1 + 8 - \channels) * 4 ++//A .endif ++//A .else ++ ldrd \r2, \r3, [IN], #(\size + 8 - \channels) * 4 ++//A .endif ++ .endif ++ .endif ++ decr_modulo IDX1, \size, \channels ++ .endm ++ ++.macro implement_pack inorder, channels, shift ++.if \inorder ++.ifc \shift, mixed ++ ++CHECK .req a1 ++IN .req a2 ++OUT .req a3 ++COUNT .req a4 ++DAT0 .req v1 ++DAT1 .req v2 ++DAT2 .req v3 ++DAT3 .req v4 ++SHIFT0 .req v5 ++SHIFT1 .req v6 ++SHIFT2 .req sl ++SHIFT3 .req fp ++SHIFT4 .req ip ++SHIFT5 .req lr ++ ++ .macro output4words ++ .set SIZE_GROUP1, IDX1 ++ .if SIZE_GROUP1 > 4 ++ .set SIZE_GROUP1, 4 ++ .endif ++ .set SIZE_GROUP2, 4 - SIZE_GROUP1 ++ load_group1 SIZE_GROUP1, \channels, DAT0, DAT1, DAT2, DAT3 ++ load_group2 SIZE_GROUP2, \channels, DAT0, DAT1, DAT2, DAT3 ++ .if \channels == 2 ++ lsl DAT0, SHIFT0 ++ lsl DAT1, SHIFT1 ++ lsl DAT2, SHIFT0 ++ lsl DAT3, SHIFT1 ++ .elseif \channels == 6 ++ .if IDX2 == 6 ++ lsl DAT0, SHIFT0 ++ lsl DAT1, SHIFT1 ++ lsl DAT2, SHIFT2 ++ lsl DAT3, SHIFT3 ++ .elseif IDX2 == 2 ++ lsl DAT0, SHIFT4 ++ lsl DAT1, SHIFT5 ++ lsl DAT2, SHIFT0 ++ lsl DAT3, SHIFT1 ++ .else // IDX2 == 4 ++ lsl DAT0, SHIFT2 ++ lsl DAT1, SHIFT3 ++ lsl DAT2, SHIFT4 ++ lsl DAT3, SHIFT5 ++ .endif ++ .elseif \channels == 8 ++ .if IDX2 == 8 ++ uxtb SHIFT0, SHIFT4, ror #0 ++ uxtb SHIFT1, SHIFT4, ror #8 ++ uxtb SHIFT2, SHIFT4, ror #16 ++ uxtb SHIFT3, SHIFT4, ror #24 ++ .else ++ uxtb SHIFT0, SHIFT5, ror #0 ++ uxtb SHIFT1, SHIFT5, ror #8 ++ uxtb SHIFT2, SHIFT5, ror #16 ++ uxtb SHIFT3, SHIFT5, ror #24 ++ .endif ++ lsl DAT0, SHIFT0 ++ lsl DAT1, SHIFT1 ++ lsl DAT2, SHIFT2 ++ lsl DAT3, SHIFT3 ++ .endif ++ eor CHECK, CHECK, DAT0, lsr #8 - (\channels - IDX2) ++ eor CHECK, CHECK, DAT1, lsr #7 - (\channels - IDX2) ++ decr_modulo IDX2, 2, \channels ++ eor CHECK, CHECK, DAT2, lsr #8 - (\channels - IDX2) ++ eor CHECK, CHECK, DAT3, lsr #7 - (\channels - IDX2) ++ decr_modulo IDX2, 2, \channels ++ stm OUT!, {DAT0 - DAT3} ++ .endm ++ ++ .set WORDS_PER_LOOP, \channels // calculate LCM (channels, 4) ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP * 4 ++ .set SAMPLES_PER_LOOP, WORDS_PER_LOOP / \channels ++ ++function ff_mlp_pack_output_inorder_\channels\()ch_mixedshift_arm, export=1 ++ .if SAMPLES_PER_LOOP > 1 ++ tst COUNT, #SAMPLES_PER_LOOP - 1 // always seems to be in practice ++ bne X(ff_mlp_pack_output) // but just in case, branch to C implementation if not ++ .endif ++ teq COUNT, #0 ++ bxeq lr ++ push {v1-v6,sl,fp,lr} ++ ldr SHIFT0, [sp, #(9+3)*4] // get output_shift from stack ++ ldr SHIFT1, =0x08080808 ++ ldr SHIFT4, [SHIFT0] ++ .if \channels == 2 ++ uadd8 SHIFT4, SHIFT4, SHIFT1 // increase all shifts by 8 ++ uxtb SHIFT0, SHIFT4, ror #0 ++ uxtb SHIFT1, SHIFT4, ror #8 ++ .else ++ ldr SHIFT5, [SHIFT0, #4] ++ uadd8 SHIFT4, SHIFT4, SHIFT1 // increase all shifts by 8 ++ uadd8 SHIFT5, SHIFT5, SHIFT1 ++ .if \channels == 6 ++ uxtb SHIFT0, SHIFT4, ror #0 ++ uxtb SHIFT1, SHIFT4, ror #8 ++ uxtb SHIFT2, SHIFT4, ror #16 ++ uxtb SHIFT3, SHIFT4, ror #24 ++ uxtb SHIFT4, SHIFT5, ror #0 ++ uxtb SHIFT5, SHIFT5, ror #8 ++ .endif ++ .endif ++ .set IDX1, \channels ++ .set IDX2, \channels ++0: ++ .rept WORDS_PER_LOOP / 4 ++ output4words ++ .endr ++ subs COUNT, COUNT, #SAMPLES_PER_LOOP ++ bne 0b ++ pop {v1-v6,sl,fp,pc} ++ .ltorg ++endfunc ++ .purgem output4words ++ ++ .unreq CHECK ++ .unreq IN ++ .unreq OUT ++ .unreq COUNT ++ .unreq DAT0 ++ .unreq DAT1 ++ .unreq DAT2 ++ .unreq DAT3 ++ .unreq SHIFT0 ++ .unreq SHIFT1 ++ .unreq SHIFT2 ++ .unreq SHIFT3 ++ .unreq SHIFT4 ++ .unreq SHIFT5 ++ ++.else // not mixed ++ ++CHECK .req a1 ++IN .req a2 ++OUT .req a3 ++COUNT .req a4 ++DAT0 .req v1 ++DAT1 .req v2 ++DAT2 .req v3 ++DAT3 .req v4 ++DAT4 .req v5 ++DAT5 .req v6 ++DAT6 .req sl // use these rather than the otherwise unused ++DAT7 .req fp // ip and lr so that we can load them usinf LDRD ++ ++ .macro output4words tail, head, r0, r1, r2, r3, r4, r5, r6, r7, pointer_dead=0 ++ .if \head ++ .set SIZE_GROUP1, IDX1 ++ .if SIZE_GROUP1 > 4 ++ .set SIZE_GROUP1, 4 ++ .endif ++ .set SIZE_GROUP2, 4 - SIZE_GROUP1 ++ load_group1 SIZE_GROUP1, \channels, \r0, \r1, \r2, \r3, \pointer_dead ++ .endif ++ .if \tail ++ eor CHECK, CHECK, \r4, lsr #8 - (\channels - IDX2) ++ eor CHECK, CHECK, \r5, lsr #7 - (\channels - IDX2) ++ decr_modulo IDX2, 2, \channels ++ .endif ++ .if \head ++ load_group2 SIZE_GROUP2, \channels, \r0, \r1, \r2, \r3, \pointer_dead ++ .endif ++ .if \tail ++ eor CHECK, CHECK, \r6, lsr #8 - (\channels - IDX2) ++ eor CHECK, CHECK, \r7, lsr #7 - (\channels - IDX2) ++ decr_modulo IDX2, 2, \channels ++ stm OUT!, {\r4, \r5, \r6, \r7} ++ .endif ++ .if \head ++ lsl \r0, #8 + \shift ++ lsl \r1, #8 + \shift ++ lsl \r2, #8 + \shift ++ lsl \r3, #8 + \shift ++ .endif ++ .endm ++ ++ .set WORDS_PER_LOOP, \channels // calculate LCM (channels, 8) ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP * 8 ++ .set SAMPLES_PER_LOOP, WORDS_PER_LOOP / \channels ++ ++function ff_mlp_pack_output_inorder_\channels\()ch_\shift\()shift_arm, export=1 ++ .if SAMPLES_PER_LOOP > 1 ++ tst COUNT, #SAMPLES_PER_LOOP - 1 // always seems to be in practice ++ bne X(ff_mlp_pack_output) // but just in case, branch to C implementation if not ++ .endif ++ subs COUNT, COUNT, #SAMPLES_PER_LOOP ++ bxlo lr ++ push {v1-v6,sl,fp,lr} ++ .set IDX1, \channels ++ .set IDX2, \channels ++ output4words 0, 1, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7 ++0: beq 1f ++ .rept WORDS_PER_LOOP / 8 ++ output4words 1, 1, DAT4, DAT5, DAT6, DAT7, DAT0, DAT1, DAT2, DAT3 ++ output4words 1, 1, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7 ++ .endr ++ subs COUNT, COUNT, #SAMPLES_PER_LOOP ++ bne 0b ++1: ++ .rept WORDS_PER_LOOP / 8 - 1 ++ output4words 1, 1, DAT4, DAT5, DAT6, DAT7, DAT0, DAT1, DAT2, DAT3 ++ output4words 1, 1, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7 ++ .endr ++ output4words 1, 1, DAT4, DAT5, DAT6, DAT7, DAT0, DAT1, DAT2, DAT3, pointer_dead=1 ++ output4words 1, 0, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7 ++ pop {v1-v6,sl,fp,pc} ++endfunc ++ .purgem output4words ++ ++ .unreq CHECK ++ .unreq IN ++ .unreq OUT ++ .unreq COUNT ++ .unreq DAT0 ++ .unreq DAT1 ++ .unreq DAT2 ++ .unreq DAT3 ++ .unreq DAT4 ++ .unreq DAT5 ++ .unreq DAT6 ++ .unreq DAT7 ++ ++.endif // mixed ++.else // not inorder ++.ifc \shift, mixed ++ ++// This case not currently handled ++ ++.else // not mixed ++ ++CHECK .req a1 ++IN .req a2 ++OUT .req a3 ++COUNT .req a4 ++DAT0 .req v1 ++DAT1 .req v2 ++DAT2 .req v3 ++DAT3 .req v4 ++CHAN0 .req v5 ++CHAN1 .req v6 ++CHAN2 .req sl ++CHAN3 .req fp ++CHAN4 .req ip ++CHAN5 .req lr ++ ++ .macro output4words ++ .if \channels == 8 ++ .if IDX1 == 8 ++ uxtb CHAN0, CHAN4, ror #0 ++ uxtb CHAN1, CHAN4, ror #8 ++ uxtb CHAN2, CHAN4, ror #16 ++ uxtb CHAN3, CHAN4, ror #24 ++ .else ++ uxtb CHAN0, CHAN5, ror #0 ++ uxtb CHAN1, CHAN5, ror #8 ++ uxtb CHAN2, CHAN5, ror #16 ++ uxtb CHAN3, CHAN5, ror #24 ++ .endif ++ ldr DAT0, [IN, CHAN0, lsl #2] ++ ldr DAT1, [IN, CHAN1, lsl #2] ++ ldr DAT2, [IN, CHAN2, lsl #2] ++ ldr DAT3, [IN, CHAN3, lsl #2] ++ .if IDX1 == 4 ++ add IN, IN, #8*4 ++ .endif ++ decr_modulo IDX1, 4, \channels ++ .else ++ .set SIZE_GROUP1, IDX1 ++ .if SIZE_GROUP1 > 4 ++ .set SIZE_GROUP1, 4 ++ .endif ++ .set SIZE_GROUP2, 4 - SIZE_GROUP1 ++ .if SIZE_GROUP1 == 2 ++ loadregoffsh2 DAT, 0, IN, CHAN, 0 + (\channels - IDX1) ++ loadregoffsh2 DAT, 1, IN, CHAN, 1 + (\channels - IDX1) ++ add IN, IN, #8*4 ++ .else // SIZE_GROUP1 == 4 ++ loadregoffsh2 DAT, 0, IN, CHAN, 0 + (\channels - IDX1) ++ loadregoffsh2 DAT, 1, IN, CHAN, 1 + (\channels - IDX1) ++ loadregoffsh2 DAT, 2, IN, CHAN, 2 + (\channels - IDX1) ++ loadregoffsh2 DAT, 3, IN, CHAN, 3 + (\channels - IDX1) ++ .if IDX1 == 4 ++ add IN, IN, #8*4 ++ .endif ++ .endif ++ decr_modulo IDX1, SIZE_GROUP1, \channels ++ .if SIZE_GROUP2 == 2 ++ loadregoffsh2 DAT, 2, IN, CHAN, 0 + (\channels - IDX1) ++ loadregoffsh2 DAT, 3, IN, CHAN, 1 + (\channels - IDX1) ++ .if IDX1 == 2 ++ add IN, IN, #8*4 ++ .endif ++ .endif ++ decr_modulo IDX1, SIZE_GROUP2, \channels ++ .endif ++ .if \channels == 8 // in this case we can corrupt CHAN0-3 ++ rsb CHAN0, CHAN0, #8 ++ rsb CHAN1, CHAN1, #8 ++ rsb CHAN2, CHAN2, #8 ++ rsb CHAN3, CHAN3, #8 ++ lsl DAT0, #8 + \shift ++ lsl DAT1, #8 + \shift ++ lsl DAT2, #8 + \shift ++ lsl DAT3, #8 + \shift ++ eor CHECK, CHECK, DAT0, lsr CHAN0 ++ eor CHECK, CHECK, DAT1, lsr CHAN1 ++ eor CHECK, CHECK, DAT2, lsr CHAN2 ++ eor CHECK, CHECK, DAT3, lsr CHAN3 ++ .else ++ .if \shift != 0 ++ lsl DAT0, #\shift ++ lsl DAT1, #\shift ++ lsl DAT2, #\shift ++ lsl DAT3, #\shift ++ .endif ++ bic DAT0, DAT0, #0xff000000 ++ bic DAT1, DAT1, #0xff000000 ++ bic DAT2, DAT2, #0xff000000 ++ bic DAT3, DAT3, #0xff000000 ++ eorlslreg CHECK, DAT0, CHAN, 0 + (\channels - IDX2) ++ eorlslreg CHECK, DAT1, CHAN, 1 + (\channels - IDX2) ++ decr_modulo IDX2, 2, \channels ++ eorlslreg CHECK, DAT2, CHAN, 0 + (\channels - IDX2) ++ eorlslreg CHECK, DAT3, CHAN, 1 + (\channels - IDX2) ++ decr_modulo IDX2, 2, \channels ++ lsl DAT0, #8 ++ lsl DAT1, #8 ++ lsl DAT2, #8 ++ lsl DAT3, #8 ++ .endif ++ stm OUT!, {DAT0 - DAT3} ++ .endm ++ ++ .set WORDS_PER_LOOP, \channels // calculate LCM (channels, 4) ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .if (WORDS_PER_LOOP % 2) == 0 ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP / 2 ++ .endif ++ .set WORDS_PER_LOOP, WORDS_PER_LOOP * 4 ++ .set SAMPLES_PER_LOOP, WORDS_PER_LOOP / \channels ++ ++function ff_mlp_pack_output_outoforder_\channels\()ch_\shift\()shift_arm, export=1 ++ .if SAMPLES_PER_LOOP > 1 ++ tst COUNT, #SAMPLES_PER_LOOP - 1 // always seems to be in practice ++ bne X(ff_mlp_pack_output) // but just in case, branch to C implementation if not ++ .endif ++ teq COUNT, #0 ++ bxeq lr ++ push {v1-v6,sl,fp,lr} ++ ldr CHAN0, [sp, #(9+2)*4] // get ch_assign from stack ++ ldr CHAN4, [CHAN0] ++ .if \channels == 2 ++ uxtb CHAN0, CHAN4, ror #0 ++ uxtb CHAN1, CHAN4, ror #8 ++ .else ++ ldr CHAN5, [CHAN0, #4] ++ .if \channels == 6 ++ uxtb CHAN0, CHAN4, ror #0 ++ uxtb CHAN1, CHAN4, ror #8 ++ uxtb CHAN2, CHAN4, ror #16 ++ uxtb CHAN3, CHAN4, ror #24 ++ uxtb CHAN4, CHAN5, ror #0 ++ uxtb CHAN5, CHAN5, ror #8 ++ .endif ++ .endif ++ .set IDX1, \channels ++ .set IDX2, \channels ++0: ++ .rept WORDS_PER_LOOP / 4 ++ output4words ++ .endr ++ subs COUNT, COUNT, #SAMPLES_PER_LOOP ++ bne 0b ++ pop {v1-v6,sl,fp,pc} ++ .ltorg ++endfunc ++ .purgem output4words ++ ++ .unreq CHECK ++ .unreq IN ++ .unreq OUT ++ .unreq COUNT ++ .unreq DAT0 ++ .unreq DAT1 ++ .unreq DAT2 ++ .unreq DAT3 ++ .unreq CHAN0 ++ .unreq CHAN1 ++ .unreq CHAN2 ++ .unreq CHAN3 ++ .unreq CHAN4 ++ .unreq CHAN5 ++ ++.endif // mixed ++.endif // inorder ++.endm // implement_pack ++ ++.macro pack_channels inorder, channels ++ implement_pack \inorder, \channels, 0 ++ implement_pack \inorder, \channels, 1 ++ implement_pack \inorder, \channels, 2 ++ implement_pack \inorder, \channels, 3 ++ implement_pack \inorder, \channels, 4 ++ implement_pack \inorder, \channels, 5 ++ implement_pack \inorder, \channels, mixed ++.endm ++ ++.macro pack_order inorder ++ pack_channels \inorder, 2 ++ pack_channels \inorder, 6 ++ pack_channels \inorder, 8 ++.endm ++ ++ pack_order 0 ++ pack_order 1 +diff --git a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c +index 268dfdd..2d8b98d 100644 +--- a/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c ++++ b/lib/ffmpeg/libavcodec/arm/mlpdsp_init_arm.c +@@ -41,8 +41,72 @@ void ff_mlp_rematrix_channel_arm(int32_t *samples, + int access_unit_size_pow2, + int32_t mask); + ++#define DECLARE_PACK(order,channels,shift) \ ++ int32_t ff_mlp_pack_output_##order##order_##channels##ch_##shift##shift_arm(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *); ++#define ENUMERATE_PACK(order,channels,shift) \ ++ ff_mlp_pack_output_##order##order_##channels##ch_##shift##shift_arm, ++#define PACK_CHANNELS(macro,order,channels) \ ++ macro(order,channels,0) \ ++ macro(order,channels,1) \ ++ macro(order,channels,2) \ ++ macro(order,channels,3) \ ++ macro(order,channels,4) \ ++ macro(order,channels,5) \ ++ macro(order,channels,mixed) ++#define PACK_ORDER(macro,order) \ ++ PACK_CHANNELS(macro,order,2) \ ++ PACK_CHANNELS(macro,order,6) \ ++ PACK_CHANNELS(macro,order,8) ++#define PACK_ALL(macro) \ ++ PACK_ORDER(macro,outof) \ ++ PACK_ORDER(macro,in) ++PACK_ALL(DECLARE_PACK) ++ ++#define ff_mlp_pack_output_outoforder_2ch_mixedshift_arm 0 ++#define ff_mlp_pack_output_outoforder_6ch_mixedshift_arm 0 ++#define ff_mlp_pack_output_outoforder_8ch_mixedshift_arm 0 ++ ++static int32_t (*mlp_select_pack_output_arm(uint8_t max_matrix_channel, ++ int is32, ++ uint8_t *ch_assign, ++ int8_t *output_shift))(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *) ++{ ++ int ch_index; ++ int shift = output_shift[0] < 0 || output_shift[0] > 5 ? 6 : output_shift[0]; ++ int inorder = 1; ++ static int32_t (*const routine[2*3*7])(int32_t, int32_t (*)[], void *, uint16_t, uint8_t, int, uint8_t*, int8_t *) = { ++ PACK_ALL(ENUMERATE_PACK) ++ }; ++ int i; ++ ++ if (!is32) // don't support 16-bit output (it's not used by TrueHD) ++ return ff_mlp_pack_output; ++ ++ switch (max_matrix_channel) { ++ case 1: ch_index = 0; break; ++ case 5: ch_index = 1; break; ++ case 7: ch_index = 2; break; ++ default: return ff_mlp_pack_output; ++ } ++ ++ for (i = 0; i <= max_matrix_channel; i++) { ++ if (shift != 6 && output_shift[i] != shift) ++ shift = 6; // indicate mixed shifts ++ if (ch_assign[i] != i) ++ inorder = 0; ++ } ++ if (shift == 6 && !inorder) ++ return ff_mlp_pack_output; // can't currently handle both an order array and a shift array ++ ++ return routine[(inorder*3+ch_index)*7+shift]; ++} ++ + av_cold void ff_mlpdsp_init_arm(MLPDSPContext *c) + { ++ int cpu_flags = av_get_cpu_flags(); ++ + c->mlp_filter_channel = ff_mlp_filter_channel_arm; + c->mlp_rematrix_channel = ff_mlp_rematrix_channel_arm; ++ if (cpu_flags & AV_CPU_FLAG_ARMV6) ++ c->mlp_select_pack_output = mlp_select_pack_output_arm; + } +-- +1.9.3 + + +From 9af15bf0b7bc7940bd8bcc9ddae23178c9723bd6 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Thu, 14 Nov 2013 19:48:41 +0000 +Subject: [PATCH 14/94] More efficient infobool expression evaluator + +Expession infobools are evaluated at runtime from one or more single infobools +and a combination of boolean NOT, AND and OR operators. Previously, parsing +produced a vector of operands (leaf nodes) and operators in postfix +(reverse-Polish) form, and evaluated all leaf nodes every time the expression +was evaluated. But this ignores the fact that in many cases, once one operand +of an AND or OR operation has been evaluated, there is no need to evaluate the +other operand because its value can have no effect on the ultimate result. It +is also worth noting that AND and OR operations are associative, meaning they +can be rearranged at runtime to better suit the selected skin. + +This patch rewrites the expression parsing and evaluation code. Now the +internal repreentation is in the form of a tree where leaf nodes represent a +single infobool, and branch nodes represent either an AND or an OR operation +on two or more child nodes. + +Expressions are rewritten at parse time into a form which favours the +formation of groups of associative nodes. These groups are then reordered at +evaluation time such that nodes whose value renders the evaluation of the +remainder of the group unnecessary tend to be evaluated first (these are +true nodes for OR subexpressions, or false nodes for AND subexpressions). +The end effect is to minimise the number of leaf nodes that need to be +evaluated in order to determine the value of the expression. The runtime +adaptability has the advantage of not being customised for any particular skin. + +The modifications to the expression at parse time fall into two groups: +1) Moving logical NOTs so that they are only applied to leaf nodes. + For example, rewriting ![A+B]|C as !A|!B|C allows reordering such that + any of the three leaves can be evaluated first. +2) Combining adjacent AND or OR operations such that each path from the root + to a leaf encounters a strictly alternating pattern of AND and OR + operations. So [A|B]|[C|D+[[E|F]|G] becomes A|B|C|[D+[E|F|G]]. + +I measured the effect while the Videos window of the default skin was open +(but idle) on a Raspberry Pi, and this reduced the CPU usage by 2.8% from +41.9% to 39.1%: + + Before After + Mean StdDev Mean StdDev Confidence Change +IdleCPU% 41.9 0.5 39.1 0.9 100.0% +7.0% +--- + xbmc/interfaces/info/InfoExpression.cpp | 313 +++++++++++++++++++++----------- + xbmc/interfaces/info/InfoExpression.h | 63 ++++++- + 2 files changed, 269 insertions(+), 107 deletions(-) + +diff --git a/xbmc/interfaces/info/InfoExpression.cpp b/xbmc/interfaces/info/InfoExpression.cpp +index f4d32c1..db461dd 100644 +--- a/xbmc/interfaces/info/InfoExpression.cpp ++++ b/xbmc/interfaces/info/InfoExpression.cpp +@@ -22,6 +22,9 @@ + #include <stack> + #include "utils/log.h" + #include "GUIInfoManager.h" ++#include <list> ++#include <boost/shared_ptr.hpp> ++#include <boost/make_shared.hpp> + + using namespace std; + using namespace INFO; +@@ -40,21 +43,89 @@ void InfoSingle::Update(const CGUIListItem *item) + InfoExpression::InfoExpression(const std::string &expression, int context) + : InfoBool(expression, context) + { +- Parse(expression); ++ if (!Parse(expression)) ++ CLog::Log(LOGERROR, "Error parsing boolean expression %s", expression.c_str()); + } + + void InfoExpression::Update(const CGUIListItem *item) + { +- Evaluate(item, m_value); ++ m_value = m_expression_tree->Evaluate(item); + } + +-#define OPERATOR_LB 5 +-#define OPERATOR_RB 4 +-#define OPERATOR_NOT 3 +-#define OPERATOR_AND 2 +-#define OPERATOR_OR 1 ++/* Expressions are rewritten at parse time into a form which favours the ++ * formation of groups of associative nodes. These groups are then reordered at ++ * evaluation time such that nodes whose value renders the evaluation of the ++ * remainder of the group unnecessary tend to be evaluated first (these are ++ * true nodes for OR subexpressions, or false nodes for AND subexpressions). ++ * The end effect is to minimise the number of leaf nodes that need to be ++ * evaluated in order to determine the value of the expression. The runtime ++ * adaptability has the advantage of not being customised for any particular skin. ++ * ++ * The modifications to the expression at parse time fall into two groups: ++ * 1) Moving logical NOTs so that they are only applied to leaf nodes. ++ * For example, rewriting ![A+B]|C as !A|!B|C allows reordering such that ++ * any of the three leaves can be evaluated first. ++ * 2) Combining adjacent AND or OR operations such that each path from the root ++ * to a leaf encounters a strictly alternating pattern of AND and OR ++ * operations. So [A|B]|[C|D+[[E|F]|G] becomes A|B|C|[D+[E|F|G]]. ++ */ ++ ++bool InfoExpression::InfoLeaf::Evaluate(const CGUIListItem *item) ++{ ++ return m_invert ^ m_info->Get(item); ++} + +-short InfoExpression::GetOperator(const char ch) const ++InfoExpression::InfoAssociativeGroup::InfoAssociativeGroup( ++ bool and_not_or, ++ const InfoSubexpressionPtr &left, ++ const InfoSubexpressionPtr &right) ++ : m_and_not_or(and_not_or) ++{ ++ AddChild(right); ++ AddChild(left); ++} ++ ++void InfoExpression::InfoAssociativeGroup::AddChild(const InfoSubexpressionPtr &child) ++{ ++ m_children.push_front(child); // largely undoes the effect of parsing right-associative ++} ++ ++void InfoExpression::InfoAssociativeGroup::Merge(InfoAssociativeGroup *other) ++{ ++ m_children.splice(m_children.end(), other->m_children); ++} ++ ++bool InfoExpression::InfoAssociativeGroup::Evaluate(const CGUIListItem *item) ++{ ++ /* Handle either AND or OR by using the relation ++ * A AND B == !(!A OR !B) ++ * to convert ANDs into ORs ++ */ ++ std::list<InfoSubexpressionPtr>::iterator last = m_children.end(); ++ std::list<InfoSubexpressionPtr>::iterator it = m_children.begin(); ++ bool result = m_and_not_or ^ (*it)->Evaluate(item); ++ while (!result && ++it != last) ++ { ++ result = m_and_not_or ^ (*it)->Evaluate(item); ++ if (result) ++ { ++ /* Move this child to the head of the list so we evaluate faster next time */ ++ InfoSubexpressionPtr p = *it; ++ m_children.erase(it); ++ m_children.push_front(p); ++ } ++ } ++ return m_and_not_or ^ result; ++} ++ ++/* Expressions are parsed using the shunting-yard algorithm. Binary operators ++ * (AND/OR) are treated as right-associative so that we don't need to make a ++ * special case for the unary NOT operator. This has no effect upon the answers ++ * generated, though the initial sequence of evaluation of leaves may be ++ * different from what you might expect. ++ */ ++ ++InfoExpression::operator_t InfoExpression::GetOperator(char ch) + { + if (ch == '[') + return OPERATOR_LB; +@@ -67,122 +138,160 @@ short InfoExpression::GetOperator(const char ch) const + else if (ch == '|') + return OPERATOR_OR; + else +- return 0; ++ return OPERATOR_NONE; + } + +-void InfoExpression::Parse(const std::string &expression) ++void InfoExpression::OperatorPop(std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes) + { +- stack<char> operators; +- std::string operand; +- for (unsigned int i = 0; i < expression.size(); i++) ++ operator_t op2 = operator_stack.top(); ++ operator_stack.pop(); ++ if (op2 == OPERATOR_NOT) + { +- if (GetOperator(expression[i])) ++ invert = !invert; ++ } ++ else ++ { ++ // At this point, it can only be OPERATOR_AND or OPERATOR_OR ++ if (invert) ++ op2 = (operator_t) (OPERATOR_AND ^ OPERATOR_OR ^ op2); ++ node_type_t new_type = op2 == OPERATOR_AND ? NODE_AND : NODE_OR; ++ ++ InfoSubexpressionPtr right = nodes.top(); ++ nodes.pop(); ++ InfoSubexpressionPtr left = nodes.top(); ++ ++ node_type_t right_type = node_types.top(); ++ node_types.pop(); ++ node_type_t left_type = node_types.top(); ++ ++ // Combine associative operations into the same node where possible ++ if (left_type == new_type && right_type == new_type) ++ (static_cast<InfoAssociativeGroup *>(left.get()))->Merge(static_cast<InfoAssociativeGroup *>(right.get())); ++ else if (left_type == new_type) ++ (static_cast<InfoAssociativeGroup *>(left.get()))->AddChild(right); ++ else + { +- // cleanup any operand, translate and put into our expression list +- if (!operand.empty()) ++ nodes.pop(); ++ node_types.pop(); ++ if (right_type == new_type) + { +- InfoPtr info = g_infoManager.Register(operand, m_context); +- if (info) +- { +- m_listItemDependent |= info->ListItemDependent(); +- m_postfix.push_back(m_operands.size()); +- m_operands.push_back(info); +- } +- operand.clear(); ++ (static_cast<InfoAssociativeGroup *>(right.get()))->AddChild(left); ++ nodes.push(right); + } +- // handle closing parenthesis +- if (expression[i] == ']') +- { +- while (!operators.empty()) +- { +- char oper = operators.top(); +- operators.pop(); ++ else ++ nodes.push(boost::make_shared<InfoAssociativeGroup>(new_type == NODE_AND, left, right)); ++ node_types.push(new_type); ++ } ++ } ++} ++ ++void InfoExpression::ProcessOperator(operator_t op, std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes) ++{ ++ // Handle any higher-priority stacked operators, except when the new operator is left-bracket. ++ // For a right-bracket, this will stop with the matching left-bracket at the top of the operator stack. ++ if (op != OPERATOR_LB) ++ { ++ while (operator_stack.size() > 0 && operator_stack.top() > op) ++ OperatorPop(operator_stack, invert, node_types, nodes); ++ } ++ if (op == OPERATOR_RB) ++ operator_stack.pop(); // remove the matching left-bracket ++ else ++ operator_stack.push(op); ++ if (op == OPERATOR_NOT) ++ invert = !invert; ++} + +- if (oper == '[') +- break; ++bool InfoExpression::ProcessOperand(std::string &operand, bool invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes) ++{ ++ InfoPtr info = g_infoManager.Register(operand, m_context); ++ if (!info) ++ return false; ++ m_listItemDependent |= info->ListItemDependent(); ++ nodes.push(boost::make_shared<InfoLeaf>(info, invert)); ++ node_types.push(NODE_LEAF); ++ operand.clear(); ++ return true; ++} + +- m_postfix.push_back(-GetOperator(oper)); // negative denotes operator +- } ++bool InfoExpression::Parse(const std::string &expression) ++{ ++ const char *s = expression.c_str(); ++ std::string operand; ++ std::stack<operator_t> operator_stack; ++ bool invert = false; ++ std::stack<node_type_t> node_types; ++ std::stack<InfoSubexpressionPtr> nodes; ++ // The next two are for syntax-checking purposes ++ bool after_binaryoperator = true; ++ int bracket_count = 0; ++ ++ char c; ++ // Skip leading whitespace - don't want it to count as an operand if that's all there is ++ do ++ { ++ c = *s++; ++ } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); ++ s--; ++ while ((c = *s++) != '\0') ++ { ++ operator_t op; ++ if ((op = GetOperator(c)) != OPERATOR_NONE) ++ { ++ // Character is an operator ++ if ((!after_binaryoperator && (c == '!' || c == '[')) || ++ (after_binaryoperator && (c == ']' || c == '+' || c == '|'))) ++ { ++ CLog::Log(LOGERROR, "Misplaced %c", c); ++ return false; + } +- else ++ if (c == '[') ++ bracket_count++; ++ else if (c == ']' && bracket_count-- == 0) ++ { ++ CLog::Log(LOGERROR, "Unmatched ]"); ++ return false; ++ } ++ if (operand.size() > 0 && !ProcessOperand(operand, invert, node_types, nodes)) + { +- // all other operators we pop off the stack any operator +- // that has a higher priority than the one we have. +- while (!operators.empty() && GetOperator(operators.top()) > GetOperator(expression[i])) +- { +- // only handle parenthesis once they're closed. +- if (operators.top() == '[' && expression[i] != ']') +- break; +- +- m_postfix.push_back(-GetOperator(operators.top())); // negative denotes operator +- operators.pop(); +- } +- operators.push(expression[i]); ++ CLog::Log(LOGERROR, "Bad operand '%s'", operand.c_str()); ++ return false; + } ++ ProcessOperator(op, operator_stack, invert, node_types, nodes); ++ if (c == '+' || c == '|') ++ after_binaryoperator = true; ++ // Skip trailing whitespace - don't want it to count as an operand if that's all there is ++ do ++ { ++ c = *s++; ++ } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); ++ s--; + } + else + { +- operand += expression[i]; ++ // Character is part of operand ++ operand += c; ++ after_binaryoperator = false; + } + } +- +- if (!operand.empty()) ++ if (bracket_count > 0) + { +- InfoPtr info = g_infoManager.Register(operand, m_context); +- if (info) +- { +- m_listItemDependent |= info->ListItemDependent(); +- m_postfix.push_back(m_operands.size()); +- m_operands.push_back(info); +- } ++ CLog::Log(LOGERROR, "Unmatched ["); ++ return false; + } +- +- // finish up by adding any operators +- while (!operators.empty()) ++ if (after_binaryoperator) + { +- m_postfix.push_back(-GetOperator(operators.top())); // negative denotes operator +- operators.pop(); ++ CLog::Log(LOGERROR, "Missing operand"); ++ return false; + } +- +- // test evaluate +- bool test; +- if (!Evaluate(NULL, test)) +- CLog::Log(LOGERROR, "Error evaluating boolean expression %s", expression.c_str()); +-} +- +-bool InfoExpression::Evaluate(const CGUIListItem *item, bool &result) +-{ +- stack<bool> save; +- for (vector<short>::const_iterator it = m_postfix.begin(); it != m_postfix.end(); ++it) ++ if (operand.size() > 0 && !ProcessOperand(operand, invert, node_types, nodes)) + { +- short expr = *it; +- if (expr == -OPERATOR_NOT) +- { // NOT the top item on the stack +- if (save.empty()) return false; +- bool expr = save.top(); +- save.pop(); +- save.push(!expr); +- } +- else if (expr == -OPERATOR_AND) +- { // AND the top two items on the stack +- if (save.size() < 2) return false; +- bool right = save.top(); save.pop(); +- bool left = save.top(); save.pop(); +- save.push(left && right); +- } +- else if (expr == -OPERATOR_OR) +- { // OR the top two items on the stack +- if (save.size() < 2) return false; +- bool right = save.top(); save.pop(); +- bool left = save.top(); save.pop(); +- save.push(left || right); +- } +- else if (expr >= 0) // operand +- save.push(m_operands[expr]->Get(item)); +- } +- if (save.size() != 1) ++ CLog::Log(LOGERROR, "Bad operand '%s'", operand.c_str()); + return false; +- result = save.top(); ++ } ++ while (operator_stack.size() > 0) ++ OperatorPop(operator_stack, invert, node_types, nodes); ++ ++ m_expression_tree = nodes.top(); + return true; + } +- +diff --git a/xbmc/interfaces/info/InfoExpression.h b/xbmc/interfaces/info/InfoExpression.h +index 4e0faee..0a91399 100644 +--- a/xbmc/interfaces/info/InfoExpression.h ++++ b/xbmc/interfaces/info/InfoExpression.h +@@ -21,6 +21,8 @@ + #pragma once + + #include <vector> ++#include <list> ++#include <stack> + #include "InfoBool.h" + + class CGUIListItem; +@@ -50,12 +52,63 @@ class InfoExpression : public InfoBool + + virtual void Update(const CGUIListItem *item); + private: +- void Parse(const std::string &expression); +- bool Evaluate(const CGUIListItem *item, bool &result); +- short GetOperator(const char ch) const; ++ typedef enum ++ { ++ OPERATOR_NONE = 0, ++ OPERATOR_LB, // 1 ++ OPERATOR_RB, // 2 ++ OPERATOR_OR, // 3 ++ OPERATOR_AND, // 4 ++ OPERATOR_NOT, // 5 ++ } operator_t; + +- std::vector<short> m_postfix; ///< the postfix form of the expression (operators and operand indicies) +- std::vector<InfoPtr> m_operands; ///< the operands in the expression ++ typedef enum ++ { ++ NODE_LEAF, ++ NODE_AND, ++ NODE_OR, ++ } node_type_t; ++ ++ // An abstract base class for nodes in the expression tree ++ class InfoSubexpression ++ { ++ public: ++ virtual ~InfoSubexpression(void) {}; // so we can destruct derived classes using a pointer to their base class ++ virtual bool Evaluate(const CGUIListItem *item) = 0; ++ }; ++ ++ typedef boost::shared_ptr<InfoSubexpression> InfoSubexpressionPtr; ++ ++ // A leaf node in the expression tree ++ class InfoLeaf : public InfoSubexpression ++ { ++ public: ++ InfoLeaf(InfoPtr info, bool invert) : m_info(info), m_invert(invert) {}; ++ virtual bool Evaluate(const CGUIListItem *item); ++ private: ++ InfoPtr m_info; ++ bool m_invert; ++ }; ++ ++ // A branch node in the expression tree ++ class InfoAssociativeGroup : public InfoSubexpression ++ { ++ public: ++ InfoAssociativeGroup(bool and_not_or, const InfoSubexpressionPtr &left, const InfoSubexpressionPtr &right); ++ void AddChild(const InfoSubexpressionPtr &child); ++ void Merge(InfoAssociativeGroup *other); ++ virtual bool Evaluate(const CGUIListItem *item); ++ private: ++ bool m_and_not_or; ++ std::list<InfoSubexpressionPtr> m_children; ++ }; ++ ++ static operator_t GetOperator(char ch); ++ static void OperatorPop(std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes); ++ static void ProcessOperator(operator_t op, std::stack<operator_t> &operator_stack, bool &invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes); ++ bool ProcessOperand(std::string &operand, bool invert, std::stack<node_type_t> &node_types, std::stack<InfoSubexpressionPtr> &nodes); ++ bool Parse(const std::string &expression); ++ InfoSubexpressionPtr m_expression_tree; + }; + + }; +-- +1.9.3 + + +From 36067cf823d539a00eea75d561ac78a4b1431a66 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Mon, 24 Mar 2014 22:26:21 +0000 +Subject: [PATCH 15/94] Where an infobool expression failed to parse, evaluate + the infobool as false. Previously, this would result in a segfault due to the + dereferencing of an uninitialised pointer to the head of the expression tree. + +--- + xbmc/interfaces/info/InfoExpression.cpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/xbmc/interfaces/info/InfoExpression.cpp b/xbmc/interfaces/info/InfoExpression.cpp +index db461dd..7c54064 100644 +--- a/xbmc/interfaces/info/InfoExpression.cpp ++++ b/xbmc/interfaces/info/InfoExpression.cpp +@@ -44,7 +44,10 @@ InfoExpression::InfoExpression(const std::string &expression, int context) + : InfoBool(expression, context) + { + if (!Parse(expression)) ++ { + CLog::Log(LOGERROR, "Error parsing boolean expression %s", expression.c_str()); ++ m_expression_tree = boost::make_shared<InfoLeaf>(g_infoManager.Register("false", 0), false); ++ } + } + + void InfoExpression::Update(const CGUIListItem *item) +-- +1.9.3 + + +From 7f2870606f1e183d70b1dc2dbc07fa8bc437d0cc Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 26 Nov 2013 20:09:48 +0000 +Subject: [PATCH 16/94] Add caching of infolabels + +The functions CGUIInfoLabel::GetLabel and CGUIInfoLabel::GetItemLabel take +a number of strings returned from CGUIInfoManager::GetImage or +CGUIInfoManager::GetLabel, and combine them with various constant strings +which were determined during CGUIInfoLabel::Parse. + +Rather than perform all the string operations on every call, this patch +changes to use a two-pass process: first it queries all the GetImage/GetLabel +strings, and then only if at least one of them has changed does it bother +rebuilding the resultant string - otherwise it re-uses the copy built on a +preceding call. + +CGUIInfoLabel::GetLabel/GetItemLabel are also changed to return string +references, rather than forcing an additional string copy. + +I have measured the effect while the Videos window of the default skin was +open (but idle) on a Raspberry Pi, and this reduced the CPU usage by 0.8% +from 36.2% to 35.4%: + + Before After + Mean StdDev Mean StdDev Confidence Change +IdleCPU% 36.2 0.5 35.4 0.5 99.9% +2.2% +--- + xbmc/guilib/GUIInfoTypes.cpp | 102 +++++++++++++++++++++++++++++++++---------- + xbmc/guilib/GUIInfoTypes.h | 11 ++++- + 2 files changed, 87 insertions(+), 26 deletions(-) + +diff --git a/xbmc/guilib/GUIInfoTypes.cpp b/xbmc/guilib/GUIInfoTypes.cpp +index 6977e0f..d78c26a 100644 +--- a/xbmc/guilib/GUIInfoTypes.cpp ++++ b/xbmc/guilib/GUIInfoTypes.cpp +@@ -136,37 +136,64 @@ void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback + Parse(label, context); + } + +-CStdString CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, CStdString *fallback /*= NULL*/) const ++const std::string &CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, CStdString *fallback /*= NULL*/) const + { +- CStdString label; +- for (unsigned int i = 0; i < m_info.size(); i++) ++ for (unsigned int i = 0, j = 0; i < m_info.size(); i++) + { + const CInfoPortion &portion = m_info[i]; + if (portion.m_info) + { +- CStdString infoLabel; ++ std::string infoLabel; + if (preferImage) + infoLabel = g_infoManager.GetImage(portion.m_info, contextWindow, fallback); + if (infoLabel.empty()) + infoLabel = g_infoManager.GetLabel(portion.m_info, contextWindow, fallback); +- if (!infoLabel.empty()) +- label += portion.GetLabel(infoLabel); ++ if (j == m_labelPortions.size()) ++ m_labelPortions.push_back(infoLabel); ++ else if (infoLabel != m_labelPortions[j]) ++ { ++ m_labelPortions[j] = infoLabel; ++ m_labelDirty = true; ++ } ++ j++; + } +- else +- { // no info, so just append the prefix +- label += portion.m_prefix; ++ } ++ if (m_labelDirty) ++ { ++ m_label.clear(); ++ for (unsigned int i = 0, j= 0; i < m_info.size(); i++) ++ { ++ const CInfoPortion &portion = m_info[i]; ++ if (portion.m_info) ++ { ++ if (!m_labelPortions[j].empty()) ++ m_label += portion.GetLabel(m_labelPortions[j]); ++ j++; ++ } ++ else ++ { // no info, so just append the prefix ++ m_label += portion.m_prefix; ++ } + } ++ if (m_label.empty()) // empty label, use the fallback ++ m_label = m_fallback; ++ m_labelDirty = false; + } +- if (label.empty()) // empty label, use the fallback +- return m_fallback; +- return label; ++ return m_label; + } + +-CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, CStdString *fallback /*= NULL*/) const ++const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, CStdString *fallback /*= NULL*/) const + { +- if (!item->IsFileItem()) return ""; +- CStdString label; +- for (unsigned int i = 0; i < m_info.size(); i++) ++ if (!item->IsFileItem()) ++ { ++ if (m_itemLabelDirty) ++ { ++ m_itemLabel = ""; ++ m_itemLabelDirty = false; ++ } ++ return m_itemLabel; ++ } ++ for (unsigned int i = 0, j = 0; i < m_info.size(); i++) + { + const CInfoPortion &portion = m_info[i]; + if (portion.m_info) +@@ -176,17 +203,38 @@ CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImag + infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info, fallback); + else + infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info, fallback); +- if (!infoLabel.empty()) +- label += portion.GetLabel(infoLabel); ++ if (j == m_itemLabelPortions.size()) ++ m_itemLabelPortions.push_back(infoLabel); ++ else if (infoLabel != m_itemLabelPortions[j]) ++ { ++ m_itemLabelPortions[j] = infoLabel; ++ m_itemLabelDirty = true; ++ } ++ j++; + } +- else +- { // no info, so just append the prefix +- label += portion.m_prefix; ++ } ++ if (m_itemLabelDirty) ++ { ++ m_itemLabel.clear(); ++ for (unsigned int i = 0, j = 0; i < m_info.size(); i++) ++ { ++ const CInfoPortion &portion = m_info[i]; ++ if (portion.m_info) ++ { ++ if (!m_itemLabelPortions[j].empty()) ++ m_itemLabel += portion.GetLabel(m_itemLabelPortions[j]); ++ j++; ++ } ++ else ++ { // no info, so just append the prefix ++ m_itemLabel += portion.m_prefix; ++ } + } ++ if (m_itemLabel.empty()) ++ m_itemLabel = m_fallback; ++ m_itemLabelDirty = false; + } +- if (label.empty()) +- return m_fallback; +- return label; ++ return m_itemLabel; + } + + bool CGUIInfoLabel::IsEmpty() const +@@ -277,6 +325,12 @@ const static infoformat infoformatmap[] = {{ "$INFO[", FORMATINFO }, + void CGUIInfoLabel::Parse(const CStdString &label, int context) + { + m_info.clear(); ++ m_labelDirty = true; ++ m_label.clear(); ++ m_labelPortions.clear(); ++ m_itemLabelDirty = true; ++ m_itemLabel.clear(); ++ m_itemLabelPortions.clear(); + // Step 1: Replace all $LOCALIZE[number] with the real string + CStdString work = ReplaceLocalize(label); + // Step 2: Replace all $ADDON[id number] with the real string +diff --git a/xbmc/guilib/GUIInfoTypes.h b/xbmc/guilib/GUIInfoTypes.h +index 8c1c1dc..418b2c4 100644 +--- a/xbmc/guilib/GUIInfoTypes.h ++++ b/xbmc/guilib/GUIInfoTypes.h +@@ -83,7 +83,7 @@ class CGUIInfoLabel + \param fallback if non-NULL, is set to an alternate value to use should the actual value be not appropriate. Defaults to NULL. + \return label (or image). + */ +- CStdString GetLabel(int contextWindow, bool preferImage = false, CStdString *fallback = NULL) const; ++ const std::string &GetLabel(int contextWindow, bool preferImage = false, CStdString *fallback = NULL) const; + + /*! + \brief Gets a label (or image) for a given listitem from the info manager. +@@ -92,7 +92,7 @@ class CGUIInfoLabel + \param fallback if non-NULL, is set to an alternate value to use should the actual value be not appropriate. Defaults to NULL. + \return label (or image). + */ +- CStdString GetItemLabel(const CGUIListItem *item, bool preferImage = false, CStdString *fallback = NULL) const; ++ const std::string &GetItemLabel(const CGUIListItem *item, bool preferImage = false, CStdString *fallback = NULL) const; + + bool IsConstant() const; + bool IsEmpty() const; +@@ -132,6 +132,13 @@ class CGUIInfoLabel + + CStdString m_fallback; + std::vector<CInfoPortion> m_info; ++ ++ mutable bool m_labelDirty; ++ mutable std::string m_label; ++ mutable std::vector<std::string> m_labelPortions; ++ mutable bool m_itemLabelDirty; ++ mutable std::string m_itemLabel; ++ mutable std::vector<std::string> m_itemLabelPortions; + }; + + #endif +-- +1.9.3 + + +From 3d5a1912ffd4556ec09208fea50d2a2919775c9f Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 10 Dec 2013 01:12:31 +0000 +Subject: [PATCH 17/94] De-duplication of string cache for non-item and item + labels + +--- + xbmc/guilib/GUIInfoTypes.cpp | 50 +++++++++++++++++++++++++------------------- + xbmc/guilib/GUIInfoTypes.h | 4 +--- + 2 files changed, 29 insertions(+), 25 deletions(-) + +diff --git a/xbmc/guilib/GUIInfoTypes.cpp b/xbmc/guilib/GUIInfoTypes.cpp +index d78c26a..8bd131f 100644 +--- a/xbmc/guilib/GUIInfoTypes.cpp ++++ b/xbmc/guilib/GUIInfoTypes.cpp +@@ -121,7 +121,7 @@ void CGUIInfoColor::Parse(const CStdString &label, int context) + m_color = g_colorManager.GetColor(label); + } + +-CGUIInfoLabel::CGUIInfoLabel() ++CGUIInfoLabel::CGUIInfoLabel() : m_labelDirty(true) + { + } + +@@ -178,7 +178,10 @@ const std::string &CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, + if (m_label.empty()) // empty label, use the fallback + m_label = m_fallback; + m_labelDirty = false; ++ m_isLabelOfListItem = false; + } ++ else ++ assert(m_isLabelOfListItem == false); + return m_label; + } + +@@ -186,12 +189,15 @@ const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool pr + { + if (!item->IsFileItem()) + { +- if (m_itemLabelDirty) ++ if (m_labelDirty) + { +- m_itemLabel = ""; +- m_itemLabelDirty = false; ++ m_label = ""; ++ m_labelDirty = false; ++ m_isLabelOfListItem = true; + } +- return m_itemLabel; ++ else ++ assert(m_isLabelOfListItem == true); ++ return m_label; + } + for (unsigned int i = 0, j = 0; i < m_info.size(); i++) + { +@@ -203,38 +209,41 @@ const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool pr + infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info, fallback); + else + infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info, fallback); +- if (j == m_itemLabelPortions.size()) +- m_itemLabelPortions.push_back(infoLabel); +- else if (infoLabel != m_itemLabelPortions[j]) ++ if (j == m_labelPortions.size()) ++ m_labelPortions.push_back(infoLabel); ++ else if (infoLabel != m_labelPortions[j]) + { +- m_itemLabelPortions[j] = infoLabel; +- m_itemLabelDirty = true; ++ m_labelPortions[j] = infoLabel; ++ m_labelDirty = true; + } + j++; + } + } +- if (m_itemLabelDirty) ++ if (m_labelDirty) + { +- m_itemLabel.clear(); ++ m_label.clear(); + for (unsigned int i = 0, j = 0; i < m_info.size(); i++) + { + const CInfoPortion &portion = m_info[i]; + if (portion.m_info) + { +- if (!m_itemLabelPortions[j].empty()) +- m_itemLabel += portion.GetLabel(m_itemLabelPortions[j]); ++ if (!m_labelPortions[j].empty()) ++ m_label += portion.GetLabel(m_labelPortions[j]); + j++; + } + else + { // no info, so just append the prefix +- m_itemLabel += portion.m_prefix; ++ m_label += portion.m_prefix; + } + } +- if (m_itemLabel.empty()) +- m_itemLabel = m_fallback; +- m_itemLabelDirty = false; ++ if (m_label.empty()) ++ m_label = m_fallback; ++ m_labelDirty = false; ++ m_isLabelOfListItem = true; + } +- return m_itemLabel; ++ else ++ assert(m_isLabelOfListItem == true); ++ return m_label; + } + + bool CGUIInfoLabel::IsEmpty() const +@@ -328,9 +337,6 @@ void CGUIInfoLabel::Parse(const CStdString &label, int context) + m_labelDirty = true; + m_label.clear(); + m_labelPortions.clear(); +- m_itemLabelDirty = true; +- m_itemLabel.clear(); +- m_itemLabelPortions.clear(); + // Step 1: Replace all $LOCALIZE[number] with the real string + CStdString work = ReplaceLocalize(label); + // Step 2: Replace all $ADDON[id number] with the real string +diff --git a/xbmc/guilib/GUIInfoTypes.h b/xbmc/guilib/GUIInfoTypes.h +index 418b2c4..6d9ebf7 100644 +--- a/xbmc/guilib/GUIInfoTypes.h ++++ b/xbmc/guilib/GUIInfoTypes.h +@@ -133,12 +133,10 @@ class CGUIInfoLabel + CStdString m_fallback; + std::vector<CInfoPortion> m_info; + ++ mutable bool m_isLabelOfListItem; + mutable bool m_labelDirty; + mutable std::string m_label; + mutable std::vector<std::string> m_labelPortions; +- mutable bool m_itemLabelDirty; +- mutable std::string m_itemLabel; +- mutable std::vector<std::string> m_itemLabelPortions; + }; + + #endif +-- +1.9.3 + + +From 1427baf4395b760227afbef8e17956ba251f2fbe Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Fri, 21 Feb 2014 15:16:13 +0000 +Subject: [PATCH 18/94] Faster and simpler portable implementation of + MathUtils::round_int(). + +Much as I like a bit of inline assembler, I have also removed the ARM versions +of MathUtils::truncate_int() and MathUtils::round_int(). The former was just +how any sane compiler should have assembled a cast from double to signed int +anyway. The latter was a much too complicated way to achieve the desired +effect, and was switched out in most ARM builds anyway in favour of the old +portable implementation that used floor(). + +Verified that MathUtils::test() still passes, and that GCC is now able to +inline MathUtils::round_int(), where it didn't previously. + +I tested on a Raspberry Pi with the default theme, displaying the front page +with the RSS ticker enabled. This saturates the CPU, so I'm measuring the +improvement using the debug window's FPS figure. This patch improves this from +~50.8 FPS to ~52.6 FPS. +--- + xbmc/utils/MathUtils.h | 129 +++++++++++++++++++++++-------------------------- + 1 file changed, 61 insertions(+), 68 deletions(-) + +diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h +index 96af9f4..0dae77d 100644 +--- a/xbmc/utils/MathUtils.h ++++ b/xbmc/utils/MathUtils.h +@@ -34,17 +34,13 @@ + + #if defined(__ppc__) || \ + defined(__powerpc__) || \ +- (defined(TARGET_DARWIN_IOS) && defined(__llvm__)) || \ +- (defined(TARGET_ANDROID) && defined(__arm__)) || \ +- defined(TARGET_RASPBERRY_PI) ++ defined(__arm__) + #define DISABLE_MATHUTILS_ASM_ROUND_INT + #endif + + #if defined(__ppc__) || \ + defined(__powerpc__) || \ +- (defined(TARGET_DARWIN) && defined(__llvm__)) || \ +- (defined(TARGET_ANDROID) && defined(__arm__)) || \ +- defined(TARGET_RASPBERRY_PI) ++ defined(__arm__) + #define DISABLE_MATHUTILS_ASM_TRUNCATE_INT + #endif + +@@ -73,60 +69,63 @@ namespace MathUtils + { + assert(x > static_cast<double>(INT_MIN / 2) - 1.0); + assert(x < static_cast<double>(INT_MAX / 2) + 1.0); +- const float round_to_nearest = 0.5f; +- int i; + + #if defined(DISABLE_MATHUTILS_ASM_ROUND_INT) +- i = floor(x + round_to_nearest); +- +-#elif defined(__arm__) +- // From 'ARM-v7-M Architecture Reference Manual' page A7-569: +- // "The floating-point to integer operation (vcvt) [normally] uses the Round towards Zero rounding mode" +- // Because of this...we must use some less-than-straightforward logic to perform this operation without +- // changing the rounding mode flags +- +- /* The assembly below implements the following logic: +- if (x < 0) +- inc = -0.5f +- else +- inc = 0.5f +- int_val = trunc(x+inc); +- err = x - int_val; +- if (err == 0.5f) +- int_val++; +- return int_val; +- */ ++ /* This implementation warrants some further explanation. ++ * ++ * First, a couple of notes on rounding: ++ * 1) C casts from float/double to integer round towards zero. ++ * 2) Float/double additions are rounded according to the normal rules, ++ * in other words: on some architectures, it's fixed at compile-time, ++ * and on others it can be set using fesetround()). The following ++ * analysis assumes round-to-nearest with ties rounding to even. This ++ * is a fairly sensible choice, and is the default with ARM VFP. ++ * ++ * What this function wants is round-to-nearest with ties rounding to ++ * +infinity. This isn't an IEEE rounding mode, even if we could guarantee ++ * that all architectures supported fesetround(), which they don't. Instead, ++ * this adds an offset of 2147483648.5 (= 0x80000000.8p0), then casts to ++ * an unsigned int (crucially, all possible inputs are now in a range where ++ * round to zero acts the same as round to -infinity) and then subtracts ++ * 0x80000000 in the integer domain. The 0.5 component of the offset ++ * converts what is effectively a round down into a round to nearest, with ++ * ties rounding up, as desired. ++ * ++ * There is a catch, that because there is a double rounding, there is a ++ * small region where the input falls just *below* a tie, where the addition ++ * of the offset causes a round *up* to an exact integer, due to the finite ++ * level of precision available in floating point. You need to be aware of ++ * this when calling this function, although at present it is not believed ++ * that XBMC ever attempts to round numbers in this window. ++ * ++ * It is worth proving the size of the affected window. Recall that double ++ * precision employs a mantissa of 52 bits. ++ * 1) For all inputs -0.5 <= x <= INT_MAX ++ * Once the offset is applied, the most significant binary digit in the ++ * floating-point representation is +2^31. ++ * At this magnitude, the smallest step representable in double precision ++ * is 2^31 / 2^52 = 0.000000476837158203125 ++ * So the size of the range which is rounded up due to the addition is ++ * half the size of this step, or 0.0000002384185791015625 ++ * ++ * 2) For all inputs INT_MIN/2 < x < -0.5 ++ * Once the offset is applied, the most significant binary digit in the ++ * floating-point representation is +2^30. ++ * At this magnitude, the smallest step representable in double precision ++ * is 2^30 / 2^52 = 0.0000002384185791015625 ++ * So the size of the range which is rounded up due to the addition is ++ * half the size of this step, or 0.00000011920928955078125 ++ * ++ * 3) For all inputs INT_MIN <= x <= INT_MIN/2 ++ * The representation once the offset is applied has equal or greater ++ * precision than the input, so the addition does not cause rounding. ++ */ ++ return ((unsigned int) (x + 0x80000000.8p0)) - 0x80000000; + +- __asm__ __volatile__ ( +-#if defined(__ARM_PCS_VFP) +- "fconstd d1,#%G[rnd_val] \n\t" // Copy round_to_nearest into a working register (d1 = 0.5) + #else +- "vmov.F64 d1,%[rnd_val] \n\t" +-#endif +- "fcmpezd %P[value] \n\t" // Check value against zero (value == 0?) +- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags +- "it mi \n\t" +- "vnegmi.F64 d1, d1 \n\t" // if N-flag is set, negate round_to_nearest (if (value < 0) d1 = -1 * d1) +- "vadd.F64 d1,%P[value],d1 \n\t" // Add round_to_nearest to value, store result in working register (d1 += value) +- "vcvt.S32.F64 s3,d1 \n\t" // Truncate(round towards zero) (s3 = (int)d1) +- "vmov %[result],s3 \n\t" // Store the integer result in a general-purpose register (result = s3) +- "vcvt.F64.S32 d1,s3 \n\t" // Convert back to floating-point (d1 = (double)s3) +- "vsub.F64 d1,%P[value],d1 \n\t" // Calculate the error (d1 = value - d1) +-#if defined(__ARM_PCS_VFP) +- "fconstd d2,#%G[rnd_val] \n\t" // d2 = 0.5; +-#else +- "vmov.F64 d2,%[rnd_val] \n\t" +-#endif +- "fcmped d1, d2 \n\t" // (d1 == 0.5?) +- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags +- "it eq \n\t" +- "addeq %[result],#1 \n\t" // (if (d1 == d2) result++;) +- : [result] "=r"(i) // Outputs +- : [rnd_val] "Dv" (round_to_nearest), [value] "w"(x) // Inputs +- : "d1", "d2", "s3" // Clobbers +- ); +- +-#elif defined(__SSE2__) ++ const float round_to_nearest = 0.5f; ++ int i; ++#if defined(__SSE2__) + const float round_dn_to_nearest = 0.4999999f; + i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest)); + +@@ -150,8 +149,8 @@ namespace MathUtils + ); + + #endif +- + return i; ++#endif + } + + /*! \brief Truncate to nearest integer. +@@ -165,20 +164,13 @@ namespace MathUtils + { + assert(x > static_cast<double>(INT_MIN / 2) - 1.0); + assert(x < static_cast<double>(INT_MAX / 2) + 1.0); +- int i; + + #if defined(DISABLE_MATHUTILS_ASM_TRUNCATE_INT) +- return i = (int)x; +- +-#elif defined(__arm__) +- __asm__ __volatile__ ( +- "vcvt.S32.F64 %[result],%P[value] \n\t" // Truncate(round towards zero) and store the result +- : [result] "=w"(i) // Outputs +- : [value] "w"(x) // Inputs +- ); +- return i; ++ return x; + +-#elif defined(TARGET_WINDOWS) ++#else ++ int i; ++#if defined(TARGET_WINDOWS) + const float round_towards_m_i = -0.5f; + __asm + { +@@ -204,6 +196,7 @@ namespace MathUtils + if (x < 0) + i = -i; + return (i); ++#endif + } + + inline int64_t abs(int64_t a) +-- +1.9.3 + + +From 0ad4df440ea225cc951a65bf9553b1f00f416d85 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 11 Dec 2013 17:21:54 +0000 +Subject: [PATCH 19/94] Move the reference-counting of Begin and End calls from + DX and GL source files into GUIFontTTF.cpp. + +--- + xbmc/guilib/GUIFontTTF.cpp | 21 ++++++++ + xbmc/guilib/GUIFontTTF.h | 6 ++- + xbmc/guilib/GUIFontTTFDX.cpp | 79 +++++++++++++---------------- + xbmc/guilib/GUIFontTTFDX.h | 4 +- + xbmc/guilib/GUIFontTTFGL.cpp | 118 +++++++++++++++++++------------------------ + xbmc/guilib/GUIFontTTFGL.h | 4 +- + 6 files changed, 117 insertions(+), 115 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 9c8e516..90b9c4a 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -309,6 +309,27 @@ bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float as + return true; + } + ++void CGUIFontTTFBase::Begin() ++{ ++ if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin()) ++ { ++ m_vertex_count = 0; ++ } ++ // Keep track of the nested begin/end calls. ++ m_nestedBeginCount++; ++} ++ ++void CGUIFontTTFBase::End() ++{ ++ if (m_nestedBeginCount == 0) ++ return; ++ ++ if (--m_nestedBeginCount > 0) ++ return; ++ ++ LastEnd(); ++} ++ + void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling) + { + Begin(); +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 9723a43..c1c4507 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -77,8 +77,8 @@ class CGUIFontTTFBase + + bool Load(const CStdString& strFilename, float height = 20.0f, float aspect = 1.0f, float lineSpacing = 1.0f, bool border = false); + +- virtual void Begin() = 0; +- virtual void End() = 0; ++ void Begin(); ++ void End(); + + const CStdString& GetFileName() const { return m_strFileName; }; + +@@ -169,6 +169,8 @@ class CGUIFontTTFBase + CStdString m_strFileName; + + private: ++ virtual bool FirstBegin() = 0; ++ virtual void LastEnd() = 0; + CGUIFontTTFBase(const CGUIFontTTFBase&); + CGUIFontTTFBase& operator=(const CGUIFontTTFBase&); + int m_referenceCount; +diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp +index e3eba24..2f90668 100644 +--- a/xbmc/guilib/GUIFontTTFDX.cpp ++++ b/xbmc/guilib/GUIFontTTFDX.cpp +@@ -51,65 +51,56 @@ CGUIFontTTFDX::~CGUIFontTTFDX(void) + free(m_index); + } + +-void CGUIFontTTFDX::Begin() ++bool CGUIFontTTFDX::FirstBegin() + { + LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice(); + + if (pD3DDevice == NULL) ++ { + CLog::Log(LOGERROR, __FUNCTION__" - failed to get Direct3D device"); ++ return false; ++ } + +- if (m_nestedBeginCount == 0 && pD3DDevice != NULL && m_texture != NULL) ++ int unit = 0; ++ // just have to blit from our texture. ++ m_texture->BindToUnit(unit); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); ++ unit++; ++ ++ if(g_Windowing.UseLimitedColor()) + { +- int unit = 0; +- // just have to blit from our texture. +- m_texture->BindToUnit(unit); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP , D3DTOP_ADD ); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ; ++ pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) ); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR ); + unit++; +- +- if(g_Windowing.UseLimitedColor()) +- { +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP , D3DTOP_ADD ); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ; +- pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) ); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR ); +- unit++; +- } +- +- // no other texture stages needed +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE); +- +- pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); +- pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); +- pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); +- pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); +- pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); +- pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); +- pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); +- pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE); +- +- pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); +- m_vertex_count = 0; + } + +- // Keep track of the nested begin/end calls. +- m_nestedBeginCount++; ++ // no other texture stages needed ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE); ++ ++ pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); ++ pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); ++ pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); ++ pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); ++ pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); ++ pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); ++ pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); ++ pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE); ++ ++ pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); ++ return true; + } + +-void CGUIFontTTFDX::End() ++void CGUIFontTTFDX::LastEnd() + { + LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice(); + +- if (m_nestedBeginCount == 0) +- return; +- +- if (--m_nestedBeginCount > 0) +- return; +- + if (m_vertex_count == 0) + return; + +diff --git a/xbmc/guilib/GUIFontTTFDX.h b/xbmc/guilib/GUIFontTTFDX.h +index 0431085..17dfefe 100644 +--- a/xbmc/guilib/GUIFontTTFDX.h ++++ b/xbmc/guilib/GUIFontTTFDX.h +@@ -41,8 +41,8 @@ class CGUIFontTTFDX : public CGUIFontTTFBase + CGUIFontTTFDX(const CStdString& strFileName); + virtual ~CGUIFontTTFDX(void); + +- virtual void Begin(); +- virtual void End(); ++ virtual bool FirstBegin(); ++ virtual void LastEnd(); + + protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 3358a5a..93b7ea6 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -50,90 +50,78 @@ CGUIFontTTFGL::~CGUIFontTTFGL(void) + { + } + +-void CGUIFontTTFGL::Begin() ++bool CGUIFontTTFGL::FirstBegin() + { +- if (m_nestedBeginCount == 0 && m_texture != NULL) ++ if (!m_bTextureLoaded) + { +- if (!m_bTextureLoaded) +- { +- // Have OpenGL generate a texture object handle for us +- glGenTextures(1, (GLuint*) &m_nTexture); ++ // Have OpenGL generate a texture object handle for us ++ glGenTextures(1, (GLuint*) &m_nTexture); + +- // Bind the texture object +- glBindTexture(GL_TEXTURE_2D, m_nTexture); ++ // Bind the texture object ++ glBindTexture(GL_TEXTURE_2D, m_nTexture); + #ifdef HAS_GL +- glEnable(GL_TEXTURE_2D); ++ glEnable(GL_TEXTURE_2D); + #endif +- // Set the texture's stretching properties +- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ // Set the texture's stretching properties ++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + +- // Set the texture image -- THIS WORKS, so the pixels must be wrong. +- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0, +- GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels()); ++ // Set the texture image -- THIS WORKS, so the pixels must be wrong. ++ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0, ++ GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels()); + +- VerifyGLState(); +- m_bTextureLoaded = true; +- } ++ VerifyGLState(); ++ m_bTextureLoaded = true; ++ } + +- // Turn Blending On +- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); +- glEnable(GL_BLEND); ++ // Turn Blending On ++ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); ++ glEnable(GL_BLEND); + #ifdef HAS_GL +- glEnable(GL_TEXTURE_2D); ++ glEnable(GL_TEXTURE_2D); + #endif +- glBindTexture(GL_TEXTURE_2D, m_nTexture); ++ glBindTexture(GL_TEXTURE_2D, m_nTexture); + + #ifdef HAS_GL +- glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); +- glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE); +- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); +- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); +- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); +- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); +- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); +- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); +- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); +- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +- VerifyGLState(); ++ glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); ++ glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE); ++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); ++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); ++ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); ++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); ++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); ++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); ++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); ++ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); ++ VerifyGLState(); ++ ++ if(g_Windowing.UseLimitedColor()) ++ { ++ glActiveTexture(GL_TEXTURE1); ++ glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind ++ glEnable(GL_TEXTURE_2D); + +- if(g_Windowing.UseLimitedColor()) +- { +- glActiveTexture(GL_TEXTURE1); +- glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind +- glEnable(GL_TEXTURE_2D); +- +- const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f}; +- glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE); +- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba); +- glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB , GL_ADD); +- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB , GL_PREVIOUS); +- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB , GL_CONSTANT); +- glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB , GL_SRC_COLOR); +- glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB , GL_SRC_COLOR); +- glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA , GL_REPLACE); +- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA , GL_PREVIOUS); +- VerifyGLState(); +- } ++ const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f}; ++ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE); ++ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba); ++ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB , GL_ADD); ++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB , GL_PREVIOUS); ++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB , GL_CONSTANT); ++ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB , GL_SRC_COLOR); ++ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB , GL_SRC_COLOR); ++ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA , GL_REPLACE); ++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA , GL_PREVIOUS); ++ VerifyGLState(); ++ } + + #else +- g_Windowing.EnableGUIShader(SM_FONTS); ++ g_Windowing.EnableGUIShader(SM_FONTS); + #endif +- +- m_vertex_count = 0; +- } +- // Keep track of the nested begin/end calls. +- m_nestedBeginCount++; ++ return true; + } + +-void CGUIFontTTFGL::End() ++void CGUIFontTTFGL::LastEnd() + { +- if (m_nestedBeginCount == 0) +- return; +- +- if (--m_nestedBeginCount > 0) +- return; +- + #ifdef HAS_GL + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + +diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h +index a0dacba..6736cf7 100644 +--- a/xbmc/guilib/GUIFontTTFGL.h ++++ b/xbmc/guilib/GUIFontTTFGL.h +@@ -41,8 +41,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + CGUIFontTTFGL(const CStdString& strFileName); + virtual ~CGUIFontTTFGL(void); + +- virtual void Begin(); +- virtual void End(); ++ virtual bool FirstBegin(); ++ virtual void LastEnd(); + + protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); +-- +1.9.3 + + +From 427373ae5bb96489afb22529714c16e8f82c2195 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 11 Dec 2013 18:47:54 +0000 +Subject: [PATCH 20/94] Convert CGUIFontTTFBase::m_vertex to be managed as a + std::vector. Also retired CGUIFontTTFBase::m_vertex_count and + CGUIFontTTFBase::m_vertex_size because these can be derived from vector + member functions. + +--- + xbmc/guilib/GUIFontTTF.cpp | 29 +++++------------------------ + xbmc/guilib/GUIFontTTF.h | 4 +--- + xbmc/guilib/GUIFontTTFDX.cpp | 12 ++++++------ + xbmc/guilib/GUIFontTTFGL.cpp | 12 ++++++------ + 4 files changed, 18 insertions(+), 39 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 90b9c4a..3f219d9 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -139,8 +139,7 @@ CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) + m_nestedBeginCount = 0; + + m_bTextureLoaded = false; +- m_vertex_size = 4*1024; +- m_vertex = (SVertex*)malloc(m_vertex_size * sizeof(SVertex)); ++ m_vertex.reserve(4*1024); + + m_face = NULL; + m_stroker = NULL; +@@ -155,7 +154,6 @@ CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) + m_textureScaleX = m_textureScaleY = 0.0; + m_ellipsesWidth = m_height = 0.0f; + m_color = 0; +- m_vertex_count = 0; + m_nTexture = 0; + } + +@@ -216,9 +214,7 @@ void CGUIFontTTFBase::Clear() + g_freeTypeLibrary.ReleaseStroker(m_stroker); + m_stroker = NULL; + +- free(m_vertex); +- m_vertex = NULL; +- m_vertex_count = 0; ++ m_vertex.clear(); + } + + bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float aspect, float lineSpacing, bool border) +@@ -313,7 +309,7 @@ void CGUIFontTTFBase::Begin() + { + if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin()) + { +- m_vertex_count = 0; ++ m_vertex.clear(); + } + // Keep track of the nested begin/end calls. + m_nestedBeginCount++; +@@ -746,22 +742,9 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + float tt = texture.y1 * m_textureScaleY; + float tb = texture.y2 * m_textureScaleY; + +- // grow the vertex buffer if required +- if(m_vertex_count >= m_vertex_size) +- { +- m_vertex_size *= 2; +- void* old = m_vertex; +- m_vertex = (SVertex*)realloc(m_vertex, m_vertex_size * sizeof(SVertex)); +- if (!m_vertex) +- { +- free(old); +- CLog::Log(LOGSEVERE, "%s: can't allocate %"PRIdS" bytes for texture", __FUNCTION__ , m_vertex_size * sizeof(SVertex)); +- return; +- } +- } +- ++ m_vertex.resize(m_vertex.size() + 4); ++ SVertex* v = &m_vertex[m_vertex.size() - 4]; + m_color = color; +- SVertex* v = m_vertex + m_vertex_count; + + unsigned char r = GET_R(color) + , g = GET_G(color) +@@ -828,8 +811,6 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + v[3].y = y[2]; + v[3].z = z[2]; + #endif +- +- m_vertex_count+=4; + } + + // Oblique code - original taken from freetype2 (ftsynth.c) +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index c1c4507..35e3cf9 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -157,9 +157,7 @@ class CGUIFontTTFBase + bool m_bTextureLoaded; + unsigned int m_nTexture; + +- SVertex* m_vertex; +- int m_vertex_count; +- int m_vertex_size; ++ std::vector<SVertex> m_vertex; + + float m_textureScaleX; + float m_textureScaleY; +diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp +index 2f90668..6ef8984 100644 +--- a/xbmc/guilib/GUIFontTTFDX.cpp ++++ b/xbmc/guilib/GUIFontTTFDX.cpp +@@ -101,17 +101,17 @@ void CGUIFontTTFDX::LastEnd() + { + LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice(); + +- if (m_vertex_count == 0) ++ if (m_vertex.size() == 0) + return; + +- unsigned index_size = m_vertex_size * 6 / 4; ++ unsigned index_size = m_vertex.capacity() * 6 / 4; + if(m_index_size < index_size) + { + uint16_t* id = (uint16_t*)calloc(index_size, sizeof(uint16_t)); + if(id == NULL) + return; + +- for(int i = 0, b = 0; i < m_vertex_size; i += 4, b += 6) ++ for(int i = 0, b = 0; i < m_vertex.capacity(); i += 4, b += 6) + { + id[b+0] = i + 0; + id[b+1] = i + 1; +@@ -140,11 +140,11 @@ void CGUIFontTTFDX::LastEnd() + + pD3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST + , 0 +- , m_vertex_count +- , m_vertex_count / 2 ++ , m_vertex.size() ++ , m_vertex.size() / 2 + , m_index + , D3DFMT_INDEX16 +- , m_vertex ++ , &m_vertex[0] + , sizeof(SVertex)); + pD3DDevice->SetTransform(D3DTS_WORLD, &orig); + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 93b7ea6..a4e8571 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -125,13 +125,13 @@ void CGUIFontTTFGL::LastEnd() + #ifdef HAS_GL + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + +- glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, r)); +- glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, x)); +- glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, u)); ++ glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, r)); ++ glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, x)); ++ glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, u)); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +- glDrawArrays(GL_QUADS, 0, m_vertex_count); ++ glDrawArrays(GL_QUADS, 0, m_vertex.size()); + glPopClientAttrib(); + + glActiveTexture(GL_TEXTURE1); +@@ -147,10 +147,10 @@ void CGUIFontTTFGL::LastEnd() + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + + // stack object until VBOs will be used +- std::vector<SVertex> vecVertices( 6 * (m_vertex_count / 4) ); ++ std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) ); + SVertex *vertices = &vecVertices[0]; + +- for (int i=0; i<m_vertex_count; i+=4) ++ for (size_t i=0; i<m_vertex.size(); i+=4) + { + *vertices++ = m_vertex[i]; + *vertices++ = m_vertex[i+1]; +-- +1.9.3 + + +From a3e8e7f1055726a050e04d1e4ab61a5355cdbd6a Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Mon, 16 Dec 2013 18:58:12 +0000 +Subject: [PATCH 21/94] CGUIFontTTFBase::RenderCharacter can now append to + arbitrary vectors of vertices rather than only CGUIFontTTFBase::m_vertex + +--- + xbmc/guilib/GUIFontTTF.cpp | 12 +++++++----- + xbmc/guilib/GUIFontTTF.h | 2 +- + 2 files changed, 8 insertions(+), 6 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 3f219d9..1aaf68b 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -330,6 +330,8 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + { + Begin(); + ++ std::vector<SVertex> &vertices = m_vertex; ++ + // save the origin, which is scaled separately + m_originX = x; + m_originY = y; +@@ -410,7 +412,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + + for (int i = 0; i < 3; i++) + { +- RenderCharacter(startX + cursorX, startY, period, color, !scrolling); ++ RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices); + cursorX += period->advance; + } + break; +@@ -419,7 +421,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) + break; // exceeded max allowed width - stop rendering + +- RenderCharacter(startX + cursorX, startY, ch, color, !scrolling); ++ RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices); + if ( alignment & XBFONT_JUSTIFIED ) + { + if ((*pos & 0xffff) == L' ') +@@ -676,7 +678,7 @@ bool CGUIFontTTFBase::CacheCharacter(wchar_t letter, uint32_t style, Character * + return true; + } + +-void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX) ++void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector<SVertex> &vertices) + { + // actual image width isn't same as the character width as that is + // just baseline width and height should include the descent +@@ -742,8 +744,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + float tt = texture.y1 * m_textureScaleY; + float tb = texture.y2 * m_textureScaleY; + +- m_vertex.resize(m_vertex.size() + 4); +- SVertex* v = &m_vertex[m_vertex.size() - 4]; ++ vertices.resize(vertices.size() + 4); ++ SVertex* v = &vertices[vertices.size() - 4]; + m_color = color; + + unsigned char r = GET_R(color) +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 35e3cf9..4a6a696 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -109,7 +109,7 @@ class CGUIFontTTFBase + // Stuff for pre-rendering for speed + inline Character *GetCharacter(character_t letter); + bool CacheCharacter(wchar_t letter, uint32_t style, Character *ch); +- void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX); ++ void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector<SVertex> &vertices); + void ClearCharacterCache(); + + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight) = 0; +-- +1.9.3 + + +From 79263f02e56ef10410984c98a844aaa9bf43199e Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 15 Jan 2014 17:18:38 +0000 +Subject: [PATCH 22/94] Add a cache of font glyph bounding box vertices. This + is implemented as a template because ultimately we will key on different + parameters and store values of different types, depending upon whether we + have a GLES or non-GLES backend, and for GLES, whether or not the currently + applicable transformation matrices permit the use of hardware clipping. + +--- + xbmc/guilib/GUIFontCache.cpp | 105 ++++++++++++++++++++ + xbmc/guilib/GUIFontCache.h | 217 ++++++++++++++++++++++++++++++++++++++++++ + xbmc/guilib/GUIFontTTF.cpp | 181 +++++++++++++++++++---------------- + xbmc/guilib/GUIFontTTF.h | 5 + + xbmc/guilib/GUIFontTTFGL.cpp | 1 + + xbmc/guilib/GraphicContext.h | 1 + + xbmc/guilib/Makefile.in | 1 + + xbmc/guilib/TransformMatrix.h | 11 +++ + 8 files changed, 438 insertions(+), 84 deletions(-) + create mode 100644 xbmc/guilib/GUIFontCache.cpp + create mode 100644 xbmc/guilib/GUIFontCache.h + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +new file mode 100644 +index 0000000..c029713 +--- /dev/null ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -0,0 +1,105 @@ ++/* ++ * Copyright (C) 2005-2013 Team XBMC ++ * http://xbmc.org ++ * ++ * This Program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This Program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with XBMC; see the file COPYING. If not, see ++ * <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <stdint.h> ++#include <vector> ++#include "utils/StdString.h" // required by GUIFontTTF.h ++#include "GUIFontTTF.h" ++#include "GraphicContext.h" ++ ++template<class Position, class Value> ++void CGUIFontCacheEntry<Position, Value>::Reassign::operator()(CGUIFontCacheEntry<Position, Value> &entry) ++{ ++ entry.m_key.m_pos = m_key.m_pos; ++ entry.m_key.m_colors.assign(m_key.m_colors.begin(), m_key.m_colors.end()); ++ entry.m_key.m_text.assign(m_key.m_text.begin(), m_key.m_text.end()); ++ entry.m_key.m_alignment = m_key.m_alignment; ++ entry.m_key.m_maxPixelWidth = m_key.m_maxPixelWidth; ++ entry.m_key.m_scrolling = m_key.m_scrolling; ++ entry.m_matrix = m_key.m_matrix; ++ entry.m_key.m_scaleX = m_key.m_scaleX; ++ entry.m_key.m_scaleY = m_key.m_scaleY; ++ ++ entry.m_lastUsedMillis = m_nowMillis; ++ entry.m_value.clear(); ++} ++ ++template<class Position, class Value> ++CGUIFontCacheEntry<Position, Value>::~CGUIFontCacheEntry() ++{ ++ delete &m_key.m_colors; ++ delete &m_key.m_text; ++ m_value.clear(); ++} ++ ++template<class Position, class Value> ++Value &CGUIFontCache<Position, Value>::Lookup(Position &pos, ++ const vecColors &colors, const vecText &text, ++ uint32_t alignment, float maxPixelWidth, ++ bool scrolling, ++ unsigned int nowMillis, bool &dirtyCache) ++{ ++ const CGUIFontCacheKey<Position> key(pos, ++ const_cast<vecColors &>(colors), const_cast<vecText &>(text), ++ alignment, maxPixelWidth, ++ scrolling, g_graphicsContext.GetGUIMatrix(), ++ g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY()); ++ EntryHashIterator i = m_list.get<Hash>().find(key); ++ if (i == m_list.get<Hash>().end()) ++ { ++ /* Cache miss */ ++ EntryAgeIterator oldest = m_list.get<Age>().begin(); ++ if (!m_list.get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) ++ { ++ /* The oldest existing entry is old enough to expire and reuse */ ++ m_list.get<Hash>().modify(m_list.project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis)); ++ m_list.get<Age>().relocate(m_list.get<Age>().end(), oldest); ++ } ++ else ++ { ++ /* We need a new entry instead */ ++ /* Yes, this causes the creation an destruction of a temporary entry, but ++ * this code ought to only be used infrequently, when the cache needs to grow */ ++ m_list.get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis)); ++ } ++ dirtyCache = true; ++ return (--m_list.get<Age>().end())->m_value; ++ } ++ else ++ { ++ /* Cache hit */ ++ /* Update time in entry and move to the back of the list */ ++ i->m_lastUsedMillis = nowMillis; ++ m_list.get<Age>().relocate(m_list.get<Age>().end(), m_list.project<Age>(i)); ++ dirtyCache = false; ++ return i->m_value; ++ } ++} ++ ++template<class Position, class Value> ++void CGUIFontCache<Position, Value>::Flush() ++{ ++ m_list.get<Age>().clear(); ++} ++ ++template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> &entry); ++template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry(); ++template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); ++template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush(); +diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h +new file mode 100644 +index 0000000..ef65845 +--- /dev/null ++++ b/xbmc/guilib/GUIFontCache.h +@@ -0,0 +1,217 @@ ++/*! ++\file GUIFontCache.h ++\brief ++*/ ++ ++#ifndef CGUILIB_GUIFONTCACHE_H ++#define CGUILIB_GUIFONTCACHE_H ++#pragma once ++ ++/* ++ * Copyright (C) 2005-2013 Team XBMC ++ * http://xbmc.org ++ * ++ * This Program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This Program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with XBMC; see the file COPYING. If not, see ++ * <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <cstddef> ++#include <cstring> ++#include <stdint.h> ++ ++#include <algorithm> ++#include <vector> ++ ++#include "boost/multi_index_container.hpp" ++#include "boost/multi_index/sequenced_index.hpp" ++#include "boost/multi_index/hashed_index.hpp" ++#include "boost/multi_index/member.hpp" ++ ++#include "TransformMatrix.h" ++ ++using namespace boost::multi_index; ++ ++#define FONT_CACHE_TIME_LIMIT (1000) ++ ++template<class Position, class Value> class CGUIFontCache; ++class CGUIFontTTFBase; ++ ++template<class Position> ++struct CGUIFontCacheKey ++{ ++ Position m_pos; ++ vecColors &m_colors; ++ vecText &m_text; ++ uint32_t m_alignment; ++ float m_maxPixelWidth; ++ bool m_scrolling; ++ const TransformMatrix &m_matrix; ++ float m_scaleX; ++ float m_scaleY; ++ ++ CGUIFontCacheKey(Position pos, ++ vecColors &colors, vecText &text, ++ uint32_t alignment, float maxPixelWidth, ++ bool scrolling, const TransformMatrix &matrix, ++ float scaleX, float scaleY) : ++ m_pos(pos), ++ m_colors(colors), m_text(text), ++ m_alignment(alignment), m_maxPixelWidth(maxPixelWidth), ++ m_scrolling(scrolling), m_matrix(matrix), ++ m_scaleX(scaleX), m_scaleY(scaleY) ++ {} ++}; ++ ++template<class Position, class Value> ++struct CGUIFontCacheEntry ++{ ++ const CGUIFontCache<Position, Value> &m_cache; ++ CGUIFontCacheKey<Position> m_key; ++ TransformMatrix m_matrix; ++ ++ /* These need to be declared as mutable to get round the fact that only ++ * const iterators are available. These fields do not affect comparison or ++ * hash functors, so from the container's point of view, they are mutable. */ ++ mutable unsigned int m_lastUsedMillis; ++ mutable Value m_value; ++ ++ CGUIFontCacheEntry(const CGUIFontCache<Position, Value> &cache, const CGUIFontCacheKey<Position> &key, unsigned int nowMillis) : ++ m_cache(cache), ++ m_key(key.m_pos, ++ *new vecColors, *new vecText, ++ key.m_alignment, key.m_maxPixelWidth, ++ key.m_scrolling, m_matrix, ++ key.m_scaleX, key.m_scaleY), ++ m_lastUsedMillis(nowMillis) ++ { ++ m_key.m_colors.assign(key.m_colors.begin(), key.m_colors.end()); ++ m_key.m_text.assign(key.m_text.begin(), key.m_text.end()); ++ m_matrix = key.m_matrix; ++ } ++ ++ CGUIFontCacheEntry(const CGUIFontCacheEntry &other) : ++ m_cache(other.m_cache), ++ m_key(other.m_key.m_pos, ++ *new vecColors, *new vecText, ++ other.m_key.m_alignment, other.m_key.m_maxPixelWidth, ++ other.m_key.m_scrolling, m_matrix, ++ other.m_key.m_scaleX, other.m_key.m_scaleY), ++ m_lastUsedMillis(other.m_lastUsedMillis), ++ m_value(other.m_value) ++ { ++ m_key.m_colors.assign(other.m_key.m_colors.begin(), other.m_key.m_colors.end()); ++ m_key.m_text.assign(other.m_key.m_text.begin(), other.m_key.m_text.end()); ++ m_matrix = other.m_key.m_matrix; ++ } ++ ++ struct Reassign ++ { ++ Reassign(const CGUIFontCacheKey<Position> &key, unsigned int nowMillis) : m_key(key), m_nowMillis(nowMillis) {} ++ void operator()(CGUIFontCacheEntry &entry); ++ private: ++ const CGUIFontCacheKey<Position> &m_key; ++ unsigned int m_nowMillis; ++ }; ++ ++ ~CGUIFontCacheEntry(); ++}; ++ ++template<class Position> ++struct CGUIFontCacheHash ++{ ++ size_t operator()(const CGUIFontCacheKey<Position> &key) const ++ { ++ /* Not much effort has gone into choosing this hash function */ ++ size_t hash = 0, i; ++ for (i = 0; i < 3 && i < key.m_text.size(); ++i) ++ hash += key.m_text[i]; ++ if (key.m_colors.size()) ++ hash += key.m_colors[0]; ++ hash += MatrixHashContribution(key); ++ return hash; ++ } ++}; ++ ++template<class Position> ++struct CGUIFontCacheKeysMatch ++{ ++ bool operator()(const CGUIFontCacheKey<Position> &a, const CGUIFontCacheKey<Position> &b) const ++ { ++ return a.m_text == b.m_text && ++ a.m_colors == b.m_colors && ++ a.m_alignment == b.m_alignment && ++ a.m_scrolling == b.m_scrolling && ++ a.m_maxPixelWidth == b.m_maxPixelWidth && ++ Match(a.m_pos, a.m_matrix, b.m_pos, b.m_matrix, a.m_scrolling) && ++ a.m_scaleX == b.m_scaleX && ++ a.m_scaleY == b.m_scaleY; ++ } ++}; ++ ++template<class Position, class Value> ++class CGUIFontCache ++{ ++ /* Empty structs used as tags to identify indexes */ ++ struct Age {}; ++ struct Hash {}; ++ ++ typedef multi_index_container< ++ CGUIFontCacheEntry<Position, Value>, ++ indexed_by< ++ sequenced<tag<Age> >, ++ hashed_unique<tag<Hash>, member<CGUIFontCacheEntry<Position, Value>, CGUIFontCacheKey<Position>, &CGUIFontCacheEntry<Position, Value>::m_key>, CGUIFontCacheHash<Position>, CGUIFontCacheKeysMatch<Position> > ++ > ++ > EntryList; ++ ++ typedef typename EntryList::template index<Age>::type::iterator EntryAgeIterator; ++ typedef typename EntryList::template index<Hash>::type::iterator EntryHashIterator; ++ ++ EntryList m_list; ++ ++public: ++ const CGUIFontTTFBase &m_font; ++ ++ CGUIFontCache(CGUIFontTTFBase &font) : m_font(font) {} ++ Value &Lookup(Position &pos, ++ const vecColors &colors, const vecText &text, ++ uint32_t alignment, float maxPixelWidth, ++ bool scrolling, ++ unsigned int nowMillis, bool &dirtyCache); ++ void Flush(); ++}; ++ ++struct CGUIFontCacheStaticPosition ++{ ++ float m_x; ++ float m_y; ++ CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {} ++}; ++ ++typedef std::vector<SVertex> CGUIFontCacheStaticValue; ++ ++inline bool Match(const CGUIFontCacheStaticPosition &a, const TransformMatrix &a_m, ++ const CGUIFontCacheStaticPosition &b, const TransformMatrix &b_m, ++ bool scrolling) ++{ ++ return a.m_x == b.m_x && a.m_y == b.m_y && a_m == b_m; ++} ++ ++inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPosition> &a) ++{ ++ /* Ensure horizontally translated versions end up in different buckets */ ++ return a.m_matrix.m[0][3]; ++} ++ ++#endif +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 1aaf68b..288e61a 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -27,6 +27,7 @@ + #include "utils/MathUtils.h" + #include "utils/log.h" + #include "windowing/WindowingFactory.h" ++#include "threads/SystemClock.h" + + #include <math.h> + +@@ -131,7 +132,7 @@ class CFreeTypeLibrary + XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library + #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary) + +-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) ++CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this) + { + m_texture = NULL; + m_char = NULL; +@@ -330,108 +331,120 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + { + Begin(); + +- std::vector<SVertex> &vertices = m_vertex; +- +- // save the origin, which is scaled separately +- m_originX = x; +- m_originY = y; +- +- // Check if we will really need to truncate or justify the text +- if ( alignment & XBFONT_TRUNCATED ) ++ bool dirtyCache; ++ CGUIFontCacheStaticPosition staticPos(x, y); ++ std::vector<SVertex> &vertices = m_staticCache.Lookup(staticPos, ++ colors, text, ++ alignment, maxPixelWidth, ++ scrolling, ++ XbmcThreads::SystemClockMillis(), ++ dirtyCache); ++ if (dirtyCache) + { +- if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) +- alignment &= ~XBFONT_TRUNCATED; +- } +- else if ( alignment & XBFONT_JUSTIFIED ) +- { +- if ( maxPixelWidth <= 0.0f ) +- alignment &= ~XBFONT_JUSTIFIED; +- } ++ // save the origin, which is scaled separately ++ m_originX = x; ++ m_originY = y; + +- // calculate sizing information +- float startX = 0; +- float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering ++ // Check if we will really need to truncate or justify the text ++ if ( alignment & XBFONT_TRUNCATED ) ++ { ++ if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) ++ alignment &= ~XBFONT_TRUNCATED; ++ } ++ else if ( alignment & XBFONT_JUSTIFIED ) ++ { ++ if ( maxPixelWidth <= 0.0f ) ++ alignment &= ~XBFONT_JUSTIFIED; ++ } + +- if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) +- { +- // Get the extent of this line +- float w = GetTextWidthInternal( text.begin(), text.end() ); ++ // calculate sizing information ++ float startX = 0; ++ float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering + +- if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues +- w = maxPixelWidth; ++ if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) ++ { ++ // Get the extent of this line ++ float w = GetTextWidthInternal( text.begin(), text.end() ); + +- if ( alignment & XBFONT_CENTER_X) +- w *= 0.5f; +- // Offset this line's starting position +- startX -= w; +- } ++ if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues ++ w = maxPixelWidth; + +- float spacePerLetter = 0; // for justification effects +- if ( alignment & XBFONT_JUSTIFIED ) +- { +- // first compute the size of the text to render in both characters and pixels +- unsigned int lineChars = 0; +- float linePixels = 0; +- for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) ++ if ( alignment & XBFONT_CENTER_X) ++ w *= 0.5f; ++ // Offset this line's starting position ++ startX -= w; ++ } ++ ++ float spacePerLetter = 0; // for justification effects ++ if ( alignment & XBFONT_JUSTIFIED ) + { +- Character *ch = GetCharacter(*pos); +- if (ch) +- { // spaces have multiple times the justification spacing of normal letters +- lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; +- linePixels += ch->advance; ++ // first compute the size of the text to render in both characters and pixels ++ unsigned int lineChars = 0; ++ float linePixels = 0; ++ for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) ++ { ++ Character *ch = GetCharacter(*pos); ++ if (ch) ++ { // spaces have multiple times the justification spacing of normal letters ++ lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; ++ linePixels += ch->advance; ++ } + } ++ if (lineChars > 1) ++ spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); + } +- if (lineChars > 1) +- spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); +- } +- float cursorX = 0; // current position along the line +- +- for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) +- { +- // If starting text on a new line, determine justification effects +- // Get the current letter in the CStdString +- color_t color = (*pos & 0xff0000) >> 16; +- if (color >= colors.size()) +- color = 0; +- color = colors[color]; ++ float cursorX = 0; // current position along the line + +- // grab the next character +- Character *ch = GetCharacter(*pos); +- if (!ch) continue; +- +- if ( alignment & XBFONT_TRUNCATED ) ++ for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) + { +- // Check if we will be exceeded the max allowed width +- if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) +- { +- // Yup. Let's draw the ellipses, then bail +- // Perhaps we should really bail to the next line in this case?? +- Character *period = GetCharacter(L'.'); +- if (!period) +- break; ++ // If starting text on a new line, determine justification effects ++ // Get the current letter in the CStdString ++ color_t color = (*pos & 0xff0000) >> 16; ++ if (color >= colors.size()) ++ color = 0; ++ color = colors[color]; ++ ++ // grab the next character ++ Character *ch = GetCharacter(*pos); ++ if (!ch) continue; + +- for (int i = 0; i < 3; i++) ++ if ( alignment & XBFONT_TRUNCATED ) ++ { ++ // Check if we will be exceeded the max allowed width ++ if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) + { +- RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices); +- cursorX += period->advance; ++ // Yup. Let's draw the ellipses, then bail ++ // Perhaps we should really bail to the next line in this case?? ++ Character *period = GetCharacter(L'.'); ++ if (!period) ++ break; ++ ++ for (int i = 0; i < 3; i++) ++ { ++ RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices); ++ cursorX += period->advance; ++ } ++ break; + } +- break; + } +- } +- else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) +- break; // exceeded max allowed width - stop rendering ++ else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) ++ break; // exceeded max allowed width - stop rendering + +- RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices); +- if ( alignment & XBFONT_JUSTIFIED ) +- { +- if ((*pos & 0xffff) == L' ') +- cursorX += ch->advance + spacePerLetter * justification_word_weight; ++ RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices); ++ if ( alignment & XBFONT_JUSTIFIED ) ++ { ++ if ((*pos & 0xffff) == L' ') ++ cursorX += ch->advance + spacePerLetter * justification_word_weight; ++ else ++ cursorX += ch->advance + spacePerLetter; ++ } + else +- cursorX += ch->advance + spacePerLetter; ++ cursorX += ch->advance; + } +- else +- cursorX += ch->advance; + } ++ /* Append the new vertices (from the cache or otherwise) to the set collected ++ * since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); + + End(); + } +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 4a6a696..7cb4669 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -64,6 +64,9 @@ struct SVertex + }; + + ++#include "GUIFontCache.h" ++ ++ + class CGUIFontTTFBase + { + friend class CGUIFont; +@@ -166,6 +169,8 @@ class CGUIFontTTFBase + + CStdString m_strFileName; + ++ CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> m_staticCache; ++ + private: + virtual bool FirstBegin() = 0; + virtual void LastEnd() = 0; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index a4e8571..cb56987 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -200,6 +200,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) + m_textureScaleX = 1.0f / m_textureWidth; + if (m_textureHeight < newHeight) + CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight); ++ m_staticCache.Flush(); + + memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch()); + if (m_texture) +diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h +index 6c2dcd4..bab2457 100644 +--- a/xbmc/guilib/GraphicContext.h ++++ b/xbmc/guilib/GraphicContext.h +@@ -146,6 +146,7 @@ class CGraphicContext : public CCriticalSection, + inline void ScaleFinalCoords(float &x, float &y, float &z) const XBMC_FORCE_INLINE { m_finalTransform.matrix.TransformPosition(x, y, z); } + bool RectIsAngled(float x1, float y1, float x2, float y2) const; + ++ inline const TransformMatrix &GetGUIMatrix() const XBMC_FORCE_INLINE { return m_finalTransform.matrix; } + inline float GetGUIScaleX() const XBMC_FORCE_INLINE { return m_finalTransform.scaleX; } + inline float GetGUIScaleY() const XBMC_FORCE_INLINE { return m_finalTransform.scaleY; } + inline color_t MergeAlpha(color_t color) const XBMC_FORCE_INLINE +diff --git a/xbmc/guilib/Makefile.in b/xbmc/guilib/Makefile.in +index 086fb0d..af82979 100644 +--- a/xbmc/guilib/Makefile.in ++++ b/xbmc/guilib/Makefile.in +@@ -23,6 +23,7 @@ SRCS += GUIEditControl.cpp + SRCS += GUIFadeLabelControl.cpp + SRCS += GUIFixedListContainer.cpp + SRCS += GUIFont.cpp ++SRCS += GUIFontCache.cpp + SRCS += GUIFontManager.cpp + SRCS += GUIFontTTF.cpp + SRCS += GUIImage.cpp +diff --git a/xbmc/guilib/TransformMatrix.h b/xbmc/guilib/TransformMatrix.h +index f351c99..9036ba9 100644 +--- a/xbmc/guilib/TransformMatrix.h ++++ b/xbmc/guilib/TransformMatrix.h +@@ -245,3 +245,14 @@ class TransformMatrix + float alpha; + bool identity; + }; ++ ++inline bool operator==(const TransformMatrix &a, const TransformMatrix &b) ++{ ++ return a.alpha == b.alpha && ((a.identity && b.identity) || ++ (!a.identity && !b.identity && std::equal(&a.m[0][0], &a.m[0][0] + sizeof a.m / sizeof a.m[0][0], &b.m[0][0]))); ++} ++ ++inline bool operator!=(const TransformMatrix &a, const TransformMatrix &b) ++{ ++ return !operator==(a, b); ++} +-- +1.9.3 + + +From 65d2b7f112b400f75140de44579e6cdf98378b67 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Thu, 23 Jan 2014 22:24:17 +0000 +Subject: [PATCH 23/94] Lay the groundwork for hardware clipping. + +For glScissor() to replace CGraphicContext::ClipRect, a necessary condition +is that no shear or rotation is introduced between the coordinate systems +they use; this depends upon the settings of the GUI matrix m_finalTransform +as well as the OpenGL model-view and projection matrices. These all remain +unchanged between paired calls of CGUIShader::OnEnabled and +CGUIShader::OnDisabled, so we scan the matrices in CGUIShader::OnEnabled to +see whether hardware clipping is possible. + +Then, in CGUIFontTTFBase::RenderCharacter, we don't apply software clipping +in such cases. However, because vertices arising from multiple +CGUIFontTTFBase::DrawTextInternal calls (each of which often uses a different +clip rectangle) get lumped into the same vector, which only at the end is +passed to OpenGL for rendering, we need to wait a few commits before we can +actually apply hardware clipping. In the meantime, expect to see rendering +errors. +--- + xbmc/guilib/GUIFontTTF.cpp | 3 +- + xbmc/guilib/GUIShader.cpp | 80 +++++++++++++++++++++++++++++++- + xbmc/guilib/GUIShader.h | 11 +++++ + xbmc/guilib/GraphicContext.cpp | 10 ++++ + xbmc/guilib/GraphicContext.h | 1 + + xbmc/rendering/RenderSystem.h | 2 + + xbmc/rendering/gles/RenderSystemGLES.cpp | 22 +++++++++ + xbmc/rendering/gles/RenderSystemGLES.h | 2 + + 8 files changed, 128 insertions(+), 3 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 288e61a..19c7ff4 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -710,7 +710,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + (posY + ch->offsetY + height) * g_graphicsContext.GetGUIScaleY()); + vertex += CPoint(m_originX, m_originY); + CRect texture(ch->left, ch->top, ch->right, ch->bottom); +- g_graphicsContext.ClipRect(vertex, texture); ++ if (!g_Windowing.ScissorsCanEffectClipping()) ++ g_graphicsContext.ClipRect(vertex, texture); + + // transform our positions - note, no scaling due to GUI calibration/resolution occurs + float x[4], y[4], z[4]; +diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp +index 11089b8..53bce09 100644 +--- a/xbmc/guilib/GUIShader.cpp ++++ b/xbmc/guilib/GUIShader.cpp +@@ -26,6 +26,8 @@ + #include "GUIShader.h" + #include "MatrixGLES.h" + #include "utils/log.h" ++#include "windowing/egl/WinSystemEGL.h" ++#include "guilib/GraphicContext.h" + + CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_vert.glsl", shader) + { +@@ -86,8 +88,82 @@ bool CGUIShader::OnEnabled() + { + // This is called after glUseProgram() + +- glUniformMatrix4fv(m_hProj, 1, GL_FALSE, g_matrices.GetMatrix(MM_PROJECTION)); +- glUniformMatrix4fv(m_hModel, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); ++ GLfloat *projMatrix = g_matrices.GetMatrix(MM_PROJECTION); ++ GLfloat *modelMatrix = g_matrices.GetMatrix(MM_MODELVIEW); ++ glUniformMatrix4fv(m_hProj, 1, GL_FALSE, projMatrix); ++ glUniformMatrix4fv(m_hModel, 1, GL_FALSE, modelMatrix); ++ ++ const TransformMatrix &guiMatrix = g_graphicsContext.GetGUIMatrix(); ++ CRect viewPort; // absolute positions of corners ++ g_Windowing.GetViewPort(viewPort); ++ ++ /* glScissor operates in window coordinates. In order that we can use it to ++ * perform clipping, we must ensure that there is an independent linear ++ * transformation from the coordinate system used by CGraphicContext::ClipRect ++ * to window coordinates, separately for X and Y (in other words, no ++ * rotation or shear is introduced at any stage). To do, this, we need to ++ * check that zeros are present in the following locations: ++ * ++ * GUI matrix: ++ * / * 0 * * \ ++ * | 0 * * * | ++ * \ 0 0 * * / ++ * ^ TransformMatrix::TransformX/Y/ZCoord are only ever called with ++ * input z = 0, so this column doesn't matter ++ * Model-view matrix: ++ * / * 0 0 * \ ++ * | 0 * 0 * | ++ * | 0 0 * * | ++ * \ * * * * / <- eye w has no influence on window x/y (last column below ++ * is either 0 or ignored) ++ * Projection matrix: ++ * / * 0 0 0 \ ++ * | 0 * 0 0 | ++ * | * * * * | <- normalised device coordinate z has no influence on window x/y ++ * \ 0 0 * 0 / ++ * ++ * Some of these zeros are not strictly required to ensure this, but they tend ++ * to be zeroed in the common case, so by checking for zeros here, we simplify ++ * the calculation of the window x/y coordinates further down the line. ++ * ++ * (Minor detail: we don't quite deal in window coordinates as defined by ++ * OpenGL, because CRenderSystemGLES::SetScissors flips the Y axis. But all ++ * that's needed to handle that is an effective negation at the stage where ++ * Y is in normalised device coordinates.) ++ */ ++ m_clipPossible = guiMatrix.m[0][1] == 0 && ++ guiMatrix.m[1][0] == 0 && ++ guiMatrix.m[2][0] == 0 && ++ guiMatrix.m[2][1] == 0 && ++ modelMatrix[0+1*4] == 0 && ++ modelMatrix[0+2*4] == 0 && ++ modelMatrix[1+0*4] == 0 && ++ modelMatrix[1+2*4] == 0 && ++ modelMatrix[2+0*4] == 0 && ++ modelMatrix[2+1*4] == 0 && ++ projMatrix[0+1*4] == 0 && ++ projMatrix[0+2*4] == 0 && ++ projMatrix[0+3*4] == 0 && ++ projMatrix[1+0*4] == 0 && ++ projMatrix[1+2*4] == 0 && ++ projMatrix[1+3*4] == 0 && ++ projMatrix[3+0*4] == 0 && ++ projMatrix[3+1*4] == 0 && ++ projMatrix[3+3*4] == 0; ++ if (m_clipPossible) ++ { ++ m_clipXFactor = guiMatrix.m[0][0] * modelMatrix[0+0*4] * projMatrix[0+0*4]; ++ m_clipXOffset = (guiMatrix.m[0][3] * modelMatrix[0+0*4] + modelMatrix[0+3*4]) * projMatrix[0+0*4]; ++ m_clipYFactor = guiMatrix.m[1][1] * modelMatrix[1+1*4] * projMatrix[1+1*4]; ++ m_clipYOffset = (guiMatrix.m[1][3] * modelMatrix[1+1*4] + modelMatrix[1+3*4]) * projMatrix[1+1*4]; ++ float clipW = (guiMatrix.m[2][3] * modelMatrix[2+2*4] + modelMatrix[2+3*4]) * projMatrix[3+2*4]; ++ float xMult = (viewPort.x2 - viewPort.x1) / (2 * clipW); ++ float yMult = (viewPort.y1 - viewPort.y2) / (2 * clipW); // correct for inverted window coordinate scheme ++ m_clipXFactor = m_clipXFactor * xMult; ++ m_clipXOffset = m_clipXOffset * xMult + (viewPort.x2 + viewPort.x1) / 2; ++ m_clipYFactor = m_clipYFactor * yMult; ++ m_clipYOffset = m_clipYOffset * yMult + (viewPort.y2 + viewPort.y1) / 2; ++ } + + return true; + } +diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h +index c7e95aa..86ce4cc 100644 +--- a/xbmc/guilib/GUIShader.h ++++ b/xbmc/guilib/GUIShader.h +@@ -41,6 +41,11 @@ class CGUIShader : public CGLSLShaderProgram + GLint GetCord1Loc() { return m_hCord1; } + GLint GetUniColLoc() { return m_hUniCol; } + GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; } ++ bool HardwareClipIsPossible() { return m_clipPossible; } ++ GLfloat GetClipXFactor() { return m_clipXFactor; } ++ GLfloat GetClipXOffset() { return m_clipXOffset; } ++ GLfloat GetClipYFactor() { return m_clipYFactor; } ++ GLfloat GetClipYOffset() { return m_clipYOffset; } + + protected: + GLint m_hTex0; +@@ -56,6 +61,12 @@ class CGUIShader : public CGLSLShaderProgram + + GLfloat *m_proj; + GLfloat *m_model; ++ ++ bool m_clipPossible; ++ GLfloat m_clipXFactor; ++ GLfloat m_clipXOffset; ++ GLfloat m_clipYFactor; ++ GLfloat m_clipYOffset; + }; + + #endif // GUI_SHADER_H +diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp +index 38f17a7..5bffdf5 100644 +--- a/xbmc/guilib/GraphicContext.cpp ++++ b/xbmc/guilib/GraphicContext.cpp +@@ -167,6 +167,16 @@ void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2) + } + } + ++CRect CGraphicContext::GetClipRegion() ++{ ++ if (m_clipRegions.empty()) ++ return CRect(0, 0, m_iScreenWidth, m_iScreenHeight); ++ CRect clipRegion(m_clipRegions.top()); ++ if (!m_origins.empty()) ++ clipRegion -= m_origins.top(); ++ return clipRegion; ++} ++ + bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */) + { + // transform coordinates - we may have a rotation which changes the positioning of the +diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h +index bab2457..0a27643 100644 +--- a/xbmc/guilib/GraphicContext.h ++++ b/xbmc/guilib/GraphicContext.h +@@ -199,6 +199,7 @@ class CGraphicContext : public CCriticalSection, + void ApplyHardwareTransform(); + void RestoreHardwareTransform(); + void ClipRect(CRect &vertex, CRect &texture, CRect *diffuse = NULL); ++ CRect GetClipRegion(); + inline void AddGUITransform() + { + m_transforms.push(m_finalTransform); +diff --git a/xbmc/rendering/RenderSystem.h b/xbmc/rendering/RenderSystem.h +index fa64eba..c1dfb93 100644 +--- a/xbmc/rendering/RenderSystem.h ++++ b/xbmc/rendering/RenderSystem.h +@@ -110,6 +110,8 @@ class CRenderSystemBase + virtual void GetViewPort(CRect& viewPort) = 0; + virtual void RestoreViewPort() {}; + ++ virtual bool ScissorsCanEffectClipping() { return false; } ++ virtual CRect ClipRectToScissorRect(const CRect &rect) { return CRect(); } + virtual void SetScissors(const CRect &rect) = 0; + virtual void ResetScissors() = 0; + +diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp +index 653c9ec..deb3afc 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.cpp ++++ b/xbmc/rendering/gles/RenderSystemGLES.cpp +@@ -533,6 +533,28 @@ void CRenderSystemGLES::SetViewPort(CRect& viewPort) + m_viewPort[3] = viewPort.Height(); + } + ++bool CRenderSystemGLES::ScissorsCanEffectClipping() ++{ ++ if (m_pGUIshader[m_method]) ++ return m_pGUIshader[m_method]->HardwareClipIsPossible(); ++ ++ return false; ++} ++ ++CRect CRenderSystemGLES::ClipRectToScissorRect(const CRect &rect) ++{ ++ if (!m_pGUIshader[m_method]) ++ return CRect(); ++ float xFactor = m_pGUIshader[m_method]->GetClipXFactor(); ++ float xOffset = m_pGUIshader[m_method]->GetClipXOffset(); ++ float yFactor = m_pGUIshader[m_method]->GetClipYFactor(); ++ float yOffset = m_pGUIshader[m_method]->GetClipYOffset(); ++ return CRect(rect.x1 * xFactor + xOffset, ++ rect.y1 * yFactor + yOffset, ++ rect.x2 * xFactor + xOffset, ++ rect.y2 * yFactor + yOffset); ++} ++ + void CRenderSystemGLES::SetScissors(const CRect &rect) + { + if (!m_bRenderCreated) +diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h +index 98e398a..81ee49e 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.h ++++ b/xbmc/rendering/gles/RenderSystemGLES.h +@@ -63,6 +63,8 @@ class CRenderSystemGLES : public CRenderSystemBase + virtual void SetViewPort(CRect& viewPort); + virtual void GetViewPort(CRect& viewPort); + ++ virtual bool ScissorsCanEffectClipping(); ++ virtual CRect ClipRectToScissorRect(const CRect &rect); + virtual void SetScissors(const CRect& rect); + virtual void ResetScissors(); + +-- +1.9.3 + + +From e372121bc53da1b0353b51f5e9897011c5f54033 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Thu, 23 Jan 2014 16:42:22 +0000 +Subject: [PATCH 24/94] Increase font cache hit rate by keying on the + fractional part of m_originX and m_originY *after* they have been through the + graphics context's transformation matrix, plus the scale/rotation elements of + the matrix, rather than the origin in the original frame of reference plus + the complete transformation matrix. All vertices of individual glyph bounding + boxes are a constant offset from this position, and when the fractional part + of the translation is a match, the rounding of each vertex will be in the + same direction; this permits us to calculate the desired vertices from the + cached ones simply by adding the integer parts of the translations with no + additional rounding steps. + +Note that this requires that software clipping is *not* performed. +--- + xbmc/guilib/GUIFontCache.cpp | 8 +++++++ + xbmc/guilib/GUIFontCache.h | 43 +++++++++++++++++++++++++++++++++++ + xbmc/guilib/GUIFontTTF.cpp | 53 +++++++++++++++++++++++++++++++++++--------- + xbmc/guilib/GUIFontTTF.h | 1 + + 4 files changed, 95 insertions(+), 10 deletions(-) + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +index c029713..b66c00b 100644 +--- a/xbmc/guilib/GUIFontCache.cpp ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -85,6 +85,9 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos, + else + { + /* Cache hit */ ++ /* Update the translation arguments so that they hold the offset to apply ++ * to the cached values (but only in the dynamic case) */ ++ pos.UpdateWithOffsets(i->m_key.m_pos, scrolling); + /* Update time in entry and move to the back of the list */ + i->m_lastUsedMillis = nowMillis; + m_list.get<Age>().relocate(m_list.get<Age>().end(), m_list.project<Age>(i)); +@@ -103,3 +106,8 @@ template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStati + template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry(); + template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); + template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush(); ++ ++template void CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue> &entry); ++template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry(); ++template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); ++template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush(); +diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h +index ef65845..d913dee 100644 +--- a/xbmc/guilib/GUIFontCache.h ++++ b/xbmc/guilib/GUIFontCache.h +@@ -44,6 +44,7 @@ + using namespace boost::multi_index; + + #define FONT_CACHE_TIME_LIMIT (1000) ++#define FONT_CACHE_DIST_LIMIT (0.01) + + template<class Position, class Value> class CGUIFontCache; + class CGUIFontTTFBase; +@@ -197,6 +198,7 @@ struct CGUIFontCacheStaticPosition + float m_x; + float m_y; + CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {} ++ void UpdateWithOffsets(const CGUIFontCacheStaticPosition &cached, bool scrolling) {} + }; + + typedef std::vector<SVertex> CGUIFontCacheStaticValue; +@@ -214,4 +216,45 @@ inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPo + return a.m_matrix.m[0][3]; + } + ++struct CGUIFontCacheDynamicPosition ++{ ++ float m_x; ++ float m_y; ++ float m_z; ++ CGUIFontCacheDynamicPosition() {} ++ CGUIFontCacheDynamicPosition(float x, float y, float z) : m_x(x), m_y(y), m_z(z) {} ++ void UpdateWithOffsets(const CGUIFontCacheDynamicPosition &cached, bool scrolling) ++ { ++ if (scrolling) ++ m_x = m_x - cached.m_x; ++ else ++ m_x = floorf(m_x - cached.m_x + FONT_CACHE_DIST_LIMIT); ++ m_y = floorf(m_y - cached.m_y + FONT_CACHE_DIST_LIMIT); ++ m_z = floorf(m_z - cached.m_z + FONT_CACHE_DIST_LIMIT); ++ } ++}; ++ ++typedef std::vector<SVertex> CGUIFontCacheDynamicValue; ++ ++inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m, ++ const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m, ++ bool scrolling) ++{ ++ float diffX = a.m_x - b.m_x + FONT_CACHE_DIST_LIMIT; ++ float diffY = a.m_y - b.m_y + FONT_CACHE_DIST_LIMIT; ++ float diffZ = a.m_z - b.m_z + FONT_CACHE_DIST_LIMIT; ++ return (scrolling || diffX - floorf(diffX) < 2 * FONT_CACHE_DIST_LIMIT) && ++ diffY - floorf(diffY) < 2 * FONT_CACHE_DIST_LIMIT && ++ diffZ - floorf(diffZ) < 2 * FONT_CACHE_DIST_LIMIT && ++ a_m.m[0][0] == b_m.m[0][0] && ++ a_m.m[1][1] == b_m.m[1][1] && ++ a_m.m[2][2] == b_m.m[2][2]; ++ // We already know the first 3 columns of both matrices are diagonal, so no need to check the other elements ++} ++ ++inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheDynamicPosition> &a) ++{ ++ return 0; ++} ++ + #endif +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 19c7ff4..73f0e50 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -132,7 +132,7 @@ class CFreeTypeLibrary + XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library + #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary) + +-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this) ++CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this), m_dynamicCache(*this) + { + m_texture = NULL; + m_char = NULL; +@@ -332,13 +332,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + Begin(); + + bool dirtyCache; ++ bool hardwareClipping = g_Windowing.ScissorsCanEffectClipping(); + CGUIFontCacheStaticPosition staticPos(x, y); +- std::vector<SVertex> &vertices = m_staticCache.Lookup(staticPos, +- colors, text, +- alignment, maxPixelWidth, +- scrolling, +- XbmcThreads::SystemClockMillis(), +- dirtyCache); ++ CGUIFontCacheDynamicPosition dynamicPos; ++ if (hardwareClipping) ++ { ++ dynamicPos = CGUIFontCacheDynamicPosition(g_graphicsContext.ScaleFinalXCoord(x, y), ++ g_graphicsContext.ScaleFinalYCoord(x, y), ++ g_graphicsContext.ScaleFinalZCoord(x, y)); ++ } ++ std::vector<SVertex> &vertices = hardwareClipping ? ++ m_dynamicCache.Lookup(dynamicPos, ++ colors, text, ++ alignment, maxPixelWidth, ++ scrolling, ++ XbmcThreads::SystemClockMillis(), ++ dirtyCache) : ++ m_staticCache.Lookup(staticPos, ++ colors, text, ++ alignment, maxPixelWidth, ++ scrolling, ++ XbmcThreads::SystemClockMillis(), ++ dirtyCache); + if (dirtyCache) + { + // save the origin, which is scaled separately +@@ -441,10 +456,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + else + cursorX += ch->advance; + } ++ if (hardwareClipping) ++ /* Append the new vertices (which we have just constructed in the cache) ++ * to the set collected since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ } ++ else if (hardwareClipping) ++ { ++ /* Apply the translation offset to the vertices from the cache after ++ * appending them to the set collected since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ SVertex *v; ++ for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++) ++ { ++ v->x += dynamicPos.m_x; ++ v->y += dynamicPos.m_y; ++ v->z += dynamicPos.m_z; ++ } + } +- /* Append the new vertices (from the cache or otherwise) to the set collected +- * since the first Begin() call */ +- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ if (!hardwareClipping) ++ /* Append the new vertices (from the cache or otherwise) to the set collected ++ * since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); + + End(); + } +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 7cb4669..78445ab 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -170,6 +170,7 @@ class CGUIFontTTFBase + CStdString m_strFileName; + + CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> m_staticCache; ++ CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue> m_dynamicCache; + + private: + virtual bool FirstBegin() = 0; +-- +1.9.3 + + +From 10eeb73ca15798de26abd5f8846214c8938f8b42 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 8 Jan 2014 12:16:33 +0000 +Subject: [PATCH 25/94] Rewrite of scrolling text code. + +No longer shuffles the string round to minimise the number of characters +before the clipping rectangle; this doesn't save much on rendering time but +does harm the effectiveness of the cache. Now uses a pixel offset into the +string rather than a character + pixel offset, and plots the entire string +every time (execpt when the wrap point is visible, in which case the entire +string is plotted twice). + +It also makes motion smoother, because (possibly unintentionally) the +previous code preferred to align the scroll offset with character boundaries. +This would lead to uneven changes of position, especially when the width of +the character currently being scrolled off the edge was only slightly more +than an integral multiple of the scroll increment. +--- + xbmc/guilib/GUIFadeLabelControl.cpp | 8 +-- + xbmc/guilib/GUIFont.cpp | 123 +++++++++++++----------------------- + xbmc/guilib/GUIFont.h | 17 ++--- + xbmc/guilib/GUIRSSControl.cpp | 6 +- + xbmc/utils/RssReader.cpp | 2 +- + xbmc/utils/RssReader.h | 2 +- + 6 files changed, 58 insertions(+), 100 deletions(-) + +diff --git a/xbmc/guilib/GUIFadeLabelControl.cpp b/xbmc/guilib/GUIFadeLabelControl.cpp +index d594c04..86ee73a 100644 +--- a/xbmc/guilib/GUIFadeLabelControl.cpp ++++ b/xbmc/guilib/GUIFadeLabelControl.cpp +@@ -109,18 +109,14 @@ void CGUIFadeLabelControl::Process(unsigned int currentTime, CDirtyRegionList &d + bool moveToNextLabel = false; + if (!m_scrollOut) + { +- vecText text; +- m_textLayout.GetFirstText(text); +- if (m_scrollInfo.characterPos && m_scrollInfo.characterPos < text.size()) +- text.erase(text.begin(), text.begin() + min((int)m_scrollInfo.characterPos - 1, (int)text.size())); +- if (m_label.font->GetTextWidth(text) < m_width) ++ if (m_scrollInfo.pixelPos + m_width > m_scrollInfo.m_textWidth) + { + if (m_fadeAnim.GetProcess() != ANIM_PROCESS_NORMAL) + m_fadeAnim.QueueAnimation(ANIM_PROCESS_NORMAL); + moveToNextLabel = true; + } + } +- else if (m_scrollInfo.characterPos > m_textLayout.GetTextLength()) ++ else if (m_scrollInfo.pixelPos > m_scrollInfo.m_textWidth) + moveToNextLabel = true; + + // apply the fading animation +diff --git a/xbmc/guilib/GUIFont.cpp b/xbmc/guilib/GUIFont.cpp +index a7ee668..eb8efdb 100644 +--- a/xbmc/guilib/GUIFont.cpp ++++ b/xbmc/guilib/GUIFont.cpp +@@ -36,7 +36,12 @@ CScrollInfo::CScrollInfo(unsigned int wait /* = 50 */, float pos /* = 0 */, + initialWait = wait; + initialPos = pos; + SetSpeed(speed ? speed : defaultSpeed); +- g_charsetConverter.utf8ToW(scrollSuffix, suffix); ++ CStdStringW wsuffix; ++ g_charsetConverter.utf8ToW(scrollSuffix, wsuffix); ++ suffix.clear(); ++ suffix.reserve(wsuffix.size()); ++ for (vecText::size_type i = 0; i < wsuffix.size(); i++) ++ suffix.push_back(wsuffix[i]); + Reset(); + } + +@@ -115,11 +120,12 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo) + { + // draw at our scroll position + // we handle the scrolling as follows: +- // We scroll on a per-pixel basis up until we have scrolled the first character outside +- // of our viewport, whereby we cycle the string around, and reset the scroll position. +- // +- // pixelPos is the amount in pixels to move the string by. +- // characterPos is the amount in characters to rotate the string by. ++ // We scroll on a per-pixel basis (eschewing the use of character indices ++ // which were also in use previously). The complete string, including suffix, ++ // is plotted to achieve the desired effect - normally just the one time, but ++ // if there is a wrap point within the viewport then it will be plotted twice. ++ // If the string is smaller than the viewport, then it may be plotted even ++ // more times than that. + // + if (scrollInfo.waitTime) + { +@@ -135,54 +141,19 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo) + // move along by the appropriate scroll amount + float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX()); + +- if (scrollInfo.pixelSpeed > 0) ++ if (!scrollInfo.m_widthValid) + { +- // we want to move scrollAmount, grab the next character +- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- if (scrollInfo.pixelPos + scrollAmount < charWidth) +- scrollInfo.pixelPos += scrollAmount; // within the current character +- else +- { // past the current character, decrement scrollAmount by the charWidth and move to the next character +- while (scrollInfo.pixelPos + scrollAmount >= charWidth) +- { +- scrollAmount -= (charWidth - scrollInfo.pixelPos); +- scrollInfo.pixelPos = 0; +- scrollInfo.characterPos++; +- if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size()) +- { +- scrollInfo.Reset(); +- break; +- } +- charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- } +- } +- } +- else if (scrollInfo.pixelSpeed < 0) +- { // scrolling backwards +- // we want to move scrollAmount, grab the next character +- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- if (scrollInfo.pixelPos + scrollAmount < charWidth) +- scrollInfo.pixelPos += scrollAmount; // within the current character +- else +- { // past the current character, decrement scrollAmount by the charWidth and move to the next character +- while (scrollInfo.pixelPos + scrollAmount >= charWidth) +- { +- scrollAmount -= (charWidth - scrollInfo.pixelPos); +- scrollInfo.pixelPos = 0; +- if (scrollInfo.characterPos == 0) +- { +- scrollInfo.Reset(); +- scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1; +- break; +- } +- scrollInfo.characterPos--; +- charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- } +- } ++ /* Calculate the pixel width of the complete string */ ++ scrollInfo.m_textWidth = GetTextWidth(text); ++ scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix); ++ scrollInfo.m_widthValid = true; + } ++ scrollInfo.pixelPos += scrollAmount; ++ assert(scrollInfo.m_totalWidth != 0); ++ while (scrollInfo.pixelPos >= scrollInfo.m_totalWidth) ++ scrollInfo.pixelPos -= scrollInfo.m_totalWidth; + +- if(scrollInfo.characterPos != old.characterPos +- || scrollInfo.pixelPos != old.pixelPos) ++ if (scrollInfo.pixelPos != old.pixelPos) + return true; + else + return false; +@@ -194,39 +165,27 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo + if (!m_font) return; + if (!shadowColor) shadowColor = m_shadowColor; + +- float spaceWidth = GetCharWidth(L' '); +- // max chars on screen + extra margin chars +- vecText::size_type maxChars = +- std::min<vecText::size_type>( +- (text.size() + (vecText::size_type)scrollInfo.suffix.size()), +- (vecText::size_type)((maxWidth * 1.05f) / spaceWidth)); +- + if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment)) + return; // nothing to render + +- maxWidth = ROUND((maxWidth + scrollInfo.pixelPos) / g_graphicsContext.GetGUIScaleX()); ++ if (!scrollInfo.m_widthValid) ++ { ++ /* Calculate the pixel width of the complete string */ ++ scrollInfo.m_textWidth = GetTextWidth(text); ++ scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix); ++ scrollInfo.m_widthValid = true; ++ } ++ ++ assert(scrollInfo.m_totalWidth != 0); ++ ++ float textPixelWidth = ROUND(scrollInfo.m_textWidth / g_graphicsContext.GetGUIScaleX()); ++ float suffixPixelWidth = ROUND((scrollInfo.m_totalWidth - scrollInfo.m_textWidth) / g_graphicsContext.GetGUIScaleX()); + +- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); + float offset; + if(scrollInfo.pixelSpeed >= 0) + offset = scrollInfo.pixelPos; + else +- offset = charWidth - scrollInfo.pixelPos; +- +- // Now rotate our string as needed, only take a slightly larger then visible part of the text. +- unsigned int pos = scrollInfo.characterPos; +- vecText renderText; +- renderText.reserve(maxChars); +- for (vecText::size_type i = 0; i < maxChars; i++) +- { +- if (pos >= text.size() + scrollInfo.suffix.size()) +- pos = 0; +- if (pos < text.size()) +- renderText.push_back(text[pos]); +- else +- renderText.push_back(scrollInfo.suffix[pos - text.size()]); +- pos++; +- } ++ offset = scrollInfo.m_totalWidth - scrollInfo.pixelPos; + + vecColors renderColors; + for (unsigned int i = 0; i < colors.size(); i++) +@@ -239,9 +198,17 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo + vecColors shadowColors; + for (unsigned int i = 0; i < renderColors.size(); i++) + shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0); +- m_font->DrawTextInternal(x - offset + 1, y + 1, shadowColors, renderText, alignment, maxWidth + m_font->GetLineHeight(2.0f), scroll); ++ for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth) ++ { ++ m_font->DrawTextInternal(x + dx + 1, y + 1, shadowColors, text, alignment, textPixelWidth, scroll); ++ m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth + 1, y + 1, shadowColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll); ++ } ++ } ++ for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth) ++ { ++ m_font->DrawTextInternal(x + dx, y, renderColors, text, alignment, textPixelWidth, scroll); ++ m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth, y, renderColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll); + } +- m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + m_font->GetLineHeight(2.0f), scroll); + + g_graphicsContext.RestoreClipRegion(); + } +diff --git a/xbmc/guilib/GUIFont.h b/xbmc/guilib/GUIFont.h +index c55db48..09cf9b3 100644 +--- a/xbmc/guilib/GUIFont.h ++++ b/xbmc/guilib/GUIFont.h +@@ -64,7 +64,6 @@ class CScrollInfo + void Reset() + { + waitTime = initialWait; +- characterPos = 0; + // pixelPos is where we start the current letter, so is measured + // to the left of the text rendering's left edge. Thus, a negative + // value will mean the text starts to the right +@@ -72,25 +71,19 @@ class CScrollInfo + // privates: + m_averageFrameTime = 1000.f / abs(defaultSpeed); + m_lastFrameTime = 0; +- } +- uint32_t GetCurrentChar(const vecText &text) const +- { +- assert(text.size()); +- if (characterPos < text.size()) +- return text[characterPos]; +- else if (characterPos < text.size() + suffix.size()) +- return suffix[characterPos - text.size()]; +- return text[0]; ++ m_widthValid = false; + } + float GetPixelsPerFrame(); + + float pixelPos; + float pixelSpeed; + unsigned int waitTime; +- unsigned int characterPos; + unsigned int initialWait; + float initialPos; +- CStdStringW suffix; ++ vecText suffix; ++ mutable float m_textWidth; ++ mutable float m_totalWidth; ++ mutable bool m_widthValid; + + static const int defaultSpeed = 60; + private: +diff --git a/xbmc/guilib/GUIRSSControl.cpp b/xbmc/guilib/GUIRSSControl.cpp +index 712e118..203c138 100644 +--- a/xbmc/guilib/GUIRSSControl.cpp ++++ b/xbmc/guilib/GUIRSSControl.cpp +@@ -119,7 +119,9 @@ void CGUIRSSControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyre + dirty = true; + + if (CRssManager::Get().GetReader(GetID(), GetParentID(), this, m_pReader)) +- m_scrollInfo.characterPos = m_pReader->m_SavedScrollPos; ++ { ++ m_scrollInfo.pixelPos = m_pReader->m_savedScrollPixelPos; ++ } + else + { + if (m_strRSSTags != "") +@@ -177,7 +179,7 @@ void CGUIRSSControl::Render() + if (m_pReader) + { + m_pReader->CheckForUpdates(); +- m_pReader->m_SavedScrollPos = m_scrollInfo.characterPos; ++ m_pReader->m_savedScrollPixelPos = m_scrollInfo.pixelPos; + } + } + CGUIControl::Render(); +diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp +index b1e53b7..f68597a 100644 +--- a/xbmc/utils/RssReader.cpp ++++ b/xbmc/utils/RssReader.cpp +@@ -54,7 +54,7 @@ CRssReader::CRssReader() : CThread("RSSReader") + m_pObserver = NULL; + m_spacesBetweenFeeds = 0; + m_bIsRunning = false; +- m_SavedScrollPos = 0; ++ m_savedScrollPixelPos = 0; + m_rtlText = false; + m_requestRefresh = false; + } +diff --git a/xbmc/utils/RssReader.h b/xbmc/utils/RssReader.h +index 2c6f366..b74faf2 100644 +--- a/xbmc/utils/RssReader.h ++++ b/xbmc/utils/RssReader.h +@@ -43,7 +43,7 @@ class CRssReader : public CThread + void SetObserver(IRssObserver* observer); + void CheckForUpdates(); + void requestRefresh(); +- unsigned int m_SavedScrollPos; ++ float m_savedScrollPixelPos; + + private: + void Process(); +-- +1.9.3 + + +From 7064920379f68a7f6114813db8ad40a21c4957cc Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Mon, 27 Jan 2014 23:21:10 +0000 +Subject: [PATCH 26/94] Move the application of the translation offsets into + the GLES code. Still all pure software at this stage. Main change is in the + data types at the interface between CGUIFontTTFBase and CGUIFontTTFGL. The + old way (array of vertices in m_vertex) are retained in addition, for the + sake`of cases that need to use software clipping on GLES, as well as for DX + and GL support where the new scheme is not (yet?) used. + +--- + xbmc/guilib/GUIFontTTF.cpp | 19 +++--------- + xbmc/guilib/GUIFontTTF.h | 17 +++++++++++ + xbmc/guilib/GUIFontTTFGL.cpp | 72 ++++++++++++++++++++++++++++++++------------ + 3 files changed, 73 insertions(+), 35 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 73f0e50..ad0a53b 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -215,6 +215,7 @@ void CGUIFontTTFBase::Clear() + g_freeTypeLibrary.ReleaseStroker(m_stroker); + m_stroker = NULL; + ++ m_vertexTrans.clear(); + m_vertex.clear(); + } + +@@ -310,6 +311,7 @@ void CGUIFontTTFBase::Begin() + { + if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin()) + { ++ m_vertexTrans.clear(); + m_vertex.clear(); + } + // Keep track of the nested begin/end calls. +@@ -457,23 +459,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + cursorX += ch->advance; + } + if (hardwareClipping) +- /* Append the new vertices (which we have just constructed in the cache) +- * to the set collected since the first Begin() call */ +- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices)); + } + else if (hardwareClipping) +- { +- /* Apply the translation offset to the vertices from the cache after +- * appending them to the set collected since the first Begin() call */ +- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); +- SVertex *v; +- for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++) +- { +- v->x += dynamicPos.m_x; +- v->y += dynamicPos.m_y; +- v->z += dynamicPos.m_z; +- } +- } ++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices)); + if (!hardwareClipping) + /* Append the new vertices (from the cache or otherwise) to the set collected + * since the first Begin() call */ +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 78445ab..c71f90d 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -61,6 +61,14 @@ struct SVertex + unsigned char r, g, b, a; + #endif + float u, v; ++ struct SVertex Offset(float translate[3]) const ++ { ++ SVertex out = *this; ++ out.x += translate[0]; ++ out.y += translate[1]; ++ out.z += translate[2]; ++ return out; ++ } + }; + + +@@ -160,6 +168,15 @@ class CGUIFontTTFBase + bool m_bTextureLoaded; + unsigned int m_nTexture; + ++ struct CTranslatedVertices ++ { ++ float translateX; ++ float translateY; ++ float translateZ; ++ const std::vector<SVertex> *vertexBuffer; ++ CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {} ++ }; ++ std::vector<CTranslatedVertices> m_vertexTrans; + std::vector<SVertex> m_vertex; + + float m_textureScaleX; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index cb56987..f6aa081 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -146,34 +146,65 @@ void CGUIFontTTFGL::LastEnd() + GLint colLoc = g_Windowing.GUIShaderGetCol(); + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + +- // stack object until VBOs will be used +- std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) ); +- SVertex *vertices = &vecVertices[0]; ++ // Enable the attributes used by this shader ++ glEnableVertexAttribArray(posLoc); ++ glEnableVertexAttribArray(colLoc); ++ glEnableVertexAttribArray(tex0Loc); + +- for (size_t i=0; i<m_vertex.size(); i+=4) ++ if (m_vertex.size() > 0) + { +- *vertices++ = m_vertex[i]; +- *vertices++ = m_vertex[i+1]; +- *vertices++ = m_vertex[i+2]; ++ // Deal with vertices that had to use software clipping ++ std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) ); ++ SVertex *vertices = &vecVertices[0]; + +- *vertices++ = m_vertex[i+1]; +- *vertices++ = m_vertex[i+3]; +- *vertices++ = m_vertex[i+2]; +- } ++ for (size_t i=0; i<m_vertex.size(); i+=4) ++ { ++ *vertices++ = m_vertex[i]; ++ *vertices++ = m_vertex[i+1]; ++ *vertices++ = m_vertex[i+2]; + +- vertices = &vecVertices[0]; ++ *vertices++ = m_vertex[i+1]; ++ *vertices++ = m_vertex[i+3]; ++ *vertices++ = m_vertex[i+2]; ++ } + +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); +- // Normalize color values. Does not affect Performance at all. +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); ++ vertices = &vecVertices[0]; + +- glEnableVertexAttribArray(posLoc); +- glEnableVertexAttribArray(colLoc); +- glEnableVertexAttribArray(tex0Loc); ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); ++ // Normalize color values. Does not affect Performance at all. ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); ++ ++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ } ++ if (m_vertexTrans.size() > 0) ++ { ++ // Deal with the vertices that can be hardware clipped and therefore translated ++ std::vector<SVertex> vecVertices; ++ for (size_t i = 0; i < m_vertexTrans.size(); i++) ++ { ++ float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ }; ++ for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4) ++ { ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); ++ } ++ } ++ SVertex *vertices = &vecVertices[0]; + +- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); ++ // Normalize color values. Does not affect Performance at all. ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); ++ ++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ } + ++ // Disable the attributes used by this shader + glDisableVertexAttribArray(posLoc); + glDisableVertexAttribArray(colLoc); + glDisableVertexAttribArray(tex0Loc); +@@ -201,6 +232,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) + if (m_textureHeight < newHeight) + CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight); + m_staticCache.Flush(); ++ m_dynamicCache.Flush(); + + memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch()); + if (m_texture) +-- +1.9.3 + + +From 476fce7bc2897e8898f4392809d934b0d5f46518 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 15 Jan 2014 15:28:06 +0000 +Subject: [PATCH 27/94] Rather than applying the translation offsets to the + vertices, now applies them to the model view matrix from the top of the + matrix stack and pushes it over to OpenGL. The vertices themselves are still + all held client-side. + +--- + xbmc/guilib/GUIFontTTF.h | 8 ------- + xbmc/guilib/GUIFontTTFGL.cpp | 40 +++++++++++++++++++++----------- + xbmc/guilib/GUIShader.h | 1 + + xbmc/rendering/gles/RenderSystemGLES.cpp | 8 +++++++ + xbmc/rendering/gles/RenderSystemGLES.h | 1 + + 5 files changed, 36 insertions(+), 22 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index c71f90d..fde2085 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -61,14 +61,6 @@ struct SVertex + unsigned char r, g, b, a; + #endif + float u, v; +- struct SVertex Offset(float translate[3]) const +- { +- SVertex out = *this; +- out.x += translate[0]; +- out.y += translate[1]; +- out.z += translate[2]; +- return out; +- } + }; + + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index f6aa081..fbffaa0 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -29,6 +29,7 @@ + #include "utils/log.h" + #include "utils/GLUtils.h" + #include "windowing/WindowingFactory.h" ++#include "guilib/MatrixGLES.h" + + // stuff for freetype + #include <ft2build.h> +@@ -145,6 +146,7 @@ void CGUIFontTTFGL::LastEnd() + GLint posLoc = g_Windowing.GUIShaderGetPos(); + GLint colLoc = g_Windowing.GUIShaderGetCol(); + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); ++ GLint modelLoc = g_Windowing.GUIShaderGetModel(); + + // Enable the attributes used by this shader + glEnableVertexAttribArray(posLoc); +@@ -183,25 +185,35 @@ void CGUIFontTTFGL::LastEnd() + std::vector<SVertex> vecVertices; + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { +- float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ }; ++ // Apply the translation to the currently active (top-of-stack) model view matrix ++ g_matrices.MatrixMode(MM_MODELVIEW); ++ g_matrices.PushMatrix(); ++ g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ); ++ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); ++ ++ vecVertices.clear(); + for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4) + { +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); + } +- } +- SVertex *vertices = &vecVertices[0]; ++ SVertex *vertices = &vecVertices[0]; + +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); +- // Normalize color values. Does not affect Performance at all. +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); ++ // Normalize color values. Does not affect Performance at all. ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); + +- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ ++ g_matrices.PopMatrix(); ++ } ++ // Restore the original model view matrix ++ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + } + + // Disable the attributes used by this shader +diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h +index 86ce4cc..ba01956 100644 +--- a/xbmc/guilib/GUIShader.h ++++ b/xbmc/guilib/GUIShader.h +@@ -41,6 +41,7 @@ class CGUIShader : public CGLSLShaderProgram + GLint GetCord1Loc() { return m_hCord1; } + GLint GetUniColLoc() { return m_hUniCol; } + GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; } ++ GLint GetModelLoc() { return m_hModel; } + bool HardwareClipIsPossible() { return m_clipPossible; } + GLfloat GetClipXFactor() { return m_clipXFactor; } + GLfloat GetClipXOffset() { return m_clipXOffset; } +diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp +index deb3afc..0904d1f 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.cpp ++++ b/xbmc/rendering/gles/RenderSystemGLES.cpp +@@ -691,4 +691,12 @@ bool CRenderSystemGLES::SupportsStereo(RENDER_STEREO_MODE mode) + } + } + ++GLint CRenderSystemGLES::GUIShaderGetModel() ++{ ++ if (m_pGUIshader[m_method]) ++ return m_pGUIshader[m_method]->GetModelLoc(); ++ ++ return -1; ++} ++ + #endif +diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h +index 81ee49e..d2f9cd1 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.h ++++ b/xbmc/rendering/gles/RenderSystemGLES.h +@@ -91,6 +91,7 @@ class CRenderSystemGLES : public CRenderSystemBase + GLint GUIShaderGetCoord1(); + GLint GUIShaderGetUniCol(); + GLint GUIShaderGetCoord0Matrix(); ++ GLint GUIShaderGetModel(); + + protected: + virtual void SetVSyncImpl(bool enable) = 0; +-- +1.9.3 + + +From 473ccc4cbe616f672a72108d2ec98107780ac7da Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 29 Jan 2014 13:21:19 +0000 +Subject: [PATCH 28/94] Enable hardware clipping. + +--- + xbmc/guilib/GUIFontTTF.cpp | 4 ++-- + xbmc/guilib/GUIFontTTF.h | 5 ++++- + xbmc/guilib/GUIFontTTFGL.cpp | 6 ++++++ + 3 files changed, 12 insertions(+), 3 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index ad0a53b..4dc4c8e 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -459,10 +459,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + cursorX += ch->advance; + } + if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices)); ++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion())); + } + else if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices)); ++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion())); + if (!hardwareClipping) + /* Append the new vertices (from the cache or otherwise) to the set collected + * since the first Begin() call */ +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index fde2085..5e7c31f 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -27,6 +27,8 @@ + * + */ + ++#include "Geometry.h" ++ + // forward definition + class CBaseTexture; + +@@ -166,7 +168,8 @@ class CGUIFontTTFBase + float translateY; + float translateZ; + const std::vector<SVertex> *vertexBuffer; +- CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {} ++ CRect clip; ++ CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} + }; + std::vector<CTranslatedVertices> m_vertexTrans; + std::vector<SVertex> m_vertex; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index fbffaa0..b7618e1 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -185,6 +185,10 @@ void CGUIFontTTFGL::LastEnd() + std::vector<SVertex> vecVertices; + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { ++ // Apply the clip rectangle ++ CRect clip = g_Windowing.ClipRectToScissorRect(m_vertexTrans[i].clip); ++ g_graphicsContext.SetScissors(clip); ++ + // Apply the translation to the currently active (top-of-stack) model view matrix + g_matrices.MatrixMode(MM_MODELVIEW); + g_matrices.PushMatrix(); +@@ -212,6 +216,8 @@ void CGUIFontTTFGL::LastEnd() + + g_matrices.PopMatrix(); + } ++ // Restore the original scissor rectangle ++ g_graphicsContext.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + } +-- +1.9.3 + + +From f39c4523a1c05425fb94d3536a510709784f9558 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 15 Jan 2014 15:32:51 +0000 +Subject: [PATCH 29/94] Move the vertex data across to a vertex buffer object + just prior to drawing. + +--- + xbmc/guilib/GUIFontTTFGL.cpp | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index b7618e1..0df3749 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -207,12 +207,24 @@ void CGUIFontTTFGL::LastEnd() + } + SVertex *vertices = &vecVertices[0]; + +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); +- // Normalize color values. Does not affect Performance at all. +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); +- ++ // Generate a unique buffer object name and put it in vertexBuffer ++ GLuint vertexBuffer; ++ glGenBuffers(1, &vertexBuffer); ++ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point ++ glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); ++ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER ++ // binding point (i.e. our buffer object) and initialise it from the ++ // specified client-side pointer ++ glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW); ++ // Set up the offsets of the various vertex attributes within the buffer ++ // object bound to GL_ARRAY_BUFFER ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x)); ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u)); ++ // Do the actual drawing operation, using the full set of vertices in the buffer + glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ // Release the buffer name for reuse ++ glDeleteBuffers(1, &vertexBuffer); + + g_matrices.PopMatrix(); + } +@@ -220,6 +232,8 @@ void CGUIFontTTFGL::LastEnd() + g_graphicsContext.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); ++ // Unbind GL_ARRAY_BUFFER ++ glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // Disable the attributes used by this shader +-- +1.9.3 + + +From 3c6c1c4f9c7aec0f41fd22d7d4e7f5918a70c69a Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Wed, 15 Jan 2014 16:04:04 +0000 +Subject: [PATCH 30/94] Move vertex data into an OpenGL VBO when the font cache + entry is populated. The font cache now stores the "name" (handle) of the VBO, + rather than a vector of vertices. + +--- + xbmc/guilib/GUIFontCache.cpp | 6 ++++ + xbmc/guilib/GUIFontCache.h | 30 +++++++++++++++++- + xbmc/guilib/GUIFontTTF.cpp | 15 +++++++-- + xbmc/guilib/GUIFontTTF.h | 7 +++-- + xbmc/guilib/GUIFontTTFGL.cpp | 74 ++++++++++++++++++++++++++++++-------------- + xbmc/guilib/GUIFontTTFGL.h | 5 +++ + 6 files changed, 107 insertions(+), 30 deletions(-) + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +index b66c00b..895fa72 100644 +--- a/xbmc/guilib/GUIFontCache.cpp ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -111,3 +111,9 @@ template void CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDyna + template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry(); + template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); + template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush(); ++ ++void CVertexBuffer::clear() ++{ ++ if (m_font != NULL) ++ m_font->DestroyVertexBuffer(*this); ++} +diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h +index d913dee..ff766bf 100644 +--- a/xbmc/guilib/GUIFontCache.h ++++ b/xbmc/guilib/GUIFontCache.h +@@ -234,7 +234,35 @@ struct CGUIFontCacheDynamicPosition + } + }; + +-typedef std::vector<SVertex> CGUIFontCacheDynamicValue; ++struct CVertexBuffer ++{ ++ void *bufferHandle; ++ size_t size; ++ CVertexBuffer() : bufferHandle(NULL), size(0), m_font(NULL) {} ++ CVertexBuffer(void *bufferHandle, size_t size, const CGUIFontTTFBase *font) : bufferHandle(bufferHandle), size(size), m_font(font) {} ++ CVertexBuffer(const CVertexBuffer &other) : bufferHandle(other.bufferHandle), size(other.size), m_font(other.m_font) ++ { ++ /* In practice, the copy constructor is only called before a vertex buffer ++ * has been attached. If this should ever change, we'll need another support ++ * function in GUIFontTTFGL/DX to duplicate a buffer, given its handle. */ ++ assert(other.bufferHandle == 0); ++ } ++ CVertexBuffer &operator=(CVertexBuffer &other) ++ { ++ /* This is used with move-assignment semantics for initialising the object in the font cache */ ++ assert(bufferHandle == 0); ++ bufferHandle = other.bufferHandle; ++ other.bufferHandle = 0; ++ size = other.size; ++ m_font = other.m_font; ++ return *this; ++ } ++ void clear(); ++private: ++ const CGUIFontTTFBase *m_font; ++}; ++ ++typedef CVertexBuffer CGUIFontCacheDynamicValue; + + inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m, + const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m, +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 4dc4c8e..8b25306 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -343,13 +343,18 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + g_graphicsContext.ScaleFinalYCoord(x, y), + g_graphicsContext.ScaleFinalZCoord(x, y)); + } +- std::vector<SVertex> &vertices = hardwareClipping ? ++ CVertexBuffer unusedVertexBuffer; ++ CVertexBuffer &vertexBuffer = hardwareClipping ? + m_dynamicCache.Lookup(dynamicPos, + colors, text, + alignment, maxPixelWidth, + scrolling, + XbmcThreads::SystemClockMillis(), + dirtyCache) : ++ unusedVertexBuffer; ++ std::vector<SVertex> tempVertices; ++ std::vector<SVertex> &vertices = hardwareClipping ? ++ tempVertices : + m_staticCache.Lookup(staticPos, + colors, text, + alignment, maxPixelWidth, +@@ -459,10 +464,14 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + cursorX += ch->advance; + } + if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion())); ++ { ++ CVertexBuffer newVertexBuffer = CreateVertexBuffer(tempVertices); ++ vertexBuffer = newVertexBuffer; ++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertexBuffer, g_graphicsContext.GetClipRegion())); ++ } + } + else if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion())); ++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertexBuffer, g_graphicsContext.GetClipRegion())); + if (!hardwareClipping) + /* Append the new vertices (from the cache or otherwise) to the set collected + * since the first Begin() call */ +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 5e7c31f..b1cd525 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -84,6 +84,9 @@ class CGUIFontTTFBase + + void Begin(); + void End(); ++ /* The next two should only be called if we've declared we can do hardware clipping */ ++ virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const { assert(false); return CVertexBuffer(); } ++ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const {} + + const CStdString& GetFileName() const { return m_strFileName; }; + +@@ -167,9 +170,9 @@ class CGUIFontTTFBase + float translateX; + float translateY; + float translateZ; +- const std::vector<SVertex> *vertexBuffer; ++ const CVertexBuffer *vertexBuffer; + CRect clip; +- CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} ++ CTranslatedVertices(float translateX, float translateY, float translateZ, const CVertexBuffer *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} + }; + std::vector<CTranslatedVertices> m_vertexTrans; + std::vector<SVertex> m_vertex; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 0df3749..1cd684b7 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -49,6 +49,10 @@ CGUIFontTTFGL::CGUIFontTTFGL(const CStdString& strFileName) + + CGUIFontTTFGL::~CGUIFontTTFGL(void) + { ++ // It's important that all the CGUIFontCacheEntry objects are ++ // destructed before the CGUIFontTTFGL goes out of scope, because ++ // our virtual methods won't be accessible after this point ++ m_dynamicCache.Flush(); + } + + bool CGUIFontTTFGL::FirstBegin() +@@ -182,7 +186,6 @@ void CGUIFontTTFGL::LastEnd() + if (m_vertexTrans.size() > 0) + { + // Deal with the vertices that can be hardware clipped and therefore translated +- std::vector<SVertex> vecVertices; + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { + // Apply the clip rectangle +@@ -195,36 +198,17 @@ void CGUIFontTTFGL::LastEnd() + g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + +- vecVertices.clear(); +- for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4) +- { +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); +- } +- SVertex *vertices = &vecVertices[0]; +- +- // Generate a unique buffer object name and put it in vertexBuffer +- GLuint vertexBuffer; +- glGenBuffers(1, &vertexBuffer); + // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point +- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); +- // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER +- // binding point (i.e. our buffer object) and initialise it from the +- // specified client-side pointer +- glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW); ++ glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle); ++ + // Set up the offsets of the various vertex attributes within the buffer + // object bound to GL_ARRAY_BUFFER + glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x)); + glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r)); + glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u)); ++ + // Do the actual drawing operation, using the full set of vertices in the buffer +- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); +- // Release the buffer name for reuse +- glDeleteBuffers(1, &vertexBuffer); ++ glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size); + + g_matrices.PopMatrix(); + } +@@ -245,6 +229,48 @@ void CGUIFontTTFGL::LastEnd() + #endif + } + ++#if HAS_GLES ++CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const ++{ ++ // Rearrange the vertices to describe triangles ++ std::vector<SVertex> triangleVertices; ++ triangleVertices.reserve(vertices.size() * 6 / 4); ++ for (size_t i = 0; i < vertices.size(); i += 4) ++ { ++ triangleVertices.push_back(vertices[i]); ++ triangleVertices.push_back(vertices[i+1]); ++ triangleVertices.push_back(vertices[i+2]); ++ triangleVertices.push_back(vertices[i+1]); ++ triangleVertices.push_back(vertices[i+3]); ++ triangleVertices.push_back(vertices[i+2]); ++ } ++ ++ // Generate a unique buffer object name and put it in bufferHandle ++ GLuint bufferHandle; ++ glGenBuffers(1, &bufferHandle); ++ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point ++ glBindBuffer(GL_ARRAY_BUFFER, bufferHandle); ++ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER ++ // binding point (i.e. our buffer object) and initialise it from the ++ // specified client-side pointer ++ glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW); ++ // Unbind GL_ARRAY_BUFFER ++ glBindBuffer(GL_ARRAY_BUFFER, 0); ++ ++ return CVertexBuffer((void *) bufferHandle, vertices.size() / 4, this); ++} ++ ++void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer &buffer) const ++{ ++ if (buffer.bufferHandle != 0) ++ { ++ // Release the buffer name for reuse ++ glDeleteBuffers(1, (GLuint *) &buffer.bufferHandle); ++ buffer.bufferHandle = 0; ++ } ++} ++#endif ++ + CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) + { + newHeight = CBaseTexture::PadPow2(newHeight); +diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h +index 6736cf7..168fb21 100644 +--- a/xbmc/guilib/GUIFontTTFGL.h ++++ b/xbmc/guilib/GUIFontTTFGL.h +@@ -29,6 +29,7 @@ + + + #include "GUIFontTTF.h" ++#include "system.h" + + + /*! +@@ -43,6 +44,10 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + + virtual bool FirstBegin(); + virtual void LastEnd(); ++#if HAS_GLES ++ virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const; ++ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const; ++#endif + + protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); +-- +1.9.3 + + +From 073c09ba7de6f6b7676c83d71b6933790626874f Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Thu, 16 Jan 2014 16:29:42 +0000 +Subject: [PATCH 31/94] Switch from glDrawArrays() to glDrawElements(). This + involves setting up a static VBO containing the indexes necessary to convert + from quads to triangles on the fly in the GPU. + +--- + xbmc/guilib/GUIFontTTFGL.cpp | 72 +++++++++++++++++++++++++------------ + xbmc/guilib/GUIFontTTFGL.h | 9 +++++ + xbmc/windowing/egl/WinSystemEGL.cpp | 17 +++++++++ + 3 files changed, 76 insertions(+), 22 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 1cd684b7..d476409 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -186,6 +186,10 @@ void CGUIFontTTFGL::LastEnd() + if (m_vertexTrans.size() > 0) + { + // Deal with the vertices that can be hardware clipped and therefore translated ++ ++ // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle); ++ + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { + // Apply the clip rectangle +@@ -201,14 +205,21 @@ void CGUIFontTTFGL::LastEnd() + // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point + glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle); + +- // Set up the offsets of the various vertex attributes within the buffer +- // object bound to GL_ARRAY_BUFFER +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x)); +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u)); ++ // Do the actual drawing operation, split into groups of characters no ++ // larger than the pre-determined size of the element array ++ for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX) ++ { ++ size_t count = m_vertexTrans[i].vertexBuffer->size - character; ++ count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX); ++ ++ // Set up the offsets of the various vertex attributes within the buffer ++ // object bound to GL_ARRAY_BUFFER ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, x))); ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, r))); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, u))); + +- // Do the actual drawing operation, using the full set of vertices in the buffer +- glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size); ++ glDrawElements(GL_TRIANGLES, 6 * count, GL_UNSIGNED_SHORT, 0); ++ } + + g_matrices.PopMatrix(); + } +@@ -216,8 +227,9 @@ void CGUIFontTTFGL::LastEnd() + g_graphicsContext.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); +- // Unbind GL_ARRAY_BUFFER ++ // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER + glBindBuffer(GL_ARRAY_BUFFER, 0); ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // Disable the attributes used by this shader +@@ -232,19 +244,6 @@ void CGUIFontTTFGL::LastEnd() + #if HAS_GLES + CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const + { +- // Rearrange the vertices to describe triangles +- std::vector<SVertex> triangleVertices; +- triangleVertices.reserve(vertices.size() * 6 / 4); +- for (size_t i = 0; i < vertices.size(); i += 4) +- { +- triangleVertices.push_back(vertices[i]); +- triangleVertices.push_back(vertices[i+1]); +- triangleVertices.push_back(vertices[i+2]); +- triangleVertices.push_back(vertices[i+1]); +- triangleVertices.push_back(vertices[i+3]); +- triangleVertices.push_back(vertices[i+2]); +- } +- + // Generate a unique buffer object name and put it in bufferHandle + GLuint bufferHandle; + glGenBuffers(1, &bufferHandle); +@@ -253,7 +252,7 @@ CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vert + // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER + // binding point (i.e. our buffer object) and initialise it from the + // specified client-side pointer +- glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW); ++ glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof (SVertex), &vertices[0], GL_STATIC_DRAW); + // Unbind GL_ARRAY_BUFFER + glBindBuffer(GL_ARRAY_BUFFER, 0); + +@@ -348,4 +347,33 @@ void CGUIFontTTFGL::DeleteHardwareTexture() + } + } + ++#if HAS_GLES ++void CGUIFontTTFGL::CreateStaticVertexBuffers(void) ++{ ++ // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point ++ glGenBuffers(1, &m_elementArrayHandle); ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle); ++ // Create an array holding the mesh indices to convert quads to triangles ++ GLushort index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6]; ++ for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++) ++ { ++ index[i][0] = 4*i; ++ index[i][1] = 4*i+1; ++ index[i][2] = 4*i+2; ++ index[i][3] = 4*i+1; ++ index[i][4] = 4*i+3; ++ index[i][5] = 4*i+2; ++ } ++ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof index, index, GL_STATIC_DRAW); ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); ++} ++ ++void CGUIFontTTFGL::DestroyStaticVertexBuffers(void) ++{ ++ glDeleteBuffers(1, &m_elementArrayHandle); ++} ++ ++GLuint CGUIFontTTFGL::m_elementArrayHandle; ++#endif ++ + #endif +diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h +index 168fb21..a14ab7a 100644 +--- a/xbmc/guilib/GUIFontTTFGL.h ++++ b/xbmc/guilib/GUIFontTTFGL.h +@@ -30,6 +30,7 @@ + + #include "GUIFontTTF.h" + #include "system.h" ++#include "system_gl.h" + + + /*! +@@ -47,6 +48,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + #if HAS_GLES + virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const; + virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const; ++ static void CreateStaticVertexBuffers(void); ++ static void DestroyStaticVertexBuffers(void); + #endif + + protected: +@@ -54,6 +57,12 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2); + virtual void DeleteHardwareTexture(); + ++#if HAS_GLES ++#define ELEMENT_ARRAY_MAX_CHAR_INDEX (1000) ++ ++ static GLuint m_elementArrayHandle; ++#endif ++ + }; + + #endif +diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp +index dfc4672..0c32947 100644 +--- a/xbmc/windowing/egl/WinSystemEGL.cpp ++++ b/xbmc/windowing/egl/WinSystemEGL.cpp +@@ -29,6 +29,7 @@ + #include "settings/AdvancedSettings.h" + #include "settings/Settings.h" + #include "settings/DisplaySettings.h" ++#include "guilib/GUIFontTTFGL.h" + #include "utils/log.h" + #include "EGLWrapper.h" + #include "EGLQuirks.h" +@@ -192,6 +193,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res) + return false; + } + ++#if HAS_GLES ++ bool newContext = false; ++#endif + if (m_context == EGL_NO_CONTEXT) + { + if (!m_egl->CreateContext(m_display, m_config, contextAttrs, &m_context)) +@@ -199,6 +203,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res) + CLog::Log(LOGERROR, "%s: Could not create context",__FUNCTION__); + return false; + } ++#if HAS_GLES ++ newContext = true; ++#endif + } + + if (!m_egl->BindContext(m_display, m_surface, m_context)) +@@ -207,6 +214,11 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res) + return false; + } + ++#if HAS_GLES ++ if (newContext) ++ CGUIFontTTFGL::CreateStaticVertexBuffers(); ++#endif ++ + // for the non-trivial dirty region modes, we need the EGL buffer to be preserved across updates + if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_COST_REDUCTION || + g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_UNION) +@@ -228,7 +240,12 @@ bool CWinSystemEGL::DestroyWindowSystem() + DestroyWindow(); + + if (m_context != EGL_NO_CONTEXT) ++ { ++#if HAS_GLES ++ CGUIFontTTFGL::DestroyStaticVertexBuffers(); ++#endif + m_egl->DestroyContext(m_display, m_context); ++ } + m_context = EGL_NO_CONTEXT; + + if (m_display != EGL_NO_DISPLAY) +-- +1.9.3 + + +From ec39dce3628b276e3ed2fe19c95a056a1aa171b8 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 4 Feb 2014 16:17:57 +0000 +Subject: [PATCH 32/94] Update Windows project files + +--- + project/VS2010Express/XBMC.vcxproj | 2 ++ + project/VS2010Express/XBMC.vcxproj.filters | 6 ++++++ + 2 files changed, 8 insertions(+) + +diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj +index c6de5ca..a9731de 100644 +--- a/project/VS2010Express/XBMC.vcxproj ++++ b/project/VS2010Express/XBMC.vcxproj +@@ -540,6 +540,7 @@ + <ClCompile Include="..\..\xbmc\guilib\GUIFadeLabelControl.cpp" /> + <ClCompile Include="..\..\xbmc\guilib\GUIFixedListContainer.cpp" /> + <ClCompile Include="..\..\xbmc\guilib\GUIFont.cpp" /> ++ <ClCompile Include="..\..\xbmc\guilib\GUIFontCache.cpp" /> + <ClCompile Include="..\..\xbmc\guilib\GUIFontManager.cpp" /> + <ClCompile Include="..\..\xbmc\guilib\GUIFontTTF.cpp" /> + <ClCompile Include="..\..\xbmc\guilib\GUIFontTTFDX.cpp" /> +@@ -2057,6 +2058,7 @@ + <ClInclude Include="..\..\xbmc\guilib\GUIFadeLabelControl.h" /> + <ClInclude Include="..\..\xbmc\guilib\GUIFixedListContainer.h" /> + <ClInclude Include="..\..\xbmc\guilib\GUIFont.h" /> ++ <ClInclude Include="..\..\xbmc\guilib\GUIFontCache.h" /> + <ClInclude Include="..\..\xbmc\guilib\GUIFontManager.h" /> + <ClInclude Include="..\..\xbmc\guilib\GUIFontTTF.h" /> + <ClInclude Include="..\..\xbmc\guilib\GUIFontTTFDX.h" /> +diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters +index b536eb3..cb34443 100644 +--- a/project/VS2010Express/XBMC.vcxproj.filters ++++ b/project/VS2010Express/XBMC.vcxproj.filters +@@ -1024,6 +1024,9 @@ + <ClCompile Include="..\..\xbmc\guilib\GUIFont.cpp"> + <Filter>guilib</Filter> + </ClCompile> ++ <ClCompile Include="..\..\xbmc\guilib\GUIFontCache.cpp"> ++ <Filter>guilib</Filter> ++ </ClCompile> + <ClCompile Include="..\..\xbmc\guilib\GUIFontManager.cpp"> + <Filter>guilib</Filter> + </ClCompile> +@@ -3978,6 +3981,9 @@ + <ClInclude Include="..\..\xbmc\guilib\GUIFont.h"> + <Filter>guilib</Filter> + </ClInclude> ++ <ClInclude Include="..\..\xbmc\guilib\GUIFontCache.h"> ++ <Filter>guilib</Filter> ++ </ClInclude> + <ClInclude Include="..\..\xbmc\guilib\GUIFontManager.h"> + <Filter>guilib</Filter> + </ClInclude> +-- +1.9.3 + + +From e25eb385d09a5378be8616f10806610df90416db Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 4 Feb 2014 16:49:45 +0000 +Subject: [PATCH 33/94] Update XCode project file + +--- + XBMC.xcodeproj/project.pbxproj | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj +index fdd10a1..62e7e69 100644 +--- a/XBMC.xcodeproj/project.pbxproj ++++ b/XBMC.xcodeproj/project.pbxproj +@@ -168,6 +168,9 @@ + 1D638128161E211E003603ED /* PeripheralImon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D638126161E211E003603ED /* PeripheralImon.cpp */; }; + 1DAFDB7C16DFDCA7007F8C68 /* PeripheralBusCEC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DAFDB7A16DFDCA7007F8C68 /* PeripheralBusCEC.cpp */; }; + 1DE0443515828F4B005DDB4D /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; }; ++ 2FD7EC5F18A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; }; ++ 2FD7EC6018A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; }; ++ 2FD7EC6118A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; }; + 32C631281423A90F00F18420 /* JpegIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32C631261423A90F00F18420 /* JpegIO.cpp */; }; + 36A9443D15821E2800727135 /* DatabaseUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443B15821E2800727135 /* DatabaseUtils.cpp */; }; + 36A9444115821E7C00727135 /* SortUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443F15821E7C00727135 /* SortUtils.cpp */; }; +@@ -3546,6 +3549,8 @@ + 1DAFDB7B16DFDCA7007F8C68 /* PeripheralBusCEC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeripheralBusCEC.h; sourceTree = "<group>"; }; + 1DE0443315828F4B005DDB4D /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Exception.cpp; path = commons/Exception.cpp; sourceTree = "<group>"; }; + 1DE0443415828F4B005DDB4D /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = commons/Exception.h; sourceTree = "<group>"; }; ++ 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIFontCache.cpp; sourceTree = "<group>"; }; ++ 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIFontCache.h; sourceTree = "<group>"; }; + 32C631261423A90F00F18420 /* JpegIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JpegIO.cpp; sourceTree = "<group>"; }; + 32C631271423A90F00F18420 /* JpegIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JpegIO.h; sourceTree = "<group>"; }; + 36A9443B15821E2800727135 /* DatabaseUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseUtils.cpp; sourceTree = "<group>"; }; +@@ -5923,6 +5928,8 @@ + 18B7C76A1294222E009E7A26 /* GUIFixedListContainer.cpp */, + 18B7C7101294222D009E7A26 /* GUIFixedListContainer.h */, + 18B7C76B1294222E009E7A26 /* GUIFont.cpp */, ++ 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */, ++ 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */, + 18B7C7111294222D009E7A26 /* GUIFont.h */, + 18B7C76C1294222E009E7A26 /* GUIFontManager.cpp */, + 18B7C7121294222D009E7A26 /* GUIFontManager.h */, +@@ -10930,6 +10937,7 @@ + 7C8AE850189DE3CD00C33786 /* CoreAudioHardware.cpp in Sources */, + 7C8AE851189DE3CD00C33786 /* CoreAudioStream.cpp in Sources */, + 7C8AE854189DE47F00C33786 /* CoreAudioHelpers.cpp in Sources */, ++ 2FD7EC5F18A14FE50047F86C /* GUIFontCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +@@ -11978,6 +11986,7 @@ + F5CC234818150277006B5E91 /* AESinkNULL.cpp in Sources */, + F5CC238918150768006B5E91 /* AESinkProfiler.cpp in Sources */, + DF374B2518AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */, ++ 2FD7EC6118A14FE50047F86C /* GUIFontCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +@@ -13028,6 +13037,7 @@ + F5CC234718150277006B5E91 /* AESinkNULL.cpp in Sources */, + F5CC238818150768006B5E91 /* AESinkProfiler.cpp in Sources */, + DF374B2418AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */, ++ 2FD7EC6018A14FE50047F86C /* GUIFontCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +-- +1.9.3 + + +From 5f4ebd2e9fd6d503220627b916e522b671d7d9ba Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 4 Feb 2014 17:44:34 +0000 +Subject: [PATCH 34/94] Clang seems to be more picky than gcc about some C++ + template syntax + +--- + xbmc/guilib/GUIFontCache.cpp | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +index 895fa72..bd84b9a 100644 +--- a/xbmc/guilib/GUIFontCache.cpp ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -61,26 +61,26 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos, + alignment, maxPixelWidth, + scrolling, g_graphicsContext.GetGUIMatrix(), + g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY()); +- EntryHashIterator i = m_list.get<Hash>().find(key); +- if (i == m_list.get<Hash>().end()) ++ EntryHashIterator i = m_list.template get<Hash>().find(key); ++ if (i == m_list.template get<Hash>().end()) + { + /* Cache miss */ +- EntryAgeIterator oldest = m_list.get<Age>().begin(); +- if (!m_list.get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) ++ EntryAgeIterator oldest = m_list.template get<Age>().begin(); ++ if (!m_list.template get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) + { + /* The oldest existing entry is old enough to expire and reuse */ +- m_list.get<Hash>().modify(m_list.project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis)); +- m_list.get<Age>().relocate(m_list.get<Age>().end(), oldest); ++ m_list.template get<Hash>().modify(m_list.template project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis)); ++ m_list.template get<Age>().relocate(m_list.template get<Age>().end(), oldest); + } + else + { + /* We need a new entry instead */ + /* Yes, this causes the creation an destruction of a temporary entry, but + * this code ought to only be used infrequently, when the cache needs to grow */ +- m_list.get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis)); ++ m_list.template get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis)); + } + dirtyCache = true; +- return (--m_list.get<Age>().end())->m_value; ++ return (--m_list.template get<Age>().end())->m_value; + } + else + { +@@ -90,7 +90,7 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos, + pos.UpdateWithOffsets(i->m_key.m_pos, scrolling); + /* Update time in entry and move to the back of the list */ + i->m_lastUsedMillis = nowMillis; +- m_list.get<Age>().relocate(m_list.get<Age>().end(), m_list.project<Age>(i)); ++ m_list.template get<Age>().relocate(m_list.template get<Age>().end(), m_list.template project<Age>(i)); + dirtyCache = false; + return i->m_value; + } +@@ -99,7 +99,7 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos, + template<class Position, class Value> + void CGUIFontCache<Position, Value>::Flush() + { +- m_list.get<Age>().clear(); ++ m_list.template get<Age>().clear(); + } + + template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> &entry); +-- +1.9.3 + + +From 33693dc9ff9ba7695bc0e702a41566d54a5135b9 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 4 Feb 2014 18:52:14 +0000 +Subject: [PATCH 35/94] Fix header to hopefully permit iOS builds to work + again. GUIShader.cpp added #include windowing/egl/WinSystemEGL.h inside a but + also need the header windowing/osx/WinSystemIOS.h instead. The only thing + GUIShader.cpp needed was g_windowing.GetViewPort, which is provided by the + common base class CRenderSystemGLES of g_windowing in both cases, so I think + it should be sufficient to use windowing/WindowingFactory.h instead, which is + abstracted away from the other header files. + +--- + xbmc/guilib/GUIShader.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp +index 53bce09..86330cc 100644 +--- a/xbmc/guilib/GUIShader.cpp ++++ b/xbmc/guilib/GUIShader.cpp +@@ -26,7 +26,7 @@ + #include "GUIShader.h" + #include "MatrixGLES.h" + #include "utils/log.h" +-#include "windowing/egl/WinSystemEGL.h" ++#include "windowing/WindowingFactory.h" + #include "guilib/GraphicContext.h" + + CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_vert.glsl", shader) +-- +1.9.3 + + +From 9b95b3b13bd714d8320dc61c5039eee6cb3c5737 Mon Sep 17 00:00:00 2001 +From: Ben Avison <bavison@riscosopen.org> +Date: Tue, 8 Apr 2014 18:14:55 +0100 +Subject: [PATCH 36/94] Fix font display in stereoscopic modes + CGUIFontTTFGL::LastEnd was previously using the relatively high-level + CGraphicContext::SetScissors function to enforce hardware clipping. However, + the coordinates it passed in already contained the stereoscopic offset, so + the CGraphicContext::SetScissors effectively ended up double-applying the + offset, with the effect that clip rectangles were always off-screen. Changed + to call the low-level SetScissors call instead (using g_Windowing to select + the correct implementation, e.g. CRenderSystemGLES::SetScissors). This also + skips the intersection of the scissors with the screen limits, but that does + not appear to matter in practice. + +--- + xbmc/guilib/GUIFontTTFGL.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index d476409..8466a81 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -194,7 +194,7 @@ void CGUIFontTTFGL::LastEnd() + { + // Apply the clip rectangle + CRect clip = g_Windowing.ClipRectToScissorRect(m_vertexTrans[i].clip); +- g_graphicsContext.SetScissors(clip); ++ g_Windowing.SetScissors(clip); + + // Apply the translation to the currently active (top-of-stack) model view matrix + g_matrices.MatrixMode(MM_MODELVIEW); +@@ -224,7 +224,7 @@ void CGUIFontTTFGL::LastEnd() + g_matrices.PopMatrix(); + } + // Restore the original scissor rectangle +- g_graphicsContext.ResetScissors(); ++ g_Windowing.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER +-- +1.9.3 + + +From d51ef43b61b50de46edb2832f457af8229995cab Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Fri, 10 Jan 2014 12:10:43 +0000 +Subject: [PATCH 37/94] [rbp] Don't override dvdplayer with omxplayer. + +Using dvdplayer can be useful on the Pi. We can actually play sd (up to 640x480 MPEG-4 video) video in real time. +This is useful for codec variants like DivX3 which we don't currently play. + +This may expose bugs where dvdplayer is incorrectly used as the default player which will need to be fixed +--- + xbmc/cores/playercorefactory/PlayerCoreConfig.h | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h +index 27f0bec..fc12bb7 100644 +--- a/xbmc/cores/playercorefactory/PlayerCoreConfig.h ++++ b/xbmc/cores/playercorefactory/PlayerCoreConfig.h +@@ -88,14 +88,7 @@ friend class CPlayerCoreFactory; + { + case EPC_MPLAYER: + // TODO: this hack needs removal until we have a better player selection +-#if defined(HAS_OMXPLAYER) +- case EPC_DVDPLAYER: +- pPlayer = new COMXPlayer(callback); +- CLog::Log(LOGINFO, "Created player %s for core %d / OMXPlayer forced as DVDPlayer", "OMXPlayer", m_eCore); +- break; +-#else + case EPC_DVDPLAYER: pPlayer = new CDVDPlayer(callback); break; +-#endif + case EPC_PAPLAYER: pPlayer = new PAPlayer(callback); break; + case EPC_EXTPLAYER: pPlayer = new CExternalPlayer(callback); break; + #if defined(HAS_OMXPLAYER) +-- +1.9.3 + + +From 3cd01da5418f1693e50ed40273cb17ca9f6d622a Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Fri, 10 Jan 2014 15:37:41 +0000 +Subject: [PATCH 38/94] [players] Use default players rather than hard coded + DVDPlayer/PAPlayer + +--- + system/playercorefactory.xml | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml +index 57dfcdd..7be9799 100644 +--- a/system/playercorefactory.xml ++++ b/system/playercorefactory.xml +@@ -11,31 +11,32 @@ + </players> + + <rules name="system rules"> +- <rule name="rtv" protocols="rtv" player="DVDPlayer" /> +- <rule name="hdhomerun/myth/mms/udp" protocols="hdhomerun|myth|cmyth|mms|mmsh|udp" player="DVDPlayer" /> +- <rule name="lastfm/shout" protocols="lastfm|shout" player="PAPlayer" /> ++ <rule name="rtv" protocols="rtv" player="videodefaultplayer" /> ++ <rule name="hdhomerun/myth/mms/udp" protocols="hdhomerun|myth|cmyth|mms|mmsh|udp" player="videodefaultplayer" /> ++ <rule name="lastfm/shout" protocols="lastfm|shout" player="audiodefaultplayer" /> + <rule name="rtmp" protocols="rtmp" player="videodefaultplayer" /> + + <!-- dvdplayer can play standard rtsp streams --> +- <rule name="rtsp" protocols="rtsp" filetypes="!(rm|ra)" player="PAPlayer" /> ++ <rule name="rtsp" protocols="rtsp" filetypes="!(rm|ra)" player="audiodefaultplayer" /> + + <!-- Internet streams --> + <rule name="streams" internetstream="true"> +- <rule name="aacp/sdp" mimetypes="audio/aacp|application/sdp" player="DVDPlayer" /> +- <rule name="mp2" mimetypes="application/octet-stream" filetypes="mp2" player="PAPlayer" /> ++ <rule name="aacp/sdp" mimetypes="audio/aacp|application/sdp" player="videodefaultplayer" /> ++ <rule name="mp2" mimetypes="application/octet-stream" filetypes="mp2" player="audiodefaultplayer" /> + </rule> + + <!-- DVDs --> +- <rule name="dvd" dvd="true" player="DVDPlayer" /> +- <rule name="dvdimage" dvdimage="true" player="DVDPlayer" /> ++ <rule name="dvd" dvd="true" player="videodefaultdvdplayer" /> ++ <rule name="dvdfile" dvdfile="true" player="videodefaultdvdplayer" /> ++ <rule name="dvdimage" dvdimage="true" player="videodefaultdvdplayer" /> + + <!-- Only dvdplayer can handle these normally --> +- <rule name="sdp/asf" filetypes="sdp|asf" player="DVDPlayer" /> ++ <rule name="sdp/asf" filetypes="sdp|asf" player="videodefaultplayer" /> + + <!-- Pass these to dvdplayer as we do not know if they are audio or video --> +- <rule name="nsv" filetypes="nsv" player="DVDPlayer" /> ++ <rule name="nsv" filetypes="nsv" player="videodefaultplayer" /> + + <!-- pvr radio channels should be played by dvdplayer because they need buffering --> +- <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="DVDPlayer" /> ++ <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="videodefaultplayer" /> + </rules> + </playercorefactory> +-- +1.9.3 + + +From 5c4de293325bba93c4b820aed6863ee8d732ce2c Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Sat, 11 Jan 2014 18:23:42 +0000 +Subject: [PATCH 39/94] [rbp] Don't force dvdplayer for airplay + +--- + xbmc/network/AirPlayServer.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/xbmc/network/AirPlayServer.cpp b/xbmc/network/AirPlayServer.cpp +index 8040d9b..182daaa 100644 +--- a/xbmc/network/AirPlayServer.cpp ++++ b/xbmc/network/AirPlayServer.cpp +@@ -906,9 +906,11 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader, + CFileItem fileToPlay(location, false); + fileToPlay.SetProperty("StartPercent", position*100.0f); + ServerInstance->AnnounceToClients(EVENT_LOADING); ++#ifndef TARGET_RASPBERRY_PI + // froce to internal dvdplayer cause it is the only + // one who will work well with airplay + g_application.m_eForcedNextPlayer = EPC_DVDPLAYER; ++#endif + CApplicationMessenger::Get().MediaPlay(fileToPlay); + } + } +-- +1.9.3 + + +From 454b77543018aac5d7e70769ef13231ae16eefb9 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Mon, 13 Jan 2014 13:11:06 +0000 +Subject: [PATCH 40/94] [rbp] Give plugins omxplayer when they request + dvdplayer on pi + +--- + xbmc/interfaces/legacy/ModuleXbmc.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/xbmc/interfaces/legacy/ModuleXbmc.cpp b/xbmc/interfaces/legacy/ModuleXbmc.cpp +index 16f0174..b172d47 100644 +--- a/xbmc/interfaces/legacy/ModuleXbmc.cpp ++++ b/xbmc/interfaces/legacy/ModuleXbmc.cpp +@@ -536,7 +536,11 @@ namespace XBMCAddon + int getPLAYLIST_MUSIC() { return PLAYLIST_MUSIC; } + int getPLAYLIST_VIDEO() { return PLAYLIST_VIDEO; } + int getPLAYER_CORE_AUTO() { return EPC_NONE; } ++#ifdef TARGET_RASPBERRY_PI ++ int getPLAYER_CORE_DVDPLAYER() { return EPC_OMXPLAYER; } ++#else + int getPLAYER_CORE_DVDPLAYER() { return EPC_DVDPLAYER; } ++#endif + int getPLAYER_CORE_MPLAYER() { return EPC_MPLAYER; } + int getPLAYER_CORE_PAPLAYER() { return EPC_PAPLAYER; } + int getTRAY_OPEN() { return TRAY_OPEN; } +-- +1.9.3 + + +From 7201ee3e0ca664518eaaf3142682b294c34c69c0 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 14 Jan 2014 18:04:07 +0000 +Subject: [PATCH 41/94] [rbp] Allow ALSA to be chosen in addition to Pi sink + +Needs --enable-alsa in ./configure step and alsa support on platform +--- + configure.in | 1 - + tools/depends/target/Makefile | 6 +++--- + xbmc/cores/AudioEngine/AESinkFactory.cpp | 17 +++++++++++++++-- + 3 files changed, 18 insertions(+), 6 deletions(-) + +diff --git a/configure.in b/configure.in +index a195d00..34dd038 100644 +--- a/configure.in ++++ b/configure.in +@@ -742,7 +742,6 @@ case $use_platform in + use_arch="arm" + use_cpu=arm1176jzf-s + use_hardcoded_tables="yes" +- use_alsa="no" + ARCH="arm" + AC_DEFINE(HAS_EGLGLES, [1], [Define if supporting EGL based GLES Framebuffer]) + USE_OMXLIB=1; AC_DEFINE([HAVE_OMXLIB],[1],["Define to 1 if OMX libs is enabled"]) +diff --git a/tools/depends/target/Makefile b/tools/depends/target/Makefile +index 4588917..0fbd3e7 100644 +--- a/tools/depends/target/Makefile ++++ b/tools/depends/target/Makefile +@@ -55,10 +55,10 @@ endif + ALSA_LIB= + LINUX_SYSTEM_LIBS= + ifeq ($(OS),linux) +- #not for raspberry pi ++ DEPENDS += alsa-lib ++ ALSA_LIB = alsa-lib + ifneq ($(CPU),arm) +- DEPENDS += alsa-lib libsdl linux-system-libs +- ALSA_LIB = alsa-lib ++ DEPENDS += libsdl linux-system-libs + LINUX_SYSTEM_LIBS = linux-system-libs + endif + endif +diff --git a/xbmc/cores/AudioEngine/AESinkFactory.cpp b/xbmc/cores/AudioEngine/AESinkFactory.cpp +index e493123..7df6807 100644 +--- a/xbmc/cores/AudioEngine/AESinkFactory.cpp ++++ b/xbmc/cores/AudioEngine/AESinkFactory.cpp +@@ -27,6 +27,7 @@ + #include "Sinks/AESinkAUDIOTRACK.h" + #elif defined(TARGET_RASPBERRY_PI) + #include "Sinks/AESinkPi.h" ++ #include "Sinks/AESinkALSA.h" + #elif defined(TARGET_DARWIN_IOS) + #include "Sinks/AESinkDARWINIOS.h" + #elif defined(TARGET_DARWIN_OSX) +@@ -66,6 +67,7 @@ void CAESinkFactory::ParseDevice(std::string &device, std::string &driver) + driver == "AUDIOTRACK" || + #elif defined(TARGET_RASPBERRY_PI) + driver == "PI" || ++ driver == "ALSA" || + #elif defined(TARGET_DARWIN_IOS) + driver == "DARWINIOS" || + #elif defined(TARGET_DARWIN_OSX) +@@ -104,7 +106,12 @@ IAESink *CAESinkFactory::TrySink(std::string &driver, std::string &device, AEAud + #elif defined(TARGET_ANDROID) + sink = new CAESinkAUDIOTRACK(); + #elif defined(TARGET_RASPBERRY_PI) +- sink = new CAESinkPi(); ++ else if (driver == "PI") ++ sink = new CAESinkPi(); ++ #if defined(HAS_ALSA) ++ else if (driver == "ALSA") ++ sink = new CAESinkALSA(); ++ #endif + #elif defined(TARGET_DARWIN_IOS) + sink = new CAESinkDARWINIOS(); + #elif defined(TARGET_DARWIN_OSX) +@@ -194,7 +201,13 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force) + CAESinkPi::EnumerateDevicesEx(info.m_deviceInfoList, force); + if(!info.m_deviceInfoList.empty()) + list.push_back(info); +- ++ #if defined(HAS_ALSA) ++ info.m_deviceInfoList.clear(); ++ info.m_sinkName = "ALSA"; ++ CAESinkALSA::EnumerateDevicesEx(info.m_deviceInfoList, force); ++ if(!info.m_deviceInfoList.empty()) ++ list.push_back(info); ++ #endif + #elif defined(TARGET_DARWIN_IOS) + + info.m_deviceInfoList.clear(); +-- +1.9.3 + + +From 8ffa85ccdc4760751849d75d37924fbf6cb1b1c8 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Thu, 16 Jan 2014 01:39:29 +0000 +Subject: [PATCH 42/94] [omxcodec] Add hardware decode to dvdplayer for Pi + +Hijack the abandoned OpenMaxVideo codec +--- + configure.in | 21 +- + xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 12 +- + xbmc/cores/VideoRenderers/LinuxRendererGLES.h | 6 +- + xbmc/cores/VideoRenderers/RenderManager.cpp | 2 +- + xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp | 7 +- + .../dvdplayer/DVDCodecs/Video/DVDVideoCodec.h | 7 +- + .../DVDCodecs/Video/DVDVideoCodecOpenMax.cpp | 295 +--- + .../DVDCodecs/Video/DVDVideoCodecOpenMax.h | 34 +- + xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in | 1 - + xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp | 269 ---- + xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h | 116 -- + .../dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 1418 ++++++++++---------- + .../cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 120 +- + xbmc/cores/dvdplayer/DVDPlayer.cpp | 2 + + xbmc/cores/dvdplayer/DVDPlayerVideo.cpp | 21 +- + xbmc/linux/OMXCore.cpp | 45 +- + xbmc/linux/OMXCore.h | 2 +- + 17 files changed, 894 insertions(+), 1484 deletions(-) + delete mode 100644 xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp + delete mode 100644 xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h + +diff --git a/configure.in b/configure.in +index 34dd038..e7550c0 100644 +--- a/configure.in ++++ b/configure.in +@@ -1956,9 +1956,24 @@ if test "$host_vendor" = "apple" ; then + USE_OPENMAX=0 + AC_MSG_NOTICE($openmax_disabled) + elif test "$target_platform" = "target_raspberry_pi"; then +- use_openmax="no" +- USE_OPENMAX=0 +- AC_MSG_NOTICE($openmax_disabled) ++ if test "$use_gles" = "yes" && test "$use_openmax" = "auto"; then ++ use_openmax="yes" ++ USE_OPENMAX=1 ++ HAVE_LIBOPENMAX=1 ++ AC_DEFINE([HAVE_LIBOPENMAX], [1], [Define to 1 if you have the 'LIBOPENMAX' library.]) ++ AC_DEFINE([OMX_SKIP64BIT], [1], [Define to 1 if you have the 'LIBOPENMAX' library.]) ++ AC_MSG_NOTICE($openmax_enabled) ++ elif test "$use_gles" = "yes" && test "$use_openmax" = "yes"; then ++ use_openmax="yes" ++ USE_OPENMAX=1 ++ HAVE_LIBOPENMAX=1 ++ AC_DEFINE([HAVE_LIBOPENMAX], [1], [Define to 1 if you have the 'LIBOPENMAX' library.]) ++ AC_MSG_NOTICE($openmax_enabled) ++ else ++ AC_MSG_NOTICE($openmax_disabled) ++ use_openmax=no ++ USE_OPENMAX=0 ++ fi + else + if test "$use_gles" = "yes" && test "$use_openmax" = "auto"; then + PKG_CHECK_MODULES([OPENMAX], [libomxil-bellagio], +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +index 30c0601..6d879e3 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +@@ -44,7 +44,7 @@ + #include "windowing/WindowingFactory.h" + #include "guilib/Texture.h" + #include "lib/DllSwScale.h" +-#include "../dvdplayer/DVDCodecs/Video/OpenMaxVideo.h" ++#include "DVDCodecs/Video/OpenMaxVideo.h" + #include "threads/SingleLock.h" + #include "RenderCapture.h" + #include "RenderFormats.h" +@@ -1330,6 +1330,10 @@ void CLinuxRendererGLES::RenderOpenMax(int index, int field) + glActiveTexture(GL_TEXTURE0); + glBindTexture(m_textureTarget, textureId); + ++ GLint filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR; ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter); ++ + g_Windowing.EnableGUIShader(SM_TEXTURE_RGBA); + + GLubyte idx[4] = {0, 1, 3, 2}; //determines order of triangle strip +@@ -2676,10 +2680,12 @@ unsigned int CLinuxRendererGLES::GetProcessorSize() + } + + #ifdef HAVE_LIBOPENMAX +-void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index) ++void CLinuxRendererGLES::AddProcessor(COpenMaxVideoBuffer *openMaxBuffer, int index) + { + YUVBUFFER &buf = m_buffers[index]; +- buf.openMaxBuffer = picture->openMaxBuffer; ++ COpenMaxVideoBuffer *pic = openMaxBuffer->Acquire(); ++ SAFE_RELEASE(buf.openMaxBuffer); ++ buf.openMaxBuffer = pic; + } + #endif + #ifdef HAVE_VIDEOTOOLBOXDECODER +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h +index 45e9c20..0ca56a2 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h +@@ -39,7 +39,7 @@ class CRenderCapture; + class CBaseTexture; + namespace Shaders { class BaseYUV2RGBShader; } + namespace Shaders { class BaseVideoFilterShader; } +-class COpenMaxVideo; ++class COpenMaxVideoBuffer; + class CDVDVideoCodecStageFright; + class CDVDMediaCodecInfo; + typedef std::vector<int> Features; +@@ -161,7 +161,7 @@ class CLinuxRendererGLES : public CBaseRenderer + virtual std::vector<ERenderFormat> SupportedFormats() { return m_formats; } + + #ifdef HAVE_LIBOPENMAX +- virtual void AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index); ++ virtual void AddProcessor(COpenMaxVideoBuffer *openMaxVideoBuffer, int index); + #endif + #ifdef HAVE_VIDEOTOOLBOXDECODER + virtual void AddProcessor(struct __CVBuffer *cvBufferRef, int index); +@@ -275,7 +275,7 @@ class CLinuxRendererGLES : public CBaseRenderer + unsigned flipindex; /* used to decide if this has been uploaded */ + + #ifdef HAVE_LIBOPENMAX +- OpenMaxVideoBuffer *openMaxBuffer; ++ COpenMaxVideoBuffer *openMaxBuffer; + #endif + #ifdef HAVE_VIDEOTOOLBOXDECODER + struct __CVBuffer *cvBufferRef; +diff --git a/xbmc/cores/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoRenderers/RenderManager.cpp +index 6832721..3503988 100644 +--- a/xbmc/cores/VideoRenderers/RenderManager.cpp ++++ b/xbmc/cores/VideoRenderers/RenderManager.cpp +@@ -912,7 +912,7 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic) + #endif + #ifdef HAVE_LIBOPENMAX + else if(pic.format == RENDER_FMT_OMXEGL) +- m_pRenderer->AddProcessor(pic.openMax, &pic, index); ++ m_pRenderer->AddProcessor(pic.openMaxBuffer, index); + #endif + #ifdef TARGET_DARWIN + else if(pic.format == RENDER_FMT_CVBREF) +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp +index 14ad038..18b8e3a 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp +@@ -270,9 +270,12 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne + #endif + + #if defined(HAVE_LIBOPENMAX) +- if (CSettings::Get().GetBool("videoplayer.useomx") && !hint.software ) ++ if (!hint.software && CSettings::Get().GetBool("videoplayer.useomx")) + { +- if (hint.codec == AV_CODEC_ID_H264 || hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_VC1) ++ if (hint.codec == AV_CODEC_ID_H264 || hint.codec == AV_CODEC_ID_H263 || hint.codec == AV_CODEC_ID_MPEG4 || ++ hint.codec == AV_CODEC_ID_MPEG1VIDEO || hint.codec == AV_CODEC_ID_MPEG2VIDEO || ++ hint.codec == AV_CODEC_ID_VP6 || hint.codec == AV_CODEC_ID_VP6F || hint.codec == AV_CODEC_ID_VP6A || hint.codec == AV_CODEC_ID_VP8 || ++ hint.codec == AV_CODEC_ID_THEORA || hint.codec == AV_CODEC_ID_MJPEG || hint.codec == AV_CODEC_ID_MJPEGB || hint.codec == AV_CODEC_ID_VC1 || hint.codec == AV_CODEC_ID_WMV3) + { + if ( (pCodec = OpenCodec(new CDVDVideoCodecOpenMax(), hint, options)) ) return pCodec; + } +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h +index f6751f4..dc047d7 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h +@@ -44,9 +44,7 @@ struct DVDCodecAvailableType + namespace DXVA { class CSurfaceContext; } + namespace VAAPI { struct CHolder; } + namespace VDPAU { class CVdpauRenderPicture; } +-class COpenMax; +-class COpenMaxVideo; +-struct OpenMaxVideoBuffer; ++class COpenMaxVideoBuffer; + class CDVDVideoCodecStageFright; + class CDVDMediaCodecInfo; + typedef void* EGLImageKHR; +@@ -75,8 +73,7 @@ struct DVDVideoPicture + }; + + struct { +- COpenMax *openMax; +- OpenMaxVideoBuffer *openMaxBuffer; ++ COpenMaxVideoBuffer *openMaxBuffer; + }; + + struct { +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp +index b2e7816..7d33192 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp +@@ -29,113 +29,43 @@ + #include "DVDStreamInfo.h" + #include "DVDVideoCodecOpenMax.h" + #include "OpenMaxVideo.h" ++#include "settings/Settings.h" + #include "utils/log.h" + +-#define CLASSNAME "COpenMax" ++#define CLASSNAME "CDVDVideoCodecOpenMax" + //////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////// +-CDVDVideoCodecOpenMax::CDVDVideoCodecOpenMax() : CDVDVideoCodec() ++CDVDVideoCodecOpenMax::CDVDVideoCodecOpenMax() + { + m_omx_decoder = NULL; +- m_pFormatName = "omx-xxxx"; +- +- m_convert_bitstream = false; +- memset(&m_videobuffer, 0, sizeof(DVDVideoPicture)); ++ CLog::Log(LOGDEBUG, "%s::%s %p\n", CLASSNAME, __func__, this); + } + + CDVDVideoCodecOpenMax::~CDVDVideoCodecOpenMax() + { ++ CLog::Log(LOGDEBUG, "%s::%s %p\n", CLASSNAME, __func__, this); + Dispose(); + } + + bool CDVDVideoCodecOpenMax::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) + { +- // we always qualify even if DVDFactoryCodec does this too. +- if (CSettings::Get().GetBool("videoplayer.useomx") && !hints.software) +- { +- m_convert_bitstream = false; +- +- switch (hints.codec) +- { +- case AV_CODEC_ID_H264: +- { +- m_pFormatName = "omx-h264"; +- if (hints.extrasize < 7 || hints.extradata == NULL) +- { +- CLog::Log(LOGNOTICE, +- "%s::%s - avcC data too small or missing", CLASSNAME, __func__); +- return false; +- } +- // valid avcC data (bitstream) always starts with the value 1 (version) +- if ( *(char*)hints.extradata == 1 ) +- m_convert_bitstream = bitstream_convert_init(hints.extradata, hints.extrasize); +- } +- break; +- case AV_CODEC_ID_MPEG4: +- m_pFormatName = "omx-mpeg4"; +- break; +- case AV_CODEC_ID_MPEG2VIDEO: +- m_pFormatName = "omx-mpeg2"; +- break; +- case AV_CODEC_ID_VC1: +- m_pFormatName = "omx-vc1"; +- break; +- default: +- return false; +- break; +- } +- +- m_omx_decoder = new COpenMaxVideo; +- if (!m_omx_decoder->Open(hints)) +- { +- CLog::Log(LOGERROR, +- "%s::%s - failed to open, codec(%d), profile(%d), level(%d)", +- CLASSNAME, __func__, hints.codec, hints.profile, hints.level); +- return false; +- } +- +- // allocate a YV12 DVDVideoPicture buffer. +- // first make sure all properties are reset. +- memset(&m_videobuffer, 0, sizeof(DVDVideoPicture)); +- +- m_videobuffer.dts = DVD_NOPTS_VALUE; +- m_videobuffer.pts = DVD_NOPTS_VALUE; +- //m_videobuffer.format = RENDER_FMT_YUV420P; +- m_videobuffer.format = RENDER_FMT_OMXEGL; +- m_videobuffer.color_range = 0; +- m_videobuffer.color_matrix = 4; +- m_videobuffer.iFlags = DVP_FLAG_ALLOCATED; +- m_videobuffer.iWidth = hints.width; +- m_videobuffer.iHeight = hints.height; +- m_videobuffer.iDisplayWidth = hints.width; +- m_videobuffer.iDisplayHeight = hints.height; +- +- return true; +- } ++ m_omx_decoder = new COpenMaxVideo; ++ return m_omx_decoder->Open(hints, options); ++} + +- return false; ++const char* CDVDVideoCodecOpenMax::GetName(void) ++{ ++ return m_omx_decoder ? m_omx_decoder->GetName() : "omx-xxx"; + } + + void CDVDVideoCodecOpenMax::Dispose() + { + if (m_omx_decoder) + { +- m_omx_decoder->Close(); ++ m_omx_decoder->Dispose(); + delete m_omx_decoder; + m_omx_decoder = NULL; + } +- if (m_videobuffer.iFlags & DVP_FLAG_ALLOCATED) +- { +- m_videobuffer.iFlags = 0; +- } +- if (m_convert_bitstream) +- { +- if (m_sps_pps_context.sps_pps_data) +- { +- free(m_sps_pps_context.sps_pps_data); +- m_sps_pps_context.sps_pps_data = NULL; +- } +- } + } + + void CDVDVideoCodecOpenMax::SetDropState(bool bDrop) +@@ -145,37 +75,12 @@ void CDVDVideoCodecOpenMax::SetDropState(bool bDrop) + + int CDVDVideoCodecOpenMax::Decode(uint8_t* pData, int iSize, double dts, double pts) + { +- if (pData) +- { +- int rtn; +- int demuxer_bytes = iSize; +- uint8_t *demuxer_content = pData; +- bool bitstream_convered = false; +- +- if (m_convert_bitstream) +- { +- // convert demuxer packet from bitstream to bytestream (AnnexB) +- int bytestream_size = 0; +- uint8_t *bytestream_buff = NULL; +- +- bitstream_convert(demuxer_content, demuxer_bytes, &bytestream_buff, &bytestream_size); +- if (bytestream_buff && (bytestream_size > 0)) +- { +- bitstream_convered = true; +- demuxer_bytes = bytestream_size; +- demuxer_content = bytestream_buff; +- } +- } +- +- rtn = m_omx_decoder->Decode(demuxer_content, demuxer_bytes, dts, pts); +- +- if (bitstream_convered) +- free(demuxer_content); ++ return m_omx_decoder->Decode(pData, iSize, dts, pts); ++} + +- return rtn; +- } +- +- return VC_BUFFER; ++unsigned CDVDVideoCodecOpenMax::GetAllowedReferences() ++{ ++ return m_omx_decoder->GetAllowedReferences(); + } + + void CDVDVideoCodecOpenMax::Reset(void) +@@ -185,172 +90,12 @@ void CDVDVideoCodecOpenMax::Reset(void) + + bool CDVDVideoCodecOpenMax::GetPicture(DVDVideoPicture* pDvdVideoPicture) + { +- m_omx_decoder->GetPicture(&m_videobuffer); +- *pDvdVideoPicture = m_videobuffer; +- +- return VC_PICTURE | VC_BUFFER; +-} +- +-//////////////////////////////////////////////////////////////////////////////////////////// +-bool CDVDVideoCodecOpenMax::bitstream_convert_init(void *in_extradata, int in_extrasize) +-{ +- // based on h264_mp4toannexb_bsf.c (ffmpeg) +- // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr> +- // and Licensed GPL 2.1 or greater +- +- m_sps_pps_size = 0; +- m_sps_pps_context.sps_pps_data = NULL; +- +- // nothing to filter +- if (!in_extradata || in_extrasize < 6) +- return false; +- +- uint16_t unit_size; +- uint32_t total_size = 0; +- uint8_t *out = NULL, unit_nb, sps_done = 0; +- const uint8_t *extradata = (uint8_t*)in_extradata + 4; +- static const uint8_t nalu_header[4] = {0, 0, 0, 1}; +- +- // retrieve length coded size +- m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1; +- if (m_sps_pps_context.length_size == 3) +- return false; +- +- // retrieve sps and pps unit(s) +- unit_nb = *extradata++ & 0x1f; // number of sps unit(s) +- if (!unit_nb) +- { +- unit_nb = *extradata++; // number of pps unit(s) +- sps_done++; +- } +- while (unit_nb--) +- { +- unit_size = extradata[0] << 8 | extradata[1]; +- total_size += unit_size + 4; +- if ( (extradata + 2 + unit_size) > ((uint8_t*)in_extradata + in_extrasize) ) +- { +- free(out); +- return false; +- } +- uint8_t* new_out = (uint8_t*)realloc(out, total_size); +- if (new_out) +- { +- out = new_out; +- } +- else +- { +- CLog::Log(LOGERROR, "bitstream_convert_init failed - %s : could not realloc the buffer out", __FUNCTION__); +- free(out); +- return false; +- } +- +- memcpy(out + total_size - unit_size - 4, nalu_header, 4); +- memcpy(out + total_size - unit_size, extradata + 2, unit_size); +- extradata += 2 + unit_size; +- +- if (!unit_nb && !sps_done++) +- unit_nb = *extradata++; // number of pps unit(s) +- } +- +- m_sps_pps_context.sps_pps_data = out; +- m_sps_pps_context.size = total_size; +- m_sps_pps_context.first_idr = 1; +- +- return true; +-} +- +-bool CDVDVideoCodecOpenMax::bitstream_convert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size) +-{ +- // based on h264_mp4toannexb_bsf.c (ffmpeg) +- // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr> +- // and Licensed GPL 2.1 or greater +- +- uint8_t *buf = pData; +- uint32_t buf_size = iSize; +- uint8_t unit_type; +- int32_t nal_size; +- uint32_t cumul_size = 0; +- const uint8_t *buf_end = buf + buf_size; +- +- do +- { +- if (buf + m_sps_pps_context.length_size > buf_end) +- goto fail; +- +- if (m_sps_pps_context.length_size == 1) +- nal_size = buf[0]; +- else if (m_sps_pps_context.length_size == 2) +- nal_size = buf[0] << 8 | buf[1]; +- else +- nal_size = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; +- +- buf += m_sps_pps_context.length_size; +- unit_type = *buf & 0x1f; +- +- if (buf + nal_size > buf_end || nal_size < 0) +- goto fail; +- +- // prepend only to the first type 5 NAL unit of an IDR picture +- if (m_sps_pps_context.first_idr && unit_type == 5) +- { +- bitstream_alloc_and_copy(poutbuf, poutbuf_size, +- m_sps_pps_context.sps_pps_data, m_sps_pps_context.size, buf, nal_size); +- m_sps_pps_context.first_idr = 0; +- } +- else +- { +- bitstream_alloc_and_copy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size); +- if (!m_sps_pps_context.first_idr && unit_type == 1) +- m_sps_pps_context.first_idr = 1; +- } +- +- buf += nal_size; +- cumul_size += nal_size + m_sps_pps_context.length_size; +- } while (cumul_size < buf_size); +- +- return true; +- +-fail: +- free(*poutbuf); +- *poutbuf = NULL; +- *poutbuf_size = 0; +- return false; ++ return m_omx_decoder->GetPicture(pDvdVideoPicture); + } + +-void CDVDVideoCodecOpenMax::bitstream_alloc_and_copy( +- uint8_t **poutbuf, int *poutbuf_size, +- const uint8_t *sps_pps, uint32_t sps_pps_size, +- const uint8_t *in, uint32_t in_size) ++bool CDVDVideoCodecOpenMax::ClearPicture(DVDVideoPicture* pDvdVideoPicture) + { +- // based on h264_mp4toannexb_bsf.c (ffmpeg) +- // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr> +- // and Licensed GPL 2.1 or greater +- +- #define CHD_WB32(p, d) { \ +- ((uint8_t*)(p))[3] = (d); \ +- ((uint8_t*)(p))[2] = (d) >> 8; \ +- ((uint8_t*)(p))[1] = (d) >> 16; \ +- ((uint8_t*)(p))[0] = (d) >> 24; } +- +- uint32_t offset = *poutbuf_size; +- uint8_t nal_header_size = offset ? 3 : 4; +- +- *poutbuf_size += sps_pps_size + in_size + nal_header_size; +- *poutbuf = (uint8_t*)realloc(*poutbuf, *poutbuf_size); +- if (sps_pps) +- memcpy(*poutbuf + offset, sps_pps, sps_pps_size); +- +- memcpy(*poutbuf + sps_pps_size + nal_header_size + offset, in, in_size); +- if (!offset) +- { +- CHD_WB32(*poutbuf + sps_pps_size, 1); +- } +- else +- { +- (*poutbuf + offset + sps_pps_size)[0] = 0; +- (*poutbuf + offset + sps_pps_size)[1] = 0; +- (*poutbuf + offset + sps_pps_size)[2] = 1; +- } ++ return m_omx_decoder->ClearPicture(pDvdVideoPicture); + } + + #endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h +index fb80d02..67cc235 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h +@@ -23,7 +23,7 @@ + + #include "DVDVideoCodec.h" + +-class COpenVideoMax; ++class COpenMaxVideo; + class CDVDVideoCodecOpenMax : public CDVDVideoCodec + { + public: +@@ -36,39 +36,13 @@ class CDVDVideoCodecOpenMax : public CDVDVideoCodec + virtual int Decode(uint8_t *pData, int iSize, double dts, double pts); + virtual void Reset(void); + virtual bool GetPicture(DVDVideoPicture *pDvdVideoPicture); ++ virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture); ++ virtual unsigned GetAllowedReferences(); + virtual void SetDropState(bool bDrop); +- virtual const char* GetName(void) { return (const char*)m_pFormatName; } ++ virtual const char* GetName(void); + + protected: +- const char *m_pFormatName; + COpenMaxVideo *m_omx_decoder; +- DVDVideoPicture m_videobuffer; +- +- // bitstream to bytestream (Annex B) conversion support. +- bool bitstream_convert_init(void *in_extradata, int in_extrasize); +- bool bitstream_convert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size); +- static void bitstream_alloc_and_copy( uint8_t **poutbuf, int *poutbuf_size, +- const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size); +- +- typedef struct omx_bitstream_ctx { +- uint8_t length_size; +- uint8_t first_idr; +- uint8_t *sps_pps_data; +- uint32_t size; +- +- omx_bitstream_ctx() +- { +- length_size = 0; +- first_idr = 0; +- sps_pps_data = NULL; +- size = 0; +- } +- +- } omx_bitstream_ctx; +- +- uint32_t m_sps_pps_size; +- omx_bitstream_ctx m_sps_pps_context; +- bool m_convert_bitstream; + }; + + #endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in +index 8a97889..ebf7123 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in +@@ -20,7 +20,6 @@ SRCS += DVDVideoCodecVDA.cpp + SRCS += VDA.cpp + endif + ifeq (@USE_OPENMAX@,1) +-SRCS += OpenMax.cpp + SRCS += OpenMaxVideo.cpp + SRCS += DVDVideoCodecOpenMax.cpp + endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp +deleted file mode 100644 +index 7b0a0c2ef..0000000 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp ++++ /dev/null +@@ -1,269 +0,0 @@ +-/* +- * Copyright (C) 2010-2013 Team XBMC +- * http://xbmc.org +- * +- * This Program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2, or (at your option) +- * any later version. +- * +- * This Program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with XBMC; see the file COPYING. If not, see +- * <http://www.gnu.org/licenses/>. +- * +- */ +- +-#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS) +- #include "config.h" +-#elif defined(TARGET_WINDOWS) +-#include "system.h" +-#endif +- +-#if defined(HAVE_LIBOPENMAX) +-#include "OpenMax.h" +-#include "DynamicDll.h" +-#include "DVDClock.h" +-#include "DVDStreamInfo.h" +-#include "windowing/WindowingFactory.h" +-#include "DVDVideoCodec.h" +-#include "utils/log.h" +-#include "utils/TimeUtils.h" +-#include "ApplicationMessenger.h" +-#include "Application.h" +- +-#include <OMX_Core.h> +-#include <OMX_Component.h> +-#include <OMX_Index.h> +-#include <OMX_Image.h> +- +-#define CLASSNAME "COpenMax" +- +- +-//////////////////////////////////////////////////////////////////////////////////////////// +-class DllLibOpenMaxInterface +-{ +-public: +- virtual ~DllLibOpenMaxInterface() {} +- +- virtual OMX_ERRORTYPE OMX_Init(void) = 0; +- virtual OMX_ERRORTYPE OMX_Deinit(void) = 0; +- virtual OMX_ERRORTYPE OMX_GetHandle( +- OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) = 0; +- virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) = 0; +- virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) = 0; +- virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) = 0; +- virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) = 0; +-}; +- +-class DllLibOpenMax : public DllDynamic, DllLibOpenMaxInterface +-{ +- DECLARE_DLL_WRAPPER(DllLibOpenMax, "/usr/lib/libnvomx.so") +- +- DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Init) +- DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Deinit) +- DEFINE_METHOD4(OMX_ERRORTYPE, OMX_GetHandle, (OMX_HANDLETYPE *p1, OMX_STRING p2, OMX_PTR p3, OMX_CALLBACKTYPE *p4)) +- DEFINE_METHOD1(OMX_ERRORTYPE, OMX_FreeHandle, (OMX_HANDLETYPE p1)) +- DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetComponentsOfRole, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) +- DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetRolesOfComponent, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) +- DEFINE_METHOD3(OMX_ERRORTYPE, OMX_ComponentNameEnum, (OMX_STRING p1, OMX_U32 p2, OMX_U32 p3)) +- BEGIN_METHOD_RESOLVE() +- RESOLVE_METHOD(OMX_Init) +- RESOLVE_METHOD(OMX_Deinit) +- RESOLVE_METHOD(OMX_GetHandle) +- RESOLVE_METHOD(OMX_FreeHandle) +- RESOLVE_METHOD(OMX_GetComponentsOfRole) +- RESOLVE_METHOD(OMX_GetRolesOfComponent) +- RESOLVE_METHOD(OMX_ComponentNameEnum) +- END_METHOD_RESOLVE() +-}; +- +-//////////////////////////////////////////////////////////////////////////////////////////// +-#define OMX_INIT_STRUCTURE(a) \ +- memset(&(a), 0, sizeof(a)); \ +- (a).nSize = sizeof(a); \ +- (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ +- (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ +- (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ +- (a).nVersion.s.nStep = OMX_VERSION_STEP +- +-//////////////////////////////////////////////////////////////////////////////////////////// +-//////////////////////////////////////////////////////////////////////////////////////////// +-COpenMax::COpenMax() +-{ +- #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__); +- #endif +- m_dll = new DllLibOpenMax; +- m_dll->Load(); +- m_is_open = false; +- +- m_omx_decoder = NULL; +- m_omx_client_state = DEAD; +- m_omx_decoder_state = 0; +- sem_init(m_omx_decoder_state_change, 0, 0); +- /* +- m_omx_flush_input = (sem_t*)malloc(sizeof(sem_t)); +- sem_init(m_omx_flush_input, 0, 0); +- m_omx_flush_output = (sem_t*)malloc(sizeof(sem_t)); +- sem_init(m_omx_flush_output, 0, 0); +- */ +-} +- +-COpenMax::~COpenMax() +-{ +- #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__); +- #endif +- /* +- sem_destroy(m_omx_flush_input); +- free(m_omx_flush_input); +- sem_destroy(m_omx_flush_output); +- free(m_omx_flush_output); +- */ +- delete m_dll; +-} +- +- +- +- +-//////////////////////////////////////////////////////////////////////////////////////////// +-// DecoderEventHandler -- OMX event callback +-OMX_ERRORTYPE COpenMax::DecoderEventHandlerCallback( +- OMX_HANDLETYPE hComponent, +- OMX_PTR pAppData, +- OMX_EVENTTYPE eEvent, +- OMX_U32 nData1, +- OMX_U32 nData2, +- OMX_PTR pEventData) +-{ +- COpenMax *ctx = (COpenMax*)pAppData; +- return ctx->DecoderEventHandler(hComponent, pAppData, eEvent, nData1, nData2, pEventData); +-} +- +-// DecoderEmptyBufferDone -- OpenMax input buffer has been emptied +-OMX_ERRORTYPE COpenMax::DecoderEmptyBufferDoneCallback( +- OMX_HANDLETYPE hComponent, +- OMX_PTR pAppData, +- OMX_BUFFERHEADERTYPE* pBuffer) +-{ +- COpenMax *ctx = (COpenMax*)pAppData; +- return ctx->DecoderEmptyBufferDone( hComponent, pAppData, pBuffer); +-} +- +-// DecoderFillBufferDone -- OpenMax output buffer has been filled +-OMX_ERRORTYPE COpenMax::DecoderFillBufferDoneCallback( +- OMX_HANDLETYPE hComponent, +- OMX_PTR pAppData, +- OMX_BUFFERHEADERTYPE* pBuffer) +-{ +- COpenMax *ctx = (COpenMax*)pAppData; +- return ctx->DecoderFillBufferDone(hComponent, pAppData, pBuffer); +-} +- +- +- +-// Wait for a component to transition to the specified state +-OMX_ERRORTYPE COpenMax::WaitForState(OMX_STATETYPE state) +-{ +- OMX_STATETYPE test_state; +- int tries = 0; +- struct timespec timeout; +- OMX_ERRORTYPE omx_error = OMX_GetState(m_omx_decoder, &test_state); +- +- #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, "%s::%s - waiting for state(%d)\n", CLASSNAME, __func__, state); +- #endif +- while ((omx_error == OMX_ErrorNone) && (test_state != state)) +- { +- clock_gettime(CLOCK_REALTIME, &timeout); +- timeout.tv_sec += 1; +- sem_timedwait(m_omx_decoder_state_change, &timeout); +- if (errno == ETIMEDOUT) +- tries++; +- if (tries > 5) +- return OMX_ErrorUndefined; +- +- omx_error = OMX_GetState(m_omx_decoder, &test_state); +- } +- +- return omx_error; +-} +- +-// SetStateForAllComponents +-// Blocks until all state changes have completed +-OMX_ERRORTYPE COpenMax::SetStateForComponent(OMX_STATETYPE state) +-{ +- OMX_ERRORTYPE omx_err; +- +- #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, "%s::%s - state(%d)\n", CLASSNAME, __func__, state); +- #endif +- omx_err = OMX_SendCommand(m_omx_decoder, OMX_CommandStateSet, state, 0); +- if (omx_err) +- CLog::Log(LOGERROR, "%s::%s - OMX_CommandStateSet failed with omx_err(0x%x)\n", +- CLASSNAME, __func__, omx_err); +- else +- omx_err = WaitForState(state); +- +- return omx_err; +-} +- +-bool COpenMax::Initialize( const CStdString &decoder_name) +-{ +- OMX_ERRORTYPE omx_err = m_dll->OMX_Init(); +- if (omx_err) +- { +- CLog::Log(LOGERROR, +- "%s::%s - OpenMax failed to init, status(%d), ", // codec(%d), profile(%d), level(%d) +- CLASSNAME, __func__, omx_err );//, hints.codec, hints.profile, hints.level); +- return false; +- } +- +- // Get video decoder handle setting up callbacks, component is in loaded state on return. +- static OMX_CALLBACKTYPE decoder_callbacks = { +- &DecoderEventHandlerCallback, &DecoderEmptyBufferDoneCallback, &DecoderFillBufferDoneCallback }; +- omx_err = m_dll->OMX_GetHandle(&m_omx_decoder, (char*)decoder_name.c_str(), this, &decoder_callbacks); +- if (omx_err) +- { +- CLog::Log(LOGERROR, +- "%s::%s - could not get decoder handle\n", CLASSNAME, __func__); +- m_dll->OMX_Deinit(); +- return false; +- } +- +- return true; +-} +- +-void COpenMax::Deinitialize() +-{ +- CLog::Log(LOGERROR, +- "%s::%s - failed to get component port parameter\n", CLASSNAME, __func__); +- m_dll->OMX_FreeHandle(m_omx_decoder); +- m_omx_decoder = NULL; +- m_dll->OMX_Deinit(); +-} +- +-// OpenMax decoder callback routines. +-OMX_ERRORTYPE COpenMax::DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, +- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData) +-{ +- return OMX_ErrorNone; +-} +- +-OMX_ERRORTYPE COpenMax::DecoderEmptyBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +-{ +- return OMX_ErrorNone; +-} +- +-OMX_ERRORTYPE COpenMax::DecoderFillBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader) +-{ +- return OMX_ErrorNone; +-} +- +-#endif +- +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h +deleted file mode 100644 +index 0d9ff18..0000000 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h ++++ /dev/null +@@ -1,116 +0,0 @@ +-#pragma once +-/* +- * Copyright (C) 2010-2013 Team XBMC +- * http://xbmc.org +- * +- * This Program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2, or (at your option) +- * any later version. +- * +- * This Program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with XBMC; see the file COPYING. If not, see +- * <http://www.gnu.org/licenses/>. +- * +- */ +- +-#if defined(HAVE_LIBOPENMAX) +- +-#include "cores/dvdplayer/DVDStreamInfo.h" +-#include "DVDVideoCodec.h" +-#include "threads/Event.h" +- +-#include <queue> +-#include <semaphore.h> +-#include <OMX_Core.h> +- +-//////////////////////////////////////////////////////////////////////////////////////////// +-// debug spew defines +-#if 0 +-#define OMX_DEBUG_VERBOSE +-#define OMX_DEBUG_EVENTHANDLER +-#define OMX_DEBUG_FILLBUFFERDONE +-#define OMX_DEBUG_EMPTYBUFFERDONE +-#endif +- +-typedef struct omx_codec_capability { +- // level is OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE, +- // or OMX_VIDEO_MPEG4PROFILETYPE depending on context. +- OMX_U32 level; +- // level is OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE, +- // or OMX_VIDEO_MPEG4PROFILETYPE depending on context. +- OMX_U32 profile; +-} omx_codec_capability; +- +-typedef struct omx_demux_packet { +- OMX_U8 *buff; +- int size; +- double dts; +- double pts; +-} omx_demux_packet; +- +-class DllLibOpenMax; +-class COpenMax +-{ +-public: +- COpenMax(); +- virtual ~COpenMax(); +- +-protected: +- enum OMX_CLIENT_STATE { +- DEAD, +- LOADED, +- LOADED_TO_IDLE, +- IDLE_TO_EXECUTING, +- EXECUTING, +- EXECUTING_TO_IDLE, +- IDLE_TO_LOADED, +- RECONFIGURING, +- ERROR +- }; +- +- // initialize OpenMax and get decoder component +- bool Initialize( const CStdString &decoder_name); +- void Deinitialize(); +- +- // OpenMax Decoder delegate callback routines. +- static OMX_ERRORTYPE DecoderEventHandlerCallback(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, +- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); +- static OMX_ERRORTYPE DecoderEmptyBufferDoneCallback( +- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); +- static OMX_ERRORTYPE DecoderFillBufferDoneCallback( +- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader); +- +- // OpenMax decoder callback routines. +- virtual OMX_ERRORTYPE DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, +- OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); +- virtual OMX_ERRORTYPE DecoderEmptyBufferDone( +- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); +- virtual OMX_ERRORTYPE DecoderFillBufferDone( +- OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader); +- +- // OpenMax helper routines +- OMX_ERRORTYPE WaitForState(OMX_STATETYPE state); +- OMX_ERRORTYPE SetStateForComponent(OMX_STATETYPE state); +- +- DllLibOpenMax *m_dll; +- bool m_is_open; +- OMX_HANDLETYPE m_omx_decoder; // openmax decoder component reference +- +- // OpenMax state tracking +- OMX_CLIENT_STATE m_omx_client_state; +- volatile int m_omx_decoder_state; +- sem_t *m_omx_decoder_state_change; +- std::vector<omx_codec_capability> m_omx_decoder_capabilities; +- +-private: +- COpenMax(const COpenMax& other); +- COpenMax& operator=(const COpenMax&); +-}; +- +-#endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp +index dcbdb1e..aca2e0d 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp +@@ -32,673 +32,891 @@ + #include "DVDVideoCodec.h" + #include "utils/log.h" + #include "utils/TimeUtils.h" ++#include "settings/Settings.h" + #include "ApplicationMessenger.h" + #include "Application.h" ++#include "threads/Atomics.h" + +-#include <OMX_Core.h> +-#include <OMX_Component.h> +-#include <OMX_Index.h> +-#include <OMX_Image.h> ++#include <IL/OMX_Core.h> ++#include <IL/OMX_Component.h> ++#include <IL/OMX_Index.h> ++#include <IL/OMX_Image.h> + ++#include "cores/omxplayer/OMXImage.h" ++ ++#define DTS_QUEUE ++ ++#define DEFAULT_TIMEOUT 1000 ++#ifdef _DEBUG ++#define OMX_DEBUG_VERBOSE ++#endif + + #define CLASSNAME "COpenMaxVideo" + +-// TODO: These are Nvidia Tegra2 dependent, need to dynamiclly find the +-// right codec matched to video format. +-#define OMX_H264BASE_DECODER "OMX.Nvidia.h264.decode" +-// OMX.Nvidia.h264ext.decode segfaults, not sure why. +-//#define OMX_H264MAIN_DECODER "OMX.Nvidia.h264ext.decode" +-#define OMX_H264MAIN_DECODER "OMX.Nvidia.h264.decode" +-#define OMX_H264HIGH_DECODER "OMX.Nvidia.h264ext.decode" +-#define OMX_MPEG4_DECODER "OMX.Nvidia.mp4.decode" +-#define OMX_MPEG4EXT_DECODER "OMX.Nvidia.mp4ext.decode" +-#define OMX_MPEG2V_DECODER "OMX.Nvidia.mpeg2v.decode" +-#define OMX_VC1_DECODER "OMX.Nvidia.vc1.decode" +- +-// EGL extension functions +-static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; +-static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; +-#define GETEXTENSION(type, ext) \ +-do \ +-{ \ +- ext = (type) eglGetProcAddress(#ext); \ +- if (!ext) \ +- { \ +- CLog::Log(LOGERROR, "%s::%s - ERROR getting proc addr of " #ext "\n", CLASSNAME, __func__); \ +- } \ +-} while (0); +- +-#define OMX_INIT_STRUCTURE(a) \ +- memset(&(a), 0, sizeof(a)); \ +- (a).nSize = sizeof(a); \ +- (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ +- (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ +- (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ +- (a).nVersion.s.nStep = OMX_VERSION_STEP ++COpenMaxVideoBuffer::COpenMaxVideoBuffer(COpenMaxVideo *omv) ++ : m_omv(omv), m_refs(0) ++{ ++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this); ++ omx_buffer = NULL; ++ width = 0; ++ height = 0; ++ index = 0; ++ egl_image = 0; ++ texture_id = 0; ++} ++ ++COpenMaxVideoBuffer::~COpenMaxVideoBuffer() ++{ ++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this); ++} + + +-COpenMaxVideo::COpenMaxVideo() ++// DecoderFillBufferDone -- OpenMax output buffer has been filled ++static OMX_ERRORTYPE DecoderFillBufferDoneCallback( ++ OMX_HANDLETYPE hComponent, ++ OMX_PTR pAppData, ++ OMX_BUFFERHEADERTYPE* pBuffer) ++{ ++ COpenMaxVideoBuffer *pic = static_cast<COpenMaxVideoBuffer*>(pBuffer->pAppPrivate); ++ COpenMaxVideo *ctx = pic->m_omv; ++ return ctx->DecoderFillBufferDone(hComponent, pBuffer); ++} ++ ++ ++COpenMaxVideoBuffer* COpenMaxVideoBuffer::Acquire() ++{ ++ long count = AtomicIncrement(&m_refs); ++ #if defined(OMX_DEBUG_VERBOSE) ++ CLog::Log(LOGDEBUG, "%s::%s %p ref:%ld", CLASSNAME, __func__, this, count); ++ #endif ++ (void)count; ++ return this; ++} ++ ++long COpenMaxVideoBuffer::Release() + { +- m_portChanging = false; ++ long count = AtomicDecrement(&m_refs); ++ if (count == 0) ++ { ++ m_omv->ReleaseOpenMaxBuffer(this); ++ } ++ ++ #if defined(OMX_DEBUG_VERBOSE) ++ CLog::Log(LOGDEBUG, "%s::%s %p ref:%ld", CLASSNAME, __func__, this, count); ++ #endif ++ return count; ++} + +- pthread_mutex_init(&m_omx_input_mutex, NULL); ++void COpenMaxVideoBuffer::Sync() ++{ ++ #if defined(OMX_DEBUG_VERBOSE) ++ CLog::Log(LOGDEBUG, "%s::%s %p ref:%ld", CLASSNAME, __func__, this, m_refs); ++ #endif ++ Release(); ++} ++ ++COpenMaxVideo::COpenMaxVideo() ++{ ++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this); + pthread_mutex_init(&m_omx_output_mutex, NULL); + +- m_omx_decoder_state_change = (sem_t*)malloc(sizeof(sem_t)); +- sem_init(m_omx_decoder_state_change, 0, 0); +- memset(&m_videobuffer, 0, sizeof(DVDVideoPicture)); + m_drop_state = false; + m_decoded_width = 0; + m_decoded_height = 0; +- m_omx_input_eos = false; +- m_omx_input_port = 0; +- m_omx_output_eos = false; +- m_omx_output_port = 0; +- m_videoplayback_done = false; ++ m_egl_buffer_count = 0; ++ ++ m_port_settings_changed = false; ++ m_pFormatName = "omx-xxxx"; + } + + COpenMaxVideo::~COpenMaxVideo() + { + #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__); ++ CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, this); + #endif +- if (m_is_open) +- Close(); +- pthread_mutex_destroy(&m_omx_input_mutex); ++ if (m_omx_decoder.IsInitialized()) ++ { ++ if (m_omx_tunnel.IsInitialized()) ++ m_omx_tunnel.Deestablish(); ++ ++ StopDecoder(); ++ ++ if (m_omx_egl_render.IsInitialized()) ++ m_omx_egl_render.Deinitialize(); ++ if (m_omx_decoder.IsInitialized()) ++ m_omx_decoder.Deinitialize(); ++ } + pthread_mutex_destroy(&m_omx_output_mutex); +- sem_destroy(m_omx_decoder_state_change); +- free(m_omx_decoder_state_change); + } + +-bool COpenMaxVideo::Open(CDVDStreamInfo &hints) ++bool COpenMaxVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) + { + #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__); ++ CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__); + #endif + ++ // we always qualify even if DVDFactoryCodec does this too. ++ if (!CSettings::Get().GetBool("videoplayer.useomx") || hints.software) ++ return false; ++ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; +- std::string decoder_name; + + m_decoded_width = hints.width; + m_decoded_height = hints.height; + ++ m_egl_display = g_Windowing.GetEGLDisplay(); ++ m_egl_context = g_Windowing.GetEGLContext(); ++ m_egl_buffer_count = 4; ++ ++ m_codingType = OMX_VIDEO_CodingUnused; ++ + switch (hints.codec) + { + case AV_CODEC_ID_H264: +- { +- switch(hints.profile) +- { +- case FF_PROFILE_H264_BASELINE: +- // (role name) video_decoder.avc +- // H.264 Baseline profile +- decoder_name = OMX_H264BASE_DECODER; +- break; +- case FF_PROFILE_H264_MAIN: +- // (role name) video_decoder.avc +- // H.264 Main profile +- decoder_name = OMX_H264MAIN_DECODER; +- break; +- case FF_PROFILE_H264_HIGH: +- // (role name) video_decoder.avc +- // H.264 Main profile +- decoder_name = OMX_H264HIGH_DECODER; +- break; +- default: +- return false; +- break; +- } +- } ++ // H.264 ++ m_codingType = OMX_VIDEO_CodingAVC; ++ m_pFormatName = "omx-h264"; + break; ++ case AV_CODEC_ID_H263: + case AV_CODEC_ID_MPEG4: +- // (role name) video_decoder.mpeg4 +- // MPEG-4, DivX 4/5 and Xvid compatible +- decoder_name = OMX_MPEG4_DECODER; +- break; +- /* +- TODO: what mpeg4 formats are "ext" ???? +- case NvxStreamType_MPEG4Ext: +- // (role name) video_decoder.mpeg4 + // MPEG-4, DivX 4/5 and Xvid compatible +- decoder_name = OMX_MPEG4EXT_DECODER; ++ m_codingType = OMX_VIDEO_CodingMPEG4; + m_pFormatName = "omx-mpeg4"; + break; +- */ ++ case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: +- // (role name) video_decoder.mpeg2 + // MPEG-2 +- decoder_name = OMX_MPEG2V_DECODER; ++ m_codingType = OMX_VIDEO_CodingMPEG2; ++ m_pFormatName = "omx-mpeg2"; ++ break; ++ case AV_CODEC_ID_VP6: ++ // this form is encoded upside down ++ // fall through ++ case AV_CODEC_ID_VP6F: ++ case AV_CODEC_ID_VP6A: ++ // VP6 ++ m_codingType = OMX_VIDEO_CodingVP6; ++ m_pFormatName = "omx-vp6"; ++ break; ++ case AV_CODEC_ID_VP8: ++ // VP8 ++ m_codingType = OMX_VIDEO_CodingVP8; ++ m_pFormatName = "omx-vp8"; ++ break; ++ case AV_CODEC_ID_THEORA: ++ // theora ++ m_codingType = OMX_VIDEO_CodingTheora; ++ m_pFormatName = "omx-theora"; ++ break; ++ case AV_CODEC_ID_MJPEG: ++ case AV_CODEC_ID_MJPEGB: ++ // mjpg ++ m_codingType = OMX_VIDEO_CodingMJPEG; ++ m_pFormatName = "omx-mjpg"; + break; + case AV_CODEC_ID_VC1: +- // (role name) video_decoder.vc1 ++ case AV_CODEC_ID_WMV3: + // VC-1, WMV9 +- decoder_name = OMX_VC1_DECODER; +- break; ++ m_codingType = OMX_VIDEO_CodingWMV; ++ m_pFormatName = "omx-vc1"; ++ break; + default: ++ CLog::Log(LOGERROR, "%s::%s : Video codec unknown: %x", CLASSNAME, __func__, hints.codec); + return false; + break; + } + + // initialize OpenMAX. +- if (!Initialize(decoder_name)) ++ if (!m_omx_decoder.Initialize("OMX.broadcom.video_decode", OMX_IndexParamVideoInit)) + { ++ CLog::Log(LOGERROR, "%s::%s error m_omx_decoder.Initialize", CLASSNAME, __func__); + return false; + } + +- // TODO: Find component from role name. +- // Get the port information. This will obtain information about the +- // number of ports and index of the first port. +- OMX_PORT_PARAM_TYPE port_param; +- OMX_INIT_STRUCTURE(port_param); +- omx_err = OMX_GetParameter(m_omx_decoder, OMX_IndexParamVideoInit, &port_param); +- if (omx_err) ++ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateIdle); ++ if (omx_err != OMX_ErrorNone) + { +- Deinitialize(); ++ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetStateForComponent omx_err(0x%08x)", CLASSNAME, __func__, omx_err); + return false; + } +- m_omx_input_port = port_param.nStartPortNumber; +- m_omx_output_port = m_omx_input_port + 1; +- #if defined(OMX_DEBUG_VERBOSE) +- CLog::Log(LOGDEBUG, +- "%s::%s - decoder_component(0x%p), input_port(0x%x), output_port(0x%x)\n", +- CLASSNAME, __func__, m_omx_decoder, m_omx_input_port, m_omx_output_port); +- #endif + +- // TODO: Set role for the component because components could have multiple roles. +- //QueryCodec(); ++ OMX_VIDEO_PARAM_PORTFORMATTYPE formatType; ++ OMX_INIT_STRUCTURE(formatType); ++ formatType.nPortIndex = m_omx_decoder.GetInputPort(); ++ formatType.eCompressionFormat = m_codingType; ++ ++ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamVideoPortFormat, &formatType); ++ if (omx_err != OMX_ErrorNone) ++ { ++ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err); ++ return false; ++ } ++ OMX_PARAM_PORTDEFINITIONTYPE portParam; ++ OMX_INIT_STRUCTURE(portParam); ++ portParam.nPortIndex = m_omx_decoder.GetInputPort(); ++ ++ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &portParam); ++ if (omx_err != OMX_ErrorNone) ++ { ++ CLog::Log(LOGERROR, "%s::%s error OMX_IndexParamPortDefinition omx_err(0x%08x)", CLASSNAME, __func__, omx_err); ++ return false; ++ } ++ ++ portParam.nPortIndex = m_omx_decoder.GetInputPort(); ++ portParam.nBufferCountActual = 20; ++ portParam.format.video.nFrameWidth = m_decoded_width; ++ portParam.format.video.nFrameHeight = m_decoded_height; ++ ++ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &portParam); ++ if (omx_err != OMX_ErrorNone) ++ { ++ CLog::Log(LOGERROR, "%s::%s error OMX_IndexParamPortDefinition omx_err(0x%08x)", CLASSNAME, __func__, omx_err); ++ return false; ++ } ++ ++ // request portsettingschanged on aspect ratio change ++ OMX_CONFIG_REQUESTCALLBACKTYPE notifications; ++ OMX_INIT_STRUCTURE(notifications); ++ notifications.nPortIndex = m_omx_decoder.GetOutputPort(); ++ notifications.nIndex = OMX_IndexParamBrcmPixelAspectRatio; ++ notifications.bEnable = OMX_TRUE; ++ ++ omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexConfigRequestCallback, ¬ifications); ++ 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, ¬ifications); ++ 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 + |