summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2019-07-01 20:59:51 +0000
committerXin Li <delphij@google.com>2019-07-01 20:59:51 +0000
commite30861d43c7b5fdcfe4cfeeae6d763ce1d0480f3 (patch)
tree2c40513c549bcd6ed9d5c8805a24209e7d44f60c
parenta91a5608eabfce0dadbc7706f7fd8393356e7419 (diff)
parent83fa2c850b78aab77baebd5143e4c555c7c2a28b (diff)
downloadav-e30861d43c7b5fdcfe4cfeeae6d763ce1d0480f3.tar.gz
DO NOT MERGE - Merge qt-dev-plus-aosp-without-vendor (5699924) into stage-aosp-master
Bug: 134405016 Change-Id: I605f62d294a4dd99e69c4ca9acddfa67b39c1c9c
-rw-r--r--Android.bp4
-rw-r--r--codec2/Android.bp5
-rw-r--r--codec2/Android.mk48
-rwxr-xr-xcodec2/docs/doxyfilter.sh100
-rw-r--r--codec2/docs/doxygen.config2446
-rw-r--r--codec2/faultinjection/Android.bp29
-rw-r--r--codec2/faultinjection/C2ComponentWrapper.cpp129
-rw-r--r--codec2/faultinjection/C2ComponentWrapper.h89
-rw-r--r--codec2/faultinjection/SimpleMethodState.cpp52
-rw-r--r--codec2/faultinjection/SimpleMethodState.h82
-rw-r--r--codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioDecTest.cpp511
-rw-r--r--codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioEncTest.cpp273
-rw-r--r--codec2/hidl/1.0/mts/audio/media_c2_audio_hidl_test_common.h3
-rw-r--r--codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.cpp126
-rw-r--r--codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.h23
-rw-r--r--codec2/hidl/1.0/mts/component/MtsHidlC2V1_0TargetComponentTest.cpp132
-rw-r--r--codec2/hidl/1.0/mts/res/bbb_av1_176_144.av1bin0 -> 307385 bytes
-rw-r--r--codec2/hidl/1.0/mts/res/bbb_av1_176_144.info300
-rw-r--r--codec2/hidl/1.0/mts/res/bbb_av1_640_360.av1bin0 -> 250646 bytes
-rw-r--r--codec2/hidl/1.0/mts/res/bbb_av1_640_360.info167
-rw-r--r--codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoDecTest.cpp307
-rw-r--r--codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoEncTest.cpp114
-rw-r--r--codec2/hidl/1.0/mts/video/media_c2_video_hidl_test_common.h3
-rw-r--r--codec2/hidl/1.0/utils/Android.bp3
-rw-r--r--codec2/hidl/1.0/utils/ComponentStore.cpp2
-rw-r--r--codec2/hidl/1.0/utils/InputSurfaceConnection.cpp2
-rw-r--r--codec2/hidl/1.0/utils/types.cpp6
-rw-r--r--codec2/hidl/client/client.cpp6
-rw-r--r--codec2/hidl/services/Android.bp66
-rw-r--r--codec2/hidl/services/Android.mk24
-rw-r--r--codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp11
-rw-r--r--codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy73
-rw-r--r--codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy57
-rw-r--r--codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy73
-rw-r--r--codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy57
-rw-r--r--codec2/include/C2Component.h1
-rw-r--r--codec2/include/C2Config.h23
-rw-r--r--codec2/include/C2Enum.h6
-rw-r--r--codec2/include/media/stagefright/codec2/1.0/InputSurface.h2
-rw-r--r--codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h2
-rw-r--r--codec2/tests/Android.bp3
-rw-r--r--codec2/tests/C2SampleComponent_test.cpp10
-rw-r--r--codec2/vndk/Android.bp10
-rw-r--r--codec2/vndk/C2AllocatorGralloc.cpp37
-rw-r--r--codec2/vndk/bufferpool/Accessor.cpp201
-rw-r--r--codec2/vndk/bufferpool/Accessor.h188
-rw-r--r--codec2/vndk/bufferpool/AccessorImpl.cpp543
-rw-r--r--codec2/vndk/bufferpool/AccessorImpl.h300
-rw-r--r--codec2/vndk/bufferpool/Android.bp29
-rw-r--r--codec2/vndk/bufferpool/BufferPoolClient.cpp708
-rw-r--r--codec2/vndk/bufferpool/BufferPoolClient.h102
-rw-r--r--codec2/vndk/bufferpool/BufferStatus.cpp191
-rw-r--r--codec2/vndk/bufferpool/BufferStatus.h143
-rw-r--r--codec2/vndk/bufferpool/ClientManager.cpp504
-rw-r--r--codec2/vndk/bufferpool/Connection.cpp89
-rw-r--r--codec2/vndk/bufferpool/Connection.h103
-rw-r--r--codec2/vndk/bufferpool/include/bufferpool/BufferPoolTypes.h118
-rw-r--r--codec2/vndk/bufferpool/include/bufferpool/ClientManager.h179
-rw-r--r--codec2/vndk/bufferpool/vts/Android.bp55
-rw-r--r--codec2/vndk/bufferpool/vts/OWNERS7
-rw-r--r--codec2/vndk/bufferpool/vts/allocator.cpp86
-rw-r--r--codec2/vndk/bufferpool/vts/allocator.h50
-rw-r--r--codec2/vndk/bufferpool/vts/multi.cpp228
-rw-r--r--codec2/vndk/bufferpool/vts/single.cpp173
-rw-r--r--codec2/vndk/include/C2BqBufferPriv.h2
-rw-r--r--codec2/vndk/platform/C2BqBuffer.cpp52
-rw-r--r--media/Android.bp4
-rw-r--r--media/codecs/Android.bp3
-rw-r--r--media/codecs/aac/C2SoftAacDec.cpp49
-rw-r--r--media/codecs/aac/C2SoftAacEnc.cpp36
-rw-r--r--media/codecs/aac/C2SoftAacEnc.h1
-rw-r--r--media/codecs/amr_nb_wb/C2SoftAmrDec.cpp7
-rw-r--r--media/codecs/amr_nb_wb/C2SoftAmrNbEnc.cpp5
-rw-r--r--media/codecs/amr_nb_wb/C2SoftAmrWbEnc.cpp5
-rw-r--r--media/codecs/avc/C2SoftAvcDec.cpp81
-rw-r--r--media/codecs/avc/C2SoftAvcDec.h4
-rw-r--r--media/codecs/avc/C2SoftAvcEnc.cpp72
-rw-r--r--media/codecs/base/Android.bp9
-rw-r--r--media/codecs/base/SimpleC2Component.cpp65
-rw-r--r--media/codecs/base/include/SimpleC2Component.h3
-rw-r--r--media/codecs/cmds/Android.bp2
-rw-r--r--media/codecs/cmds/codec2.cpp26
-rw-r--r--media/codecs/flac/C2SoftFlacDec.cpp6
-rw-r--r--media/codecs/flac/C2SoftFlacEnc.cpp10
-rw-r--r--media/codecs/g711/C2SoftG711Dec.cpp7
-rw-r--r--media/codecs/gsm/C2SoftGsmDec.cpp7
-rw-r--r--media/codecs/hevc/C2SoftHevcDec.cpp71
-rw-r--r--media/codecs/hevc/C2SoftHevcDec.h3
-rw-r--r--media/codecs/mp3/C2SoftMp3Dec.cpp8
-rw-r--r--media/codecs/mpeg2/C2SoftMpeg2Dec.cpp65
-rw-r--r--media/codecs/mpeg2/C2SoftMpeg2Dec.h5
-rw-r--r--media/codecs/mpeg4_h263/C2SoftMpeg4Dec.cpp37
-rw-r--r--media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp18
-rw-r--r--media/codecs/opus/C2SoftOpusDec.cpp5
-rw-r--r--media/codecs/raw/C2SoftRawDec.cpp17
-rw-r--r--media/codecs/vorbis/C2SoftVorbisDec.cpp5
-rw-r--r--media/codecs/vpx/Android.bp8
-rw-r--r--media/codecs/vpx/C2SoftVpxDec.cpp72
-rw-r--r--media/codecs/vpx/C2SoftVpxEnc.cpp6
-rw-r--r--media/codecs/xaac/C2SoftXaacDec.cpp72
-rw-r--r--media/codecs/xaac/C2SoftXaacDec.h2
-rw-r--r--media/eco/.clang-format33
-rw-r--r--media/eco/Android.bp65
-rw-r--r--media/eco/ECOData.cpp421
-rw-r--r--media/eco/ECODebug.cpp32
-rw-r--r--media/eco/ECOService.cpp155
-rw-r--r--media/eco/ECOSession.cpp579
-rw-r--r--media/eco/ECOUtils.cpp61
-rw-r--r--media/eco/OWNERS2
-rw-r--r--media/eco/aidl/android/media/eco/ECOData.aidl20
-rw-r--r--media/eco/aidl/android/media/eco/IECOService.aidl64
-rw-r--r--media/eco/aidl/android/media/eco/IECOServiceInfoListener.aidl63
-rw-r--r--media/eco/aidl/android/media/eco/IECOServiceStatsProvider.aidl56
-rw-r--r--media/eco/aidl/android/media/eco/IECOSession.aidl115
-rw-r--r--media/eco/include/eco/ECOData.h219
-rw-r--r--media/eco/include/eco/ECODataKey.h99
-rw-r--r--media/eco/include/eco/ECODebug.h67
-rw-r--r--media/eco/include/eco/ECOService.h124
-rw-r--r--media/eco/include/eco/ECOServiceConstants.h260
-rw-r--r--media/eco/include/eco/ECOServiceInfoListener.h66
-rw-r--r--media/eco/include/eco/ECOServiceStatsProvider.h66
-rw-r--r--media/eco/include/eco/ECOSession.h206
-rw-r--r--media/eco/include/eco/ECOUtils.h130
-rw-r--r--media/eco/tests/Android.bp57
-rw-r--r--media/eco/tests/EcoDataTest.cpp367
-rw-r--r--media/eco/tests/EcoServiceTest.cpp228
-rw-r--r--media/eco/tests/EcoSessionTest.cpp608
-rw-r--r--media/eco/tests/FakeECOServiceInfoListener.cpp85
-rw-r--r--media/eco/tests/FakeECOServiceInfoListener.h86
-rw-r--r--media/eco/tests/FakeECOServiceStatsProvider.cpp103
-rw-r--r--media/eco/tests/FakeECOServiceStatsProvider.h100
-rw-r--r--media/eco/tests/run_all_unit_tests.sh13
-rw-r--r--media/sfplugin/C2OMXNode.cpp4
-rw-r--r--media/sfplugin/CCodec.cpp15
-rw-r--r--media/sfplugin/CCodecBufferChannel.cpp205
-rw-r--r--media/sfplugin/CCodecBufferChannel.h3
-rw-r--r--media/sfplugin/CCodecConfig.cpp24
-rw-r--r--media/sfplugin/Codec2Buffer.cpp8
-rw-r--r--media/sfplugin/Codec2InfoBuilder.cpp93
-rw-r--r--media/sfplugin/ReflectedParamUpdater.cpp2
-rw-r--r--media/sfplugin/SkipCutBuffer.cpp6
-rw-r--r--media/sfplugin/tests/Android.bp2
142 files changed, 7179 insertions, 7997 deletions
diff --git a/Android.bp b/Android.bp
index ca555fb..699ede1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,7 +3,3 @@ soong_namespace {
"hardware/google/interfaces",
],
}
-
-subdirs = [
- "media",
-]
diff --git a/codec2/Android.bp b/codec2/Android.bp
index ee46c24..9addc79 100644
--- a/codec2/Android.bp
+++ b/codec2/Android.bp
@@ -40,8 +40,3 @@ cc_library_shared {
ldflags: ["-Wl,-Bsymbolic"],
}
-
-subdirs = [
- "tests",
- "vndk",
-]
diff --git a/codec2/Android.mk b/codec2/Android.mk
deleted file mode 100644
index 82d739f..0000000
--- a/codec2/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# =============================================================================
-# DOCUMENTATION GENERATION
-# =============================================================================
-C2_ROOT := $(call my-dir)
-
-C2_DOCS_ROOT := $(OUT_DIR)/target/common/docs/codec2
-
-C2_OUT_TEMP := $(PRODUCT_OUT)/gen/ETC/Codec2-docs_intermediates
-
-C2_DOXY := $(or $(shell command -v doxygen),\
- $(shell command -v /Applications/Doxygen.app/Contents/Resources/doxygen))
-
-.PHONY: check-doxygen
-check-doxygen:
-ifndef C2_DOXY
- $(error 'doxygen is not available')
-endif
-
-$(C2_OUT_TEMP)/doxy-api.config: $(C2_ROOT)/docs/doxygen.config
- # only document include directory, no internal sections
- sed 's/\(^INPUT *=.*\)/\1include\//; \
- s/\(^INTERNAL_DOCS *= *\).*/\1NO/; \
- s/\(^ENABLED_SECTIONS *=.*\)INTERNAL\(.*\).*/\1\2/; \
- s:\(^OUTPUT_DIRECTORY *= \)out:\1'$(OUT_DIR)':;' \
- $(C2_ROOT)/docs/doxygen.config > $@
-
-$(C2_OUT_TEMP)/doxy-internal.config: $(C2_ROOT)/docs/doxygen.config
- sed 's:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$(OUT_DIR)'\2internal:;' \
- $(C2_ROOT)/docs/doxygen.config > $@
-
-.PHONY: docs-api
-docs-api: $(C2_OUT_TEMP)/doxy-api.config check-doxygen
- echo API docs are building in $(C2_DOCS_ROOT)/api
- rm -rf $(C2_DOCS_ROOT)/api
- mkdir -p $(C2_DOCS_ROOT)/api
- $(C2_DOXY) $(C2_OUT_TEMP)/doxy-api.config
-
-.PHONY: docs-internal
-docs-internal: $(C2_OUT_TEMP)/doxy-internal.config check-doxygen
- echo Internal docs are building in $(C2_DOCS_ROOT)/internal
- rm -rf $(C2_DOCS_ROOT)/internal
- mkdir -p $(C2_DOCS_ROOT)/internal
- $(C2_DOXY) $(C2_OUT_TEMP)/doxy-internal.config
-
-.PHONY: docs-all
-docs-all: docs-api docs-internal
-
-include $(call all-makefiles-under,$(call my-dir))
diff --git a/codec2/docs/doxyfilter.sh b/codec2/docs/doxyfilter.sh
deleted file mode 100755
index d813153..0000000
--- a/codec2/docs/doxyfilter.sh
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python3
-import re, sys
-
-global in_comment, current, indent, hold
-in_comment, current, indent, hold = False, None, '', []
-
-class ChangeCStyleCommentsToDoxy:
- def dump_hold():
- global hold
- for h in hold:
- print(h, end='')
- hold[:] = []
-
- def doxy_hold():
- global current, hold
- if current == '//':
- for h in hold:
- print(re.sub(r'^( *//(?!/))', r'\1/', h), end='')
- else:
- first = True
- for h in hold:
- if first:
- h = re.sub(r'^( */[*](?![*]))', r'\1*', h)
- first = False
- print(h, end='')
- hold[:] = []
-
- def process_comment(t, ind, line):
- global current, indent, hold
- if t != current or ind not in (indent, indent + ' '):
- dump_hold()
- current, indent = t, ind
- hold.append(line)
-
- def process_line(ind, line):
- global current, indent
- if ind in (indent, ''):
- doxy_hold()
- else:
- dump_hold()
- current, indent = None, None
- print(line, end='')
-
- def process(self, input, path):
- for line in input:
- ind = re.match(r'^( *)', line).group(1)
- if in_comment:
- # TODO: this is not quite right, but good enough
- m = re.match(r'^ *[*]/', line)
- if m:
- process_comment('/*', ind, line)
- in_comment = False
- else:
- process_comment('/*', ind, line)
- continue
- m = re.match(r'^ *//', line)
- if m:
- # one-line comment
- process_comment('//', ind, line)
- continue
- m = re.match(r'^ */[*]', line)
- if m:
- # multi-line comment
- process_comment('/*', ind, line)
- # TODO: this is not quite right, but good enough
- in_comment = not re.match(r'^ *[*]/', line)
- continue
- process_line(ind, line)
-
-class AutoGroup:
- def process(self, input, path):
- if '/codec2/include/' in path:
- group = 'API Codec2 API'
- elif False:
- return
- elif '/codec2/vndk/' in path:
- group = 'VNDK Platform provided glue'
- elif '/codec2/tests/' in path:
- group = 'Tests Unit tests'
- else:
- group = 'Random Misc. sandbox'
-
- print('#undef __APPLE__')
-
- for line in input:
- if re.match(r'^namespace android {', line):
- print(line, end='')
- print()
- print(r'/// \addtogroup {}'.format(group))
- print(r'/// @{')
- continue
- elif re.match(r'^} +// +namespace', line):
- print(r'/// @}')
- print()
- print(line, end='')
-
-P = AutoGroup()
-for path in sys.argv[1:]:
- with open(path, 'rt') as input:
- P.process(input, path)
diff --git a/codec2/docs/doxygen.config b/codec2/docs/doxygen.config
deleted file mode 100644
index 11a921f..0000000
--- a/codec2/docs/doxygen.config
+++ /dev/null
@@ -1,2446 +0,0 @@
-# Doxyfile 1.8.11
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed in
-# front of the TAG it is preceding.
-#
-# All text after a single hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists, items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (\" \").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
-# for the list of possible encodings.
-# The default value is: UTF-8.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
-# double-quotes, unless you are using Doxywizard) that should identify the
-# project for which the documentation is generated. This name is used in the
-# title of most generated pages and in a few other places.
-# The default value is: My Project.
-
-PROJECT_NAME = Codec2
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
-# could be handy for archiving the generated documentation or if some version
-# control system is used.
-
-PROJECT_NUMBER =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer a
-# quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF =
-
-# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
-# in the documentation. The maximum height of the logo should not exceed 55
-# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
-# the logo to the output directory.
-
-PROJECT_LOGO =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
-# into which the generated documentation will be written. If a relative path is
-# entered, it will be relative to the location where doxygen was started. If
-# left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = out/target/common/docs/codec2/api
-
-# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
-# option can be useful when feeding doxygen a huge amount of source files, where
-# putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
-# The default value is: NO.
-
-CREATE_SUBDIRS = NO
-
-# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
-# characters to appear in the names of generated files. If set to NO, non-ASCII
-# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
-# U+3044.
-# The default value is: NO.
-
-ALLOW_UNICODE_NAMES = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
-# The default value is: English.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
-# descriptions after the members that are listed in the file and class
-# documentation (similar to Javadoc). Set to NO to disable this.
-# The default value is: YES.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
-# description of a member or function before the detailed description
-#
-# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-# The default value is: YES.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator that is
-# used to form the text in various listings. Each string in this list, if found
-# as the leading text of the brief description, will be stripped from the text
-# and the result, after processing the whole list, is used as the annotated
-# text. Otherwise, the brief description is used as-is. If left blank, the
-# following values are used ($name is automatically replaced with the name of
-# the entity):The $name class, The $name widget, The $name file, is, provides,
-# specifies, contains, represents, a, an and the.
-
-ABBREVIATE_BRIEF = "The $name class" \
- "The $name widget" \
- "The $name file" \
- is \
- provides \
- specifies \
- contains \
- represents \
- a \
- an \
- the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# doxygen will generate a detailed section even if there is only a brief
-# description.
-# The default value is: NO.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-# The default value is: NO.
-
-INLINE_INHERITED_MEMB = YES
-
-# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
-# before files name in the file list and in the header files. If set to NO the
-# shortest path that makes the file name unique will be used
-# The default value is: YES.
-
-FULL_PATH_NAMES = YES
-
-# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
-# Stripping is only done if one of the specified strings matches the left-hand
-# part of the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the path to
-# strip.
-#
-# Note that you can specify absolute paths here, but also relative paths, which
-# will be relative from the directory where doxygen is started.
-# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-
-STRIP_FROM_PATH = frameworks/av/media/libstagefright/codec2
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
-# path mentioned in the documentation of a class, which tells the reader which
-# header file to include in order to use a class. If left blank only the name of
-# the header file containing the class definition is used. Otherwise one should
-# specify the list of include paths that are normally passed to the compiler
-# using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
-# less readable) file names. This can be useful is your file systems doesn't
-# support long names like on DOS, Mac, or CD-ROM.
-# The default value is: NO.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
-# first line (until the first dot) of a Javadoc-style comment as the brief
-# description. If set to NO, the Javadoc-style will behave just like regular Qt-
-# style comments (thus requiring an explicit @brief command for a brief
-# description.)
-# The default value is: NO.
-
-JAVADOC_AUTOBRIEF = YES
-
-# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
-# line (until the first dot) of a Qt-style comment as the brief description. If
-# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
-# requiring an explicit \brief command for a brief description.)
-# The default value is: NO.
-
-QT_AUTOBRIEF = YES
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
-# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
-# a brief description. This used to be the default behavior. The new default is
-# to treat a multi-line C++ comment block as a detailed description. Set this
-# tag to YES if you prefer the old behavior instead.
-#
-# Note that setting this tag to YES also means that rational rose comments are
-# not recognized any more.
-# The default value is: NO.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
-# documentation from any documented member that it re-implements.
-# The default value is: YES.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
-# page for each member. If set to NO, the documentation of a member will be part
-# of the file/class/namespace that contains it.
-# The default value is: NO.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
-# uses this value to replace tabs by spaces in code fragments.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that act as commands in
-# the documentation. An alias has the form:
-# name=value
-# For example adding
-# "sideeffect=@par Side Effects:\n"
-# will allow you to put the command \sideeffect (or @sideeffect) in the
-# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines.
-
-ALIASES =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
-# only. Doxygen will then generate output that is more tailored for C. For
-# instance, some of the names that are used will be different. The list of all
-# members will be omitted, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
-# Python sources only. Doxygen will then generate output that is more tailored
-# for that language. For instance, namespaces will be presented as packages,
-# qualified scopes will look different, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources. Doxygen will then generate output that is tailored for Fortran.
-# The default value is: NO.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for VHDL.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
-# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
-# Fortran. In the later case the parser tries to guess whether the code is fixed
-# or free formatted code, this is the default for Fortran type files), VHDL. For
-# instance to make doxygen treat .inc files as Fortran files (default is PHP),
-# and .f files as C (default is Fortran), use: inc=Fortran f=C.
-#
-# Note: For files without extension you can use no_extension as a placeholder.
-#
-# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
-
-EXTENSION_MAPPING =
-
-# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
-# according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you can
-# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
-# case of backward compatibilities issues.
-# The default value is: YES.
-
-MARKDOWN_SUPPORT = YES
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by putting a % sign in front of the word or
-# globally by setting AUTOLINK_SUPPORT to NO.
-# The default value is: YES.
-
-AUTOLINK_SUPPORT = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should set this
-# tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-# The default value is: NO.
-
-BUILTIN_STL_SUPPORT = YES
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-# The default value is: NO.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
-# will parse them like normal C++ but will assume all classes use public instead
-# of private inheritance when no explicit protection keyword is present.
-# The default value is: NO.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES will make
-# doxygen to replace the get and set methods by a property in the documentation.
-# This will only work if the methods are indeed getting or setting a simple
-# type. If this is not the case, or you want to show the methods anyway, you
-# should set this option to NO.
-# The default value is: YES.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-# The default value is: NO.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# If one adds a struct or class to a group and this option is enabled, then also
-# any nested class or struct is added to the same group. By default this option
-# is disabled and one has to add nested compounds explicitly via \ingroup.
-# The default value is: NO.
-
-GROUP_NESTED_COMPOUNDS = NO
-
-# Set the SUBGROUPING tag to YES to allow class member groups of the same type
-# (for instance a group of public functions) to be put as a subgroup of that
-# type (e.g. under the Public Functions section). Set it to NO to prevent
-# subgrouping. Alternatively, this can be done per class using the
-# \nosubgrouping command.
-# The default value is: YES.
-
-SUBGROUPING = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
-# are shown inside the group in which they are included (e.g. using \ingroup)
-# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
-# and RTF).
-#
-# Note that this feature does not work in combination with
-# SEPARATE_MEMBER_PAGES.
-# The default value is: NO.
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
-# with only public data fields or simple typedef fields will be shown inline in
-# the documentation of the scope in which they are defined (i.e. file,
-# namespace, or group documentation), provided this scope is documented. If set
-# to NO, structs, classes, and unions are shown on a separate page (for HTML and
-# Man pages) or section (for LaTeX and RTF).
-# The default value is: NO.
-
-INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
-# enum is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically be
-# useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-# The default value is: NO.
-
-TYPEDEF_HIDES_STRUCT = YES
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can be
-# an expensive process and often the same symbol appears multiple times in the
-# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
-# doxygen will become slower. If the cache is too large, memory is wasted. The
-# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
-# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
-# symbols. At the end of a run doxygen will report the cache usage and suggest
-# the optimal cache size from a speed point of view.
-# Minimum value: 0, maximum value: 9, default value: 0.
-
-LOOKUP_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
-# documentation are documented, even if no documentation was available. Private
-# class members and static file members will be hidden unless the
-# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
-# Note: This will also disable the warnings about undocumented members that are
-# normally produced when WARNINGS is set to YES.
-# The default value is: NO.
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
-# be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
-# scope will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PACKAGE = NO
-
-# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
-# included in the documentation.
-# The default value is: NO.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
-# locally in source files will be included in the documentation. If set to NO,
-# only classes defined in header files are included. Does not have any effect
-# for Java sources.
-# The default value is: YES.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. If set to YES, local methods,
-# which are defined in the implementation section but not in the interface are
-# included in the documentation. If set to NO, only methods in the interface are
-# included.
-# The default value is: NO.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base name of
-# the file that contains the anonymous namespace. By default anonymous namespace
-# are hidden.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
-# undocumented members inside documented classes or files. If set to NO these
-# members will be included in the various overviews, but no documentation
-# section is generated. This option has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy. If set
-# to NO, these classes will be included in the various overviews. This option
-# has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
-# The default value is: NO.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
-# documentation blocks found inside the body of a function. If set to NO, these
-# blocks will be appended to the function's detailed documentation block.
-# The default value is: NO.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation that is typed after a
-# \internal command is included. If the tag is set to NO then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-INTERNAL_DOCS = YES
-
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-# The default value is: system dependent.
-
-CASE_SENSE_NAMES = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
-# their full class and namespace scopes in the documentation. If set to YES, the
-# scope will be hidden.
-# The default value is: NO.
-
-HIDE_SCOPE_NAMES = YES
-
-# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
-# append additional text to a page's title, such as Class Reference. If set to
-# YES the compound reference will be hidden.
-# The default value is: NO.
-
-HIDE_COMPOUND_REFERENCE= NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
-# the files that are included by a file in the documentation of that file.
-# The default value is: YES.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
-# grouped member an include statement to the documentation, telling the reader
-# which file to include in order to use the member.
-# The default value is: NO.
-
-SHOW_GROUPED_MEMB_INC = NO
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
-# files with double quotes in the documentation rather than with sharp brackets.
-# The default value is: NO.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
-# documentation for inline members.
-# The default value is: YES.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
-# (detailed) documentation of file and class members alphabetically by member
-# name. If set to NO, the members will appear in declaration order.
-# The default value is: YES.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
-# descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO, the members will appear in declaration order. Note that
-# this will also influence the order of the classes in the class list.
-# The default value is: NO.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
-# (brief and detailed) documentation of class members so that constructors and
-# destructors are listed first. If set to NO the constructors will appear in the
-# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
-# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
-# member documentation.
-# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
-# detailed member documentation.
-# The default value is: NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
-# of group names into alphabetical order. If set to NO the group names will
-# appear in their defined order.
-# The default value is: NO.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
-# fully-qualified names, including namespaces. If set to NO, the class list will
-# be sorted only by class name, not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the alphabetical
-# list.
-# The default value is: NO.
-
-SORT_BY_SCOPE_NAME = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
-# type resolution of all parameters of a function it will reject a match between
-# the prototype and the implementation of a member function even if there is
-# only one candidate or it is obvious which candidate to choose by doing a
-# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
-# accept a match between prototype and implementation in such cases.
-# The default value is: NO.
-
-STRICT_PROTO_MATCHING = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
-# list. This list is created by putting \todo commands in the documentation.
-# The default value is: YES.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
-# list. This list is created by putting \test commands in the documentation.
-# The default value is: YES.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
-# list. This list is created by putting \bug commands in the documentation.
-# The default value is: YES.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
-# the deprecated list. This list is created by putting \deprecated commands in
-# the documentation.
-# The default value is: YES.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional documentation
-# sections, marked by \if <section_label> ... \endif and \cond <section_label>
-# ... \endcond blocks.
-
-ENABLED_SECTIONS = INTERNAL
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
-# initial value of a variable or macro / define can have for it to appear in the
-# documentation. If the initializer consists of more lines than specified here
-# it will be hidden. Use a value of 0 to hide initializers completely. The
-# appearance of the value of individual variables and macros / defines can be
-# controlled using \showinitializer or \hideinitializer command in the
-# documentation regardless of this setting.
-# Minimum value: 0, maximum value: 10000, default value: 30.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
-# the bottom of the documentation of classes and structs. If set to YES, the
-# list will mention the files that were used to generate the documentation.
-# The default value is: YES.
-
-SHOW_USED_FILES = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
-# will remove the Files entry from the Quick Index and from the Folder Tree View
-# (if specified).
-# The default value is: YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
-# page. This will remove the Namespaces entry from the Quick Index and from the
-# Folder Tree View (if specified).
-# The default value is: YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command command input-file, where command is the value of the
-# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
-# by doxygen. Whatever the program writes to standard output is used as the file
-# version. For an example see the documentation.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option. You can
-# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
-#
-# Note that if you run doxygen from a directory containing a file called
-# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
-# tag is left empty.
-
-LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
-# the reference definitions. This must be a list of .bib files. The .bib
-# extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
-# For LaTeX the style of the bibliography can be controlled using
-# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
-# search path. See also \cite for info how to create references.
-
-CITE_BIB_FILES =
-
-#---------------------------------------------------------------------------
-# Configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated to
-# standard output by doxygen. If QUIET is set to YES this implies that the
-# messages are off.
-# The default value is: NO.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
-# this implies that the warnings are on.
-#
-# Tip: Turn warnings on while writing the documentation.
-# The default value is: YES.
-
-WARNINGS = YES
-
-# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
-# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
-# will automatically be disabled.
-# The default value is: YES.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
-# The default value is: YES.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
-# are documented, but have no documentation for their parameters or return
-# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation.
-# The default value is: NO.
-
-WARN_NO_PARAMDOC = NO
-
-# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
-# The default value is: NO.
-
-WARN_AS_ERROR = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that doxygen
-# can produce. The string should contain the $file, $line, and $text tags, which
-# will be replaced by the file and line number from which the warning originated
-# and the warning text. Optionally the format may contain $version, which will
-# be replaced by the version of the file (if it could be obtained via
-# FILE_VERSION_FILTER)
-# The default value is: $file:$line: $text.
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning and error
-# messages should be written. If left blank the output is written to standard
-# error (stderr).
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag is used to specify the files and/or directories that contain
-# documented source files. You may enter file names like myfile.cpp or
-# directories like /usr/src/myproject. Separate the files or directories with
-# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
-# Note: If this tag is empty the current directory is searched.
-
-INPUT = frameworks/av/media/libstagefright/codec2/
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
-# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
-# possible encodings.
-# The default value is: UTF-8.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
-# *.h) to filter out the source-files in the directories.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# read by doxygen.
-#
-# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
-# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
-# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,
-# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
-
-FILE_PATTERNS = C2*.c \
- C2*.cpp \
- C2*.h
-
-# The RECURSIVE tag can be used to specify whether or not subdirectories should
-# be searched for input files as well.
-# The default value is: NO.
-
-RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-#
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-# The default value is: NO.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories for example use the pattern */test/*
-
-EXCLUDE_PATTERNS = ._*
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or directories
-# that contain example code fragments that are included (see the \include
-# command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank all
-# files are included.
-
-EXAMPLE_PATTERNS = *
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude commands
-# irrespective of the value of the RECURSIVE tag.
-# The default value is: NO.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or directories
-# that contain images that are to be included in the documentation (see the
-# \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command:
-#
-# <filter> <input-file>
-#
-# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
-# name of an input file. Doxygen will then use the output that the filter
-# program writes to standard output. If FILTER_PATTERNS is specified, this tag
-# will be ignored.
-#
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# properly processed by doxygen.
-
-INPUT_FILTER = frameworks/av/media/libstagefright/codec2/docs/doxyfilter.sh
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form: pattern=filter
-# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
-# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
-# patterns match the file name, INPUT_FILTER is applied.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# properly processed by doxygen.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will also be used to filter the input files that are used for
-# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
-# The default value is: NO.
-
-FILTER_SOURCE_FILES = YES
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
-# it is also possible to disable source filtering for a specific pattern using
-# *.ext= (so without naming a filter).
-# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
-# generated. Documented entities will be cross-referenced with these sources.
-#
-# Note: To get rid of all source code in the generated output, make sure that
-# also VERBATIM_HEADERS is set to NO.
-# The default value is: NO.
-
-SOURCE_BROWSER = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
-# special comment blocks from generated source code fragments. Normal C, C++ and
-# Fortran comments will always remain visible.
-# The default value is: YES.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
-# The default value is: NO.
-
-REFERENCED_BY_RELATION = YES
-
-# If the REFERENCES_RELATION tag is set to YES then for each documented function
-# all documented entities called/used by that function will be listed.
-# The default value is: NO.
-
-REFERENCES_RELATION = YES
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
-# to YES then the hyperlinks from functions in REFERENCES_RELATION and
-# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
-# link to the documentation.
-# The default value is: YES.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
-# source code will show a tooltip with additional information such as prototype,
-# brief description and links to the definition and documentation. Since this
-# will make the HTML file larger and loading of large files a bit slower, you
-# can opt to disable this feature.
-# The default value is: YES.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-SOURCE_TOOLTIPS = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code will
-# point to the HTML generated by the htags(1) tool instead of doxygen built-in
-# source browser. The htags tool is part of GNU's global source tagging system
-# (see http://www.gnu.org/software/global/global.html). You will need version
-# 4.8.6 or higher.
-#
-# To use it do the following:
-# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
-# - Make sure the INPUT points to the root of the source tree
-# - Run doxygen as normal
-#
-# Doxygen will invoke htags (and that will in turn invoke gtags), so these
-# tools must be available from the command line (i.e. in the search path).
-#
-# The result: instead of the source browser generated by doxygen, the links to
-# source code will now point to the output of htags.
-# The default value is: NO.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
-# verbatim copy of the header file for each class for which an include is
-# specified. Set to NO to disable this.
-# See also: Section \class.
-# The default value is: YES.
-
-VERBATIM_HEADERS = YES
-
-# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
-# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
-# cost of reduced performance. This can be particularly helpful with template
-# rich C++ code for which doxygen's built-in parser lacks the necessary type
-# information.
-# Note: The availability of this option depends on whether or not doxygen was
-# generated with the -Duse-libclang=ON option for CMake.
-# The default value is: NO.
-
-CLANG_ASSISTED_PARSING = YES
-
-# If clang assisted parsing is enabled you can provide the compiler with command
-# line options that you would normally use when invoking the compiler. Note that
-# the include paths will already be set by doxygen for the files and directories
-# specified with INPUT and INCLUDE_PATH.
-# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
-
-CLANG_OPTIONS = -std=c++11
-
-#---------------------------------------------------------------------------
-# Configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
-# compounds will be generated. Enable this if the project contains a lot of
-# classes, structs, unions or interfaces.
-# The default value is: YES.
-
-ALPHABETICAL_INDEX = YES
-
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
-# The default value is: YES.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
-# generated HTML page (for example: .htm, .php, .asp).
-# The default value is: .html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
-# each generated HTML page. If the tag is left blank doxygen will generate a
-# standard header.
-#
-# To get valid HTML the header file that includes any scripts and style sheets
-# that doxygen needs, which is dependent on the configuration options used (e.g.
-# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
-# default header using
-# doxygen -w html new_header.html new_footer.html new_stylesheet.css
-# YourConfigFile
-# and then modify the file new_header.html. See also section "Doxygen usage"
-# for information on how to generate the default header that doxygen normally
-# uses.
-# Note: The header is subject to change so you typically have to regenerate the
-# default header when upgrading to a newer version of doxygen. For a description
-# of the possible markers and block names see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
-# generated HTML page. If the tag is left blank doxygen will generate a standard
-# footer. See HTML_HEADER for more information on how to generate a default
-# footer and what special commands can be used inside the footer. See also
-# section "Doxygen usage" for information on how to generate the default footer
-# that doxygen normally uses.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
-# sheet that is used by each HTML page. It can be used to fine-tune the look of
-# the HTML output. If left blank doxygen will generate a default style sheet.
-# See also section "Doxygen usage" for information on how to generate the style
-# sheet that doxygen normally uses.
-# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
-# it is more robust and this tag (HTML_STYLESHEET) will in the future become
-# obsolete.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_STYLESHEET =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
-# cascading style sheets that are included after the standard style sheets
-# created by doxygen. Using this option one can overrule certain style aspects.
-# This is preferred over using HTML_STYLESHEET since it does not replace the
-# standard style sheet and is therefore more robust against future updates.
-# Doxygen will copy the style sheet files to the output directory.
-# Note: The order of the extra style sheet files is of importance (e.g. the last
-# style sheet in the list overrules the setting of the previous ones in the
-# list). For an example see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_STYLESHEET =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
-# files will be copied as-is; there are no commands or markers available.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_FILES =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
-# will adjust the colors in the style sheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
-# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
-# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
-# purple, and 360 is red again.
-# Minimum value: 0, maximum value: 359, default value: 220.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_HUE = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
-# value of 255 will produce the most vivid colors.
-# Minimum value: 0, maximum value: 255, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
-# luminance component of the colors in the HTML output. Values below 100
-# gradually make the output lighter, whereas values above 100 make the output
-# darker. The value divided by 100 is the actual gamma applied, so 80 represents
-# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
-# change the gamma.
-# Minimum value: 40, maximum value: 240, default value: 80.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_GAMMA = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting this
-# to YES can help to show when doxygen was last run and thus if the
-# documentation is up to date.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP = NO
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_SECTIONS = YES
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
-# shown in the various tree structured indices initially; the user can expand
-# and collapse entries dynamically later on. Doxygen will expand the tree to
-# such a level that at most the specified number of entries are visible (unless
-# a fully collapsed tree already exceeds this amount). So setting the number of
-# entries 1 will produce a full collapsed tree by default. 0 is a special value
-# representing an infinite number of entries and will result in a full expanded
-# tree by default.
-# Minimum value: 0, maximum value: 9999, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files will be
-# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: http://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_DOCSET = NO
-
-# This tag determines the name of the docset feed. A documentation feed provides
-# an umbrella under which multiple documentation sets from a single provider
-# (such as a company or product suite) can be grouped.
-# The default value is: Doxygen generated docs.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# This tag specifies a string that should uniquely identify the documentation
-# set bundle. This should be a reverse domain-name style string, e.g.
-# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-# The default value is: org.doxygen.Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
-# The default value is: Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
-# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
-# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
-#
-# The HTML Help Workshop contains a compiler that can convert all HTML output
-# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
-# files are now used as the Windows 98 help format, and will replace the old
-# Windows help format (.hlp) on all Windows platforms in the future. Compressed
-# HTML files also contain an index, a table of contents, and you can search for
-# words in the documentation. The HTML workshop also contains a viewer for
-# compressed HTML files.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_HTMLHELP = NO
-
-# The CHM_FILE tag can be used to specify the file name of the resulting .chm
-# file. You can add a path in front of the file if the result should not be
-# written to the html output directory.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_FILE =
-
-# The HHC_LOCATION tag can be used to specify the location (absolute path
-# including file name) of the HTML help compiler (hhc.exe). If non-empty,
-# doxygen will try to run the HTML help compiler on the generated index.hhp.
-# The file has to be specified with full path.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-HHC_LOCATION =
-
-# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-GENERATE_CHI = NO
-
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
-# and project file content.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_INDEX_ENCODING =
-
-# The BINARY_TOC flag controls whether a binary table of contents is generated
-# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
-# enables the Previous and Next buttons.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members to
-# the table of contents of the HTML help documentation and to the tree view.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
-# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
-# (.qch) of the generated HTML documentation.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
-# the file name of the resulting .qch file. The path specified is relative to
-# the HTML output folder.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
-# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
-# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
-# folders).
-# The default value is: doc.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
-# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_SECT_FILTER_ATTRS =
-
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
-# generated, together with the HTML files, they form an Eclipse help plugin. To
-# install this plugin and make it available under the help contents menu in
-# Eclipse, the contents of the directory containing the HTML and XML files needs
-# to be copied into the plugins directory of eclipse. The name of the directory
-# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
-# After copying Eclipse needs to be restarted before the help appears.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the Eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have this
-# name. Each documentation set should have its own identifier.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# If you want full control over the layout of the generated HTML pages it might
-# be necessary to disable the index and replace it with your own. The
-# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
-# of each HTML page. A value of NO enables the index and the value YES disables
-# it. Since the tabs in the index contain the same information as the navigation
-# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-DISABLE_INDEX = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information. If the tag
-# value is set to YES, a side panel will be generated containing a tree-like
-# index structure (just like the one that is generated for HTML Help). For this
-# to work a browser that supports JavaScript, DHTML, CSS and frames is required
-# (i.e. any modern browser). Windows users are probably better off using the
-# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_TREEVIEW = YES
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
-# doxygen will group on one line in the generated HTML documentation.
-#
-# Note that a value of 0 will completely suppress the enum values from appearing
-# in the overview section.
-# Minimum value: 0, maximum value: 20, default value: 4.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-ENUM_VALUES_PER_LINE = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
-# to set the initial width (in pixels) of the frame in which the tree is shown.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-TREEVIEW_WIDTH = 250
-
-# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
-# external symbols imported via tag files in a separate window.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of LaTeX formulas included as images in
-# the HTML documentation. When you change the font size after a successful
-# doxygen run you need to manually remove any form_*.png images from the HTML
-# output directory to force them to be regenerated.
-# Minimum value: 8, maximum value: 50, default value: 10.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_TRANSPARENT = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# http://www.mathjax.org) which uses client side Javascript for the rendering
-# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
-# installed or if you want to formulas look prettier in the HTML output. When
-# enabled you may also need to install MathJax separately and configure the path
-# to it using the MATHJAX_RELPATH option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-USE_MATHJAX = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
-# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
-# The default value is: HTML-CSS.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_FORMAT = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the HTML
-# output directory using the MATHJAX_RELPATH option. The destination directory
-# should contain the MathJax.js script. For instance, if the mathjax directory
-# is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
-# Content Delivery Network so you can quickly see the result without installing
-# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
-# extension names that should be enabled during MathJax rendering. For example
-# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_EXTENSIONS =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
-# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
-# example see the documentation.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_CODEFILE =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
-# the HTML output. The underlying search engine uses javascript and DHTML and
-# should work on any modern browser. Note that when using HTML help
-# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
-# there is already a search function so this one should typically be disabled.
-# For large projects the javascript based search engine can be slow, then
-# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
-# search using the keyboard; to jump to the search box use <access key> + S
-# (what the <access key> is depends on the OS and browser, but it is typically
-# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
-# key> to jump into the search results window, the results can be navigated
-# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
-# the search. The filter options can be selected when the cursor is inside the
-# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
-# to select a filter and <Enter> or <escape> to activate or cancel the filter
-# option.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-SEARCHENGINE = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
-# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
-# setting. When disabled, doxygen will generate a PHP script for searching and
-# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
-# and searching needs to be provided by external tools. See the section
-# "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SERVER_BASED_SEARCH = NO
-
-# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
-# search results.
-#
-# Doxygen ships with an example indexer (doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/).
-#
-# See the section "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH = NO
-
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will return the search results when EXTERNAL_SEARCH is enabled.
-#
-# Doxygen ships with an example indexer (doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/). See the section "External Indexing and
-# Searching" for details.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHENGINE_URL =
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
-# SEARCHDATA_FILE tag the name of this file can be specified.
-# The default file is: searchdata.xml.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHDATA_FILE = searchdata.xml
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
-# projects and redirect the results back to the right project.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH_ID =
-
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
-# to a relative location where the documentation can be found. The format is:
-# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTRA_SEARCH_MAPPINGS =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
-# The default value is: YES.
-
-GENERATE_LATEX = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked.
-#
-# Note that when enabling USE_PDFLATEX this option is only used for generating
-# bitmaps for formulas in the HTML output, but not in the Makefile that is
-# written to the output directory.
-# The default file is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
-# index for LaTeX.
-# The default file is: makeindex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
-# documents. This may be useful for small projects and may help to save some
-# trees in general.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used by the
-# printer.
-# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
-# 14 inches) and executive (7.25 x 10.5 inches).
-# The default value is: a4.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PAPER_TYPE = a4
-
-# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
-# that should be included in the LaTeX output. The package can be specified just
-# by its name or with the correct syntax as to be used with the LaTeX
-# \usepackage command. To get the times font for instance you can specify :
-# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
-# To use the option intlimits with the amsmath package you can specify:
-# EXTRA_PACKAGES=[intlimits]{amsmath}
-# If left blank no extra packages will be included.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
-# generated LaTeX document. The header should contain everything until the first
-# chapter. If it is left blank doxygen will generate a standard header. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
-#
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
-# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
-# string, for the replacement values of the other commands the user is referred
-# to HTML_HEADER.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HEADER =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
-# generated LaTeX document. The footer should contain everything after the last
-# chapter. If it is left blank doxygen will generate a standard footer. See
-# LATEX_HEADER for more information on how to generate a default footer and what
-# special commands can be used inside the footer.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_FOOTER =
-
-# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
-# LaTeX style sheets that are included after the standard style sheets created
-# by doxygen. Using this option one can overrule certain style aspects. Doxygen
-# will copy the style sheet files to the output directory.
-# Note: The order of the extra style sheet files is of importance (e.g. the last
-# style sheet in the list overrules the setting of the previous ones in the
-# list).
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EXTRA_STYLESHEET =
-
-# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the LATEX_OUTPUT output
-# directory. Note that the files will be copied as-is; there are no commands or
-# markers available.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EXTRA_FILES =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
-# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
-# contain links (just like the HTML output) instead of page references. This
-# makes the output suitable for online browsing using a PDF viewer.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PDF_HYPERLINKS = YES
-
-# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES, to get a
-# higher quality PDF documentation.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-USE_PDFLATEX = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
-# command to the generated LaTeX files. This will instruct LaTeX to keep running
-# if errors occur, instead of asking the user for help. This option is also used
-# when generating formulas in HTML.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BATCHMODE = NO
-
-# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
-# index chapters (such as File Index, Compound Index, etc.) in the output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HIDE_INDICES = NO
-
-# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
-# code with syntax highlighting in the LaTeX output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_SOURCE_CODE = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. See
-# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
-# The default value is: plain.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BIB_STYLE = plain
-
-# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
-# page will contain the date and time when the page was generated. Setting this
-# to NO can help when comparing the output of multiple runs.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_TIMESTAMP = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
-# RTF output is optimized for Word 97 and may not look too pretty with other RTF
-# readers/editors.
-# The default value is: NO.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: rtf.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
-# documents. This may be useful for small projects and may help to save some
-# trees in general.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
-# contain hyperlink fields. The RTF file will contain links (just like the HTML
-# output) instead of page references. This makes the output suitable for online
-# browsing using Word or some other Word compatible readers that support those
-# fields.
-#
-# Note: WordPad (write) and others do not support links.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's config
-# file, i.e. a series of assignments. You only have to provide replacements,
-# missing definitions are set to their default value.
-#
-# See also section "Doxygen usage" for information on how to generate the
-# default style sheet that doxygen normally uses.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_EXTENSIONS_FILE =
-
-# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
-# with syntax highlighting in the RTF output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_SOURCE_CODE = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
-# classes and files.
-# The default value is: NO.
-
-GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it. A directory man3 will be created inside the directory specified by
-# MAN_OUTPUT.
-# The default directory is: man.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to the generated
-# man pages. In case the manual section does not start with a number, the number
-# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
-# optional.
-# The default value is: .3.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_EXTENSION = .3
-
-# The MAN_SUBDIR tag determines the name of the directory created within
-# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
-# MAN_EXTENSION with the initial . removed.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_SUBDIR =
-
-# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
-# will generate one additional man file for each entity documented in the real
-# man page(s). These additional files only source the real man page, but without
-# them the man command would be unable to find the correct page.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
-# captures the structure of the code including all documentation.
-# The default value is: NO.
-
-GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: xml.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_OUTPUT = xml
-
-# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
-# listings (including syntax highlighting and cross-referencing information) to
-# the XML output. Note that enabling this will significantly increase the size
-# of the XML output.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the DOCBOOK output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
-# that can be used to generate PDF.
-# The default value is: NO.
-
-GENERATE_DOCBOOK = NO
-
-# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# front of it.
-# The default directory is: docbook.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_OUTPUT = docbook
-
-# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
-# program listings (including syntax highlighting and cross-referencing
-# information) to the DOCBOOK output. Note that enabling this will significantly
-# increase the size of the DOCBOOK output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_PROGRAMLISTING = NO
-
-#---------------------------------------------------------------------------
-# Configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sf.net) file that captures the
-# structure of the code including all documentation. Note that this feature is
-# still experimental and incomplete at the moment.
-# The default value is: NO.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
-# file that captures the structure of the code including all documentation.
-#
-# Note that this feature is still experimental and incomplete at the moment.
-# The default value is: NO.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
-# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
-# output from the Perl module output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
-# formatted so it can be parsed by a human reader. This is useful if you want to
-# understand what is going on. On the other hand, if this tag is set to NO, the
-# size of the Perl module output will be much smaller and Perl will parse it
-# just the same.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file are
-# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
-# so different doxyrules.make files included by the same Makefile don't
-# overwrite each other's variables.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
-# C-preprocessor directives found in the sources and include files.
-# The default value is: YES.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
-# in the source code. If set to NO, only conditional compilation will be
-# performed. Macro expansion can be done in a controlled way by setting
-# EXPAND_ONLY_PREDEF to YES.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-MACRO_EXPANSION = YES
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
-# the macro expansion is limited to the macros specified with the PREDEFINED and
-# EXPAND_AS_DEFINED tags.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_ONLY_PREDEF = YES
-
-# If the SEARCH_INCLUDES tag is set to YES, the include files in the
-# INCLUDE_PATH will be searched if a #include is found.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by the
-# preprocessor.
-# This tag requires that the tag SEARCH_INCLUDES is set to YES.
-
-INCLUDE_PATH = /Volumes/A/aosp/prebuilts/clang/darwin-x86/host/3.6/lib/clang/3.6/include \
- /Volumes/A/aosp/external/libcxx/include \
- /Volumes/A/aosp/bionic/libc/include \
- /Volumes/A/aosp/bionic/libc/kernel/uapi \
- /Volumes/A/aosp/bionic/libc/kernel/uapi/asm-arm64 \
- /Volumes/A/aosp/external/gtest
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will be
-# used.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that are
-# defined before the preprocessor is started (similar to the -D option of e.g.
-# gcc). The argument of the tag is a list of macros of the form: name or
-# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
-# is assumed. To prevent a macro definition from being undefined via #undef or
-# recursively expanded use the := operator instead of the = operator.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-PREDEFINED = __APPLE__= \
- __ANDROID__=1 \
- ANDROID:=1 \
- __unused=
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
-# tag can be used to specify a list of macro names that should be expanded. The
-# macro definition that is found in the sources will be used. Use the PREDEFINED
-# tag if you want to use a different macro definition that overrules the
-# definition found in the source code.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_AS_DEFINED = DEFINE_FLEXIBLE_METHODS \
- DEFINE_CAST_OPERATORS
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
-# remove all references to function-like macros that are alone on a line, have
-# an all uppercase name, and do not end with a semicolon. Such function macros
-# are typically used for boiler-plate code, and will confuse the parser if not
-# removed.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES tag can be used to specify one or more tag files. For each tag
-# file the location of the external documentation should be added. The format of
-# a tag file without this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where loc1 and loc2 can be relative or absolute paths or URLs. See the
-# section "Linking to external documentation" for more information about the use
-# of tag files.
-# Note: Each tag file must have a unique name (where the name does NOT include
-# the path). If a tag file is not located in the directory in which doxygen is
-# run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
-# tag file that is based on the input files it reads. See section "Linking to
-# external documentation" for more information about the usage of tag files.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
-# the class index. If set to NO, only the inherited external classes will be
-# listed.
-# The default value is: NO.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will be
-# listed.
-# The default value is: YES.
-
-EXTERNAL_GROUPS = YES
-
-# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
-# the related pages index. If set to NO, only the current project's pages will
-# be listed.
-# The default value is: YES.
-
-EXTERNAL_PAGES = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
-# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
-# NO turns the diagrams off. Note that this option also works with HAVE_DOT
-# disabled, but it is recommended to install and use dot, since it yields more
-# powerful graphs.
-# The default value is: YES.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# You can include diagrams made with dia in doxygen documentation. Doxygen will
-# then run dia to produce the diagram and insert it in the documentation. The
-# DIA_PATH tag allows you to specify the directory where the dia binary resides.
-# If left empty dia is assumed to be found in the default search path.
-
-DIA_PATH =
-
-# If set to YES the inheritance and collaboration graphs will hide inheritance
-# and usage relations if the target is undocumented or is not a class.
-# The default value is: YES.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz (see:
-# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
-# Bell Labs. The other options in this section have no effect if this option is
-# set to NO
-# The default value is: NO.
-
-HAVE_DOT = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
-# to run in parallel. When set to 0 doxygen will base this on the number of
-# processors available in the system. You can set it explicitly to a value
-# larger than 0 to get control over the balance between CPU load and processing
-# speed.
-# Minimum value: 0, maximum value: 32, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_NUM_THREADS = 0
-
-# When you want a differently looking font in the dot files that doxygen
-# generates you can specify the font name using DOT_FONTNAME. You need to make
-# sure dot is able to find the font, which can be done by putting it in a
-# standard location or by setting the DOTFONTPATH environment variable or by
-# setting DOT_FONTPATH to the directory containing the font.
-# The default value is: Helvetica.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTNAME = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
-# dot graphs.
-# Minimum value: 4, maximum value: 24, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the default font as specified with
-# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
-# the path where dot can find it using this tag.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
-# each documented class showing the direct and indirect inheritance relations.
-# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
-# graph for each documented class showing the direct and indirect implementation
-# dependencies (inheritance, containment, and class references variables) of the
-# class with other documented classes.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LOOK = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
-# class node. If there are many fields or methods and many nodes the graph may
-# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
-# number of items for each type to make the size more manageable. Set this to 0
-# for no limit. Note that the threshold may be exceeded by 50% before the limit
-# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
-# but if the number exceeds 15, the total amount of fields shown is limited to
-# 10.
-# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LIMIT_NUM_FIELDS = 10
-
-# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
-# collaboration graphs will show the relations between templates and their
-# instances.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-TEMPLATE_RELATIONS = NO
-
-# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
-# YES then doxygen will generate a graph for each documented file showing the
-# direct and indirect include dependencies of the file with other documented
-# files.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDE_GRAPH = YES
-
-# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
-# set to YES then doxygen will generate a graph for each documented file showing
-# the direct and indirect include dependencies of the file with other documented
-# files.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
-# dependency graph for every global function or class method.
-#
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable call graphs for selected
-# functions only using the \callgraph command. Disabling a call graph can be
-# accomplished by means of the command \hidecallgraph.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALL_GRAPH = NO
-
-# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
-# dependency graph for every global function or class method.
-#
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable caller graphs for selected
-# functions only using the \callergraph command. Disabling a caller graph can be
-# accomplished by means of the command \hidecallergraph.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
-# hierarchy of all classes instead of a textual one.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
-# dependencies a directory has on other directories in a graphical way. The
-# dependency relations are determined by the #include relations between the
-# files in the directories.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. For an explanation of the image formats see the section
-# output formats in the documentation of the dot tool (Graphviz (see:
-# http://www.graphviz.org/)).
-# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
-# to make the SVG files visible in IE 9+ (other browsers do not have this
-# requirement).
-# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
-# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
-# png:gdiplus:gdiplus.
-# The default value is: png.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_IMAGE_FORMAT = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-#
-# Note that this requires a modern browser other than Internet Explorer. Tested
-# and working are Firefox, Chrome, Safari, and Opera.
-# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
-# the SVG files visible. Older versions of IE do not have SVG support.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INTERACTIVE_SVG = NO
-
-# The DOT_PATH tag can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the \dotfile
-# command).
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOTFILE_DIRS =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the \mscfile
-# command).
-
-MSCFILE_DIRS =
-
-# The DIAFILE_DIRS tag can be used to specify one or more directories that
-# contain dia files that are included in the documentation (see the \diafile
-# command).
-
-DIAFILE_DIRS =
-
-# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
-# path where java can find the plantuml.jar file. If left blank, it is assumed
-# PlantUML is not used or called during a preprocessing step. Doxygen will
-# generate a warning when it encounters a \startuml command in this case and
-# will not generate output for the diagram.
-
-PLANTUML_JAR_PATH =
-
-# When using plantuml, the specified paths are searched for files specified by
-# the !include statement in a plantuml block.
-
-PLANTUML_INCLUDE_PATH =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
-# that will be shown in the graph. If the number of nodes in a graph becomes
-# larger than this value, doxygen will truncate the graph, which is visualized
-# by representing a node as a red box. Note that doxygen if the number of direct
-# children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
-# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-# Minimum value: 0, maximum value: 10000, default value: 50.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
-# generated by dot. A depth value of 3 means that only nodes reachable from the
-# root by following a path via at most 3 edges will be shown. Nodes that lay
-# further from the root node will be omitted. Note that setting this option to 1
-# or 2 may greatly reduce the computation time needed for large code bases. Also
-# note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-# Minimum value: 0, maximum value: 1000, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-MAX_DOT_GRAPH_DEPTH = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not seem
-# to support this out of the box.
-#
-# Warning: Depending on the platform used, enabling this option may lead to
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
-# read).
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10) support
-# this, this feature is disabled by default.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
-# explaining the meaning of the various boxes and arrows in the dot generated
-# graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
-# files that are used to generate the various graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_CLEANUP = YES
diff --git a/codec2/faultinjection/Android.bp b/codec2/faultinjection/Android.bp
new file mode 100644
index 0000000..ef7871e
--- /dev/null
+++ b/codec2/faultinjection/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+ name: "libc2_component_wrapper",
+ vendor_available: true,
+
+ srcs: [
+ "C2ComponentWrapper.cpp",
+ "SimpleMethodState.cpp",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
diff --git a/codec2/faultinjection/C2ComponentWrapper.cpp b/codec2/faultinjection/C2ComponentWrapper.cpp
new file mode 100644
index 0000000..c45f8bf
--- /dev/null
+++ b/codec2/faultinjection/C2ComponentWrapper.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2ComponentWrapper"
+
+#include <chrono>
+#include <functional>
+#include <thread>
+
+#include <C2ComponentWrapper.h>
+#include <C2Config.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::chrono_literals;
+
+c2_status_t WrapSimpleMethod(
+ std::function<c2_status_t(void)> op, const SimpleMethodState &state) {
+ c2_status_t result = C2_OK;
+ switch (state.getMode()) {
+ case SimpleMethodState::EXECUTE:
+ result = op();
+ break;
+ case SimpleMethodState::NO_OP:
+ break;
+ case SimpleMethodState::HANG:
+ while (true) {
+ std::this_thread::sleep_for(1s);
+ }
+ break;
+ }
+ (void)state.overrideResult(&result);
+ return result;
+}
+
+} // namespace
+
+C2ComponentWrapper::Injecter::Injecter(C2ComponentWrapper *thiz) : mThiz(thiz) {}
+
+SimpleMethodState::Injecter C2ComponentWrapper::Injecter::start() {
+ return SimpleMethodState::Injecter(&mThiz->mStartState);
+}
+
+C2ComponentWrapper::Listener::Listener(
+ const std::shared_ptr<C2Component::Listener> &listener) : mListener(listener) {}
+
+void C2ComponentWrapper::Listener::onWorkDone_nb(std::weak_ptr<C2Component> component,
+ std::list<std::unique_ptr<C2Work>> workItems) {
+ mListener->onWorkDone_nb(component, std::move(workItems));
+}
+
+void C2ComponentWrapper::Listener::onTripped_nb(std::weak_ptr<C2Component> component,
+ std::vector<std::shared_ptr<C2SettingResult>> settingResult) {
+ mListener->onTripped_nb(component,settingResult);
+}
+
+void C2ComponentWrapper::Listener::onError_nb(
+ std::weak_ptr<C2Component> component, uint32_t errorCode) {
+ mListener->onError_nb(component, errorCode);
+}
+
+C2ComponentWrapper::C2ComponentWrapper(
+ const std::shared_ptr<C2Component> &comp) : mComp(comp) {}
+
+c2_status_t C2ComponentWrapper::setListener_vb(
+ const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
+ mListener = std::make_shared<Listener>(listener);
+ return mComp->setListener_vb(mListener, mayBlock);
+}
+
+c2_status_t C2ComponentWrapper::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
+ return mComp->queue_nb(items);
+}
+
+c2_status_t C2ComponentWrapper::announce_nb(const std::vector<C2WorkOutline> &items) {
+ return mComp->announce_nb(items);
+}
+
+c2_status_t C2ComponentWrapper::flush_sm(
+ C2Component::flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+ return mComp->flush_sm(mode, flushedWork);
+}
+
+c2_status_t C2ComponentWrapper::drain_nb(C2Component::drain_mode_t mode) {
+ return mComp->drain_nb(mode);
+}
+
+c2_status_t C2ComponentWrapper::start() {
+ return WrapSimpleMethod([this] { return mComp->start(); }, mStartState);
+}
+
+c2_status_t C2ComponentWrapper::stop() {
+ return mComp->stop();
+}
+
+c2_status_t C2ComponentWrapper::reset() {
+ return mComp->reset();
+}
+
+c2_status_t C2ComponentWrapper::release() {
+ return mComp->release();
+}
+
+std::shared_ptr<C2ComponentInterface> C2ComponentWrapper::intf(){
+ return mComp->intf();
+}
+
+C2ComponentWrapper::Injecter C2ComponentWrapper::inject() {
+ return Injecter(this);
+}
+
+} // namespace android
diff --git a/codec2/faultinjection/C2ComponentWrapper.h b/codec2/faultinjection/C2ComponentWrapper.h
new file mode 100644
index 0000000..737350d
--- /dev/null
+++ b/codec2/faultinjection/C2ComponentWrapper.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2_COMPONENT_WRAPPER_H_
+#define C2_COMPONENT_WRAPPER_H_
+
+#include <C2Component.h>
+
+#include "SimpleMethodState.h"
+
+namespace android {
+
+/**
+ * Creates a Wrapper around the class C2Component and its methods. The wrapper is used to
+ * simulate errors in the android media components by fault injection technique.
+ * This is done to check how the framework handles the error situation.
+ */
+class C2ComponentWrapper
+ : public C2Component, public std::enable_shared_from_this<C2ComponentWrapper> {
+public:
+ class Injecter {
+ public:
+ explicit Injecter(C2ComponentWrapper *thiz);
+
+ SimpleMethodState::Injecter start();
+ private:
+ C2ComponentWrapper *const mThiz;
+ };
+
+ /**
+ * A wrapper around the listener class inside C2Component class.
+ */
+ class Listener : public C2Component::Listener {
+ public:
+ explicit Listener(const std::shared_ptr<C2Component::Listener> &listener);
+ virtual ~Listener() = default;
+
+ void onWorkDone_nb(std::weak_ptr<C2Component> component,
+ std::list<std::unique_ptr<C2Work>> workItems) override;
+ void onTripped_nb(std::weak_ptr<C2Component> component,
+ std::vector<std::shared_ptr<C2SettingResult>> settingResult) override;
+ void onError_nb(std::weak_ptr<C2Component> component, uint32_t errorCode) override;
+
+ private:
+ std::shared_ptr<C2Component::Listener> mListener;
+ };
+
+ explicit C2ComponentWrapper(const std::shared_ptr<C2Component> &comp);
+ virtual ~C2ComponentWrapper() = default;
+
+ virtual c2_status_t setListener_vb(
+ const std::shared_ptr<C2Component::Listener> &listener,
+ c2_blocking_t mayBlock) override;
+ virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
+ virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
+ virtual c2_status_t flush_sm(
+ flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
+ virtual c2_status_t drain_nb(drain_mode_t mode) override;
+ virtual c2_status_t start() override;
+ virtual c2_status_t stop() override;
+ virtual c2_status_t reset() override;
+ virtual c2_status_t release() override;
+ virtual std::shared_ptr<C2ComponentInterface> intf() override;
+
+ Injecter inject();
+
+private:
+ std::shared_ptr<Listener> mListener;
+ std::shared_ptr<C2Component> mComp;
+
+ SimpleMethodState mStartState;
+};
+
+} // namespace android
+
+#endif // C2_COMPONENT_WRAPPER_H_
diff --git a/codec2/faultinjection/SimpleMethodState.cpp b/codec2/faultinjection/SimpleMethodState.cpp
new file mode 100644
index 0000000..179d64e
--- /dev/null
+++ b/codec2/faultinjection/SimpleMethodState.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "SimpleMethodState"
+#include <log/log.h>
+
+#include "SimpleMethodState.h"
+
+namespace android {
+
+SimpleMethodState::Injecter::Injecter(SimpleMethodState *thiz) : mThiz(thiz) {}
+
+void SimpleMethodState::Injecter::hang() {
+ mThiz->mMode = HANG;
+}
+
+void SimpleMethodState::Injecter::fail(c2_status_t err, bool execute) {
+ mThiz->mMode = execute ? EXECUTE : NO_OP;
+ mThiz->mOverride = true;
+ mThiz->mResultOverride = err;
+}
+
+SimpleMethodState::SimpleMethodState()
+ : mMode(EXECUTE), mOverride(false), mResultOverride(C2_OK) {}
+
+SimpleMethodState::Mode SimpleMethodState::getMode() const {
+ return mMode;
+}
+
+bool SimpleMethodState::overrideResult(c2_status_t *result) const {
+ if (!mOverride) {
+ return false;
+ }
+ *result = mResultOverride;
+ return true;
+}
+
+} // namespace android
diff --git a/codec2/faultinjection/SimpleMethodState.h b/codec2/faultinjection/SimpleMethodState.h
new file mode 100644
index 0000000..dc0459d
--- /dev/null
+++ b/codec2/faultinjection/SimpleMethodState.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_METHOD_STATE_H_
+#define SIMPLE_METHOD_STATE_H_
+
+#include <C2.h>
+
+namespace android {
+
+/**
+ * State for a simple method which returns c2_status_t and takes no parameters.
+ */
+class SimpleMethodState {
+public:
+ enum Mode {
+ // Execute the normal operation
+ EXECUTE,
+ // Don't do anything
+ NO_OP,
+ // Hang; never return
+ HANG,
+ };
+
+ /**
+ * Injecter class that modifies the internal states of this class.
+ */
+ class Injecter {
+ public:
+ explicit Injecter(SimpleMethodState *thiz);
+
+ /**
+ * Hang the operation.
+ */
+ void hang();
+
+ /**
+ * Fail the operation with given params.
+ *
+ * \param err error code to replace the actual return value
+ * \param execute whether the wrapper should execute the operation
+ */
+ void fail(c2_status_t err, bool execute = false);
+
+ private:
+ SimpleMethodState *const mThiz;
+ };
+
+ SimpleMethodState();
+
+ /**
+ * Get execution mode.
+ */
+ Mode getMode() const;
+
+ /**
+ * Override result from running the operation if configured so.
+ */
+ bool overrideResult(c2_status_t *result) const;
+
+private:
+ Mode mMode;
+ bool mOverride;
+ c2_status_t mResultOverride;
+};
+
+} // namespace android
+
+#endif // SIMPLE_METHOD_STATE_H_
diff --git a/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioDecTest.cpp b/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioDecTest.cpp
index f9ae8ba..1e87f38 100644
--- a/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioDecTest.cpp
+++ b/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioDecTest.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <algorithm>
#include <stdio.h>
#include <fstream>
@@ -133,6 +134,8 @@ class Codec2AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
}
mEos = false;
mFramesReceived = 0;
+ mTimestampUs = 0u;
+ mTimestampDevTest = false;
if (mCompName == unknown_comp) mDisableTest = true;
if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
}
@@ -146,38 +149,39 @@ class Codec2AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
Super::TearDown();
}
+ struct outputMetaData {
+ uint64_t timestampUs;
+ uint32_t rangeLength;
+ };
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
- // handle configuration changes in work done
- if (!work->worklets.empty() &&
- (work->worklets.front()->output.configUpdate.size() != 0)) {
- ALOGV("Config Update");
- std::vector<std::unique_ptr<C2Param>> updates =
- std::move(work->worklets.front()->output.configUpdate);
- std::vector<C2Param*> configParam;
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- for (size_t i = 0; i < updates.size(); ++i) {
- C2Param* param = updates[i].get();
- if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) ||
- (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE)) {
- configParam.push_back(param);
+ if (!work->worklets.empty()) {
+ // For decoder components current timestamp always exceeds
+ // previous timestamp
+ bool codecConfig = ((work->worklets.front()->output.flags &
+ C2FrameData::FLAG_CODEC_CONFIG) != 0);
+ if (!codecConfig &&
+ !work->worklets.front()->output.buffers.empty()) {
+ EXPECT_GE(work->worklets.front()->output.ordinal.timestamp.peeku(),
+ mTimestampUs);
+ mTimestampUs =
+ work->worklets.front()->output.ordinal.timestamp.peeku();
+ uint32_t rangeLength =
+ work->worklets.front()->output.buffers[0]->data()
+ .linearBlocks().front().map().get().capacity();
+ //List of timestamp values and output size to calculate timestamp
+ if (mTimestampDevTest) {
+ outputMetaData meta = {mTimestampUs, rangeLength};
+ oBufferMetaData.push_back(meta);
}
}
- mComponent->config(configParam, C2_DONT_BLOCK, &failures);
- ASSERT_EQ(failures.size(), 0u);
+ bool mCsd = false;
+ workDone(mComponent, work, mFlushedIndices, mQueueLock,
+ mQueueCondition, mWorkQueue, mEos, mCsd,
+ mFramesReceived);
+ (void)mCsd;
}
- mFramesReceived++;
- mEos = (work->worklets.front()->output.flags &
- C2FrameData::FLAG_END_OF_STREAM) != 0;
- ALOGV("WorkDone: frameID received %d",
- (int)work->worklets.front()->output.ordinal.frameIndex.peeku());
- work->input.buffers.clear();
- work->worklets.clear();
- typedef std::unique_lock<std::mutex> ULock;
- ULock l(mQueueLock);
- mWorkQueue.push_back(std::move(work));
- mQueueCondition.notify_all();
}
}
@@ -200,8 +204,14 @@ class Codec2AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
bool mEos;
bool mDisableTest;
+ bool mTimestampDevTest;
standardComp mCompName;
+ uint64_t mTimestampUs;
uint32_t mFramesReceived;
+ std::list<uint64_t> mFlushedIndices;
+ std::list<uint64_t> mTimestampUslist;
+ ::android::List<outputMetaData> oBufferMetaData;
+
C2BlockPool::local_id_t mBlockPoolId;
std::shared_ptr<C2BlockPool> mLinearPool;
std::shared_ptr<C2Allocator> mLinearAllocator;
@@ -254,7 +264,7 @@ void validateComponent(
// Validates component name
if (compName == Codec2AudioDecHidlTest::unknown_comp) {
- ALOGD("Component InValid");
+ ALOGE("Component InValid");
disableTest = true;
return;
}
@@ -295,37 +305,9 @@ void getInputChannelInfo(
ASSERT_TRUE(false);
} else {
size_t offset = sizeof(C2Param);
- ALOGD("--------------PARAMS-----------------");
for (size_t i = 0; i < inParams.size(); ++i) {
C2Param* param = inParams[i].get();
bitStreamInfo[i] = *(int32_t*)((uint8_t*)param + offset);
- ALOGD("Param value : %u", *(int32_t*)((uint8_t*)param + offset));
- ALOGD("Param isVendor %d", param->isVendor());
- ALOGD("Param forInput %d", param->forInput());
- ALOGD("Param forOutput %d", param->forOutput());
- switch (param->kind()) {
- case C2Param::NONE:
- ALOGD("Param kind NONE");
- break;
- case C2Param::STRUCT:
- ALOGD("Param kind STRUCT");
- break;
- case C2Param::INFO:
- ALOGD("Param kind INFO");
- break;
- case C2Param::SETTING:
- ALOGD("Param kind SETTING");
- break;
- case C2Param::TUNING:
- ALOGD("Param kind TUNING");
- break;
- default:
- ALOGD("Param kind Invalid");
- break;
- }
- ALOGD("Param size (if size is 0 parameter is invalid) %zu",
- param->size());
- ALOGD("--------------------------------------");
}
switch (compName) {
case Codec2AudioDecHidlTest::amrnb: {
@@ -404,20 +386,16 @@ void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL,
}
}
-void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &component,
- std::mutex &queueLock, std::condition_variable &queueCondition,
- std::list<std::unique_ptr<C2Work>> &workQueue,
- std::shared_ptr<C2Allocator> &linearAllocator,
- std::shared_ptr<C2BlockPool> &linearPool,
- C2BlockPool::local_id_t blockPoolId,
+void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue,
+ std::list<uint64_t>& flushedIndices,
+ std::shared_ptr<C2BlockPool>& linearPool,
std::ifstream& eleStream,
android::Vector<FrameInfo>* Info,
int offset, int range, bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
- int frameID = 0;
- linearPool =
- std::make_shared<C2PooledBlockPool>(linearAllocator, blockPoolId++);
- component->start();
+ int frameID = offset;
int maxRetry = 0;
while (1) {
if (frameID == (int)Info->size() || frameID == (offset + range)) break;
@@ -440,42 +418,49 @@ void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
int64_t timestamp = (*Info)[frameID].timestamp;
if ((*Info)[frameID].flags) flags = 1u << ((*Info)[frameID].flags - 1);
if (signalEOS && ((frameID == (int)Info->size() - 1) ||
- (frameID == (offset + range - 1))))
- flags |= C2FrameData::FLAG_END_OF_STREAM;
+ (frameID == (offset + range - 1))))
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
work->input.flags = (C2FrameData::flags_t)flags;
work->input.ordinal.timestamp = timestamp;
work->input.ordinal.frameIndex = frameID;
+ {
+ ULock l(queueLock);
+ flushedIndices.emplace_back(frameID);
+ }
int size = (*Info)[frameID].bytesCount;
char* data = (char*)malloc(size);
+ ASSERT_NE(data, nullptr);
eleStream.read(data, size);
ASSERT_EQ(eleStream.gcount(), size);
- std::shared_ptr<C2LinearBlock> block;
- ASSERT_EQ(C2_OK,
- linearPool->fetchLinearBlock(
- size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
- &block));
- ASSERT_TRUE(block);
-
- // Write View
- C2WriteView view = block->map().get();
- if (view.error() != C2_OK) {
- fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
- break;
- }
- ASSERT_EQ((size_t)size, view.capacity());
- ASSERT_EQ(0u, view.offset());
- ASSERT_EQ((size_t)size, view.size());
+ work->input.buffers.clear();
+ if (size) {
+ std::shared_ptr<C2LinearBlock> block;
+ ASSERT_EQ(C2_OK,
+ linearPool->fetchLinearBlock(
+ size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+ &block));
+ ASSERT_TRUE(block);
+
+ // Write View
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
+ break;
+ }
+ ASSERT_EQ((size_t)size, view.capacity());
+ ASSERT_EQ(0u, view.offset());
+ ASSERT_EQ((size_t)size, view.size());
- memcpy(view.base(), data, size);
+ memcpy(view.base(), data, size);
- work->input.buffers.clear();
- work->input.buffers.emplace_back(new LinearBuffer(block));
+ work->input.buffers.emplace_back(new LinearBuffer(block));
+ free(data);
+ }
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
- free(data);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
@@ -488,28 +473,6 @@ void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
}
}
-void waitOnInputConsumption(std::mutex& queueLock,
- std::condition_variable& queueCondition,
- std::list<std::unique_ptr<C2Work>>& workQueue) {
- typedef std::unique_lock<std::mutex> ULock;
- uint32_t queueSize;
- uint32_t maxRetry = 0;
- {
- ULock l(queueLock);
- queueSize = workQueue.size();
- }
- while ((maxRetry < MAX_RETRY) && (queueSize < MAX_INPUT_BUFFERS)) {
- ULock l(queueLock);
- if (queueSize != workQueue.size()) {
- queueSize = workQueue.size();
- maxRetry = 0;
- } else {
- queueCondition.wait_for(l, TIME_OUT);
- maxRetry++;
- }
- }
-}
-
TEST_F(Codec2AudioDecHidlTest, validateCompName) {
if (mDisableTest) return;
ALOGV("Checks if the given component is a valid audio component");
@@ -520,16 +483,204 @@ TEST_F(Codec2AudioDecHidlTest, validateCompName) {
TEST_F(Codec2AudioDecHidlTest, configComp) {
description("Tests component specific configuration");
if (mDisableTest) return;
+ ASSERT_EQ(mComponent->start(), C2_OK);
int32_t bitStreamInfo[2] = {0};
ASSERT_NO_FATAL_FAILURE(
getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
setupConfigParam(mComponent, bitStreamInfo);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
}
TEST_F(Codec2AudioDecHidlTest, DecodeTest) {
description("Decodes input file");
if (mDisableTest) return;
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ mTimestampDevTest = true;
+ char mURL[512], info[512];
+ std::ifstream eleStream, eleInfo;
+
+ strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(info, gEnv->getRes().c_str());
+ GetURLForComponent(mCompName, mURL, info);
+
+ eleInfo.open(info);
+ ASSERT_EQ(eleInfo.is_open(), true);
+ android::Vector<FrameInfo> Info;
+ int bytesCount = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ while (1) {
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ bool codecConfig =
+ ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+ if (mTimestampDevTest && !codecConfig)
+ mTimestampUslist.push_back(timestamp);
+ Info.push_back({bytesCount, flags, timestamp});
+ }
+ eleInfo.close();
+ int32_t bitStreamInfo[2] = {0};
+ if (mCompName == raw) {
+ bitStreamInfo[0] = 8000;
+ bitStreamInfo[1] = 1;
+ } else {
+ ASSERT_NO_FATAL_FAILURE(
+ getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ }
+ setupConfigParam(mComponent, bitStreamInfo);
+ ALOGV("mURL : %s", mURL);
+ eleStream.open(mURL, std::ifstream::binary);
+ ASSERT_EQ(eleStream.is_open(), true);
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(
+ mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
+ mLinearPool, eleStream, &Info, 0, (int)Info.size()));
+ // blocking call to ensures application to Wait till all the inputs are
+ // consumed
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ eleStream.close();
+ if (mFramesReceived != Info.size()) {
+ ALOGE("Input buffer count and Output buffer count mismatch");
+ ALOGE("framesReceived : %d inputFrames : %zu", mFramesReceived,
+ Info.size());
+ ASSERT_TRUE(false);
+ }
+ ASSERT_EQ(mEos, true);
+ if (mTimestampDevTest) {
+ uint64_t expTs;
+ uint32_t samplesReceived = 0;
+ // Update SampleRate and ChannelCount
+ ASSERT_NO_FATAL_FAILURE(
+ getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ int nSampleRate = bitStreamInfo[0];
+ int nChannels = bitStreamInfo[1];
+ std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
+ android::List<outputMetaData>::iterator itOut = oBufferMetaData.begin();
+ EXPECT_EQ(*itIn, itOut->timestampUs);
+ expTs = *itIn;
+ while (itOut != oBufferMetaData.end()) {
+ EXPECT_EQ(expTs, itOut->timestampUs);
+ if (expTs != itOut->timestampUs) break;
+ // buffer samples = ((total bytes) / (ac * (bits per sample / 8))
+ samplesReceived += ((itOut->rangeLength) / (nChannels * 2));
+ expTs = samplesReceived * 1000000ll / nSampleRate;
+ itOut++;
+ }
+ itIn = mTimestampUslist.end();
+ --itIn;
+ EXPECT_GT(expTs, *itIn);
+ oBufferMetaData.clear();
+ mTimestampUslist.clear();
+ }
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
+// thumbnail test
+TEST_F(Codec2AudioDecHidlTest, ThumbnailTest) {
+ description("Test Request for thumbnail");
+ if (mDisableTest) return;
+
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ char mURL[512], info[512];
+ std::ifstream eleStream, eleInfo;
+
+ strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(info, gEnv->getRes().c_str());
+ GetURLForComponent(mCompName, mURL, info);
+
+ eleInfo.open(info);
+ ASSERT_EQ(eleInfo.is_open(), true);
+ android::Vector<FrameInfo> Info;
+ int bytesCount = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ while (1) {
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ Info.push_back({bytesCount, flags, timestamp});
+ }
+ eleInfo.close();
+ int32_t bitStreamInfo[2] = {0};
+ if (mCompName == raw) {
+ bitStreamInfo[0] = 8000;
+ bitStreamInfo[1] = 1;
+ } else {
+ ASSERT_NO_FATAL_FAILURE(
+ getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ }
+ setupConfigParam(mComponent, bitStreamInfo);
+ ALOGV("mURL : %s", mURL);
+
+ // request EOS for thumbnail
+ // signal EOS flag with last frame
+ size_t i = -1;
+ do {
+ i++;
+ flags = 0;
+ if (Info[i].flags) flags = 1u << (Info[i].flags - 1);
+
+ } while (!(flags & SYNC_FRAME));
+ eleStream.open(mURL, std::ifstream::binary);
+ ASSERT_EQ(eleStream.is_open(), true);
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(
+ mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
+ mLinearPool, eleStream, &Info, 0, i + 1));
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ eleStream.close();
+ EXPECT_GE(mFramesReceived, 1U);
+ ASSERT_EQ(mEos, true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
+TEST_F(Codec2AudioDecHidlTest, EOSTest) {
+ description("Test empty input buffer with EOS flag");
+ if (mDisableTest) return;
+ typedef std::unique_lock<std::mutex> ULock;
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ std::unique_ptr<C2Work> work;
+ // Prepare C2Work
+ {
+ ULock l(mQueueLock);
+ if (!mWorkQueue.empty()) {
+ work.swap(mWorkQueue.front());
+ mWorkQueue.pop_front();
+ } else {
+ ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test";
+ }
+ }
+ ASSERT_NE(work, nullptr);
+
+ work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
+ work->input.ordinal.timestamp = 0;
+ work->input.ordinal.frameIndex = 0;
+ work->input.buffers.clear();
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+ ASSERT_EQ(mComponent->queue(&items), C2_OK);
+
+ {
+ ULock l(mQueueLock);
+ if (mWorkQueue.size() != MAX_INPUT_BUFFERS) {
+ mQueueCondition.wait_for(l, TIME_OUT);
+ }
+ }
+ ASSERT_EQ(mEos, true);
+ ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
+TEST_F(Codec2AudioDecHidlTest, FlushTest) {
+ description("Tests Flush calls");
+ if (mDisableTest) return;
+ typedef std::unique_lock<std::mutex> ULock;
+ ASSERT_EQ(mComponent->start(), C2_OK);
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
@@ -543,6 +694,7 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTest) {
int bytesCount = 0;
uint32_t flags = 0;
uint32_t timestamp = 0;
+ mFlushedIndices.clear();
while (1) {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
@@ -562,14 +714,141 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTest) {
ALOGV("mURL : %s", mURL);
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
+ // Decode 128 frames and flush. here 128 is chosen to ensure there is a key
+ // frame after this so that the below section can be covered for all
+ // components
+ uint32_t numFramesFlushed = 128;
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(
+ mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
+ mLinearPool, eleStream, &Info, 0, numFramesFlushed, false));
+ // flush
+ std::list<std::unique_ptr<C2Work>> flushedWork;
+ c2_status_t err =
+ mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ ASSERT_EQ(err, C2_OK);
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ uint64_t frameIndex;
+ {
+ //Update mFlushedIndices based on the index received from flush()
+ ULock l(mQueueLock);
+ for (std::unique_ptr<C2Work>& work : flushedWork) {
+ ASSERT_NE(work, nullptr);
+ frameIndex = work->input.ordinal.frameIndex.peeku();
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
+ frameIndex);
+ if (!mFlushedIndices.empty() &&
+ (frameIndexIt != mFlushedIndices.end())) {
+ mFlushedIndices.erase(frameIndexIt);
+ work->input.buffers.clear();
+ work->worklets.clear();
+ mWorkQueue.push_back(std::move(work));
+ }
+ }
+ }
+ // Seek to next key frame and start decoding till the end
+ mFlushedIndices.clear();
+ int index = numFramesFlushed;
+ bool keyFrame = false;
+ flags = 0;
+ while (index < (int)Info.size()) {
+ if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
+ if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+ keyFrame = true;
+ break;
+ }
+ flags = 0;
+ eleStream.ignore(Info[index].bytesCount);
+ index++;
+ }
+ if (keyFrame) {
+ ASSERT_NO_FATAL_FAILURE(
+ decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, index,
+ (int)Info.size() - index));
+ }
+ eleStream.close();
+ err =
+ mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ ASSERT_EQ(err, C2_OK);
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ {
+ //Update mFlushedIndices based on the index received from flush()
+ ULock l(mQueueLock);
+ for (std::unique_ptr<C2Work>& work : flushedWork) {
+ ASSERT_NE(work, nullptr);
+ frameIndex = work->input.ordinal.frameIndex.peeku();
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
+ frameIndex);
+ if (!mFlushedIndices.empty() &&
+ (frameIndexIt != mFlushedIndices.end())) {
+ mFlushedIndices.erase(frameIndexIt);
+ work->input.buffers.clear();
+ work->worklets.clear();
+ mWorkQueue.push_back(std::move(work));
+ }
+ }
+ }
+ ASSERT_EQ(mFlushedIndices.empty(), true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
+TEST_F(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) {
+ description("Decode with multiple empty input frames");
+ if (mDisableTest) return;
+
+ ASSERT_EQ(mComponent->start(), C2_OK);
+
+ char mURL[512], info[512];
+ std::ifstream eleStream, eleInfo;
+
+ strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(info, gEnv->getRes().c_str());
+ GetURLForComponent(mCompName, mURL, info);
+
+ eleInfo.open(info);
+ ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
+ android::Vector<FrameInfo> Info;
+ int bytesCount = 0;
+ uint32_t frameId = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ bool codecConfig = false;
+ // This test introduces empty CSD after every 20th frame
+ // and empty input frames at an interval of 5 frames.
+ while (1) {
+ if (!(frameId % 5)) {
+ if (!(frameId % 20)) flags = 32;
+ else flags = 0;
+ bytesCount = 0;
+ } else {
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ codecConfig = flags ?
+ ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ }
+ Info.push_back({bytesCount, flags, timestamp});
+ frameId++;
+ }
+ eleInfo.close();
+
+ ALOGV("mURL : %s", mURL);
+ eleStream.open(mURL, std::ifstream::binary);
+ ASSERT_EQ(eleStream.is_open(), true);
ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mLinearAllocator,
- mLinearPool, mBlockPoolId, eleStream, &Info, 0, (int)Info.size()));
+ mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
+ mLinearPool, eleStream, &Info, 0, (int)Info.size()));
// blocking call to ensures application to Wait till all the inputs are
// consumed
if (!mEos) {
- ALOGD("Waiting for input consumption");
+ ALOGV("Waiting for input consumption");
ASSERT_NO_FATAL_FAILURE(
waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
}
@@ -577,18 +856,16 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTest) {
eleStream.close();
if (mFramesReceived != Info.size()) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGE("framesReceived : %d inputFrames : %zu", mFramesReceived,
+ ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
Info.size());
ASSERT_TRUE(false);
}
+
+ ASSERT_EQ(mComponent->stop(), C2_OK);
}
} // anonymous namespace
-// TODO : Thumbnail Test
-// TODO : Test EOS
-// TODO : Flush Test
-// TODO : Timestamps deviation
int main(int argc, char** argv) {
gEnv = new ComponentTestEnvironment();
::testing::AddGlobalTestEnvironment(gEnv);
diff --git a/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioEncTest.cpp b/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioEncTest.cpp
index af71e38..5d66ee5 100644
--- a/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioEncTest.cpp
+++ b/codec2/hidl/1.0/mts/audio/MtsHidlC2V1_0TargetAudioEncTest.cpp
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include <stdio.h>
#include <fstream>
+#include <algorithm>
#include <codec2/hidl/client.h>
#include <C2AllocatorIon.h>
@@ -95,6 +96,7 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
const StringToName kStringToName[] = {
{"aac", aac},
{"flac", flac},
+ {"opus", opus},
{"amrnb", amrnb},
{"amrwb", amrwb},
};
@@ -134,37 +136,17 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
- // handle configuration changes in work done
- if (!work->worklets.empty() &&
- (work->worklets.front()->output.configUpdate.size() != 0)) {
- ALOGV("Config Update");
- std::vector<std::unique_ptr<C2Param>> updates =
- std::move(work->worklets.front()->output.configUpdate);
- std::vector<C2Param*> configParam;
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- for (size_t i = 0; i < updates.size(); ++i) {
- C2Param* param = updates[i].get();
- if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) {
- mCsd = true;
- }
- }
+ if (!work->worklets.empty()) {
+ workDone(mComponent, work, mFlushedIndices, mQueueLock,
+ mQueueCondition, mWorkQueue, mEos, mCsd,
+ mFramesReceived);
}
- mFramesReceived++;
- mEos = (work->worklets.front()->output.flags &
- C2FrameData::FLAG_END_OF_STREAM) != 0;
- ALOGV("WorkDone: frameID received %d",
- (int)work->worklets.front()->output.ordinal.frameIndex.peeku());
- work->input.buffers.clear();
- work->worklets.clear();
- typedef std::unique_lock<std::mutex> ULock;
- ULock l(mQueueLock);
- mWorkQueue.push_back(std::move(work));
- mQueueCondition.notify_all();
}
}
enum standardComp {
aac,
flac,
+ opus,
amrnb,
amrwb,
unknown_comp,
@@ -175,6 +157,8 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
bool mDisableTest;
standardComp mCompName;
uint32_t mFramesReceived;
+ std::list<uint64_t> mFlushedIndices;
+
C2BlockPool::local_id_t mBlockPoolId;
std::shared_ptr<C2BlockPool> mLinearPool;
std::shared_ptr<C2Allocator> mLinearAllocator;
@@ -264,6 +248,8 @@ void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) {
"bbb_raw_1ch_16khz_s16le.raw"},
{Codec2AudioEncHidlTest::standardComp::flac,
"bbb_raw_2ch_48khz_s16le.raw"},
+ {Codec2AudioEncHidlTest::standardComp::opus,
+ "bbb_raw_2ch_48khz_s16le.raw"},
};
for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
@@ -274,19 +260,16 @@ void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) {
}
}
-void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &component,
- std::mutex &queueLock, std::condition_variable &queueCondition,
- std::list<std::unique_ptr<C2Work>> &workQueue,
- std::shared_ptr<C2Allocator> &linearAllocator,
- std::shared_ptr<C2BlockPool> &linearPool,
- C2BlockPool::local_id_t blockPoolId,
+void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue,
+ std::list<uint64_t>& flushedIndices,
+ std::shared_ptr<C2BlockPool>& linearPool,
std::ifstream& eleStream, uint32_t nFrames,
int32_t samplesPerFrame, int32_t nChannels,
- int32_t nSampleRate,bool signalEOS = true) {
+ int32_t nSampleRate, bool flushed = false,
+ bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
- linearPool =
- std::make_shared<C2PooledBlockPool>(linearAllocator, blockPoolId++);
- component->start();
uint32_t frameID = 0;
uint32_t maxRetry = 0;
@@ -314,11 +297,19 @@ void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
}
if (signalEOS && (nFrames == 1))
flags |= C2FrameData::FLAG_END_OF_STREAM;
-
+ if (flushed) {
+ flags |= SYNC_FRAME;
+ flushed = false;
+ }
work->input.flags = (C2FrameData::flags_t)flags;
work->input.ordinal.timestamp = timestamp;
work->input.ordinal.frameIndex = frameID;
+ {
+ ULock l(queueLock);
+ flushedIndices.emplace_back(frameID);
+ }
char* data = (char*)malloc(bytesCount);
+ ASSERT_NE(data, nullptr);
eleStream.read(data, bytesCount);
ASSERT_EQ(eleStream.gcount(), bytesCount);
std::shared_ptr<C2LinearBlock> block;
@@ -357,28 +348,6 @@ void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
}
}
-void waitOnInputConsumption(std::mutex& queueLock,
- std::condition_variable& queueCondition,
- std::list<std::unique_ptr<C2Work>>& workQueue) {
- typedef std::unique_lock<std::mutex> ULock;
- uint32_t queueSize;
- uint32_t maxRetry = 0;
- {
- ULock l(queueLock);
- queueSize = workQueue.size();
- }
- while ((maxRetry < MAX_RETRY) && (queueSize < MAX_INPUT_BUFFERS)) {
- ULock l(queueLock);
- if (queueSize != workQueue.size()) {
- queueSize = workQueue.size();
- maxRetry = 0;
- } else {
- queueCondition.wait_for(l, TIME_OUT);
- maxRetry++;
- }
- }
-}
-
TEST_F(Codec2AudioEncHidlTest, validateCompName) {
if (mDisableTest) return;
ALOGV("Checks if the given component is a valid audio component");
@@ -389,6 +358,7 @@ TEST_F(Codec2AudioEncHidlTest, validateCompName) {
TEST_F(Codec2AudioEncHidlTest, EncodeTest) {
ALOGV("EncodeTest");
if (mDisableTest) return;
+ ASSERT_EQ(mComponent->start(), C2_OK);
char mURL[512];
strcpy(mURL, gEnv->getRes().c_str());
GetURLForComponent(mCompName, mURL);
@@ -408,6 +378,11 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) {
nSampleRate = 48000;
samplesPerFrame = 1152;
break;
+ case opus:
+ nChannels = 2;
+ nSampleRate = 48000;
+ samplesPerFrame = 960;
+ break;
case amrnb:
nChannels = 1;
nSampleRate = 8000;
@@ -423,42 +398,192 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) {
}
setupConfigParam(mComponent, nChannels, nSampleRate);
std::ifstream eleStream;
+ uint32_t numFrames = 128;
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
ALOGV("mURL : %s", mURL);
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mLinearAllocator, mLinearPool, mBlockPoolId, eleStream,
- 128, samplesPerFrame, nChannels, nSampleRate));
-
+ mFlushedIndices, mLinearPool, eleStream, numFrames,
+ samplesPerFrame, nChannels, nSampleRate));
// blocking call to ensures application to Wait till all the inputs are
// consumed
- if (!mEos) {
- ALOGD("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
- }
-
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
eleStream.close();
- if (mFramesReceived != 128) {
+ if (mFramesReceived != numFrames) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGE("framesReceived : %d inputFrames : 128", mFramesReceived);
+ ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
ASSERT_TRUE(false);
}
- if ((mCompName == flac || mCompName == aac)) {
+ if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
if (!mCsd) {
ALOGE("CSD buffer missing");
ASSERT_TRUE(false);
}
}
+ ASSERT_EQ(mEos, true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
+TEST_F(Codec2AudioEncHidlTest, EOSTest) {
+ description("Test empty input buffer with EOS flag");
+ if (mDisableTest) return;
+ ASSERT_EQ(mComponent->start(), C2_OK);
+
+ typedef std::unique_lock<std::mutex> ULock;
+ std::unique_ptr<C2Work> work;
+ {
+ ULock l(mQueueLock);
+ if (!mWorkQueue.empty()) {
+ work.swap(mWorkQueue.front());
+ mWorkQueue.pop_front();
+ } else {
+ ALOGE("mWorkQueue Empty is not expected at the start of the test");
+ ASSERT_TRUE(false);
+ }
+ }
+ ASSERT_NE(work, nullptr);
+ work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
+ work->input.ordinal.timestamp = 0;
+ work->input.ordinal.frameIndex = 0;
+ work->input.buffers.clear();
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+ ASSERT_EQ(mComponent->queue(&items), C2_OK);
+ uint32_t queueSize;
+ {
+ ULock l(mQueueLock);
+ queueSize = mWorkQueue.size();
+ if (queueSize < MAX_INPUT_BUFFERS) {
+ mQueueCondition.wait_for(l, TIME_OUT);
+ }
+ }
+ ASSERT_EQ(mEos, true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
+TEST_F(Codec2AudioEncHidlTest, FlushTest) {
+ description("Test Request for flush");
+ if (mDisableTest) return;
+ ASSERT_EQ(mComponent->start(), C2_OK);
+
+ typedef std::unique_lock<std::mutex> ULock;
+ char mURL[512];
+ strcpy(mURL, gEnv->getRes().c_str());
+ GetURLForComponent(mCompName, mURL);
+
+ // Setting default configuration
+ mFlushedIndices.clear();
+ int32_t nChannels = 2;
+ int32_t nSampleRate = 44100;
+ int32_t samplesPerFrame = 1024;
+ switch (mCompName) {
+ case aac:
+ nChannels = 2;
+ nSampleRate = 48000;
+ samplesPerFrame = 1024;
+ break;
+ case flac:
+ nChannels = 2;
+ nSampleRate = 48000;
+ samplesPerFrame = 1152;
+ break;
+ case opus:
+ nChannels = 2;
+ nSampleRate = 48000;
+ samplesPerFrame = 960;
+ break;
+ case amrnb:
+ nChannels = 1;
+ nSampleRate = 8000;
+ samplesPerFrame = 160;
+ break;
+ case amrwb:
+ nChannels = 1;
+ nSampleRate = 16000;
+ samplesPerFrame = 160;
+ break;
+ default:
+ ASSERT_TRUE(false);
+ }
+ setupConfigParam(mComponent, nChannels, nSampleRate);
+ std::ifstream eleStream;
+ uint32_t numFramesFlushed = 30;
+ uint32_t numFrames = 128;
+ eleStream.open(mURL, std::ifstream::binary);
+ ASSERT_EQ(eleStream.is_open(), true);
+ ALOGV("mURL : %s", mURL);
+ ASSERT_NO_FATAL_FAILURE(
+ encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, numFramesFlushed,
+ samplesPerFrame, nChannels, nSampleRate));
+ std::list<std::unique_ptr<C2Work>> flushedWork;
+ c2_status_t err =
+ mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ ASSERT_EQ(err, C2_OK);
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ uint64_t frameIndex;
+ {
+ //Update mFlushedIndices based on the index received from flush()
+ ULock l(mQueueLock);
+ for (std::unique_ptr<C2Work>& work : flushedWork) {
+ ASSERT_NE(work, nullptr);
+ frameIndex = work->input.ordinal.frameIndex.peeku();
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
+ frameIndex);
+ if (!mFlushedIndices.empty() &&
+ (frameIndexIt != mFlushedIndices.end())) {
+ mFlushedIndices.erase(frameIndexIt);
+ work->input.buffers.clear();
+ work->worklets.clear();
+ mWorkQueue.push_back(std::move(work));
+ }
+ }
+ }
+ mFlushedIndices.clear();
+ ASSERT_NO_FATAL_FAILURE(
+ encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream,
+ numFrames - numFramesFlushed, samplesPerFrame,
+ nChannels, nSampleRate, true));
+ eleStream.close();
+ err =
+ mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ ASSERT_EQ(err, C2_OK);
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ {
+ //Update mFlushedIndices based on the index received from flush()
+ ULock l(mQueueLock);
+ for (std::unique_ptr<C2Work>& work : flushedWork) {
+ ASSERT_NE(work, nullptr);
+ frameIndex = work->input.ordinal.frameIndex.peeku();
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
+ frameIndex);
+ if (!mFlushedIndices.empty() &&
+ (frameIndexIt != mFlushedIndices.end())) {
+ mFlushedIndices.erase(frameIndexIt);
+ work->input.buffers.clear();
+ work->worklets.clear();
+ mWorkQueue.push_back(std::move(work));
+ }
+ }
+ }
+ ASSERT_EQ(mFlushedIndices.empty(), true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
}
} // anonymous namespace
-// TODO : Thumbnail Test
-// TODO : Test EOS
-// TODO : Flush Test
-// TODO : Timestamps deviation
int main(int argc, char** argv) {
gEnv = new ComponentTestEnvironment();
::testing::AddGlobalTestEnvironment(gEnv);
diff --git a/codec2/hidl/1.0/mts/audio/media_c2_audio_hidl_test_common.h b/codec2/hidl/1.0/mts/audio/media_c2_audio_hidl_test_common.h
index 63370b3..4d773ce 100644
--- a/codec2/hidl/1.0/mts/audio/media_c2_audio_hidl_test_common.h
+++ b/codec2/hidl/1.0/mts/audio/media_c2_audio_hidl_test_common.h
@@ -17,8 +17,5 @@
#ifndef MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H
#define MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H
-#define MAX_RETRY 20
-#define TIME_OUT 400ms
-#define MAX_INPUT_BUFFERS 8
#endif // MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H
diff --git a/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.cpp b/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.cpp
index 1b2772f..64a458c 100644
--- a/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.cpp
+++ b/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.cpp
@@ -14,39 +14,115 @@
* limitations under the License.
*/
+// #define LOG_NDEBUG 0
#define LOG_TAG "media_c2_hidl_test_common"
#include <stdio.h>
#include "media_c2_hidl_test_common.h"
-using ::hardware::google::media::c2::V1_0::FieldSupportedValues;
-void dumpFSV(const FieldSupportedValues& sv) {
- ALOGD("Dumping FSV data");
- using namespace std;
- if (sv.type == FieldSupportedValues::Type::EMPTY) {
- ALOGD("FSV Value is Empty");
+// Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set
+void testInputBuffer(
+ const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
+ uint32_t flags, bool isNullBuffer) {
+ std::unique_ptr<C2Work> work;
+ {
+ typedef std::unique_lock<std::mutex> ULock;
+ ULock l(queueLock);
+ if (!workQueue.empty()) {
+ work.swap(workQueue.front());
+ workQueue.pop_front();
+ } else {
+ ASSERT_TRUE(false) << "workQueue Empty at the start of test";
+ }
+ }
+ ASSERT_NE(work, nullptr);
+
+ work->input.flags = (C2FrameData::flags_t)flags;
+ work->input.ordinal.timestamp = 0;
+ work->input.ordinal.frameIndex = 0;
+ work->input.buffers.clear();
+ if (isNullBuffer) {
+ work->input.buffers.emplace_back(nullptr);
}
- if (sv.type == FieldSupportedValues::Type::RANGE) {
- ALOGD("Dumping FSV range");
- cout << ".range(" << sv.range.min;
- if (sv.range.step != 0) {
- cout << ":" << sv.range.step;
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+ ASSERT_EQ(component->queue(&items), C2_OK);
+}
+
+// Wait for all the inputs to be consumed by the plugin.
+void waitOnInputConsumption(std::mutex& queueLock,
+ std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue,
+ size_t bufferCount) {
+ typedef std::unique_lock<std::mutex> ULock;
+ uint32_t queueSize;
+ uint32_t maxRetry = 0;
+ {
+ ULock l(queueLock);
+ queueSize = workQueue.size();
+ }
+ while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) {
+ ULock l(queueLock);
+ if (queueSize != workQueue.size()) {
+ queueSize = workQueue.size();
+ maxRetry = 0;
+ } else {
+ queueCondition.wait_for(l, TIME_OUT);
+ maxRetry++;
}
- if (sv.range.num != 1 || sv.range.denom != 1) {
- cout << ":" << sv.range.num << "/" << sv.range.denom;
+ }
+}
+
+// process onWorkDone received by Listener
+void workDone(
+ const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
+ uint32_t& framesReceived) {
+ // handle configuration changes in work done
+ if (work->worklets.front()->output.configUpdate.size() != 0) {
+ ALOGV("Config Update");
+ std::vector<std::unique_ptr<C2Param>> updates =
+ std::move(work->worklets.front()->output.configUpdate);
+ std::vector<C2Param*> configParam;
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ for (size_t i = 0; i < updates.size(); ++i) {
+ C2Param* param = updates[i].get();
+ if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) {
+ csd = true;
+ } else if ((param->index() ==
+ C2StreamSampleRateInfo::output::PARAM_TYPE) ||
+ (param->index() ==
+ C2StreamChannelCountInfo::output::PARAM_TYPE) ||
+ (param->index() ==
+ C2VideoSizeStreamInfo::output::PARAM_TYPE)) {
+ configParam.push_back(param);
+ }
}
- cout << " " << sv.range.max << ")";
+ component->config(configParam, C2_DONT_BLOCK, &failures);
+ ASSERT_EQ(failures.size(), 0u);
}
- if (sv.values.size()) {
- ALOGD("Dumping FSV value");
- cout << (sv.type == FieldSupportedValues::Type::FLAGS ? ".flags("
- : ".list(");
- const char* sep = "";
- for (const auto& p : sv.values) {
- cout << sep << p;
- sep = ",";
+ framesReceived++;
+ eos = (work->worklets.front()->output.flags &
+ C2FrameData::FLAG_END_OF_STREAM) != 0;
+ auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(),
+ work->input.ordinal.frameIndex.peeku());
+ ALOGV("WorkDone: frameID received %d",
+ (int)work->worklets.front()->output.ordinal.frameIndex.peeku());
+ work->input.buffers.clear();
+ work->worklets.clear();
+ {
+ typedef std::unique_lock<std::mutex> ULock;
+ ULock l(queueLock);
+ workQueue.push_back(std::move(work));
+ if (!flushedIndices.empty()) {
+ flushedIndices.erase(frameIndexIt);
}
- cout << ")";
+ queueCondition.notify_all();
}
- cout << endl;
-}
+} \ No newline at end of file
diff --git a/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.h b/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.h
index 0e7db26..04e02c9 100644
--- a/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.h
+++ b/codec2/hidl/1.0/mts/common/media_c2_hidl_test_common.h
@@ -22,6 +22,7 @@
#include <hardware/google/media/c2/1.0/types.h>
#include <C2Component.h>
+#include <C2Config.h>
#include <getopt.h>
#include <hidl/HidlSupport.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -38,6 +39,10 @@ using ::android::hardware::hidl_string;
#include <VtsHalHidlTargetTestEnvBase.h>
+#define MAX_RETRY 20
+#define TIME_OUT 400ms
+#define MAX_INPUT_BUFFERS 8
+
/*
* Handle Callback functions onWorkDone(), onTripped(),
* onError(), onDeath(), onFramesRendered()
@@ -176,5 +181,21 @@ class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
/*
* common functions declarations
*/
-void dumpFSV(const FieldSupportedValues& sv);
+void testInputBuffer(
+ const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
+ uint32_t flags, bool isNullBuffer);
+
+void waitOnInputConsumption(std::mutex& queueLock,
+ std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue,
+ size_t bufferCount = MAX_INPUT_BUFFERS);
+
+void workDone(
+ const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
+ uint32_t& framesReceived);
+
#endif // MEDIA_C2_HIDL_TEST_COMMON_H
diff --git a/codec2/hidl/1.0/mts/component/MtsHidlC2V1_0TargetComponentTest.cpp b/codec2/hidl/1.0/mts/component/MtsHidlC2V1_0TargetComponentTest.cpp
index 03b1716..ec803d7 100644
--- a/codec2/hidl/1.0/mts/component/MtsHidlC2V1_0TargetComponentTest.cpp
+++ b/codec2/hidl/1.0/mts/component/MtsHidlC2V1_0TargetComponentTest.cpp
@@ -31,21 +31,28 @@ static ComponentTestEnvironment* gEnv = nullptr;
namespace {
// google.codec2 Component test setup
-class Codec2ComponentHalTest : public ::testing::VtsHalHidlTargetTestBase {
+class Codec2ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase {
private:
typedef ::testing::VtsHalHidlTargetTestBase Super;
public:
virtual void SetUp() override {
Super::SetUp();
+ mEos = false;
mClient = android::Codec2Client::CreateFromService(
gEnv->getInstance().c_str());
ASSERT_NE(mClient, nullptr);
- mListener.reset(new CodecListener());
+ mListener.reset(new CodecListener(
+ [this](std::list<std::unique_ptr<C2Work>>& workItems) {
+ handleWorkDone(workItems);
+ }));
ASSERT_NE(mListener, nullptr);
mClient->createComponent(gEnv->getComponent().c_str(), mListener,
&mComponent);
ASSERT_NE(mComponent, nullptr);
+ for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
+ mWorkQueue.emplace_back(new C2Work);
+ }
}
virtual void TearDown() override {
@@ -59,6 +66,23 @@ class Codec2ComponentHalTest : public ::testing::VtsHalHidlTargetTestBase {
}
Super::TearDown();
}
+ // callback function to process onWorkDone received by Listener
+ void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
+ for (std::unique_ptr<C2Work>& work : workItems) {
+ if (!work->worklets.empty()) {
+ bool mCsd = false;
+ uint32_t mFramesReceived = 0;
+ std::list<uint64_t> mFlushedIndices;
+ workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition,
+ mWorkQueue, mEos, mCsd, mFramesReceived);
+ }
+ }
+ }
+
+ bool mEos;
+ std::mutex mQueueLock;
+ std::condition_variable mQueueCondition;
+ std::list<std::unique_ptr<C2Work>> mWorkQueue;
std::shared_ptr<android::Codec2Client> mClient;
std::shared_ptr<android::Codec2Client::Listener> mListener;
@@ -71,7 +95,7 @@ class Codec2ComponentHalTest : public ::testing::VtsHalHidlTargetTestBase {
};
// Test Empty Flush
-TEST_F(Codec2ComponentHalTest, EmptyFlush) {
+TEST_F(Codec2ComponentHidlTest, EmptyFlush) {
ALOGV("Empty Flush Test");
c2_status_t err = mComponent->start();
ASSERT_EQ(err, C2_OK);
@@ -87,7 +111,7 @@ TEST_F(Codec2ComponentHalTest, EmptyFlush) {
}
// Test Queue Empty Work
-TEST_F(Codec2ComponentHalTest, QueueEmptyWork) {
+TEST_F(Codec2ComponentHidlTest, QueueEmptyWork) {
ALOGV("Queue Empty Work Test");
c2_status_t err = mComponent->start();
ASSERT_EQ(err, C2_OK);
@@ -102,45 +126,39 @@ TEST_F(Codec2ComponentHalTest, QueueEmptyWork) {
}
// Test Component Configuration
-TEST_F(Codec2ComponentHalTest, Config) {
+TEST_F(Codec2ComponentHidlTest, Config) {
ALOGV("Configuration Test");
C2String name = mComponent->getName();
EXPECT_NE(name.empty(), true) << "Invalid Component Name";
+ c2_status_t err = C2_OK;
+ std::vector<std::unique_ptr<C2Param>> queried;
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+
// Query supported params by the component
std::vector<std::shared_ptr<C2ParamDescriptor>> params;
- c2_status_t err = mComponent->querySupportedParams(&params);
+ err = mComponent->querySupportedParams(&params);
ASSERT_EQ(err, C2_OK);
ALOGV("Number of total params - %zu", params.size());
- // Query Component Domain Type
- std::vector<std::unique_ptr<C2Param>> queried;
- err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
- C2_DONT_BLOCK, &queried);
- EXPECT_NE(queried.size(), 0u);
- std::string inputDomain =
- ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
- EXPECT_NE(inputDomain.empty(), true) << "Invalid Component Domain";
-
- // Configure Component Domain
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- C2PortMediaTypeSetting::input* portMediaType =
- C2PortMediaTypeSetting::input::From(queried[0].get());
- err = mComponent->config({portMediaType}, C2_DONT_BLOCK, &failures);
- ASSERT_EQ(err, C2_OK);
- ASSERT_EQ(failures.size(), 0u);
-
- // TODO: Query/Config on other common Params
+ // Query and config all the supported params
+ for (std::shared_ptr<C2ParamDescriptor> p : params) {
+ ALOGD("Querying index %d", (int)p->index());
+ err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
+ EXPECT_NE(queried.size(), 0u);
+ EXPECT_EQ(err, C2_OK);
+ err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
+ ASSERT_EQ(err, C2_OK);
+ ASSERT_EQ(failures.size(), 0u);
+ }
}
// Test Multiple Start Stop Reset Test
-TEST_F(Codec2ComponentHalTest, MultipleStartStopReset) {
+TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) {
ALOGV("Multiple Start Stop and Reset Test");
c2_status_t err = C2_OK;
-#define MAX_RETRY 16
-
for (size_t i = 0; i < MAX_RETRY; i++) {
err = mComponent->start();
ASSERT_EQ(err, C2_OK);
@@ -168,9 +186,67 @@ TEST_F(Codec2ComponentHalTest, MultipleStartStopReset) {
ASSERT_NE(err, C2_OK);
}
+// Test Component Release API
+TEST_F(Codec2ComponentHidlTest, MultipleRelease) {
+ ALOGV("Multiple Release Test");
+ c2_status_t err = mComponent->start();
+ ASSERT_EQ(err, C2_OK);
+
+ // Query Component Domain Type
+ std::vector<std::unique_ptr<C2Param>> queried;
+ err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &queried);
+ EXPECT_NE(queried.size(), 0u);
+
+ // Configure Component Domain
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ C2PortMediaTypeSetting::input* portMediaType =
+ C2PortMediaTypeSetting::input::From(queried[0].get());
+ err = mComponent->config({portMediaType}, C2_DONT_BLOCK, &failures);
+ ASSERT_EQ(err, C2_OK);
+ ASSERT_EQ(failures.size(), 0u);
+
+ for (size_t i = 0; i < MAX_RETRY; i++) {
+ err = mComponent->release();
+ ASSERT_EQ(err, C2_OK);
+ }
+}
+
+class Codec2ComponentInputTests : public Codec2ComponentHidlTest,
+ public ::testing::WithParamInterface<std::pair<uint32_t, bool> > {
+};
+
+TEST_P(Codec2ComponentInputTests, InputBufferTest) {
+ description("Tests for different inputs");
+
+ uint32_t flags = GetParam().first;
+ bool isNullBuffer = GetParam().second;
+ if (isNullBuffer) ALOGD("Testing for null input buffer with flag : %u", flags);
+ else ALOGD("Testing for empty input buffer with flag : %u", flags);
+ mEos = false;
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ ASSERT_NO_FATAL_FAILURE(testInputBuffer(
+ mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer));
+
+ ALOGD("Waiting for input consumption");
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+
+ if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+ ASSERT_EQ(mComponent->reset(), C2_OK);
+}
+
+INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests, ::testing::Values(
+ std::make_pair(0, true),
+ std::make_pair(C2FrameData::FLAG_END_OF_STREAM, true),
+ std::make_pair(0, false),
+ std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false),
+ std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false)));
+
} // anonymous namespace
-// TODO: Add test for Invalid work, Invalid Config handling
+// TODO: Add test for Invalid work,
// TODO: Add test for Invalid states
int main(int argc, char** argv) {
gEnv = new ComponentTestEnvironment();
diff --git a/codec2/hidl/1.0/mts/res/bbb_av1_176_144.av1 b/codec2/hidl/1.0/mts/res/bbb_av1_176_144.av1
new file mode 100644
index 0000000..1d67af9
--- /dev/null
+++ b/codec2/hidl/1.0/mts/res/bbb_av1_176_144.av1
Binary files differ
diff --git a/codec2/hidl/1.0/mts/res/bbb_av1_176_144.info b/codec2/hidl/1.0/mts/res/bbb_av1_176_144.info
new file mode 100644
index 0000000..cc51168
--- /dev/null
+++ b/codec2/hidl/1.0/mts/res/bbb_av1_176_144.info
@@ -0,0 +1,300 @@
+6027 1 0
+6879 0 33000
+5 0 66000
+532 0 100000
+5 0 133000
+2458 0 166000
+5 0 200000
+475 0 233000
+5 0 266000
+1262 0 300000
+5 0 333000
+554 0 366000
+27 0 400000
+6971 0 433000
+5 0 466000
+601 0 500000
+5 0 533000
+3276 0 566000
+5 0 600000
+658 0 633000
+5 0 666000
+1680 0 700000
+5 0 733000
+610 0 766000
+24 0 800000
+6728 0 833000
+5 0 866000
+764 0 900000
+5 0 933000
+2656 0 966000
+5 0 1000000
+462 0 1033000
+5 0 1066000
+1459 0 1100000
+5 0 1133000
+608 0 1166000
+24 0 1200000
+7038 0 1233000
+5 0 1266000
+721 0 1300000
+5 0 1333000
+3102 0 1366000
+5 0 1400000
+752 0 1433000
+5 0 1466000
+1815 0 1500000
+5 0 1533000
+755 0 1566000
+25 0 1600000
+7657 0 1633000
+5 0 1666000
+852 0 1700000
+5 0 1733000
+3537 0 1766000
+5 0 1800000
+673 0 1833000
+5 0 1866000
+1774 0 1900000
+5 0 1933000
+554 0 1966000
+24 0 2000000
+8028 0 2033000
+5 0 2066000
+715 0 2100000
+5 0 2133000
+3395 0 2166000
+5 0 2200000
+736 0 2233000
+5 0 2266000
+1759 0 2300000
+5 0 2333000
+605 0 2366000
+23 0 2400000
+7651 0 2433000
+5 0 2466000
+619 0 2500000
+5 0 2533000
+2788 0 2566000
+5 0 2600000
+556 0 2633000
+5 0 2666000
+1335 0 2700000
+5 0 2733000
+521 0 2766000
+24 0 2800000
+2274 0 2833000
+676 0 2866000
+25 0 2900000
+6224 0 2933000
+5798 0 2966000
+5 0 3000000
+448 0 3033000
+5 0 3066000
+1950 0 3100000
+5 0 3133000
+386 0 3166000
+5 0 3200000
+1218 0 3233000
+5 0 3266000
+1316 0 3300000
+5 0 3333000
+580 0 3366000
+26 0 3400000
+6673 0 3433000
+5 0 3466000
+473 0 3500000
+5 0 3533000
+2467 0 3566000
+5 0 3600000
+429 0 3633000
+5 0 3666000
+1420 0 3700000
+5 0 3733000
+583 0 3766000
+29 0 3800000
+8492 0 3833000
+5 0 3866000
+720 0 3900000
+5 0 3933000
+3635 0 3966000
+5 0 4000000
+621 0 4033000
+5 0 4066000
+1969 0 4100000
+5 0 4133000
+49 0 4166000
+25 0 4200000
+7416 0 4233000
+5 0 4266000
+947 0 4300000
+5 0 4333000
+3713 0 4366000
+5 0 4400000
+714 0 4433000
+5 0 4466000
+2003 0 4500000
+5 0 4533000
+750 0 4566000
+25 0 4600000
+8470 0 4633000
+5 0 4666000
+737 0 4700000
+5 0 4733000
+4094 0 4766000
+5 0 4800000
+1019 0 4833000
+5 0 4866000
+2160 0 4900000
+5 0 4933000
+828 0 4966000
+24 0 5000000
+9282 0 5033000
+5 0 5066000
+655 0 5100000
+5 0 5133000
+3491 0 5166000
+5 0 5200000
+651 0 5233000
+5 0 5266000
+1906 0 5300000
+5 0 5333000
+662 0 5366000
+24 0 5400000
+9724 0 5433000
+5 0 5466000
+617 0 5500000
+5 0 5533000
+3145 0 5566000
+5 0 5600000
+578 0 5633000
+5 0 5666000
+1592 0 5700000
+5 0 5733000
+569 0 5766000
+25 0 5800000
+10015 0 5833000
+5 0 5866000
+609 0 5900000
+5 0 5933000
+3618 0 5966000
+5 0 6000000
+734 0 6033000
+5 0 6066000
+1748 0 6100000
+5 0 6133000
+550 0 6166000
+24 0 6200000
+8806 0 6233000
+5 0 6266000
+498 0 6300000
+5 0 6333000
+2913 0 6366000
+5 0 6400000
+597 0 6433000
+5 0 6466000
+1235 0 6500000
+5 0 6533000
+362 0 6566000
+24 0 6600000
+6592 0 6633000
+5 0 6666000
+468 0 6700000
+5 0 6733000
+1920 0 6766000
+5 0 6800000
+419 0 6833000
+5 0 6866000
+843 0 6900000
+5 0 6933000
+237 0 6966000
+24 0 7000000
+2687 0 7033000
+5 0 7066000
+399 0 7100000
+5 0 7133000
+200 0 7166000
+143 0 7200000
+25 0 7233000
+12603 0 7266000
+1139 0 7300000
+5 0 7333000
+56 0 7366000
+5 0 7400000
+273 0 7433000
+5 0 7466000
+48 0 7500000
+5 0 7533000
+102 0 7566000
+5 0 7600000
+39 0 7633000
+24 0 7666000
+3635 0 7700000
+5 0 7733000
+46 0 7766000
+5 0 7800000
+647 0 7833000
+5 0 7866000
+61 0 7900000
+5 0 7933000
+824 0 7966000
+5 0 8000000
+691 0 8033000
+27 0 8066000
+4573 0 8100000
+5 0 8133000
+473 0 8166000
+5 0 8200000
+1637 0 8233000
+5 0 8266000
+451 0 8300000
+5 0 8333000
+969 0 8366000
+5 0 8400000
+234 0 8433000
+24 0 8466000
+3361 0 8500000
+5 0 8533000
+168 0 8566000
+5 0 8600000
+662 0 8633000
+5 0 8666000
+129 0 8700000
+5 0 8733000
+443 0 8766000
+5 0 8800000
+183 0 8833000
+23 0 8866000
+2769 0 8900000
+5 0 8933000
+182 0 8966000
+5 0 9000000
+890 0 9033000
+5 0 9066000
+171 0 9100000
+5 0 9133000
+599 0 9166000
+5 0 9200000
+236 0 9233000
+23 0 9266000
+2316 0 9300000
+5 0 9333000
+333 0 9366000
+5 0 9400000
+759 0 9433000
+5 0 9466000
+210 0 9500000
+5 0 9533000
+324 0 9566000
+5 0 9600000
+98 0 9633000
+23 0 9666000
+1107 0 9700000
+5 0 9733000
+42 0 9766000
+5 0 9800000
+145 0 9833000
+5 0 9866000
+109 0 9900000
+89 0 9933000
+24 0 9966000 \ No newline at end of file
diff --git a/codec2/hidl/1.0/mts/res/bbb_av1_640_360.av1 b/codec2/hidl/1.0/mts/res/bbb_av1_640_360.av1
new file mode 100644
index 0000000..529bace
--- /dev/null
+++ b/codec2/hidl/1.0/mts/res/bbb_av1_640_360.av1
Binary files differ
diff --git a/codec2/hidl/1.0/mts/res/bbb_av1_640_360.info b/codec2/hidl/1.0/mts/res/bbb_av1_640_360.info
new file mode 100644
index 0000000..fca7833
--- /dev/null
+++ b/codec2/hidl/1.0/mts/res/bbb_av1_640_360.info
@@ -0,0 +1,167 @@
+12571 1 0
+9881 0 33000
+5 0 66000
+544 0 100000
+5 0 133000
+2642 0 166000
+5 0 200000
+531 0 233000
+5 0 266000
+1359 0 300000
+5 0 333000
+551 0 366000
+28 0 400000
+10791 0 433000
+5 0 466000
+655 0 500000
+5 0 533000
+3769 0 566000
+5 0 600000
+711 0 633000
+5 0 666000
+2004 0 700000
+5 0 733000
+657 0 766000
+26 0 800000
+8969 0 833000
+5 0 866000
+630 0 900000
+5 0 933000
+2787 0 966000
+5 0 1000000
+404 0 1033000
+5 0 1066000
+1518 0 1100000
+5 0 1133000
+493 0 1166000
+26 0 1200000
+9900 0 1233000
+5 0 1266000
+620 0 1300000
+5 0 1333000
+3072 0 1366000
+5 0 1400000
+668 0 1433000
+5 0 1466000
+1821 0 1500000
+5 0 1533000
+682 0 1566000
+26 0 1600000
+9560 0 1633000
+5 0 1666000
+667 0 1700000
+5 0 1733000
+3249 0 1766000
+5 0 1800000
+589 0 1833000
+5 0 1866000
+1816 0 1900000
+5 0 1933000
+548 0 1966000
+26 0 2000000
+9916 0 2033000
+5 0 2066000
+628 0 2100000
+5 0 2133000
+3034 0 2166000
+5 0 2200000
+590 0 2233000
+5 0 2266000
+1581 0 2300000
+5 0 2333000
+524 0 2366000
+26 0 2400000
+8182 0 2433000
+5 0 2466000
+552 0 2500000
+5 0 2533000
+2412 0 2566000
+5 0 2600000
+489 0 2633000
+5 0 2666000
+1227 0 2700000
+5 0 2733000
+432 0 2766000
+26 0 2800000
+2017 0 2833000
+516 0 2866000
+26 0 2900000
+16619 0 2933000
+6710 0 2966000
+5 0 3000000
+425 0 3033000
+5 0 3066000
+1964 0 3100000
+5 0 3133000
+386 0 3166000
+5 0 3200000
+1129 0 3233000
+5 0 3266000
+1156 0 3300000
+5 0 3333000
+486 0 3366000
+28 0 3400000
+10304 0 3433000
+5 0 3466000
+412 0 3500000
+5 0 3533000
+2608 0 3566000
+5 0 3600000
+397 0 3633000
+5 0 3666000
+1514 0 3700000
+5 0 3733000
+533 0 3766000
+26 0 3800000
+11698 0 3833000
+5 0 3866000
+542 0 3900000
+5 0 3933000
+3334 0 3966000
+5 0 4000000
+547 0 4033000
+5 0 4066000
+1809 0 4100000
+5 0 4133000
+55 0 4166000
+26 0 4200000
+9496 0 4233000
+5 0 4266000
+658 0 4300000
+5 0 4333000
+3232 0 4366000
+5 0 4400000
+600 0 4433000
+5 0 4466000
+1766 0 4500000
+5 0 4533000
+550 0 4566000
+25 0 4600000
+9951 0 4633000
+5 0 4666000
+624 0 4700000
+5 0 4733000
+3617 0 4766000
+5 0 4800000
+644 0 4833000
+5 0 4866000
+1841 0 4900000
+5 0 4933000
+649 0 4966000
+25 0 5000000
+9901 0 5033000
+5 0 5066000
+515 0 5100000
+5 0 5133000
+2814 0 5166000
+5 0 5200000
+511 0 5233000
+5 0 5266000
+1521 0 5300000
+5 0 5333000
+509 0 5366000
+26 0 5400000
+10579 0 5433000
+5 0 5466000
+575 0 5500000
+5 0 5533000 \ No newline at end of file
diff --git a/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoDecTest.cpp b/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoDecTest.cpp
index 1a3820d..9a42d72 100644
--- a/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoDecTest.cpp
+++ b/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoDecTest.cpp
@@ -101,7 +101,7 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
const StringToName kStringToName[] = {
{"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
- {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9},
+ {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
};
const size_t kNumStringToName =
@@ -142,79 +142,48 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
- // handle configuration changes in work done
- if (!work->worklets.empty() &&
- (work->worklets.front()->output.configUpdate.size() != 0)) {
- ALOGV("Config Update");
- std::vector<std::unique_ptr<C2Param>> updates =
- std::move(work->worklets.front()->output.configUpdate);
- std::vector<C2Param*> configParam;
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- for (size_t i = 0; i < updates.size(); ++i) {
- C2Param* param = updates[i].get();
- if (param->index() ==
- C2VideoSizeStreamInfo::output::PARAM_TYPE) {
- ALOGV("Received C2VideoSizeStreamInfo");
- configParam.push_back(param);
- }
- }
- mComponent->config(configParam, C2_DONT_BLOCK, &failures);
- ASSERT_EQ(failures.size(), 0u);
- }
-
- mFramesReceived++;
- mEos = (work->worklets.front()->output.flags &
- C2FrameData::FLAG_END_OF_STREAM) != 0;
- auto frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- work->input.ordinal.frameIndex.peeku());
-
- // For decoder components current timestamp always exceeds
- // previous timestamp
- typedef std::unique_lock<std::mutex> ULock;
- bool codecConfig = ((work->worklets.front()->output.flags &
- C2FrameData::FLAG_CODEC_CONFIG) != 0);
- if (!codecConfig &&
- !work->worklets.front()->output.buffers.empty()) {
- EXPECT_GE(
- (work->worklets.front()->output.ordinal.timestamp.peeku()),
- mTimestampUs);
- mTimestampUs =
- work->worklets.front()->output.ordinal.timestamp.peeku();
-
- ULock l(mQueueLock);
- if (mTimestampDevTest) {
- bool tsHit = false;
- std::list<uint64_t>::iterator it = mTimestampUslist.begin();
- while (it != mTimestampUslist.end()) {
- if (*it == mTimestampUs) {
- mTimestampUslist.erase(it);
- tsHit = true;
- break;
+ if (!work->worklets.empty()) {
+ // For decoder components current timestamp always exceeds
+ // previous timestamp
+ typedef std::unique_lock<std::mutex> ULock;
+ bool codecConfig = ((work->worklets.front()->output.flags &
+ C2FrameData::FLAG_CODEC_CONFIG) != 0);
+ if (!codecConfig &&
+ !work->worklets.front()->output.buffers.empty()) {
+ EXPECT_GE(
+ (work->worklets.front()->output.ordinal.timestamp.peeku()),
+ mTimestampUs);
+ mTimestampUs =
+ work->worklets.front()->output.ordinal.timestamp.peeku();
+
+ ULock l(mQueueLock);
+ if (mTimestampDevTest) {
+ bool tsHit = false;
+ std::list<uint64_t>::iterator it = mTimestampUslist.begin();
+ while (it != mTimestampUslist.end()) {
+ if (*it == mTimestampUs) {
+ mTimestampUslist.erase(it);
+ tsHit = true;
+ break;
+ }
+ it++;
}
- it++;
- }
- if (tsHit == false) {
- if (mTimestampUslist.empty() == false) {
- EXPECT_EQ(tsHit, true)
- << "TimeStamp not recognized";
- } else {
- std::cout << "[ INFO ] Received non-zero "
- "output / TimeStamp not recognized \n";
+ if (tsHit == false) {
+ if (mTimestampUslist.empty() == false) {
+ EXPECT_EQ(tsHit, true)
+ << "TimeStamp not recognized";
+ } else {
+ std::cout << "[ INFO ] Received non-zero "
+ "output / TimeStamp not recognized \n";
+ }
}
}
}
- }
-
- work->input.buffers.clear();
- work->worklets.clear();
- {
- ULock l(mQueueLock);
- mWorkQueue.push_back(std::move(work));
- if (!mFlushedIndices.empty()) {
- mFlushedIndices.erase(frameIndexIt);
- }
- mQueueCondition.notify_all();
+ bool mCsd;
+ workDone(mComponent, work, mFlushedIndices, mQueueLock,
+ mQueueCondition, mWorkQueue, mEos, mCsd,
+ mFramesReceived);
+ (void)mCsd;
}
}
}
@@ -227,6 +196,7 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
hevc,
vp8,
vp9,
+ av1,
unknown_comp,
};
@@ -341,6 +311,11 @@ void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL,
"bbb_vp9_640x360_1600kbps_30fps.vp9"},
{"bbb_vp9_176x144_285kbps_60fps.info",
"bbb_vp9_640x360_1600kbps_30fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::av1,
+ {"bbb_av1_640_360.av1",
+ "bbb_av1_176_144.av1"},
+ {"bbb_av1_640_360.info",
+ "bbb_av1_176_144.info"}},
};
for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
@@ -352,11 +327,11 @@ void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL,
}
}
-void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &component,
- std::mutex &queueLock, std::condition_variable &queueCondition,
- std::list<std::unique_ptr<C2Work>> &workQueue,
- std::list<uint64_t> &flushedIndices,
- std::shared_ptr<C2BlockPool> &linearPool,
+void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue,
+ std::list<uint64_t>& flushedIndices,
+ std::shared_ptr<C2BlockPool>& linearPool,
std::ifstream& eleStream,
android::Vector<FrameInfo>* Info,
int offset, int range, bool signalEOS = true) {
@@ -397,35 +372,37 @@ void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
int size = (*Info)[frameID].bytesCount;
char* data = (char*)malloc(size);
+ ASSERT_NE(data, nullptr);
eleStream.read(data, size);
ASSERT_EQ(eleStream.gcount(), size);
- std::shared_ptr<C2LinearBlock> block;
- ASSERT_EQ(C2_OK,
- linearPool->fetchLinearBlock(
- size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
- &block));
- ASSERT_TRUE(block);
-
- // Write View
- C2WriteView view = block->map().get();
- if (view.error() != C2_OK) {
- fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
- break;
- }
- ASSERT_EQ((size_t)size, view.capacity());
- ASSERT_EQ(0u, view.offset());
- ASSERT_EQ((size_t)size, view.size());
+ work->input.buffers.clear();
+ if (size) {
+ std::shared_ptr<C2LinearBlock> block;
+ ASSERT_EQ(C2_OK,
+ linearPool->fetchLinearBlock(
+ size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+ &block));
+ ASSERT_TRUE(block);
+
+ // Write View
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
+ break;
+ }
+ ASSERT_EQ((size_t)size, view.capacity());
+ ASSERT_EQ(0u, view.offset());
+ ASSERT_EQ((size_t)size, view.size());
- memcpy(view.base(), data, size);
+ memcpy(view.base(), data, size);
- work->input.buffers.clear();
- work->input.buffers.emplace_back(new LinearBuffer(block));
+ work->input.buffers.emplace_back(new LinearBuffer(block));
+ free(data);
+ }
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
- free(data);
-
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
@@ -437,29 +414,6 @@ void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
}
}
-void waitOnInputConsumption(std::mutex& queueLock,
- std::condition_variable& queueCondition,
- std::list<std::unique_ptr<C2Work>>& workQueue,
- size_t bufferCount = MAX_INPUT_BUFFERS) {
- typedef std::unique_lock<std::mutex> ULock;
- uint32_t queueSize;
- uint32_t maxRetry = 0;
- {
- ULock l(queueLock);
- queueSize = workQueue.size();
- }
- while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) {
- ULock l(queueLock);
- if (queueSize != workQueue.size()) {
- queueSize = workQueue.size();
- maxRetry = 0;
- } else {
- queueCondition.wait_for(l, TIME_OUT);
- maxRetry++;
- }
- }
-}
-
TEST_F(Codec2VideoDecHidlTest, validateCompName) {
if (mDisableTest) return;
ALOGV("Checks if the given component is a valid video component");
@@ -489,8 +443,8 @@ TEST_F(Codec2VideoDecHidlTest, DecodeTest) {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
eleInfo >> timestamp;
- bool codecConfig =
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+ bool codecConfig = flags ?
+ ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
if (mTimestampDevTest && !codecConfig)
mTimestampUslist.push_back(timestamp);
Info.push_back({bytesCount, flags, timestamp});
@@ -560,8 +514,9 @@ TEST_F(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
eleInfo >> timestamp;
timestamp += timestampOffset;
Info.push_back({bytesCount, flags, timestamp});
- bool codecConfig =
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+ bool codecConfig = flags ?
+ ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+
{
ULock l(mQueueLock);
if (mTimestampDevTest && !codecConfig)
@@ -677,8 +632,8 @@ TEST_F(Codec2VideoDecHidlTest, ThumbnailTest) {
EXPECT_GE(mFramesReceived, 1U);
ASSERT_EQ(mEos, true);
ASSERT_EQ(mComponent->stop(), C2_OK);
- ASSERT_EQ(mComponent->release(), C2_OK);
}
+ ASSERT_EQ(mComponent->release(), C2_OK);
}
TEST_F(Codec2VideoDecHidlTest, EOSTest) {
@@ -711,7 +666,6 @@ TEST_F(Codec2VideoDecHidlTest, EOSTest) {
ASSERT_EQ(mComponent->queue(&items), C2_OK);
{
- typedef std::unique_lock<std::mutex> ULock;
ULock l(mQueueLock);
if (mWorkQueue.size() != MAX_INPUT_BUFFERS) {
mQueueCondition.wait_for(l, TIME_OUT);
@@ -722,46 +676,6 @@ TEST_F(Codec2VideoDecHidlTest, EOSTest) {
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2VideoDecHidlTest, EmptyBufferTest) {
- description("Tests empty input buffer");
- if (mDisableTest) return;
- typedef std::unique_lock<std::mutex> ULock;
- ASSERT_EQ(mComponent->start(), C2_OK);
- std::unique_ptr<C2Work> work;
- // Prepare C2Work
- {
- ULock l(mQueueLock);
- if (!mWorkQueue.empty()) {
- work.swap(mWorkQueue.front());
- mWorkQueue.pop_front();
- } else {
- ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test";
- }
- }
- ASSERT_NE(work, nullptr);
-
- work->input.flags = (C2FrameData::flags_t)0;
- work->input.ordinal.timestamp = 0;
- work->input.ordinal.frameIndex = 0;
- work->input.buffers.clear();
- work->worklets.clear();
- work->worklets.emplace_back(new C2Worklet);
-
- std::list<std::unique_ptr<C2Work>> items;
- items.push_back(std::move(work));
- ASSERT_EQ(mComponent->queue(&items), C2_OK);
-
- {
- typedef std::unique_lock<std::mutex> ULock;
- ULock l(mQueueLock);
- if (mWorkQueue.size() != MAX_INPUT_BUFFERS) {
- mQueueCondition.wait_for(l, TIME_OUT);
- }
- }
- ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
- ASSERT_EQ(mComponent->stop(), C2_OK);
-}
-
TEST_F(Codec2VideoDecHidlTest, FlushTest) {
description("Tests Flush calls");
if (mDisableTest) return;
@@ -873,6 +787,69 @@ TEST_F(Codec2VideoDecHidlTest, FlushTest) {
ASSERT_EQ(mComponent->stop(), C2_OK);
}
+TEST_F(Codec2VideoDecHidlTest, DecodeTestEmptyBuffersInserted) {
+ description("Decode with multiple empty input frames");
+ if (mDisableTest) return;
+
+ char mURL[512], info[512];
+ std::ifstream eleStream, eleInfo;
+
+ strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(info, gEnv->getRes().c_str());
+ GetURLForComponent(mCompName, mURL, info);
+
+ eleInfo.open(info);
+ ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
+ android::Vector<FrameInfo> Info;
+ int bytesCount = 0;
+ uint32_t frameId = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ bool codecConfig = false;
+ // This test introduces empty CSD after every 20th frame
+ // and empty input frames at an interval of 5 frames.
+ while (1) {
+ if (!(frameId % 5)) {
+ if (!(frameId % 20)) flags = 32;
+ else flags = 0;
+ bytesCount = 0;
+ } else {
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ codecConfig = flags ?
+ ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ }
+ Info.push_back({bytesCount, flags, timestamp});
+ frameId++;
+ }
+ eleInfo.close();
+
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ ALOGV("mURL : %s", mURL);
+ eleStream.open(mURL, std::ifstream::binary);
+ ASSERT_EQ(eleStream.is_open(), true);
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(
+ mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
+ mLinearPool, eleStream, &Info, 0, (int)Info.size()));
+
+ // blocking call to ensures application to Wait till all the inputs are
+ // consumed
+ if (!mEos) {
+ ALOGV("Waiting for input consumption");
+ ASSERT_NO_FATAL_FAILURE(
+ waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ }
+
+ eleStream.close();
+ if (mFramesReceived != Info.size()) {
+ ALOGE("Input buffer count and Output buffer count mismatch");
+ ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
+ Info.size());
+ ASSERT_TRUE(false);
+ }
+}
+
} // anonymous namespace
// TODO : Video specific configuration Test
diff --git a/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoEncTest.cpp b/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoEncTest.cpp
index 87b7902..8585c87 100644
--- a/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoEncTest.cpp
+++ b/codec2/hidl/1.0/mts/video/MtsHidlC2V1_0TargetVideoEncTest.cpp
@@ -139,40 +139,11 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
- // handle configuration changes in work done
- if (!work->worklets.empty() &&
- (work->worklets.front()->output.configUpdate.size() != 0)) {
- ALOGV("Config Update");
- std::vector<std::unique_ptr<C2Param>> updates =
- std::move(work->worklets.front()->output.configUpdate);
- std::vector<C2Param*> configParam;
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- for (size_t i = 0; i < updates.size(); ++i) {
- C2Param* param = updates[i].get();
- if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) {
- mCsd = true;
- }
- }
- }
- mFramesReceived++;
- if (work->result != C2_OK) mFailedWorkReceived++;
- mEos = (work->worklets.front()->output.flags &
- C2FrameData::FLAG_END_OF_STREAM) != 0;
- auto frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- work->input.ordinal.frameIndex.peeku());
- ALOGV("WorkDone: frameID received %d",
- (int)work->worklets.front()->output.ordinal.frameIndex.peeku());
- work->input.buffers.clear();
- work->worklets.clear();
- {
- typedef std::unique_lock<std::mutex> ULock;
- ULock l(mQueueLock);
- mWorkQueue.push_back(std::move(work));
- if (!mFlushedIndices.empty()) {
- mFlushedIndices.erase(frameIndexIt);
- }
- mQueueCondition.notify_all();
+ if (!work->worklets.empty()) {
+ if (work->result != C2_OK) mFailedWorkReceived++;
+ workDone(mComponent, work, mFlushedIndices, mQueueLock,
+ mQueueCondition, mWorkQueue, mEos, mCsd,
+ mFramesReceived);
}
}
}
@@ -272,11 +243,11 @@ void GetURLForComponent(char* URL) {
strcat(URL, "bbb_352x288_420p_30fps_32frames.yuv");
}
-void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &component,
- std::mutex &queueLock, std::condition_variable &queueCondition,
- std::list<std::unique_ptr<C2Work>> &workQueue,
- std::list<uint64_t> &flushedIndices,
- std::shared_ptr<C2BlockPool> &graphicPool,
+void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue,
+ std::list<uint64_t>& flushedIndices,
+ std::shared_ptr<C2BlockPool>& graphicPool,
std::ifstream& eleStream, uint32_t frameID,
uint32_t nFrames, uint32_t nWidth, int32_t nHeight,
bool flushed = false,bool signalEOS = true) {
@@ -319,6 +290,7 @@ void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
flushedIndices.emplace_back(frameID);
}
char* data = (char*)malloc(bytesCount);
+ ASSERT_NE(data, nullptr);
memset(data, 0, bytesCount);
if (eleStream.is_open()) {
eleStream.read(data, bytesCount);
@@ -365,30 +337,6 @@ void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &comp
}
}
-void waitOnInputConsumption(std::mutex &queueLock,
- std::condition_variable &queueCondition,
- std::list<std::unique_ptr<C2Work>> &workQueue,
- size_t bufferCount = MAX_INPUT_BUFFERS) {
- typedef std::unique_lock<std::mutex> ULock;
- uint32_t queueSize;
- int maxRetry = 0;
- {
- ULock l(queueLock);
- queueSize = workQueue.size();
- }
- while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) {
- ULock l(queueLock);
- if (queueSize != workQueue.size()) {
- queueSize = workQueue.size();
- maxRetry = 0;
- } else {
- queueCondition.wait_for(l, TIME_OUT);
- maxRetry++;
- }
- }
-}
-
-
TEST_F(Codec2VideoEncHidlTest, validateCompName) {
if (mDisableTest) return;
ALOGV("Checks if the given component is a valid video component");
@@ -488,46 +436,6 @@ TEST_F(Codec2VideoEncHidlTest, EOSTest) {
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2VideoEncHidlTest, EmptyBufferTest) {
- description("Tests empty input buffer");
- if (mDisableTest) return;
- ASSERT_EQ(mComponent->start(), C2_OK);
-
- typedef std::unique_lock<std::mutex> ULock;
- std::unique_ptr<C2Work> work;
- {
- ULock l(mQueueLock);
- if (!mWorkQueue.empty()) {
- work.swap(mWorkQueue.front());
- mWorkQueue.pop_front();
- } else {
- ALOGE("mWorkQueue Empty is not expected at the start of the test");
- ASSERT_TRUE(false);
- }
- }
- ASSERT_NE(work, nullptr);
- work->input.flags = (C2FrameData::flags_t)0;
- work->input.ordinal.timestamp = 0;
- work->input.ordinal.frameIndex = 0;
- work->input.buffers.clear();
- work->worklets.clear();
- work->worklets.emplace_back(new C2Worklet);
-
- std::list<std::unique_ptr<C2Work>> items;
- items.push_back(std::move(work));
- ASSERT_EQ(mComponent->queue(&items), C2_OK);
- uint32_t queueSize;
- {
- ULock l(mQueueLock);
- queueSize = mWorkQueue.size();
- if (queueSize < MAX_INPUT_BUFFERS) {
- mQueueCondition.wait_for(l, TIME_OUT);
- }
- }
- ASSERT_EQ(mWorkQueue.size(), (uint32_t)MAX_INPUT_BUFFERS);
- ASSERT_EQ(mComponent->stop(), C2_OK);
-}
-
TEST_F(Codec2VideoEncHidlTest, FlushTest) {
description("Test Request for flush");
if (mDisableTest) return;
diff --git a/codec2/hidl/1.0/mts/video/media_c2_video_hidl_test_common.h b/codec2/hidl/1.0/mts/video/media_c2_video_hidl_test_common.h
index 1215b13..dd45557 100644
--- a/codec2/hidl/1.0/mts/video/media_c2_video_hidl_test_common.h
+++ b/codec2/hidl/1.0/mts/video/media_c2_video_hidl_test_common.h
@@ -17,9 +17,6 @@
#ifndef MEDIA_C2_VIDEO_HIDL_TEST_COMMON_H
#define MEDIA_C2_VIDEO_HIDL_TEST_COMMON_H
-#define MAX_RETRY 20
-#define TIME_OUT 400ms
-#define MAX_INPUT_BUFFERS 8
#define ENCODER_TIMESTAMP_INCREMENT 40000
#define ENC_NUM_FRAMES 32
#define ENC_DEFAULT_FRAME_WIDTH 352
diff --git a/codec2/hidl/1.0/utils/Android.bp b/codec2/hidl/1.0/utils/Android.bp
index 1641e51..ed404ad 100644
--- a/codec2/hidl/1.0/utils/Android.bp
+++ b/codec2/hidl/1.0/utils/Android.bp
@@ -16,6 +16,7 @@ cc_library {
],
header_libs: [
+ "libgui_headers",
"libsystem_headers",
"libstagefright_codec2_internal", // private
],
@@ -26,6 +27,7 @@ cc_library {
"android.hardware.media.bufferpool@1.0",
"android.hardware.media.omx@1.0",
"android.hardware.media@1.0",
+ "android.hidl.token@1.0-utils",
"hardware.google.media.c2@1.0",
"libbase",
"libcutils",
@@ -46,6 +48,7 @@ cc_library {
],
export_shared_lib_headers: [
+ "android.hidl.token@1.0-utils",
"hardware.google.media.c2@1.0",
"libhidlbase",
"libstagefright_bufferpool@1.0",
diff --git a/codec2/hidl/1.0/utils/ComponentStore.cpp b/codec2/hidl/1.0/utils/ComponentStore.cpp
index 4941ede..9a46b77 100644
--- a/codec2/hidl/1.0/utils/ComponentStore.cpp
+++ b/codec2/hidl/1.0/utils/ComponentStore.cpp
@@ -24,7 +24,7 @@
#include <codec2/hidl/1.0/ConfigurableC2Intf.h>
#include <codec2/hidl/1.0/types.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <media/stagefright/bqhelper/GraphicBufferSource.h>
#include <C2PlatformSupport.h>
diff --git a/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
index eea4e9d..4f4af6a 100644
--- a/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
+++ b/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
@@ -168,7 +168,7 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper {
std::shared_ptr<C2GraphicAllocation> alloc;
C2Handle* handle = WrapNativeCodec2GrallocHandle(
- native_handle_clone(buffer->handle),
+ buffer->handle,
buffer->width, buffer->height,
buffer->format, buffer->usage, buffer->stride);
mAllocatorMutex.lock();
diff --git a/codec2/hidl/1.0/utils/types.cpp b/codec2/hidl/1.0/utils/types.cpp
index 56db4d5..10a7cef 100644
--- a/codec2/hidl/1.0/utils/types.cpp
+++ b/codec2/hidl/1.0/utils/types.cpp
@@ -20,8 +20,7 @@
#include <codec2/hidl/1.0/types.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
-
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <C2AllocatorIon.h>
#include <C2AllocatorGralloc.h>
#include <C2BlockInternal.h>
@@ -1657,7 +1656,8 @@ void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
}
sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) {
- sp<HGraphicBufferProducer> hgbp = igbp->getHalInterface();
+ sp<HGraphicBufferProducer> hgbp =
+ igbp->getHalInterface<HGraphicBufferProducer>();
return hgbp ? hgbp :
new TWGraphicBufferProducer<HGraphicBufferProducer>(igbp);
}
diff --git a/codec2/hidl/client/client.cpp b/codec2/hidl/client/client.cpp
index ff67681..8b4b48b 100644
--- a/codec2/hidl/client/client.cpp
+++ b/codec2/hidl/client/client.cpp
@@ -30,8 +30,8 @@
#include <bufferpool/ClientManager.h>
#include <cutils/native_handle.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <hidl/HidlSupport.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
#undef LOG
#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
@@ -553,6 +553,7 @@ const std::vector<C2Component::Traits>& Codec2Client::listComponents() const {
for (size_t i = 0; i < t.size(); ++i) {
c2_status_t status = objcpy(
&mTraitsList[i], &mAliasesBuffer[i], t[i]);
+ mTraitsList[i].owner = mInstanceName;
if (status != C2_OK) {
ALOGE("listComponents -- corrupted output.");
return;
@@ -1130,7 +1131,8 @@ c2_status_t Codec2Client::Component::setOutputSurface(
C2BlockPool::local_id_t blockPoolId,
const sp<IGraphicBufferProducer>& surface,
uint32_t generation) {
- sp<HGraphicBufferProducer> igbp = surface->getHalInterface();
+ sp<HGraphicBufferProducer> igbp =
+ surface->getHalInterface<HGraphicBufferProducer>();
if (!igbp) {
igbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(surface);
}
diff --git a/codec2/hidl/services/Android.bp b/codec2/hidl/services/Android.bp
index 6b73383..00d25ca 100644
--- a/codec2/hidl/services/Android.bp
+++ b/codec2/hidl/services/Android.bp
@@ -38,8 +38,9 @@ cc_binary {
}
cc_library_shared {
- name: "libmedia_codecserviceregistrant",
- soc_specific: true,
+ name: "libcodec2_serviceregistrant",
+ // need vendor version for update packaging, system version may have more dependencies
+ vendor_available: true,
srcs: [
"C2SoftwareCodecServiceRegistrant.cpp",
],
@@ -49,46 +50,45 @@ cc_library_shared {
],
shared_libs: [
- "android.hardware.media.omx@1.0",
"hardware.google.media.c2@1.0",
"liblog",
"libcodec2_hidl_utils@1.0",
"libstagefright_codec2_vndk",
- "libstagefright_omx",
- "libstagefright_xmlparser",
"libutils",
],
// Codecs
- required: [
- "libstagefright_soft_c2avcdec.vendor",
- "libstagefright_soft_c2avcenc.vendor",
- "libstagefright_soft_c2aacdec.vendor",
- "libstagefright_soft_c2aacenc.vendor",
- "libstagefright_soft_c2amrnbdec.vendor",
- "libstagefright_soft_c2amrnbenc.vendor",
- "libstagefright_soft_c2amrwbdec.vendor",
- "libstagefright_soft_c2amrwbenc.vendor",
- "libstagefright_soft_c2hevcdec.vendor",
- "libstagefright_soft_c2g711alawdec.vendor",
- "libstagefright_soft_c2g711mlawdec.vendor",
- "libstagefright_soft_c2mpeg2dec.vendor",
- "libstagefright_soft_c2h263dec.vendor",
- "libstagefright_soft_c2h263enc.vendor",
- "libstagefright_soft_c2mpeg4dec.vendor",
- "libstagefright_soft_c2mpeg4enc.vendor",
- "libstagefright_soft_c2mp3dec.vendor",
- "libstagefright_soft_c2vorbisdec.vendor",
- "libstagefright_soft_c2opusdec.vendor",
- "libstagefright_soft_c2vp8dec.vendor",
- "libstagefright_soft_c2vp9dec.vendor",
- "libstagefright_soft_c2vp8enc.vendor",
- "libstagefright_soft_c2vp9enc.vendor",
- "libstagefright_soft_c2rawdec.vendor",
- "libstagefright_soft_c2flacdec.vendor",
- "libstagefright_soft_c2flacenc.vendor",
- "libstagefright_soft_c2gsmdec.vendor",
+ runtime_libs: [
+ "libstagefright_soft_c2avcdec",
+ "libstagefright_soft_c2avcenc",
+ "libstagefright_soft_c2aacdec",
+ "libstagefright_soft_c2aacenc",
+ "libstagefright_soft_c2amrnbdec",
+ "libstagefright_soft_c2amrnbenc",
+ "libstagefright_soft_c2amrwbdec",
+ "libstagefright_soft_c2amrwbenc",
+ "libstagefright_soft_c2hevcdec",
+ "libstagefright_soft_c2g711alawdec",
+ "libstagefright_soft_c2g711mlawdec",
+ "libstagefright_soft_c2mpeg2dec",
+ "libstagefright_soft_c2h263dec",
+ "libstagefright_soft_c2h263enc",
+ "libstagefright_soft_c2mpeg4dec",
+ "libstagefright_soft_c2mpeg4enc",
+ "libstagefright_soft_c2mp3dec",
+ "libstagefright_soft_c2vorbisdec",
+ "libstagefright_soft_c2opusdec",
+ "libstagefright_soft_c2vp8dec",
+ "libstagefright_soft_c2vp9dec",
+ "libstagefright_soft_c2vp8enc",
+ "libstagefright_soft_c2vp9enc",
+ "libstagefright_soft_c2rawdec",
+ "libstagefright_soft_c2flacdec",
+ "libstagefright_soft_c2flacenc",
+ "libstagefright_soft_c2gsmdec",
+ "libstagefright_soft_c2xaacdec",
],
+
compile_multilib: "32",
}
diff --git a/codec2/hidl/services/Android.mk b/codec2/hidl/services/Android.mk
deleted file mode 100644
index d9b28e7..0000000
--- a/codec2/hidl/services/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# vendor service seccomp policy
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH), x86 x86_64 arm arm64))
-include $(CLEAR_VARS)
-LOCAL_MODULE := codec2.vendor.base.policy
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy
-LOCAL_REQUIRED_MODULES := crash_dump.policy
-ifdef TARGET_2ND_ARCH
- ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
- LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_2ND_ARCH).policy
- else
- LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
- endif
-else
- LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
-endif
-include $(BUILD_PREBUILT)
-endif
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
-
diff --git a/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp b/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp
index eddfbcb..04efa44 100644
--- a/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp
+++ b/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp
@@ -20,7 +20,6 @@
#include <C2PlatformSupport.h>
#include <codec2/hidl/1.0/ComponentStore.h>
#include <media/CodecServiceRegistrant.h>
-#include <media/stagefright/omx/1.0/Omx.h>
#include <log/log.h>
extern "C" void RegisterCodecServices() {
@@ -38,15 +37,5 @@ extern "C" void RegisterCodecServices() {
ALOGI("Codec2's IComponentStore software service created.");
}
}
-
- // Default codec services
- using namespace ::android::hardware::media::omx::V1_0;
- android::sp<IOmx> omx = new implementation::Omx();
- if (omx == nullptr) {
- ALOGE("Cannot create IOmx HAL service.");
- } else if (omx->registerAsService() != android::OK) {
- ALOGE("Cannot register IOmx HAL service.");
- }
-
}
diff --git a/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy b/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy
deleted file mode 100644
index d5871d1..0000000
--- a/codec2/hidl/services/seccomp_policy/codec2.software.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Organized by frequency of systemcall - in descending order for
-# best performance.
-futex: 1
-ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
-close: 1
-writev: 1
-dup: 1
-ppoll: 1
-mmap2: 1
-getrandom: 1
-
-# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
-# parser support for '<' is in this needs to be modified to also prevent
-# |old_address| and |new_address| from touching the exception vector page, which
-# on ARM is statically loaded at 0xffff 0000. See
-# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
-# for more details.
-mremap: arg3 == 3
-munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
-sigaltstack: 1
-clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
-exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
-lseek: 1
-_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
-fstatat64: 1
-ugetrlimit: 1
-getdents64: 1
-getrandom: 1
-
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
-
diff --git a/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy b/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy
deleted file mode 100644
index 20c7625..0000000
--- a/codec2/hidl/services/seccomp_policy/codec2.software.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-read: 1
-mprotect: 1
-prctl: 1
-openat: 1
-getuid32: 1
-writev: 1
-ioctl: 1
-close: 1
-mmap2: 1
-fstat64: 1
-madvise: 1
-fstatat64: 1
-futex: 1
-munmap: 1
-faccessat: 1
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-exit_group: 1
-rt_sigreturn: 1
-ugetrlimit: 1
-readlinkat: 1
-_llseek: 1
-fstatfs64: 1
-pread64: 1
-mremap: 1
-dup: 1
-set_tid_address: 1
-write: 1
-nanosleep: 1
-
-# Required by AddressSanitizer
-gettid: 1
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy b/codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy
deleted file mode 100644
index d5871d1..0000000
--- a/codec2/hidl/services/seccomp_policy/codec2.vendor.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Organized by frequency of systemcall - in descending order for
-# best performance.
-futex: 1
-ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
-close: 1
-writev: 1
-dup: 1
-ppoll: 1
-mmap2: 1
-getrandom: 1
-
-# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
-# parser support for '<' is in this needs to be modified to also prevent
-# |old_address| and |new_address| from touching the exception vector page, which
-# on ARM is statically loaded at 0xffff 0000. See
-# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
-# for more details.
-mremap: arg3 == 3
-munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
-sigaltstack: 1
-clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
-exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
-lseek: 1
-_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
-fstatat64: 1
-ugetrlimit: 1
-getdents64: 1
-getrandom: 1
-
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
-
diff --git a/codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy b/codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy
deleted file mode 100644
index 20c7625..0000000
--- a/codec2/hidl/services/seccomp_policy/codec2.vendor.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-read: 1
-mprotect: 1
-prctl: 1
-openat: 1
-getuid32: 1
-writev: 1
-ioctl: 1
-close: 1
-mmap2: 1
-fstat64: 1
-madvise: 1
-fstatat64: 1
-futex: 1
-munmap: 1
-faccessat: 1
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-exit_group: 1
-rt_sigreturn: 1
-ugetrlimit: 1
-readlinkat: 1
-_llseek: 1
-fstatfs64: 1
-pread64: 1
-mremap: 1
-dup: 1
-set_tid_address: 1
-write: 1
-nanosleep: 1
-
-# Required by AddressSanitizer
-gettid: 1
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/codec2/include/C2Component.h b/codec2/include/C2Component.h
index 8810725..7624851 100644
--- a/codec2/include/C2Component.h
+++ b/codec2/include/C2Component.h
@@ -409,6 +409,7 @@ public:
kind_t kind; ///< component kind
rank_t rank; ///< component rank
C2String mediaType; ///< media type supported by the component
+ C2String owner; ///< name of the component store owning this component
/**
* name alias(es) for backward compatibility.
diff --git a/codec2/include/C2Config.h b/codec2/include/C2Config.h
index 838ae49..be5f128 100644
--- a/codec2/include/C2Config.h
+++ b/codec2/include/C2Config.h
@@ -180,6 +180,7 @@ enum C2ParamIndexKind : C2Param::type_index_t {
kParamIndexPictureTypeMask,
kParamIndexPictureType,
+ kParamIndexHdr10PlusMetadata,
/* ------------------------------------ video components ------------------------------------ */
@@ -238,6 +239,8 @@ enum C2ParamIndexKind : C2Param::type_index_t {
kParamIndexMinFrameRate, // input-surface, float
kParamIndexTimestampGapAdjustment, // input-surface, struct
+ kParamIndexSurfaceAllocator, // u32
+
// deprecated indices due to renaming
kParamIndexAacStreamFormat = kParamIndexAacPackaging,
kParamIndexCsd = kParamIndexInitData,
@@ -904,6 +907,18 @@ typedef C2GlobalParam<C2Tuning, C2SimpleArrayStruct<C2Allocator::id_t>, kParamIn
constexpr char C2_PARAMKEY_PRIVATE_ALLOCATORS[] = "algo.buffers.allocator-ids";
/**
+ * Allocator to use for outputting to surface.
+ *
+ * Components can optionally request allocator type for outputting to surface.
+ *
+ * If none specified, client will use the default BufferQueue-backed allocator ID for outputting to
+ * surface.
+ */
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexSurfaceAllocator>
+ C2PortSurfaceAllocatorTuning;
+constexpr char C2_PARAMKEY_OUTPUT_SURFACE_ALLOCATOR[] = "output.buffers.surface-allocator-id";
+
+/**
* Block pools to use.
*
* These are allocated by the client for the component using the allocator IDs specified by the
@@ -1546,6 +1561,14 @@ typedef C2StreamParam<C2Info, C2HdrStaticMetadataStruct, kParamIndexHdrStaticMet
C2StreamHdrStaticInfo;
constexpr char C2_PARAMKEY_HDR_STATIC_INFO[] = "raw.hdr-static-info";
+/**
+ * HDR10+ Metadata Info.
+ */
+typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexHdr10PlusMetadata>
+ C2StreamHdr10PlusInfo;
+constexpr char C2_PARAMKEY_INPUT_HDR10_PLUS_INFO[] = "input.hdr10-plus-info";
+constexpr char C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO[] = "output.hdr10-plus-info";
+
/* ------------------------------------ block-based coding ----------------------------------- */
/**
diff --git a/codec2/include/C2Enum.h b/codec2/include/C2Enum.h
index a7bbcdc..b0fad8f 100644
--- a/codec2/include/C2Enum.h
+++ b/codec2/include/C2Enum.h
@@ -63,7 +63,7 @@ class _C2EnumUtils {
static std::vector<C2String> sanitizeEnumValueNames(
const std::vector<C2StringLiteral> names,
- C2StringLiteral _prefix = NULL);
+ C2StringLiteral _prefix = nullptr);
friend class C2UtilTest_EnumUtilsTest_Test;
@@ -75,7 +75,7 @@ public:
static C2_HIDE C2FieldDescriptor::NamedValuesType sanitizeEnumValues(
std::vector<T> values,
std::vector<C2StringLiteral> names,
- C2StringLiteral prefix = NULL) {
+ C2StringLiteral prefix = nullptr) {
C2FieldDescriptor::NamedValuesType namedValues;
std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
@@ -147,7 +147,7 @@ C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const name
*/
#define C2ENUM(name, type, ...) \
enum name : type { __VA_ARGS__ }; \
-DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, NULL, __VA_ARGS__)
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, nullptr, __VA_ARGS__)
/**
* Defines an enum type with the default named value mapper but custom prefix. The default
diff --git a/codec2/include/media/stagefright/codec2/1.0/InputSurface.h b/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
index b011a06..226dcc1 100644
--- a/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
+++ b/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
@@ -20,7 +20,7 @@
#include <memory>
#include <C2Component.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
namespace android {
diff --git a/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
index b24a416..06e52cf 100644
--- a/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
+++ b/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -20,8 +20,8 @@
#include <memory>
#include <C2Component.h>
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <media/stagefright/bqhelper/GraphicBufferSource.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
namespace android {
diff --git a/codec2/tests/Android.bp b/codec2/tests/Android.bp
index 9f167bf..f54c039 100644
--- a/codec2/tests/Android.bp
+++ b/codec2/tests/Android.bp
@@ -20,7 +20,6 @@ cc_test {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
}
@@ -48,7 +47,6 @@ cc_test {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
}
@@ -74,6 +72,5 @@ cc_test {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
}
diff --git a/codec2/tests/C2SampleComponent_test.cpp b/codec2/tests/C2SampleComponent_test.cpp
index ea445e8..cd354ad 100644
--- a/codec2/tests/C2SampleComponent_test.cpp
+++ b/codec2/tests/C2SampleComponent_test.cpp
@@ -361,11 +361,11 @@ void dumpStruct(const C2StructDescriptor &sd) {
for (const C2FieldDescriptor::NamedValueType &p : f.namedValues()) {
cout << sep << p.first << "=";
switch (f.type()) {
- case C2Value::INT32: cout << get(p.second, (int32_t *)0); break;
- case C2Value::INT64: cout << get(p.second, (int64_t *)0); break;
- case C2Value::UINT32: cout << get(p.second, (uint32_t *)0); break;
- case C2Value::UINT64: cout << get(p.second, (uint64_t *)0); break;
- case C2Value::FLOAT: cout << get(p.second, (float *)0); break;
+ case C2FieldDescriptor::INT32: cout << get(p.second, (int32_t *)0); break;
+ case C2FieldDescriptor::INT64: cout << get(p.second, (int64_t *)0); break;
+ case C2FieldDescriptor::UINT32: cout << get(p.second, (uint32_t *)0); break;
+ case C2FieldDescriptor::UINT64: cout << get(p.second, (uint64_t *)0); break;
+ case C2FieldDescriptor::FLOAT: cout << get(p.second, (float *)0); break;
default: cout << "???"; break;
}
sep = ",";
diff --git a/codec2/vndk/Android.bp b/codec2/vndk/Android.bp
index cc1db02..845d113 100644
--- a/codec2/vndk/Android.bp
+++ b/codec2/vndk/Android.bp
@@ -36,6 +36,7 @@ cc_library_shared {
export_shared_lib_headers: [
"libbase",
"android.hardware.media.bufferpool@1.0",
+ "android.hidl.token@1.0-utils",
],
local_include_dirs: [
@@ -47,11 +48,16 @@ cc_library_shared {
"hardware/google/av/codec2/include",
],
+ header_libs: [
+ "libgui_headers",
+ ],
+
shared_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.mapper@2.0",
"android.hardware.media.bufferpool@1.0",
+ "android.hidl.token@1.0-utils",
"libbase",
"libbinder",
"libcutils",
@@ -71,7 +77,6 @@ cc_library_shared {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
}
@@ -101,6 +106,3 @@ cc_defaults {
// TODO: separate internal headers so they can be exposed here
}
-subdirs = [
- "bufferpool",
-]
diff --git a/codec2/vndk/C2AllocatorGralloc.cpp b/codec2/vndk/C2AllocatorGralloc.cpp
index b0ec5f1..d3fbbd0 100644
--- a/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/codec2/vndk/C2AllocatorGralloc.cpp
@@ -159,7 +159,7 @@ public:
return xd != nullptr && xd->magic == MAGIC;
}
- static C2HandleGralloc* WrapNativeHandle(
+ static C2HandleGralloc* WrapAndMoveNativeHandle(
const native_handle_t *const handle,
uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) {
@@ -181,6 +181,26 @@ public:
return reinterpret_cast<C2HandleGralloc *>(res);
}
+ static C2HandleGralloc* WrapNativeHandle(
+ const native_handle_t *const handle,
+ uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+ uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) {
+ if (handle == nullptr) {
+ return nullptr;
+ }
+ native_handle_t *clone = native_handle_clone(handle);
+ if (clone == nullptr) {
+ return nullptr;
+ }
+ C2HandleGralloc *res = WrapAndMoveNativeHandle(
+ clone, width, height, format, usage, stride, generation, igbp_id, igbp_slot);
+ if (res == nullptr) {
+ native_handle_close(clone);
+ }
+ native_handle_delete(clone);
+ return res;
+ }
+
static native_handle_t* UnwrapNativeHandle(
const C2Handle *const handle) {
const ExtraData *xd = getExtraData(handle);
@@ -283,6 +303,7 @@ private:
const C2HandleGralloc *mLockedHandle;
bool mLocked;
C2Allocator::id_t mAllocatorId;
+ std::mutex mMappedLock;
};
C2AllocationGralloc::C2AllocationGralloc(
@@ -315,6 +336,11 @@ C2AllocationGralloc::~C2AllocationGralloc() {
native_handle_delete(
const_cast<native_handle_t *>(reinterpret_cast<const native_handle_t *>(mHandle)));
}
+ if (mLockedHandle) {
+ native_handle_delete(
+ const_cast<native_handle_t *>(
+ reinterpret_cast<const native_handle_t *>(mLockedHandle)));
+ }
}
c2_status_t C2AllocationGralloc::map(
@@ -327,6 +353,7 @@ c2_status_t C2AllocationGralloc::map(
// TODO
(void) fence;
+ std::lock_guard<std::mutex> lock(mMappedLock);
if (mBuffer && mLocked) {
ALOGD("already mapped");
return C2_DUPLICATE;
@@ -359,7 +386,7 @@ c2_status_t C2AllocationGralloc::map(
if (mHandle) {
mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot);
}
- mLockedHandle = C2HandleGralloc::WrapNativeHandle(
+ mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height,
(uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride,
generation, igbp_id, igbp_slot);
@@ -533,6 +560,8 @@ c2_status_t C2AllocationGralloc::unmap(
// TODO: check addr and size, use fence
(void)addr;
(void)rect;
+
+ std::lock_guard<std::mutex> lock(mMappedLock);
c2_status_t err = C2_OK;
mMapper->unlock(
const_cast<native_handle_t *>(mBuffer),
@@ -658,7 +687,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
return;
}
info.stride = stride;
- buffer = std::move(buffers[0]);
+ buffer = buffers[0];
});
if (err != C2_OK) {
return err;
@@ -667,7 +696,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
allocation->reset(new C2AllocationGralloc(
info, mMapper, buffer,
- C2HandleGralloc::WrapNativeHandle(
+ C2HandleGralloc::WrapAndMoveNativeHandle(
buffer.getNativeHandle(),
info.mapperInfo.width, info.mapperInfo.height,
(uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride,
diff --git a/codec2/vndk/bufferpool/Accessor.cpp b/codec2/vndk/bufferpool/Accessor.cpp
deleted file mode 100644
index b1dfc08..0000000
--- a/codec2/vndk/bufferpool/Accessor.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_TAG "BufferPoolConnection"
-
-#include "Accessor.h"
-#include "AccessorImpl.h"
-#include "Connection.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-void ConnectionDeathRecipient::add(
- int64_t connectionId,
- const sp<Accessor> &accessor) {
- std::lock_guard<std::mutex> lock(mLock);
- if (mAccessors.find(connectionId) == mAccessors.end()) {
- mAccessors.insert(std::make_pair(connectionId, accessor));
- }
-}
-
-void ConnectionDeathRecipient::remove(int64_t connectionId) {
- std::lock_guard<std::mutex> lock(mLock);
- mAccessors.erase(connectionId);
- auto it = mConnectionToCookie.find(connectionId);
- if (it != mConnectionToCookie.end()) {
- uint64_t cookie = it->second;
- mConnectionToCookie.erase(it);
- auto cit = mCookieToConnections.find(cookie);
- if (cit != mCookieToConnections.end()) {
- cit->second.erase(connectionId);
- if (cit->second.size() == 0) {
- mCookieToConnections.erase(cit);
- }
- }
- }
-}
-
-void ConnectionDeathRecipient::addCookieToConnection(
- uint64_t cookie,
- int64_t connectionId) {
- std::lock_guard<std::mutex> lock(mLock);
- if (mAccessors.find(connectionId) == mAccessors.end()) {
- return;
- }
- mConnectionToCookie.insert(std::make_pair(connectionId, cookie));
- auto it = mCookieToConnections.find(cookie);
- if (it != mCookieToConnections.end()) {
- it->second.insert(connectionId);
- } else {
- mCookieToConnections.insert(std::make_pair(
- cookie, std::set<int64_t>{connectionId}));
- }
-}
-
-void ConnectionDeathRecipient::serviceDied(
- uint64_t cookie,
- const wp<::android::hidl::base::V1_0::IBase>& /* who */
- ) {
- std::map<int64_t, const wp<Accessor>> connectionsToClose;
- {
- std::lock_guard<std::mutex> lock(mLock);
-
- auto it = mCookieToConnections.find(cookie);
- if (it != mCookieToConnections.end()) {
- for (auto conIt = it->second.begin(); conIt != it->second.end(); ++conIt) {
- auto accessorIt = mAccessors.find(*conIt);
- if (accessorIt != mAccessors.end()) {
- connectionsToClose.insert(std::make_pair(*conIt, accessorIt->second));
- mAccessors.erase(accessorIt);
- }
- mConnectionToCookie.erase(*conIt);
- }
- mCookieToConnections.erase(it);
- }
- }
-
- if (connectionsToClose.size() > 0) {
- sp<Accessor> accessor;
- for (auto it = connectionsToClose.begin(); it != connectionsToClose.end(); ++it) {
- accessor = it->second.promote();
-
- if (accessor) {
- accessor->close(it->first);
- ALOGD("connection %lld closed on death", (long long)it->first);
- }
- }
- }
-}
-
-namespace {
-static sp<ConnectionDeathRecipient> sConnectionDeathRecipient =
- new ConnectionDeathRecipient();
-}
-
-sp<ConnectionDeathRecipient> Accessor::getConnectionDeathRecipient() {
- return sConnectionDeathRecipient;
-}
-
-// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
-Return<void> Accessor::connect(connect_cb _hidl_cb) {
- sp<Connection> connection;
- ConnectionId connectionId;
- const QueueDescriptor* fmqDesc;
-
- ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
- if (status == ResultStatus::OK) {
- _hidl_cb(status, connection, connectionId, *fmqDesc);
- } else {
- _hidl_cb(status, nullptr, -1LL,
- android::hardware::MQDescriptorSync<BufferStatusMessage>(
- std::vector<android::hardware::GrantorDescriptor>(),
- nullptr /* nhandle */, 0 /* size */));
- }
- return Void();
-}
-
-Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
- : mImpl(new Impl(allocator)) {}
-
-Accessor::~Accessor() {
-}
-
-bool Accessor::isValid() {
- return (bool)mImpl;
-}
-
-ResultStatus Accessor::allocate(
- ConnectionId connectionId,
- const std::vector<uint8_t> &params,
- BufferId *bufferId, const native_handle_t** handle) {
- if (mImpl) {
- return mImpl->allocate(connectionId, params, bufferId, handle);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus Accessor::fetch(
- ConnectionId connectionId, TransactionId transactionId,
- BufferId bufferId, const native_handle_t** handle) {
- if (mImpl) {
- return mImpl->fetch(connectionId, transactionId, bufferId, handle);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus Accessor::connect(
- sp<Connection> *connection, ConnectionId *pConnectionId,
- const QueueDescriptor** fmqDescPtr, bool local) {
- if (mImpl) {
- ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
- if (!local && status == ResultStatus::OK) {
- sp<Accessor> accessor(this);
- sConnectionDeathRecipient->add(*pConnectionId, accessor);
- }
- return status;
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus Accessor::close(ConnectionId connectionId) {
- if (mImpl) {
- ResultStatus status = mImpl->close(connectionId);
- sConnectionDeathRecipient->remove(connectionId);
- return status;
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-void Accessor::cleanUp(bool clearCache) {
- if (mImpl) {
- mImpl->cleanUp(clearCache);
- }
-}
-
-//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
-// return new Accessor();
-//}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
diff --git a/codec2/vndk/bufferpool/Accessor.h b/codec2/vndk/bufferpool/Accessor.h
deleted file mode 100644
index 2f86f7b..0000000
--- a/codec2/vndk/bufferpool/Accessor.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
-
-#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
-#include <bufferpool/BufferPoolTypes.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-#include "BufferStatus.h"
-
-#include <set>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::sp;
-
-struct Accessor;
-struct Connection;
-
-/**
- * Receives death notifications from remote connections.
- * On death notifications, the connections are closed and used resources
- * are released.
- */
-struct ConnectionDeathRecipient : public hardware::hidl_death_recipient {
- /**
- * Registers a newly connected connection from remote processes.
- */
- void add(int64_t connectionId, const sp<Accessor> &accessor);
-
- /**
- * Removes a connection.
- */
- void remove(int64_t connectionId);
-
- void addCookieToConnection(uint64_t cookie, int64_t connectionId);
-
- virtual void serviceDied(
- uint64_t /* cookie */,
- const wp<::android::hidl::base::V1_0::IBase>& /* who */
- ) override;
-
-private:
- std::mutex mLock;
- std::map<uint64_t, std::set<int64_t>> mCookieToConnections;
- std::map<int64_t, uint64_t> mConnectionToCookie;
- std::map<int64_t, const wp<Accessor>> mAccessors;
-};
-
-/**
- * A buffer pool accessor which enables a buffer pool to communicate with buffer
- * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
- */
-struct Accessor : public IAccessor {
- // Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
- Return<void> connect(connect_cb _hidl_cb) override;
-
- /**
- * Creates a buffer pool accessor which uses the specified allocator.
- *
- * @param allocator buffer allocator.
- */
- explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
-
- /** Destructs a buffer pool accessor. */
- ~Accessor();
-
- /** Returns whether the accessor is valid. */
- bool isValid();
-
- /** Allocates a buffer from a buffer pool.
- *
- * @param connectionId the connection id of the client.
- * @param params the allocation parameters.
- * @param bufferId the id of the allocated buffer.
- * @param handle the native handle of the allocated buffer.
- *
- * @return OK when a buffer is successfully allocated.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus allocate(
- ConnectionId connectionId,
- const std::vector<uint8_t>& params,
- BufferId *bufferId,
- const native_handle_t** handle);
-
- /**
- * Fetches a buffer for the specified transaction.
- *
- * @param connectionId the id of receiving connection(client).
- * @param transactionId the id of the transfer transaction.
- * @param bufferId the id of the buffer to be fetched.
- * @param handle the native handle of the fetched buffer.
- *
- * @return OK when a buffer is successfully fetched.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus fetch(
- ConnectionId connectionId,
- TransactionId transactionId,
- BufferId bufferId,
- const native_handle_t** handle);
-
- /**
- * Makes a connection to the buffer pool. The buffer pool client uses the
- * created connection in order to communicate with the buffer pool. An
- * FMQ for buffer status message is also created for the client.
- *
- * @param connection created connection
- * @param pConnectionId the id of the created connection
- * @param fmqDescPtr FMQ descriptor for shared buffer status message
- * queue between a buffer pool and the client.
- * @param local true when a connection request comes from local process,
- * false otherwise.
- *
- * @return OK when a connection is successfully made.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus connect(
- sp<Connection> *connection, ConnectionId *pConnectionId,
- const QueueDescriptor** fmqDescPtr, bool local);
-
- /**
- * Closes the specified connection to the client.
- *
- * @param connectionId the id of the connection.
- *
- * @return OK when the connection is closed.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus close(ConnectionId connectionId);
-
- /**
- * Processes pending buffer status messages and perfoms periodic cache
- * cleaning.
- *
- * @param clearCache if clearCache is true, it frees all buffers waiting
- * to be recycled.
- */
- void cleanUp(bool clearCache);
-
- /**
- * Gets a hidl_death_recipient for remote connection death.
- */
- static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
-
-private:
- class Impl;
- std::unique_ptr<Impl> mImpl;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
diff --git a/codec2/vndk/bufferpool/AccessorImpl.cpp b/codec2/vndk/bufferpool/AccessorImpl.cpp
deleted file mode 100644
index 06d49c1..0000000
--- a/codec2/vndk/bufferpool/AccessorImpl.cpp
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BufferPoolAccessor"
-//#define LOG_NDEBUG 0
-
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <utils/Log.h>
-#include "AccessorImpl.h"
-#include "Connection.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-namespace {
- static constexpr int64_t kCleanUpDurationUs = 500000; // TODO tune 0.5 sec
- static constexpr int64_t kLogDurationUs = 5000000; // 5 secs
-
- static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
- static constexpr size_t kMinBufferCountForEviction = 40;
-}
-
-// Buffer structure in bufferpool process
-struct InternalBuffer {
- BufferId mId;
- size_t mOwnerCount;
- size_t mTransactionCount;
- const std::shared_ptr<BufferPoolAllocation> mAllocation;
- const size_t mAllocSize;
- const std::vector<uint8_t> mConfig;
-
- InternalBuffer(
- BufferId id,
- const std::shared_ptr<BufferPoolAllocation> &alloc,
- const size_t allocSize,
- const std::vector<uint8_t> &allocConfig)
- : mId(id), mOwnerCount(0), mTransactionCount(0),
- mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
-
- const native_handle_t *handle() {
- return mAllocation->handle();
- }
-};
-
-struct TransactionStatus {
- TransactionId mId;
- BufferId mBufferId;
- ConnectionId mSender;
- ConnectionId mReceiver;
- BufferStatus mStatus;
- int64_t mTimestampUs;
- bool mSenderValidated;
-
- TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
- mId = message.transactionId;
- mBufferId = message.bufferId;
- mStatus = message.newStatus;
- mTimestampUs = timestampUs;
- if (mStatus == BufferStatus::TRANSFER_TO) {
- mSender = message.connectionId;
- mReceiver = message.targetConnectionId;
- mSenderValidated = true;
- } else {
- mSender = -1LL;
- mReceiver = message.connectionId;
- mSenderValidated = false;
- }
- }
-};
-
-// Helper template methods for handling map of set.
-template<class T, class U>
-bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
- auto iter = mapOfSet->find(key);
- if (iter == mapOfSet->end()) {
- std::set<U> valueSet{value};
- mapOfSet->insert(std::make_pair(key, valueSet));
- return true;
- } else if (iter->second.find(value) == iter->second.end()) {
- iter->second.insert(value);
- return true;
- }
- return false;
-}
-
-template<class T, class U>
-bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
- bool ret = false;
- auto iter = mapOfSet->find(key);
- if (iter != mapOfSet->end()) {
- if (iter->second.erase(value) > 0) {
- ret = true;
- }
- if (iter->second.size() == 0) {
- mapOfSet->erase(iter);
- }
- }
- return ret;
-}
-
-template<class T, class U>
-bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
- auto iter = mapOfSet->find(key);
- if (iter != mapOfSet->end()) {
- auto setIter = iter->second.find(value);
- return setIter != iter->second.end();
- }
- return false;
-}
-
-int32_t Accessor::Impl::sPid = getpid();
-uint32_t Accessor::Impl::sSeqId = time(NULL);
-
-Accessor::Impl::Impl(
- const std::shared_ptr<BufferPoolAllocator> &allocator)
- : mAllocator(allocator) {}
-
-Accessor::Impl::~Impl() {
-}
-
-ResultStatus Accessor::Impl::connect(
- const sp<Accessor> &accessor, sp<Connection> *connection,
- ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) {
- sp<Connection> newConnection = new Connection();
- ResultStatus status = ResultStatus::CRITICAL_ERROR;
- {
- std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
- if (newConnection) {
- ConnectionId id = (int64_t)sPid << 32 | sSeqId;
- status = mBufferPool.mObserver.open(id, fmqDescPtr);
- if (status == ResultStatus::OK) {
- newConnection->initialize(accessor, id);
- *connection = newConnection;
- *pConnectionId = id;
- ++sSeqId;
- }
- }
- mBufferPool.processStatusMessages();
- mBufferPool.cleanUp();
- }
- return status;
-}
-
-ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
- std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
- mBufferPool.processStatusMessages();
- mBufferPool.handleClose(connectionId);
- mBufferPool.mObserver.close(connectionId);
- // Since close# will be called after all works are finished, it is OK to
- // evict unused buffers.
- mBufferPool.cleanUp(true);
- return ResultStatus::OK;
-}
-
-ResultStatus Accessor::Impl::allocate(
- ConnectionId connectionId, const std::vector<uint8_t>& params,
- BufferId *bufferId, const native_handle_t** handle) {
- std::unique_lock<std::mutex> lock(mBufferPool.mMutex);
- mBufferPool.processStatusMessages();
- ResultStatus status = ResultStatus::OK;
- if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
- lock.unlock();
- std::shared_ptr<BufferPoolAllocation> alloc;
- size_t allocSize;
- status = mAllocator->allocate(params, &alloc, &allocSize);
- lock.lock();
- if (status == ResultStatus::OK) {
- status = mBufferPool.addNewBuffer(alloc, allocSize, params, bufferId, handle);
- }
- ALOGV("create a buffer %d : %u %p",
- status == ResultStatus::OK, *bufferId, *handle);
- }
- if (status == ResultStatus::OK) {
- // TODO: handle ownBuffer failure
- mBufferPool.handleOwnBuffer(connectionId, *bufferId);
- }
- mBufferPool.cleanUp();
- return status;
-}
-
-ResultStatus Accessor::Impl::fetch(
- ConnectionId connectionId, TransactionId transactionId,
- BufferId bufferId, const native_handle_t** handle) {
- std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
- mBufferPool.processStatusMessages();
- auto found = mBufferPool.mTransactions.find(transactionId);
- if (found != mBufferPool.mTransactions.end() &&
- contains(&mBufferPool.mPendingTransactions,
- connectionId, transactionId)) {
- if (found->second->mSenderValidated &&
- found->second->mStatus == BufferStatus::TRANSFER_FROM &&
- found->second->mBufferId == bufferId) {
- found->second->mStatus = BufferStatus::TRANSFER_FETCH;
- auto bufferIt = mBufferPool.mBuffers.find(bufferId);
- if (bufferIt != mBufferPool.mBuffers.end()) {
- mBufferPool.mStats.onBufferFetched();
- *handle = bufferIt->second->handle();
- return ResultStatus::OK;
- }
- }
- }
- mBufferPool.cleanUp();
- return ResultStatus::CRITICAL_ERROR;
-}
-
-void Accessor::Impl::cleanUp(bool clearCache) {
- // transaction timeout, buffer cacheing TTL handling
- std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
- mBufferPool.processStatusMessages();
- mBufferPool.cleanUp(clearCache);
-}
-
-Accessor::Impl::Impl::BufferPool::BufferPool()
- : mTimestampUs(getTimestampNow()),
- mLastCleanUpUs(mTimestampUs),
- mLastLogUs(mTimestampUs),
- mSeq(0) {}
-
-
-// Statistics helper
-template<typename T, typename S>
-int percentage(T base, S total) {
- return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
-}
-
-Accessor::Impl::Impl::BufferPool::~BufferPool() {
- std::lock_guard<std::mutex> lock(mMutex);
- ALOGD("Destruction - bufferpool %p "
- "cached: %zu/%zuM, %zu/%d%% in use; "
- "allocs: %zu, %d%% recycled; "
- "transfers: %zu, %d%% unfetced",
- this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
- mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
- mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
- mStats.mTotalTransfers,
- percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
-}
-
-bool Accessor::Impl::BufferPool::handleOwnBuffer(
- ConnectionId connectionId, BufferId bufferId) {
-
- bool added = insert(&mUsingBuffers, connectionId, bufferId);
- if (added) {
- auto iter = mBuffers.find(bufferId);
- iter->second->mOwnerCount++;
- }
- insert(&mUsingConnections, bufferId, connectionId);
- return added;
-}
-
-bool Accessor::Impl::BufferPool::handleReleaseBuffer(
- ConnectionId connectionId, BufferId bufferId) {
- bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
- if (deleted) {
- auto iter = mBuffers.find(bufferId);
- iter->second->mOwnerCount--;
- if (iter->second->mOwnerCount == 0 &&
- iter->second->mTransactionCount == 0) {
- mStats.onBufferUnused(iter->second->mAllocSize);
- mFreeBuffers.insert(bufferId);
- }
- }
- erase(&mUsingConnections, bufferId, connectionId);
- ALOGV("release buffer %u : %d", bufferId, deleted);
- return deleted;
-}
-
-bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
- auto completed = mCompletedTransactions.find(
- message.transactionId);
- if (completed != mCompletedTransactions.end()) {
- // already completed
- mCompletedTransactions.erase(completed);
- return true;
- }
- // the buffer should exist and be owned.
- auto bufferIter = mBuffers.find(message.bufferId);
- if (bufferIter == mBuffers.end() ||
- !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
- return false;
- }
- auto found = mTransactions.find(message.transactionId);
- if (found != mTransactions.end()) {
- // transfer_from was received earlier.
- found->second->mSender = message.connectionId;
- found->second->mSenderValidated = true;
- return true;
- }
- // TODO: verify there is target connection Id
- mStats.onBufferSent();
- mTransactions.insert(std::make_pair(
- message.transactionId,
- std::make_unique<TransactionStatus>(message, mTimestampUs)));
- insert(&mPendingTransactions, message.targetConnectionId,
- message.transactionId);
- bufferIter->second->mTransactionCount++;
- return true;
-}
-
-bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
- auto found = mTransactions.find(message.transactionId);
- if (found == mTransactions.end()) {
- // TODO: is it feasible to check ownership here?
- mStats.onBufferSent();
- mTransactions.insert(std::make_pair(
- message.transactionId,
- std::make_unique<TransactionStatus>(message, mTimestampUs)));
- insert(&mPendingTransactions, message.connectionId,
- message.transactionId);
- auto bufferIter = mBuffers.find(message.bufferId);
- bufferIter->second->mTransactionCount++;
- } else {
- if (message.connectionId == found->second->mReceiver) {
- found->second->mStatus = BufferStatus::TRANSFER_FROM;
- }
- }
- return true;
-}
-
-bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
- auto found = mTransactions.find(message.transactionId);
- if (found != mTransactions.end()) {
- bool deleted = erase(&mPendingTransactions, message.connectionId,
- message.transactionId);
- if (deleted) {
- if (!found->second->mSenderValidated) {
- mCompletedTransactions.insert(message.transactionId);
- }
- auto bufferIter = mBuffers.find(message.bufferId);
- if (message.newStatus == BufferStatus::TRANSFER_OK) {
- handleOwnBuffer(message.connectionId, message.bufferId);
- }
- bufferIter->second->mTransactionCount--;
- if (bufferIter->second->mOwnerCount == 0
- && bufferIter->second->mTransactionCount == 0) {
- mStats.onBufferUnused(bufferIter->second->mAllocSize);
- mFreeBuffers.insert(message.bufferId);
- }
- mTransactions.erase(found);
- }
- ALOGV("transfer finished %llu %u - %d", (unsigned long long)message.transactionId,
- message.bufferId, deleted);
- return deleted;
- }
- ALOGV("transfer not found %llu %u", (unsigned long long)message.transactionId,
- message.bufferId);
- return false;
-}
-
-void Accessor::Impl::BufferPool::processStatusMessages() {
- std::vector<BufferStatusMessage> messages;
- mObserver.getBufferStatusChanges(messages);
- mTimestampUs = getTimestampNow();
- for (BufferStatusMessage& message: messages) {
- bool ret = false;
- switch (message.newStatus) {
- case BufferStatus::NOT_USED:
- ret = handleReleaseBuffer(
- message.connectionId, message.bufferId);
- break;
- case BufferStatus::USED:
- // not happening
- break;
- case BufferStatus::TRANSFER_TO:
- ret = handleTransferTo(message);
- break;
- case BufferStatus::TRANSFER_FROM:
- ret = handleTransferFrom(message);
- break;
- case BufferStatus::TRANSFER_TIMEOUT:
- // TODO
- break;
- case BufferStatus::TRANSFER_LOST:
- // TODO
- break;
- case BufferStatus::TRANSFER_FETCH:
- // not happening
- break;
- case BufferStatus::TRANSFER_OK:
- case BufferStatus::TRANSFER_ERROR:
- ret = handleTransferResult(message);
- break;
- }
- if (ret == false) {
- ALOGW("buffer status message processing failure - message : %d connection : %lld",
- message.newStatus, (long long)message.connectionId);
- }
- }
- messages.clear();
-}
-
-bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
- // Cleaning buffers
- auto buffers = mUsingBuffers.find(connectionId);
- if (buffers != mUsingBuffers.end()) {
- for (const BufferId& bufferId : buffers->second) {
- bool deleted = erase(&mUsingConnections, bufferId, connectionId);
- if (deleted) {
- auto bufferIter = mBuffers.find(bufferId);
- bufferIter->second->mOwnerCount--;
- if (bufferIter->second->mOwnerCount == 0 &&
- bufferIter->second->mTransactionCount == 0) {
- // TODO: handle freebuffer insert fail
- mStats.onBufferUnused(bufferIter->second->mAllocSize);
- mFreeBuffers.insert(bufferId);
- }
- }
- }
- mUsingBuffers.erase(buffers);
- }
-
- // Cleaning transactions
- auto pending = mPendingTransactions.find(connectionId);
- if (pending != mPendingTransactions.end()) {
- for (const TransactionId& transactionId : pending->second) {
- auto iter = mTransactions.find(transactionId);
- if (iter != mTransactions.end()) {
- if (!iter->second->mSenderValidated) {
- mCompletedTransactions.insert(transactionId);
- }
- BufferId bufferId = iter->second->mBufferId;
- auto bufferIter = mBuffers.find(bufferId);
- bufferIter->second->mTransactionCount--;
- if (bufferIter->second->mOwnerCount == 0 &&
- bufferIter->second->mTransactionCount == 0) {
- // TODO: handle freebuffer insert fail
- mStats.onBufferUnused(bufferIter->second->mAllocSize);
- mFreeBuffers.insert(bufferId);
- }
- mTransactions.erase(iter);
- }
- }
- }
- return true;
-}
-
-bool Accessor::Impl::BufferPool::getFreeBuffer(
- const std::shared_ptr<BufferPoolAllocator> &allocator,
- const std::vector<uint8_t> &params, BufferId *pId,
- const native_handle_t** handle) {
- auto bufferIt = mFreeBuffers.begin();
- for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
- BufferId bufferId = *bufferIt;
- if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
- break;
- }
- }
- if (bufferIt != mFreeBuffers.end()) {
- BufferId id = *bufferIt;
- mFreeBuffers.erase(bufferIt);
- mStats.onBufferRecycled(mBuffers[id]->mAllocSize);
- *handle = mBuffers[id]->handle();
- *pId = id;
- ALOGV("recycle a buffer %u %p", id, *handle);
- return true;
- }
- return false;
-}
-
-ResultStatus Accessor::Impl::BufferPool::addNewBuffer(
- const std::shared_ptr<BufferPoolAllocation> &alloc,
- const size_t allocSize,
- const std::vector<uint8_t> &params,
- BufferId *pId,
- const native_handle_t** handle) {
-
- BufferId bufferId = mSeq++;
- if (mSeq == Connection::SYNC_BUFFERID) {
- mSeq = 0;
- }
- std::unique_ptr<InternalBuffer> buffer =
- std::make_unique<InternalBuffer>(
- bufferId, alloc, allocSize, params);
- if (buffer) {
- auto res = mBuffers.insert(std::make_pair(
- bufferId, std::move(buffer)));
- if (res.second) {
- mStats.onBufferAllocated(allocSize);
- *handle = alloc->handle();
- *pId = bufferId;
- return ResultStatus::OK;
- }
- }
- return ResultStatus::NO_MEMORY;
-}
-
-void Accessor::Impl::BufferPool::cleanUp(bool clearCache) {
- if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) {
- mLastCleanUpUs = mTimestampUs;
- if (mTimestampUs > mLastLogUs + kLogDurationUs) {
- mLastLogUs = mTimestampUs;
- ALOGD("bufferpool %p : %zu(%zu size) total buffers - "
- "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - "
- "%zu/%zu (fetch/transfer)",
- this, mStats.mBuffersCached, mStats.mSizeCached,
- mStats.mBuffersInUse, mStats.mSizeInUse,
- mStats.mTotalRecycles, mStats.mTotalAllocations,
- mStats.mTotalFetches, mStats.mTotalTransfers);
- }
- for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
- if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction
- && mBuffers.size() < kMinBufferCountForEviction) {
- break;
- }
- auto it = mBuffers.find(*freeIt);
- if (it != mBuffers.end() &&
- it->second->mOwnerCount == 0 && it->second->mTransactionCount == 0) {
- mStats.onBufferEvicted(it->second->mAllocSize);
- mBuffers.erase(it);
- freeIt = mFreeBuffers.erase(freeIt);
- } else {
- ++freeIt;
- ALOGW("bufferpool inconsistent!");
- }
- }
- }
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
diff --git a/codec2/vndk/bufferpool/AccessorImpl.h b/codec2/vndk/bufferpool/AccessorImpl.h
deleted file mode 100644
index c04dbf3..0000000
--- a/codec2/vndk/bufferpool/AccessorImpl.h
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
-
-#include <map>
-#include <set>
-#include "Accessor.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-struct InternalBuffer;
-struct TransactionStatus;
-
-/**
- * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
-class Accessor::Impl {
-public:
- Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
-
- ~Impl();
-
- ResultStatus connect(
- const sp<Accessor> &accessor, sp<Connection> *connection,
- ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr);
-
- ResultStatus close(ConnectionId connectionId);
-
- ResultStatus allocate(ConnectionId connectionId,
- const std::vector<uint8_t>& params,
- BufferId *bufferId,
- const native_handle_t** handle);
-
- ResultStatus fetch(ConnectionId connectionId,
- TransactionId transactionId,
- BufferId bufferId,
- const native_handle_t** handle);
-
- void cleanUp(bool clearCache);
-
-private:
- // ConnectionId = pid : (timestamp_created + seqId)
- // in order to guarantee uniqueness for each connection
- static uint32_t sSeqId;
- static int32_t sPid;
-
- const std::shared_ptr<BufferPoolAllocator> mAllocator;
-
- /**
- * Buffer pool implementation.
- *
- * Handles buffer status messages. Handles buffer allocation/recycling.
- * Handles buffer transfer between buffer pool clients.
- */
- struct BufferPool {
- private:
- std::mutex mMutex;
- int64_t mTimestampUs;
- int64_t mLastCleanUpUs;
- int64_t mLastLogUs;
- BufferId mSeq;
- BufferStatusObserver mObserver;
-
- std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
- std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
-
- std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
- // Transactions completed before TRANSFER_TO message arrival.
- // Fetch does not occur for the transactions.
- // Only transaction id is kept for the transactions in short duration.
- std::set<TransactionId> mCompletedTransactions;
- // Currently active(pending) transations' status & information.
- std::map<TransactionId, std::unique_ptr<TransactionStatus>>
- mTransactions;
-
- std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
- std::set<BufferId> mFreeBuffers;
-
- /// Buffer pool statistics which tracks allocation and transfer statistics.
- struct Stats {
- /// Total size of allocations which are used or available to use.
- /// (bytes or pixels)
- size_t mSizeCached;
- /// # of cached buffers which are used or available to use.
- size_t mBuffersCached;
- /// Total size of allocations which are currently used. (bytes or pixels)
- size_t mSizeInUse;
- /// # of currently used buffers
- size_t mBuffersInUse;
-
- /// # of allocations called on bufferpool. (# of fetched from BlockPool)
- size_t mTotalAllocations;
- /// # of allocations that were served from the cache.
- /// (# of allocator alloc prevented)
- size_t mTotalRecycles;
- /// # of buffer transfers initiated.
- size_t mTotalTransfers;
- /// # of transfers that had to be fetched.
- size_t mTotalFetches;
-
- Stats()
- : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0),
- mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {}
-
- /// A new buffer is allocated on an allocation request.
- void onBufferAllocated(size_t allocSize) {
- mSizeCached += allocSize;
- mBuffersCached++;
-
- mSizeInUse += allocSize;
- mBuffersInUse++;
-
- mTotalAllocations++;
- }
-
- /// A buffer is evicted and destroyed.
- void onBufferEvicted(size_t allocSize) {
- mSizeCached -= allocSize;
- mBuffersCached--;
- }
-
- /// A buffer is recycled on an allocation request.
- void onBufferRecycled(size_t allocSize) {
- mSizeInUse += allocSize;
- mBuffersInUse++;
-
- mTotalAllocations++;
- mTotalRecycles++;
- }
-
- /// A buffer is available to be recycled.
- void onBufferUnused(size_t allocSize) {
- mSizeInUse -= allocSize;
- mBuffersInUse--;
- }
-
- /// A buffer transfer is initiated.
- void onBufferSent() {
- mTotalTransfers++;
- }
-
- /// A buffer fetch is invoked by a buffer transfer.
- void onBufferFetched() {
- mTotalFetches++;
- }
- } mStats;
-
- public:
- /** Creates a buffer pool. */
- BufferPool();
-
- /** Destroys a buffer pool. */
- ~BufferPool();
-
- /**
- * Processes all pending buffer status messages, and returns the result.
- * Each status message is handled by methods with 'handle' prefix.
- */
- void processStatusMessages();
-
- /**
- * Handles a buffer being owned by a connection.
- *
- * @param connectionId the id of the buffer owning connection.
- * @param bufferId the id of the buffer.
- *
- * @return {@code true} when the buffer is owned,
- * {@code false} otherwise.
- */
- bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
-
- /**
- * Handles a buffer being released by a connection.
- *
- * @param connectionId the id of the buffer owning connection.
- * @param bufferId the id of the buffer.
- *
- * @return {@code true} when the buffer ownership is released,
- * {@code false} otherwise.
- */
- bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
-
- /**
- * Handles a transfer transaction start message from the sender.
- *
- * @param message a buffer status message for the transaction.
- *
- * @result {@code true} when transfer_to message is acknowledged,
- * {@code false} otherwise.
- */
- bool handleTransferTo(const BufferStatusMessage &message);
-
- /**
- * Handles a transfer transaction being acked by the receiver.
- *
- * @param message a buffer status message for the transaction.
- *
- * @result {@code true} when transfer_from message is acknowledged,
- * {@code false} otherwise.
- */
- bool handleTransferFrom(const BufferStatusMessage &message);
-
- /**
- * Handles a transfer transaction result message from the receiver.
- *
- * @param message a buffer status message for the transaction.
- *
- * @result {@code true} when the exisitng transaction is finished,
- * {@code false} otherwise.
- */
- bool handleTransferResult(const BufferStatusMessage &message);
-
- /**
- * Handles a connection being closed, and returns the result. All the
- * buffers and transactions owned by the connection will be cleaned up.
- * The related FMQ will be cleaned up too.
- *
- * @param connectionId the id of the connection.
- *
- * @result {@code true} when the connection existed,
- * {@code false} otherwise.
- */
- bool handleClose(ConnectionId connectionId);
-
- /**
- * Recycles a existing free buffer if it is possible.
- *
- * @param allocator the buffer allocator
- * @param params the allocation parameters.
- * @param pId the id of the recycled buffer.
- * @param handle the native handle of the recycled buffer.
- *
- * @return {@code true} when a buffer is recycled, {@code false}
- * otherwise.
- */
- bool getFreeBuffer(
- const std::shared_ptr<BufferPoolAllocator> &allocator,
- const std::vector<uint8_t> &params,
- BufferId *pId, const native_handle_t **handle);
-
- /**
- * Adds a newly allocated buffer to bufferpool.
- *
- * @param alloc the newly allocated buffer.
- * @param allocSize the size of the newly allocated buffer.
- * @param params the allocation parameters.
- * @param pId the buffer id for the newly allocated buffer.
- * @param handle the native handle for the newly allocated buffer.
- *
- * @return OK when an allocation is successfully allocated.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus addNewBuffer(
- const std::shared_ptr<BufferPoolAllocation> &alloc,
- const size_t allocSize,
- const std::vector<uint8_t> &params,
- BufferId *pId,
- const native_handle_t **handle);
-
- /**
- * Processes pending buffer status messages and performs periodic cache
- * cleaning.
- *
- * @param clearCache if clearCache is true, it frees all buffers
- * waiting to be recycled.
- */
- void cleanUp(bool clearCache = false);
-
- friend class Accessor::Impl;
- } mBufferPool;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace ufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
diff --git a/codec2/vndk/bufferpool/Android.bp b/codec2/vndk/bufferpool/Android.bp
deleted file mode 100644
index c7ea70f..0000000
--- a/codec2/vndk/bufferpool/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-cc_library {
- name: "libstagefright_bufferpool@1.0",
- vendor_available: true,
- srcs: [
- "Accessor.cpp",
- "AccessorImpl.cpp",
- "BufferPoolClient.cpp",
- "BufferStatus.cpp",
- "ClientManager.cpp",
- "Connection.cpp",
- ],
- export_include_dirs: [
- "include",
- ],
- shared_libs: [
- "libcutils",
- "libfmq",
- "libhidlbase",
- "libhwbinder",
- "libhidltransport",
- "liblog",
- "libutils",
- "android.hardware.media.bufferpool@1.0",
- ],
- export_shared_lib_headers: [
- "libfmq",
- "android.hardware.media.bufferpool@1.0",
- ],
-}
diff --git a/codec2/vndk/bufferpool/BufferPoolClient.cpp b/codec2/vndk/bufferpool/BufferPoolClient.cpp
deleted file mode 100644
index 3626004..0000000
--- a/codec2/vndk/bufferpool/BufferPoolClient.cpp
+++ /dev/null
@@ -1,708 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BufferPoolClient"
-//#define LOG_NDEBUG 0
-
-#include <thread>
-#include <utils/Log.h>
-#include "BufferPoolClient.h"
-#include "Connection.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
-static constexpr int kPostMaxRetry = 3;
-static constexpr int kCacheTtlUs = 1000000; // TODO: tune
-
-class BufferPoolClient::Impl
- : public std::enable_shared_from_this<BufferPoolClient::Impl> {
-public:
- explicit Impl(const sp<Accessor> &accessor);
-
- explicit Impl(const sp<IAccessor> &accessor);
-
- bool isValid() {
- return mValid;
- }
-
- bool isLocal() {
- return mValid && mLocal;
- }
-
- ConnectionId getConnectionId() {
- return mConnectionId;
- }
-
- sp<IAccessor> &getAccessor() {
- return mAccessor;
- }
-
- bool isActive(int64_t *lastTransactionUs, bool clearCache);
-
- ResultStatus allocate(const std::vector<uint8_t> &params,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- ResultStatus receive(
- TransactionId transactionId, BufferId bufferId,
- int64_t timestampUs,
- native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer);
-
- void postBufferRelease(BufferId bufferId);
-
- bool postSend(
- BufferId bufferId, ConnectionId receiver,
- TransactionId *transactionId, int64_t *timestampUs);
-private:
-
- bool postReceive(
- BufferId bufferId, TransactionId transactionId,
- int64_t timestampUs);
-
- bool postReceiveResult(
- BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync);
-
- void trySyncFromRemote();
-
- bool syncReleased();
-
- void evictCaches(bool clearCache = false);
-
- ResultStatus allocateBufferHandle(
- const std::vector<uint8_t>& params, BufferId *bufferId,
- native_handle_t **handle);
-
- ResultStatus fetchBufferHandle(
- TransactionId transactionId, BufferId bufferId,
- native_handle_t **handle);
-
- struct BlockPoolDataDtor;
- struct ClientBuffer;
-
- bool mLocal;
- bool mValid;
- sp<IAccessor> mAccessor;
- sp<Connection> mLocalConnection;
- sp<IConnection> mRemoteConnection;
- uint32_t mSeqId;
- ConnectionId mConnectionId;
- int64_t mLastEvictCacheUs;
-
- // CachedBuffers
- struct BufferCache {
- std::mutex mLock;
- bool mCreating;
- std::condition_variable mCreateCv;
- std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
- int mActive;
- int64_t mLastChangeUs;
-
- BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {}
-
- void incActive_l() {
- ++mActive;
- mLastChangeUs = getTimestampNow();
- }
-
- void decActive_l() {
- --mActive;
- mLastChangeUs = getTimestampNow();
- }
- } mCache;
-
- // FMQ - release notifier
- struct {
- std::mutex mLock;
- // TODO: use only one list?(using one list may dealy sending messages?)
- std::list<BufferId> mReleasingIds;
- std::list<BufferId> mReleasedIds;
- std::unique_ptr<BufferStatusChannel> mStatusChannel;
- } mReleasing;
-
- // This lock is held during synchronization from remote side.
- // In order to minimize remote calls and locking durtaion, this lock is held
- // by best effort approach using try_lock().
- std::mutex mRemoteSyncLock;
-};
-
-struct BufferPoolClient::Impl::BlockPoolDataDtor {
- BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
- : mImpl(impl) {}
-
- void operator()(BufferPoolData *buffer) {
- BufferId id = buffer->mId;
- delete buffer;
-
- auto impl = mImpl.lock();
- if (impl && impl->isValid()) {
- impl->postBufferRelease(id);
- }
- }
- const std::weak_ptr<BufferPoolClient::Impl> mImpl;
-};
-
-struct BufferPoolClient::Impl::ClientBuffer {
-private:
- bool mInvalidated; // TODO: implement
- int64_t mExpireUs;
- bool mHasCache;
- ConnectionId mConnectionId;
- BufferId mId;
- native_handle_t *mHandle;
- std::weak_ptr<BufferPoolData> mCache;
-
- void updateExpire() {
- mExpireUs = getTimestampNow() + kCacheTtlUs;
- }
-
-public:
- ClientBuffer(
- ConnectionId connectionId, BufferId id, native_handle_t *handle)
- : mInvalidated(false), mHasCache(false),
- mConnectionId(connectionId), mId(id), mHandle(handle) {
- (void)mInvalidated;
- mExpireUs = getTimestampNow() + kCacheTtlUs;
- }
-
- ~ClientBuffer() {
- if (mHandle) {
- native_handle_close(mHandle);
- native_handle_delete(mHandle);
- }
- }
-
- bool expire() const {
- int64_t now = getTimestampNow();
- return now >= mExpireUs;
- }
-
- bool hasCache() const {
- return mHasCache;
- }
-
- std::shared_ptr<BufferPoolData> fetchCache(native_handle_t **pHandle) {
- if (mHasCache) {
- std::shared_ptr<BufferPoolData> cache = mCache.lock();
- if (cache) {
- *pHandle = mHandle;
- }
- return cache;
- }
- return nullptr;
- }
-
- std::shared_ptr<BufferPoolData> createCache(
- const std::shared_ptr<BufferPoolClient::Impl> &impl,
- native_handle_t **pHandle) {
- if (!mHasCache) {
- // Allocates a raw ptr in order to avoid sending #postBufferRelease
- // from deleter, in case of native_handle_clone failure.
- BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId);
- if (ptr) {
- std::shared_ptr<BufferPoolData> cache(ptr, BlockPoolDataDtor(impl));
- if (cache) {
- mCache = cache;
- mHasCache = true;
- *pHandle = mHandle;
- return cache;
- }
- }
- if (ptr) {
- delete ptr;
- }
- }
- return nullptr;
- }
-
- bool onCacheRelease() {
- if (mHasCache) {
- // TODO: verify mCache is not valid;
- updateExpire();
- mHasCache = false;
- return true;
- }
- return false;
- }
-};
-
-BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
- : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
- mLastEvictCacheUs(getTimestampNow()) {
- const QueueDescriptor *fmqDesc;
- ResultStatus status = accessor->connect(
- &mLocalConnection, &mConnectionId, &fmqDesc, true);
- if (status == ResultStatus::OK) {
- mReleasing.mStatusChannel =
- std::make_unique<BufferStatusChannel>(*fmqDesc);
- mValid = mReleasing.mStatusChannel &&
- mReleasing.mStatusChannel->isValid();
- }
-}
-
-BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
- : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
- mLastEvictCacheUs(getTimestampNow()) {
- bool valid = false;
- sp<IConnection>& outConnection = mRemoteConnection;
- ConnectionId& id = mConnectionId;
- std::unique_ptr<BufferStatusChannel>& outChannel =
- mReleasing.mStatusChannel;
- Return<void> transResult = accessor->connect(
- [&valid, &outConnection, &id, &outChannel]
- (ResultStatus status, sp<IConnection> connection,
- ConnectionId connectionId, const QueueDescriptor& desc) {
- if (status == ResultStatus::OK) {
- outConnection = connection;
- id = connectionId;
- outChannel = std::make_unique<BufferStatusChannel>(desc);
- if (outChannel && outChannel->isValid()) {
- valid = true;
- }
- }
- });
- mValid = transResult.isOk() && valid;
-}
-
-bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) {
- bool active = false;
- {
- std::lock_guard<std::mutex> lock(mCache.mLock);
- syncReleased();
- evictCaches(clearCache);
- *lastTransactionUs = mCache.mLastChangeUs;
- active = mCache.mActive > 0;
- }
- if (mValid && mLocal && mLocalConnection) {
- mLocalConnection->cleanUp(clearCache);
- return true;
- }
- return active;
-}
-
-ResultStatus BufferPoolClient::Impl::allocate(
- const std::vector<uint8_t> &params,
- native_handle_t **pHandle,
- std::shared_ptr<BufferPoolData> *buffer) {
- if (!mLocal || !mLocalConnection || !mValid) {
- return ResultStatus::CRITICAL_ERROR;
- }
- BufferId bufferId;
- native_handle_t *handle = NULL;
- buffer->reset();
- ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
- if (status == ResultStatus::OK) {
- if (handle) {
- std::unique_lock<std::mutex> lock(mCache.mLock);
- syncReleased();
- evictCaches();
- auto cacheIt = mCache.mBuffers.find(bufferId);
- if (cacheIt != mCache.mBuffers.end()) {
- // TODO: verify it is recycled. (not having active ref)
- mCache.mBuffers.erase(cacheIt);
- }
- auto clientBuffer = std::make_unique<ClientBuffer>(
- mConnectionId, bufferId, handle);
- if (clientBuffer) {
- auto result = mCache.mBuffers.insert(std::make_pair(
- bufferId, std::move(clientBuffer)));
- if (result.second) {
- *buffer = result.first->second->createCache(
- shared_from_this(), pHandle);
- if (*buffer) {
- mCache.incActive_l();
- }
- }
- }
- }
- if (!*buffer) {
- ALOGV("client cache creation failure %d: %lld",
- handle != NULL, (long long)mConnectionId);
- status = ResultStatus::NO_MEMORY;
- postBufferRelease(bufferId);
- }
- }
- return status;
-}
-
-ResultStatus BufferPoolClient::Impl::receive(
- TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
- native_handle_t **pHandle,
- std::shared_ptr<BufferPoolData> *buffer) {
- if (!mValid) {
- return ResultStatus::CRITICAL_ERROR;
- }
- if (timestampUs != 0) {
- timestampUs += kReceiveTimeoutUs;
- }
- if (!postReceive(bufferId, transactionId, timestampUs)) {
- return ResultStatus::CRITICAL_ERROR;
- }
- ResultStatus status = ResultStatus::CRITICAL_ERROR;
- buffer->reset();
- while(1) {
- std::unique_lock<std::mutex> lock(mCache.mLock);
- syncReleased();
- evictCaches();
- auto cacheIt = mCache.mBuffers.find(bufferId);
- if (cacheIt != mCache.mBuffers.end()) {
- if (cacheIt->second->hasCache()) {
- *buffer = cacheIt->second->fetchCache(pHandle);
- if (!*buffer) {
- // check transfer time_out
- lock.unlock();
- std::this_thread::yield();
- continue;
- }
- ALOGV("client receive from reference %lld", (long long)mConnectionId);
- break;
- } else {
- *buffer = cacheIt->second->createCache(shared_from_this(), pHandle);
- if (*buffer) {
- mCache.incActive_l();
- }
- ALOGV("client receive from cache %lld", (long long)mConnectionId);
- break;
- }
- } else {
- if (!mCache.mCreating) {
- mCache.mCreating = true;
- lock.unlock();
- native_handle_t* handle = NULL;
- status = fetchBufferHandle(transactionId, bufferId, &handle);
- lock.lock();
- if (status == ResultStatus::OK) {
- if (handle) {
- auto clientBuffer = std::make_unique<ClientBuffer>(
- mConnectionId, bufferId, handle);
- if (clientBuffer) {
- auto result = mCache.mBuffers.insert(
- std::make_pair(bufferId, std::move(
- clientBuffer)));
- if (result.second) {
- *buffer = result.first->second->createCache(
- shared_from_this(), pHandle);
- if (*buffer) {
- mCache.incActive_l();
- }
- }
- }
- }
- if (!*buffer) {
- status = ResultStatus::NO_MEMORY;
- }
- }
- mCache.mCreating = false;
- lock.unlock();
- mCache.mCreateCv.notify_all();
- break;
- }
- mCache.mCreateCv.wait(lock);
- }
- }
- bool needsSync = false;
- bool posted = postReceiveResult(bufferId, transactionId,
- *buffer ? true : false, &needsSync);
- ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId,
- *buffer ? "ok" : "fail", posted);
- if (mValid && mLocal && mLocalConnection) {
- mLocalConnection->cleanUp(false);
- }
- if (needsSync && mRemoteConnection) {
- trySyncFromRemote();
- }
- if (*buffer) {
- if (!posted) {
- buffer->reset();
- return ResultStatus::CRITICAL_ERROR;
- }
- return ResultStatus::OK;
- }
- return status;
-}
-
-
-void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
- std::lock_guard<std::mutex> lock(mReleasing.mLock);
- mReleasing.mReleasingIds.push_back(bufferId);
- mReleasing.mStatusChannel->postBufferRelease(
- mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
-}
-
-// TODO: revise ad-hoc posting data structure
-bool BufferPoolClient::Impl::postSend(
- BufferId bufferId, ConnectionId receiver,
- TransactionId *transactionId, int64_t *timestampUs) {
- bool ret = false;
- bool needsSync = false;
- {
- std::lock_guard<std::mutex> lock(mReleasing.mLock);
- *timestampUs = getTimestampNow();
- *transactionId = (mConnectionId << 32) | mSeqId++;
- // TODO: retry, add timeout, target?
- ret = mReleasing.mStatusChannel->postBufferStatusMessage(
- *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
- receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
- needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
- }
- if (mValid && mLocal && mLocalConnection) {
- mLocalConnection->cleanUp(false);
- }
- if (needsSync && mRemoteConnection) {
- trySyncFromRemote();
- }
- return ret;
-}
-
-bool BufferPoolClient::Impl::postReceive(
- BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
- for (int i = 0; i < kPostMaxRetry; ++i) {
- std::unique_lock<std::mutex> lock(mReleasing.mLock);
- int64_t now = getTimestampNow();
- if (timestampUs == 0 || now < timestampUs) {
- bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
- transactionId, bufferId, BufferStatus::TRANSFER_FROM,
- mConnectionId, -1, mReleasing.mReleasingIds,
- mReleasing.mReleasedIds);
- if (result) {
- return true;
- }
- lock.unlock();
- std::this_thread::yield();
- } else {
- mReleasing.mStatusChannel->postBufferStatusMessage(
- transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
- mConnectionId, -1, mReleasing.mReleasingIds,
- mReleasing.mReleasedIds);
- return false;
- }
- }
- return false;
-}
-
-bool BufferPoolClient::Impl::postReceiveResult(
- BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) {
- std::lock_guard<std::mutex> lock(mReleasing.mLock);
- // TODO: retry, add timeout
- bool ret = mReleasing.mStatusChannel->postBufferStatusMessage(
- transactionId, bufferId,
- result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
- mConnectionId, -1, mReleasing.mReleasingIds,
- mReleasing.mReleasedIds);
- *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
- return ret;
-}
-
-void BufferPoolClient::Impl::trySyncFromRemote() {
- if (mRemoteSyncLock.try_lock()) {
- bool needsSync = false;
- {
- std::lock_guard<std::mutex> lock(mReleasing.mLock);
- needsSync = mReleasing.mStatusChannel->needsSync();
- }
- if (needsSync) {
- TransactionId transactionId = (mConnectionId << 32);
- BufferId bufferId = Connection::SYNC_BUFFERID;
- Return<void> transResult = mRemoteConnection->fetch(
- transactionId, bufferId,
- []
- (ResultStatus outStatus, Buffer outBuffer) {
- (void) outStatus;
- (void) outBuffer;
- });
- }
- mRemoteSyncLock.unlock();
- }
-}
-
-// should have mCache.mLock
-bool BufferPoolClient::Impl::syncReleased() {
- std::lock_guard<std::mutex> lock(mReleasing.mLock);
- if (mReleasing.mReleasingIds.size() > 0) {
- mReleasing.mStatusChannel->postBufferRelease(
- mConnectionId, mReleasing.mReleasingIds,
- mReleasing.mReleasedIds);
- }
- if (mReleasing.mReleasedIds.size() > 0) {
- for (BufferId& id: mReleasing.mReleasedIds) {
- ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
- auto found = mCache.mBuffers.find(id);
- if (found != mCache.mBuffers.end()) {
- if (found->second->onCacheRelease()) {
- mCache.decActive_l();
- } else {
- // should not happen!
- ALOGW("client %lld cache release status inconsitent!",
- (long long)mConnectionId);
- }
- } else {
- // should not happen!
- ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
- }
- }
- mReleasing.mReleasedIds.clear();
- return true;
- }
- return false;
-}
-
-// should have mCache.mLock
-void BufferPoolClient::Impl::evictCaches(bool clearCache) {
- int64_t now = getTimestampNow();
- if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
- size_t evicted = 0;
- for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
- if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
- it = mCache.mBuffers.erase(it);
- ++evicted;
- } else {
- ++it;
- }
- }
- ALOGV("cache count %lld : total %zu, active %d, evicted %zu",
- (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted);
- mLastEvictCacheUs = now;
- }
-}
-
-ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
- const std::vector<uint8_t>& params, BufferId *bufferId,
- native_handle_t** handle) {
- if (mLocalConnection) {
- const native_handle_t* allocHandle = NULL;
- ResultStatus status = mLocalConnection->allocate(
- params, bufferId, &allocHandle);
- if (status == ResultStatus::OK) {
- *handle = native_handle_clone(allocHandle);
- }
- ALOGV("client allocate result %lld %d : %u clone %p",
- (long long)mConnectionId, status == ResultStatus::OK,
- *handle ? *bufferId : 0 , *handle);
- return status;
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
- TransactionId transactionId, BufferId bufferId,
- native_handle_t **handle) {
- sp<IConnection> connection;
- if (mLocal) {
- connection = mLocalConnection;
- } else {
- connection = mRemoteConnection;
- }
- ResultStatus status;
- Return<void> transResult = connection->fetch(
- transactionId, bufferId,
- [&status, &handle]
- (ResultStatus outStatus, Buffer outBuffer) {
- status = outStatus;
- if (status == ResultStatus::OK) {
- *handle = native_handle_clone(
- outBuffer.buffer.getNativeHandle());
- }
- });
- return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR;
-}
-
-
-BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
- mImpl = std::make_shared<Impl>(accessor);
-}
-
-BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
- mImpl = std::make_shared<Impl>(accessor);
-}
-
-BufferPoolClient::~BufferPoolClient() {
- // TODO: how to handle orphaned buffers?
-}
-
-bool BufferPoolClient::isValid() {
- return mImpl && mImpl->isValid();
-}
-
-bool BufferPoolClient::isLocal() {
- return mImpl && mImpl->isLocal();
-}
-
-bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) {
- if (!isValid()) {
- *lastTransactionUs = 0;
- return false;
- }
- return mImpl->isActive(lastTransactionUs, clearCache);
-}
-
-ConnectionId BufferPoolClient::getConnectionId() {
- if (isValid()) {
- return mImpl->getConnectionId();
- }
- return -1;
-}
-
-ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
- if (isValid()) {
- *accessor = mImpl->getAccessor();
- return ResultStatus::OK;
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus BufferPoolClient::allocate(
- const std::vector<uint8_t> &params,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer) {
- if (isValid()) {
- return mImpl->allocate(params, handle, buffer);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus BufferPoolClient::receive(
- TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
- native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
- if (isValid()) {
- return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus BufferPoolClient::postSend(
- ConnectionId receiverId,
- const std::shared_ptr<BufferPoolData> &buffer,
- TransactionId *transactionId,
- int64_t *timestampUs) {
- if (isValid()) {
- bool result = mImpl->postSend(
- buffer->mId, receiverId, transactionId, timestampUs);
- return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
diff --git a/codec2/vndk/bufferpool/BufferPoolClient.h b/codec2/vndk/bufferpool/BufferPoolClient.h
deleted file mode 100644
index 577efed..0000000
--- a/codec2/vndk/bufferpool/BufferPoolClient.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
-
-#include <memory>
-#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
-#include <android/hardware/media/bufferpool/1.0/IConnection.h>
-#include <bufferpool/BufferPoolTypes.h>
-#include <cutils/native_handle.h>
-#include "Accessor.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::media::bufferpool::V1_0::IAccessor;
-using ::android::hardware::media::bufferpool::V1_0::IConnection;
-using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
-using ::android::sp;
-
-/**
- * A buffer pool client for a buffer pool. For a specific buffer pool, at most
- * one buffer pool client exists per process. This class will not be exposed
- * outside. A buffer pool client will be used via ClientManager.
- */
-class BufferPoolClient {
-public:
- /**
- * Creates a buffer pool client from a local buffer pool
- * (via ClientManager#create).
- */
- explicit BufferPoolClient(const sp<Accessor> &accessor);
-
- /**
- * Creates a buffer pool client from a remote buffer pool
- * (via ClientManager#registerSender).
- * Note: A buffer pool client created with remote buffer pool cannot
- * allocate a buffer.
- */
- explicit BufferPoolClient(const sp<IAccessor> &accessor);
-
- /** Destructs a buffer pool client. */
- ~BufferPoolClient();
-
-private:
- bool isValid();
-
- bool isLocal();
-
- bool isActive(int64_t *lastTransactionUs, bool clearCache);
-
- ConnectionId getConnectionId();
-
- ResultStatus getAccessor(sp<IAccessor> *accessor);
-
- ResultStatus allocate(const std::vector<uint8_t> &params,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- ResultStatus receive(TransactionId transactionId,
- BufferId bufferId,
- int64_t timestampUs,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- ResultStatus postSend(ConnectionId receiver,
- const std::shared_ptr<BufferPoolData> &buffer,
- TransactionId *transactionId,
- int64_t *timestampUs);
-
- class Impl;
- std::shared_ptr<Impl> mImpl;
-
- friend struct ClientManager;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
diff --git a/codec2/vndk/bufferpool/BufferStatus.cpp b/codec2/vndk/bufferpool/BufferStatus.cpp
deleted file mode 100644
index 596cfa5..0000000
--- a/codec2/vndk/bufferpool/BufferStatus.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BufferPoolStatus"
-//#define LOG_NDEBUG 0
-
-#include <time.h>
-#include "BufferStatus.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-int64_t getTimestampNow() {
- int64_t stamp;
- struct timespec ts;
- // TODO: CLOCK_MONOTONIC_COARSE?
- clock_gettime(CLOCK_MONOTONIC, &ts);
- stamp = ts.tv_nsec / 1000;
- stamp += (ts.tv_sec * 1000000LL);
- return stamp;
-}
-
-static constexpr int kNumElementsInQueue = 1024*16;
-static constexpr int kMinElementsToSyncInQueue = 128;
-
-ResultStatus BufferStatusObserver::open(
- ConnectionId id, const QueueDescriptor** fmqDescPtr) {
- if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
- // TODO: id collision log?
- return ResultStatus::CRITICAL_ERROR;
- }
- std::unique_ptr<BufferStatusQueue> queue =
- std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
- if (!queue || queue->isValid() == false) {
- *fmqDescPtr = NULL;
- return ResultStatus::NO_MEMORY;
- } else {
- *fmqDescPtr = queue->getDesc();
- }
- auto result = mBufferStatusQueues.insert(
- std::make_pair(id, std::move(queue)));
- if (!result.second) {
- *fmqDescPtr = NULL;
- return ResultStatus::NO_MEMORY;
- }
- return ResultStatus::OK;
-}
-
-ResultStatus BufferStatusObserver::close(ConnectionId id) {
- if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
- return ResultStatus::CRITICAL_ERROR;
- }
- mBufferStatusQueues.erase(id);
- return ResultStatus::OK;
-}
-
-void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
- for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
- BufferStatusMessage message;
- size_t avail = it->second->availableToRead();
- while (avail > 0) {
- if (!it->second->read(&message, 1)) {
- // Since avaliable # of reads are already confirmed,
- // this should not happen.
- // TODO: error handling (spurious client?)
- ALOGW("FMQ message cannot be read from %lld", (long long)it->first);
- return;
- }
- message.connectionId = it->first;
- messages.push_back(message);
- --avail;
- }
- }
-}
-
-BufferStatusChannel::BufferStatusChannel(
- const QueueDescriptor &fmqDesc) {
- std::unique_ptr<BufferStatusQueue> queue =
- std::make_unique<BufferStatusQueue>(fmqDesc);
- if (!queue || queue->isValid() == false) {
- mValid = false;
- return;
- }
- mValid = true;
- mBufferStatusQueue = std::move(queue);
-}
-
-bool BufferStatusChannel::isValid() {
- return mValid;
-}
-
-bool BufferStatusChannel::needsSync() {
- if (mValid) {
- size_t avail = mBufferStatusQueue->availableToWrite();
- return avail + kMinElementsToSyncInQueue < kNumElementsInQueue;
- }
- return false;
-}
-
-void BufferStatusChannel::postBufferRelease(
- ConnectionId connectionId,
- std::list<BufferId> &pending, std::list<BufferId> &posted) {
- if (mValid && pending.size() > 0) {
- size_t avail = mBufferStatusQueue->availableToWrite();
- avail = std::min(avail, pending.size());
- BufferStatusMessage message;
- for (size_t i = 0 ; i < avail; ++i) {
- BufferId id = pending.front();
- message.newStatus = BufferStatus::NOT_USED;
- message.bufferId = id;
- message.connectionId = connectionId;
- if (!mBufferStatusQueue->write(&message, 1)) {
- // Since avaliable # of writes are already confirmed,
- // this should not happen.
- // TODO: error handing?
- ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
- return;
- }
- pending.pop_front();
- posted.push_back(id);
- }
- }
-}
-
-bool BufferStatusChannel::postBufferStatusMessage(
- TransactionId transactionId, BufferId bufferId,
- BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
- std::list<BufferId> &pending, std::list<BufferId> &posted) {
- if (mValid) {
- size_t avail = mBufferStatusQueue->availableToWrite();
- size_t numPending = pending.size();
- if (avail >= numPending + 1) {
- BufferStatusMessage release, message;
- for (size_t i = 0; i < numPending; ++i) {
- BufferId id = pending.front();
- release.newStatus = BufferStatus::NOT_USED;
- release.bufferId = id;
- release.connectionId = connectionId;
- if (!mBufferStatusQueue->write(&release, 1)) {
- // Since avaliable # of writes are already confirmed,
- // this should not happen.
- // TODO: error handling?
- ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
- return false;
- }
- pending.pop_front();
- posted.push_back(id);
- }
- message.transactionId = transactionId;
- message.bufferId = bufferId;
- message.newStatus = status;
- message.connectionId = connectionId;
- message.targetConnectionId = targetId;
- // TODO : timesatamp
- message.timestampUs = 0;
- if (!mBufferStatusQueue->write(&message, 1)) {
- // Since avaliable # of writes are already confirmed,
- // this should not happen.
- ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
- return false;
- }
- return true;
- }
- }
- return false;
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
diff --git a/codec2/vndk/bufferpool/BufferStatus.h b/codec2/vndk/bufferpool/BufferStatus.h
deleted file mode 100644
index a18a921..0000000
--- a/codec2/vndk/bufferpool/BufferStatus.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
-
-#include <android/hardware/media/bufferpool/1.0/types.h>
-#include <bufferpool/BufferPoolTypes.h>
-#include <fmq/MessageQueue.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-#include <memory>
-#include <mutex>
-#include <vector>
-#include <list>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-/** Returns monotonic timestamp in Us since fixed point in time. */
-int64_t getTimestampNow();
-
-/**
- * A collection of FMQ for a buffer pool. buffer ownership/status change
- * messages are sent via the FMQs from the clients.
- */
-class BufferStatusObserver {
-private:
- std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
- mBufferStatusQueues;
-
-public:
- /** Creates an FMQ for the specified connection(client).
- *
- * @param connectionId connection Id of the specified client.
- * @param fmqDescPtr double ptr of created FMQ's descriptor.
- *
- * @return OK if FMQ is created successfully.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr);
-
- /** Closes an FMQ for the specified connection(client).
- *
- * @param connectionId connection Id of the specified client.
- *
- * @return OK if the specified connection is closed successfully.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus close(ConnectionId id);
-
- /** Retrieves all pending FMQ buffer status messages from clients.
- *
- * @param messages retrieved pending messages.
- */
- void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
-};
-
-/**
- * An FMQ for a buffer pool client. Buffer ownership/status change messages
- * are sent via the fmq to the buffer pool.
- */
-class BufferStatusChannel {
-private:
- bool mValid;
- std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
-
-public:
- /**
- * Connects to an FMQ from a descriptor of the created FMQ.
- *
- * @param fmqDesc Descriptor of the created FMQ.
- */
- BufferStatusChannel(const QueueDescriptor &fmqDesc);
-
- /** Returns whether the FMQ is connected successfully. */
- bool isValid();
-
- /** Returns whether the FMQ needs to be synced from the buffer pool */
- bool needsSync();
-
- /**
- * Posts a buffer release message to the buffer pool.
- *
- * @param connectionId connection Id of the client.
- * @param pending currently pending buffer release messages.
- * @param posted posted buffer release messages.
- */
- void postBufferRelease(
- ConnectionId connectionId,
- std::list<BufferId> &pending, std::list<BufferId> &posted);
-
- /**
- * Posts a buffer status message regarding the specified buffer
- * transfer transaction.
- *
- * @param transactionId Id of the specified transaction.
- * @param bufferId buffer Id of the specified transaction.
- * @param status new status of the buffer.
- * @param connectionId connection Id of the client.
- * @param targetId connection Id of the receiver(only when the sender
- * posts a status message).
- * @param pending currently pending buffer release messages.
- * @param posted posted buffer release messages.
- *
- * @return {@code true} when the specified message is posted,
- * {@code false} otherwise.
- */
- bool postBufferStatusMessage(
- TransactionId transactionId,
- BufferId bufferId,
- BufferStatus status,
- ConnectionId connectionId,
- ConnectionId targetId,
- std::list<BufferId> &pending, std::list<BufferId> &posted);
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
diff --git a/codec2/vndk/bufferpool/ClientManager.cpp b/codec2/vndk/bufferpool/ClientManager.cpp
deleted file mode 100644
index ecea0a4..0000000
--- a/codec2/vndk/bufferpool/ClientManager.cpp
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_TAG "BufferPoolManager"
-//#define LOG_NDEBUG 0
-
-#include <bufferpool/ClientManager.h>
-#include <hidl/HidlTransportSupport.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <utils/Log.h>
-#include "BufferPoolClient.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
-static constexpr int64_t kCleanUpDurationUs = 1000000; // TODO: 1 sec tune
-static constexpr int64_t kClientTimeoutUs = 5000000; // TODO: 5 secs tune
-
-/**
- * The holder of the cookie of remote IClientManager.
- * The cookie is process locally unique for each IClientManager.
- * (The cookie is used to notify death of clients to bufferpool process.)
- */
-class ClientManagerCookieHolder {
-public:
- /**
- * Creates a cookie holder for remote IClientManager(s).
- */
- ClientManagerCookieHolder();
-
- /**
- * Gets a cookie for a remote IClientManager.
- *
- * @param manager the specified remote IClientManager.
- * @param added true when the specified remote IClientManager is added
- * newly, false otherwise.
- *
- * @return the process locally unique cookie for the specified IClientManager.
- */
- uint64_t getCookie(const sp<IClientManager> &manager, bool *added);
-
-private:
- uint64_t mSeqId;
- std::mutex mLock;
- std::list<std::pair<const wp<IClientManager>, uint64_t>> mManagers;
-};
-
-ClientManagerCookieHolder::ClientManagerCookieHolder() : mSeqId(0){}
-
-uint64_t ClientManagerCookieHolder::getCookie(
- const sp<IClientManager> &manager,
- bool *added) {
- std::lock_guard<std::mutex> lock(mLock);
- for (auto it = mManagers.begin(); it != mManagers.end();) {
- const sp<IClientManager> key = it->first.promote();
- if (key) {
- if (interfacesEqual(key, manager)) {
- *added = false;
- return it->second;
- }
- ++it;
- } else {
- it = mManagers.erase(it);
- }
- }
- uint64_t id = mSeqId++;
- *added = true;
- mManagers.push_back(std::make_pair(manager, id));
- return id;
-}
-
-class ClientManager::Impl {
-public:
- Impl();
-
- // BnRegisterSender
- ResultStatus registerSender(const sp<IAccessor> &accessor,
- ConnectionId *pConnectionId);
-
- // BpRegisterSender
- ResultStatus registerSender(const sp<IClientManager> &receiver,
- ConnectionId senderId,
- ConnectionId *receiverId);
-
- ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
- ConnectionId *pConnectionId);
-
- ResultStatus close(ConnectionId connectionId);
-
- ResultStatus allocate(ConnectionId connectionId,
- const std::vector<uint8_t> &params,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- ResultStatus receive(ConnectionId connectionId,
- TransactionId transactionId,
- BufferId bufferId,
- int64_t timestampUs,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- ResultStatus postSend(ConnectionId receiverId,
- const std::shared_ptr<BufferPoolData> &buffer,
- TransactionId *transactionId,
- int64_t *timestampUs);
-
- ResultStatus getAccessor(ConnectionId connectionId,
- sp<IAccessor> *accessor);
-
- void cleanUp(bool clearCache = false);
-
-private:
- // In order to prevent deadlock between multiple locks,
- // always lock ClientCache.lock before locking ActiveClients.lock.
- struct ClientCache {
- // This lock is held for brief duration.
- // Blocking operation is not performed while holding the lock.
- std::mutex mMutex;
- std::list<std::pair<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>>
- mClients;
- std::condition_variable mConnectCv;
- bool mConnecting;
- int64_t mLastCleanUpUs;
-
- ClientCache() : mConnecting(false), mLastCleanUpUs(getTimestampNow()) {}
- } mCache;
-
- // Active clients which can be retrieved via ConnectionId
- struct ActiveClients {
- // This lock is held for brief duration.
- // Blocking operation is not performed holding the lock.
- std::mutex mMutex;
- std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
- mClients;
- } mActive;
-
- ClientManagerCookieHolder mRemoteClientCookies;
-};
-
-ClientManager::Impl::Impl() {}
-
-ResultStatus ClientManager::Impl::registerSender(
- const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
- cleanUp();
- int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
- do {
- std::unique_lock<std::mutex> lock(mCache.mMutex);
- for (auto it = mCache.mClients.begin(); it != mCache.mClients.end(); ++it) {
- sp<IAccessor> sAccessor = it->first.promote();
- if (sAccessor && interfacesEqual(sAccessor, accessor)) {
- const std::shared_ptr<BufferPoolClient> client = it->second.lock();
- if (client) {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- *pConnectionId = client->getConnectionId();
- if (mActive.mClients.find(*pConnectionId) != mActive.mClients.end()) {
- ALOGV("register existing connection %lld", (long long)*pConnectionId);
- return ResultStatus::ALREADY_EXISTS;
- }
- }
- mCache.mClients.erase(it);
- break;
- }
- }
- if (!mCache.mConnecting) {
- mCache.mConnecting = true;
- lock.unlock();
- ResultStatus result = ResultStatus::OK;
- const std::shared_ptr<BufferPoolClient> client =
- std::make_shared<BufferPoolClient>(accessor);
- lock.lock();
- if (!client) {
- result = ResultStatus::NO_MEMORY;
- } else if (!client->isValid()) {
- result = ResultStatus::CRITICAL_ERROR;
- }
- if (result == ResultStatus::OK) {
- // TODO: handle insert fail. (malloc fail)
- const std::weak_ptr<BufferPoolClient> wclient = client;
- mCache.mClients.push_back(std::make_pair(accessor, wclient));
- ConnectionId conId = client->getConnectionId();
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- mActive.mClients.insert(std::make_pair(conId, client));
- }
- *pConnectionId = conId;
- ALOGV("register new connection %lld", (long long)*pConnectionId);
- }
- mCache.mConnecting = false;
- lock.unlock();
- mCache.mConnectCv.notify_all();
- return result;
- }
- mCache.mConnectCv.wait_for(
- lock, std::chrono::microseconds(kRegisterTimeoutUs));
- } while (getTimestampNow() < timeoutUs);
- // TODO: return timeout error
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus ClientManager::Impl::registerSender(
- const sp<IClientManager> &receiver,
- ConnectionId senderId,
- ConnectionId *receiverId) {
- sp<IAccessor> accessor;
- bool local = false;
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- auto it = mActive.mClients.find(senderId);
- if (it == mActive.mClients.end()) {
- return ResultStatus::NOT_FOUND;
- }
- it->second->getAccessor(&accessor);
- local = it->second->isLocal();
- }
- ResultStatus rs = ResultStatus::CRITICAL_ERROR;
- if (accessor) {
- Return<void> transResult = receiver->registerSender(
- accessor,
- [&rs, receiverId](
- ResultStatus status,
- int64_t connectionId) {
- rs = status;
- *receiverId = connectionId;
- });
- if (!transResult.isOk()) {
- return ResultStatus::CRITICAL_ERROR;
- } else if (local && rs == ResultStatus::OK) {
- sp<ConnectionDeathRecipient> recipient = Accessor::getConnectionDeathRecipient();
- if (recipient) {
- ALOGV("client death recipient registered %lld", (long long)*receiverId);
- bool added;
- uint64_t cookie = mRemoteClientCookies.getCookie(receiver, &added);
- recipient->addCookieToConnection(cookie, *receiverId);
- if (added) {
- Return<bool> transResult = receiver->linkToDeath(recipient, cookie);
- }
- }
- }
- }
- return rs;
-}
-
-ResultStatus ClientManager::Impl::create(
- const std::shared_ptr<BufferPoolAllocator> &allocator,
- ConnectionId *pConnectionId) {
- const sp<Accessor> accessor = new Accessor(allocator);
- if (!accessor || !accessor->isValid()) {
- return ResultStatus::CRITICAL_ERROR;
- }
- std::shared_ptr<BufferPoolClient> client =
- std::make_shared<BufferPoolClient>(accessor);
- if (!client || !client->isValid()) {
- return ResultStatus::CRITICAL_ERROR;
- }
- // Since a new bufferpool is created, evict memories which are used by
- // existing bufferpools and clients.
- cleanUp(true);
- {
- // TODO: handle insert fail. (malloc fail)
- std::lock_guard<std::mutex> lock(mCache.mMutex);
- const std::weak_ptr<BufferPoolClient> wclient = client;
- mCache.mClients.push_back(std::make_pair(accessor, wclient));
- ConnectionId conId = client->getConnectionId();
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- mActive.mClients.insert(std::make_pair(conId, client));
- }
- *pConnectionId = conId;
- ALOGV("create new connection %lld", (long long)*pConnectionId);
- }
- return ResultStatus::OK;
-}
-
-ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
- std::lock_guard<std::mutex> lock1(mCache.mMutex);
- std::lock_guard<std::mutex> lock2(mActive.mMutex);
- auto it = mActive.mClients.find(connectionId);
- if (it != mActive.mClients.end()) {
- sp<IAccessor> accessor;
- it->second->getAccessor(&accessor);
- mActive.mClients.erase(connectionId);
- for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
- // clean up dead client caches
- sp<IAccessor> cAccessor = cit->first.promote();
- if (!cAccessor || (accessor && interfacesEqual(cAccessor, accessor))) {
- cit = mCache.mClients.erase(cit);
- } else {
- cit++;
- }
- }
- return ResultStatus::OK;
- }
- return ResultStatus::NOT_FOUND;
-}
-
-ResultStatus ClientManager::Impl::allocate(
- ConnectionId connectionId, const std::vector<uint8_t> &params,
- native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
- std::shared_ptr<BufferPoolClient> client;
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- auto it = mActive.mClients.find(connectionId);
- if (it == mActive.mClients.end()) {
- return ResultStatus::NOT_FOUND;
- }
- client = it->second;
- }
- return client->allocate(params, handle, buffer);
-}
-
-ResultStatus ClientManager::Impl::receive(
- ConnectionId connectionId, TransactionId transactionId,
- BufferId bufferId, int64_t timestampUs,
- native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
- std::shared_ptr<BufferPoolClient> client;
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- auto it = mActive.mClients.find(connectionId);
- if (it == mActive.mClients.end()) {
- return ResultStatus::NOT_FOUND;
- }
- client = it->second;
- }
- return client->receive(transactionId, bufferId, timestampUs, handle, buffer);
-}
-
-ResultStatus ClientManager::Impl::postSend(
- ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
- TransactionId *transactionId, int64_t *timestampUs) {
- ConnectionId connectionId = buffer->mConnectionId;
- std::shared_ptr<BufferPoolClient> client;
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- auto it = mActive.mClients.find(connectionId);
- if (it == mActive.mClients.end()) {
- return ResultStatus::NOT_FOUND;
- }
- client = it->second;
- }
- return client->postSend(receiverId, buffer, transactionId, timestampUs);
-}
-
-ResultStatus ClientManager::Impl::getAccessor(
- ConnectionId connectionId, sp<IAccessor> *accessor) {
- std::shared_ptr<BufferPoolClient> client;
- {
- std::lock_guard<std::mutex> lock(mActive.mMutex);
- auto it = mActive.mClients.find(connectionId);
- if (it == mActive.mClients.end()) {
- return ResultStatus::NOT_FOUND;
- }
- client = it->second;
- }
- return client->getAccessor(accessor);
-}
-
-void ClientManager::Impl::cleanUp(bool clearCache) {
- int64_t now = getTimestampNow();
- int64_t lastTransactionUs;
- std::lock_guard<std::mutex> lock1(mCache.mMutex);
- if (clearCache || mCache.mLastCleanUpUs + kCleanUpDurationUs < now) {
- std::lock_guard<std::mutex> lock2(mActive.mMutex);
- int cleaned = 0;
- for (auto it = mActive.mClients.begin(); it != mActive.mClients.end();) {
- if (!it->second->isActive(&lastTransactionUs, clearCache)) {
- if (lastTransactionUs + kClientTimeoutUs < now) {
- sp<IAccessor> accessor;
- it->second->getAccessor(&accessor);
- it = mActive.mClients.erase(it);
- ++cleaned;
- continue;
- }
- }
- ++it;
- }
- for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
- // clean up dead client caches
- sp<IAccessor> cAccessor = cit->first.promote();
- if (!cAccessor) {
- cit = mCache.mClients.erase(cit);
- } else {
- ++cit;
- }
- }
- ALOGV("# of cleaned connections: %d", cleaned);
- mCache.mLastCleanUpUs = now;
- }
-}
-
-// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
-Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
- if (mImpl) {
- ConnectionId connectionId = -1;
- ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
- _hidl_cb(status, connectionId);
- } else {
- _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
- }
- return Void();
-}
-
-// Methods for local use.
-sp<ClientManager> ClientManager::sInstance;
-std::mutex ClientManager::sInstanceLock;
-
-sp<ClientManager> ClientManager::getInstance() {
- std::lock_guard<std::mutex> lock(sInstanceLock);
- if (!sInstance) {
- sInstance = new ClientManager();
- }
- return sInstance;
-}
-
-ClientManager::ClientManager() : mImpl(new Impl()) {}
-
-ClientManager::~ClientManager() {
-}
-
-ResultStatus ClientManager::create(
- const std::shared_ptr<BufferPoolAllocator> &allocator,
- ConnectionId *pConnectionId) {
- if (mImpl) {
- return mImpl->create(allocator, pConnectionId);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus ClientManager::registerSender(
- const sp<IClientManager> &receiver,
- ConnectionId senderId,
- ConnectionId *receiverId) {
- if (mImpl) {
- return mImpl->registerSender(receiver, senderId, receiverId);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus ClientManager::close(ConnectionId connectionId) {
- if (mImpl) {
- return mImpl->close(connectionId);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus ClientManager::allocate(
- ConnectionId connectionId, const std::vector<uint8_t> &params,
- native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
- if (mImpl) {
- return mImpl->allocate(connectionId, params, handle, buffer);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus ClientManager::receive(
- ConnectionId connectionId, TransactionId transactionId,
- BufferId bufferId, int64_t timestampUs,
- native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
- if (mImpl) {
- return mImpl->receive(connectionId, transactionId, bufferId,
- timestampUs, handle, buffer);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus ClientManager::postSend(
- ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
- TransactionId *transactionId, int64_t* timestampUs) {
- if (mImpl && buffer) {
- return mImpl->postSend(receiverId, buffer, transactionId, timestampUs);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-void ClientManager::cleanUp() {
- if (mImpl) {
- mImpl->cleanUp(true);
- }
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
diff --git a/codec2/vndk/bufferpool/Connection.cpp b/codec2/vndk/bufferpool/Connection.cpp
deleted file mode 100644
index 9e741e7..0000000
--- a/codec2/vndk/bufferpool/Connection.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Connection.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
-Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
- ResultStatus status = ResultStatus::CRITICAL_ERROR;
- if (mInitialized && mAccessor) {
- if (bufferId != SYNC_BUFFERID) {
- const native_handle_t *handle = NULL;
- status = mAccessor->fetch(
- mConnectionId, transactionId, bufferId, &handle);
- if (status == ResultStatus::OK) {
- _hidl_cb(status, Buffer{bufferId, handle});
- return Void();
- }
- } else {
- mAccessor->cleanUp(false);
- }
- }
- _hidl_cb(status, Buffer{0, nullptr});
- return Void();
-}
-
-Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
-
-Connection::~Connection() {
- if (mInitialized && mAccessor) {
- mAccessor->close(mConnectionId);
- }
-}
-
-void Connection::initialize(
- const sp<Accessor>& accessor, ConnectionId connectionId) {
- if (!mInitialized) {
- mAccessor = accessor;
- mConnectionId = connectionId;
- mInitialized = true;
- }
-}
-
-ResultStatus Connection::allocate(
- const std::vector<uint8_t> &params, BufferId *bufferId,
- const native_handle_t **handle) {
- if (mInitialized && mAccessor) {
- return mAccessor->allocate(mConnectionId, params, bufferId, handle);
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-void Connection::cleanUp(bool clearCache) {
- if (mInitialized && mAccessor) {
- mAccessor->cleanUp(clearCache);
- }
-}
-
-// Methods from ::android::hidl::base::V1_0::IBase follow.
-
-//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
-// return new Connection();
-//}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
diff --git a/codec2/vndk/bufferpool/Connection.h b/codec2/vndk/bufferpool/Connection.h
deleted file mode 100644
index e19cb67..0000000
--- a/codec2/vndk/bufferpool/Connection.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
-
-#include <android/hardware/media/bufferpool/1.0/IConnection.h>
-#include <bufferpool/BufferPoolTypes.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-#include "Accessor.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::media::bufferpool::V1_0::implementation::Accessor;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::sp;
-
-struct Connection : public IConnection {
- // Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
- Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
-
- /**
- * Allocates a buffer using the specified parameters. Recycles a buffer if
- * it is possible. The returned buffer can be transferred to other remote
- * clients(Connection).
- *
- * @param params allocation parameters.
- * @param bufferId Id of the allocated buffer.
- * @param handle native handle of the allocated buffer.
- *
- * @return OK if a buffer is successfully allocated.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus allocate(const std::vector<uint8_t> &params,
- BufferId *bufferId, const native_handle_t **handle);
-
- /**
- * Processes pending buffer status messages and performs periodic cache cleaning
- * from bufferpool.
- *
- * @param clearCache if clearCache is true, bufferpool frees all buffers
- * waiting to be recycled.
- */
- void cleanUp(bool clearCache);
-
- /** Destructs a connection. */
- ~Connection();
-
- /** Creates a connection. */
- Connection();
-
- /**
- * Initializes with the specified buffer pool and the connection id.
- * The connection id should be unique in the whole system.
- *
- * @param accessor the specified buffer pool.
- * @param connectionId Id.
- */
- void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
-
- enum : uint32_t {
- SYNC_BUFFERID = UINT32_MAX,
- };
-
-private:
- bool mInitialized;
- sp<Accessor> mAccessor;
- ConnectionId mConnectionId;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
diff --git a/codec2/vndk/bufferpool/include/bufferpool/BufferPoolTypes.h b/codec2/vndk/bufferpool/include/bufferpool/BufferPoolTypes.h
deleted file mode 100644
index 710f015..0000000
--- a/codec2/vndk/bufferpool/include/bufferpool/BufferPoolTypes.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
-
-#include <android/hardware/media/bufferpool/1.0/types.h>
-#include <cutils/native_handle.h>
-#include <fmq/MessageQueue.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-
-struct BufferPoolData {
- // For local use, to specify a bufferpool (client connection) for buffers.
- // Return value from connect#IAccessor(android.hardware.media.bufferpool@1.0).
- int64_t mConnectionId;
- // BufferId
- uint32_t mId;
-
- BufferPoolData() : mConnectionId(0), mId(0) {}
-
- BufferPoolData(
- int64_t connectionId, uint32_t id)
- : mConnectionId(connectionId), mId(id) {}
-
- ~BufferPoolData() {}
-};
-
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::kSynchronizedReadWrite;
-
-typedef uint32_t BufferId;
-typedef uint64_t TransactionId;
-typedef int64_t ConnectionId;
-
-enum : ConnectionId {
- INVALID_CONNECTIONID = 0,
-};
-
-typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
-typedef BufferStatusQueue::Descriptor QueueDescriptor;
-
-/**
- * Allocation wrapper class for buffer pool.
- */
-struct BufferPoolAllocation {
- const native_handle_t *mHandle;
-
- const native_handle_t *handle() {
- return mHandle;
- }
-
- BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
-
- ~BufferPoolAllocation() {};
-};
-
-/**
- * Allocator wrapper class for buffer pool.
- */
-class BufferPoolAllocator {
-public:
-
- /**
- * Allocate an allocation(buffer) for buffer pool.
- *
- * @param params allocation parameters
- * @param alloc created allocation
- * @param allocSize size of created allocation
- *
- * @return OK when an allocation is created successfully.
- */
- virtual ResultStatus allocate(
- const std::vector<uint8_t> &params,
- std::shared_ptr<BufferPoolAllocation> *alloc,
- size_t *allocSize) = 0;
-
- /**
- * Returns whether allocation parameters of an old allocation are
- * compatible with new allocation parameters.
- */
- virtual bool compatible(const std::vector<uint8_t> &newParams,
- const std::vector<uint8_t> &oldParams) = 0;
-
-protected:
- BufferPoolAllocator() = default;
-
- virtual ~BufferPoolAllocator() = default;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
diff --git a/codec2/vndk/bufferpool/include/bufferpool/ClientManager.h b/codec2/vndk/bufferpool/include/bufferpool/ClientManager.h
deleted file mode 100644
index be5779f..0000000
--- a/codec2/vndk/bufferpool/include/bufferpool/ClientManager.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
-#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
-
-#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-#include <memory>
-#include "BufferPoolTypes.h"
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace bufferpool {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::media::bufferpool::V1_0::IAccessor;
-using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::sp;
-
-struct ClientManager : public IClientManager {
- // Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
- Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
-
- /** Gets an instance. */
- static sp<ClientManager> getInstance();
-
- /**
- * Creates a local connection with a newly created buffer pool.
- *
- * @param allocator for new buffer allocation.
- * @param pConnectionId Id of the created connection. This is
- * system-wide unique.
- *
- * @return OK when a buffer pool and a local connection is successfully
- * created.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
- ConnectionId *pConnectionId);
-
- /**
- * Register a created connection as sender for remote process.
- *
- * @param receiver The remote receiving process.
- * @param senderId A local connection which will send buffers to.
- * @param receiverId Id of the created receiving connection on the receiver
- * process.
- *
- * @return OK when the receiving connection is successfully created on the
- * receiver process.
- * NOT_FOUND when the sender connection was not found.
- * ALREADY_EXISTS the receiving connection is already made.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus registerSender(const sp<IClientManager> &receiver,
- ConnectionId senderId,
- ConnectionId *receiverId);
-
- /**
- * Closes the specified connection.
- *
- * @param connectionId The id of the connection.
- *
- * @return OK when the connection is closed.
- * NOT_FOUND when the specified connection was not found.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus close(ConnectionId connectionId);
-
- /**
- * Allocates a buffer from the specified connection.
- *
- * @param connectionId The id of the connection.
- * @param params The allocation parameters.
- * @param handle The native handle to the allocated buffer. handle
- * should be cloned before use.
- * @param buffer The allocated buffer.
- *
- * @return OK when a buffer was allocated successfully.
- * NOT_FOUND when the specified connection was not found.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus allocate(ConnectionId connectionId,
- const std::vector<uint8_t> &params,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- /**
- * Receives a buffer for the transaction.
- *
- * @param connectionId The id of the receiving connection.
- * @param transactionId The id for the transaction.
- * @param bufferId The id for the buffer.
- * @param timestampUs The timestamp of the buffer is being sent.
- * @param handle The native handle to the allocated buffer. handle
- * should be cloned before use.
- * @param buffer The received buffer.
- *
- * @return OK when a buffer was received successfully.
- * NOT_FOUND when the specified connection was not found.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus receive(ConnectionId connectionId,
- TransactionId transactionId,
- BufferId bufferId,
- int64_t timestampUs,
- native_handle_t **handle,
- std::shared_ptr<BufferPoolData> *buffer);
-
- /**
- * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
- * to other remote clients(connection) after this call has been succeeded.
- *
- * @param receiverId The id of the receiving connection.
- * @param buffer to transfer
- * @param transactionId Id of the transfer transaction.
- * @param timestampUs The timestamp of the buffer transaction is being
- * posted.
- *
- * @return OK when a buffer transaction was posted successfully.
- * NOT_FOUND when the sending connection was not found.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus postSend(ConnectionId receiverId,
- const std::shared_ptr<BufferPoolData> &buffer,
- TransactionId *transactionId,
- int64_t *timestampUs);
-
- /**
- * Time out inactive lingering connections and close.
- */
- void cleanUp();
-
- /** Destructs the manager of buffer pool clients. */
- ~ClientManager();
-private:
- static sp<ClientManager> sInstance;
- static std::mutex sInstanceLock;
-
- class Impl;
- const std::unique_ptr<Impl> mImpl;
-
- ClientManager();
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferpool
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
diff --git a/codec2/vndk/bufferpool/vts/Android.bp b/codec2/vndk/bufferpool/vts/Android.bp
deleted file mode 100644
index 62286f3..0000000
--- a/codec2/vndk/bufferpool/vts/Android.bp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-cc_test {
- name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "allocator.cpp",
- "single.cpp",
- ],
- static_libs: [
- "android.hardware.media.bufferpool@1.0",
- "libion",
- "libstagefright_bufferpool@1.0",
- ],
- shared_libs: [
- "libfmq",
- "libstagefright_codec2",
- "libstagefright_codec2_vndk",
- ],
- compile_multilib: "both",
-}
-
-cc_test {
- name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "allocator.cpp",
- "multi.cpp",
- ],
- static_libs: [
- "android.hardware.media.bufferpool@1.0",
- "libion",
- "libstagefright_bufferpool@1.0",
- ],
- shared_libs: [
- "libfmq",
- "libstagefright_codec2",
- "libstagefright_codec2_vndk",
- ],
- compile_multilib: "both",
-}
diff --git a/codec2/vndk/bufferpool/vts/OWNERS b/codec2/vndk/bufferpool/vts/OWNERS
deleted file mode 100644
index 4c3f7a1..0000000
--- a/codec2/vndk/bufferpool/vts/OWNERS
+++ /dev/null
@@ -1,7 +0,0 @@
-# Media team
-taklee@google.com
-lajos@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
diff --git a/codec2/vndk/bufferpool/vts/allocator.cpp b/codec2/vndk/bufferpool/vts/allocator.cpp
deleted file mode 100644
index 04e3b4e..0000000
--- a/codec2/vndk/bufferpool/vts/allocator.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <C2Buffer.h>
-#include "allocator.h"
-
-union Params {
- struct {
- uint32_t capacity;
- C2MemoryUsage usage;
- } data;
- uint8_t array[0];
- Params() : data{0, {0, 0}} {}
- Params(uint32_t size)
- : data{size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
-};
-
-struct AllocationDtor {
- AllocationDtor(const std::shared_ptr<C2LinearAllocation> &alloc)
- : mAlloc(alloc) {}
-
- void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
-
- const std::shared_ptr<C2LinearAllocation> mAlloc;
-};
-
-ResultStatus VtsBufferPoolAllocator::allocate(
- const std::vector<uint8_t> &params,
- std::shared_ptr<BufferPoolAllocation> *alloc,
- size_t *allocSize) {
- Params ionParams;
- memcpy(&ionParams, params.data(), std::min(sizeof(Params), params.size()));
-
- std::shared_ptr<C2LinearAllocation> linearAlloc;
- c2_status_t status = mAllocator->newLinearAllocation(
- ionParams.data.capacity, ionParams.data.usage, &linearAlloc);
- if (status == C2_OK && linearAlloc) {
- BufferPoolAllocation *ptr = new BufferPoolAllocation(linearAlloc->handle());
- if (ptr) {
- *alloc = std::shared_ptr<BufferPoolAllocation>(
- ptr, AllocationDtor(linearAlloc));
- if (*alloc) {
- *allocSize = ionParams.data.capacity;
- return ResultStatus::OK;
- }
- delete ptr;
- return ResultStatus::NO_MEMORY;
- }
- }
- return ResultStatus::CRITICAL_ERROR;
-}
-
-bool VtsBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
- const std::vector<uint8_t> &oldParams) {
- size_t newSize = newParams.size();
- size_t oldSize = oldParams.size();
- if (newSize == oldSize) {
- for (size_t i = 0; i < newSize; ++i) {
- if (newParams[i] != oldParams[i]) {
- return false;
- }
- }
- return true;
- }
- return false;
-}
-
-void getVtsAllocatorParams(std::vector<uint8_t> *params) {
- constexpr static int kAllocationSize = 1024 * 10;
- Params ionParams(kAllocationSize);
-
- params->assign(ionParams.array, ionParams.array + sizeof(ionParams));
-}
diff --git a/codec2/vndk/bufferpool/vts/allocator.h b/codec2/vndk/bufferpool/vts/allocator.h
deleted file mode 100644
index b88d074..0000000
--- a/codec2/vndk/bufferpool/vts/allocator.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
-#define VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
-
-#include <bufferpool/BufferPoolTypes.h>
-
-using android::hardware::media::bufferpool::V1_0::ResultStatus;
-using android::hardware::media::bufferpool::V1_0::implementation::
- BufferPoolAllocation;
-using android::hardware::media::bufferpool::V1_0::implementation::
- BufferPoolAllocator;
-
-// buffer allocator for the tests
-class VtsBufferPoolAllocator : public BufferPoolAllocator {
- public:
- VtsBufferPoolAllocator(const std::shared_ptr<C2Allocator> &allocator)
- : mAllocator(allocator) {}
-
- ~VtsBufferPoolAllocator() override {}
-
- ResultStatus allocate(const std::vector<uint8_t> &params,
- std::shared_ptr<BufferPoolAllocation> *alloc,
- size_t *allocSize) override;
-
- bool compatible(const std::vector<uint8_t> &newParams,
- const std::vector<uint8_t> &oldParams) override;
-
- private:
- const std::shared_ptr<C2Allocator> mAllocator;
-};
-
-// retrieve buffer allocator paramters
-void getVtsAllocatorParams(std::vector<uint8_t> *params);
-
-#endif // VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
diff --git a/codec2/vndk/bufferpool/vts/multi.cpp b/codec2/vndk/bufferpool/vts/multi.cpp
deleted file mode 100644
index 71ca6f1..0000000
--- a/codec2/vndk/bufferpool/vts/multi.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "buffferpool_unit_test"
-
-#include <gtest/gtest.h>
-
-#include <C2AllocatorIon.h>
-#include <C2Buffer.h>
-#include <C2PlatformSupport.h>
-#include <android-base/logging.h>
-#include <binder/ProcessState.h>
-#include <bufferpool/ClientManager.h>
-#include <hidl/HidlSupport.h>
-#include <hidl/HidlTransportSupport.h>
-#include <hidl/LegacySupport.h>
-#include <hidl/Status.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <vector>
-#include "allocator.h"
-
-using android::C2AllocatorIon;
-using android::C2PlatformAllocatorStore;
-using android::hardware::configureRpcThreadpool;
-using android::hardware::hidl_handle;
-using android::hardware::media::bufferpool::V1_0::IClientManager;
-using android::hardware::media::bufferpool::V1_0::ResultStatus;
-using android::hardware::media::bufferpool::V1_0::implementation::BufferId;
-using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
-using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
-using android::hardware::media::bufferpool::V1_0::implementation::TransactionId;
-using android::hardware::media::bufferpool::BufferPoolData;
-
-namespace {
-
-// communication message types between processes.
-enum PipeCommand : int32_t {
- INIT_OK = 0,
- INIT_ERROR,
- SEND,
- RECEIVE_OK,
- RECEIVE_ERROR,
-};
-
-// communication message between processes.
-union PipeMessage {
- struct {
- int32_t command;
- BufferId bufferId;
- ConnectionId connectionId;
- TransactionId transactionId;
- int64_t timestampUs;
- } data;
- char array[0];
-};
-
-// media.bufferpool test setup
-class BufferpoolMultiTest : public ::testing::Test {
- public:
- virtual void SetUp() override {
- ResultStatus status;
- mReceiverPid = -1;
- mConnectionValid = false;
-
- ASSERT_TRUE(pipe(mCommandPipeFds) == 0);
- ASSERT_TRUE(pipe(mResultPipeFds) == 0);
-
- mReceiverPid = fork();
- ASSERT_TRUE(mReceiverPid >= 0);
-
- if (mReceiverPid == 0) {
- doReceiver();
- // In order to ignore gtest behaviour, wait for being killed from
- // tearDown
- pause();
- }
-
- mManager = ClientManager::getInstance();
- ASSERT_NE(mManager, nullptr);
-
- std::shared_ptr<C2Allocator> allocator =
- std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
- ASSERT_TRUE((bool)allocator);
-
- mAllocator = std::make_shared<VtsBufferPoolAllocator>(allocator);
- ASSERT_TRUE((bool)mAllocator);
-
- status = mManager->create(mAllocator, &mConnectionId);
- ASSERT_TRUE(status == ResultStatus::OK);
- mConnectionValid = true;
- }
-
- virtual void TearDown() override {
- if (mReceiverPid > 0) {
- kill(mReceiverPid, SIGKILL);
- int wstatus;
- wait(&wstatus);
- }
-
- if (mConnectionValid) {
- mManager->close(mConnectionId);
- }
- }
-
- protected:
- static void description(const std::string& description) {
- RecordProperty("description", description);
- }
-
- android::sp<ClientManager> mManager;
- std::shared_ptr<BufferPoolAllocator> mAllocator;
- bool mConnectionValid;
- ConnectionId mConnectionId;
- pid_t mReceiverPid;
- int mCommandPipeFds[2];
- int mResultPipeFds[2];
-
- bool sendMessage(int *pipes, const PipeMessage &message) {
- int ret = write(pipes[1], message.array, sizeof(PipeMessage));
- return ret == sizeof(PipeMessage);
- }
-
- bool receiveMessage(int *pipes, PipeMessage *message) {
- int ret = read(pipes[0], message->array, sizeof(PipeMessage));
- return ret == sizeof(PipeMessage);
- }
-
- void doReceiver() {
- configureRpcThreadpool(1, false);
- PipeMessage message;
- mManager = ClientManager::getInstance();
- if (!mManager) {
- message.data.command = PipeCommand::INIT_ERROR;
- sendMessage(mResultPipeFds, message);
- return;
- }
- android::status_t status = mManager->registerAsService();
- if (status != android::OK) {
- message.data.command = PipeCommand::INIT_ERROR;
- sendMessage(mResultPipeFds, message);
- return;
- }
- message.data.command = PipeCommand::INIT_OK;
- sendMessage(mResultPipeFds, message);
-
- receiveMessage(mCommandPipeFds, &message);
- {
- native_handle_t *rhandle = nullptr;
- std::shared_ptr<BufferPoolData> rbuffer;
- ResultStatus status = mManager->receive(
- message.data.connectionId, message.data.transactionId,
- message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
- mManager->close(message.data.connectionId);
- if (status != ResultStatus::OK) {
- message.data.command = PipeCommand::RECEIVE_ERROR;
- sendMessage(mResultPipeFds, message);
- return;
- }
- }
- message.data.command = PipeCommand::RECEIVE_OK;
- sendMessage(mResultPipeFds, message);
- }
-};
-
-// Buffer transfer test between processes.
-TEST_F(BufferpoolMultiTest, TransferBuffer) {
- ResultStatus status;
- PipeMessage message;
-
- ASSERT_TRUE(receiveMessage(mResultPipeFds, &message));
-
- android::sp<IClientManager> receiver = IClientManager::getService();
- ConnectionId receiverId;
- ASSERT_TRUE((bool)receiver);
-
- status = mManager->registerSender(receiver, mConnectionId, &receiverId);
- ASSERT_TRUE(status == ResultStatus::OK);
- {
- native_handle_t *shandle = nullptr;
- std::shared_ptr<BufferPoolData> sbuffer;
- TransactionId transactionId;
- int64_t postUs;
- std::vector<uint8_t> vecParams;
-
- getVtsAllocatorParams(&vecParams);
- status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer);
- ASSERT_TRUE(status == ResultStatus::OK);
-
- status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
- ASSERT_TRUE(status == ResultStatus::OK);
-
- message.data.command = PipeCommand::SEND;
- message.data.bufferId = sbuffer->mId;
- message.data.connectionId = receiverId;
- message.data.transactionId = transactionId;
- message.data.timestampUs = postUs;
- sendMessage(mCommandPipeFds, message);
- }
- EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
-}
-
-} // anonymous namespace
-
-int main(int argc, char** argv) {
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
- ::testing::InitGoogleTest(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
diff --git a/codec2/vndk/bufferpool/vts/single.cpp b/codec2/vndk/bufferpool/vts/single.cpp
deleted file mode 100644
index 474f6b6..0000000
--- a/codec2/vndk/bufferpool/vts/single.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "buffferpool_unit_test"
-
-#include <gtest/gtest.h>
-
-#include <C2AllocatorIon.h>
-#include <C2Buffer.h>
-#include <C2PlatformSupport.h>
-#include <android-base/logging.h>
-#include <binder/ProcessState.h>
-#include <bufferpool/ClientManager.h>
-#include <hidl/HidlSupport.h>
-#include <hidl/HidlTransportSupport.h>
-#include <hidl/LegacySupport.h>
-#include <hidl/Status.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <vector>
-#include "allocator.h"
-
-using android::C2AllocatorIon;
-using android::C2PlatformAllocatorStore;
-using android::hardware::hidl_handle;
-using android::hardware::media::bufferpool::V1_0::ResultStatus;
-using android::hardware::media::bufferpool::V1_0::implementation::BufferId;
-using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
-using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
-using android::hardware::media::bufferpool::V1_0::implementation::TransactionId;
-using android::hardware::media::bufferpool::BufferPoolData;
-
-namespace {
-
-// Number of iteration for buffer allocation test.
-constexpr static int kNumAllocationTest = 3;
-
-// Number of iteration for buffer recycling test.
-constexpr static int kNumRecycleTest = 3;
-
-// media.bufferpool test setup
-class BufferpoolSingleTest : public ::testing::Test {
- public:
- virtual void SetUp() override {
- ResultStatus status;
- mConnectionValid = false;
-
- mManager = ClientManager::getInstance();
- ASSERT_NE(mManager, nullptr);
-
- std::shared_ptr<C2Allocator> allocator =
- std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
- ASSERT_TRUE((bool)allocator);
-
- mAllocator = std::make_shared<VtsBufferPoolAllocator>(allocator);
- ASSERT_TRUE((bool)mAllocator);
-
- status = mManager->create(mAllocator, &mConnectionId);
- ASSERT_TRUE(status == ResultStatus::OK);
-
- mConnectionValid = true;
-
- status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
- ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS &&
- mReceiverId == mConnectionId);
- }
-
- virtual void TearDown() override {
- if (mConnectionValid) {
- mManager->close(mConnectionId);
- }
- }
-
- protected:
- static void description(const std::string& description) {
- RecordProperty("description", description);
- }
-
- android::sp<ClientManager> mManager;
- std::shared_ptr<BufferPoolAllocator> mAllocator;
- bool mConnectionValid;
- ConnectionId mConnectionId;
- ConnectionId mReceiverId;
-
-};
-
-// Buffer allocation test.
-// Check whether each buffer allocation is done successfully with
-// unique buffer id.
-TEST_F(BufferpoolSingleTest, AllocateBuffer) {
- ResultStatus status;
- std::vector<uint8_t> vecParams;
- getVtsAllocatorParams(&vecParams);
-
- std::shared_ptr<BufferPoolData> buffer[kNumAllocationTest];
- native_handle_t *allocHandle = nullptr;
- for (int i = 0; i < kNumAllocationTest; ++i) {
- status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]);
- ASSERT_TRUE(status == ResultStatus::OK);
- }
- for (int i = 0; i < kNumAllocationTest; ++i) {
- for (int j = i + 1; j < kNumAllocationTest; ++j) {
- ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId);
- }
- }
- EXPECT_TRUE(kNumAllocationTest > 1);
-}
-
-// Buffer recycle test.
-// Check whether de-allocated buffers are recycled.
-TEST_F(BufferpoolSingleTest, RecycleBuffer) {
- ResultStatus status;
- std::vector<uint8_t> vecParams;
- getVtsAllocatorParams(&vecParams);
-
- BufferId bid[kNumRecycleTest];
- for (int i = 0; i < kNumRecycleTest; ++i) {
- std::shared_ptr<BufferPoolData> buffer;
- native_handle_t *allocHandle = nullptr;
- status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer);
- ASSERT_TRUE(status == ResultStatus::OK);
- bid[i] = buffer->mId;
- }
- for (int i = 1; i < kNumRecycleTest; ++i) {
- ASSERT_TRUE(bid[i - 1] == bid[i]);
- }
- EXPECT_TRUE(kNumRecycleTest > 1);
-}
-
-// Buffer transfer test.
-// Check whether buffer is transferred to another client successfully.
-TEST_F(BufferpoolSingleTest, TransferBuffer) {
- ResultStatus status;
- std::vector<uint8_t> vecParams;
- getVtsAllocatorParams(&vecParams);
- std::shared_ptr<BufferPoolData> sbuffer, rbuffer;
- native_handle_t *allocHandle = nullptr;
- native_handle_t *recvHandle = nullptr;
-
- TransactionId transactionId;
- int64_t postUs;
-
- status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer);
- ASSERT_TRUE(status == ResultStatus::OK);
- status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs);
- ASSERT_TRUE(status == ResultStatus::OK);
- status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs,
- &recvHandle, &rbuffer);
- EXPECT_TRUE(status == ResultStatus::OK);
-}
-
-} // anonymous namespace
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
diff --git a/codec2/vndk/include/C2BqBufferPriv.h b/codec2/vndk/include/C2BqBufferPriv.h
index 9271a71..0141f62 100644
--- a/codec2/vndk/include/C2BqBufferPriv.h
+++ b/codec2/vndk/include/C2BqBufferPriv.h
@@ -20,7 +20,7 @@
#include <functional>
#include <C2Buffer.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
class C2BufferQueueBlockPool : public C2BlockPool {
public:
diff --git a/codec2/vndk/platform/C2BqBuffer.cpp b/codec2/vndk/platform/C2BqBuffer.cpp
index cbacd97..90eba49 100644
--- a/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/codec2/vndk/platform/C2BqBuffer.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "C2BqBuffer"
#include <utils/Log.h>
-#include <gui/BufferQueueDefs.h>
+#include <ui/BufferQueueDefs.h>
#include <list>
#include <map>
#include <mutex>
@@ -269,36 +269,28 @@ private:
}
}
if (slotBuffer) {
- native_handle_t *grallocHandle = native_handle_clone(slotBuffer->handle);
-
- if (grallocHandle) {
- ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
- C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
- grallocHandle,
- slotBuffer->width,
- slotBuffer->height,
- slotBuffer->format,
- slotBuffer->usage,
- slotBuffer->stride,
- slotBuffer->getGenerationNumber(),
- mProducerId, slot);
- if (c2Handle) {
- // Moved everything to c2Handle.
- native_handle_delete(grallocHandle);
- std::shared_ptr<C2GraphicAllocation> alloc;
- c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
- if (err != C2_OK) {
- return err;
- }
- std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
- std::make_shared<C2BufferQueueBlockPoolData>(
- slotBuffer->getGenerationNumber(),
- mProducerId, slot, shared_from_this());
- *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
- return C2_OK;
+ ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
+ C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
+ slotBuffer->handle,
+ slotBuffer->width,
+ slotBuffer->height,
+ slotBuffer->format,
+ slotBuffer->usage,
+ slotBuffer->stride,
+ slotBuffer->getGenerationNumber(),
+ mProducerId, slot);
+ if (c2Handle) {
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
+ if (err != C2_OK) {
+ return err;
}
- native_handle_close(grallocHandle);
- native_handle_delete(grallocHandle);
+ std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::make_shared<C2BufferQueueBlockPoolData>(
+ slotBuffer->getGenerationNumber(),
+ mProducerId, slot, shared_from_this());
+ *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
+ return C2_OK;
}
// Block was not created. call requestBuffer# again next time.
slotBuffer.clear();
diff --git a/media/Android.bp b/media/Android.bp
deleted file mode 100644
index 573d3ce..0000000
--- a/media/Android.bp
+++ /dev/null
@@ -1,4 +0,0 @@
-subdirs = [
- "codecs",
- "sfplugin",
-]
diff --git a/media/codecs/Android.bp b/media/codecs/Android.bp
deleted file mode 100644
index e8176cf..0000000
--- a/media/codecs/Android.bp
+++ /dev/null
@@ -1,3 +0,0 @@
-subdirs = [
- "*",
-]
diff --git a/media/codecs/aac/C2SoftAacDec.cpp b/media/codecs/aac/C2SoftAacDec.cpp
index 02be0ea..c7c8442 100644
--- a/media/codecs/aac/C2SoftAacDec.cpp
+++ b/media/codecs/aac/C2SoftAacDec.cpp
@@ -239,10 +239,10 @@ C2SoftAacDec::C2SoftAacDec(
const std::shared_ptr<IntfImpl> &intfImpl)
: SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
mIntf(intfImpl),
- mAACDecoder(NULL),
- mStreamInfo(NULL),
+ mAACDecoder(nullptr),
+ mStreamInfo(nullptr),
mSignalledError(false),
- mOutputDelayRingBuffer(NULL) {
+ mOutputDelayRingBuffer(nullptr) {
}
C2SoftAacDec::~C2SoftAacDec() {
@@ -279,11 +279,11 @@ void C2SoftAacDec::onReset() {
void C2SoftAacDec::onRelease() {
if (mAACDecoder) {
aacDecoder_Close(mAACDecoder);
- mAACDecoder = NULL;
+ mAACDecoder = nullptr;
}
if (mOutputDelayRingBuffer) {
delete[] mOutputDelayRingBuffer;
- mOutputDelayRingBuffer = NULL;
+ mOutputDelayRingBuffer = nullptr;
}
}
@@ -291,9 +291,9 @@ status_t C2SoftAacDec::initDecoder() {
ALOGV("initDecoder()");
status_t status = UNKNOWN_ERROR;
mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1);
- if (mAACDecoder != NULL) {
+ if (mAACDecoder != nullptr) {
mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder);
- if (mStreamInfo != NULL) {
+ if (mStreamInfo != nullptr) {
status = OK;
}
}
@@ -305,7 +305,7 @@ status_t C2SoftAacDec::initDecoder() {
mOutputDelayRingBufferReadPos = 0;
mOutputDelayRingBufferFilled = 0;
- if (mAACDecoder == NULL) {
+ if (mAACDecoder == nullptr) {
ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
}
@@ -401,7 +401,7 @@ int32_t C2SoftAacDec::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t
&& (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
|| mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
// faster memcopy loop without checks, if the preconditions allow this
- if (samples != 0) {
+ if (samples != nullptr) {
for (int32_t i = 0; i < numSamples; i++) {
samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
}
@@ -415,7 +415,7 @@ int32_t C2SoftAacDec::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t
ALOGV("slow C2SoftAacDec::outputDelayRingBufferGetSamples()");
for (int32_t i = 0; i < numSamples; i++) {
- if (samples != 0) {
+ if (samples != nullptr) {
samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
}
mOutputDelayRingBufferReadPos++;
@@ -486,7 +486,6 @@ void C2SoftAacDec::drainRingBuffer(
numSamples * sizeof(int16_t), usage, &block);
if (err != C2_OK) {
ALOGD("failed to fetch a linear block (%d)", err);
- mSignalledError = true;
return std::bind(fillEmptyWork, _1, C2_NO_MEMORY);
}
C2WriteView wView = block->map().get();
@@ -524,9 +523,12 @@ void C2SoftAacDec::drainRingBuffer(
void C2SoftAacDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
- work->workletsProcessed = 0u;
+ // Initialize output work
work->result = C2_OK;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError) {
return;
}
@@ -567,13 +569,12 @@ void C2SoftAacDec::process(
if (decoderErr != AAC_DEC_OK) {
ALOGE("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
mSignalledError = true;
- // TODO: error
+ work->result = C2_CORRUPTED;
return;
}
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.ordinal = work->input.ordinal;
work->worklets.front()->output.buffers.clear();
- work->workletsProcessed = 1u;
return;
}
@@ -629,7 +630,7 @@ void C2SoftAacDec::process(
if (signalError) {
mSignalledError = true;
- // TODO: notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL);
+ work->result = C2_CORRUPTED;
return;
}
} else {
@@ -687,7 +688,7 @@ void C2SoftAacDec::process(
if (bytesValid[0] != 0) {
ALOGE("bytesValid[0] != 0 should never happen");
mSignalledError = true;
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ work->result = C2_CORRUPTED;
return;
}
@@ -698,7 +699,7 @@ void C2SoftAacDec::process(
if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
mStreamInfo->frameSize * mStreamInfo->numChannels)) {
mSignalledError = true;
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ work->result = C2_CORRUPTED;
return;
}
} else {
@@ -709,7 +710,7 @@ void C2SoftAacDec::process(
if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
mStreamInfo->frameSize * mStreamInfo->numChannels)) {
mSignalledError = true;
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ work->result = C2_CORRUPTED;
return;
}
@@ -766,8 +767,12 @@ void C2SoftAacDec::process(
C2FrameData &output = work->worklets.front()->output;
output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
+ } else {
+ ALOGE("Config Update failed");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
}
- // TODO: error handling
}
ALOGV("size = %zu", size);
} while (decoderErr == AAC_DEC_OK);
@@ -776,7 +781,7 @@ void C2SoftAacDec::process(
int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
mBuffersInfo.push_back(std::move(inInfo));
-
+ work->workletsProcessed = 0u;
if (!eos && mOutputDelayCompensated < outputDelay) {
// discard outputDelay at the beginning
int32_t toCompensate = outputDelay - mOutputDelayCompensated;
@@ -784,7 +789,7 @@ void C2SoftAacDec::process(
if (discard > toCompensate) {
discard = toCompensate;
}
- int32_t discarded = outputDelayRingBufferGetSamples(0, discard);
+ int32_t discarded = outputDelayRingBufferGetSamples(nullptr, discard);
mOutputDelayCompensated += discarded;
return;
}
@@ -849,7 +854,7 @@ c2_status_t C2SoftAacDec::onFlush_sm() {
if (avail > mStreamInfo->frameSize * mStreamInfo->numChannels) {
avail = mStreamInfo->frameSize * mStreamInfo->numChannels;
}
- int32_t ns = outputDelayRingBufferGetSamples(0, avail);
+ int32_t ns = outputDelayRingBufferGetSamples(nullptr, avail);
if (ns != avail) {
ALOGW("not a complete frame of samples available");
break;
diff --git a/media/codecs/aac/C2SoftAacEnc.cpp b/media/codecs/aac/C2SoftAacEnc.cpp
index 630b249..87730ae 100644
--- a/media/codecs/aac/C2SoftAacEnc.cpp
+++ b/media/codecs/aac/C2SoftAacEnc.cpp
@@ -144,14 +144,13 @@ C2SoftAacEnc::C2SoftAacEnc(
const std::shared_ptr<IntfImpl> &intfImpl)
: SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
mIntf(intfImpl),
- mAACEncoder(NULL),
+ mAACEncoder(nullptr),
mSBRMode(-1),
mSBRRatio(0),
mAACProfile(AOT_AAC_LC),
mNumBytesPerInputFrame(0u),
mOutBufferSize(0u),
mSentCodecSpecificData(false),
- mInputTimeSet(false),
mInputSize(0),
mInputTimeUs(-1ll),
mSignalledError(false),
@@ -177,7 +176,6 @@ status_t C2SoftAacEnc::initEncoder() {
c2_status_t C2SoftAacEnc::onStop() {
mSentCodecSpecificData = false;
- mInputTimeSet = false;
mInputSize = 0u;
mInputTimeUs = -1ll;
mSignalledError = false;
@@ -195,7 +193,6 @@ void C2SoftAacEnc::onRelease() {
c2_status_t C2SoftAacEnc::onFlush_sm() {
mSentCodecSpecificData = false;
- mInputTimeSet = false;
mInputSize = 0u;
return C2_OK;
}
@@ -285,8 +282,10 @@ status_t C2SoftAacEnc::setAudioParams() {
void C2SoftAacEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
if (mSignalledError) {
return;
@@ -300,10 +299,10 @@ void C2SoftAacEnc::process(
// The very first thing we want to output is the codec specific
// data.
- if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) {
+ if (AACENC_OK != aacEncEncode(mAACEncoder, nullptr, nullptr, nullptr, nullptr)) {
ALOGE("Unable to initialize encoder for profile / sample-rate / bit-rate / channels");
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
@@ -316,14 +315,19 @@ void C2SoftAacEnc::process(
AACENC_InfoStruct encInfo;
if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) {
ALOGE("Failed to get AAC encoder info");
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
std::unique_ptr<C2StreamCsdInfo::output> csd =
C2StreamCsdInfo::output::AllocUnique(encInfo.confSize, 0u);
- // TODO: check NO_MEMORY
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
memcpy(csd->m.value, encInfo.confBuf, encInfo.confSize);
ALOGV("put csd");
#if defined(LOG_NDEBUG) && !LOG_NDEBUG
@@ -333,6 +337,7 @@ void C2SoftAacEnc::process(
mOutBufferSize = encInfo.maxOutBufBytes;
mNumBytesPerInputFrame = encInfo.frameLength * channelCount * sizeof(int16_t);
+ mInputTimeUs = work->input.ordinal.timestamp;
mSentCodecSpecificData = true;
}
@@ -346,10 +351,6 @@ void C2SoftAacEnc::process(
data = view.data();
capacity = view.capacity();
}
- if (!mInputTimeSet && capacity > 0) {
- mInputTimeUs = work->input.ordinal.timestamp;
- mInputTimeSet = true;
- }
size_t numFrames = (capacity + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
/ mNumBytesPerInputFrame;
@@ -432,7 +433,9 @@ void C2SoftAacEnc::process(
// TODO: error handling, proper usage, etc.
c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
if (err != C2_OK) {
- ALOGE("err = %d", err);
+ ALOGE("fetchLinearBlock failed : err = %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
}
wView.reset(new C2WriteView(block->map().get()));
@@ -496,7 +499,9 @@ void C2SoftAacEnc::process(
// TODO: error handling, proper usage, etc.
c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
if (err != C2_OK) {
- ALOGE("err = %d", err);
+ ALOGE("fetchLinearBlock failed : err = %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
}
wView.reset(new C2WriteView(block->map().get()));
@@ -545,7 +550,6 @@ c2_status_t C2SoftAacEnc::drain(
(void)pool;
mSentCodecSpecificData = false;
- mInputTimeSet = false;
mInputSize = 0u;
// TODO: we don't have any pending work at this time to drain.
diff --git a/media/codecs/aac/C2SoftAacEnc.h b/media/codecs/aac/C2SoftAacEnc.h
index 779365b..82fb438 100644
--- a/media/codecs/aac/C2SoftAacEnc.h
+++ b/media/codecs/aac/C2SoftAacEnc.h
@@ -57,7 +57,6 @@ private:
UINT mOutBufferSize;
bool mSentCodecSpecificData;
- bool mInputTimeSet;
size_t mInputSize;
c2_cntr64_t mInputTimeUs;
diff --git a/media/codecs/amr_nb_wb/C2SoftAmrDec.cpp b/media/codecs/amr_nb_wb/C2SoftAmrDec.cpp
index 400149a..c591e21 100644
--- a/media/codecs/amr_nb_wb/C2SoftAmrDec.cpp
+++ b/media/codecs/amr_nb_wb/C2SoftAmrDec.cpp
@@ -245,8 +245,11 @@ static status_t calculateNumFrames(const uint8 *input, size_t inSize,
void C2SoftAmrDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -270,7 +273,6 @@ void C2SoftAmrDec::process(
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledOutputEos = true;
ALOGV("signalled EOS");
@@ -368,7 +370,6 @@ void C2SoftAmrDec::process(
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(createLinearBuffer(block));
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledOutputEos = true;
ALOGV("signalled EOS");
diff --git a/media/codecs/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codecs/amr_nb_wb/C2SoftAmrNbEnc.cpp
index b34c3a9..ca21480 100644
--- a/media/codecs/amr_nb_wb/C2SoftAmrNbEnc.cpp
+++ b/media/codecs/amr_nb_wb/C2SoftAmrNbEnc.cpp
@@ -188,8 +188,11 @@ static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
void C2SoftAmrNbEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
diff --git a/media/codecs/amr_nb_wb/C2SoftAmrWbEnc.cpp b/media/codecs/amr_nb_wb/C2SoftAmrWbEnc.cpp
index 1e5378c..be3892f 100644
--- a/media/codecs/amr_nb_wb/C2SoftAmrWbEnc.cpp
+++ b/media/codecs/amr_nb_wb/C2SoftAmrWbEnc.cpp
@@ -271,8 +271,11 @@ static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
void C2SoftAmrWbEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
diff --git a/media/codecs/avc/C2SoftAvcDec.cpp b/media/codecs/avc/C2SoftAvcDec.cpp
index 7f0b725..3e62744 100644
--- a/media/codecs/avc/C2SoftAvcDec.cpp
+++ b/media/codecs/avc/C2SoftAvcDec.cpp
@@ -328,7 +328,8 @@ C2SoftAvcDec::C2SoftAvcDec(
mOutBufferFlush(nullptr),
mIvColorFormat(IV_YUV_420P),
mWidth(320),
- mHeight(240) {
+ mHeight(240),
+ mHeaderDecoded(false) {
GENERATE_FILE_NAMES();
CREATE_DUMP_FILE(mInFile);
}
@@ -385,8 +386,10 @@ c2_status_t C2SoftAvcDec::onFlush_sm() {
}
}
- ivd_aligned_free(nullptr, mOutBufferFlush);
- mOutBufferFlush = nullptr;
+ if (mOutBufferFlush) {
+ ivd_aligned_free(nullptr, mOutBufferFlush);
+ mOutBufferFlush = nullptr;
+ }
return C2_OK;
}
@@ -438,7 +441,7 @@ status_t C2SoftAvcDec::setNumCores() {
return OK;
}
-status_t C2SoftAvcDec::setParams(size_t stride) {
+status_t C2SoftAvcDec::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) {
ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
ivd_ctl_set_config_op_t s_set_dyn_params_op;
@@ -448,7 +451,7 @@ status_t C2SoftAvcDec::setParams(size_t stride) {
s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
- s_set_dyn_params_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+ s_set_dyn_params_ip.e_vid_dec_mode = dec_mode;
s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
&s_set_dyn_params_ip,
@@ -491,7 +494,7 @@ status_t C2SoftAvcDec::initDecoder() {
mSignalledError = false;
resetPlugin();
(void) setNumCores();
- if (OK != setParams(mStride)) return UNKNOWN_ERROR;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return UNKNOWN_ERROR;
(void) getVersion();
return OK;
@@ -629,6 +632,7 @@ status_t C2SoftAvcDec::resetDecoder() {
mStride = 0;
(void) setNumCores();
mSignalledError = false;
+ mHeaderDecoded = false;
return OK;
}
@@ -682,14 +686,8 @@ void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &wor
buffer->setInfo(mIntf->getColorAspects_l());
}
- auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
- uint32_t flags = 0;
- if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
- (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
- flags |= C2FrameData::FLAG_END_OF_STREAM;
- ALOGV("signalling eos");
- }
- work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
@@ -709,7 +707,7 @@ c2_status_t C2SoftAvcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool>
}
if (mStride != ALIGN64(mWidth)) {
mStride = ALIGN64(mWidth);
- if (OK != setParams(mStride)) return C2_CORRUPTED;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
}
if (mOutBlock &&
(mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
@@ -739,8 +737,10 @@ c2_status_t C2SoftAvcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool>
void C2SoftAvcDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 0u;
+ work->worklets.front()->output.flags = work->input.flags;
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -769,6 +769,7 @@ void C2SoftAvcDec::process(
while (inPos < inSize) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -785,11 +786,18 @@ void C2SoftAvcDec::process(
if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
inOffset + inPos, inSize - inPos, workIndex)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
+
+ if (false == mHeaderDecoded) {
+ /* Decode header and get dimensions */
+ setParams(mStride, IVD_DECODE_HEADER);
+ }
+
WORD32 delay;
- GETTIME(&mTimeStart, NULL);
+ GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
WORD32 decodeTime;
@@ -800,22 +808,32 @@ void C2SoftAvcDec::process(
}
if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & 0xFF)) {
ALOGE("allocation failure in decoder");
- work->result = C2_CORRUPTED;
mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
return;
} else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & 0xFF)) {
ALOGE("unsupported resolution : %dx%d", mWidth, mHeight);
- work->result = C2_CORRUPTED;
mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
return;
} else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) {
ALOGV("resolution changed");
drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
resetDecoder();
resetPlugin();
- continue;
+ work->workletsProcessed = 0u;
+
+ /* Decode header and get new dimensions */
+ setParams(mStride, IVD_DECODE_HEADER);
+ (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
}
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+ if (mHeaderDecoded == false) {
+ mHeaderDecoded = true;
+ setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ }
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
mHeight = s_decode_op.u4_pic_ht;
@@ -823,8 +841,17 @@ void C2SoftAvcDec::process(
C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight);
std::vector<std::unique_ptr<C2SettingResult>> failures;
- (void)mIntf->config({&size}, C2_MAY_BLOCK, &failures);
- work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(size));
+ c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(size));
+ } else {
+ ALOGE("Cannot set width and height");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
continue;
}
}
@@ -833,6 +860,10 @@ void C2SoftAvcDec::process(
if (s_decode_op.u4_output_present) {
finishWork(s_decode_op.u4_ts, work);
}
+ if (0 == s_decode_op.u4_num_bytes_consumed) {
+ ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
+ break;
+ }
inPos += s_decode_op.u4_num_bytes_consumed;
if (hasPicture && (inSize - inPos)) {
ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
@@ -865,6 +896,7 @@ c2_status_t C2SoftAvcDec::drainInternal(
while (true) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return C2_CORRUPTED;
}
@@ -877,21 +909,18 @@ c2_status_t C2SoftAvcDec::drainInternal(
ivd_video_decode_op_t s_decode_op;
if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
return C2_CORRUPTED;
}
(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
if (s_decode_op.u4_output_present) {
finishWork(s_decode_op.u4_ts, work);
} else {
+ fillEmptyWork(work);
break;
}
}
- if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
- work && work->workletsProcessed == 0u) {
- fillEmptyWork(work);
- }
-
return C2_OK;
}
diff --git a/media/codecs/avc/C2SoftAvcDec.h b/media/codecs/avc/C2SoftAvcDec.h
index abf0fc6..2127a93 100644
--- a/media/codecs/avc/C2SoftAvcDec.h
+++ b/media/codecs/avc/C2SoftAvcDec.h
@@ -117,7 +117,7 @@ public:
private:
status_t createDecoder();
status_t setNumCores();
- status_t setParams(size_t stride);
+ status_t setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
void getVersion();
status_t initDecoder();
bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
@@ -162,7 +162,7 @@ private:
uint32_t mStride;
bool mSignalledOutputEos;
bool mSignalledError;
-
+ bool mHeaderDecoded;
// Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
// converting them to C2 values for each frame
struct VuiColorAspects {
diff --git a/media/codecs/avc/C2SoftAvcEnc.cpp b/media/codecs/avc/C2SoftAvcEnc.cpp
index c10de61..bfe745c 100644
--- a/media/codecs/avc/C2SoftAvcEnc.cpp
+++ b/media/codecs/avc/C2SoftAvcEnc.cpp
@@ -216,7 +216,7 @@ public:
};
uint64_t mbs = uint64_t((size.v.width + 15) / 16) * ((size.v.height + 15) / 16);
- float mbsPerSec = float(mbs) / frameRate.v.value;
+ float mbsPerSec = float(mbs) * frameRate.v.value;
// Check if the supplied level meets the MB / bitrate requirements. If
// not, update the level with the lowest level meeting the requirements.
@@ -374,7 +374,7 @@ C2SoftAvcEnc::C2SoftAvcEnc(
mSawInputEOS(false),
mSawOutputEOS(false),
mSignalledError(false),
- mCodecCtx(NULL),
+ mCodecCtx(nullptr),
// TODO: output buffer size
mOutBufferSize(524288) {
@@ -414,8 +414,8 @@ c2_status_t C2SoftAvcEnc::onFlush_sm() {
}
void C2SoftAvcEnc::initEncParams() {
- mCodecCtx = NULL;
- mMemRecords = NULL;
+ mCodecCtx = nullptr;
+ mMemRecords = nullptr;
mNumMemRecords = DEFAULT_MEM_REC_CNT;
mHeaderGenerated = 0;
mNumCores = GetCPUCoreCount();
@@ -436,8 +436,8 @@ void C2SoftAvcEnc::initEncParams() {
mEntropyMode = DEFAULT_ENTROPY_MODE;
mBframes = DEFAULT_B_FRAMES;
- gettimeofday(&mTimeStart, NULL);
- gettimeofday(&mTimeEnd, NULL);
+ gettimeofday(&mTimeStart, nullptr);
+ gettimeofday(&mTimeEnd, nullptr);
}
c2_status_t C2SoftAvcEnc::setDimensions() {
@@ -872,7 +872,7 @@ c2_status_t C2SoftAvcEnc::initEncoder() {
s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
- status = ive_api_function(0, &s_num_mem_rec_ip, &s_num_mem_rec_op);
+ status = ive_api_function(nullptr, &s_num_mem_rec_ip, &s_num_mem_rec_op);
if (status != IV_SUCCESS) {
ALOGE("Get number of memory records failed = 0x%x\n",
@@ -889,7 +889,7 @@ c2_status_t C2SoftAvcEnc::initEncoder() {
return C2_CORRUPTED;
}
mMemRecords = (iv_mem_rec_t *)malloc(mNumMemRecords * sizeof(iv_mem_rec_t));
- if (NULL == mMemRecords) {
+ if (nullptr == mMemRecords) {
ALOGE("Unable to allocate memory for hold memory records: Size %zu",
mNumMemRecords * sizeof(iv_mem_rec_t));
mSignalledError = true;
@@ -901,7 +901,7 @@ c2_status_t C2SoftAvcEnc::initEncoder() {
ps_mem_rec = mMemRecords;
for (size_t i = 0; i < mNumMemRecords; i++) {
ps_mem_rec->u4_size = sizeof(iv_mem_rec_t);
- ps_mem_rec->pv_base = NULL;
+ ps_mem_rec->pv_base = nullptr;
ps_mem_rec->u4_mem_size = 0;
ps_mem_rec->u4_mem_alignment = 0;
ps_mem_rec->e_mem_type = IV_NA_MEM_TYPE;
@@ -930,7 +930,7 @@ c2_status_t C2SoftAvcEnc::initEncoder() {
s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
- status = ive_api_function(0, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
+ status = ive_api_function(nullptr, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
if (status != IV_SUCCESS) {
ALOGE("Fill memory records failed = 0x%x\n",
@@ -949,7 +949,7 @@ c2_status_t C2SoftAvcEnc::initEncoder() {
for (size_t i = 0; i < mNumMemRecords; i++) {
ps_mem_rec->pv_base = ive_aligned_malloc(
ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
- if (ps_mem_rec->pv_base == NULL) {
+ if (ps_mem_rec->pv_base == nullptr) {
ALOGE("Allocation failure for mem record id %zu size %u\n", i,
ps_mem_rec->u4_mem_size);
return C2_CORRUPTED;
@@ -1085,14 +1085,18 @@ c2_status_t C2SoftAvcEnc::releaseEncoder() {
/* Free memory records */
ps_mem_rec = mMemRecords;
for (size_t i = 0; i < s_retrieve_mem_op.u4_num_mem_rec_filled; i++) {
- ive_aligned_free(ps_mem_rec->pv_base);
+ if (ps_mem_rec) ive_aligned_free(ps_mem_rec->pv_base);
+ else {
+ ALOGE("memory record is null.");
+ return C2_CORRUPTED;
+ }
ps_mem_rec++;
}
- free(mMemRecords);
+ if (mMemRecords) free(mMemRecords);
// clear other pointers into the space being free()d
- mCodecCtx = NULL;
+ mCodecCtx = nullptr;
mStarted = false;
@@ -1116,15 +1120,15 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs(
ps_encode_op->u4_size = sizeof(ive_video_encode_op_t);
ps_encode_ip->e_cmd = IVE_CMD_VIDEO_ENCODE;
- ps_encode_ip->pv_bufs = NULL;
- ps_encode_ip->pv_mb_info = NULL;
- ps_encode_ip->pv_pic_info = NULL;
+ ps_encode_ip->pv_bufs = nullptr;
+ ps_encode_ip->pv_mb_info = nullptr;
+ ps_encode_ip->pv_pic_info = nullptr;
ps_encode_ip->u4_mb_info_type = 0;
ps_encode_ip->u4_pic_info_type = 0;
ps_encode_ip->u4_is_last = 0;
ps_encode_ip->u4_timestamp_high = timestamp >> 32;
ps_encode_ip->u4_timestamp_low = timestamp & 0xFFFFFFFF;
- ps_encode_op->s_out_buf.pv_buf = NULL;
+ ps_encode_op->s_out_buf.pv_buf = nullptr;
/* Initialize color formats */
memset(ps_inp_raw_buf, 0, sizeof(iv_raw_buf_t));
@@ -1273,18 +1277,20 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs(
void C2SoftAvcEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
IV_STATUS_T status;
WORD32 timeDelay, timeTaken;
uint64_t timestamp = work->input.ordinal.timestamp.peekull();
// Initialize encoder if not already initialized
- if (mCodecCtx == NULL) {
+ if (mCodecCtx == nullptr) {
if (C2_OK != initEncoder()) {
ALOGE("Failed to initialize encoder");
- work->workletsProcessed = 1u;
+ mSignalledError = true;
work->result = C2_CORRUPTED;
return;
}
@@ -1302,12 +1308,11 @@ void C2SoftAvcEnc::process(
constexpr uint32_t kHeaderLength = MIN_STREAM_SIZE;
uint8_t header[kHeaderLength];
error = setEncodeArgs(
- &s_encode_ip, &s_encode_op, NULL, header, kHeaderLength, timestamp);
+ &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, timestamp);
if (error != C2_OK) {
ALOGE("setEncodeArgs failed: %d", error);
mSignalledError = true;
- work->workletsProcessed = 1u;
- work->result = error;
+ work->result = C2_CORRUPTED;
return;
}
status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
@@ -1325,6 +1330,12 @@ void C2SoftAvcEnc::process(
std::unique_ptr<C2StreamCsdInfo::output> csd =
C2StreamCsdInfo::output::AllocUnique(s_encode_op.s_out_buf.u4_bytes, 0u);
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes);
work->worklets.front()->output.configUpdate.push_back(std::move(csd));
@@ -1398,14 +1409,12 @@ void C2SoftAvcEnc::process(
c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
if (err != C2_OK) {
ALOGE("fetch linear block err = %d", err);
- work->workletsProcessed = 1u;
work->result = err;
return;
}
C2WriteView wView = block->map().get();
if (wView.error() != C2_OK) {
ALOGE("write view map err = %d", wView.error());
- work->workletsProcessed = 1u;
work->result = wView.error();
return;
}
@@ -1413,9 +1422,8 @@ void C2SoftAvcEnc::process(
error = setEncodeArgs(
&s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), timestamp);
if (error != C2_OK) {
- mSignalledError = true;
ALOGE("setEncodeArgs failed : %d", error);
- work->workletsProcessed = 1u;
+ mSignalledError = true;
work->result = error;
return;
}
@@ -1424,7 +1432,7 @@ void C2SoftAvcEnc::process(
// mInFile, s_encode_ip.s_inp_buf.apv_bufs[0],
// (mHeight * mStride * 3 / 2));
- GETTIME(&mTimeStart, NULL);
+ GETTIME(&mTimeStart, nullptr);
/* Compute time elapsed between end of previous decode()
* to start of current decode() */
TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
@@ -1439,7 +1447,6 @@ void C2SoftAvcEnc::process(
ALOGE("Encode Frame failed = 0x%x\n",
s_encode_op.u4_error_code);
mSignalledError = true;
- work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -1450,7 +1457,7 @@ void C2SoftAvcEnc::process(
mBuffers[s_encode_ip.s_inp_buf.apv_bufs[0]] = inputBuffer;
}
- GETTIME(&mTimeEnd, NULL);
+ GETTIME(&mTimeEnd, nullptr);
/* Compute time taken for decode() */
TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
@@ -1459,7 +1466,7 @@ void C2SoftAvcEnc::process(
void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
/* If encoder frees up an input buffer, mark it as free */
- if (freed != NULL) {
+ if (freed != nullptr) {
if (mBuffers.count(freed) == 0u) {
ALOGD("buffer not tracked");
} else {
@@ -1485,7 +1492,6 @@ void C2SoftAvcEnc::process(
}
work->worklets.front()->output.buffers.push_back(buffer);
}
- work->workletsProcessed = 1u;
if (s_encode_op.u4_is_last) {
// outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
diff --git a/media/codecs/base/Android.bp b/media/codecs/base/Android.bp
index 2aa84f6..473cb4d 100644
--- a/media/codecs/base/Android.bp
+++ b/media/codecs/base/Android.bp
@@ -74,9 +74,6 @@ cc_defaults {
"signed-integer-overflow",
],
cfi: true,
- diag: {
- cfi: true,
- },
},
}
@@ -90,9 +87,6 @@ cc_defaults {
"signed-integer-overflow",
],
cfi: true,
- diag: {
- cfi: true,
- },
},
}
@@ -128,9 +122,6 @@ cc_library_shared {
"signed-integer-overflow",
],
cfi: true,
- diag: {
- cfi: true,
- },
},
ldflags: ["-Wl,-Bsymbolic"],
diff --git a/media/codecs/base/SimpleC2Component.cpp b/media/codecs/base/SimpleC2Component.cpp
index 6129543..50b4d20 100644
--- a/media/codecs/base/SimpleC2Component.cpp
+++ b/media/codecs/base/SimpleC2Component.cpp
@@ -132,56 +132,6 @@ void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg)
}
}
-class SimpleC2Component::BlockingBlockPool : public C2BlockPool {
-public:
- BlockingBlockPool(const std::shared_ptr<C2BlockPool>& base): mBase{base} {}
-
- virtual local_id_t getLocalId() const override {
- return mBase->getLocalId();
- }
-
- virtual C2Allocator::id_t getAllocatorId() const override {
- return mBase->getAllocatorId();
- }
-
- virtual c2_status_t fetchLinearBlock(
- uint32_t capacity,
- C2MemoryUsage usage,
- std::shared_ptr<C2LinearBlock>* block) {
- c2_status_t status;
- do {
- status = mBase->fetchLinearBlock(capacity, usage, block);
- } while (status == C2_TIMED_OUT);
- return status;
- }
-
- virtual c2_status_t fetchCircularBlock(
- uint32_t capacity,
- C2MemoryUsage usage,
- std::shared_ptr<C2CircularBlock>* block) {
- c2_status_t status;
- do {
- status = mBase->fetchCircularBlock(capacity, usage, block);
- } while (status == C2_TIMED_OUT);
- return status;
- }
-
- virtual c2_status_t fetchGraphicBlock(
- uint32_t width, uint32_t height, uint32_t format,
- C2MemoryUsage usage,
- std::shared_ptr<C2GraphicBlock>* block) {
- c2_status_t status;
- do {
- status = mBase->fetchGraphicBlock(width, height, format, usage,
- block);
- } while (status == C2_TIMED_OUT);
- return status;
- }
-
-private:
- std::shared_ptr<C2BlockPool> mBase;
-};
-
////////////////////////////////////////////////////////////////////////////////
namespace {
@@ -496,16 +446,12 @@ bool SimpleC2Component::processQueue() {
}
}
- std::shared_ptr<C2BlockPool> blockPool;
- err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
+ err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
ALOGD("Using output block pool with poolID %llu => got %llu - %d",
(unsigned long long)poolId,
(unsigned long long)(
- blockPool ? blockPool->getLocalId() : 111000111),
+ mOutputBlockPool ? mOutputBlockPool->getLocalId() : 111000111),
err);
- if (err == C2_OK) {
- mOutputBlockPool = std::make_shared<BlockingBlockPool>(blockPool);
- }
return err;
}();
if (err != C2_OK) {
@@ -543,6 +489,13 @@ bool SimpleC2Component::processQueue() {
}
ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
+ // If input buffer list is not empty, it means we have some input to process on.
+ // However, input could be a null buffer. In such case, clear the buffer list
+ // before making call to process().
+ if (!work->input.buffers.empty() && !work->input.buffers[0]) {
+ ALOGD("Encountered null input buffer. Clearing the input buffer");
+ work->input.buffers.clear();
+ }
process(work, mOutputBlockPool);
ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
{
diff --git a/media/codecs/base/include/SimpleC2Component.h b/media/codecs/base/include/SimpleC2Component.h
index 43029a9..b3a98f4 100644
--- a/media/codecs/base/include/SimpleC2Component.h
+++ b/media/codecs/base/include/SimpleC2Component.h
@@ -234,8 +234,7 @@ private:
typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
Mutexed<PendingWork> mPendingWork;
- class BlockingBlockPool;
- std::shared_ptr<BlockingBlockPool> mOutputBlockPool;
+ std::shared_ptr<C2BlockPool> mOutputBlockPool;
SimpleC2Component() = delete;
};
diff --git a/media/codecs/cmds/Android.bp b/media/codecs/cmds/Android.bp
index 4019fbe..6a4ca8c 100644
--- a/media/codecs/cmds/Android.bp
+++ b/media/codecs/cmds/Android.bp
@@ -15,7 +15,6 @@ cc_binary {
"libcutils",
"libgui",
"liblog",
- "libmediaextractor",
"libstagefright",
"libstagefright_foundation",
"libui",
@@ -25,7 +24,6 @@ cc_binary {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
sanitize: {
diff --git a/media/codecs/cmds/codec2.cpp b/media/codecs/cmds/codec2.cpp
index 4d0fdd4..f2cf545 100644
--- a/media/codecs/cmds/codec2.cpp
+++ b/media/codecs/cmds/codec2.cpp
@@ -33,7 +33,6 @@
#include <media/DataSource.h>
#include <media/ICrypto.h>
#include <media/IMediaHTTPService.h>
-#include <media/MediaExtractor.h>
#include <media/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -155,7 +154,7 @@ SimplePlayer::SimplePlayer()
HAL_PIXEL_FORMAT_YV12);
//PIXEL_FORMAT_RGB_565);
- CHECK(mControl != NULL);
+ CHECK(mControl != nullptr);
CHECK(mControl->isValid());
SurfaceComposerClient::Transaction{}
@@ -164,7 +163,7 @@ SimplePlayer::SimplePlayer()
.apply();
mSurface = mControl->getSurface();
- CHECK(mSurface != NULL);
+ CHECK(mSurface != nullptr);
mSurface->connect(NATIVE_WINDOW_API_CPU, mProducerListener);
}
@@ -305,7 +304,7 @@ void SimplePlayer::play(const sp<IMediaSource> &source) {
} else {
status_t err = source->read(&buffer);
if (err != OK) {
- CHECK(buffer == NULL);
+ CHECK(buffer == nullptr);
if (err == INFO_FORMAT_CHANGED) {
continue;
@@ -367,7 +366,7 @@ void SimplePlayer::play(const sp<IMediaSource> &source) {
if (buffer) {
buffer->release();
- buffer = NULL;
+ buffer = nullptr;
}
++numFrames;
@@ -419,9 +418,9 @@ int main(int argc, char **argv) {
const char *filename = argv[k];
sp<DataSource> dataSource =
- DataSourceFactory::CreateFromURI(NULL /* httpService */, filename);
+ DataSourceFactory::CreateFromURI(nullptr /* httpService */, filename);
- if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) {
+ if (strncasecmp(filename, "sine:", 5) && dataSource == nullptr) {
fprintf(stderr, "Unable to create data source.\n");
return 1;
}
@@ -431,14 +430,14 @@ int main(int argc, char **argv) {
sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource);
- if (extractor == NULL) {
+ if (extractor == nullptr) {
fprintf(stderr, "could not create extractor.\n");
return -1;
}
sp<MetaData> meta = extractor->getMetaData();
- if (meta != NULL) {
+ if (meta != nullptr) {
const char *mime;
if (!meta->findCString(kKeyMIMEType, &mime)) {
fprintf(stderr, "extractor did not provide MIME type.\n");
@@ -450,10 +449,9 @@ int main(int argc, char **argv) {
size_t i;
for (i = 0; i < numTracks; ++i) {
- meta = extractor->getTrackMetaData(
- i, MediaExtractor::kIncludeExtensiveMetaData);
+ meta = extractor->getTrackMetaData(i, 0);
- if (meta == NULL) {
+ if (meta == nullptr) {
break;
}
const char *mime;
@@ -464,10 +462,10 @@ int main(int argc, char **argv) {
break;
}
- meta = NULL;
+ meta = nullptr;
}
- if (meta == NULL) {
+ if (meta == nullptr) {
fprintf(stderr, "No AVC track found.\n");
return -1;
}
diff --git a/media/codecs/flac/C2SoftFlacDec.cpp b/media/codecs/flac/C2SoftFlacDec.cpp
index 1358d66..f1e2f51 100644
--- a/media/codecs/flac/C2SoftFlacDec.cpp
+++ b/media/codecs/flac/C2SoftFlacDec.cpp
@@ -169,9 +169,12 @@ static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
void C2SoftFlacDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -297,7 +300,6 @@ void C2SoftFlacDec::process(
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledOutputEos = true;
ALOGV("signalled EOS");
diff --git a/media/codecs/flac/C2SoftFlacEnc.cpp b/media/codecs/flac/C2SoftFlacEnc.cpp
index 13c888e..e4192c7 100644
--- a/media/codecs/flac/C2SoftFlacEnc.cpp
+++ b/media/codecs/flac/C2SoftFlacEnc.cpp
@@ -176,8 +176,11 @@ static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
void C2SoftFlacEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -208,7 +211,12 @@ void C2SoftFlacEnc::process(
if (!mWroteHeader) {
std::unique_ptr<C2StreamCsdInfo::output> csd =
C2StreamCsdInfo::output::AllocUnique(mHeaderOffset, 0u);
- // TODO: check NO_MEMORY
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
memcpy(csd->m.value, mHeader, mHeaderOffset);
ALOGV("put csd, %d bytes", mHeaderOffset);
diff --git a/media/codecs/g711/C2SoftG711Dec.cpp b/media/codecs/g711/C2SoftG711Dec.cpp
index 2f84b46..1c71d45 100644
--- a/media/codecs/g711/C2SoftG711Dec.cpp
+++ b/media/codecs/g711/C2SoftG711Dec.cpp
@@ -140,8 +140,11 @@ c2_status_t C2SoftG711Dec::onFlush_sm() {
void C2SoftG711Dec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -169,7 +172,6 @@ void C2SoftG711Dec::process(
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledOutputEos = true;
ALOGV("signalled EOS");
@@ -205,7 +207,6 @@ void C2SoftG711Dec::process(
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(createLinearBuffer(block));
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledOutputEos = true;
diff --git a/media/codecs/gsm/C2SoftGsmDec.cpp b/media/codecs/gsm/C2SoftGsmDec.cpp
index eedaa14..7101c79 100644
--- a/media/codecs/gsm/C2SoftGsmDec.cpp
+++ b/media/codecs/gsm/C2SoftGsmDec.cpp
@@ -174,8 +174,11 @@ static size_t decodeGSM(gsm handle, int16_t *out, size_t outCapacity,
void C2SoftGsmDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledEos) {
work->result = C2_BAD_VALUE;
return;
@@ -199,7 +202,6 @@ void C2SoftGsmDec::process(
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledEos = true;
ALOGV("signalled EOS");
@@ -239,7 +241,6 @@ void C2SoftGsmDec::process(
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledEos = true;
ALOGV("signalled EOS");
diff --git a/media/codecs/hevc/C2SoftHevcDec.cpp b/media/codecs/hevc/C2SoftHevcDec.cpp
index f71a7c5..99892ce 100644
--- a/media/codecs/hevc/C2SoftHevcDec.cpp
+++ b/media/codecs/hevc/C2SoftHevcDec.cpp
@@ -324,7 +324,8 @@ C2SoftHevcDec::C2SoftHevcDec(
mOutBufferFlush(nullptr),
mIvColorformat(IV_YUV_420P),
mWidth(320),
- mHeight(240) {
+ mHeight(240),
+ mHeaderDecoded(false) {
}
C2SoftHevcDec::~C2SoftHevcDec() {
@@ -381,8 +382,10 @@ c2_status_t C2SoftHevcDec::onFlush_sm() {
}
}
- ivd_aligned_free(nullptr, mOutBufferFlush);
- mOutBufferFlush = nullptr;
+ if (mOutBufferFlush) {
+ ivd_aligned_free(nullptr, mOutBufferFlush);
+ mOutBufferFlush = nullptr;
+ }
return C2_OK;
}
@@ -434,7 +437,7 @@ status_t C2SoftHevcDec::setNumCores() {
return OK;
}
-status_t C2SoftHevcDec::setParams(size_t stride) {
+status_t C2SoftHevcDec::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) {
ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
ivd_ctl_set_config_op_t s_set_dyn_params_op;
@@ -444,7 +447,7 @@ status_t C2SoftHevcDec::setParams(size_t stride) {
s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
- s_set_dyn_params_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+ s_set_dyn_params_ip.e_vid_dec_mode = dec_mode;
s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
&s_set_dyn_params_ip,
@@ -489,7 +492,7 @@ status_t C2SoftHevcDec::initDecoder() {
mSignalledError = false;
resetPlugin();
(void) setNumCores();
- if (OK != setParams(mStride)) return UNKNOWN_ERROR;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return UNKNOWN_ERROR;
(void) getVersion();
return OK;
@@ -628,7 +631,7 @@ status_t C2SoftHevcDec::resetDecoder() {
mStride = 0;
(void) setNumCores();
mSignalledError = false;
-
+ mHeaderDecoded = false;
return OK;
}
@@ -681,14 +684,8 @@ void C2SoftHevcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &wo
buffer->setInfo(mIntf->getColorAspects_l());
}
- auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
- uint32_t flags = 0;
- if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
- (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
- flags |= C2FrameData::FLAG_END_OF_STREAM;
- ALOGV("signalling eos");
- }
- work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
@@ -708,7 +705,7 @@ c2_status_t C2SoftHevcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool>
}
if (mStride != ALIGN64(mWidth)) {
mStride = ALIGN64(mWidth);
- if (OK != setParams(mStride)) return C2_CORRUPTED;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
}
if (mOutBlock &&
(mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
@@ -738,9 +735,12 @@ c2_status_t C2SoftHevcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool>
void C2SoftHevcDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 0u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -769,6 +769,7 @@ void C2SoftHevcDec::process(
while (inPos < inSize) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -783,11 +784,17 @@ void C2SoftHevcDec::process(
if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
inOffset + inPos, inSize - inPos, workIndex)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
+
+ if (false == mHeaderDecoded) {
+ /* Decode header and get dimensions */
+ setParams(mStride, IVD_DECODE_HEADER);
+ }
WORD32 delay;
- GETTIME(&mTimeStart, NULL);
+ GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
WORD32 decodeTime;
@@ -797,22 +804,32 @@ void C2SoftHevcDec::process(
s_decode_op.u4_num_bytes_consumed);
if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & 0xFF)) {
ALOGE("allocation failure in decoder");
- work->result = C2_CORRUPTED;
mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
return;
} else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & 0xFF)) {
ALOGE("unsupported resolution : %dx%d", mWidth, mHeight);
- work->result = C2_CORRUPTED;
mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
return;
} else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) {
ALOGV("resolution changed");
drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
resetDecoder();
resetPlugin();
- continue;
+ work->workletsProcessed = 0u;
+
+ /* Decode header and get new dimensions */
+ setParams(mStride, IVD_DECODE_HEADER);
+ (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
}
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+ if (mHeaderDecoded == false) {
+ mHeaderDecoded = true;
+ setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ }
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
mHeight = s_decode_op.u4_pic_ht;
@@ -828,9 +845,11 @@ void C2SoftHevcDec::process(
} else {
ALOGE("Cannot set width and height");
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
+ continue;
}
}
(void) getVuiParams();
@@ -838,6 +857,10 @@ void C2SoftHevcDec::process(
if (s_decode_op.u4_output_present) {
finishWork(s_decode_op.u4_ts, work);
}
+ if (0 == s_decode_op.u4_num_bytes_consumed) {
+ ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
+ break;
+ }
inPos += s_decode_op.u4_num_bytes_consumed;
if (hasPicture && (inSize - inPos)) {
ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
@@ -871,6 +894,7 @@ c2_status_t C2SoftHevcDec::drainInternal(
while (true) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return C2_CORRUPTED;
}
@@ -883,21 +907,18 @@ c2_status_t C2SoftHevcDec::drainInternal(
ivd_video_decode_op_t s_decode_op;
if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
return C2_CORRUPTED;
}
(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
if (s_decode_op.u4_output_present) {
finishWork(s_decode_op.u4_ts, work);
} else {
+ fillEmptyWork(work);
break;
}
}
- if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
- work && work->workletsProcessed == 0u) {
- fillEmptyWork(work);
- }
-
return C2_OK;
}
diff --git a/media/codecs/hevc/C2SoftHevcDec.h b/media/codecs/hevc/C2SoftHevcDec.h
index 2b3154d..75111fc 100644
--- a/media/codecs/hevc/C2SoftHevcDec.h
+++ b/media/codecs/hevc/C2SoftHevcDec.h
@@ -69,7 +69,7 @@ struct C2SoftHevcDec : public SimpleC2Component {
private:
status_t createDecoder();
status_t setNumCores();
- status_t setParams(size_t stride);
+ status_t setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
status_t getVersion();
status_t initDecoder();
bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
@@ -120,6 +120,7 @@ struct C2SoftHevcDec : public SimpleC2Component {
uint32_t mStride;
bool mSignalledOutputEos;
bool mSignalledError;
+ bool mHeaderDecoded;
// Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
// converting them to C2 values for each frame
diff --git a/media/codecs/mp3/C2SoftMp3Dec.cpp b/media/codecs/mp3/C2SoftMp3Dec.cpp
index 48de625..c8b8397 100644
--- a/media/codecs/mp3/C2SoftMp3Dec.cpp
+++ b/media/codecs/mp3/C2SoftMp3Dec.cpp
@@ -336,9 +336,12 @@ c2_status_t C2SoftMP3::drain(
void C2SoftMP3::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -361,7 +364,6 @@ void C2SoftMP3::process(
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
return;
}
ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
@@ -494,7 +496,6 @@ void C2SoftMP3::process(
mProcessedSamples += ((outSize - outOffset) / (numChannels * sizeof(int16_t)));
ALOGV("out buffer attr. offset %d size %d timestamp %u", outOffset, outSize - outOffset,
(uint32_t)(mAnchorTimeStamp + outTimeStamp));
-
decodedSizes.clear();
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.buffers.clear();
@@ -502,7 +503,6 @@ void C2SoftMP3::process(
createLinearBuffer(block, outOffset, outSize - outOffset));
work->worklets.front()->output.ordinal = work->input.ordinal;
work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
- work->workletsProcessed = 1u;
if (eos) {
mSignalledOutputEos = true;
ALOGV("signalled EOS");
diff --git a/media/codecs/mpeg2/C2SoftMpeg2Dec.cpp b/media/codecs/mpeg2/C2SoftMpeg2Dec.cpp
index 855598d..da32ec0 100644
--- a/media/codecs/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codecs/mpeg2/C2SoftMpeg2Dec.cpp
@@ -379,8 +379,10 @@ c2_status_t C2SoftMpeg2Dec::onFlush_sm() {
}
}
- ivd_aligned_free(mOutBufferDrain);
- mOutBufferDrain = nullptr;
+ if (mOutBufferDrain) {
+ ivd_aligned_free(mOutBufferDrain);
+ mOutBufferDrain = nullptr;
+ }
return C2_OK;
}
@@ -764,14 +766,8 @@ void C2SoftMpeg2Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &w
buffer->setInfo(mIntf->getColorAspects_l());
}
- auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
- uint32_t flags = 0;
- if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
- (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
- flags |= C2FrameData::FLAG_END_OF_STREAM;
- ALOGV("signalling eos");
- }
- work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
@@ -812,29 +808,6 @@ c2_status_t C2SoftMpeg2Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool
return C2_OK;
}
-void C2SoftMpeg2Dec::setTimeStampFrameIndexMap(uint64_t frameIndex, uint64_t timeStamp) {
- mFrameIndices[timeStamp] = frameIndex;
- return;
-}
-
-uint32_t C2SoftMpeg2Dec::getMinTimeStampFrameIndex() {
- uint64_t frameIndex = 0;
- uint64_t minTimeStamp = 0;
- std::map<uint64_t , uint64_t >::iterator it = mFrameIndices.begin();
-
- minTimeStamp = it->first;
- while(it != mFrameIndices.end()) {
- if(minTimeStamp > it->first) minTimeStamp = it->first;
- it++;
- }
- if (mFrameIndices.count(minTimeStamp) == 0u) {
- ALOGV("timestamp %d not tracked", (int)minTimeStamp);
- } else {
- frameIndex = mFrameIndices[minTimeStamp];
- mFrameIndices.erase(minTimeStamp);
- }
- return frameIndex & 0xFFFFFFFF;
-}
// TODO: can overall error checking be improved?
// TODO: allow configuration of color format and usage for graphic buffers instead
// of hard coding them to HAL_PIXEL_FORMAT_YV12
@@ -844,9 +817,12 @@ uint32_t C2SoftMpeg2Dec::getMinTimeStampFrameIndex() {
void C2SoftMpeg2Dec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 0u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -864,8 +840,6 @@ void C2SoftMpeg2Dec::process(
work->result = C2_CORRUPTED;
return;
}
- setTimeStampFrameIndexMap((uint64_t)work->input.ordinal.frameIndex.peeku(),
- (uint64_t)work->input.ordinal.timestamp.peeku());
}
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
bool hasPicture = false;
@@ -877,6 +851,7 @@ void C2SoftMpeg2Dec::process(
while (inPos < inSize) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -892,13 +867,14 @@ void C2SoftMpeg2Dec::process(
if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
inOffset + inPos, inSize - inPos, workIndex)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
// If input dump is enabled, then write to file
DUMP_TO_FILE(mInFile, s_decode_ip.pv_stream_buffer, s_decode_ip.u4_num_Bytes);
WORD32 delay;
- GETTIME(&mTimeStart, NULL);
+ GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
WORD32 decodeTime;
@@ -910,6 +886,7 @@ void C2SoftMpeg2Dec::process(
ALOGV("unsupported resolution : %dx%d", s_decode_op.u4_pic_wd, s_decode_op.u4_pic_ht);
drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
resetPlugin();
+ work->workletsProcessed = 0u;
mWidth = s_decode_op.u4_pic_wd;
mHeight = s_decode_op.u4_pic_ht;
@@ -925,6 +902,7 @@ void C2SoftMpeg2Dec::process(
} else {
ALOGE("Cannot set width and height");
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -932,6 +910,7 @@ void C2SoftMpeg2Dec::process(
if (OK != reInitDecoder()) {
ALOGE("Failed to reinitialize decoder");
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -941,6 +920,7 @@ void C2SoftMpeg2Dec::process(
drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
resetDecoder();
resetPlugin();
+ work->workletsProcessed = 0u;
continue;
}
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
@@ -961,6 +941,7 @@ void C2SoftMpeg2Dec::process(
} else {
ALOGE("Cannot set width and height");
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
@@ -970,8 +951,9 @@ void C2SoftMpeg2Dec::process(
(void) getSeqInfo();
hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
if (s_decode_op.u4_output_present) {
- finishWork(getMinTimeStampFrameIndex(), work);
+ finishWork(s_decode_op.u4_ts, work);
}
+
inPos += s_decode_op.u4_num_bytes_consumed;
if (hasPicture && (inSize - inPos) != 0) {
ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
@@ -1005,6 +987,7 @@ c2_status_t C2SoftMpeg2Dec::drainInternal(
while (true) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return C2_CORRUPTED;
}
@@ -1017,19 +1000,17 @@ c2_status_t C2SoftMpeg2Dec::drainInternal(
ivd_video_decode_op_t s_decode_op;
if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
mSignalledError = true;
+ work->workletsProcessed = 1u;
return C2_CORRUPTED;
}
(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
if (s_decode_op.u4_output_present) {
- finishWork(getMinTimeStampFrameIndex(), work);
+ finishWork(s_decode_op.u4_ts, work);
} else {
+ fillEmptyWork(work);
break;
}
}
- if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
- work && work->workletsProcessed == 0u) {
- fillEmptyWork(work);
- }
return C2_OK;
}
diff --git a/media/codecs/mpeg2/C2SoftMpeg2Dec.h b/media/codecs/mpeg2/C2SoftMpeg2Dec.h
index 2ea3d85..9999872 100644
--- a/media/codecs/mpeg2/C2SoftMpeg2Dec.h
+++ b/media/codecs/mpeg2/C2SoftMpeg2Dec.h
@@ -136,8 +136,7 @@ struct C2SoftMpeg2Dec : public SimpleC2Component {
void resetPlugin();
status_t deleteDecoder();
status_t reInitDecoder();
- uint32_t getMinTimeStampFrameIndex();
- void setTimeStampFrameIndexMap(uint64_t frameIndex, uint64_t timeStamp);
+
// TODO:This is not the right place for this enum. These should
// be part of c2-vndk so that they can be accessed by all video plugins
// until then, make them feel at home
@@ -181,8 +180,6 @@ struct C2SoftMpeg2Dec : public SimpleC2Component {
}
} mBitstreamColorAspects;
- std::map<uint64_t , uint64_t > mFrameIndices;
-
// profile
struct timeval mTimeStart;
struct timeval mTimeEnd;
diff --git a/media/codecs/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codecs/mpeg4_h263/C2SoftMpeg4Dec.cpp
index 793a515..901f5ed 100644
--- a/media/codecs/mpeg4_h263/C2SoftMpeg4Dec.cpp
+++ b/media/codecs/mpeg4_h263/C2SoftMpeg4Dec.cpp
@@ -324,6 +324,10 @@ status_t C2SoftMpeg4Dec::initDecoder() {
if (!mDecHandle) {
mDecHandle = new tagvideoDecControls;
}
+ if (!mDecHandle) {
+ ALOGE("mDecHandle is null");
+ return NO_MEMORY;
+ }
memset(mDecHandle, 0, sizeof(tagvideoDecControls));
/* TODO: bring these values to 352 and 288. It cannot be done as of now
@@ -446,8 +450,8 @@ bool C2SoftMpeg4Dec::handleResChange(const std::unique_ptr<C2Work> &work) {
if (!PVInitVideoDecoder(
mDecHandle, vol_data, &vol_size, 1, mIntf->getMaxWidth(), mIntf->getMaxHeight(), H263_MODE)) {
ALOGE("Error in PVInitVideoDecoder H263_MODE while resChanged was set to true");
- work->result = C2_CORRUPTED;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return true;
}
}
@@ -491,12 +495,14 @@ static void copyOutputBufferToYV12Frame(uint8_t *dst, uint8_t *src, size_t dstYS
void C2SoftMpeg4Dec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
- work->workletsProcessed = 1u;
return;
}
@@ -548,18 +554,16 @@ void C2SoftMpeg4Dec::process(
mDecHandle, vol_data, &vol_size, 1,
mIntf->getMaxWidth(), mIntf->getMaxHeight(), mode)) {
ALOGE("PVInitVideoDecoder failed. Unsupported content?");
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
mInitialized = true;
MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
if (mode != actualMode) {
ALOGE("Decoded mode not same as actual mode of the decoder");
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
@@ -576,7 +580,6 @@ void C2SoftMpeg4Dec::process(
ALOGE("Config update size failed");
mSignalledError = true;
work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
return;
}
}
@@ -592,23 +595,20 @@ void C2SoftMpeg4Dec::process(
if (C2_OK != err) {
mSignalledError = true;
work->result = err;
- work->workletsProcessed = 1u;
return;
}
C2GraphicView wView = mOutBlock->map().get();
if (wView.error()) {
ALOGE("graphic view map failed %d", wView.error());
work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
return;
}
uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size;
if (mOutputBufferSize < yFrameSize * 3 / 2){
ALOGE("Too small output buffer: %zu bytes", mOutputBufferSize);
- work->result = C2_NO_MEMORY;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_NO_MEMORY;
return;
}
@@ -628,9 +628,8 @@ void C2SoftMpeg4Dec::process(
&header_info, &useExtTimestamp,
mOutputBuffer[mNumSamplesOutput & 1]) != PV_TRUE) {
ALOGE("failed to decode vop header.");
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
@@ -638,9 +637,8 @@ void C2SoftMpeg4Dec::process(
// decoder may detect size change after PVDecodeVopHeader.
bool resChange = handleResChange(work);
if (mIsMpeg4 && resChange) {
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
} else if (resChange) {
ALOGI("Setting width and height");
@@ -653,7 +651,6 @@ void C2SoftMpeg4Dec::process(
ALOGE("Config update size failed");
mSignalledError = true;
work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
return;
}
continue;
@@ -661,15 +658,13 @@ void C2SoftMpeg4Dec::process(
if (PVDecodeVopBody(mDecHandle, &tmpInSize) != PV_TRUE) {
ALOGE("failed to decode video frame.");
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
if (handleResChange(work)) {
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
}
diff --git a/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp
index dd9d94b..c8796f3 100644
--- a/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp
+++ b/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp
@@ -93,8 +93,8 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper {
C2F(mSize, width).inRange(16, 176, 16),
C2F(mSize, height).inRange(16, 144, 16),
#else
- C2F(mSize, width).inRange(176, 176, 16),
- C2F(mSize, height).inRange(144, 144, 16),
+ C2F(mSize, width).oneOf({176, 352}),
+ C2F(mSize, height).oneOf({144, 288}),
#endif
})
.withSetter(SizeSetter)
@@ -253,9 +253,13 @@ c2_status_t C2SoftMpeg4Enc::onInit() {
if (!mHandle) {
mHandle = new tagvideoEncControls;
}
+
if (!mEncParams) {
mEncParams = new tagvideoEncOptions;
}
+
+ if (!(mEncParams && mHandle)) return C2_NO_MEMORY;
+
mSignalledOutputEos = false;
mSignalledError = false;
@@ -395,8 +399,10 @@ c2_status_t C2SoftMpeg4Enc::initEncoder() {
void C2SoftMpeg4Enc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -432,8 +438,8 @@ void C2SoftMpeg4Enc::process(
int32_t outputSize = mOutBufferSize;
if (!PVGetVolHeader(mHandle, outPtr, &outputSize, 0)) {
ALOGE("Failed to get VOL header");
- work->result = C2_CORRUPTED;
mSignalledError = true;
+ work->result = C2_CORRUPTED;
return;
} else {
ALOGV("Bytes Generated in header %d\n", outputSize);
@@ -442,6 +448,12 @@ void C2SoftMpeg4Enc::process(
++mNumInputFrames;
std::unique_ptr<C2StreamCsdInfo::output> csd =
C2StreamCsdInfo::output::AllocUnique(outputSize, 0u);
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
memcpy(csd->m.value, outPtr, outputSize);
work->worklets.front()->output.configUpdate.push_back(std::move(csd));
}
diff --git a/media/codecs/opus/C2SoftOpusDec.cpp b/media/codecs/opus/C2SoftOpusDec.cpp
index 996a87c..2439c3c 100644
--- a/media/codecs/opus/C2SoftOpusDec.cpp
+++ b/media/codecs/opus/C2SoftOpusDec.cpp
@@ -300,9 +300,12 @@ static uint64_t ns_to_samples(uint64_t ns, int rate) {
void C2SoftOpusDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
diff --git a/media/codecs/raw/C2SoftRawDec.cpp b/media/codecs/raw/C2SoftRawDec.cpp
index 27895c4..5c83481 100644
--- a/media/codecs/raw/C2SoftRawDec.cpp
+++ b/media/codecs/raw/C2SoftRawDec.cpp
@@ -83,6 +83,18 @@ public:
DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
.withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 64 * 1024))
.build());
+
+ addParameter(
+ DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING)
+ .withDefault(new C2StreamPcmEncodingInfo::output(0u, C2Config::PCM_16))
+ .withFields({C2F(mPcmEncodingInfo, value).oneOf({
+ C2Config::PCM_16,
+ C2Config::PCM_8,
+ C2Config::PCM_FLOAT})
+ })
+ .withSetter((Setter<decltype(*mPcmEncodingInfo)>::StrictValueWithNoDeps))
+ .build());
+
}
private:
@@ -94,6 +106,7 @@ private:
std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
std::shared_ptr<C2BitrateTuning::input> mBitrate;
std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+ std::shared_ptr<C2StreamPcmEncodingInfo::output> mPcmEncodingInfo;
};
C2SoftRawDec::C2SoftRawDec(
@@ -134,7 +147,8 @@ void C2SoftRawDec::process(
const std::shared_ptr<C2BlockPool> &pool) {
(void)pool;
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
+
if (mSignalledEos) {
work->result = C2_BAD_VALUE;
return;
@@ -149,7 +163,6 @@ void C2SoftRawDec::process(
if (!work->input.buffers.empty()) {
work->worklets.front()->output.buffers.push_back(work->input.buffers[0]);
}
- work->workletsProcessed = 1u;
if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
mSignalledEos = true;
ALOGV("signalled EOS");
diff --git a/media/codecs/vorbis/C2SoftVorbisDec.cpp b/media/codecs/vorbis/C2SoftVorbisDec.cpp
index f67cf72..280ae36 100644
--- a/media/codecs/vorbis/C2SoftVorbisDec.cpp
+++ b/media/codecs/vorbis/C2SoftVorbisDec.cpp
@@ -231,9 +231,12 @@ static void makeBitReader(
void C2SoftVorbisDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
- work->workletsProcessed = 0u;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
diff --git a/media/codecs/vpx/Android.bp b/media/codecs/vpx/Android.bp
index c09f365..cc83371 100644
--- a/media/codecs/vpx/Android.bp
+++ b/media/codecs/vpx/Android.bp
@@ -7,7 +7,7 @@ cc_library_shared {
srcs: ["C2SoftVpxDec.cpp"],
- static_libs: ["libvpx"],
+ shared_libs: ["libvpx"],
cflags: [
"-DVP9",
@@ -23,7 +23,7 @@ cc_library_shared {
srcs: ["C2SoftVpxDec.cpp"],
- static_libs: ["libvpx"],
+ shared_libs: ["libvpx"],
}
cc_library_shared {
@@ -38,7 +38,7 @@ cc_library_shared {
"C2SoftVpxEnc.cpp",
],
- static_libs: ["libvpx"],
+ shared_libs: ["libvpx"],
cflags: ["-DVP9"],
}
@@ -55,6 +55,6 @@ cc_library_shared {
"C2SoftVpxEnc.cpp",
],
- static_libs: ["libvpx"],
+ shared_libs: ["libvpx"],
}
diff --git a/media/codecs/vpx/C2SoftVpxDec.cpp b/media/codecs/vpx/C2SoftVpxDec.cpp
index d6e49f7..8ecbf5d 100644
--- a/media/codecs/vpx/C2SoftVpxDec.cpp
+++ b/media/codecs/vpx/C2SoftVpxDec.cpp
@@ -97,6 +97,26 @@ public:
.withSetter(ProfileLevelSetter, mSize)
.build());
+ mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0);
+ addParameter(
+ DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO)
+ .withDefault(mHdr10PlusInfoInput)
+ .withFields({
+ C2F(mHdr10PlusInfoInput, m.value).any(),
+ })
+ .withSetter(Hdr10PlusInfoInputSetter)
+ .build());
+
+ mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0);
+ addParameter(
+ DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO)
+ .withDefault(mHdr10PlusInfoOutput)
+ .withFields({
+ C2F(mHdr10PlusInfoOutput, m.value).any(),
+ })
+ .withSetter(Hdr10PlusInfoOutputSetter)
+ .build());
+
#if 0
// sample BT.2020 static info
mHdrStaticInfo = std::make_shared<C2StreamHdrStaticInfo::output>();
@@ -217,6 +237,18 @@ public:
return C2R::Ok();
}
+ static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::input> &me) {
+ (void)mayBlock;
+ (void)me; // TODO: validate
+ return C2R::Ok();
+ }
+
+ static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::output> &me) {
+ (void)mayBlock;
+ (void)me; // TODO: validate
+ return C2R::Ok();
+ }
+
private:
std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
@@ -228,6 +260,8 @@ private:
#if 0
std::shared_ptr<C2StreamHdrStaticInfo::output> mHdrStaticInfo;
#endif
+ std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
+ std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
#endif
};
@@ -320,6 +354,10 @@ status_t C2SoftVpxDec::initDecoder() {
if (!mCodecCtx) {
mCodecCtx = new vpx_codec_ctx_t;
}
+ if (!mCodecCtx) {
+ ALOGE("mCodecCtx is null");
+ return NO_MEMORY;
+ }
vpx_codec_dec_cfg_t cfg;
memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
@@ -366,7 +404,8 @@ void C2SoftVpxDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &wor
const std::shared_ptr<C2GraphicBlock> &block) {
std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block,
C2Rect(mWidth, mHeight));
- auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+ auto fillWork = [buffer, index, intf = this->mIntf](
+ const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
(c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
@@ -378,6 +417,28 @@ void C2SoftVpxDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &wor
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
work->workletsProcessed = 1u;
+
+ for (const std::unique_ptr<C2Param> &param: work->input.configUpdate) {
+ if (param) {
+ C2StreamHdr10PlusInfo::input *hdr10PlusInfo =
+ C2StreamHdr10PlusInfo::input::From(param.get());
+
+ if (hdr10PlusInfo != nullptr) {
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ std::unique_ptr<C2Param> outParam = C2Param::CopyAsStream(
+ *param.get(), true /*output*/, param->stream());
+ c2_status_t err = intf->config(
+ { outParam.get() }, C2_MAY_BLOCK, &failures);
+ if (err == C2_OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(*outParam.get()));
+ } else {
+ ALOGE("finishWork: Config update size failed");
+ }
+ break;
+ }
+ }
+ }
};
if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
fillWork(work);
@@ -389,9 +450,12 @@ void C2SoftVpxDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &wor
void C2SoftVpxDec::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 0u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -440,9 +504,9 @@ void C2SoftVpxDec::process(
mCodecCtx, bitstream, inSize, &frameIndex, 0);
if (err != VPX_CODEC_OK) {
ALOGE("on2 decoder failed to decode frame. err: %d", err);
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
return;
}
}
@@ -510,8 +574,8 @@ bool C2SoftVpxDec::outputBuffer(
} else {
ALOGE("Config update size failed");
mSignalledError = true;
- work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
return false;
}
diff --git a/media/codecs/vpx/C2SoftVpxEnc.cpp b/media/codecs/vpx/C2SoftVpxEnc.cpp
index 37fd59d..155a84f 100644
--- a/media/codecs/vpx/C2SoftVpxEnc.cpp
+++ b/media/codecs/vpx/C2SoftVpxEnc.cpp
@@ -412,8 +412,11 @@ vpx_enc_frame_flags_t C2SoftVpxEnc::getEncodeFlags() {
void C2SoftVpxEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
work->result = C2_OK;
work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -457,6 +460,7 @@ void C2SoftVpxEnc::process(
ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
inBuffer.width(), mSize->width, inBuffer.height(),
mSize->height);
+ mSignalledError = true;
work->result = C2_BAD_VALUE;
return;
}
@@ -563,6 +567,7 @@ void C2SoftVpxEnc::process(
if (res != VPX_CODEC_OK) {
ALOGE("vpx encoder failed to update bitrate: %s",
vpx_codec_err_to_string(res));
+ mSignalledError = true;
work->result = C2_CORRUPTED;
return;
}
@@ -589,6 +594,7 @@ void C2SoftVpxEnc::process(
VPX_DL_REALTIME);
if (codec_return != VPX_CODEC_OK) {
ALOGE("vpx encoder failed to encode frame");
+ mSignalledError = true;
work->result = C2_CORRUPTED;
return;
}
diff --git a/media/codecs/xaac/C2SoftXaacDec.cpp b/media/codecs/xaac/C2SoftXaacDec.cpp
index ba856d1..1c0e70b 100644
--- a/media/codecs/xaac/C2SoftXaacDec.cpp
+++ b/media/codecs/xaac/C2SoftXaacDec.cpp
@@ -327,7 +327,10 @@ IA_ERRORCODE C2SoftXaacDec::initDecoder() {
return IA_FATAL_ERROR;
}
- mOutputDrainBuffer = new short[kOutputDrainBufferSize];
+ if (!mOutputDrainBuffer) {
+ mOutputDrainBuffer = new (std::nothrow) char[kOutputDrainBufferSize];
+ if (!mOutputDrainBuffer) return IA_FATAL_ERROR;
+ }
err_code = initXAACDrc();
RETURN_IF_FATAL(err_code, "initXAACDrc");
@@ -357,7 +360,11 @@ void C2SoftXaacDec::finishWork(const std::unique_ptr<C2Work>& work,
// TODO: error handling, proper usage, etc.
c2_status_t err =
pool->fetchLinearBlock(mOutputDrainBufferWritePos, usage, &block);
- if (err != C2_OK) ALOGE("err = %d", err);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock failed : err = %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
C2WriteView wView = block->map().get();
int16_t* outBuffer = reinterpret_cast<int16_t*>(wView.data());
memcpy(outBuffer, mOutputDrainBuffer, mOutputDrainBufferWritePos);
@@ -387,9 +394,12 @@ void C2SoftXaacDec::finishWork(const std::unique_ptr<C2Work>& work,
void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work,
const std::shared_ptr<C2BlockPool>& pool) {
- work->workletsProcessed = 0u;
+ // Initialize output work
work->result = C2_OK;
+ work->workletsProcessed = 1u;
work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
@@ -412,7 +422,6 @@ void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work,
bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
bool codecConfig =
(work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
-
if (codecConfig) {
if (size == 0u) {
ALOGE("empty codec config");
@@ -435,13 +444,13 @@ void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work,
work->worklets.front()->output.flags = work->input.flags;
work->worklets.front()->output.ordinal = work->input.ordinal;
work->worklets.front()->output.buffers.clear();
- work->workletsProcessed = 1u;
return;
}
mCurFrameIndex = work->input.ordinal.frameIndex.peeku();
mCurTimestamp = work->input.ordinal.timestamp.peeku();
mOutputDrainBufferWritePos = 0;
+ char* tempOutputDrainBuffer = mOutputDrainBuffer;
while (size > 0u) {
if ((kOutputDrainBufferSize * sizeof(int16_t) -
mOutputDrainBufferWritePos) <
@@ -537,7 +546,7 @@ void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work,
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(channelCountInfo));
} else {
- ALOGE("Cannot set width and height");
+ ALOGE("Config Update failed");
mSignalledError = true;
work->result = C2_CORRUPTED;
return;
@@ -588,7 +597,8 @@ void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work,
// fall through
}
- memcpy(mOutputDrainBuffer, mOutputBuffer, mNumOutBytes);
+ memcpy(tempOutputDrainBuffer, mOutputBuffer, mNumOutBytes);
+ tempOutputDrainBuffer += mNumOutBytes;
mOutputDrainBufferWritePos += mNumOutBytes;
}
@@ -623,7 +633,7 @@ IA_ERRORCODE C2SoftXaacDec::configflushDecode() {
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INIT,
IA_CMD_TYPE_FLUSH_MEM,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_FLUSH_MEM");
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
@@ -635,7 +645,7 @@ IA_ERRORCODE C2SoftXaacDec::configflushDecode() {
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INIT,
IA_CMD_TYPE_FLUSH_MEM,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_FLUSH_MEM");
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
@@ -695,7 +705,7 @@ IA_ERRORCODE C2SoftXaacDec::initXAACDecoder() {
/* API size */
uint32_t pui_api_size;
/* Get the API size */
- IA_ERRORCODE err_code = ixheaacd_dec_api(NULL,
+ IA_ERRORCODE err_code = ixheaacd_dec_api(nullptr,
IA_API_CMD_GET_API_SIZE,
0,
&pui_api_size);
@@ -713,11 +723,11 @@ IA_ERRORCODE C2SoftXaacDec::initXAACDecoder() {
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INIT,
IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS");
/* Get the API size */
- err_code = ia_drc_dec_api(NULL, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size);
+ err_code = ia_drc_dec_api(nullptr, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size);
RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_API_SIZE");
@@ -731,7 +741,7 @@ IA_ERRORCODE C2SoftXaacDec::initXAACDecoder() {
/* Set the config params to default values */
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL);
+ IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS");
@@ -775,7 +785,7 @@ IA_ERRORCODE C2SoftXaacDec::initXAACDecoder() {
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INIT,
IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS");
/* ******************************************************************/
@@ -906,12 +916,12 @@ IA_ERRORCODE C2SoftXaacDec::deInitXAACDecoder() {
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INPUT_OVER,
0,
- NULL);
+ nullptr);
}
/* Irrespective of error returned in IA_API_CMD_INPUT_OVER, free allocated memory */
for (void* buf : mMemoryVec) {
- free(buf);
+ if (buf) free(buf);
}
mMemoryVec.clear();
mXheaacCodecHandle = nullptr;
@@ -923,7 +933,7 @@ IA_ERRORCODE C2SoftXaacDec::deInitMPEGDDDrc() {
ALOGV("deInitMPEGDDDrc");
for (void* buf : mDrcMemoryVec) {
- free(buf);
+ if (buf) free(buf);
}
mDrcMemoryVec.clear();
return IA_NO_ERROR;
@@ -951,14 +961,14 @@ IA_ERRORCODE C2SoftXaacDec::configXAACDecoder(uint8_t* inBuffer, uint32_t inBuff
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INIT,
IA_CMD_TYPE_GA_HDR,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_GA_HDR");
} else {
/* Initialize the process */
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_INIT,
IA_CMD_TYPE_INIT_PROCESS,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_PROCESS");
}
@@ -1014,7 +1024,7 @@ IA_ERRORCODE C2SoftXaacDec::initMPEGDDDrc() {
RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE");
pv_alloc_ptr = memalign(4, ui_size);
- if (pv_alloc_ptr == NULL) {
+ if (pv_alloc_ptr == nullptr) {
ALOGE(" Cannot create requested memory %d", ui_size);
return IA_FATAL_ERROR;
}
@@ -1030,7 +1040,7 @@ IA_ERRORCODE C2SoftXaacDec::initMPEGDDDrc() {
ui_size = 8192 * 2;
mDrcInBuf = (int8_t*)memalign(4, ui_size);
- if (mDrcInBuf == NULL) {
+ if (mDrcInBuf == nullptr) {
ALOGE(" Cannot create requested memory %d", ui_size);
return IA_FATAL_ERROR;
}
@@ -1040,7 +1050,7 @@ IA_ERRORCODE C2SoftXaacDec::initMPEGDDDrc() {
RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR");
mDrcOutBuf = (int8_t*)memalign(4, ui_size);
- if (mDrcOutBuf == NULL) {
+ if (mDrcOutBuf == nullptr) {
ALOGE(" Cannot create requested memory %d", ui_size);
return IA_FATAL_ERROR;
}
@@ -1106,7 +1116,7 @@ int C2SoftXaacDec::configMPEGDDrc() {
RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE");
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL);
+ IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS");
@@ -1145,7 +1155,7 @@ int C2SoftXaacDec::configMPEGDDrc() {
RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_PTR");
err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_SET_BUFF_PTR, 0);
+ ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_SET_BUFF_PTR, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_SET_BUFF_PTR");
err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
@@ -1173,7 +1183,7 @@ int C2SoftXaacDec::configMPEGDDrc() {
/* Execute process */
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF, NULL);
+ IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF");
mDRCFlag = 1;
@@ -1197,7 +1207,7 @@ int C2SoftXaacDec::configMPEGDDrc() {
/* Execute process */
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF, NULL);
+ IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF");
@@ -1238,11 +1248,11 @@ int C2SoftXaacDec::configMPEGDDrc() {
/* Execute process */
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF, NULL);
+ IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF");
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_PROCESS, NULL);
+ IA_CMD_TYPE_INIT_PROCESS, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_PROCESS");
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_CONFIG_PARAM,
@@ -1276,7 +1286,7 @@ IA_ERRORCODE C2SoftXaacDec::decodeXAACStream(uint8_t* inBuffer,
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
IA_API_CMD_EXECUTE,
IA_CMD_TYPE_DO_EXECUTE,
- NULL);
+ nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
/* Checking for end of processing */
@@ -1319,7 +1329,7 @@ IA_ERRORCODE C2SoftXaacDec::decodeXAACStream(uint8_t* inBuffer,
/* Execute process */
err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
+ IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
mMpegDDRCPresent = 1;
@@ -1346,7 +1356,7 @@ IA_ERRORCODE C2SoftXaacDec::decodeXAACStream(uint8_t* inBuffer,
RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, NULL);
+ ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
diff --git a/media/codecs/xaac/C2SoftXaacDec.h b/media/codecs/xaac/C2SoftXaacDec.h
index b99d2ea..5c8567f 100644
--- a/media/codecs/xaac/C2SoftXaacDec.h
+++ b/media/codecs/xaac/C2SoftXaacDec.h
@@ -97,7 +97,7 @@ private:
size_t mOutputBufferCount __unused;
bool mSignalledOutputEos;
bool mSignalledError;
- short* mOutputDrainBuffer;
+ char* mOutputDrainBuffer;
uint32_t mOutputDrainBufferWritePos;
IA_ERRORCODE initDecoder();
diff --git a/media/eco/.clang-format b/media/eco/.clang-format
new file mode 100644
index 0000000..b043d46
--- /dev/null
+++ b/media/eco/.clang-format
@@ -0,0 +1,33 @@
+---
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+
+# Deviations from the above file:
+# "Don't indent the section label"
+AccessModifierOffset: -4
+# "Each line of text in your code should be at most 100 columns long."
+ColumnLimit: 100
+# "Constructor initializer lists can be all on one line or with subsequent
+# lines indented eight spaces.". clang-format does not support having the colon
+# on the same line as the constructor function name, so this is the best
+# approximation of that rule, which makes all entries in the list (except the
+# first one) have an eight space indentation.
+ConstructorInitializerIndentWidth: 6
+# There is nothing in go/droidcppstyle about case labels, but there seems to be
+# more code that does not indent the case labels in frameworks/base.
+IndentCaseLabels: false
+# There have been some bugs in which subsequent formatting operations introduce
+# weird comment jumps.
+ReflowComments: false
+# Android does support C++11 now.
+Standard: Cpp11
diff --git a/media/eco/Android.bp b/media/eco/Android.bp
new file mode 100644
index 0000000..0475861
--- /dev/null
+++ b/media/eco/Android.bp
@@ -0,0 +1,65 @@
+filegroup {
+ name: "libmedia_ecoservice_aidl",
+ srcs: [
+ "aidl/android/media/eco/IECOService.aidl",
+ "aidl/android/media/eco/IECOSession.aidl",
+ "aidl/android/media/eco/IECOServiceStatsProvider.aidl",
+ "aidl/android/media/eco/IECOServiceInfoListener.aidl",
+ ],
+ path: "aidl",
+}
+
+cc_library_shared {
+ name: "libmedia_ecoservice",
+ vendor_available: true,
+
+ srcs: [
+ ":libmedia_ecoservice_aidl",
+ "ECOData.cpp",
+ "ECODebug.cpp",
+ "ECOService.cpp",
+ "ECOSession.cpp",
+ "ECOUtils.cpp",
+ ],
+
+ aidl: {
+ local_include_dirs: [
+ "include",
+ "aidl",
+ ],
+ export_aidl_headers: true,
+ },
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: false, // true,
+ diag: {
+ cfi: false, // true,
+ },
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
diff --git a/media/eco/ECOData.cpp b/media/eco/ECOData.cpp
new file mode 100644
index 0000000..189e609
--- /dev/null
+++ b/media/eco/ECOData.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ECOData"
+
+#include "eco/ECOData.h"
+
+#include <binder/Parcel.h>
+#include <inttypes.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <string>
+
+#include "eco/ECODataKey.h"
+#include "eco/ECOUtils.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using namespace ::android;
+
+status_t ECOData::readFromParcel(const Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("readFromParcel failed. Parcel pointer can not be null");
+ return BAD_VALUE;
+ }
+
+ // Reads the data type and time.
+ RETURN_STATUS_IF_ERROR(parcel->readInt32(&mDataType));
+ RETURN_STATUS_IF_ERROR(parcel->readInt64(&mDataTimeUs));
+
+ // Reads the number of items.
+ uint32_t numOfItems = 0;
+ RETURN_STATUS_IF_ERROR(parcel->readUint32(&numOfItems));
+
+ // Reads the key-value pairs one by one.
+ for (size_t i = 0; i < numOfItems; ++i) {
+ // Reads the name of the key.
+ const char* name = parcel->readCString();
+ if (name == NULL) {
+ ALOGE("Failed reading name for the key. Parsing aborted.");
+ return NAME_NOT_FOUND;
+ }
+
+ int32_t type;
+ RETURN_STATUS_IF_ERROR(parcel->readInt32(&type));
+ switch (static_cast<ValueType>(type)) {
+ case kTypeInt32: {
+ int32_t value32;
+ RETURN_STATUS_IF_ERROR(parcel->readInt32(&value32));
+ setInt32(std::string(name), value32);
+ break;
+ }
+ case kTypeInt64: {
+ int64_t value64;
+ RETURN_STATUS_IF_ERROR(parcel->readInt64(&value64));
+ setInt64(std::string(name), value64);
+ break;
+ }
+ case kTypeSize: {
+ int32_t valueSize;
+ RETURN_STATUS_IF_ERROR(parcel->readInt32(&valueSize));
+ setInt32(std::string(name), valueSize);
+ break;
+ }
+ case kTypeFloat: {
+ float valueFloat;
+ RETURN_STATUS_IF_ERROR(parcel->readFloat(&valueFloat));
+ setFloat(std::string(name), valueFloat);
+ break;
+ }
+ case kTypeDouble: {
+ double valueDouble;
+ RETURN_STATUS_IF_ERROR(parcel->readDouble(&valueDouble));
+ setDouble(std::string(name), valueDouble);
+ break;
+ }
+ case kTypeString: {
+ const char* valueStr = parcel->readCString();
+ if (valueStr == NULL) {
+ ALOGE("Failed reading name for the key. Parsing aborted.");
+ return NAME_NOT_FOUND;
+ }
+ setString(std::string(name), valueStr);
+ break;
+ }
+ case kTypeInt8: {
+ int8_t value8;
+ RETURN_STATUS_IF_ERROR(parcel->readByte(&value8));
+ setInt8(std::string(name), value8);
+ break;
+ }
+ default: {
+ return BAD_TYPE;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t ECOData::writeToParcel(Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("writeToParcel failed. Parcel pointer can not be null");
+ return BAD_VALUE;
+ }
+
+ // Writes out the data type and time.
+ RETURN_STATUS_IF_ERROR(parcel->writeInt32(mDataType));
+ RETURN_STATUS_IF_ERROR(parcel->writeInt64(mDataTimeUs));
+
+ // Writes out number of items.
+ RETURN_STATUS_IF_ERROR(parcel->writeUint32(int32_t(mKeyValueStore.size())));
+
+ // Writes out the key-value pairs one by one.
+ for (const auto& it : mKeyValueStore) {
+ // Writes out the key.
+ RETURN_STATUS_IF_ERROR(parcel->writeCString(it.first.c_str()));
+
+ // Writes out the data type.
+ const ECODataValueType& value = it.second;
+ RETURN_STATUS_IF_ERROR(parcel->writeInt32(static_cast<int32_t>(value.index())));
+ switch (static_cast<ValueType>(value.index())) {
+ case kTypeInt32:
+ RETURN_STATUS_IF_ERROR(parcel->writeInt32(std::get<int32_t>(it.second)));
+ break;
+
+ case kTypeInt64:
+ RETURN_STATUS_IF_ERROR(parcel->writeInt64(std::get<int64_t>(it.second)));
+ break;
+
+ case kTypeSize:
+ RETURN_STATUS_IF_ERROR(parcel->writeUint32(std::get<size_t>(it.second)));
+ break;
+
+ case kTypeFloat:
+ RETURN_STATUS_IF_ERROR(parcel->writeFloat(std::get<float>(it.second)));
+ break;
+
+ case kTypeDouble:
+ RETURN_STATUS_IF_ERROR(parcel->writeDouble(std::get<double>(it.second)));
+ break;
+
+ case kTypeString:
+ RETURN_STATUS_IF_ERROR(parcel->writeCString(std::get<std::string>(it.second).c_str()));
+ break;
+
+ case kTypeInt8:
+ RETURN_STATUS_IF_ERROR(parcel->writeByte(std::get<int8_t>(it.second)));
+ break;
+
+ default:
+ return BAD_TYPE;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+int32_t ECOData::getDataType() const {
+ return mDataType;
+}
+
+int64_t ECOData::getDataTimeUs() const {
+ return mDataTimeUs;
+}
+
+// Inserts a new key into store if the key does not exist yet. Otherwise, this will override the
+// existing key's value.
+ECODataStatus ECOData::setString(const std::string& key, const std::string& value) {
+ if (key.empty() || value.empty()) {
+ return ECODataStatus::INVALID_ARGUMENT;
+ }
+
+ mKeyValueStore[key] = value;
+
+ // TODO(hkuang): Check the valueType is valid for the key.
+ return ECODataStatus::OK;
+}
+
+ECODataStatus ECOData::findString(const std::string& key, std::string* value) const {
+ if (key.empty()) {
+ return ECODataStatus::INVALID_ARGUMENT;
+ }
+
+ // Check if the key exists.
+ if (mKeyValueStore.find(key) == mKeyValueStore.end()) {
+ return ECODataStatus::KEY_NOT_EXIST;
+ }
+
+ // Safely access the value.
+ const std::string& entryValue = std::get<std::string>(mKeyValueStore.at(key));
+ value->assign(entryValue);
+
+ return ECODataStatus::OK;
+}
+
+// Inserts a new key into store if the key does not exist yet. Otherwise, this will override the
+// existing key's value.
+template <typename T>
+ECODataStatus ECOData::setValue(const std::string& key, T value) {
+ if (key.empty()) {
+ return ECODataStatus::INVALID_ARGUMENT;
+ }
+
+ mKeyValueStore[key] = value;
+ return ECODataStatus::OK;
+}
+
+template <typename T>
+ECODataStatus ECOData::findValue(const std::string& key, T* out) const {
+ if (key.empty() || out == nullptr) {
+ return ECODataStatus::INVALID_ARGUMENT;
+ }
+
+ if (mKeyValueStore.find(key) == mKeyValueStore.end()) {
+ return ECODataStatus::KEY_NOT_EXIST;
+ }
+
+ // Safely access the value.
+ *out = std::get<T>(mKeyValueStore.at(key));
+
+ return ECODataStatus::OK;
+}
+
+ECODataStatus ECOData::setInt32(const std::string& key, int32_t value) {
+ return setValue<int32_t>(key, value);
+}
+
+ECODataStatus ECOData::findInt32(const std::string& key, int32_t* out) const {
+ return findValue<int32_t>(key, out);
+}
+
+ECODataStatus ECOData::setInt64(const std::string& key, int64_t value) {
+ return setValue<int64_t>(key, value);
+}
+
+ECODataStatus ECOData::findInt64(const std::string& key, int64_t* out) const {
+ return findValue<int64_t>(key, out);
+}
+
+ECODataStatus ECOData::setDouble(const std::string& key, double value) {
+ return setValue<double>(key, value);
+}
+
+ECODataStatus ECOData::findDouble(const std::string& key, double* out) const {
+ return findValue<double>(key, out);
+}
+
+ECODataStatus ECOData::setSize(const std::string& key, size_t value) {
+ return setValue<size_t>(key, value);
+}
+
+ECODataStatus ECOData::findSize(const std::string& key, size_t* out) const {
+ return findValue<size_t>(key, out);
+}
+
+ECODataStatus ECOData::setFloat(const std::string& key, float value) {
+ return setValue<float>(key, value);
+}
+
+ECODataStatus ECOData::findFloat(const std::string& key, float* out) const {
+ return findValue<float>(key, out);
+}
+
+ECODataStatus ECOData::setInt8(const std::string& key, int8_t value) {
+ return setValue<int8_t>(key, value);
+}
+
+ECODataStatus ECOData::findInt8(const std::string& key, int8_t* out) const {
+ return findValue<int8_t>(key, out);
+}
+
+ECODataStatus ECOData::set(const std::string& key, const ECOData::ECODataValueType& value) {
+ if (key.empty()) {
+ return ECODataStatus::INVALID_ARGUMENT;
+ }
+ mKeyValueStore[key] = value;
+ return ECODataStatus::OK;
+}
+
+ECODataStatus ECOData::find(const std::string& key, ECOData::ECODataValueType* out) const {
+ if (key.empty() || out == nullptr) {
+ return ECODataStatus::INVALID_ARGUMENT;
+ }
+
+ if (mKeyValueStore.find(key) == mKeyValueStore.end()) {
+ return ECODataStatus::KEY_NOT_EXIST;
+ }
+
+ // Safely access the value.
+ *out = mKeyValueStore.at(key);
+
+ return ECODataStatus::OK;
+}
+
+std::string ECOData::getDataTypeString() const {
+ switch (mDataType) {
+ case DATA_TYPE_UNKNOWN:
+ return "DATA_TYPE_UNKNOWN";
+ case DATA_TYPE_STATS:
+ return "DATA_TYPE_STATS";
+ case DATA_TYPE_INFO:
+ return "DATA_TYPE_INFO";
+ case DATA_TYPE_STATS_PROVIDER_CONFIG:
+ return "DATA_TYPE_STATS_PROVIDER_CONFIG";
+ case DATA_TYPE_INFO_LISTENER_CONFIG:
+ return "DATA_TYPE_INFO_LISTENER_CONFIG";
+ }
+ return {};
+}
+
+// TODO(hkuang): Add test for this.
+bool ECODataKeyValueIterator::hasNext() {
+ if (mIterator == mKeyValueStore.end()) return false;
+
+ if (!mBeginReturned) {
+ // mIterator has been initialized to the beginning and
+ // hasn't been returned. Do not advance:
+ mBeginReturned = true;
+ } else {
+ std::advance(mIterator, 1);
+ }
+ return mIterator != mKeyValueStore.end();
+}
+
+// TODO(hkuang): Add test for this.
+ECOData::ECODataKeyValuePair ECODataKeyValueIterator::next() const {
+ return ECOData::ECODataKeyValuePair(mIterator->first, mIterator->second);
+}
+
+std::string ECOData::debugString() const {
+ std::string s = "ECOData(type = ";
+
+ std::string tmp;
+ switch (mDataType) {
+ case DATA_TYPE_UNKNOWN:
+ tmp = "Unknown";
+ break;
+ case DATA_TYPE_STATS:
+ tmp = "Stats";
+ break;
+ case DATA_TYPE_INFO:
+ tmp = "Info";
+ break;
+ case DATA_TYPE_STATS_PROVIDER_CONFIG:
+ tmp = "Stats provider config";
+ break;
+ case DATA_TYPE_INFO_LISTENER_CONFIG:
+ tmp = "Info listener config";
+ break;
+ default:
+ break;
+ }
+ s.append(tmp);
+ s.append(") = {\n ");
+
+ // Writes out the key-value pairs one by one.
+ for (const auto& it : mKeyValueStore) {
+ const size_t SIZE = 100;
+ char keyValue[SIZE];
+ const ECODataValueType& value = it.second;
+ switch (static_cast<ValueType>(value.index())) {
+ case kTypeInt32:
+ snprintf(keyValue, SIZE, "int32_t %s = %d, ", it.first.c_str(),
+ std::get<int32_t>(it.second));
+ break;
+ case kTypeInt64:
+ snprintf(keyValue, SIZE, "int64_t %s = %" PRId64 ", ", it.first.c_str(),
+ std::get<int64_t>(it.second));
+ break;
+ case kTypeSize:
+ snprintf(keyValue, SIZE, "size_t %s = %zu, ", it.first.c_str(),
+ std::get<size_t>(it.second));
+ break;
+ case kTypeFloat:
+ snprintf(keyValue, SIZE, "float %s = %f, ", it.first.c_str(),
+ std::get<float>(it.second));
+ break;
+ case kTypeDouble:
+ snprintf(keyValue, SIZE, "double %s = %f, ", it.first.c_str(),
+ std::get<double>(it.second));
+ break;
+ case kTypeString:
+ snprintf(keyValue, SIZE, "string %s = %s, ", it.first.c_str(),
+ std::get<std::string>(it.second).c_str());
+ break;
+ case kTypeInt8:
+ snprintf(keyValue, SIZE, "int8_t %s = %d, ", it.first.c_str(),
+ std::get<int8_t>(it.second));
+ break;
+ default:
+ break;
+ }
+ s.append(keyValue);
+ }
+
+ s.append("\n }");
+
+ return s;
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/ECODebug.cpp b/media/eco/ECODebug.cpp
new file mode 100644
index 0000000..5ed08ab
--- /dev/null
+++ b/media/eco/ECODebug.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "eco/ECODebug.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+uint32_t gECOLogLevel = 0;
+
+void updateLogLevel() {
+ gECOLogLevel = property_get_int32(kDebugLogsLevelProperty, 0);
+ ALOGI("ECOService log level is %d", gECOLogLevel);
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/ECOService.cpp b/media/eco/ECOService.cpp
new file mode 100644
index 0000000..6cc4be4
--- /dev/null
+++ b/media/eco/ECOService.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ECOService"
+
+#include "eco/ECOService.h"
+
+#include <binder/BinderService.h>
+#include <cutils/atomic.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <climits>
+#include <cstring>
+#include <ctime>
+#include <string>
+
+#include "eco/ECODebug.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+ECOService::ECOService() : BnECOService() {
+ ALOGD("ECOService created");
+ updateLogLevel();
+}
+
+/*virtual*/ ::android::binder::Status ECOService::obtainSession(
+ int32_t width, int32_t height, bool isCameraRecording,
+ ::android::sp<::android::media::eco::IECOSession>* _aidl_return) {
+ ECOLOGI("ECOService::obtainSession w: %d, h: %d, isCameraRecording: %d", width, height,
+ isCameraRecording);
+
+ if (width <= 0) {
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Width can not be <= 0");
+ }
+
+ if (height <= 0) {
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Height can not be <= 0");
+ }
+
+ SessionConfig newCfg(width, height, isCameraRecording);
+
+ ECOLOGD("session count before is %zu", mSessionConfigToSessionMap.size());
+
+ Mutex::Autolock lock(mServiceLock);
+ bool foundSession = false;
+ // Instead of looking up the map directly, take the chance to scan the map and evict all the
+ // invalid sessions.
+ SanitizeSession([&](MapIterType iter) {
+ if (iter->first == newCfg) {
+ sp<ECOSession> session = iter->second.promote();
+ foundSession = true;
+ *_aidl_return = session;
+ }
+ });
+
+ if (foundSession) {
+ return binder::Status::ok();
+ }
+
+ // Create a new session and add it to the record.
+ sp<ECOSession> newSession = ECOSession::createECOSession(width, height, isCameraRecording);
+ if (newSession == nullptr) {
+ ECOLOGE("ECOService failed to create ECOSession w: %d, h: %d, isCameraRecording: %d", width,
+ height, isCameraRecording);
+ return STATUS_ERROR(ERROR_UNSUPPORTED, "Failed to create eco session");
+ }
+ *_aidl_return = newSession;
+ // Insert the new session into the map.
+ mSessionConfigToSessionMap[newCfg] = newSession;
+ ECOLOGD("session count after is %zu", mSessionConfigToSessionMap.size());
+
+ return binder::Status::ok();
+}
+
+/*virtual*/ ::android::binder::Status ECOService::getNumOfSessions(int32_t* _aidl_return) {
+ Mutex::Autolock lock(mServiceLock);
+ SanitizeSession(std::function<void(MapIterType it)>()); // empty callback
+ *_aidl_return = mSessionConfigToSessionMap.size();
+ return binder::Status::ok();
+}
+
+/*virtual*/ ::android::binder::Status ECOService::getSessions(
+ ::std::vector<::android::sp<::android::IBinder>>* _aidl_return) {
+ // Clear all the entries in the vector.
+ _aidl_return->clear();
+
+ Mutex::Autolock lock(mServiceLock);
+ SanitizeSession([&](MapIterType iter) {
+ sp<ECOSession> session = iter->second.promote();
+ _aidl_return->push_back(IInterface::asBinder(session));
+ });
+ return binder::Status::ok();
+}
+
+inline bool isEmptySession(const android::wp<ECOSession>& entry) {
+ sp<ECOSession> session = entry.promote();
+ return session == nullptr;
+}
+
+void ECOService::SanitizeSession(
+ const std::function<void(std::unordered_map<SessionConfig, wp<ECOSession>,
+ SessionConfigHash>::iterator it)>& callback) {
+ for (auto it = mSessionConfigToSessionMap.begin(), end = mSessionConfigToSessionMap.end();
+ it != end;) {
+ if (isEmptySession(it->second)) {
+ it = mSessionConfigToSessionMap.erase(it);
+ } else {
+ if (callback != nullptr) {
+ callback(it);
+ };
+ it++;
+ }
+ }
+}
+
+/*virtual*/ void ECOService::binderDied(const wp<IBinder>& /*who*/) {}
+
+status_t ECOService::dump(int fd, const Vector<String16>& args) {
+ Mutex::Autolock lock(mServiceLock);
+ dprintf(fd, "\n== ECO Service info: ==\n\n");
+ dprintf(fd, "Number of ECOServices: %zu\n", mSessionConfigToSessionMap.size());
+ for (auto it = mSessionConfigToSessionMap.begin(), end = mSessionConfigToSessionMap.end();
+ it != end; it++) {
+ sp<ECOSession> session = it->second.promote();
+ if (session != nullptr) {
+ session->dump(fd, args);
+ }
+ }
+
+ return NO_ERROR;
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android \ No newline at end of file
diff --git a/media/eco/ECOSession.cpp b/media/eco/ECOSession.cpp
new file mode 100644
index 0000000..1bdfa61
--- /dev/null
+++ b/media/eco/ECOSession.cpp
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ECOSession"
+//#define DEBUG_ECO_SESSION
+#include "eco/ECOSession.h"
+
+#include <binder/BinderService.h>
+#include <cutils/atomic.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <climits>
+#include <cstring>
+#include <ctime>
+#include <string>
+
+#include "eco/ECODataKey.h"
+#include "eco/ECODebug.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using android::binder::Status;
+using android::sp;
+
+#define RETURN_IF_ERROR(expr) \
+ { \
+ status_t _errorCode = (expr); \
+ if (_errorCode != true) { \
+ return _errorCode; \
+ } \
+ }
+
+// static
+sp<ECOSession> ECOSession::createECOSession(int32_t width, int32_t height, bool isCameraRecording) {
+ // Only support up to 720P.
+ // TODO: Support the same resolution as in EAF.
+ if (width <= 0 || height <= 0 || width > 5120 || height > 5120 ||
+ width > 1280 * 720 / height) {
+ ECOLOGE("Failed to create ECOSession with w: %d, h: %d, isCameraRecording: %d", width,
+ height, isCameraRecording);
+ return nullptr;
+ }
+ return new ECOSession(width, height, isCameraRecording);
+}
+
+ECOSession::ECOSession(int32_t width, int32_t height, bool isCameraRecording)
+ : BnECOSession(),
+ mStopThread(false),
+ mLastReportedQp(0),
+ mListener(nullptr),
+ mProvider(nullptr),
+ mWidth(width),
+ mHeight(height),
+ mIsCameraRecording(isCameraRecording) {
+ ECOLOGI("ECOSession created with w: %d, h: %d, isCameraRecording: %d", mWidth, mHeight,
+ mIsCameraRecording);
+ mThread = std::thread(startThread, this);
+
+ // Read the debug properies.
+ mLogStats = property_get_bool(kDebugLogStats, false);
+ mLogStatsEntries = mLogStats ? property_get_int32(kDebugLogStatsSize, 0) : 0;
+
+ mLogInfo = property_get_bool(kDebugLogStats, false);
+ mLogInfoEntries = mLogInfo ? property_get_int32(kDebugLogInfosSize, 0) : 0;
+
+ ECOLOGI("ECOSession debug settings: logStats: %s, entries: %d, logInfo: %s entries: %d",
+ mLogStats ? "true" : "false", mLogStatsEntries, mLogInfo ? "true" : "false",
+ mLogInfoEntries);
+}
+
+ECOSession::~ECOSession() {
+ mStopThread = true;
+
+ mWorkerWaitCV.notify_all();
+ if (mThread.joinable()) {
+ ECOLOGD("ECOSession: join the thread");
+ mThread.join();
+ }
+ ECOLOGI("ECOSession destroyed with w: %d, h: %d, isCameraRecording: %d", mWidth, mHeight,
+ mIsCameraRecording);
+}
+
+// static
+void ECOSession::startThread(ECOSession* session) {
+ session->run();
+}
+
+void ECOSession::run() {
+ ECOLOGD("ECOSession: starting main thread");
+
+ while (!mStopThread) {
+ std::unique_lock<std::mutex> runLock(mStatsQueueLock);
+
+ mWorkerWaitCV.wait(runLock, [this] {
+ return mStopThread == true || !mStatsQueue.empty() || mNewListenerAdded;
+ });
+
+ if (mStopThread) return;
+
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ if (mNewListenerAdded) {
+ // Check if there is any session info available.
+ ECOData sessionInfo = generateLatestSessionInfoEcoData();
+ if (!sessionInfo.isEmpty()) {
+ Status status = mListener->onNewInfo(sessionInfo);
+ if (!status.isOk()) {
+ ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
+ sessionInfo.debugString().c_str());
+ // Remove the listener. The lock has been acquired outside this function.
+ mListener = nullptr;
+ }
+ }
+ mNewListenerAdded = false;
+ }
+
+ if (!mStatsQueue.empty()) {
+ ECOData stats = mStatsQueue.front();
+ mStatsQueue.pop_front();
+ processStats(stats); // TODO: Handle the error from processStats
+ }
+ }
+
+ ECOLOGD("ECOSession: exiting main thread");
+}
+
+bool ECOSession::processStats(const ECOData& stats) {
+ ECOLOGV("%s: receive stats: %s", __FUNCTION__, stats.debugString().c_str());
+
+ if (stats.getDataType() != ECOData::DATA_TYPE_STATS) {
+ ECOLOGE("Invalid stats. ECOData with type: %s", stats.getDataTypeString().c_str());
+ return false;
+ }
+
+ // Get the type of the stats.
+ std::string statsType;
+ if (stats.findString(KEY_STATS_TYPE, &statsType) != ECODataStatus::OK) {
+ ECOLOGE("Invalid stats ECOData without statsType");
+ return false;
+ }
+
+ if (statsType.compare(VALUE_STATS_TYPE_SESSION) == 0) {
+ processSessionStats(stats);
+ } else if (statsType.compare(VALUE_STATS_TYPE_FRAME) == 0) {
+ processFrameStats(stats);
+ } else {
+ ECOLOGE("processStats:: Failed to process stats as ECOData contains unknown stats type");
+ return false;
+ }
+
+ return true;
+}
+
+void ECOSession::processSessionStats(const ECOData& stats) {
+ ECOLOGV("processSessionStats");
+
+ ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
+ info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_SESSION);
+
+ ECODataKeyValueIterator iter(stats);
+ while (iter.hasNext()) {
+ ECOData::ECODataKeyValuePair entry = iter.next();
+ const std::string& key = entry.first;
+ const ECOData::ECODataValueType value = entry.second;
+ ECOLOGV("Processing key: %s", key.c_str());
+ if (!key.compare(KEY_STATS_TYPE)) {
+ // Skip the key KEY_STATS_TYPE as that has been parsed already.
+ continue;
+ } else if (!key.compare(ENCODER_TYPE)) {
+ mCodecType = std::get<int32_t>(value);
+ ECOLOGV("codec type is %d", mCodecType);
+ } else if (!key.compare(ENCODER_PROFILE)) {
+ mCodecProfile = std::get<int32_t>(value);
+ ECOLOGV("codec profile is %d", mCodecProfile);
+ } else if (!key.compare(ENCODER_LEVEL)) {
+ mCodecLevel = std::get<int32_t>(value);
+ ECOLOGV("codec level is %d", mCodecLevel);
+ } else if (!key.compare(ENCODER_TARGET_BITRATE_BPS)) {
+ mTargetBitrateBps = std::get<int32_t>(value);
+ ECOLOGV("codec target bitrate is %d", mTargetBitrateBps);
+ } else if (!key.compare(ENCODER_KFI_FRAMES)) {
+ mKeyFrameIntervalFrames = std::get<int32_t>(value);
+ ECOLOGV("codec kfi is %d", mKeyFrameIntervalFrames);
+ } else if (!key.compare(ENCODER_FRAMERATE_FPS)) {
+ mFramerateFps = std::get<float>(value);
+ ECOLOGV("codec framerate is %f", mFramerateFps);
+ } else if (!key.compare(ENCODER_INPUT_WIDTH)) {
+ int32_t width = std::get<int32_t>(value);
+ if (width != mWidth) {
+ ECOLOGW("Codec width: %d, expected: %d", width, mWidth);
+ }
+ ECOLOGV("codec width is %d", width);
+ } else if (!key.compare(ENCODER_INPUT_HEIGHT)) {
+ int32_t height = std::get<int32_t>(value);
+ if (height != mHeight) {
+ ECOLOGW("Codec height: %d, expected: %d", height, mHeight);
+ }
+ ECOLOGV("codec height is %d", height);
+ } else {
+ ECOLOGW("Unknown session stats key %s from provider.", key.c_str());
+ continue;
+ }
+ info.set(key, value);
+ }
+
+ if (mListener != nullptr) {
+ Status status = mListener->onNewInfo(info);
+ if (!status.isOk()) {
+ ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
+ info.debugString().c_str());
+ // Remove the listener. The lock has been acquired outside this function.
+ mListener = nullptr;
+ }
+ }
+}
+
+ECOData ECOSession::generateLatestSessionInfoEcoData() {
+ bool hasInfo = false;
+
+ ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
+
+ if (mOutputWidth != -1) {
+ info.setInt32(ENCODER_OUTPUT_WIDTH, mOutputWidth);
+ hasInfo = true;
+ }
+
+ if (mOutputHeight != -1) {
+ info.setInt32(ENCODER_OUTPUT_HEIGHT, mOutputHeight);
+ hasInfo = true;
+ }
+
+ if (mCodecType != -1) {
+ info.setInt32(ENCODER_TYPE, mCodecType);
+ hasInfo = true;
+ }
+
+ if (mCodecProfile != -1) {
+ info.setInt32(ENCODER_PROFILE, mCodecProfile);
+ hasInfo = true;
+ }
+
+ if (mCodecLevel != -1) {
+ info.setInt32(ENCODER_LEVEL, mCodecLevel);
+ hasInfo = true;
+ }
+
+ if (mTargetBitrateBps != -1) {
+ info.setInt32(ENCODER_TARGET_BITRATE_BPS, mTargetBitrateBps);
+ hasInfo = true;
+ }
+
+ if (mKeyFrameIntervalFrames != -1) {
+ info.setInt32(ENCODER_KFI_FRAMES, mKeyFrameIntervalFrames);
+ hasInfo = true;
+ }
+
+ if (mFramerateFps > 0) {
+ info.setFloat(ENCODER_FRAMERATE_FPS, mFramerateFps);
+ hasInfo = true;
+ }
+
+ if (hasInfo) {
+ info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_SESSION);
+ }
+ return info;
+}
+
+void ECOSession::processFrameStats(const ECOData& stats) {
+ ECOLOGD("processFrameStats");
+
+ bool needToNotifyListener = false;
+ ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
+ info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_FRAME);
+
+ ECODataKeyValueIterator iter(stats);
+ while (iter.hasNext()) {
+ ECOData::ECODataKeyValuePair entry = iter.next();
+ const std::string& key = entry.first;
+ const ECOData::ECODataValueType value = entry.second;
+ ECOLOGD("Processing %s key", key.c_str());
+
+ if (!key.compare(KEY_STATS_TYPE)) {
+ // Skip the key KEY_STATS_TYPE as that has been parsed already.
+ continue;
+ } else if (!key.compare(FRAME_NUM) || !key.compare(FRAME_PTS_US) ||
+ !key.compare(FRAME_TYPE) || !key.compare(FRAME_SIZE_BYTES) ||
+ !key.compare(ENCODER_ACTUAL_BITRATE_BPS)) {
+ // Only process the keys that are supported by ECOService 1.0.
+ info.set(key, value);
+ } else if (!key.compare(FRAME_AVG_QP)) {
+ // Check the qp to see if need to notify the listener.
+ const int32_t currAverageQp = std::get<int32_t>(value);
+
+ // Check if the delta between current QP and last reported QP is larger than the
+ // threshold specified by the listener.
+ const bool largeQPChangeDetected =
+ abs(currAverageQp - mLastReportedQp) > mListenerQpCondition.mQpChangeThreshold;
+
+ // Check if the qp is going from below threshold to beyond threshold.
+ const bool exceedQpBlockinessThreshold =
+ (mLastReportedQp <= mListenerQpCondition.mQpBlocknessThreshold &&
+ currAverageQp > mListenerQpCondition.mQpBlocknessThreshold);
+
+ // Check if the qp is going from beyond threshold to below threshold.
+ const bool fallBelowQpBlockinessThreshold =
+ (mLastReportedQp > mListenerQpCondition.mQpBlocknessThreshold &&
+ currAverageQp <= mListenerQpCondition.mQpBlocknessThreshold);
+
+ // Notify the listener if any of the above three conditions met.
+ if (largeQPChangeDetected || exceedQpBlockinessThreshold ||
+ fallBelowQpBlockinessThreshold) {
+ mLastReportedQp = currAverageQp;
+ needToNotifyListener = true;
+ }
+
+ info.set(key, value);
+ } else {
+ ECOLOGW("Unknown frame stats key %s from provider.", key.c_str());
+ }
+ }
+
+ if (needToNotifyListener && mListener != nullptr) {
+ Status status = mListener->onNewInfo(info);
+ if (!status.isOk()) {
+ ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
+ info.debugString().c_str());
+ // Remove the listener. The lock has been acquired outside this function.
+ mListener = nullptr;
+ }
+ }
+}
+
+Status ECOSession::getIsCameraRecording(bool* _aidl_return) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ *_aidl_return = mIsCameraRecording;
+ return binder::Status::ok();
+}
+
+Status ECOSession::addStatsProvider(
+ const sp<::android::media::eco::IECOServiceStatsProvider>& provider,
+ const ::android::media::eco::ECOData& config, bool* status) {
+ ::android::String16 name;
+ Status result = provider->getName(&name);
+ if (!result.isOk()) {
+ // This binder transaction failure may due to permission issue.
+ *status = false;
+ ALOGE("Failed to get provider name");
+ return STATUS_ERROR(ERROR_PERMISSION_DENIED, "Failed to get provider name");
+ }
+
+ ECOLOGV("Try to add stats provider name: %s uid: %d pid %d", ::android::String8(name).string(),
+ IPCThreadState::self()->getCallingUid(), IPCThreadState::self()->getCallingPid());
+
+ if (provider == nullptr) {
+ ECOLOGE("%s: provider must not be null", __FUNCTION__);
+ *status = false;
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null provider given to addStatsProvider");
+ }
+
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+
+ if (mProvider != nullptr) {
+ ::android::String16 name;
+ mProvider->getName(&name);
+ String8 errorMsg = String8::format(
+ "ECOService 1.0 only supports one stats provider, current provider: %s",
+ ::android::String8(name).string());
+ ECOLOGE("%s", errorMsg.string());
+ *status = false;
+ return STATUS_ERROR(ERROR_ALREADY_EXISTS, errorMsg.string());
+ }
+
+ // TODO: Handle the provider config.
+ if (config.getDataType() != ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG) {
+ ECOLOGE("Provider config is invalid");
+ *status = false;
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Provider config is invalid");
+ }
+
+ mProvider = provider;
+ mProviderName = name;
+ *status = true;
+ return binder::Status::ok();
+}
+
+Status ECOSession::removeStatsProvider(
+ const sp<::android::media::eco::IECOServiceStatsProvider>& provider, bool* status) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ // Check if the provider is the same as current provider for the session.
+ if (provider.get() != mProvider.get()) {
+ *status = false;
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Provider does not match");
+ }
+
+ mProvider = nullptr;
+ *status = true;
+ return binder::Status::ok();
+}
+
+Status ECOSession::addInfoListener(
+ const sp<::android::media::eco::IECOServiceInfoListener>& listener,
+ const ::android::media::eco::ECOData& config, bool* status) {
+ ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+
+ ::android::String16 name;
+ Status result = listener->getName(&name);
+ if (!result.isOk()) {
+ // This binder transaction failure may due to permission issue.
+ *status = false;
+ ALOGE("Failed to get listener name");
+ return STATUS_ERROR(ERROR_PERMISSION_DENIED, "Failed to get listener name");
+ }
+
+ if (mListener != nullptr) {
+ ECOLOGE("ECOService 1.0 only supports one listener");
+ *status = false;
+ return STATUS_ERROR(ERROR_ALREADY_EXISTS, "ECOService 1.0 only supports one listener");
+ }
+
+ if (listener == nullptr) {
+ ECOLOGE("%s: listener must not be null", __FUNCTION__);
+ *status = false;
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null listener given to addInfoListener");
+ }
+
+ if (config.getDataType() != ECOData::DATA_TYPE_INFO_LISTENER_CONFIG) {
+ *status = false;
+ ECOLOGE("%s: listener config is invalid", __FUNCTION__);
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is invalid");
+ }
+
+ if (config.isEmpty()) {
+ *status = false;
+ ECOLOGE("Listener must provide listening criterion");
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is empty");
+ }
+
+ // For ECOService 1.0, listener must specify the two threshold in order to receive info.
+ if (config.findInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD,
+ &mListenerQpCondition.mQpBlocknessThreshold) != ECODataStatus::OK ||
+ config.findInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD,
+ &mListenerQpCondition.mQpChangeThreshold) != ECODataStatus::OK ||
+ mListenerQpCondition.mQpBlocknessThreshold < ENCODER_MIN_QP ||
+ mListenerQpCondition.mQpBlocknessThreshold > ENCODER_MAX_QP) {
+ *status = false;
+ ECOLOGE("%s: listener config is invalid", __FUNCTION__);
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is not valid");
+ }
+
+ ECOLOGD("Info listener name: %s uid: %d pid %d", ::android::String8(name).string(),
+ IPCThreadState::self()->getCallingUid(), IPCThreadState::self()->getCallingPid());
+
+ mListener = listener;
+ mListenerName = name;
+ mNewListenerAdded = true;
+ mWorkerWaitCV.notify_all();
+
+ *status = true;
+ return binder::Status::ok();
+}
+
+Status ECOSession::removeInfoListener(
+ const sp<::android::media::eco::IECOServiceInfoListener>& listener, bool* _aidl_return) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ // Check if the listener is the same as current listener for the session.
+ if (listener.get() != mListener.get()) {
+ *_aidl_return = false;
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Listener does not match");
+ }
+
+ mListener = nullptr;
+ mNewListenerAdded = false;
+ *_aidl_return = true;
+ return binder::Status::ok();
+}
+
+Status ECOSession::pushNewStats(const ::android::media::eco::ECOData& stats, bool*) {
+ ECOLOGV("ECOSession get new stats type: %s", stats.getDataTypeString().c_str());
+ std::unique_lock<std::mutex> lock(mStatsQueueLock);
+ mStatsQueue.push_back(stats);
+ mWorkerWaitCV.notify_all();
+ return binder::Status::ok();
+}
+
+Status ECOSession::getWidth(int32_t* _aidl_return) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ *_aidl_return = mWidth;
+ return binder::Status::ok();
+}
+
+Status ECOSession::getHeight(int32_t* _aidl_return) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ *_aidl_return = mHeight;
+ return binder::Status::ok();
+}
+
+Status ECOSession::getNumOfListeners(int32_t* _aidl_return) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ *_aidl_return = (mListener == nullptr ? 0 : 1);
+ return binder::Status::ok();
+}
+
+Status ECOSession::getNumOfProviders(int32_t* _aidl_return) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ *_aidl_return = (mProvider == nullptr ? 0 : 1);
+ return binder::Status::ok();
+}
+
+/*virtual*/ void ECOSession::binderDied(const wp<IBinder>& /*who*/) {
+ ECOLOGV("binderDied");
+}
+
+status_t ECOSession::dump(int fd, const Vector<String16>& /*args*/) {
+ std::scoped_lock<std::mutex> lock(mSessionLock);
+ dprintf(fd, "\n== Session Info: ==\n\n");
+ dprintf(fd,
+ "Width: %d Height: %d isCameraRecording: %d, target-bitrate: %d bps codetype: %d "
+ "profile: %d level: %d\n",
+ mWidth, mHeight, mIsCameraRecording, mTargetBitrateBps, mCodecType, mCodecProfile,
+ mCodecLevel);
+ if (mProvider != nullptr) {
+ dprintf(fd, "Provider: %s \n", ::android::String8(mProviderName).string());
+ }
+ if (mListener != nullptr) {
+ dprintf(fd, "Listener: %s \n", ::android::String8(mListenerName).string());
+ }
+ dprintf(fd, "\n===================\n\n");
+
+ return NO_ERROR;
+}
+
+void ECOSession::logStats(const ECOData& data) {
+ // Check if mLogStats is true;
+ if (!mLogStats || mLogStatsEntries == 0) return;
+
+ // Check if we need to remove the old entry.
+ if (mStatsDebugBuffer.size() >= mLogStatsEntries) {
+ mStatsDebugBuffer.pop_front();
+ }
+
+ mStatsDebugBuffer.push_back(data);
+}
+
+void ECOSession::logInfos(const ECOData& data) {
+ // Check if mLogInfo is true;
+ if (!mLogInfo || mLogInfoEntries == 0) return;
+
+ // Check if we need to remove the old entry.
+ if (mInfosDebugBuffer.size() >= mLogInfoEntries) {
+ mInfosDebugBuffer.pop_front();
+ }
+
+ mInfosDebugBuffer.push_back(data);
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/ECOUtils.cpp b/media/eco/ECOUtils.cpp
new file mode 100644
index 0000000..ab82252
--- /dev/null
+++ b/media/eco/ECOUtils.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ECOUtils"
+
+#include "eco/ECOUtils.h"
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace media {
+namespace eco {
+
+// Convert this SimpleEncoderConfig to ECOData with dataType.
+ECOData SimpleEncoderConfig::toEcoData(ECOData::ECODatatype dataType) {
+ ECOData data(dataType, systemTime(SYSTEM_TIME_BOOTTIME));
+ data.setString(KEY_STATS_TYPE, VALUE_STATS_TYPE_SESSION);
+ data.setInt32(ENCODER_TYPE, mCodecType);
+ data.setInt32(ENCODER_PROFILE, mProfile);
+ data.setInt32(ENCODER_LEVEL, mLevel);
+ data.setInt32(ENCODER_TARGET_BITRATE_BPS, mTargetBitrate);
+ data.setInt32(ENCODER_KFI_FRAMES, mKeyFrameIntervalFrames);
+ data.setFloat(ENCODER_FRAMERATE_FPS, mFrameRateFps);
+ return data;
+}
+
+// Convert this SimpleEncodedFrameData to ECOData with dataType.
+ECOData SimpleEncodedFrameData::toEcoData(ECOData::ECODatatype dataType) {
+ ECOData data(dataType, systemTime(SYSTEM_TIME_BOOTTIME));
+ data.setString(KEY_STATS_TYPE, VALUE_STATS_TYPE_FRAME);
+ data.setInt32(FRAME_NUM, mFrameNum);
+ data.setInt8(FRAME_TYPE, mFrameType);
+ data.setInt64(FRAME_PTS_US, mFramePtsUs);
+ data.setInt32(FRAME_AVG_QP, mAvgQp);
+ data.setInt32(FRAME_SIZE_BYTES, mFrameSizeBytes);
+ return data;
+}
+
+bool copyKeyValue(const ECOData& src, ECOData* dst) {
+ if (src.isEmpty() || dst == nullptr) return false;
+ dst->mKeyValueStore = src.mKeyValueStore;
+ return true;
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android \ No newline at end of file
diff --git a/media/eco/OWNERS b/media/eco/OWNERS
new file mode 100644
index 0000000..2239c4f
--- /dev/null
+++ b/media/eco/OWNERS
@@ -0,0 +1,2 @@
+hkuang@google.com
+derekpang@google.com \ No newline at end of file
diff --git a/media/eco/aidl/android/media/eco/ECOData.aidl b/media/eco/aidl/android/media/eco/ECOData.aidl
new file mode 100644
index 0000000..162cd89
--- /dev/null
+++ b/media/eco/aidl/android/media/eco/ECOData.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.eco;
+
+/** @hide */
+parcelable ECOData cpp_header "eco/ECOData.h"; \ No newline at end of file
diff --git a/media/eco/aidl/android/media/eco/IECOService.aidl b/media/eco/aidl/android/media/eco/IECOService.aidl
new file mode 100644
index 0000000..995abad
--- /dev/null
+++ b/media/eco/aidl/android/media/eco/IECOService.aidl
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.eco;
+
+import android.media.eco.ECOData;
+import android.media.eco.IECOServiceStatsProvider;
+import android.media.eco.IECOServiceInfoListener;
+import android.media.eco.IECOSession;
+
+/**
+ * Binder interface for ECO (Encoder Camera Optimization) service.
+ * @hide
+ */
+interface IECOService {
+ /**
+ * All ECO service Binder calls may return a ServiceSpecificException with the following error
+ * codes.
+ */
+ const int ERROR_PERMISSION_DENIED = 1;
+ const int ERROR_ALREADY_EXISTS = 2;
+ const int ERROR_ILLEGAL_ARGUMENT = 3;
+ const int ERROR_DISCONNECTED = 4;
+ const int ERROR_INVALID_OPERATION = 5;
+ const int ERROR_UNSUPPORTED = 6;
+
+ /**
+ * Obtains an IECOSession from the ECO service.
+ *
+ * <p>ECOService will first check if the requested session already existed. If not, it will
+ * create and return the new session. This should be called by IECOServiceStatsProvider or
+ * IECOServiceInfoListener to obtain an ECOSession interface upon start.</p>
+ *
+ * @param width Width of the requested video session (in pixel).
+ * @param height Height of the requested video session (in pixel).
+ * @param isCameraRecording A boolean indicates whether the session is for camera recording.
+ *
+ * @return IECOSession The session instance created by the ECOService.
+ */
+ IECOSession obtainSession(int width, int height, boolean isCameraRecording);
+
+ /**
+ * Return the total number of sessions inside ECO service.
+ */
+ int getNumOfSessions();
+
+ /**
+ * Return a list containing all ECOSessions.
+ */
+ List<IBinder> getSessions();
+}
diff --git a/media/eco/aidl/android/media/eco/IECOServiceInfoListener.aidl b/media/eco/aidl/android/media/eco/IECOServiceInfoListener.aidl
new file mode 100644
index 0000000..bf5bd88
--- /dev/null
+++ b/media/eco/aidl/android/media/eco/IECOServiceInfoListener.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.eco;
+
+import android.media.eco.ECOData;
+import android.os.IBinder;
+
+/**
+ * Binder interface for ECO service information listener.
+*
+* {@hide}
+*/
+interface IECOServiceInfoListener {
+ /**
+ * All listener Binder calls may return a ServiceSpecificException with the following error
+ * codes.
+ */
+ const int ERROR_PERMISSION_DENIED = 1;
+ const int ERROR_ILLEGAL_ARGUMENT = 2;
+ const int ERROR_INVALID_OPERATION = 3;
+ const int ERROR_UNSUPPORTED = 4;
+
+ /**
+ * Constants for the type of the listener.
+ */
+ const int INFO_LISTENER_TYPE_UNKNOWN = 1;
+ const int INFO_LISTENER_TYPE_VIDEO_ENCODER = 2;
+ const int INFO_LISTENER_TYPE_CAMERA = 3;
+
+ /**
+ * Return the type of the listener.
+ */
+ int getType();
+
+ /**
+ * Return the name of the listener.
+ */
+ String getName();
+
+ /**
+ * Return the IBinder instance of the ECOSession associated the provider.
+ */
+ IBinder getECOSession();
+
+ /**
+ * Handle the new info from ECOSession. This should only be called by ECOSession.
+ */
+ oneway void onNewInfo(in ECOData newInfo);
+} \ No newline at end of file
diff --git a/media/eco/aidl/android/media/eco/IECOServiceStatsProvider.aidl b/media/eco/aidl/android/media/eco/IECOServiceStatsProvider.aidl
new file mode 100644
index 0000000..6e2f9c3
--- /dev/null
+++ b/media/eco/aidl/android/media/eco/IECOServiceStatsProvider.aidl
@@ -0,0 +1,56 @@
+/*
+* Copyright (C) 2019 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.media.eco;
+
+import android.os.IBinder;
+
+/**
+* An interface for providers that provides various statistics to ECO service.
+* {@hide}
+*/
+interface IECOServiceStatsProvider {
+ /**
+ * All provider Binder calls may return a ServiceSpecificException with the following error
+ * codes.
+ */
+ const int ERROR_PERMISSION_DENIED = 1;
+ const int ERROR_ILLEGAL_ARGUMENT = 2;
+ const int ERROR_INVALID_OPERATION = 3;
+ const int ERROR_UNSUPPORTED = 4;
+
+ /**
+ * Constants for the type of the provider.
+ */
+ const int STATS_PROVIDER_TYPE_UNKNOWN = 1;
+ const int STATS_PROVIDER_TYPE_VIDEO_ENCODER = 2;
+ const int STATS_PROVIDER_TYPE_CAMERA = 3;
+
+ /**
+ * Return the type of the provider.
+ */
+ int getType();
+
+ /**
+ * Return the name of the provider.
+ */
+ String getName();
+
+ /**
+ * Return the IBinder instance of the ECOSession associated the listener.
+ */
+ IBinder getECOSession();
+}
diff --git a/media/eco/aidl/android/media/eco/IECOSession.aidl b/media/eco/aidl/android/media/eco/IECOSession.aidl
new file mode 100644
index 0000000..e4c1136
--- /dev/null
+++ b/media/eco/aidl/android/media/eco/IECOSession.aidl
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.eco;
+
+import android.media.eco.ECOData;
+import android.media.eco.IECOServiceStatsProvider;
+import android.media.eco.IECOServiceInfoListener;
+
+/**
+ * Binder interface for ECO Session.
+ * @hide
+ */
+interface IECOSession {
+ /**
+ * All ECO session Binder calls may return a ServiceSpecificException with the following error
+ * codes.
+ */
+ const int ERROR_PERMISSION_DENIED = 1;
+ const int ERROR_ALREADY_EXISTS = 2;
+ const int ERROR_ILLEGAL_ARGUMENT = 3;
+ const int ERROR_DISCONNECTED = 4;
+ const int ERROR_INVALID_OPERATION = 5;
+ const int ERROR_UNSUPPORTED = 6;
+
+ /**
+ * Adds an ECOServiceStasProvider.
+ *
+ * <p>This is called by ECOServiceStasProvider to add itself to the ECOSession.</p>
+ *
+ * @param provider Provider that implements IECOServiceStatsProvider interface.
+ * @param config Config that specifies the types of the stats that the provider is going to
+ * provide. The ECOData must be of type DATA_TYPE_STATS_PROVIDER_CONFIG. Note:
+ * The provider must specify the following keys when adding itself:
+ * KEY_PROVIDER_NAME in string and KEY_PROVIDER_TYPE as specified in
+ * IECOServiceStatsProvider.
+ *
+ * @return true if the provider was successfully added, false otherwise.
+ */
+ boolean addStatsProvider(IECOServiceStatsProvider provider, in ECOData config);
+
+ /**
+ * Removes an ECOServiceStasProvider from ECOSession.
+ *
+ * @param provider Provider that implements IECOServiceStatsProvider interface.
+ *
+ * @return true if the provider was successfully removed, false otherwise.
+ */
+ boolean removeStatsProvider(IECOServiceStatsProvider provider);
+
+ /**
+ * Registers an ECOServiceInfoListener.
+ *
+ * <p>This is called by IECOServiceInfoListener to add itself to the ECOSession.</p>
+ *
+ * @param listener Listener that implements IECOServiceInfoListener interface.
+ * @param config Config that specifies the condition for the data that the listener is
+ * interested in.
+ *
+ * @return true if the listener was successfully added, false otherwise.
+ */
+ boolean addInfoListener(IECOServiceInfoListener listener, in ECOData config);
+
+ /**
+ * Removes an ECOServiceInfoListener from ECOSession.
+ *
+ * @param listener Listener that implements IECOServiceInfoListener interface.
+ *
+ * @return true if the listener was successfully removed, false otherwise.
+ */
+ boolean removeInfoListener(IECOServiceInfoListener listener);
+
+ /**
+ * Push new stats to the ECOSession. This should only be called by IECOServiceStatsProvider.
+ */
+ boolean pushNewStats(in ECOData newStats);
+
+ /**
+ * Return the width in pixels.
+ */
+ int getWidth();
+
+ /**
+ * Return the height in pixels.
+ */
+ int getHeight();
+
+ /**
+ * Return whether the session is for camera recording.
+ */
+ boolean getIsCameraRecording();
+
+ /**
+ * Query the number of listeners that a session has.
+ */
+ int getNumOfListeners();
+
+ /**
+ * Query the number of providers that a session has.
+ */
+ int getNumOfProviders();
+}
diff --git a/media/eco/include/eco/ECOData.h b/media/eco/include/eco/ECOData.h
new file mode 100644
index 0000000..cb42982
--- /dev/null
+++ b/media/eco/include/eco/ECOData.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_ECO_DATA_H_
+#define ANDROID_MEDIA_ECO_DATA_H_
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <string>
+#include <unordered_map>
+#include <variant>
+
+namespace android {
+namespace media {
+namespace eco {
+
+enum class ECODataStatus {
+ OK,
+ FAIL_TO_INSERT,
+ INVALID_ECODATA_TYPE,
+ KEY_NOT_EXIST,
+ INVALID_VALUE_TYPE,
+ INVALID_ARGUMENT,
+};
+
+/**
+* ECOData is the container for all messages passed between different components in ECOService.
+* All messages in ECOServices are represented by a list of key-value pairs.
+* For example:
+* "bit-rate" -> 22000000
+* "Provider-Name" -> "QCOM-Video-Encoder".
+* "avg-frame-qp" -> 40
+* ECOData follows the same design pattern of AMessage and Metadata in Media Framework. The key
+* must be non-empty string. Below are the supported data types:
+*
+* // Item types set/find function suffixes
+* // ==========================================
+* // int32_t Int32
+* // int64_t Int64
+* // size_t Size
+* // float Float
+* // double Double
+* // String String
+*
+* ECOData does not support duplicate keys with different values. When inserting a key-value pair,
+* a new entry will be created if the key does not exist. Othewise, they key's value will be
+* overwritten with the new value.
+*
+* Sample usage:
+*
+* // Create the ECOData
+* std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+*
+* // Set the encoder name.
+* data->setString("stats-encoder-type", "google-avc");
+*
+* // Set encoding bitrate.
+* data->setInt32("stats-encoder-target-bitrate-bps", 22000000);
+*/
+class ECOData : public Parcelable {
+public:
+ using ECODataValueType =
+ std::variant<int32_t, int64_t, size_t, float, double, std::string, int8_t>;
+ using ECODataKeyValuePair = std::pair<std::string, ECODataValueType>;
+
+ ECOData() : mDataType(0), mDataTimeUs(-1) {}
+ ECOData(int32_t type) : mDataType(type), mDataTimeUs(-1) {}
+ ECOData(int32_t type, int64_t timeUs) : mDataType(type), mDataTimeUs(timeUs) {}
+
+ // Constants for mDataType.
+ typedef enum {
+ DATA_TYPE_UNKNOWN = 0,
+ /* Data sent from the ECOServiceStatsProvider to ECOService. */
+ DATA_TYPE_STATS = 1,
+ /* Data sent from the ECOService to ECOServiceInfoListener. */
+ DATA_TYPE_INFO = 2,
+ /* Configuration data sent by ECOServiceStatsProvider when connects with ECOService. */
+ DATA_TYPE_STATS_PROVIDER_CONFIG = 3,
+ /* Configuration data sent by ECOServiceInfoListener when connects with ECOService. */
+ DATA_TYPE_INFO_LISTENER_CONFIG = 4,
+ } ECODatatype;
+
+ // set/find functions that could be used for all the value types.
+ ECODataStatus set(const std::string& key, const ECODataValueType& value);
+ ECODataStatus find(const std::string& key, ECODataValueType* out) const;
+
+ // Convenient set/find functions for string value type.
+ ECODataStatus setString(const std::string& key, const std::string& value);
+ ECODataStatus findString(const std::string& key, std::string* out) const;
+
+ // Convenient set/find functions for int32_t value type.
+ ECODataStatus setInt32(const std::string& key, int32_t value);
+ ECODataStatus findInt32(const std::string& key, int32_t* out) const;
+
+ // Convenient set/find functions for int64_t value type.
+ ECODataStatus setInt64(const std::string& key, int64_t value);
+ ECODataStatus findInt64(const std::string& key, int64_t* out) const;
+
+ // Convenient set/find functions for float value type.
+ ECODataStatus setFloat(const std::string& key, float value);
+ ECODataStatus findFloat(const std::string& key, float* out) const;
+
+ // Convenient set/find functions for double value type.
+ ECODataStatus setDouble(const std::string& key, double value);
+ ECODataStatus findDouble(const std::string& key, double* out) const;
+
+ // Convenient set/find functions for size_t value type.
+ ECODataStatus setSize(const std::string& key, size_t value);
+ ECODataStatus findSize(const std::string& key, size_t* out) const;
+
+ // Convenient set/find functions for int8_t value type.
+ // TODO(hkuang): Add unit test.
+ ECODataStatus setInt8(const std::string& key, int8_t value);
+ ECODataStatus findInt8(const std::string& key, int8_t* out) const;
+
+ /**
+ * Serialization over Binder
+ */
+ status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcel(Parcel* parcel) const override;
+
+ /* Returns the type of the data. */
+ int32_t getDataType() const;
+
+ /* Returns the type of the data in string. */
+ std::string getDataTypeString() const;
+
+ /* Returns the timestamp associated with the data. */
+ int64_t getDataTimeUs() const;
+
+ /* Sets the type of the data. */
+ void setDataType(int32_t type);
+
+ /* Sets the timestamp associated with the data. */
+ void setDataTimeUs();
+
+ /* Gets the number of keys in the ECOData. */
+ size_t getNumOfEntries() const { return mKeyValueStore.size(); }
+
+ /* Whether the ECOData is empty. */
+ size_t isEmpty() const { return mKeyValueStore.size() == 0; }
+
+ friend class ECODataKeyValueIterator;
+
+ friend bool copyKeyValue(const ECOData& src, ECOData* dst);
+
+ // Dump the ECOData as a string.
+ std::string debugString() const;
+
+protected:
+ // ValueType. This must match the index in ECODataValueType.
+ enum ValueType {
+ kTypeInt32 = 0,
+ kTypeInt64 = 1,
+ kTypeSize = 2,
+ kTypeFloat = 3,
+ kTypeDouble = 4,
+ kTypeString = 5,
+ kTypeInt8 = 6,
+ };
+
+ /* The type of the data */
+ int32_t mDataType;
+
+ // The timestamp time associated with the data in microseconds. The timestamp should be in
+ // boottime time base. This is only used when the data type is stats or info. -1 means
+ // unavailable.
+ int64_t mDataTimeUs;
+
+ // Internal store for the key value pairs.
+ std::unordered_map<std::string, ECODataValueType> mKeyValueStore;
+
+ template <typename T>
+ ECODataStatus setValue(const std::string& key, T value);
+
+ template <typename T>
+ ECODataStatus findValue(const std::string& key, T* out) const;
+};
+
+// A simple ECOData iterator that will iterate over all the key value paris in ECOData.
+// To be used like:
+// while (it.hasNext()) {
+// entry = it.next();
+// }
+class ECODataKeyValueIterator {
+public:
+ ECODataKeyValueIterator(const ECOData& data)
+ : mKeyValueStore(data.mKeyValueStore), mBeginReturned(false) {
+ mIterator = mKeyValueStore.begin();
+ }
+ ~ECODataKeyValueIterator() = default;
+ bool hasNext();
+ ECOData::ECODataKeyValuePair next() const;
+
+private:
+ const std::unordered_map<std::string, ECOData::ECODataValueType>& mKeyValueStore;
+ std::unordered_map<std::string, ECOData::ECODataValueType>::const_iterator mIterator;
+ bool mBeginReturned;
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_DATA_H_
diff --git a/media/eco/include/eco/ECODataKey.h b/media/eco/include/eco/ECODataKey.h
new file mode 100644
index 0000000..c70c328
--- /dev/null
+++ b/media/eco/include/eco/ECODataKey.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_MEDIA_ECO_DATA_KEY_H_
+#define ANDROID_MEDIA_ECO_DATA_KEY_H_
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+namespace android {
+namespace media {
+namespace eco {
+
+// ================================================================================================
+// Standard ECOData keys.
+// ================================================================================================
+constexpr char KEY_ECO_DATA_TYPE[] = "eco-data-type";
+constexpr char KEY_ECO_DATA_TIME_US[] = "eco-data-time-us";
+
+// ================================================================================================
+// Standard ECOServiceStatsProvider config keys. These keys are used in the ECOData as an config
+// when StatsProvider connects with ECOService.
+// ================================================================================================
+constexpr char KEY_PROVIDER_NAME[] = "provider-name";
+constexpr char KEY_PROVIDER_TYPE[] = "provider-type";
+
+// ================================================================================================
+// Standard ECOServiceInfoListener config keys. These keys are used in the ECOData as config
+// when ECOServiceInfoListener connects with ECOService to specify the informations that the
+// listener wants to listen to.
+// ================================================================================================
+constexpr char KEY_LISTENER_NAME[] = "listener-name";
+constexpr char KEY_LISTENER_TYPE[] = "listener-type";
+
+// Following two keys are used together for the listener to specify the condition when it wants to
+// receive notification. When a frame's avg-qp crosses KEY_LISTENER_QP_BLOCKINESS_THRESHOLD or
+// the detla of qp between current frame and previous frame also goes beyond
+// KEY_LISTENER_QP_CHANGE_THRESHOLD, ECOService will notify the listener.
+constexpr char KEY_LISTENER_QP_BLOCKINESS_THRESHOLD[] = "listener-qp-blockness-threshold";
+constexpr char KEY_LISTENER_QP_CHANGE_THRESHOLD[] = "listener-qp-change-threshold";
+
+// ================================================================================================
+// ECOService Stats keys. These key MUST BE specified when provider pushes the stats to ECOService
+// to indicate the stats is session stats or frame stats.
+// ================================================================================================
+constexpr char KEY_STATS_TYPE[] = "stats-type";
+constexpr char VALUE_STATS_TYPE_SESSION[] = "stats-type-session"; // value for KEY_STATS_TYPE.
+constexpr char VALUE_STATS_TYPE_FRAME[] = "stats-type-frame"; // value for KEY_STATS_TYPE.
+
+// ================================================================================================
+// Standard ECOService Info keys. These key will be in the info provided by ECOService to indicate
+// the info is session info or frame info.
+// ================================================================================================
+constexpr char KEY_INFO_TYPE[] = "info-type";
+constexpr char VALUE_INFO_TYPE_SESSION[] = "info-type-session"; // value for KEY_INFO_TYPE.
+constexpr char VALUE_INFO_TYPE_FRAME[] = "info-type-frame"; // value for KEY_INFO_TYPE.
+
+// ================================================================================================
+// General keys to be used by both stats and info in the ECOData.
+// ================================================================================================
+constexpr char ENCODER_NAME[] = "encoder-name";
+constexpr char ENCODER_TYPE[] = "encoder-type";
+constexpr char ENCODER_PROFILE[] = "encoder-profile";
+constexpr char ENCODER_LEVEL[] = "encoder-level";
+constexpr char ENCODER_INPUT_WIDTH[] = "encoder-input-width";
+constexpr char ENCODER_INPUT_HEIGHT[] = "encoder-input-height";
+constexpr char ENCODER_OUTPUT_WIDTH[] = "encoder-output-width";
+constexpr char ENCODER_OUTPUT_HEIGHT[] = "encoder-output-height";
+constexpr char ENCODER_TARGET_BITRATE_BPS[] = "encoder-target-bitrate-bps"; // Session info
+constexpr char ENCODER_ACTUAL_BITRATE_BPS[] = "encoder-actual-bitrate-bps"; // Frame info
+constexpr char ENCODER_KFI_FRAMES[] = "encoder-kfi-frames";
+constexpr char ENCODER_FRAMERATE_FPS[] = "encoder-framerate-fps";
+
+constexpr char FRAME_NUM[] = "frame-num";
+constexpr char FRAME_PTS_US[] = "frame-pts-us";
+constexpr char FRAME_AVG_QP[] = "frame-avg-qp";
+constexpr char FRAME_TYPE[] = "frame-type";
+constexpr char FRAME_SIZE_BYTES[] = "frame-size-bytes";
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_DATA_KEY_H_
diff --git a/media/eco/include/eco/ECODebug.h b/media/eco/include/eco/ECODebug.h
new file mode 100644
index 0000000..b250f64
--- /dev/null
+++ b/media/eco/include/eco/ECODebug.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_ECO_DEBUG_H_
+#define ANDROID_MEDIA_ECO_DEBUG_H_
+
+#include <cutils/atomic.h>
+#include <cutils/properties.h>
+
+#include "ECOData.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+static const char* kDebugLogsLevelProperty = "media.ecoservice.log.level";
+static const char* kDebugLogStats = "media.ecoservice.log.stats";
+static const char* kDebugLogStatsSize = "media.ecoservice.log.stats.size";
+static const char* kDebugLogInfos = "media.ecoservice.log.info";
+static const char* kDebugLogInfosSize = "media.ecoservice.log.info.size";
+
+// A debug variable that should only be accessed by ECOService through updateLogLevel. It is rare
+// that this variable will have race condition. But if so, it is ok as this is just for debugging.
+extern uint32_t gECOLogLevel;
+
+enum ECOLogLevel : uint32_t {
+ ECO_MSGLEVEL_DEBUG = 0x01, ///< debug logs
+ ECO_MSGLEVEL_VERBOSE = 0x02, ///< very detailed logs
+ ECO_MSGLEVEL_ALL = 0x03, ///< both debug logs and detailed logs
+};
+
+#define ECOLOGV(format, args...) ALOGD_IF((gECOLogLevel & ECO_MSGLEVEL_VERBOSE), format, ##args)
+#define ECOLOGD(format, args...) ALOGD_IF((gECOLogLevel & ECO_MSGLEVEL_DEBUG), format, ##args)
+#define ECOLOGI(format, args...) ALOGI(format, ##args)
+#define ECOLOGW(format, args...) ALOGW(format, ##args)
+#define ECOLOGE(format, args...) ALOGE(format, ##args)
+
+void updateLogLevel();
+
+// Convenience methods for constructing binder::Status objects for error returns
+#define STATUS_ERROR(errorCode, errorString) \
+ binder::Status::fromServiceSpecificError( \
+ errorCode, String8::format("%s:%d: %s", __FUNCTION__, __LINE__, errorString))
+
+#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
+ binder::Status::fromServiceSpecificError( \
+ errorCode, \
+ String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, __VA_ARGS__))
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_DEBUG_H_
diff --git a/media/eco/include/eco/ECOService.h b/media/eco/include/eco/ECOService.h
new file mode 100644
index 0000000..f83edff
--- /dev/null
+++ b/media/eco/include/eco/ECOService.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_ECO_SERVICE_H_
+#define ANDROID_MEDIA_ECO_SERVICE_H_
+
+#include <android/media/eco/BnECOService.h>
+#include <binder/BinderService.h>
+
+#include <list>
+
+#include "eco/ECODebug.h"
+#include "eco/ECOSession.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using android::sp;
+using android::binder::Status;
+using android::media::eco::ECOSession;
+
+/**
+ * ECO (Encoder Camera Optimization) service.
+ *
+ * ECOService creates and manages EcoSession to relay feedback information between one or multiple
+ * ECOServiceStatsProvider and ECOServiceInfoListener. The relationship can be many-to-many. In
+ * general, ECOServiceStatsProvider extracts information from an encoder for a given encoding
+ * session. EcoSession then relays the encoder information to any subscribed
+ * ECOServiceInfoListener.
+ *
+ * Internally, ECOService creates an ECOSession for each encoding session. Upon start, both
+ * ECOServiceStatsProvider and ECOServiceInfoListener should call obtainSession to get the
+ * ECOSession instance. After that, ECOServiceStatsProvider should push Stats to ECOSession and
+ * ECOServiceInfoListener should listen to the info from ECOSession. Upon finish, both
+ * ECOServiceStatsProvider and ECOServiceInfoListener should remove themselves from ECOSession.
+ * Then ECOService will safely destroy the ECOSession.
+ */
+class ECOService : public BinderService<ECOService>,
+ public BnECOService,
+ public virtual IBinder::DeathRecipient {
+ friend class BinderService<ECOService>;
+
+public:
+ ECOService();
+
+ virtual ~ECOService() = default;
+
+ virtual Status obtainSession(int32_t width, int32_t height, bool isCameraRecording,
+ sp<IECOSession>* _aidl_return);
+
+ virtual Status getNumOfSessions(int32_t* _aidl_return);
+
+ virtual Status getSessions(::std::vector<sp<IBinder>>* _aidl_return);
+
+ // Implementation of BinderService<T>
+ static char const* getServiceName() { return "media.ecoservice"; }
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+private:
+ // Lock guarding ECO service state
+ Mutex mServiceLock;
+
+ struct SessionConfig {
+ int32_t mWidth;
+ int32_t mHeight;
+ bool mIsCameraRecording;
+
+ SessionConfig(int w, int h, bool isCameraRecording)
+ : mWidth(w), mHeight(h), mIsCameraRecording(isCameraRecording) {}
+
+ bool operator==(const SessionConfig& cfg) {
+ return mWidth == cfg.mWidth && mHeight == cfg.mHeight &&
+ mIsCameraRecording == cfg.mIsCameraRecording;
+ }
+ };
+
+ friend bool operator==(const SessionConfig& p1, const SessionConfig& p2) {
+ return p1.mWidth == p2.mWidth && p1.mHeight == p2.mHeight &&
+ p1.mIsCameraRecording == p2.mIsCameraRecording;
+ }
+
+ // Hash function for mSessionConfigToSessionMap.
+ // TODO(hkuang): Add test for this hash function.
+ struct SessionConfigHash {
+ size_t operator()(const SessionConfig& cfg) const {
+ // Generate a hash by bit shifting and concatenation.
+ return cfg.mWidth | (cfg.mHeight << 16) | ((int32_t)cfg.mIsCameraRecording << 31);
+ }
+ };
+
+ // Map from SessionConfig to session.
+ std::unordered_map<SessionConfig, wp<ECOSession>, SessionConfigHash> mSessionConfigToSessionMap;
+
+ using MapIterType =
+ std::unordered_map<SessionConfig, wp<ECOSession>, SessionConfigHash>::iterator;
+
+ // A helpful function to traverse the mSessionConfigToSessionMap, remove the entry that
+ // does not exist any more and call |callback| when the entry is valid.
+ void SanitizeSession(const std::function<void(MapIterType it)>& callback);
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_SERVICE_H_
diff --git a/media/eco/include/eco/ECOServiceConstants.h b/media/eco/include/eco/ECOServiceConstants.h
new file mode 100644
index 0000000..5d753d0
--- /dev/null
+++ b/media/eco/include/eco/ECOServiceConstants.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_MEDIA_ECO_SERVICE_CONSTANTS_H_
+#define ANDROID_MEDIA_ECO_SERVICE_CONSTANTS_H_
+
+#include <stdint.h>
+#include <sys/mman.h>
+
+namespace android {
+namespace media {
+namespace eco {
+
+// Codec type.
+constexpr int32_t CodecTypeUnknown = 0x00;
+constexpr int32_t CodecTypeAVC = 0x01;
+constexpr int32_t CodecTypeHEVC = 0x02;
+
+// Encoded frame type.
+constexpr int32_t FrameTypeUnknown = 0x0;
+constexpr int32_t FrameTypeI = 0x01;
+constexpr int32_t FrameTypeP = 0x02;
+constexpr int32_t FrameTypeB = 0x04;
+
+// Below constants are borrowed from
+// frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+
+// from MediaCodecInfo.java
+
+// Profile types:
+constexpr int32_t AVCProfileBaseline = 0x01;
+constexpr int32_t AVCProfileMain = 0x02;
+constexpr int32_t AVCProfileExtended = 0x04;
+constexpr int32_t AVCProfileHigh = 0x08;
+constexpr int32_t AVCProfileHigh10 = 0x10;
+constexpr int32_t AVCProfileHigh422 = 0x20;
+constexpr int32_t AVCProfileHigh444 = 0x40;
+constexpr int32_t AVCProfileConstrainedBaseline = 0x10000;
+constexpr int32_t AVCProfileConstrainedHigh = 0x80000;
+
+constexpr int32_t HEVCProfileMain = 0x01;
+constexpr int32_t HEVCProfileMain10 = 0x02;
+constexpr int32_t HEVCProfileMainStill = 0x04;
+constexpr int32_t HEVCProfileMain10HDR10 = 0x1000;
+constexpr int32_t HEVCProfileMain10HDR10Plus = 0x2000;
+
+// Level types:
+constexpr int32_t AVCLevel1 = 0x01;
+constexpr int32_t AVCLevel1b = 0x02;
+constexpr int32_t AVCLevel11 = 0x04;
+constexpr int32_t AVCLevel12 = 0x08;
+constexpr int32_t AVCLevel13 = 0x10;
+constexpr int32_t AVCLevel2 = 0x20;
+constexpr int32_t AVCLevel21 = 0x40;
+constexpr int32_t AVCLevel22 = 0x80;
+constexpr int32_t AVCLevel3 = 0x100;
+constexpr int32_t AVCLevel31 = 0x200;
+constexpr int32_t AVCLevel32 = 0x400;
+constexpr int32_t AVCLevel4 = 0x800;
+constexpr int32_t AVCLevel41 = 0x1000;
+constexpr int32_t AVCLevel42 = 0x2000;
+constexpr int32_t AVCLevel5 = 0x4000;
+constexpr int32_t AVCLevel51 = 0x8000;
+constexpr int32_t AVCLevel52 = 0x10000;
+constexpr int32_t AVCLevel6 = 0x20000;
+constexpr int32_t AVCLevel61 = 0x40000;
+constexpr int32_t AVCLevel62 = 0x80000;
+
+constexpr int32_t HEVCMainTierLevel1 = 0x1;
+constexpr int32_t HEVCHighTierLevel1 = 0x2;
+constexpr int32_t HEVCMainTierLevel2 = 0x4;
+constexpr int32_t HEVCHighTierLevel2 = 0x8;
+constexpr int32_t HEVCMainTierLevel21 = 0x10;
+constexpr int32_t HEVCHighTierLevel21 = 0x20;
+constexpr int32_t HEVCMainTierLevel3 = 0x40;
+constexpr int32_t HEVCHighTierLevel3 = 0x80;
+constexpr int32_t HEVCMainTierLevel31 = 0x100;
+constexpr int32_t HEVCHighTierLevel31 = 0x200;
+constexpr int32_t HEVCMainTierLevel4 = 0x400;
+constexpr int32_t HEVCHighTierLevel4 = 0x800;
+constexpr int32_t HEVCMainTierLevel41 = 0x1000;
+constexpr int32_t HEVCHighTierLevel41 = 0x2000;
+constexpr int32_t HEVCMainTierLevel5 = 0x4000;
+constexpr int32_t HEVCHighTierLevel5 = 0x8000;
+constexpr int32_t HEVCMainTierLevel51 = 0x10000;
+constexpr int32_t HEVCHighTierLevel51 = 0x20000;
+constexpr int32_t HEVCMainTierLevel52 = 0x40000;
+constexpr int32_t HEVCHighTierLevel52 = 0x80000;
+constexpr int32_t HEVCMainTierLevel6 = 0x100000;
+constexpr int32_t HEVCHighTierLevel6 = 0x200000;
+constexpr int32_t HEVCMainTierLevel61 = 0x400000;
+constexpr int32_t HEVCHighTierLevel61 = 0x800000;
+constexpr int32_t HEVCMainTierLevel62 = 0x1000000;
+constexpr int32_t HEVCHighTierLevel62 = 0x2000000;
+
+inline static const char* asString_AVCProfile(int32_t i, const char* def = "??") {
+ switch (i) {
+ case AVCProfileBaseline:
+ return "Baseline";
+ case AVCProfileMain:
+ return "Main";
+ case AVCProfileExtended:
+ return "Extended";
+ case AVCProfileHigh:
+ return "High";
+ case AVCProfileHigh10:
+ return "High10";
+ case AVCProfileHigh422:
+ return "High422";
+ case AVCProfileHigh444:
+ return "High444";
+ case AVCProfileConstrainedBaseline:
+ return "ConstrainedBaseline";
+ case AVCProfileConstrainedHigh:
+ return "ConstrainedHigh";
+ default:
+ return def;
+ }
+}
+
+inline static const char* asString_AVCLevel(int32_t i, const char* def = "??") {
+ switch (i) {
+ case AVCLevel1:
+ return "1";
+ case AVCLevel1b:
+ return "1b";
+ case AVCLevel11:
+ return "1.1";
+ case AVCLevel12:
+ return "1.2";
+ case AVCLevel13:
+ return "1.3";
+ case AVCLevel2:
+ return "2";
+ case AVCLevel21:
+ return "2.1";
+ case AVCLevel22:
+ return "2.2";
+ case AVCLevel3:
+ return "3";
+ case AVCLevel31:
+ return "3.1";
+ case AVCLevel32:
+ return "3.2";
+ case AVCLevel4:
+ return "4";
+ case AVCLevel41:
+ return "4.1";
+ case AVCLevel42:
+ return "4.2";
+ case AVCLevel5:
+ return "5";
+ case AVCLevel51:
+ return "5.1";
+ case AVCLevel52:
+ return "5.2";
+ case AVCLevel6:
+ return "6";
+ case AVCLevel61:
+ return "6.1";
+ case AVCLevel62:
+ return "6.2";
+ default:
+ return def;
+ }
+}
+
+inline static const char* asString_HEVCProfile(int32_t i, const char* def = "??") {
+ switch (i) {
+ case HEVCProfileMain:
+ return "Main";
+ case HEVCProfileMain10:
+ return "Main10";
+ case HEVCProfileMainStill:
+ return "MainStill";
+ case HEVCProfileMain10HDR10:
+ return "Main10HDR10";
+ case HEVCProfileMain10HDR10Plus:
+ return "Main10HDR10Plus";
+ default:
+ return def;
+ }
+}
+
+inline static const char* asString_HEVCTierLevel(int32_t i, const char* def = "??") {
+ switch (i) {
+ case HEVCMainTierLevel1:
+ return "Main 1";
+ case HEVCHighTierLevel1:
+ return "High 1";
+ case HEVCMainTierLevel2:
+ return "Main 2";
+ case HEVCHighTierLevel2:
+ return "High 2";
+ case HEVCMainTierLevel21:
+ return "Main 2.1";
+ case HEVCHighTierLevel21:
+ return "High 2.1";
+ case HEVCMainTierLevel3:
+ return "Main 3";
+ case HEVCHighTierLevel3:
+ return "High 3";
+ case HEVCMainTierLevel31:
+ return "Main 3.1";
+ case HEVCHighTierLevel31:
+ return "High 3.1";
+ case HEVCMainTierLevel4:
+ return "Main 4";
+ case HEVCHighTierLevel4:
+ return "High 4";
+ case HEVCMainTierLevel41:
+ return "Main 4.1";
+ case HEVCHighTierLevel41:
+ return "High 4.1";
+ case HEVCMainTierLevel5:
+ return "Main 5";
+ case HEVCHighTierLevel5:
+ return "High 5";
+ case HEVCMainTierLevel51:
+ return "Main 5.1";
+ case HEVCHighTierLevel51:
+ return "High 5.1";
+ case HEVCMainTierLevel52:
+ return "Main 5.2";
+ case HEVCHighTierLevel52:
+ return "High 5.2";
+ case HEVCMainTierLevel6:
+ return "Main 6";
+ case HEVCHighTierLevel6:
+ return "High 6";
+ case HEVCMainTierLevel61:
+ return "Main 6.1";
+ case HEVCHighTierLevel61:
+ return "High 6.1";
+ case HEVCMainTierLevel62:
+ return "Main 6.2";
+ case HEVCHighTierLevel62:
+ return "High 6.2";
+ default:
+ return def;
+ }
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_SERVICE_CONSTANTS_H_
diff --git a/media/eco/include/eco/ECOServiceInfoListener.h b/media/eco/include/eco/ECOServiceInfoListener.h
new file mode 100644
index 0000000..8a08a69
--- /dev/null
+++ b/media/eco/include/eco/ECOServiceInfoListener.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_ECO_SERVICE_INFO_LISTENER_H_
+#define ANDROID_MEDIA_ECO_SERVICE_INFO_LISTENER_H_
+
+#include <android/media/eco/BnECOServiceInfoListener.h>
+#include <android/media/eco/IECOSession.h>
+#include <binder/BinderService.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "ECOData.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using ::android::binder::Status;
+
+/**
+ * ECOServiceInfoListener interface class.
+ */
+class ECOServiceInfoListener : public BinderService<IECOServiceInfoListener>,
+ public BnECOServiceInfoListener,
+ public virtual IBinder::DeathRecipient {
+ friend class BinderService<IECOServiceInfoListener>;
+
+public:
+ // Create a ECOServiceInfoListener with specifed width, height and isCameraRecording.
+ ECOServiceInfoListener(int32_t width, int32_t height, bool isCameraRecording);
+
+ virtual ~ECOServiceInfoListener() {}
+
+ virtual Status getType(int32_t* _aidl_return) = 0;
+ virtual Status getName(::android::String16* _aidl_return) = 0;
+ virtual Status getECOSession(::android::sp<::android::IBinder>* _aidl_return) = 0;
+ virtual Status onNewInfo(const ::android::media::eco::ECOData& newInfo) = 0;
+
+ // IBinder::DeathRecipient implementation.
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_SERVICE_INFO_LISTENER_H_
diff --git a/media/eco/include/eco/ECOServiceStatsProvider.h b/media/eco/include/eco/ECOServiceStatsProvider.h
new file mode 100644
index 0000000..a6e1586
--- /dev/null
+++ b/media/eco/include/eco/ECOServiceStatsProvider.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_ECO_SERVICE_STATS_PROVIDER_H_
+#define ANDROID_MEDIA_ECO_SERVICE_STATS_PROVIDER_H_
+
+#include <android/media/eco/BnECOServiceStatsProvider.h>
+#include <android/media/eco/IECOSession.h>
+#include <binder/BinderService.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "ECOData.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using ::android::binder::Status;
+
+/**
+ * ECOServiceStatsProvider interface class.
+ */
+class ECOServiceStatsProvider : public BinderService<IECOServiceStatsProvider>,
+ public BnECOServiceStatsProvider,
+ public virtual IBinder::DeathRecipient {
+ friend class BinderService<IECOServiceStatsProvider>;
+
+public:
+ // Create a ECOServiceStatsProvider with specifed width, height and isCameraRecording.
+ ECOServiceStatsProvider(int32_t width, int32_t height, bool isCameraRecording);
+
+ virtual ~ECOServiceStatsProvider() {}
+
+ virtual Status getType(int32_t* _aidl_return) = 0;
+ virtual Status getName(::android::String16* _aidl_return) = 0;
+ virtual Status getECOSession(::android::sp<::android::IBinder>* _aidl_return) = 0;
+ virtual Status isCameraRecording(bool* _aidl_return) = 0;
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_SERVICE_STATS_PROVIDER_H_
diff --git a/media/eco/include/eco/ECOSession.h b/media/eco/include/eco/ECOSession.h
new file mode 100644
index 0000000..90f9d94
--- /dev/null
+++ b/media/eco/include/eco/ECOSession.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_ECO_SESSION_H_
+#define ANDROID_MEDIA_ECO_SESSION_H_
+
+#include <android/media/eco/BnECOSession.h>
+#include <android/media/eco/IECOServiceStatsProvider.h>
+#include <binder/BinderService.h>
+
+#include <condition_variable>
+#include <deque>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "ECOData.h"
+#include "ECOServiceInfoListener.h"
+#include "ECOServiceStatsProvider.h"
+#include "ECOUtils.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using ::android::binder::Status;
+
+/**
+ * ECO Session.
+ *
+ * ECOSession is created by ECOService to manage an encoding session. Both the providers and
+ * listeners should interact with ECO session after obtain it from ECOService. For ECOService 1.0,
+ * it only supports resolution of up to 720P and only for camera recording use case. Also, it only
+ * supports encoder as the provider and camera as listener.
+ */
+class ECOSession : public BinderService<ECOSession>,
+ public BnECOSession,
+ public virtual IBinder::DeathRecipient {
+ friend class BinderService<ECOSession>;
+
+public:
+ virtual ~ECOSession();
+
+ virtual Status addStatsProvider(const sp<IECOServiceStatsProvider>& provider,
+ const ECOData& statsConfig, /*out*/ bool* status);
+
+ virtual Status removeStatsProvider(const sp<IECOServiceStatsProvider>&, bool*);
+
+ virtual Status addInfoListener(const sp<IECOServiceInfoListener>&,
+ const ECOData& listenerConfig,
+ /*out*/ bool* status);
+
+ virtual Status removeInfoListener(const sp<IECOServiceInfoListener>&, bool*);
+
+ virtual Status pushNewStats(const ECOData&, bool*);
+
+ virtual Status getWidth(int32_t* _aidl_return);
+
+ virtual Status getHeight(int32_t* _aidl_return);
+
+ virtual Status getIsCameraRecording(bool*);
+
+ virtual Status getNumOfListeners(int32_t*);
+
+ virtual Status getNumOfProviders(int32_t*);
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+ // Grant permission to EcoSessionTest to run test.
+ friend class EcoSessionTest;
+
+ // Let ECOService create the session.
+ friend class ECOService;
+
+protected:
+ static android::sp<ECOSession> createECOSession(int32_t width, int32_t height,
+ bool isCameraRecording);
+
+private:
+ // Only the ECOService could create ECOSession.
+ ECOSession(int32_t width, int32_t height, bool isCameraRecording);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ // Start the main thread for processing the stats and pushing info to listener.
+ static void startThread(ECOSession* session);
+
+ void run();
+
+ bool processStats(const ECOData& stats);
+
+ // Lock guarding ECO session state
+ std::mutex mSessionLock;
+
+ // Process the session stats received from provider.
+ void processSessionStats(const ECOData& stats);
+
+ // Process the frame stats received from provider.
+ void processFrameStats(const ECOData& stats);
+
+ // Generate the latest session info if available.
+ ECOData generateLatestSessionInfoEcoData();
+
+ std::atomic<bool> mStopThread;
+
+ std::mutex mStatsQueueLock;
+ std::deque<ECOData> mStatsQueue; // GUARDED_BY(mStatsQueueLock)
+ std::condition_variable mWorkerWaitCV;
+
+ bool mNewListenerAdded = false;
+
+ constexpr static int32_t ENCODER_MIN_QP = 0;
+ constexpr static int32_t ENCODER_MAX_QP = 51;
+
+ // Save the QP last reported to the listener. Init to be 0.
+ int32_t mLastReportedQp;
+
+ typedef struct QpRange {
+ int32_t mQpBlocknessThreshold = 50;
+ int32_t mQpChangeThreshold = 50;
+ } QpCondition;
+ QpCondition mListenerQpCondition;
+
+ android::sp<IECOServiceInfoListener> mListener;
+ String16 mListenerName;
+
+ android::sp<IECOServiceStatsProvider> mProvider;
+ String16 mProviderName;
+
+ // Main thread for processing the events from provider.
+ std::thread mThread;
+
+ // Width of the encoding session in number of pixels.
+ const int32_t mWidth;
+
+ // Height of the encoding session in number of pixels.
+ const int32_t mHeight;
+
+ // Whether the encoding is for camera recording.
+ const bool mIsCameraRecording;
+
+ // Ouput width of the encoding session in number of pixels, -1 means not available.
+ int32_t mOutputWidth = -1;
+
+ // Output height of the encoding session in number of pixels. -1 means not available.
+ int32_t mOutputHeight = -1;
+
+ // Encoder codec type of the encoding session. -1 means not available.
+ int32_t mCodecType = -1;
+
+ // Encoder codec profile. -1 means not available.
+ int32_t mCodecProfile = -1;
+
+ // Encoder codec level. -1 means not available.
+ int32_t mCodecLevel = -1;
+
+ // Target bitrate in bits per second. This should be provided by the provider. -1 means not
+ // available.
+ int32_t mTargetBitrateBps = -1;
+
+ // Actual bitrate in bits per second. This should be provided by the provider. -1 means not
+ // available.
+ int32_t mActualBitrateBps = -1;
+
+ // Key frame interval in number of frames. -1 means not available.
+ int32_t mKeyFrameIntervalFrames = -1;
+
+ // Frame rate in frames per second. -1 means not available.
+ float mFramerateFps;
+
+ // Debug related flags.
+ bool mLogStats;
+ uint32_t mLogStatsEntries; // number of stats received from the provider.
+ std::list<ECOData> mStatsDebugBuffer;
+
+ // Pushes the ECOData to the debug buffer.
+ void logStats(const ECOData& data);
+
+ bool mLogInfo;
+ uint32_t mLogInfoEntries; // number of infos sent to the listener.
+ std::list<ECOData> mInfosDebugBuffer;
+
+ // Pushes the ECOData to the debug buffer.
+ void logInfos(const ECOData& data);
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_SESSION_H_
diff --git a/media/eco/include/eco/ECOUtils.h b/media/eco/include/eco/ECOUtils.h
new file mode 100644
index 0000000..e5bbfcc
--- /dev/null
+++ b/media/eco/include/eco/ECOUtils.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Helper classes and functions to be used by provider and listener.
+#ifndef ANDROID_MEDIA_ECO_UTILS_H_
+#define ANDROID_MEDIA_ECO_UTILS_H_
+
+#include <cutils/atomic.h>
+#include <utils/Errors.h>
+
+#include "ECOData.h"
+#include "ECODataKey.h"
+#include "ECOServiceConstants.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+#define RETURN_STATUS_IF_ERROR(expr) \
+ { \
+ status_t _errorCode = (expr); \
+ if (_errorCode != NO_ERROR) { \
+ return _errorCode; \
+ } \
+ }
+
+// Helper structure to hold encoder config. This is used by EncoderProvider to provide stats to
+// ECOService or by ECOService to provide info to the listener. Providers could implement their
+// own more complex encoder config as long as ECOService supports parsing them.
+struct SimpleEncoderConfig {
+ // Encoder name as defined in device/google/[device]/media_codecs.xml.
+ std::string mEncoderName;
+
+ // Codec Type as defined in ECOServiceConstants.h. -1 means unavailable.
+ int32_t mCodecType;
+
+ // Codec profile as defined in ECOServiceConstants.h. -1 means unavailable.
+ int32_t mProfile;
+
+ // Codec level as defined in ECOServiceConstants.h. -1 means unavailable.
+ int32_t mLevel;
+
+ // Target bitrate in bits per second. -1 means unavailable.
+ int32_t mTargetBitrate;
+
+ // Key frame interval in frames. -1 means unavailable.
+ int32_t mKeyFrameIntervalFrames;
+
+ // Frame rate in frames per second. -1 means unavailable.
+ float mFrameRateFps;
+
+ SimpleEncoderConfig()
+ : mEncoderName(""),
+ mCodecType(-1),
+ mProfile(-1),
+ mLevel(-1),
+ mTargetBitrate(-1),
+ mKeyFrameIntervalFrames(-1),
+ mFrameRateFps(-1) {}
+
+ SimpleEncoderConfig(const std::string& name, int32_t codecType, int32_t profile, int32_t level,
+ int32_t bitrate, int32_t kfi, float framerateFps)
+ : mEncoderName(name),
+ mCodecType(codecType),
+ mProfile(profile),
+ mLevel(level),
+ mTargetBitrate(bitrate),
+ mKeyFrameIntervalFrames(kfi),
+ mFrameRateFps(framerateFps) {}
+
+ // Convert this SimpleEncoderConfig to ECOData with dataType.
+ ECOData toEcoData(ECOData::ECODatatype dataType);
+};
+
+// Helper structure for
+struct SimpleEncodedFrameData {
+ // Frame sequence number starts from 0.
+ int32_t mFrameNum;
+
+ // Frame type as defined in ECOServiceConstants.h. -1 means unavailable.
+ int8_t mFrameType;
+
+ // Frame presentation timestamp in us. -1 means unavailable.
+ int64_t mFramePtsUs;
+
+ // Avg quantization parameter of the frame. -1 means unavailable.
+ int32_t mAvgQp;
+
+ // Frame size in bytes. -1 means unavailable.
+ int32_t mFrameSizeBytes;
+
+ SimpleEncodedFrameData()
+ : mFrameNum(-1),
+ mFrameType(FrameTypeUnknown),
+ mFramePtsUs(-1),
+ mAvgQp(-1),
+ mFrameSizeBytes(-1) {}
+
+ SimpleEncodedFrameData(int32_t frameNum, int8_t frameType, int64_t ptsUs, int32_t qp,
+ int32_t sizeBytes)
+ : mFrameNum(frameNum),
+ mFrameType(frameType),
+ mFramePtsUs(ptsUs),
+ mAvgQp(qp),
+ mFrameSizeBytes(sizeBytes) {}
+
+ // Convert this SimpleEncoderConfig to ECOData with dataType.
+ ECOData toEcoData(ECOData::ECODatatype dataType);
+};
+
+bool copyKeyValue(const ECOData& src, ECOData* dst);
+
+} // namespace eco
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_ECO_UTILS_H_
diff --git a/media/eco/tests/Android.bp b/media/eco/tests/Android.bp
new file mode 100644
index 0000000..6eb3015
--- /dev/null
+++ b/media/eco/tests/Android.bp
@@ -0,0 +1,57 @@
+cc_defaults{
+ name : "libmedia_ecoservice_tests_defaults",
+ cflags : [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "EcoDataTest",
+ defaults: ["libmedia_ecoservice_tests_defaults"],
+ srcs: ["EcoDataTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libmedia_ecoservice",
+ ],
+}
+
+cc_test {
+ name: "EcoSessionTest",
+ defaults: ["libmedia_ecoservice_tests_defaults"],
+ srcs: [
+ "EcoSessionTest.cpp",
+ "FakeECOServiceStatsProvider.cpp",
+ "FakeECOServiceInfoListener.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libmedia_ecoservice",
+ ],
+}
+
+cc_test {
+ name: "EcoServiceTest",
+ vendor: true,
+ defaults: ["libmedia_ecoservice_tests_defaults"],
+ srcs: [
+ "EcoServiceTest.cpp",
+ "FakeECOServiceStatsProvider.cpp",
+ "FakeECOServiceInfoListener.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libmedia_ecoservice",
+ ],
+} \ No newline at end of file
diff --git a/media/eco/tests/EcoDataTest.cpp b/media/eco/tests/EcoDataTest.cpp
new file mode 100644
index 0000000..f93b692
--- /dev/null
+++ b/media/eco/tests/EcoDataTest.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for EcoData.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ECODataTest"
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+#include "eco/ECOData.h"
+#include "eco/ECODataKey.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+TEST(EcoDataTest, TestConstructor1) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>();
+ EXPECT_EQ(data->getDataType(), ECOData::DATA_TYPE_UNKNOWN);
+ EXPECT_EQ(data->getDataTimeUs(), -1);
+}
+
+TEST(EcoDataTest, TestConstructor2) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS);
+ EXPECT_EQ(data->getDataType(), ECOData::DATA_TYPE_STATS);
+ EXPECT_EQ(data->getDataTimeUs(), -1);
+}
+
+TEST(EcoDataTest, TestConstructor3) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+ EXPECT_EQ(data->getDataType(), ECOData::DATA_TYPE_STATS);
+ EXPECT_EQ(data->getDataTimeUs(), 1000);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindString) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ data->setString(ENCODER_TYPE, "avc");
+ std::string testValue;
+ EXPECT_TRUE(data->findString(ENCODER_TYPE, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, "avc");
+
+ // Override existing key.
+ data->setString(ENCODER_TYPE, "hevc");
+ EXPECT_EQ(data->findString(ENCODER_TYPE, &testValue), ECODataStatus::OK);
+ EXPECT_EQ(testValue, "hevc");
+}
+
+TEST(EcoDataTest, TestSetAndFindMultipleString) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ std::unordered_map<std::string, std::string> inputEntries = {
+ {"name1", "avc"}, {"name2", "avc2"}, {"name3", "avc3"}, {"name4", "avc4"},
+ {"name5", "avc5"}, {"name6", "avc6"}, {"name7", "avc7"}, {"name8", "avc8"},
+ {"name9", "avc9"}, {"name10", "avc10"}, {"name11", "avc11"}, {"name12", "avc12"}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ data->setString(it->first, it->second);
+ }
+
+ // Checks if the string exist in the ECOData.
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ std::string testValue;
+ EXPECT_TRUE(data->findString(it->first, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, it->second);
+ }
+}
+
+TEST(EcoDataTest, TestSetAndFindInvalidString) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ // Test read to null ptr and expect failure
+ EXPECT_TRUE(data->findString("encoder-name", nullptr) != ECODataStatus::OK);
+
+ // Test find non-existing key and expect failure.
+ std::string testValue;
+ EXPECT_TRUE(data->findString("encoder-name", &testValue) != ECODataStatus::OK);
+
+ // Test set empty key and expect failure
+ EXPECT_TRUE(data->setString("", "avc") != ECODataStatus::OK);
+
+ // Test read empty key and expect failure
+ EXPECT_TRUE(data->findString("", &testValue) != ECODataStatus::OK);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindInt32) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ data->setInt32(ENCODER_TARGET_BITRATE_BPS, 2000000);
+ int32_t testValue;
+ EXPECT_TRUE(data->findInt32(ENCODER_TARGET_BITRATE_BPS, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, 2000000);
+
+ // Override existing key.
+ data->setInt32(ENCODER_TARGET_BITRATE_BPS, 2200000);
+ EXPECT_EQ(data->findInt32(ENCODER_TARGET_BITRATE_BPS, &testValue), ECODataStatus::OK);
+ EXPECT_EQ(testValue, 2200000);
+}
+
+TEST(EcoDataTest, TestSetAndFindMultipleInt32) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ std::unordered_map<std::string, int32_t> inputEntries = {
+ {"name1", 100}, {"name2", 200}, {"name3", 300}, {"name4", 400},
+ {"name5", 500}, {"name6", 600}, {"name7", 700}, {"name8", 800},
+ {"name9", 900}, {"name10", 10000}, {"name11", 110000}, {"name12", 120000}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ data->setInt32(it->first, it->second);
+ }
+
+ // Checks if the string exist in the ECOData.
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ int32_t testValue;
+ EXPECT_TRUE(data->findInt32(it->first, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, it->second);
+ }
+}
+
+TEST(EcoDataTest, TestSetAndFindInvalidInt32) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ // Test read to null ptr and expect failure
+ EXPECT_TRUE(data->findInt32("encoder-name", nullptr) != ECODataStatus::OK);
+
+ // Test find non-existing key and expect failure.
+ int32_t testValue;
+ EXPECT_TRUE(data->findInt32("encoder-name", &testValue) != ECODataStatus::OK);
+
+ // Test set empty key and expect failure
+ EXPECT_TRUE(data->setInt32("", 1000) != ECODataStatus::OK);
+
+ // Test read empty key and expect failure
+ EXPECT_TRUE(data->findInt32("", &testValue) != ECODataStatus::OK);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindInt64) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ data->setInt64(ENCODER_TARGET_BITRATE_BPS, 2000000);
+ int64_t testValue;
+ EXPECT_TRUE(data->findInt64(ENCODER_TARGET_BITRATE_BPS, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, 2000000);
+
+ // Override existing key.
+ data->setInt64(ENCODER_TARGET_BITRATE_BPS, 2200000);
+ EXPECT_EQ(data->findInt64(ENCODER_TARGET_BITRATE_BPS, &testValue), ECODataStatus::OK);
+ EXPECT_EQ(testValue, 2200000);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindMultipleInt64) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ std::unordered_map<std::string, int64_t> inputEntries = {
+ {"name1", 100}, {"name2", 200}, {"name3", 300}, {"name4", 400},
+ {"name5", 500}, {"name6", 600}, {"name7", 700}, {"name8", 800},
+ {"name9", 900}, {"name10", 10000}, {"name11", 110000}, {"name12", 120000}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ data->setInt64(it->first, it->second);
+ }
+
+ // Checks if the string exist in the ECOData.
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ int64_t testValue;
+ EXPECT_TRUE(data->findInt64(it->first, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, it->second);
+ }
+}
+
+TEST(EcoDataTest, TestSetAndFindInvalidInt64) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ // Test read to null ptr and expect failure
+ EXPECT_TRUE(data->findInt64("encoder-name", nullptr) != ECODataStatus::OK);
+
+ // Test find non-existing key and expect failure.
+ int64_t testValue;
+ EXPECT_TRUE(data->findInt64("encoder-name", &testValue) != ECODataStatus::OK);
+
+ // Test set empty key and expect failure
+ EXPECT_TRUE(data->setInt64("", 1000) != ECODataStatus::OK);
+
+ // Test read empty key and expect failure
+ EXPECT_TRUE(data->findInt64("", &testValue) != ECODataStatus::OK);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindFloat) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ data->setFloat(ENCODER_TARGET_BITRATE_BPS, 2000000.0);
+ float testValue;
+ EXPECT_TRUE(data->findFloat(ENCODER_TARGET_BITRATE_BPS, &testValue) == ECODataStatus::OK);
+ EXPECT_FLOAT_EQ(testValue, 2000000.0);
+
+ // Override existing key.
+ data->setFloat(ENCODER_TARGET_BITRATE_BPS, 2200000.0);
+ EXPECT_TRUE(data->findFloat(ENCODER_TARGET_BITRATE_BPS, &testValue) == ECODataStatus::OK);
+ EXPECT_FLOAT_EQ(testValue, 2200000.0);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindMultipleFloat) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ std::unordered_map<std::string, float> inputEntries = {
+ {"name1", 100.0}, {"name2", 200.0}, {"name3", 300.0}, {"name4", 400.0},
+ {"name5", 500.0}, {"name6", 600.0}, {"name7", 700.0}, {"name8", 800.0},
+ {"name9", 900.0}, {"name10", 10000.0}, {"name11", 110000.0}, {"name12", 120000.0}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ data->setFloat(it->first, it->second);
+ }
+
+ // Checks if the string exist in the ECOData.
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ float testValue;
+ EXPECT_TRUE(data->findFloat(it->first, &testValue) == ECODataStatus::OK);
+ EXPECT_FLOAT_EQ(testValue, it->second);
+ }
+}
+
+TEST(EcoDataTest, TestSetAndFindInvalidFloat) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ // Test read to null ptr and expect failure
+ EXPECT_TRUE(data->findFloat("encoder-name", nullptr) != ECODataStatus::OK);
+
+ // Test find non-existing key and expect failure.
+ float testValue;
+ EXPECT_TRUE(data->findFloat("encoder-name", &testValue) != ECODataStatus::OK);
+
+ // Test set empty key and expect failure
+ EXPECT_TRUE(data->setFloat("", 1000.0) != ECODataStatus::OK);
+
+ // Test read empty key and expect failure
+ EXPECT_TRUE(data->findFloat("", &testValue) != ECODataStatus::OK);
+}
+
+TEST(EcoDataTest, TestNormalSetAndFindMixedDataType) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ std::unordered_map<std::string, ECOData::ECODataValueType> inputEntries = {
+ {"name1", "google-encoder"}, {"name2", "avc"}, {"profile", 1}, {"level", 2},
+ {"framerate", 4.1}, {"kfi", 30}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ data->set(it->first, it->second);
+ }
+
+ // Checks if the string exist in the ECOData.
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ ECOData::ECODataValueType testValue;
+ EXPECT_TRUE(data->find(it->first, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, it->second);
+ }
+}
+
+TEST(EcoDataTest, TestSetAndFindInvalidDataType) {
+ std::unique_ptr<ECOData> data = std::make_unique<ECOData>(ECOData::DATA_TYPE_STATS, 1000);
+
+ // Test read to null ptr and expect failure
+ EXPECT_TRUE(data->find("encoder-name", nullptr) != ECODataStatus::OK);
+
+ // Test find non-existing key and expect failure.
+ ECOData::ECODataValueType testValue;
+ EXPECT_TRUE(data->find("encoder-name2", &testValue) != ECODataStatus::OK);
+
+ // Test set empty key and expect failure
+ EXPECT_TRUE(data->set("", 1000) != ECODataStatus::OK);
+
+ // Test read empty key and expect failure
+ EXPECT_TRUE(data->find("", &testValue) != ECODataStatus::OK);
+}
+
+TEST(EcoDataTest, TestNormalWriteReadParcel) {
+ constexpr int32_t kDataType = ECOData::DATA_TYPE_STATS;
+ constexpr int64_t kDataTimeUs = 1000;
+
+ std::unique_ptr<ECOData> sourceData = std::make_unique<ECOData>(kDataType, kDataTimeUs);
+
+ std::unordered_map<std::string, ECOData::ECODataValueType> inputEntries = {
+ {"name1", "google-encoder"}, {"name2", "avc"}, {"profile", 1}, {"level", 2},
+ {"framerate", 4.1}, {"kfi", 30}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ sourceData->set(it->first, it->second);
+ }
+
+ std::unique_ptr<Parcel> parcel = std::make_unique<Parcel>();
+ EXPECT_TRUE(sourceData->writeToParcel(parcel.get()) == NO_ERROR);
+
+ // Rewind the data position of the parcel for this test. Otherwise, the following read will not
+ // start from the beginning.
+ parcel->setDataPosition(0);
+
+ // Reads the parcel back into a new ECOData
+ std::unique_ptr<ECOData> dstData = std::make_unique<ECOData>();
+ EXPECT_TRUE(dstData->readFromParcel(parcel.get()) == NO_ERROR);
+
+ // Checks the data type, time and number of entries.
+ EXPECT_EQ(sourceData->getNumOfEntries(), dstData->getNumOfEntries());
+ EXPECT_EQ(dstData->getDataType(), kDataType);
+ EXPECT_EQ(dstData->getDataTimeUs(), kDataTimeUs);
+
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ ECOData::ECODataValueType testValue;
+ EXPECT_TRUE(dstData->find(it->first, &testValue) == ECODataStatus::OK);
+ EXPECT_EQ(testValue, it->second);
+ }
+}
+
+TEST(EcoDataTest, TestWriteInvalidParcel) {
+ constexpr int32_t kDataType = ECOData::DATA_TYPE_STATS;
+ constexpr int64_t kDataTimeUs = 1000;
+
+ std::unique_ptr<ECOData> sourceData = std::make_unique<ECOData>(kDataType, kDataTimeUs);
+
+ std::unique_ptr<Parcel> parcel = std::make_unique<Parcel>();
+ EXPECT_TRUE(sourceData->writeToParcel(nullptr) != NO_ERROR);
+}
+
+TEST(EcoDataTest, TestReadInvalidParcel) {
+ constexpr int32_t kDataType = ECOData::DATA_TYPE_STATS;
+ constexpr int64_t kDataTimeUs = 1000;
+
+ std::unique_ptr<ECOData> sourceData = std::make_unique<ECOData>(kDataType, kDataTimeUs);
+
+ std::unordered_map<std::string, ECOData::ECODataValueType> inputEntries = {
+ {"name1", "google-encoder"}, {"name2", "avc"}, {"profile", 1}, {"level", 2},
+ {"framerate", 4.1}, {"kfi", 30}};
+ for (auto it = inputEntries.begin(); it != inputEntries.end(); ++it) {
+ sourceData->set(it->first, it->second);
+ }
+
+ std::unique_ptr<Parcel> parcel = std::make_unique<Parcel>();
+ EXPECT_TRUE(sourceData->writeToParcel(parcel.get()) == NO_ERROR);
+
+ // Corrupt the parcel by write random data to the beginning.
+ parcel->setDataPosition(4);
+ parcel->writeCString("invalid-data");
+
+ parcel->setDataPosition(0);
+
+ // Reads the parcel back into a new ECOData
+ std::unique_ptr<ECOData> dstData = std::make_unique<ECOData>();
+ EXPECT_TRUE(dstData->readFromParcel(parcel.get()) != NO_ERROR);
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android \ No newline at end of file
diff --git a/media/eco/tests/EcoServiceTest.cpp b/media/eco/tests/EcoServiceTest.cpp
new file mode 100644
index 0000000..dc589dd
--- /dev/null
+++ b/media/eco/tests/EcoServiceTest.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for ECOService.
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "ECOServiceTest"
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+#include "FakeECOServiceInfoListener.h"
+#include "FakeECOServiceStatsProvider.h"
+#include "eco/ECOService.h"
+#include "eco/ECOUtils.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using ::android::IBinder;
+using android::sp;
+using ::android::binder::Status;
+
+namespace {
+
+static constexpr uint32_t kTestWidth = 1280;
+static constexpr uint32_t kTestHeight = 720;
+static constexpr bool kIsCameraRecording = true;
+static constexpr float kFrameRate = 30.0f;
+
+} // namespace
+
+// A helper class to help create ECOService and manage ECOService.
+class EcoServiceTest : public ::testing::Test {
+public:
+ EcoServiceTest() { ALOGD("EcoServiceTest created"); }
+
+ sp<IECOService> createService() {
+ android::sp<android::IServiceManager> sm = android::defaultServiceManager();
+ assert(sm != 0);
+ android::sp<android::IBinder> binder = sm->getService(String16("media.ecoservice"));
+
+ if (binder == 0) {
+ ALOGE("Failed to connect to ecoservice");
+ return nullptr;
+ } else {
+ ALOGD("Successfully connect to ecoservice");
+ }
+
+ mECOService = android::interface_cast<IECOService>(binder);
+ return mECOService;
+ }
+
+ ~EcoServiceTest() { ALOGD("EcoServiceTest destroyed"); }
+
+private:
+ sp<IECOService> mECOService = nullptr;
+};
+
+TEST_F(EcoServiceTest, NormalObtainSessionWithInvalidWidth) {
+ sp<IECOService> service = createService();
+ EXPECT_TRUE(service != nullptr);
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session = nullptr;
+
+ service->obtainSession(-1 /* width */, kTestHeight, kIsCameraRecording, &session);
+ EXPECT_FALSE(session);
+}
+
+TEST_F(EcoServiceTest, NormalObtainSessionWithInvalidHeight) {
+ sp<IECOService> service = createService();
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session = nullptr;
+
+ service->obtainSession(kTestWidth, -1 /* height */, kIsCameraRecording, &session);
+ EXPECT_FALSE(session);
+}
+
+TEST_F(EcoServiceTest, NormalObtainSessionWithCameraRecordingFalse) {
+ sp<IECOService> service = createService();
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session = nullptr;
+
+ service->obtainSession(kTestWidth, kTestHeight, false /* isCameraRecording */, &session);
+ EXPECT_TRUE(session);
+}
+
+TEST_F(EcoServiceTest, NormalObtainSingleSession) {
+ sp<IECOService> service = createService();
+ EXPECT_TRUE(service != nullptr);
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session = nullptr;
+
+ service->obtainSession(kTestWidth, kTestHeight, kIsCameraRecording, &session);
+ EXPECT_TRUE(session);
+}
+
+TEST_F(EcoServiceTest, NormalObtainSessionTwice) {
+ sp<IECOService> service = createService();
+ EXPECT_TRUE(service != nullptr);
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session1 = nullptr;
+
+ service->obtainSession(kTestWidth, kTestHeight, kIsCameraRecording, &session1);
+ EXPECT_TRUE(session1);
+
+ sp<IECOSession> session2 = nullptr;
+
+ service->obtainSession(kTestWidth, kTestHeight, kIsCameraRecording, &session2);
+ EXPECT_TRUE(session2);
+
+ // The two session instances should be the same.
+ EXPECT_TRUE(IInterface::asBinder(session1) == IInterface::asBinder(session2));
+}
+
+TEST_F(EcoServiceTest, ObtainTwoSessions) {
+ sp<IECOService> service = createService();
+ EXPECT_TRUE(service != nullptr);
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session1 = nullptr;
+
+ service->obtainSession(kTestWidth, kTestHeight, kIsCameraRecording, &session1);
+ EXPECT_TRUE(session1);
+
+ sp<IECOSession> session2 = nullptr;
+
+ service->obtainSession(kTestWidth - 1, kTestHeight - 1, kIsCameraRecording, &session2);
+ EXPECT_TRUE(session2);
+
+ // The two session instances must not be the same.
+ EXPECT_TRUE(IInterface::asBinder(session1) != IInterface::asBinder(session2));
+
+ // Check the session number.
+ int32_t count = 0;
+ service->getNumOfSessions(&count);
+ EXPECT_EQ(count, 2);
+
+ // Get the list of sessions from service.
+ std::vector<sp<IBinder>> sessionList;
+ service->getSessions(&sessionList);
+ bool foundFirstSession = false, foundSecondSession = false;
+
+ for (std::vector<sp<IBinder>>::iterator it = sessionList.begin(); it != sessionList.end();
+ ++it) {
+ if (IInterface::asBinder(session1) == it->get()) {
+ foundFirstSession = true;
+ }
+ if (IInterface::asBinder(session2) == it->get()) {
+ foundSecondSession = true;
+ }
+ }
+
+ // Expect found both sessions.
+ EXPECT_TRUE(foundFirstSession);
+ EXPECT_TRUE(foundSecondSession);
+}
+
+TEST_F(EcoServiceTest, TestNormalFlowWithOneListenerAndOneProvider) {
+ sp<IECOService> service = createService();
+ EXPECT_TRUE(service != nullptr);
+
+ // Provider obtains the session from the service.
+ sp<IECOSession> session = nullptr;
+
+ service->obtainSession(kTestWidth, kTestHeight, kIsCameraRecording, &session);
+ EXPECT_TRUE(session);
+
+ // Create provider and add it to the session.
+ sp<FakeECOServiceStatsProvider> fakeProvider = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate);
+ fakeProvider->setECOSession(session);
+
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ providerConfig.setString(KEY_PROVIDER_NAME, "FakeECOServiceStatsProvider");
+ providerConfig.setInt32(KEY_PROVIDER_TYPE,
+ ECOServiceStatsProvider::STATS_PROVIDER_TYPE_VIDEO_ENCODER);
+ bool res;
+ Status status = session->addStatsProvider(fakeProvider, providerConfig, &res);
+
+ // Create listener and add it to the session.
+ sp<FakeECOServiceInfoListener> fakeListener =
+ new FakeECOServiceInfoListener(kTestWidth, kTestHeight, kIsCameraRecording);
+ fakeListener->setECOSession(session);
+
+ // Create the listener config.
+ ECOData listenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ listenerConfig.setString(KEY_LISTENER_NAME, "FakeECOServiceInfoListener");
+ listenerConfig.setInt32(KEY_LISTENER_TYPE, ECOServiceInfoListener::INFO_LISTENER_TYPE_CAMERA);
+
+ // Specify the qp thresholds for receiving notification.
+ listenerConfig.setInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD, 40);
+ listenerConfig.setInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD, 5);
+
+ status = session->addInfoListener(fakeListener, listenerConfig, &res);
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/tests/EcoSessionTest.cpp b/media/eco/tests/EcoSessionTest.cpp
new file mode 100644
index 0000000..33a449d
--- /dev/null
+++ b/media/eco/tests/EcoSessionTest.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for ECOSession.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ECOSessionTest"
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+#include "FakeECOServiceInfoListener.h"
+#include "FakeECOServiceStatsProvider.h"
+#include "eco/ECOSession.h"
+#include "eco/ECOUtils.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using android::sp;
+using ::android::binder::Status;
+
+static constexpr uint32_t kTestWidth = 1280;
+static constexpr uint32_t kTestHeight = 720;
+static constexpr bool kIsCameraRecording = true;
+static constexpr int32_t kTargetBitrateBps = 22000000;
+static constexpr int32_t kKeyFrameIntervalFrames = 30;
+static constexpr float kFrameRate = 30.0f;
+
+// A helpful class to help create ECOSession and manage ECOSession.
+class EcoSessionTest : public ::testing::Test {
+public:
+ EcoSessionTest() { ALOGD("EcoSessionTest created"); }
+
+ sp<ECOSession> createSession(int32_t width, int32_t height, bool isCameraRecording) {
+ mSession = ECOSession::createECOSession(width, height, isCameraRecording);
+ if (mSession == nullptr) return nullptr;
+ return mSession;
+ }
+
+private:
+ sp<ECOSession> mSession = nullptr;
+};
+
+TEST_F(EcoSessionTest, TestConstructorWithInvalidParameters) {
+ // Expects failure as ECOService1.0 will only support up to 720P and camera recording case.
+ EXPECT_TRUE(createSession(1920 /* width */, 1080 /* height */, true /* isCameraRecording */) ==
+ nullptr);
+
+ // Expects failure as ECOService1.0 will only support up to 720P and camera recording case.
+ EXPECT_TRUE(createSession(1920 /* width */, 1080 /* height */, false /* isCameraRecording */) ==
+ nullptr);
+
+ EXPECT_TRUE(createSession(1920 /* width */, -1 /* height */, true /* isCameraRecording */) ==
+ nullptr);
+
+ EXPECT_TRUE(createSession(-1 /* width */, 1080 /* height */, true /* isCameraRecording */) ==
+ nullptr);
+}
+
+TEST_F(EcoSessionTest, TestConstructorWithValidParameters) {
+ // Expects success with <= 720P and is for camera recording.
+ EXPECT_TRUE(createSession(1280 /* width */, 720 /* height */, true /* isCameraRecording */) !=
+ nullptr);
+
+ // Expects success with <= 720P and is for camera recording.
+ EXPECT_TRUE(createSession(640 /* width */, 480 /* height */, true /* isCameraRecording */) !=
+ nullptr);
+}
+
+TEST_F(EcoSessionTest, TestAddProviderWithoutSpecifyEcoDataType) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceStatsProvider> fakeProvider = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ ECOData providerConfig;
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider, providerConfig, &res);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestAddProviderWithWrongEcoDataType) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceStatsProvider> fakeProvider = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ ECOData providerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider, providerConfig, &res);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestAddNormalProvider) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceStatsProvider> fakeProvider = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider, providerConfig, &res);
+ EXPECT_TRUE(status.isOk());
+}
+
+// Add two providers and expect failure as ECOService1.0 only supports one provider and one
+// listener.
+TEST_F(EcoSessionTest, TestAddTwoProvider) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceStatsProvider> fakeProvider1 = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider1, providerConfig, &res);
+ EXPECT_TRUE(status.isOk());
+
+ sp<FakeECOServiceStatsProvider> fakeProvider2 = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+ status = ecoSession->addStatsProvider(fakeProvider2, providerConfig, &res);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestAddListenerWithDifferentHeight) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceInfoListener> fakeListener = new FakeECOServiceInfoListener(
+ kTestWidth - 1, kTestHeight, kIsCameraRecording, ecoSession);
+
+ ECOData ListenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addInfoListener(fakeListener, ListenerConfig, &res);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestAddListenerWithDifferentWidth) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceInfoListener> fakeListener = new FakeECOServiceInfoListener(
+ kTestWidth, kTestHeight - 1, kIsCameraRecording, ecoSession);
+
+ ECOData ListenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addInfoListener(fakeListener, ListenerConfig, &res);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestAddListenerWithCameraRecordingFalse) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceInfoListener> fakeListener = new FakeECOServiceInfoListener(
+ kTestWidth, kTestHeight, !kIsCameraRecording, ecoSession);
+
+ ECOData ListenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addInfoListener(fakeListener, ListenerConfig, &res);
+ EXPECT_FALSE(status.isOk());
+}
+
+// Test the ECOSession with FakeECOServiceStatsProvider and FakeECOServiceInfoListener. Push the
+// stats to ECOSession through FakeECOServiceStatsProvider and check the info received in
+// from FakeECOServiceInfoListener ECOSession.
+TEST_F(EcoSessionTest, TestSessionWithProviderAndListenerSimpleTest) {
+ // The time that listener needs to wait for the info from ECOService.
+ static constexpr int kServiceWaitTimeMs = 10;
+
+ // Create the session.
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+
+ // Add provider.
+ sp<FakeECOServiceStatsProvider> fakeProvider = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ providerConfig.setString(KEY_PROVIDER_NAME, "FakeECOServiceStatsProvider");
+ providerConfig.setInt32(KEY_PROVIDER_TYPE,
+ ECOServiceStatsProvider::STATS_PROVIDER_TYPE_VIDEO_ENCODER);
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider, providerConfig, &res);
+
+ // Create listener.
+ sp<FakeECOServiceInfoListener> fakeListener =
+ new FakeECOServiceInfoListener(kTestWidth, kTestHeight, kIsCameraRecording, ecoSession);
+
+ // Create the listener config.
+ ECOData listenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ listenerConfig.setString(KEY_LISTENER_NAME, "FakeECOServiceInfoListener");
+ listenerConfig.setInt32(KEY_LISTENER_TYPE, ECOServiceInfoListener::INFO_LISTENER_TYPE_CAMERA);
+
+ // Specify the qp thresholds for receiving notification.
+ listenerConfig.setInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD, 40);
+ listenerConfig.setInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD, 5);
+
+ status = ecoSession->addInfoListener(fakeListener, listenerConfig, &res);
+
+ ECOData info;
+ bool getInfo = false;
+
+ // Set the getInfo flag to true and copy the info from fakeListener.
+ fakeListener->setInfoAvailableCallback(
+ [&info, &getInfo](const ::android::media::eco::ECOData& newInfo) {
+ getInfo = true;
+ info = newInfo;
+ });
+
+ // Inject the session stats into the ECOSession through fakeProvider.
+ SimpleEncoderConfig sessionEncoderConfig("google-avc", CodecTypeAVC, AVCProfileHigh, AVCLevel52,
+ kTargetBitrateBps, kKeyFrameIntervalFrames,
+ kFrameRate);
+ fakeProvider->injectSessionStats(sessionEncoderConfig.toEcoData(ECOData::DATA_TYPE_STATS));
+
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+ // Check the Session info matches with the session stats sent by provider.
+ EXPECT_TRUE(getInfo);
+ EXPECT_TRUE(info.getDataType() == ECOData::DATA_TYPE_INFO);
+
+ std::string infoType;
+ EXPECT_TRUE(info.findString(KEY_INFO_TYPE, &infoType) == ECODataStatus::OK);
+ EXPECT_EQ(infoType, VALUE_INFO_TYPE_SESSION);
+
+ // Check the session info matches the session stats provided by FakeECOServiceStatsProvider.
+ int32_t codecType;
+ EXPECT_TRUE(info.findInt32(ENCODER_TYPE, &codecType) == ECODataStatus::OK);
+ EXPECT_EQ(codecType, CodecTypeAVC);
+
+ int32_t profile;
+ EXPECT_TRUE(info.findInt32(ENCODER_PROFILE, &profile) == ECODataStatus::OK);
+ EXPECT_EQ(profile, AVCProfileHigh);
+
+ int32_t level;
+ EXPECT_TRUE(info.findInt32(ENCODER_LEVEL, &level) == ECODataStatus::OK);
+ EXPECT_EQ(level, AVCLevel52);
+
+ int32_t bitrate;
+ EXPECT_TRUE(info.findInt32(ENCODER_TARGET_BITRATE_BPS, &bitrate) == ECODataStatus::OK);
+ EXPECT_EQ(bitrate, kTargetBitrateBps);
+
+ int32_t kfi;
+ EXPECT_TRUE(info.findInt32(ENCODER_KFI_FRAMES, &kfi) == ECODataStatus::OK);
+ EXPECT_EQ(kfi, kKeyFrameIntervalFrames);
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 30. Expect receiving notification for the first frame.
+ SimpleEncodedFrameData frameStats(1 /* seq number */, FrameTypeI, 0 /* framePtsUs */,
+ 30 /* avg-qp */, 56 /* frameSize */);
+
+ getInfo = false;
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+ // Check the Session info matches with the session stats sent by provider.
+ EXPECT_TRUE(getInfo);
+
+ EXPECT_TRUE(info.findString(KEY_INFO_TYPE, &infoType) == ECODataStatus::OK);
+ EXPECT_EQ(infoType, VALUE_INFO_TYPE_FRAME);
+
+ int8_t frameType;
+ EXPECT_TRUE(info.findInt8(FRAME_TYPE, &frameType) == ECODataStatus::OK);
+ EXPECT_EQ(frameType, FrameTypeI);
+
+ int32_t frameNum;
+ EXPECT_TRUE(info.findInt32(FRAME_NUM, &frameNum) == ECODataStatus::OK);
+ EXPECT_EQ(frameNum, 1);
+
+ int64_t framePtsUs;
+ EXPECT_TRUE(info.findInt64(FRAME_PTS_US, &framePtsUs) == ECODataStatus::OK);
+ EXPECT_EQ(framePtsUs, 0);
+
+ int32_t frameQp;
+ EXPECT_TRUE(info.findInt32(FRAME_AVG_QP, &frameQp) == ECODataStatus::OK);
+ EXPECT_EQ(frameQp, 30);
+
+ int32_t frameSize;
+ EXPECT_TRUE(info.findInt32(FRAME_SIZE_BYTES, &frameSize) == ECODataStatus::OK);
+ EXPECT_EQ(frameSize, 56);
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 35. Expect not receiving notification as 35 is below
+ // threshold.
+ frameStats = SimpleEncodedFrameData(2 /* seq number */, FrameTypeP, 333333 /* framePtsUs */,
+ 35 /* avg-qp */, 56 /* frameSize */);
+ getInfo = false;
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+ EXPECT_FALSE(getInfo);
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 41. Expect receiving notification as 41 goes beyond
+ // threshold 40.
+ frameStats = SimpleEncodedFrameData(3 /* seq number */, FrameTypeP, 666666 /* framePtsUs */,
+ 41 /* avg-qp */, 56 /* frameSize */);
+ getInfo = false;
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+
+ // Check the frame info matches with the frame stats sent by provider.
+ EXPECT_TRUE(getInfo);
+ EXPECT_TRUE(info.findString(KEY_INFO_TYPE, &infoType) == ECODataStatus::OK);
+ EXPECT_EQ(infoType, VALUE_INFO_TYPE_FRAME);
+ EXPECT_TRUE(info.findInt8(FRAME_TYPE, &frameType) == ECODataStatus::OK);
+ EXPECT_EQ(frameType, FrameTypeP);
+ EXPECT_TRUE(info.findInt32(FRAME_NUM, &frameNum) == ECODataStatus::OK);
+ EXPECT_EQ(frameNum, 3);
+ EXPECT_TRUE(info.findInt64(FRAME_PTS_US, &framePtsUs) == ECODataStatus::OK);
+ EXPECT_EQ(framePtsUs, 666666);
+ EXPECT_TRUE(info.findInt32(FRAME_AVG_QP, &frameQp) == ECODataStatus::OK);
+ EXPECT_EQ(frameQp, 41);
+ EXPECT_TRUE(info.findInt32(FRAME_SIZE_BYTES, &frameSize) == ECODataStatus::OK);
+ EXPECT_EQ(frameSize, 56);
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 42. Expect not receiving notification as 42 goes beyond
+ // threshold 40 but delta oes not go beyond the mQpChangeThreshold threshold.
+ frameStats = SimpleEncodedFrameData(4 /* seq number */, FrameTypeP, 999999 /* framePtsUs */,
+ 42 /* avg-qp */, 56 /* frameSize */);
+ getInfo = false;
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+ EXPECT_FALSE(getInfo);
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 10. Expect receiving notification as the detal from
+ // last reported QP is larger than threshold 4.
+ frameStats = SimpleEncodedFrameData(5 /* seq number */, FrameTypeB, 1333332 /* framePtsUs */,
+ 49 /* avg-qp */, 56 /* frameSize */);
+ getInfo = false;
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+
+ // Check the frame info matches with the frame stats sent by provider.
+ EXPECT_TRUE(getInfo);
+ EXPECT_TRUE(info.findString(KEY_INFO_TYPE, &infoType) == ECODataStatus::OK);
+ EXPECT_EQ(infoType, VALUE_INFO_TYPE_FRAME);
+ EXPECT_TRUE(info.findInt8(FRAME_TYPE, &frameType) == ECODataStatus::OK);
+ EXPECT_EQ(frameType, FrameTypeB);
+ EXPECT_TRUE(info.findInt32(FRAME_NUM, &frameNum) == ECODataStatus::OK);
+ EXPECT_EQ(frameNum, 5);
+ EXPECT_TRUE(info.findInt64(FRAME_PTS_US, &framePtsUs) == ECODataStatus::OK);
+ EXPECT_EQ(framePtsUs, 1333332);
+ EXPECT_TRUE(info.findInt32(FRAME_AVG_QP, &frameQp) == ECODataStatus::OK);
+ EXPECT_EQ(frameQp, 49);
+ EXPECT_TRUE(info.findInt32(FRAME_SIZE_BYTES, &frameSize) == ECODataStatus::OK);
+ EXPECT_EQ(frameSize, 56);
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 41. Expect receiving notification as the detal from
+ // last reported QP is larger than threshold 4.
+ frameStats = SimpleEncodedFrameData(6 /* seq number */, FrameTypeB, 1666665 /* framePtsUs */,
+ 41 /* avg-qp */, 56 /* frameSize */);
+ getInfo = false;
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+
+ // Check the frame info matches with the frame stats sent by provider.
+ EXPECT_TRUE(getInfo);
+ EXPECT_TRUE(info.findString(KEY_INFO_TYPE, &infoType) == ECODataStatus::OK);
+ EXPECT_EQ(infoType, VALUE_INFO_TYPE_FRAME);
+ EXPECT_TRUE(info.findInt8(FRAME_TYPE, &frameType) == ECODataStatus::OK);
+ EXPECT_EQ(frameType, FrameTypeB);
+ EXPECT_TRUE(info.findInt32(FRAME_NUM, &frameNum) == ECODataStatus::OK);
+ EXPECT_EQ(frameNum, 6);
+ EXPECT_TRUE(info.findInt64(FRAME_PTS_US, &framePtsUs) == ECODataStatus::OK);
+ EXPECT_EQ(framePtsUs, 1666665);
+ EXPECT_TRUE(info.findInt32(FRAME_AVG_QP, &frameQp) == ECODataStatus::OK);
+ EXPECT_EQ(frameQp, 41);
+ EXPECT_TRUE(info.findInt32(FRAME_SIZE_BYTES, &frameSize) == ECODataStatus::OK);
+ EXPECT_EQ(frameSize, 56);
+}
+
+TEST_F(EcoSessionTest, TestRemoveMatchProvider) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceStatsProvider> fakeProvider1 = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider1, providerConfig, &res);
+ EXPECT_TRUE(res);
+ EXPECT_TRUE(status.isOk());
+
+ status = ecoSession->removeStatsProvider(fakeProvider1, &res);
+ EXPECT_TRUE(res);
+ EXPECT_TRUE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestRemoveMisMatchProvider) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ sp<FakeECOServiceStatsProvider> fakeProvider1 = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider1, providerConfig, &res);
+ EXPECT_TRUE(res);
+ EXPECT_TRUE(status.isOk());
+
+ sp<FakeECOServiceStatsProvider> fakeProvider2 = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+
+ status = ecoSession->removeStatsProvider(fakeProvider2, &res);
+ EXPECT_FALSE(res);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestRemoveMatchListener) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ // Create listener.
+ sp<FakeECOServiceInfoListener> fakeListener =
+ new FakeECOServiceInfoListener(kTestWidth, kTestHeight, kIsCameraRecording, ecoSession);
+
+ // Create the listener config.
+ ECOData listenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ listenerConfig.setString(KEY_LISTENER_NAME, "FakeECOServiceInfoListener");
+ listenerConfig.setInt32(KEY_LISTENER_TYPE, ECOServiceInfoListener::INFO_LISTENER_TYPE_CAMERA);
+
+ // Specify the qp thresholds for receiving notification.
+ listenerConfig.setInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD, 40);
+ listenerConfig.setInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD, 5);
+
+ bool res;
+ Status status = ecoSession->addInfoListener(fakeListener, listenerConfig, &res);
+
+ status = ecoSession->removeInfoListener(fakeListener, &res);
+ EXPECT_TRUE(res);
+ EXPECT_TRUE(status.isOk());
+}
+
+TEST_F(EcoSessionTest, TestRemoveMisMatchListener) {
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+ EXPECT_TRUE(ecoSession);
+
+ // Create listener.
+ sp<FakeECOServiceInfoListener> fakeListener =
+ new FakeECOServiceInfoListener(kTestWidth, kTestHeight, kIsCameraRecording, ecoSession);
+
+ // Create the listener config.
+ ECOData listenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ listenerConfig.setString(KEY_LISTENER_NAME, "FakeECOServiceInfoListener");
+ listenerConfig.setInt32(KEY_LISTENER_TYPE, ECOServiceInfoListener::INFO_LISTENER_TYPE_CAMERA);
+
+ // Specify the qp thresholds for receiving notification.
+ listenerConfig.setInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD, 40);
+ listenerConfig.setInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD, 5);
+
+ bool res;
+ Status status = ecoSession->addInfoListener(fakeListener, listenerConfig, &res);
+
+ // Create listener.
+ sp<FakeECOServiceInfoListener> fakeListener2 =
+ new FakeECOServiceInfoListener(kTestWidth, kTestHeight, kIsCameraRecording, ecoSession);
+
+ status = ecoSession->removeInfoListener(fakeListener2, &res);
+ EXPECT_FALSE(res);
+ EXPECT_FALSE(status.isOk());
+}
+
+// Test the listener connects to the ECOSession after provider sends the session info. Listener
+// should recieve the session info right after adding itself to the ECOSession.
+TEST_F(EcoSessionTest, TestAddListenerAferProviderStarts) {
+ // The time that listener needs to wait for the info from ECOService.
+ static constexpr int kServiceWaitTimeMs = 10;
+
+ // Create the session.
+ sp<ECOSession> ecoSession = createSession(kTestWidth, kTestHeight, kIsCameraRecording);
+
+ // Add provider.
+ sp<FakeECOServiceStatsProvider> fakeProvider = new FakeECOServiceStatsProvider(
+ kTestWidth, kTestHeight, kIsCameraRecording, kFrameRate, ecoSession);
+ ECOData providerConfig(ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ providerConfig.setString(KEY_PROVIDER_NAME, "FakeECOServiceStatsProvider");
+ providerConfig.setInt32(KEY_PROVIDER_TYPE,
+ ECOServiceStatsProvider::STATS_PROVIDER_TYPE_VIDEO_ENCODER);
+ bool res;
+ Status status = ecoSession->addStatsProvider(fakeProvider, providerConfig, &res);
+
+ // Inject the session stats into the ECOSession through fakeProvider.
+ SimpleEncoderConfig sessionEncoderConfig("google-avc", CodecTypeAVC, AVCProfileHigh, AVCLevel52,
+ kTargetBitrateBps, kKeyFrameIntervalFrames,
+ kFrameRate);
+ fakeProvider->injectSessionStats(sessionEncoderConfig.toEcoData(ECOData::DATA_TYPE_STATS));
+
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+
+ // =======================================================================================
+ // Inject the frame stats with qp = 30. Expect receiving notification for the first frame.
+ SimpleEncodedFrameData frameStats(1 /* seq number */, FrameTypeI, 0 /* framePtsUs */,
+ 30 /* avg-qp */, 56 /* frameSize */);
+
+ fakeProvider->injectFrameStats(frameStats.toEcoData(ECOData::DATA_TYPE_STATS));
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+
+ // =======================================================================================
+ // Create and add the listener to the ECOSession. Expect to receive the session infor right
+ // after addInfoListener.
+ sp<FakeECOServiceInfoListener> fakeListener =
+ new FakeECOServiceInfoListener(kTestWidth, kTestHeight, kIsCameraRecording, ecoSession);
+
+ // Create the listener config.
+ ECOData listenerConfig(ECOData::DATA_TYPE_INFO_LISTENER_CONFIG,
+ systemTime(SYSTEM_TIME_BOOTTIME));
+ listenerConfig.setString(KEY_LISTENER_NAME, "FakeECOServiceInfoListener");
+ listenerConfig.setInt32(KEY_LISTENER_TYPE, ECOServiceInfoListener::INFO_LISTENER_TYPE_CAMERA);
+
+ // Specify the qp thresholds for receiving notification.
+ listenerConfig.setInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD, 40);
+ listenerConfig.setInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD, 5);
+
+ ECOData info;
+ bool getInfo = false;
+
+ // Set the getInfo flag to true and copy the info from fakeListener.
+ fakeListener->setInfoAvailableCallback(
+ [&info, &getInfo](const ::android::media::eco::ECOData& newInfo) {
+ getInfo = true;
+ info = newInfo;
+ });
+
+ status = ecoSession->addInfoListener(fakeListener, listenerConfig, &res);
+
+ // Wait as ECOService may take some time to process.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kServiceWaitTimeMs));
+
+ // Check the Session info matches with the session stats sent by provider.
+ EXPECT_TRUE(getInfo);
+ EXPECT_TRUE(info.getDataType() == ECOData::DATA_TYPE_INFO);
+
+ std::string infoType;
+ EXPECT_TRUE(info.findString(KEY_INFO_TYPE, &infoType) == ECODataStatus::OK);
+ EXPECT_EQ(infoType, VALUE_INFO_TYPE_SESSION);
+
+ // Check the session info matches the session stats provided by FakeECOServiceStatsProvider.
+ int32_t codecType;
+ EXPECT_TRUE(info.findInt32(ENCODER_TYPE, &codecType) == ECODataStatus::OK);
+ EXPECT_EQ(codecType, CodecTypeAVC);
+
+ int32_t profile;
+ EXPECT_TRUE(info.findInt32(ENCODER_PROFILE, &profile) == ECODataStatus::OK);
+ EXPECT_EQ(profile, AVCProfileHigh);
+
+ int32_t level;
+ EXPECT_TRUE(info.findInt32(ENCODER_LEVEL, &level) == ECODataStatus::OK);
+ EXPECT_EQ(level, AVCLevel52);
+
+ int32_t bitrate;
+ EXPECT_TRUE(info.findInt32(ENCODER_TARGET_BITRATE_BPS, &bitrate) == ECODataStatus::OK);
+ EXPECT_EQ(bitrate, kTargetBitrateBps);
+
+ int32_t kfi;
+ EXPECT_TRUE(info.findInt32(ENCODER_KFI_FRAMES, &kfi) == ECODataStatus::OK);
+ EXPECT_EQ(kfi, kKeyFrameIntervalFrames);
+}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/tests/FakeECOServiceInfoListener.cpp b/media/eco/tests/FakeECOServiceInfoListener.cpp
new file mode 100644
index 0000000..2a4a285
--- /dev/null
+++ b/media/eco/tests/FakeECOServiceInfoListener.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "FakeFakeECOServiceInfoListener"
+
+#include "FakeECOServiceInfoListener.h"
+
+#include <android-base/unique_fd.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace media {
+namespace eco {
+
+FakeECOServiceInfoListener::FakeECOServiceInfoListener(int32_t width, int32_t height,
+ bool isCameraRecording,
+ android::sp<IECOSession> session)
+ : mWidth(width),
+ mHeight(height),
+ mIsCameraRecording(isCameraRecording),
+ mECOSession(session) {
+ ALOGD("FakeECOServiceInfoListener construct with w: %d, h: %d, isCameraRecording: %d", mWidth,
+ mHeight, mIsCameraRecording);
+}
+
+FakeECOServiceInfoListener::FakeECOServiceInfoListener(int32_t width, int32_t height,
+ bool isCameraRecording)
+ : mWidth(width), mHeight(height), mIsCameraRecording(isCameraRecording) {
+ ALOGD("FakeECOServiceInfoListener construct with w: %d, h: %d, isCameraRecording: %d", mWidth,
+ mHeight, mIsCameraRecording);
+}
+
+FakeECOServiceInfoListener::~FakeECOServiceInfoListener() {
+ ALOGD("FakeECOServiceInfoListener destructor");
+}
+
+Status FakeECOServiceInfoListener::getType(int32_t* /*_aidl_return*/) {
+ return binder::Status::ok();
+}
+
+Status FakeECOServiceInfoListener::getName(::android::String16* _aidl_return) {
+ *_aidl_return = String16("FakeECOServiceInfoListener");
+ return binder::Status::ok();
+}
+
+Status FakeECOServiceInfoListener::getECOSession(sp<::android::IBinder>* _aidl_return) {
+ *_aidl_return = IInterface::asBinder(mECOSession);
+ return binder::Status::ok();
+}
+
+Status FakeECOServiceInfoListener::onNewInfo(const ::android::media::eco::ECOData& newInfo) {
+ ALOGD("FakeECOServiceInfoListener get new info");
+ mInfoAvaiableCallback(newInfo);
+ return binder::Status::ok();
+}
+
+// IBinder::DeathRecipient implementation
+void FakeECOServiceInfoListener::binderDied(const wp<IBinder>& /*who*/) {}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/tests/FakeECOServiceInfoListener.h b/media/eco/tests/FakeECOServiceInfoListener.h
new file mode 100644
index 0000000..9974b63
--- /dev/null
+++ b/media/eco/tests/FakeECOServiceInfoListener.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A fake ECOServiceInfoListener for testing ECOService and ECOSession.
+
+#include <android-base/unique_fd.h>
+#include <android/media/eco/BnECOServiceInfoListener.h>
+#include <android/media/eco/IECOSession.h>
+#include <binder/BinderService.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+#include "eco/ECOData.h"
+#include "eco/ECODataKey.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using ::android::sp;
+using ::android::binder::Status;
+
+/**
+ * A fake ECOServiceInfoListener.
+ *
+ * FakeECOServiceInfoListener is a fake ECOServiceInfoListener that used for testing.
+ */
+class FakeECOServiceInfoListener : public BnECOServiceInfoListener {
+public:
+ // Method called by the FakeECOServiceInfoListener when there is new info from ECOService.
+ // This is used by the test to verify the information is sent by the ECOService correctly.
+ using InfoAvailableCallback =
+ std::function<void(const ::android::media::eco::ECOData& newInfo)>;
+
+ FakeECOServiceInfoListener(int32_t width, int32_t height, bool isCameraRecording,
+ sp<IECOSession> session);
+
+ FakeECOServiceInfoListener(int32_t width, int32_t height, bool isCameraRecording);
+
+ void setECOSession(android::sp<IECOSession> session) { mECOSession = session; }
+
+ virtual ~FakeECOServiceInfoListener();
+
+ virtual Status getType(int32_t* _aidl_return);
+ virtual Status getName(::android::String16* _aidl_return);
+ virtual Status getECOSession(::android::sp<::android::IBinder>* _aidl_return);
+ virtual Status onNewInfo(const ::android::media::eco::ECOData& newInfo);
+
+ // Helper callback to send the info to the test.
+ void setInfoAvailableCallback(InfoAvailableCallback callback) {
+ mInfoAvaiableCallback = callback;
+ }
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
+ int32_t mWidth;
+ int32_t mHeight;
+ bool mIsCameraRecording;
+ android::sp<IECOSession> mECOSession;
+ InfoAvailableCallback mInfoAvaiableCallback;
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/tests/FakeECOServiceStatsProvider.cpp b/media/eco/tests/FakeECOServiceStatsProvider.cpp
new file mode 100644
index 0000000..883a830
--- /dev/null
+++ b/media/eco/tests/FakeECOServiceStatsProvider.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "FakeECOServiceStatsProvider"
+
+#include "FakeECOServiceStatsProvider.h"
+
+#include <android-base/unique_fd.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace media {
+namespace eco {
+
+FakeECOServiceStatsProvider::FakeECOServiceStatsProvider(int32_t width, int32_t height,
+ bool isCameraRecording, float frameRate,
+ android::sp<IECOSession> session)
+ : mWidth(width),
+ mHeight(height),
+ mIsCameraRecording(isCameraRecording),
+ mFrameRate(frameRate),
+ mFrameNumber(0),
+ mECOSession(session) {
+ ALOGD("FakeECOServiceStatsProvider construct with w: %d, h: %d, isCameraRecording: %d, "
+ "frameRate: %f",
+ mWidth, mHeight, mIsCameraRecording, mFrameRate);
+}
+
+FakeECOServiceStatsProvider::FakeECOServiceStatsProvider(int32_t width, int32_t height,
+ bool isCameraRecording, float frameRate)
+ : mWidth(width),
+ mHeight(height),
+ mIsCameraRecording(isCameraRecording),
+ mFrameRate(frameRate),
+ mFrameNumber(0) {
+ ALOGD("FakeECOServiceStatsProvider construct with w: %d, h: %d, isCameraRecording: %d, "
+ "frameRate: %f",
+ mWidth, mHeight, mIsCameraRecording, mFrameRate);
+}
+
+FakeECOServiceStatsProvider::~FakeECOServiceStatsProvider() {
+ ALOGD("FakeECOServiceStatsProvider destructor");
+}
+
+Status FakeECOServiceStatsProvider::getType(int32_t* /*_aidl_return*/) {
+ return binder::Status::ok();
+}
+
+Status FakeECOServiceStatsProvider::getName(::android::String16* _aidl_return) {
+ *_aidl_return = String16("FakeECOServiceStatsProvider");
+ return binder::Status::ok();
+}
+
+Status FakeECOServiceStatsProvider::getECOSession(sp<::android::IBinder>* _aidl_return) {
+ *_aidl_return = IInterface::asBinder(mECOSession);
+ return binder::Status::ok();
+}
+
+bool FakeECOServiceStatsProvider::injectSessionStats(const ECOData& stats) {
+ if (mECOSession == nullptr) return false;
+ ALOGD("injectSessionStats");
+ bool res;
+ mECOSession->pushNewStats(stats, &res);
+ return res;
+}
+
+bool FakeECOServiceStatsProvider::injectFrameStats(const ECOData& stats) {
+ if (mECOSession == nullptr) return false;
+ ALOGD("injectPerFrameStats");
+ bool res;
+ mECOSession->pushNewStats(stats, &res);
+ return res;
+}
+
+// IBinder::DeathRecipient implementation
+void FakeECOServiceStatsProvider::binderDied(const wp<IBinder>& /*who*/) {}
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/tests/FakeECOServiceStatsProvider.h b/media/eco/tests/FakeECOServiceStatsProvider.h
new file mode 100644
index 0000000..0f60d02
--- /dev/null
+++ b/media/eco/tests/FakeECOServiceStatsProvider.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A fake ECOServiceStatsProvider for testing ECOService and ECOSession.
+
+#include <android-base/unique_fd.h>
+#include <android/media/eco/BnECOServiceStatsProvider.h>
+#include <binder/BinderService.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+#include <condition_variable>
+#include <deque>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "eco/ECOData.h"
+#include "eco/ECODataKey.h"
+#include "eco/ECOService.h"
+
+namespace android {
+namespace media {
+namespace eco {
+
+using ::android::sp;
+using ::android::binder::Status;
+
+/**
+ * A fake ECOServiceStatsProvider.
+ *
+ * FakeECOServiceStatsProvider is a fake ECOServiceStatsProvider that used for testing.
+ */
+
+class FakeECOServiceStatsProvider : public BnECOServiceStatsProvider {
+public:
+ FakeECOServiceStatsProvider(int32_t width, int32_t height, bool isCameraRecording,
+ float frameRate, android::sp<IECOSession> session);
+
+ FakeECOServiceStatsProvider(int32_t width, int32_t height, bool isCameraRecording,
+ float frameRate);
+
+ void setECOSession(android::sp<IECOSession> session) { mECOSession = session; }
+
+ // Helper function to inject session stats to the FakeECOServiceStatsProvider so provider
+ // could push to the service.
+ bool injectSessionStats(const ECOData& stats);
+
+ // Helper function to inject each frame's stats to the FakeECOServiceStatsProvider so provider
+ // could push to the service.
+ bool injectFrameStats(const ECOData& stats);
+
+ /* Starts the FakeECOServiceStatsProvider */
+ void start();
+
+ /* Stops FakeECOServiceStatsProvider */
+ void stop();
+
+ virtual ~FakeECOServiceStatsProvider();
+
+ virtual Status getType(int32_t* _aidl_return);
+ virtual Status getName(::android::String16* _aidl_return);
+ virtual Status getECOSession(::android::sp<::android::IBinder>* _aidl_return);
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
+ int32_t mWidth;
+ int32_t mHeight;
+ bool mIsCameraRecording;
+ float mFrameRate;
+ uint32_t mFrameNumber;
+
+ android::sp<IECOSession> mECOSession;
+};
+
+} // namespace eco
+} // namespace media
+} // namespace android
diff --git a/media/eco/tests/run_all_unit_tests.sh b/media/eco/tests/run_all_unit_tests.sh
new file mode 100644
index 0000000..7cbe27b
--- /dev/null
+++ b/media/eco/tests/run_all_unit_tests.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+echo "waiting for device"
+adb root && adb wait-for-device remount && adb sync
+
+adb shell /data/nativetest/EcoDataTest/EcoDataTest
+adb shell /data/nativetest/EcoSessionTest/EcoSessionTest
+#ECOService test lives in vendor side.
+adb shell data/nativetest/vendor/EcoServiceTest/EcoServiceTest
diff --git a/media/sfplugin/C2OMXNode.cpp b/media/sfplugin/C2OMXNode.cpp
index 5b9aebb..03d859a 100644
--- a/media/sfplugin/C2OMXNode.cpp
+++ b/media/sfplugin/C2OMXNode.cpp
@@ -104,7 +104,7 @@ status_t C2OMXNode::getParameter(OMX_INDEXTYPE index, void *params, size_t size)
status_t C2OMXNode::setParameter(OMX_INDEXTYPE index, const void *params, size_t size) {
// handle max/fixed frame duration control
- if (index == OMX_IndexParamMaxFrameDurationForBitrateControl
+ if (index == (OMX_INDEXTYPE)OMX_IndexParamMaxFrameDurationForBitrateControl
&& params != NULL
&& size == sizeof(OMX_PARAM_U32TYPE)) {
// The incoming number is an int32_t contained in OMX_U32.
@@ -226,7 +226,7 @@ status_t C2OMXNode::emptyBuffer(
&& omxBuf.mGraphicBuffer != nullptr) {
std::shared_ptr<C2GraphicAllocation> alloc;
handle = WrapNativeCodec2GrallocHandle(
- native_handle_clone(omxBuf.mGraphicBuffer->handle),
+ omxBuf.mGraphicBuffer->handle,
omxBuf.mGraphicBuffer->width,
omxBuf.mGraphicBuffer->height,
omxBuf.mGraphicBuffer->format,
diff --git a/media/sfplugin/CCodec.cpp b/media/sfplugin/CCodec.cpp
index 1e74658..944a8a5 100644
--- a/media/sfplugin/CCodec.cpp
+++ b/media/sfplugin/CCodec.cpp
@@ -1378,9 +1378,7 @@ void CCodec::signalResume() {
}
void CCodec::signalSetParameters(const sp<AMessage> &params) {
- sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
- msg->setMessage("params", params);
- msg->post();
+ setParameters(params);
}
void CCodec::setParameters(const sp<AMessage> &params) {
@@ -1509,8 +1507,7 @@ void CCodec::onMessageReceived(const sp<AMessage> &msg) {
}
case kWhatStart: {
// C2Component::start() should return within 500ms.
- // WORKAROUND: start sometimes takes longer than expected.
- setDeadline(now, 2500ms, "start");
+ setDeadline(now, 550ms, "start");
mQueuedWorkCount = 0;
start();
break;
@@ -1546,13 +1543,6 @@ void CCodec::onMessageReceived(const sp<AMessage> &msg) {
setInputSurface(surface);
break;
}
- case kWhatSetParameters: {
- setDeadline(now, 50ms, "setParameters");
- sp<AMessage> params;
- CHECK(msg->findMessage("params", &params));
- setParameters(params);
- break;
- }
case kWhatWorkDone: {
std::unique_ptr<C2Work> work;
size_t numDiscardedInputBuffers;
@@ -1625,6 +1615,7 @@ void CCodec::onMessageReceived(const sp<AMessage> &msg) {
C2StreamColorAspectsInfo::output::PARAM_TYPE,
C2StreamDataSpaceInfo::output::PARAM_TYPE,
C2StreamHdrStaticInfo::output::PARAM_TYPE,
+ C2StreamHdr10PlusInfo::output::PARAM_TYPE,
C2StreamPixelAspectRatioInfo::output::PARAM_TYPE,
C2StreamSurfaceScalingInfo::output::PARAM_TYPE
};
diff --git a/media/sfplugin/CCodecBufferChannel.cpp b/media/sfplugin/CCodecBufferChannel.cpp
index 69de800..247eb9b 100644
--- a/media/sfplugin/CCodecBufferChannel.cpp
+++ b/media/sfplugin/CCodecBufferChannel.cpp
@@ -1301,21 +1301,7 @@ public:
sp<Codec2Buffer> allocateArrayBuffer() override {
// TODO: proper max output size
- size_t capacity = kLinearBufferSize;
- int32_t width = 0, height = 0;
- bool video = mFormat->findInt32(KEY_MAX_WIDTH, &width)
- && mFormat->findInt32(KEY_MAX_HEIGHT, &height);
- if (!video) {
- video = mFormat->findInt32(KEY_WIDTH, &width)
- && mFormat->findInt32(KEY_HEIGHT, &height);
- }
- if (video) {
- // Assuming data compression ratio better than 3:1.
- capacity = std::min(std::max(capacity, (size_t)width * height / 2),
- kMaxLinearBufferSize);
- }
- ALOGD("[%s] Using linear capacity of %zu for array buffer", mName, capacity);
- return new LocalLinearBuffer(mFormat, new ABuffer(capacity));
+ return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
}
};
@@ -1593,8 +1579,7 @@ CCodecBufferChannel::CCodecBufferChannel(
mFirstValidFrameIndex(0u),
mMetaMode(MODE_NONE),
mAvailablePipelineCapacity(),
- mInputMetEos(false),
- mPendingEosTimestamp(INT64_MIN) {
+ mInputMetEos(false) {
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
buffers->reset(new DummyInputBuffers(""));
}
@@ -1679,13 +1664,23 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const sp<MediaCodecBuffer
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
c2_status_t err = mComponent->queue(&items);
+
+ if (err == C2_OK && eos && buffer->size() > 0u) {
+ mCCodecCallback->onWorkQueued(false);
+ work.reset(new C2Work);
+ work->input.ordinal.timestamp = timeUs;
+ work->input.ordinal.frameIndex = mFrameIndex++;
+ // WORKAROUND: keep client timestamp in customOrdinal
+ work->input.ordinal.customOrdinal = timeUs;
+ work->input.buffers.clear();
+ work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
+
+ items.clear();
+ items.push_back(std::move(work));
+ err = mComponent->queue(&items);
+ }
if (err == C2_OK) {
- if (eos && buffer->size() > 0u) {
- mCCodecCallback->onWorkQueued(false);
- mPendingEosTimestamp = timeUs;
- } else {
- mCCodecCallback->onWorkQueued(eos);
- }
+ mCCodecCallback->onWorkQueued(eos);
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
bool released = (*buffers)->releaseBuffer(buffer, nullptr, true);
@@ -1735,6 +1730,7 @@ status_t CCodecBufferChannel::queueSecureInputBuffer(
sp<EncryptedLinearBlockBuffer> encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get());
ssize_t result = -1;
+ ssize_t codecDataOffset = 0;
if (mCrypto != nullptr) {
ICrypto::DestinationBuffer destination;
if (secure) {
@@ -1775,9 +1771,16 @@ status_t CCodecBufferChannel::queueSecureInputBuffer(
CasStatus status = CasStatus::OK;
hidl_string detailedError;
+ ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED;
+
+ if (key != nullptr) {
+ sctrl = (ScramblingControl)key[0];
+ // Adjust for the PES offset
+ codecDataOffset = key[2] | (key[3] << 8);
+ }
auto returnVoid = mDescrambler->descramble(
- key != NULL ? (ScramblingControl)key[0] : ScramblingControl::UNSCRAMBLED,
+ sctrl,
hidlSubSamples,
srcBuffer,
0,
@@ -1797,6 +1800,11 @@ status_t CCodecBufferChannel::queueSecureInputBuffer(
return UNKNOWN_ERROR;
}
+ if (result < codecDataOffset) {
+ ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result);
+ return BAD_VALUE;
+ }
+
ALOGV("[%s] descramble succeeded, %zd bytes", mName, result);
if (dstBuffer.type == BufferType::SHARED_MEMORY) {
@@ -1804,7 +1812,7 @@ status_t CCodecBufferChannel::queueSecureInputBuffer(
}
}
- buffer->setRange(0, result);
+ buffer->setRange(codecDataOffset, result - codecDataOffset);
return queueInputBufferInternal(buffer);
}
@@ -1818,31 +1826,9 @@ void CCodecBufferChannel::feedInputBufferIfAvailable() {
}
void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
- while ((!mInputMetEos || mPendingEosTimestamp != INT64_MIN) &&
+ while (!mInputMetEos &&
!mReorderStash.lock()->hasPending() &&
mAvailablePipelineCapacity.allocate("feedInputBufferIfAvailable")) {
- int64_t pendingEosTimestamp = mPendingEosTimestamp.exchange(INT64_MIN);
- if (pendingEosTimestamp != INT64_MIN) {
- mAvailablePipelineCapacity.freeInputSlots(1, "feedInputBufferIfAvailable: queue eos");
-
- std::unique_ptr<C2Work> work(new C2Work);
- work->input.ordinal.timestamp = pendingEosTimestamp;
- work->input.ordinal.frameIndex = mFrameIndex++;
- // WORKAROUND: keep client timestamp in customOrdinal
- work->input.ordinal.customOrdinal = pendingEosTimestamp;
- work->input.buffers.clear();
- work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
-
- std::list<std::unique_ptr<C2Work>> items;
- items.push_back(std::move(work));
- if (mComponent->queue(&items) == C2_OK) {
- mCCodecCallback->onWorkQueued(true);
- } else {
- ALOGD("[%s] failed to queue EOS to the component", mName);
- mCCodecCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- }
- continue;
- }
sp<MediaCodecBuffer> inBuffer;
size_t index;
{
@@ -1936,6 +1922,11 @@ status_t CCodecBufferChannel::renderOutputBuffer(
std::static_pointer_cast<const C2StreamHdrStaticInfo::output>(
c2Buffer->getInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE));
+ // HDR10 plus info
+ std::shared_ptr<const C2StreamHdr10PlusInfo::output> hdr10PlusInfo =
+ std::static_pointer_cast<const C2StreamHdr10PlusInfo::output>(
+ c2Buffer->getInfo(C2StreamHdr10PlusInfo::output::PARAM_TYPE));
+
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
if (output->surface == nullptr) {
@@ -1963,33 +1954,41 @@ status_t CCodecBufferChannel::renderOutputBuffer(
videoScalingMode,
transform,
Fence::NO_FENCE, 0);
- if (hdrStaticInfo) {
- struct android_smpte2086_metadata smpte2086_meta = {
- .displayPrimaryRed = {
- hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y
- },
- .displayPrimaryGreen = {
- hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y
- },
- .displayPrimaryBlue = {
- hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y
- },
- .whitePoint = {
- hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y
- },
- .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
- .minLuminance = hdrStaticInfo->mastering.minLuminance,
- };
-
- struct android_cta861_3_metadata cta861_meta = {
- .maxContentLightLevel = hdrStaticInfo->maxCll,
- .maxFrameAverageLightLevel = hdrStaticInfo->maxFall,
- };
-
+ if (hdrStaticInfo || hdr10PlusInfo) {
HdrMetadata hdr;
- hdr.validTypes = HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3;
- hdr.smpte2086 = smpte2086_meta;
- hdr.cta8613 = cta861_meta;
+ if (hdrStaticInfo) {
+ struct android_smpte2086_metadata smpte2086_meta = {
+ .displayPrimaryRed = {
+ hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y
+ },
+ .displayPrimaryGreen = {
+ hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y
+ },
+ .displayPrimaryBlue = {
+ hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y
+ },
+ .whitePoint = {
+ hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y
+ },
+ .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
+ .minLuminance = hdrStaticInfo->mastering.minLuminance,
+ };
+
+ struct android_cta861_3_metadata cta861_meta = {
+ .maxContentLightLevel = hdrStaticInfo->maxCll,
+ .maxFrameAverageLightLevel = hdrStaticInfo->maxFall,
+ };
+
+ hdr.validTypes = HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3;
+ hdr.smpte2086 = smpte2086_meta;
+ hdr.cta8613 = cta861_meta;
+ }
+ if (hdr10PlusInfo) {
+ hdr.validTypes |= HdrMetadata::HDR10PLUS;
+ hdr.hdr10plus.assign(
+ hdr10PlusInfo->m.value,
+ hdr10PlusInfo->m.value + hdr10PlusInfo->flexCount());
+ }
qbi.setHdrMetadata(hdr);
}
// we don't have dirty regions
@@ -2078,6 +2077,11 @@ status_t CCodecBufferChannel::start(
if (!iStreamFormat || !oStreamFormat) {
return UNKNOWN_ERROR;
}
+ } else if (err != C2_OK) {
+ return UNKNOWN_ERROR;
+ }
+
+ {
Mutexed<ReorderStash>::Locked reorder(mReorderStash);
reorder->clear();
if (reorderDepth) {
@@ -2086,10 +2090,7 @@ status_t CCodecBufferChannel::start(
if (reorderKey) {
reorder->setKey(reorderKey.value);
}
- } else if (err != C2_OK) {
- return UNKNOWN_ERROR;
}
-
// TODO: get this from input format
bool secure = mComponent->getName().find(".secure") != std::string::npos;
@@ -2238,7 +2239,7 @@ status_t CCodecBufferChannel::start(
C2_DONT_BLOCK,
&params);
if ((err != C2_OK && err != C2_BAD_INDEX) || params.size() != 1) {
- ALOGD("[%s] Query input allocators returned %zu params => %s (%u)",
+ ALOGD("[%s] Query output allocators returned %zu params => %s (%u)",
mName, params.size(), asString(err), err);
} else if (err == C2_OK && params.size() == 1) {
C2PortAllocatorsTuning::output *outputAllocators =
@@ -2256,11 +2257,39 @@ status_t CCodecBufferChannel::start(
}
}
- // use bufferqueue if outputting to a surface
- if (pools->outputAllocatorId == C2PlatformAllocatorStore::GRALLOC
- && outputSurface
- && ((poolMask >> C2PlatformAllocatorStore::BUFFERQUEUE) & 1)) {
- pools->outputAllocatorId = C2PlatformAllocatorStore::BUFFERQUEUE;
+ // use bufferqueue if outputting to a surface.
+ // query C2PortSurfaceAllocatorTuning::output from component, or use default allocator
+ // if unsuccessful.
+ if (outputSurface) {
+ params.clear();
+ err = mComponent->query({ },
+ { C2PortSurfaceAllocatorTuning::output::PARAM_TYPE },
+ C2_DONT_BLOCK,
+ &params);
+ if ((err != C2_OK && err != C2_BAD_INDEX) || params.size() != 1) {
+ ALOGD("[%s] Query output surface allocator returned %zu params => %s (%u)",
+ mName, params.size(), asString(err), err);
+ } else if (err == C2_OK && params.size() == 1) {
+ C2PortSurfaceAllocatorTuning::output *surfaceAllocator =
+ C2PortSurfaceAllocatorTuning::output::From(params[0].get());
+ if (surfaceAllocator) {
+ std::shared_ptr<C2Allocator> allocator;
+ // verify allocator IDs and resolve default allocator
+ allocatorStore->fetchAllocator(surfaceAllocator->value, &allocator);
+ if (allocator) {
+ pools->outputAllocatorId = allocator->getId();
+ } else {
+ ALOGD("[%s] component requested invalid surface output allocator ID %u",
+ mName, surfaceAllocator->value);
+ err = C2_BAD_VALUE;
+ }
+ }
+ }
+ if (pools->outputAllocatorId == C2PlatformAllocatorStore::GRALLOC
+ && err != C2_OK
+ && ((poolMask >> C2PlatformAllocatorStore::BUFFERQUEUE) & 1)) {
+ pools->outputAllocatorId = C2PlatformAllocatorStore::BUFFERQUEUE;
+ }
}
if ((poolMask >> pools->outputAllocatorId) & 1) {
@@ -2313,11 +2342,12 @@ status_t CCodecBufferChannel::start(
outputGeneration);
}
- (*buffers) = (*buffers)->toArrayMode(kMinOutputBufferArraySize);
- if (oStreamFormat.value == C2BufferData::LINEAR) {
+ if (oStreamFormat.value == C2BufferData::LINEAR
+ && mComponentName.find("c2.qti.") == std::string::npos) {
// WORKAROUND: if we're using early CSD workaround we convert to
// array mode, to appease apps assuming the output
// buffers to be of the same size.
+ (*buffers) = (*buffers)->toArrayMode(kMinOutputBufferArraySize);
int32_t channelCount;
int32_t sampleRate;
@@ -2368,7 +2398,6 @@ status_t CCodecBufferChannel::start(
#endif
mInputMetEos = false;
- mPendingEosTimestamp = INT64_MIN;
mSync.start();
return OK;
}
@@ -2418,7 +2447,8 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() {
ALOGD("[%s] buffer capacity too small for the config (%zu < %zu)",
mName, buffer->capacity(), config->size());
}
- } else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0) {
+ } else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0
+ && mComponentName.find("c2.qti.") == std::string::npos) {
// WORKAROUND: Some apps expect CSD available without queueing
// any input. Queue an empty buffer to get the CSD.
buffer->setRange(0, 0);
@@ -2549,6 +2579,7 @@ bool CCodecBufferChannel::handleWork(
}
const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
+
std::shared_ptr<C2Buffer> buffer;
// NOTE: MediaCodec usage supposedly have only one output stream.
if (worklet->output.buffers.size() > 1u) {
diff --git a/media/sfplugin/CCodecBufferChannel.h b/media/sfplugin/CCodecBufferChannel.h
index e354fd0..431baaa 100644
--- a/media/sfplugin/CCodecBufferChannel.h
+++ b/media/sfplugin/CCodecBufferChannel.h
@@ -376,10 +376,9 @@ private:
Mutexed<ReorderStash> mReorderStash;
std::atomic_bool mInputMetEos;
- std::atomic_int64_t mPendingEosTimestamp;
inline bool hasCryptoOrDescrambler() {
- return mCrypto != NULL || mDescrambler != NULL;
+ return mCrypto != nullptr || mDescrambler != nullptr;
}
};
diff --git a/media/sfplugin/CCodecConfig.cpp b/media/sfplugin/CCodecConfig.cpp
index 8dbfd0e..ef02e74 100644
--- a/media/sfplugin/CCodecConfig.cpp
+++ b/media/sfplugin/CCodecConfig.cpp
@@ -570,6 +570,12 @@ void CCodecConfig::initializeStandardParams() {
add(ConfigMapper("csd-0", C2_PARAMKEY_INIT_DATA, "value")
.limitTo(D::OUTPUT & D::READ));
+ add(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO, "value")
+ .limitTo(D::VIDEO & D::PARAM & D::INPUT));
+
+ add(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO, "value")
+ .limitTo(D::VIDEO & D::OUTPUT));
+
add(ConfigMapper(C2_PARAMKEY_TEMPORAL_LAYERING, C2_PARAMKEY_TEMPORAL_LAYERING, "")
.limitTo(D::ENCODER & D::VIDEO & D::OUTPUT));
@@ -624,7 +630,23 @@ void CCodecConfig::initializeStandardParams() {
.limitTo(D::AUDIO & D::CODED));
add(ConfigMapper(KEY_PCM_ENCODING, C2_PARAMKEY_PCM_ENCODING, "value")
- .limitTo(D::AUDIO));
+ .limitTo(D::AUDIO)
+ .withMappers([](C2Value v) -> C2Value {
+ int32_t value;
+ C2Config::pcm_encoding_t to;
+ if (v.get(&value) && C2Mapper::map(value, &to)) {
+ return to;
+ }
+ return C2Value();
+ }, [](C2Value v) -> C2Value {
+ C2Config::pcm_encoding_t value;
+ int32_t to;
+ using C2ValueType=typename _c2_reduce_enum_to_underlying_type<decltype(value)>::type;
+ if (v.get((C2ValueType*)&value) && C2Mapper::map(value, &to)) {
+ return to;
+ }
+ return C2Value();
+ }));
add(ConfigMapper(KEY_IS_ADTS, C2_PARAMKEY_AAC_PACKAGING, "value")
.limitTo(D::AUDIO & D::CODED)
diff --git a/media/sfplugin/Codec2Buffer.cpp b/media/sfplugin/Codec2Buffer.cpp
index 5cb719e..597e8f3 100644
--- a/media/sfplugin/Codec2Buffer.cpp
+++ b/media/sfplugin/Codec2Buffer.cpp
@@ -109,9 +109,11 @@ bool LocalLinearBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
// DummyContainerBuffer
+static uint8_t sDummyByte[1] = { 0 };
+
DummyContainerBuffer::DummyContainerBuffer(
const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer)
- : Codec2Buffer(format, new ABuffer(nullptr, 1)),
+ : Codec2Buffer(format, new ABuffer(sDummyByte, 1)),
mBufferRef(buffer) {
setRange(0, buffer ? 1 : 0);
}
@@ -562,7 +564,7 @@ GraphicMetadataBuffer::GraphicMetadataBuffer(
const std::shared_ptr<C2Allocator> &alloc)
: Codec2Buffer(format, new ABuffer(sizeof(VideoNativeMetadata))),
mAlloc(alloc) {
- ((VideoNativeMetadata *)base())->pBuffer = 0;
+ ((VideoNativeMetadata *)base())->pBuffer = nullptr;
}
std::shared_ptr<C2Buffer> GraphicMetadataBuffer::asC2Buffer() {
@@ -576,7 +578,7 @@ std::shared_ptr<C2Buffer> GraphicMetadataBuffer::asC2Buffer() {
ALOGV("VideoNativeMetadata: %dx%d", buffer->width, buffer->height);
C2Handle *handle = WrapNativeCodec2GrallocHandle(
- native_handle_clone(buffer->handle),
+ buffer->handle,
buffer->width,
buffer->height,
buffer->format,
diff --git a/media/sfplugin/Codec2InfoBuilder.cpp b/media/sfplugin/Codec2InfoBuilder.cpp
index d883d46..4a6d672 100644
--- a/media/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/sfplugin/Codec2InfoBuilder.cpp
@@ -74,11 +74,11 @@ constexpr OMX_U32 kPortIndexOutput = 1;
constexpr OMX_U32 kMaxIndicesToCheck = 32;
status_t queryOmxCapabilities(
- const char* name, const char* mime, bool isEncoder,
+ const char* name, const char* mediaType, bool isEncoder,
MediaCodecInfo::CapabilitiesWriter* caps) {
- const char *role = GetComponentRole(isEncoder, mime);
- if (role == NULL) {
+ const char *role = GetComponentRole(isEncoder, mediaType);
+ if (role == nullptr) {
return BAD_VALUE;
}
@@ -129,8 +129,8 @@ status_t queryOmxCapabilities(
return err;
}
- bool isVideo = hasPrefix(mime, "video/") == 0;
- bool isImage = hasPrefix(mime, "image/") == 0;
+ bool isVideo = hasPrefix(mediaType, "video/") == 0;
+ bool isImage = hasPrefix(mediaType, "image/") == 0;
if (isVideo || isImage) {
OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
@@ -150,7 +150,7 @@ status_t queryOmxCapabilities(
// AVC components may not list the constrained profiles explicitly, but
// decoders that support a profile also support its constrained version.
// Encoders must explicitly support constrained profiles.
- if (!isEncoder && strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
+ if (!isEncoder && strcasecmp(mediaType, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
} else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
@@ -194,7 +194,7 @@ status_t queryOmxCapabilities(
asString(portFormat.eColorFormat), portFormat.eColorFormat);
}
}
- } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
+ } else if (strcasecmp(mediaType, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
// More audio codecs if they have profiles.
OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
InitOMXParams(&param);
@@ -225,18 +225,18 @@ status_t queryOmxCapabilities(
}
if (isVideo && !isEncoder) {
- native_handle_t *sidebandHandle = NULL;
+ native_handle_t *sidebandHandle = nullptr;
if (omxNode->configureVideoTunnelMode(
kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
// tunneled playback includes adaptive playback
- caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
- | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
+ caps->addDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK, 1);
+ caps->addDetail(MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK, 1);
} else if (omxNode->setPortMode(
kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
omxNode->prepareForAdaptivePlayback(
kPortIndexOutput, OMX_TRUE,
1280 /* width */, 720 /* height */) == OK) {
- caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+ caps->addDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK, 1);
}
}
@@ -248,7 +248,7 @@ status_t queryOmxCapabilities(
if (omxNode->getConfig(
(OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
&params, sizeof(params)) == OK) {
- caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+ caps->addDetail(MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH, 1);
}
}
@@ -271,12 +271,25 @@ void buildOmxInfo(const MediaCodecsXmlParser& parser,
writer->addMediaCodecInfo();
info->setName(name.c_str());
info->setOwner("default");
- info->setEncoder(encoder);
+ typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
+ if (encoder) {
+ attrs |= MediaCodecInfo::kFlagIsEncoder;
+ }
+ // NOTE: we don't support software-only codecs in OMX
+ if (!hasPrefix(name, "OMX.google.")) {
+ attrs |= MediaCodecInfo::kFlagIsVendor;
+ if (properties.quirkSet.find("attribute::software-codec")
+ == properties.quirkSet.end()) {
+ attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
+ }
+ }
+ info->setAttributes(attrs);
info->setRank(omxRank);
- for (const MediaCodecsXmlParser::Type& type : properties.typeMap) {
- const std::string &mime = type.first;
+ // OMX components don't have aliases
+ for (const MediaCodecsXmlParser::Type &type : properties.typeMap) {
+ const std::string &mediaType = type.first;
std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
- info->addMime(mime.c_str());
+ info->addMediaType(mediaType.c_str());
const MediaCodecsXmlParser::AttributeMap &attrMap = type.second;
for (const MediaCodecsXmlParser::Attribute& attr : attrMap) {
const std::string &key = attr.first;
@@ -290,13 +303,13 @@ void buildOmxInfo(const MediaCodecsXmlParser& parser,
}
status_t err = queryOmxCapabilities(
name.c_str(),
- mime.c_str(),
+ mediaType.c_str(),
encoder,
caps.get());
if (err != OK) {
- ALOGE("Failed to query capabilities for %s (mime: %s). Error: %d",
+ ALOGI("Failed to query capabilities for %s (media type: %s). Error: %d",
name.c_str(),
- mime.c_str(),
+ mediaType.c_str(),
static_cast<int>(err));
}
}
@@ -339,12 +352,13 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
// Obtain Codec2Client
std::vector<Traits> traits = Codec2Client::ListComponents();
- MediaCodecsXmlParser parser(
- MediaCodecsXmlParser::defaultSearchDirs,
- option == 0 ? "media_codecs.xml" :
- "media_codecs_c2.xml",
- option == 0 ? "media_codecs_performance.xml" :
- "media_codecs_performance_c2.xml");
+ MediaCodecsXmlParser parser;
+ if (option == 0) {
+ parser.parseXmlFilesInSearchDirs();
+ } else {
+ parser.parseXmlFilesInSearchDirs(
+ { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" });
+ }
if (parser.getParsingStatus() != OK) {
ALOGD("XML parser no good");
return OK;
@@ -411,18 +425,37 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
ALOGV("canonName = %s", canonName.c_str());
std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
codecInfo->setName(trait.name.c_str());
- codecInfo->setOwner("codec2");
+ codecInfo->setOwner(("codec2::" + trait.owner).c_str());
+ const MediaCodecsXmlParser::CodecProperties &codec = parser.getCodecMap().at(canonName);
+
bool encoder = trait.kind == C2Component::KIND_ENCODER;
- codecInfo->setEncoder(encoder);
+ typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
+
+ if (encoder) {
+ attrs |= MediaCodecInfo::kFlagIsEncoder;
+ }
+ if (trait.owner == "software") {
+ attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
+ } else {
+ attrs |= MediaCodecInfo::kFlagIsVendor;
+ if (trait.owner == "vendor-software") {
+ attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
+ } else if (codec.quirkSet.find("attribute::software-codec") == codec.quirkSet.end()) {
+ attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
+ }
+ }
+ codecInfo->setAttributes(attrs);
codecInfo->setRank(rank);
- const MediaCodecsXmlParser::CodecProperties &codec =
- parser.getCodecMap().at(canonName);
+
+ for (const std::string &alias : codec.aliases) {
+ codecInfo->addAlias(alias.c_str());
+ }
for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
const std::string &mediaType = typeIt->first;
const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
- codecInfo->addMime(mediaType.c_str());
+ codecInfo->addMediaType(mediaType.c_str());
for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
std::string key, value;
std::tie(key, value) = *attrIt;
diff --git a/media/sfplugin/ReflectedParamUpdater.cpp b/media/sfplugin/ReflectedParamUpdater.cpp
index d2a5091..880d4a5 100644
--- a/media/sfplugin/ReflectedParamUpdater.cpp
+++ b/media/sfplugin/ReflectedParamUpdater.cpp
@@ -94,7 +94,7 @@ std::string ReflectedParamUpdater::Dict::debugString(size_t indent_) const {
s << "string " << it.first << " = \"" << strValue.c_str() << "\"";
} else if (it.second.find(&bufValue)) {
s << "Buffer " << it.first << " = ";
- if (bufValue != NULL && bufValue->data() != NULL && bufValue->size() <= 64) {
+ if (bufValue != nullptr && bufValue->data() != nullptr && bufValue->size() <= 64) {
s << "{" << std::endl;
AString tmp;
hexdump(bufValue->data(), bufValue->size(), indent_ + 4, &tmp);
diff --git a/media/sfplugin/SkipCutBuffer.cpp b/media/sfplugin/SkipCutBuffer.cpp
index ee9016d..5762440 100644
--- a/media/sfplugin/SkipCutBuffer.cpp
+++ b/media/sfplugin/SkipCutBuffer.cpp
@@ -29,7 +29,7 @@ SkipCutBuffer::SkipCutBuffer(size_t skip, size_t cut, size_t num16BitChannels) {
mWriteHead = 0;
mReadHead = 0;
mCapacity = 0;
- mCutBuffer = NULL;
+ mCutBuffer = nullptr;
if (num16BitChannels == 0 || num16BitChannels > INT32_MAX / 2) {
ALOGW("# channels out of range: %zu, using passthrough instead", num16BitChannels);
@@ -57,7 +57,7 @@ SkipCutBuffer::~SkipCutBuffer() {
}
void SkipCutBuffer::submit(MediaBuffer *buffer) {
- if (mCutBuffer == NULL) {
+ if (mCutBuffer == nullptr) {
// passthrough mode
return;
}
@@ -90,7 +90,7 @@ void SkipCutBuffer::submit(MediaBuffer *buffer) {
template <typename T>
void SkipCutBuffer::submitInternal(const sp<T>& buffer) {
- if (mCutBuffer == NULL) {
+ if (mCutBuffer == nullptr) {
// passthrough mode
return;
}
diff --git a/media/sfplugin/tests/Android.bp b/media/sfplugin/tests/Android.bp
index f04aa41..b08d3d6 100644
--- a/media/sfplugin/tests/Android.bp
+++ b/media/sfplugin/tests/Android.bp
@@ -19,7 +19,6 @@ cc_test {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
}
@@ -49,6 +48,5 @@ cc_test {
cflags: [
"-Werror",
"-Wall",
- "-std=c++14",
],
}