From 1353d8feca19f2f84019797942d70864054db1b0 Mon Sep 17 00:00:00 2001 From: Ben Avison 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ö --- 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 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ö --- 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 ih264dsp.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 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ö --- 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 + * + * 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 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 +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ö +--- + 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 +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ö +--- + 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 +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ö +--- + 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 ++ * ++ * 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 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 --- 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 - * - * 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 + * + * 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 + * + * 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 + */ + +#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 + * + * 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 + */ + +#ifndef AVCODEC_STARTCODE_H +#define AVCODEC_STARTCODE_H + +#include + +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 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 --- 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 + +#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 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 --- 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; iticks_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(; iframe_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 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 + * + * 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 + * + * 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 + +#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 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 +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 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 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 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 +#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 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 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 #include "utils/log.h" #include "GUIInfoManager.h" +#include +#include +#include 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::iterator last = m_children.end(); + std::list::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_stack, bool &invert, std::stack &node_types, std::stack &nodes) { - stack 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(left.get()))->Merge(static_cast(right.get())); + else if (left_type == new_type) + (static_cast(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(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(new_type == NODE_AND, left, right)); + node_types.push(new_type); + } + } +} + +void InfoExpression::ProcessOperator(operator_t op, std::stack &operator_stack, bool &invert, std::stack &node_types, std::stack &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_types, std::stack &nodes) +{ + InfoPtr info = g_infoManager.Register(operand, m_context); + if (!info) + return false; + m_listItemDependent |= info->ListItemDependent(); + nodes.push(boost::make_shared(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_stack; + bool invert = false; + std::stack node_types; + std::stack 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 save; - for (vector::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 +#include +#include #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 m_postfix; ///< the postfix form of the expression (operators and operand indicies) - std::vector 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 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 m_children; + }; + + static operator_t GetOperator(char ch); + static void OperatorPop(std::stack &operator_stack, bool &invert, std::stack &node_types, std::stack &nodes); + static void ProcessOperator(operator_t op, std::stack &operator_stack, bool &invert, std::stack &node_types, std::stack &nodes); + bool ProcessOperand(std::string &operand, bool invert, std::stack &node_types, std::stack &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 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(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 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 m_info; + + mutable bool m_labelDirty; + mutable std::string m_label; + mutable std::vector m_labelPortions; + mutable bool m_itemLabelDirty; + mutable std::string m_itemLabel; + mutable std::vector m_itemLabelPortions; }; #endif -- 1.9.3 From 3d5a1912ffd4556ec09208fea50d2a2919775c9f Mon Sep 17 00:00:00 2001 From: Ben Avison 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 m_info; + mutable bool m_isLabelOfListItem; mutable bool m_labelDirty; mutable std::string m_label; mutable std::vector m_labelPortions; - mutable bool m_itemLabelDirty; - mutable std::string m_itemLabel; - mutable std::vector m_itemLabelPortions; }; #endif -- 1.9.3 From 1427baf4395b760227afbef8e17956ba251f2fbe Mon Sep 17 00:00:00 2001 From: popcornmix 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(INT_MIN / 2) - 1.0); assert(x < static_cast(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(INT_MIN / 2) - 1.0); assert(x < static_cast(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 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 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 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 vecVertices( 6 * (m_vertex_count / 4) ); + std::vector vecVertices( 6 * (m_vertex.size() / 4) ); SVertex *vertices = &vecVertices[0]; - for (int i=0; i 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 &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 &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 &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 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 + * . + * + */ + +#include +#include +#include "utils/StdString.h" // required by GUIFontTTF.h +#include "GUIFontTTF.h" +#include "GraphicContext.h" + +template +void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &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 +CGUIFontCacheEntry::~CGUIFontCacheEntry() +{ + delete &m_key.m_colors; + delete &m_key.m_text; + m_value.clear(); +} + +template +Value &CGUIFontCache::Lookup(Position &pos, + const vecColors &colors, const vecText &text, + uint32_t alignment, float maxPixelWidth, + bool scrolling, + unsigned int nowMillis, bool &dirtyCache) +{ + const CGUIFontCacheKey key(pos, + const_cast(colors), const_cast(text), + alignment, maxPixelWidth, + scrolling, g_graphicsContext.GetGUIMatrix(), + g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY()); + EntryHashIterator i = m_list.get().find(key); + if (i == m_list.get().end()) + { + /* Cache miss */ + EntryAgeIterator oldest = m_list.get().begin(); + if (!m_list.get().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) + { + /* The oldest existing entry is old enough to expire and reuse */ + m_list.get().modify(m_list.project(oldest), typename CGUIFontCacheEntry::Reassign(key, nowMillis)); + m_list.get().relocate(m_list.get().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().push_back(CGUIFontCacheEntry(*this, key, nowMillis)); + } + dirtyCache = true; + return (--m_list.get().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().relocate(m_list.get().end(), m_list.project(i)); + dirtyCache = false; + return i->m_value; + } +} + +template +void CGUIFontCache::Flush() +{ + m_list.get().clear(); +} + +template void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry); +template CGUIFontCacheEntry::~CGUIFontCacheEntry(); +template CGUIFontCacheStaticValue &CGUIFontCache::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); +template void CGUIFontCache::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 + * . + * + */ + +#include +#include +#include + +#include +#include + +#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 CGUIFontCache; +class CGUIFontTTFBase; + +template +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 +struct CGUIFontCacheEntry +{ + const CGUIFontCache &m_cache; + CGUIFontCacheKey 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 &cache, const CGUIFontCacheKey &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 &key, unsigned int nowMillis) : m_key(key), m_nowMillis(nowMillis) {} + void operator()(CGUIFontCacheEntry &entry); + private: + const CGUIFontCacheKey &m_key; + unsigned int m_nowMillis; + }; + + ~CGUIFontCacheEntry(); +}; + +template +struct CGUIFontCacheHash +{ + size_t operator()(const CGUIFontCacheKey &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 +struct CGUIFontCacheKeysMatch +{ + bool operator()(const CGUIFontCacheKey &a, const CGUIFontCacheKey &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 CGUIFontCache +{ + /* Empty structs used as tags to identify indexes */ + struct Age {}; + struct Hash {}; + + typedef multi_index_container< + CGUIFontCacheEntry, + indexed_by< + sequenced >, + hashed_unique, member, CGUIFontCacheKey, &CGUIFontCacheEntry::m_key>, CGUIFontCacheHash, CGUIFontCacheKeysMatch > + > + > EntryList; + + typedef typename EntryList::template index::type::iterator EntryAgeIterator; + typedef typename EntryList::template index::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 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 &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 @@ -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 &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 &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 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 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 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::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().relocate(m_list.get().end(), m_list.project(i)); @@ -103,3 +106,8 @@ template void CGUIFontCacheEntry::~CGUIFontCacheEntry(); template CGUIFontCacheStaticValue &CGUIFontCache::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); template void CGUIFontCache::Flush(); + +template void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry); +template CGUIFontCacheEntry::~CGUIFontCacheEntry(); +template CGUIFontCacheDynamicValue &CGUIFontCache::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); +template void CGUIFontCache::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 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 CGUIFontCacheStaticValue; @@ -214,4 +216,45 @@ inline float MatrixHashContribution(const CGUIFontCacheKey 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 &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 &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 &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 m_staticCache; + CGUIFontCache m_dynamicCache; private: virtual bool FirstBegin() = 0; -- 1.9.3 From 10eeb73ca15798de26abd5f8846214c8938f8b42 Mon Sep 17 00:00:00 2001 From: Ben Avison 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( - (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 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 *vertexBuffer; + CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {} + }; + std::vector m_vertexTrans; std::vector 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 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 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 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 0) + { + // Deal with the vertices that can be hardware clipped and therefore translated + std::vector 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 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 @@ -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 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 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 *vertexBuffer; - CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {} + CRect clip; + CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} }; std::vector m_vertexTrans; std::vector 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 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 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 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::~CGUIFontCacheEntry(); template CGUIFontCacheDynamicValue &CGUIFontCache::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); template void CGUIFontCache::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 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 &vertices = hardwareClipping ? + CVertexBuffer unusedVertexBuffer; + CVertexBuffer &vertexBuffer = hardwareClipping ? m_dynamicCache.Lookup(dynamicPos, colors, text, alignment, maxPixelWidth, scrolling, XbmcThreads::SystemClockMillis(), dirtyCache) : + unusedVertexBuffer; + std::vector tempVertices; + std::vector &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 &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 *vertexBuffer; + const CVertexBuffer *vertexBuffer; CRect clip; - CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *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 m_vertexTrans; std::vector 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 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 &vertices) const +{ + // Rearrange the vertices to describe triangles + std::vector 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 &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 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(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 &vertices) const { - // Rearrange the vertices to describe triangles - std::vector 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 &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 &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 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 @@ + @@ -2057,6 +2058,7 @@ + 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 @@ guilib + + guilib + guilib @@ -3978,6 +3981,9 @@ guilib + + guilib + guilib -- 1.9.3 From e25eb385d09a5378be8616f10806610df90416db Mon Sep 17 00:00:00 2001 From: Ben Avison 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 = ""; }; 1DE0443315828F4B005DDB4D /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Exception.cpp; path = commons/Exception.cpp; sourceTree = ""; }; 1DE0443415828F4B005DDB4D /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = commons/Exception.h; sourceTree = ""; }; + 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIFontCache.cpp; sourceTree = ""; }; + 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIFontCache.h; sourceTree = ""; }; 32C631261423A90F00F18420 /* JpegIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JpegIO.cpp; sourceTree = ""; }; 32C631271423A90F00F18420 /* JpegIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JpegIO.h; sourceTree = ""; }; 36A9443B15821E2800727135 /* DatabaseUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseUtils.cpp; sourceTree = ""; }; @@ -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 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::Lookup(Position &pos, alignment, maxPixelWidth, scrolling, g_graphicsContext.GetGUIMatrix(), g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY()); - EntryHashIterator i = m_list.get().find(key); - if (i == m_list.get().end()) + EntryHashIterator i = m_list.template get().find(key); + if (i == m_list.template get().end()) { /* Cache miss */ - EntryAgeIterator oldest = m_list.get().begin(); - if (!m_list.get().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) + EntryAgeIterator oldest = m_list.template get().begin(); + if (!m_list.template get().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) { /* The oldest existing entry is old enough to expire and reuse */ - m_list.get().modify(m_list.project(oldest), typename CGUIFontCacheEntry::Reassign(key, nowMillis)); - m_list.get().relocate(m_list.get().end(), oldest); + m_list.template get().modify(m_list.template project(oldest), typename CGUIFontCacheEntry::Reassign(key, nowMillis)); + m_list.template get().relocate(m_list.template get().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().push_back(CGUIFontCacheEntry(*this, key, nowMillis)); + m_list.template get().push_back(CGUIFontCacheEntry(*this, key, nowMillis)); } dirtyCache = true; - return (--m_list.get().end())->m_value; + return (--m_list.template get().end())->m_value; } else { @@ -90,7 +90,7 @@ Value &CGUIFontCache::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().relocate(m_list.get().end(), m_list.project(i)); + m_list.template get().relocate(m_list.template get().end(), m_list.template project(i)); dirtyCache = false; return i->m_value; } @@ -99,7 +99,7 @@ Value &CGUIFontCache::Lookup(Position &pos, template void CGUIFontCache::Flush() { - m_list.get().clear(); + m_list.template get().clear(); } template void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry); -- 1.9.3 From 33693dc9ff9ba7695bc0e702a41566d54a5135b9 Mon Sep 17 00:00:00 2001 From: Ben Avison 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 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 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 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 @@ - - - + + + - + - - + + - - + + + - + - + - + -- 1.9.3 From 5c4de293325bba93c4b820aed6863ee8d732ce2c Mon Sep 17 00:00:00 2001 From: popcornmix 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 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 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 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 Features; @@ -161,7 +161,7 @@ class CLinuxRendererGLES : public CBaseRenderer virtual std::vector 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 - // 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 - // 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 - // 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 - * . - * - */ - -#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 -#include -#include -#include - -#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 - * . - * - */ - -#if defined(HAVE_LIBOPENMAX) - -#include "cores/dvdplayer/DVDStreamInfo.h" -#include "DVDVideoCodec.h" -#include "threads/Event.h" - -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////// -// 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 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 -#include -#include -#include +#include +#include +#include +#include +#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(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(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(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(userdata); - omx->AllocOMXOutputEGLTextures(); + return omx->AllocOMXOutputEGLTextures() == OMX_ErrorNone; } -void COpenMaxVideo::CallbackFreeOMXEGLTextures(void *userdata) +bool COpenMaxVideo::CallbackFreeOMXEGLTextures(void *userdata) { COpenMaxVideo *omx = static_cast(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(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 #include +#include "linux/OMXCore.h" +#include "linux/OMXClock.h" + +#include "cores/dvdplayer/DVDStreamInfo.h" +#include "DVDVideoCodec.h" +#include "threads/Event.h" + +#include +#include + +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 m_dts_queue; std::queue m_demux_queue; - // OpenMax input buffers (demuxer packets) - pthread_mutex_t m_omx_input_mutex; - std::queue m_omx_input_avaliable; - std::vector 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 m_omx_output_busy; - std::queue m_omx_output_ready; - std::vector m_omx_output_buffers; - bool m_omx_output_eos; - int m_omx_output_port; - //sem_t *m_omx_flush_output; + std::vector m_omx_output_busy; + std::queue m_omx_output_ready; + std::vector 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 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 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 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 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 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 m_dts_queue; std::queue m_demux_queue; -- 1.9.3 From e8f40e625203fe4113e2687d3730c38770cc0857 Mon Sep 17 00:00:00 2001 From: popcornmix 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 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 #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 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 #include @@ -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 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& 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& 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 #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& 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 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 @@
- - - - false - - - false -- 1.9.3 From 4fb1419b986a36f2e53a5bca71caf90bd13443ba Mon Sep 17 00:00:00 2001 From: popcornmix 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(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(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 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 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 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 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 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 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 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 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 + * . + * + */ + +#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; outCount(); 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%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 + * . + * + */ + +#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 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 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 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 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 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 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; outblock_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< -#include -#include -#include - -//#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 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::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 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 -#include -#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 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 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 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 &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 &deinterlaceMethods) -- 1.9.3 From 97a3f2347e45eee8f960a2190891d4bd9de3dda0 Mon Sep 17 00:00:00 2001 From: popcornmix 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 +#include #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 &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 +#include //////////////////////////////////////////////////////////////////////////////////////////// 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 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(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 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 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 #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 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 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 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 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 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 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 &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 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 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 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 @@ -- 1.9.3 From f417d7d303eaa5113edba8ba562cb61ed8a6c59a Mon Sep 17 00:00:00 2001 From: popcornmix 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 @@ - + -- 1.9.3 From 0563f1df1295ac5600fd330fb201e854ad900e02 Mon Sep 17 00:00:00 2001 From: popcornmix 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 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 &resolutions) +int CEGLNativeTypeRaspberryPI::AddUniqueResolution(RESOLUTION_INFO &res, std::vector &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 &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 &resolutions); - int AddUniqueResolution(const RESOLUTION_INFO &res, std::vector &resolutions); + int AddUniqueResolution(RESOLUTION_INFO &res, std::vector &resolutions); #endif }; -- 1.9.3 From a4c36a4925e780b63d9821fb04504453ac982205 Mon Sep 17 00:00:00 2001 From: popcornmix 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 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 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 #include #include @@ -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 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 m_packets; + + CDroppingStats m_droppingStats; }; -- 1.9.3 From 16d2c2ee305eb2cd3ec42fd0aad474dbf356d75d Mon Sep 17 00:00:00 2001 From: xbmc 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 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 #include @@ -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 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 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 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 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 &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 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 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 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 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