From 8cbf634cb2923e75110324b8454362ec93bd7c8a Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Fri, 24 Sep 2021 16:48:12 +0800 Subject: [PATCH 01/34] add histreamer media framework. Signed-off-by: Chen Guodong --- .gitignore | 23 + BUILD.gn | 179 ++++- CMakeLists.txt | 21 + README.md | 9 +- README_zh.md | 9 +- engine/CMakeLists.txt | 67 ++ engine/foundation/blocking_queue.h | 172 +++++ engine/foundation/buffer_pool.h | 177 +++++ engine/foundation/constants.cpp | 59 ++ engine/foundation/constants.h | 59 ++ engine/foundation/error_code.h | 50 ++ engine/foundation/event.h | 42 ++ engine/foundation/log.h | 136 ++++ engine/foundation/memory_helper.h | 54 ++ engine/foundation/meta.cpp | 170 +++++ engine/foundation/meta.h | 108 +++ engine/foundation/osal/base/common.h | 40 ++ engine/foundation/osal/base/synchronizer.h | 112 +++ engine/foundation/osal/osal_config.h | 27 + .../osal/platform/ohos/ohos_config.h | 32 + .../osal/thread/condition_variable.cpp | 86 +++ .../osal/thread/condition_variable.h | 85 +++ engine/foundation/osal/thread/mutex.cpp | 78 ++ engine/foundation/osal/thread/mutex.h | 49 ++ engine/foundation/osal/thread/scoped_lock.cpp | 90 +++ engine/foundation/osal/thread/scoped_lock.h | 50 ++ engine/foundation/osal/thread/task.cpp | 131 ++++ engine/foundation/osal/thread/task.h | 76 ++ engine/foundation/osal/thread/thread.cpp | 87 +++ engine/foundation/osal/thread/thread.h | 65 ++ engine/foundation/osal/utils/util.cpp | 58 ++ engine/foundation/osal/utils/util.h | 27 + engine/foundation/singleton.h | 38 + engine/foundation/type_define.h | 41 ++ engine/foundation/utils.h | 41 ++ engine/pipeline/core/compatible_check.cpp | 218 ++++++ engine/pipeline/core/compatible_check.h | 31 + engine/pipeline/core/filter.h | 107 +++ engine/pipeline/core/filter_base.cpp | 195 +++++ engine/pipeline/core/filter_base.h | 126 ++++ engine/pipeline/core/filter_callback.h | 56 ++ engine/pipeline/core/parameter.h | 47 ++ engine/pipeline/core/pipeline.h | 76 ++ engine/pipeline/core/pipeline_core.cpp | 281 ++++++++ engine/pipeline/core/pipeline_core.h | 153 ++++ engine/pipeline/core/port.cpp | 217 ++++++ engine/pipeline/core/port.h | 144 ++++ engine/pipeline/factory/filter_factory.cpp | 48 ++ engine/pipeline/factory/filter_factory.h | 77 ++ .../audio_decoder/audio_decoder_filter.cpp | 482 +++++++++++++ .../audio_decoder/audio_decoder_filter.h | 80 +++ .../filters/codec/decoder_filter_base.cpp | 90 +++ .../filters/codec/decoder_filter_base.h | 67 ++ .../video_decoder/video_decoder_filter.cpp | 446 ++++++++++++ .../video_decoder/video_decoder_filter.h | 97 +++ .../pipeline/filters/common/plugin_utils.cpp | 43 ++ engine/pipeline/filters/common/plugin_utils.h | 86 +++ engine/pipeline/filters/demux/data_packer.cpp | 282 ++++++++ engine/pipeline/filters/demux/data_packer.h | 68 ++ .../pipeline/filters/demux/demuxer_filter.cpp | 474 ++++++++++++ .../pipeline/filters/demux/demuxer_filter.h | 140 ++++ engine/pipeline/filters/demux/type_finder.cpp | 244 +++++++ engine/pipeline/filters/demux/type_finder.h | 78 ++ .../sink/audio_sink/audio_sink_filter.cpp | 290 ++++++++ .../sink/audio_sink/audio_sink_filter.h | 78 ++ .../sink/video_sink/video_sink_filter.cpp | 319 +++++++++ .../sink/video_sink/video_sink_filter.h | 87 +++ .../filters/source/media_source_filter.cpp | 364 ++++++++++ .../filters/source/media_source_filter.h | 82 +++ engine/player/hiplayer.cpp | 251 +++++++ engine/player/hiplayer_impl.cpp | 455 ++++++++++++ engine/player/hiplayer_impl.h | 154 ++++ engine/player/internal/init_state.h | 67 ++ engine/player/internal/pause_state.h | 75 ++ engine/player/internal/playing_state.h | 83 +++ engine/player/internal/prepare_state.h | 77 ++ engine/player/internal/ready_state.h | 68 ++ engine/player/internal/state.cpp | 131 ++++ engine/player/internal/state.h | 104 +++ engine/player/internal/state_machine.cpp | 203 ++++++ engine/player/internal/state_machine.h | 94 +++ engine/player/play_executor.h | 87 +++ engine/plugin/common/any.h | 605 ++++++++++++++++ engine/plugin/common/plugin_audio_tags.h | 171 +++++ engine/plugin/common/plugin_buffer.cpp | 219 ++++++ engine/plugin/common/plugin_buffer.h | 330 +++++++++ engine/plugin/common/plugin_caps.h | 149 ++++ engine/plugin/common/plugin_tags.h | 135 ++++ engine/plugin/common/plugin_types.h | 103 +++ engine/plugin/common/plugin_video_tags.h | 53 ++ engine/plugin/core/all_plugin_static.h | 80 +++ engine/plugin/core/audio_sink.cpp | 45 ++ engine/plugin/core/audio_sink.h | 57 ++ engine/plugin/core/base.cpp | 79 ++ engine/plugin/core/base.h | 75 ++ engine/plugin/core/codec.cpp | 76 ++ engine/plugin/core/codec.h | 68 ++ engine/plugin/core/demuxer.cpp | 50 ++ engine/plugin/core/demuxer.h | 68 ++ engine/plugin/core/plugin_info.h | 74 ++ engine/plugin/core/plugin_loader.cpp | 101 +++ engine/plugin/core/plugin_loader.h | 57 ++ engine/plugin/core/plugin_manager.cpp | 135 ++++ engine/plugin/core/plugin_manager.h | 69 ++ engine/plugin/core/plugin_meta.h | 78 ++ engine/plugin/core/plugin_register.cpp | 344 +++++++++ engine/plugin/core/plugin_register.h | 126 ++++ engine/plugin/core/plugin_wrapper.cpp | 78 ++ engine/plugin/core/plugin_wrapper.h | 74 ++ engine/plugin/core/source.cpp | 49 ++ engine/plugin/core/source.h | 59 ++ engine/plugin/core/video_sink.cpp | 45 ++ engine/plugin/core/video_sink.h | 56 ++ engine/plugin/interface/audio_sink_plugin.h | 223 ++++++ engine/plugin/interface/codec_plugin.h | 162 +++++ engine/plugin/interface/demuxer_plugin.h | 222 ++++++ engine/plugin/interface/plugin_base.h | 217 ++++++ engine/plugin/interface/plugin_definition.h | 202 ++++++ engine/plugin/interface/source_plugin.h | 134 ++++ engine/plugin/interface/video_sink_plugin.h | 111 +++ .../audio_ffmpeg_decoder_plugin.cpp | 562 +++++++++++++++ .../audio_ffmpeg_decoder_plugin.h | 128 ++++ .../demuxer/ffmpeg_demuxer_plugin.cpp | 647 +++++++++++++++++ .../demuxer/ffmpeg_demuxer_plugin.h | 115 +++ .../demuxer/ffmpeg_track_meta.cpp | 192 +++++ .../demuxer/ffmpeg_track_meta.h | 56 ++ .../utils/aac_audio_config_parser.cpp | 341 +++++++++ .../utils/aac_audio_config_parser.h | 75 ++ .../utils/avc_config_data_parser.cpp | 258 +++++++ .../utils/avc_config_data_parser.h | 81 +++ .../ffmpeg_adapter/utils/bit_reader.cpp | 88 +++ .../plugins/ffmpeg_adapter/utils/bit_reader.h | 105 +++ .../ffmpeg_adapter/utils/ffmpeg_utils.cpp | 304 ++++++++ .../ffmpeg_adapter/utils/ffmpeg_utils.h | 95 +++ .../video_ffmpeg_decoder_plugin.cpp | 658 +++++++++++++++++ .../video_ffmpeg_decoder_plugin.h | 142 ++++ .../plugins/hdi_adapter/sink/hos_au_sink.cpp | 675 ++++++++++++++++++ .../plugins/hdi_adapter/sink/hos_au_sink.h | 122 ++++ .../plugins/hdi_adapter/sink/ring_buffer.cpp | 130 ++++ .../plugins/hdi_adapter/sink/ring_buffer.h | 62 ++ .../hdi_adapter/utils/hdi_au_utils.cpp | 140 ++++ .../plugins/hdi_adapter/utils/hdi_au_utils.h | 52 ++ .../sdl/audio_sink/sdl_audio_sink_plugin.cpp | 354 +++++++++ .../sdl/audio_sink/sdl_audio_sink_plugin.h | 114 +++ engine/plugin/plugins/sink/sdl/ring_buffer.h | 106 +++ .../sdl/video_sink/sdl_video_sink_plugin.cpp | 494 +++++++++++++ .../sdl/video_sink/sdl_video_sink_plugin.h | 110 +++ .../plugins/source/file_source/CMakeLists.txt | 20 + .../source/file_source/file_source_plugin.cpp | 309 ++++++++ .../source/file_source/file_source_plugin.h | 77 ++ .../stream_source/stream_source_plugin.cpp | 293 ++++++++ .../stream_source/stream_source_plugin.h | 118 +++ interface/histreamer/hiplayer.h | 67 ++ tests/CMakeLists.txt | 16 + tests/main.cpp | 19 + tests/ut/CMakeLists.txt | 93 +++ tests/ut/TestAny.cpp | 235 ++++++ tests/ut/TestBitReader.cpp | 107 +++ tests/ut/TestBufferPool.cpp | 69 ++ tests/ut/TestCompatibleCheck.cpp | 209 ++++++ tests/ut/TestFFmpegUtils.cpp | 47 ++ tests/ut/TestFilter.cpp | 49 ++ tests/ut/TestHiPlayer.cpp | 119 +++ tests/ut/TestMeta.cpp | 127 ++++ tests/ut/TestMetaBundle.cpp | 91 +++ tests/ut/TestMimeDefs.cpp | 48 ++ tests/ut/TestStateMachine.cpp | 160 +++++ tests/ut/TestSynchronizer.cpp | 117 +++ 168 files changed, 23611 insertions(+), 34 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 engine/CMakeLists.txt create mode 100644 engine/foundation/blocking_queue.h create mode 100644 engine/foundation/buffer_pool.h create mode 100644 engine/foundation/constants.cpp create mode 100644 engine/foundation/constants.h create mode 100644 engine/foundation/error_code.h create mode 100644 engine/foundation/event.h create mode 100644 engine/foundation/log.h create mode 100644 engine/foundation/memory_helper.h create mode 100644 engine/foundation/meta.cpp create mode 100644 engine/foundation/meta.h create mode 100644 engine/foundation/osal/base/common.h create mode 100644 engine/foundation/osal/base/synchronizer.h create mode 100644 engine/foundation/osal/osal_config.h create mode 100644 engine/foundation/osal/platform/ohos/ohos_config.h create mode 100644 engine/foundation/osal/thread/condition_variable.cpp create mode 100644 engine/foundation/osal/thread/condition_variable.h create mode 100644 engine/foundation/osal/thread/mutex.cpp create mode 100644 engine/foundation/osal/thread/mutex.h create mode 100644 engine/foundation/osal/thread/scoped_lock.cpp create mode 100644 engine/foundation/osal/thread/scoped_lock.h create mode 100644 engine/foundation/osal/thread/task.cpp create mode 100644 engine/foundation/osal/thread/task.h create mode 100644 engine/foundation/osal/thread/thread.cpp create mode 100644 engine/foundation/osal/thread/thread.h create mode 100644 engine/foundation/osal/utils/util.cpp create mode 100644 engine/foundation/osal/utils/util.h create mode 100644 engine/foundation/singleton.h create mode 100644 engine/foundation/type_define.h create mode 100644 engine/foundation/utils.h create mode 100644 engine/pipeline/core/compatible_check.cpp create mode 100644 engine/pipeline/core/compatible_check.h create mode 100644 engine/pipeline/core/filter.h create mode 100644 engine/pipeline/core/filter_base.cpp create mode 100644 engine/pipeline/core/filter_base.h create mode 100644 engine/pipeline/core/filter_callback.h create mode 100644 engine/pipeline/core/parameter.h create mode 100644 engine/pipeline/core/pipeline.h create mode 100644 engine/pipeline/core/pipeline_core.cpp create mode 100644 engine/pipeline/core/pipeline_core.h create mode 100644 engine/pipeline/core/port.cpp create mode 100644 engine/pipeline/core/port.h create mode 100644 engine/pipeline/factory/filter_factory.cpp create mode 100644 engine/pipeline/factory/filter_factory.h create mode 100644 engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp create mode 100644 engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.h create mode 100644 engine/pipeline/filters/codec/decoder_filter_base.cpp create mode 100644 engine/pipeline/filters/codec/decoder_filter_base.h create mode 100644 engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp create mode 100644 engine/pipeline/filters/codec/video_decoder/video_decoder_filter.h create mode 100644 engine/pipeline/filters/common/plugin_utils.cpp create mode 100644 engine/pipeline/filters/common/plugin_utils.h create mode 100644 engine/pipeline/filters/demux/data_packer.cpp create mode 100644 engine/pipeline/filters/demux/data_packer.h create mode 100644 engine/pipeline/filters/demux/demuxer_filter.cpp create mode 100644 engine/pipeline/filters/demux/demuxer_filter.h create mode 100644 engine/pipeline/filters/demux/type_finder.cpp create mode 100644 engine/pipeline/filters/demux/type_finder.h create mode 100644 engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp create mode 100644 engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h create mode 100644 engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp create mode 100644 engine/pipeline/filters/sink/video_sink/video_sink_filter.h create mode 100644 engine/pipeline/filters/source/media_source_filter.cpp create mode 100644 engine/pipeline/filters/source/media_source_filter.h create mode 100644 engine/player/hiplayer.cpp create mode 100644 engine/player/hiplayer_impl.cpp create mode 100644 engine/player/hiplayer_impl.h create mode 100644 engine/player/internal/init_state.h create mode 100644 engine/player/internal/pause_state.h create mode 100644 engine/player/internal/playing_state.h create mode 100644 engine/player/internal/prepare_state.h create mode 100644 engine/player/internal/ready_state.h create mode 100644 engine/player/internal/state.cpp create mode 100644 engine/player/internal/state.h create mode 100644 engine/player/internal/state_machine.cpp create mode 100644 engine/player/internal/state_machine.h create mode 100644 engine/player/play_executor.h create mode 100644 engine/plugin/common/any.h create mode 100644 engine/plugin/common/plugin_audio_tags.h create mode 100644 engine/plugin/common/plugin_buffer.cpp create mode 100644 engine/plugin/common/plugin_buffer.h create mode 100644 engine/plugin/common/plugin_caps.h create mode 100644 engine/plugin/common/plugin_tags.h create mode 100644 engine/plugin/common/plugin_types.h create mode 100644 engine/plugin/common/plugin_video_tags.h create mode 100644 engine/plugin/core/all_plugin_static.h create mode 100644 engine/plugin/core/audio_sink.cpp create mode 100644 engine/plugin/core/audio_sink.h create mode 100644 engine/plugin/core/base.cpp create mode 100644 engine/plugin/core/base.h create mode 100644 engine/plugin/core/codec.cpp create mode 100644 engine/plugin/core/codec.h create mode 100644 engine/plugin/core/demuxer.cpp create mode 100644 engine/plugin/core/demuxer.h create mode 100644 engine/plugin/core/plugin_info.h create mode 100644 engine/plugin/core/plugin_loader.cpp create mode 100644 engine/plugin/core/plugin_loader.h create mode 100644 engine/plugin/core/plugin_manager.cpp create mode 100644 engine/plugin/core/plugin_manager.h create mode 100644 engine/plugin/core/plugin_meta.h create mode 100644 engine/plugin/core/plugin_register.cpp create mode 100644 engine/plugin/core/plugin_register.h create mode 100644 engine/plugin/core/plugin_wrapper.cpp create mode 100644 engine/plugin/core/plugin_wrapper.h create mode 100644 engine/plugin/core/source.cpp create mode 100644 engine/plugin/core/source.h create mode 100644 engine/plugin/core/video_sink.cpp create mode 100644 engine/plugin/core/video_sink.h create mode 100644 engine/plugin/interface/audio_sink_plugin.h create mode 100644 engine/plugin/interface/codec_plugin.h create mode 100644 engine/plugin/interface/demuxer_plugin.h create mode 100644 engine/plugin/interface/plugin_base.h create mode 100644 engine/plugin/interface/plugin_definition.h create mode 100644 engine/plugin/interface/source_plugin.h create mode 100644 engine/plugin/interface/video_sink_plugin.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h create mode 100644 engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp create mode 100644 engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h create mode 100644 engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp create mode 100644 engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h create mode 100644 engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp create mode 100644 engine/plugin/plugins/hdi_adapter/sink/ring_buffer.h create mode 100644 engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp create mode 100644 engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.h create mode 100644 engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp create mode 100644 engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h create mode 100644 engine/plugin/plugins/sink/sdl/ring_buffer.h create mode 100644 engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp create mode 100644 engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h create mode 100644 engine/plugin/plugins/source/file_source/CMakeLists.txt create mode 100644 engine/plugin/plugins/source/file_source/file_source_plugin.cpp create mode 100644 engine/plugin/plugins/source/file_source/file_source_plugin.h create mode 100644 engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp create mode 100644 engine/plugin/plugins/source/stream_source/stream_source_plugin.h create mode 100644 interface/histreamer/hiplayer.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/main.cpp create mode 100644 tests/ut/CMakeLists.txt create mode 100644 tests/ut/TestAny.cpp create mode 100644 tests/ut/TestBitReader.cpp create mode 100644 tests/ut/TestBufferPool.cpp create mode 100644 tests/ut/TestCompatibleCheck.cpp create mode 100644 tests/ut/TestFFmpegUtils.cpp create mode 100644 tests/ut/TestFilter.cpp create mode 100644 tests/ut/TestHiPlayer.cpp create mode 100644 tests/ut/TestMeta.cpp create mode 100644 tests/ut/TestMetaBundle.cpp create mode 100644 tests/ut/TestMimeDefs.cpp create mode 100644 tests/ut/TestStateMachine.cpp create mode 100644 tests/ut/TestSynchronizer.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..456a3c7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +build*/ +.vscode/ +.idea/ +.ccls +.ccls-cache +.clang-format +__pycache__ +cmake-build-debug/ +ffmpeg-player.exe +tests/it/CMakeFiles/ +tests/it/CTestTestfile.cmake +tests/it/Makefile +tests/it/cmake_install.cmake +tests/ut/CMakeFiles/ +tests/ut/CTestTestfile.cmake +tests/ut/Makefile +tests/ut/cmake_install.cmake +CMakeCache.txt +CMakeFiles/ +Makefile +cmake_install.cmake +*.mp3 +*.mp4 diff --git a/BUILD.gn b/BUILD.gn index 2a24a0e9..5353cc6f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,26 +1,153 @@ -# Copyright (C) 2021 Huawei Device Co., Ltd. -# 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. - -import("//build/ohos.gni") - -group("histreamer_packages") { - deps = [ - "//third_party/glib:glib_packages", - "//third_party/gstreamer/gst_libav:gst_libav_packages", - "//third_party/gstreamer/gstplugins_bad:gstplugins_bad_packages", - "//third_party/gstreamer/gstplugins_base:gstplugins_base_packages", - "//third_party/gstreamer/gstplugins_good:gstplugins_good_packages", - "//third_party/gstreamer/gstreamer:gstreamer_packages", - "//third_party/libffi:ffi", - ] -} +# Copyright (c) 2021-2021 Huawei Device Co., Ltd. +# 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. +# + +declare_args() { + compile_histreamer = false +} + +if (compile_histreamer) { + import("//build/lite/config/component/lite_component.gni") + import("//build/lite/ndk/ndk.gni") + + gcc_arm_path = getenv("GCCARM") + + print("gcc_arm_path: ", gcc_arm_path) + shared_library("histreamer") { + sources = [ + "engine/foundation/osal/thread/task.cpp", + "engine/foundation/osal/thread/condition_variable.cpp", + "engine/foundation/osal/thread/mutex.cpp", + "engine/foundation/osal/thread/scoped_lock.cpp", + "engine/foundation/osal/thread/thread.cpp", + "engine/foundation/osal/utils/util.cpp", + "engine/foundation/meta.cpp", + "engine/foundation/constants.cpp", + "engine/pipeline/filters/codec/decoder_filter_base.cpp", + "engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp", + "engine/pipeline/filters/common/plugin_utils.cpp", + "engine/pipeline/filters/demux/demuxer_filter.cpp", + "engine/pipeline/filters/demux/type_finder.cpp", + "engine/pipeline/filters/demux/data_packer.cpp", + "engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp", + "engine/pipeline/filters/source/media_source_filter.cpp", + "engine/pipeline/factory/filter_factory.cpp", + "engine/pipeline/core/compatible_check.cpp", + "engine/pipeline/core/filter_base.cpp", + "engine/pipeline/core/pipeline_core.cpp", + "engine/pipeline/core/port.cpp", + "engine/player/internal/state.cpp", + "engine/player/internal/state_machine.cpp", + "engine/player/hiplayer.cpp", + "engine/player/hiplayer_impl.cpp", + "engine/plugin/common/plugin_buffer.cpp", + "engine/plugin/core/base.cpp", + "engine/plugin/core/audio_sink.cpp", + "engine/plugin/core/video_sink.cpp", + "engine/plugin/core/codec.cpp", + "engine/plugin/core/demuxer.cpp", + "engine/plugin/core/plugin_loader.cpp", + "engine/plugin/core/plugin_wrapper.cpp", + "engine/plugin/core/plugin_manager.cpp", + "engine/plugin/core/plugin_register.cpp", + "engine/plugin/core/source.cpp", + "engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp", + "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp", + "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp", + "engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp", + "engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp", + "engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp", + "engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp", + "engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp", + "engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp", + "engine/plugin/plugins/source/file_source/file_source_plugin.cpp", + "engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp", + ] + defines = [ "__NEED_wint_t", "OSAL_OHOS", "__STDC_FORMAT_MACROS", "__HOS_LITE_SINK__", "HI3516DV300" ] + cflags = [ "-O2", "-fPIC", "-Wall" ] + cflags_cc = cflags + include_dirs = [ + "interface", + "engine", + "engine/foundation", + "engine/foundation/osal", + "engine/pipeline", + "engine/player", + "engine/pipeline/filters", + "engine/pipeline/core", + "engine/plugin", + "${gcc_arm_path}/../arm-none-eabi/include", + "//foundation/multimedia/media_lite/interfaces", + "//foundation/multimedia/drivers/audio/include", + "//foundation/multimedia/drivers/codec/include", + "//foundation/multimedia/drivers/format/include", + "//foundation/multimedia/utils/lite/interfaces/kits", + "//foundation/multimedia/media_lite/interfaces/kits/player_lite", + "//base/hiviewdfx/hilog_lite/interfaces/native/kits/hilog", + "//kernel/liteos_m/kal/posix/include/", + "//kernel/liteos_m/kernel/include", + "//kernel/liteos_m/kernel/arch/include", + "//drivers/peripheral/audio/interfaces/include", + "//drivers/peripheral/base", + "//drivers/peripheral/audio/hal/hdi_binder/proxy/include", + "//drivers/peripheral/audio/interfaces/include", + "//third_party/ffmpeg", + "//third_party/bounds_checking_function/include", + ] + outdir = rebase_path("$root_out_dir") + #public_configs = [ ":player_external_library_config" ] + ldflags = [ "-L$outdir" ] + ldflags += [ "-laudio_hw" ] + ldflags += [ "-lcodec" ] + ldflags += [ "-lformat_hw" ] + ldflags += [ "-lavformat" ] + ldflags += [ "-lavcodec" ] + ldflags += [ "-lavutil" ] + ldflags += [ "-lhdi_videodisplayer" ] + ldflags += [ "-lmedia_common" ] + + deps = [ + "//foundation/graphic/surface:lite_surface", + "//device/hisilicon/hardware:hardware_media_sdk", + "//device/hisilicon/modules/middleware:middleware_source_sdk", + ] + public_deps = [ + #"//foundation/multimedia/utils/lite:media_common", + #"//kernel/liteos_m/kal/posix:posix", + "//third_party/bounds_checking_function:libsec_shared", + ] + } + + # for test only. + copy("music_files") { + sources = [ + "//foundation/multimedia/histreamer/tests/resource/dream_it_possible.mp3", + ] + outputs = ["$root_out_dir/data/{{source_file_part}}"] + } + +} else { + import("//build/ohos.gni") + + group("histreamer_packages") { + deps = [ + "//third_party/glib:glib_packages", + "//third_party/gstreamer/gst_libav:gst_libav_packages", + "//third_party/gstreamer/gstplugins_bad:gstplugins_bad_packages", + "//third_party/gstreamer/gstplugins_base:gstplugins_base_packages", + "//third_party/gstreamer/gstplugins_good:gstplugins_good_packages", + "//third_party/gstreamer/gstreamer:gstreamer_packages", + "//third_party/libffi:ffi", + ] + } +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..2e21b48c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ + +CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) + +project(histreamer) + +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") +else () +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") +endif () + +set(CMAKE_CXX_STANDARD 11) + +set(TOP_DIR ${CMAKE_SOURCE_DIR}) + +add_definitions(-DOSAL_OHOS) # add -DVIDEO_SUPPORT to enable video play + +set(CMAKE_VERBOSE_MAKEFILE ON) + +add_subdirectory(engine/plugin/plugins/source/file_source) +add_subdirectory(tests) diff --git a/README.md b/README.md index d7fa6920..ad57f7ea 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # HiStreamer -- [Introduction](#section1158716411637) -- [Directory Structure](#section161941989596) -- [Repositories Involved](#section1533973044317) +- [HiStreamer](#histreamer) + - [Introduction](#introduction) + - [Directory Structure](#directory-structure) + - [Repositories Involved](#repositories-involved) ## Introduction -HiStreamer, based on the open-source GStreamer, is the foundation module of the multimedia subsystem. It provides a processing pipeline and plug-ins required by the media framework, such as the file source, codecs, muxer and demuxer, and audio and video data processor. +HiStreamer is the foundation module of the multimedia subsystem. It provides a processing pipeline and plug-ins required by the media framework, such as the file source, codecs, muxer and demuxer, and audio and video data processor. ## Directory Structure diff --git a/README_zh.md b/README_zh.md index 2a5ea1f9..f2c11029 100755 --- a/README_zh.md +++ b/README_zh.md @@ -1,12 +1,13 @@ # 媒体单框架系统底座组件 -- [简介](#section1158716411637) -- [目录](#section161941989596) -- [相关仓](#section1533973044317) +- [媒体单框架系统底座组件](#媒体单框架系统底座组件) + - [简介](#简介) + - [目录](#目录) + - [相关仓](#相关仓) ## 简介 -HiStreamer系统底座组件,基于开源软件GStreamer,提供媒体框架所需的流水线处理,媒体源,编解码,封装解封装,音视频数据处理算法等插件能力。 +HiStreamer系统底座组件,提供媒体框架所需的流水线处理,媒体源,编解码,封装解封装,音视频数据处理算法等插件能力。 ## 目录 diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 00000000..6e5d170f --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,67 @@ + +IF(MSVC) + ADD_DEFINITIONS(-DMSVC_VMG_ENABLED /std:c++11 ) # /Z7) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /vmg") +ENDIF(MSVC) + +#IF(UNIX) +#MESSAGE("UNIX") +#ADD_DEFINITIONS( +# -g -ggdb +#) +#ENDIF(UNIX) + +IF(UNIX AND NOT CYGWIN) +ADD_DEFINITIONS( + -fPIC +) +ENDIF(UNIX AND NOT CYGWIN) + +ADD_DEFINITIONS( + -std=c++11 +) + +IF(CMAKE_CL_64) + ADD_DEFINITIONS(-DWIN64) +ENDIF(CMAKE_CL_64) + + +###################################################### +# include directories +include_directories( + ${TOP_DIR}/interface + ${TOP_DIR}/engine + ${TOP_DIR}/engine/external + ${TOP_DIR}/engine/player + ${TOP_DIR}/engine/pipeline + ${TOP_DIR}/engine/pipeline/core + ${TOP_DIR}/engine/foundation + ${TOP_DIR}/engine/foundation/osal + ${TOP_DIR}/engine/pipeline/filters + ${TOP_DIR}/engine/pipeline/filters/player + ${TOP_DIR}/engine/plugin + ${TOP_DIR}/engine/3rdparty/curl/include +) + +###################################################### +#file(GLOB_RECURSE MY_SRCS ./*.cpp) # source files from all subdirectories recursely + +file(GLOB_RECURSE HISTREAMER_SRCS + ${TOP_DIR}/engine/pipeline/*.cpp + ${TOP_DIR}/engine/plugin/common/*.cpp + ${TOP_DIR}/engine/plugin/core/*.cpp + ${TOP_DIR}/engine/plugin/plugins/ffmpeg_adapter/*.cpp + ${TOP_DIR}/engine/plugin/plugins/sink/*.cpp + ${TOP_DIR}/engine/plugin/plugins/source/file_source/*.cpp + ${TOP_DIR}/engine/plugin/plugins/source/stream_source/*.cpp + ${TOP_DIR}/engine/plugin/types/*.cpp + ${TOP_DIR}/engine/player/*.cpp + ${TOP_DIR}/engine/foundation/*.cpp + ${TOP_DIR}/engine/external/*.cpp + ) + +file(GLOB_RECURSE PLUGINS_STATIC_BUILD_SRCS + ${TOP_DIR}/engine/plugin/plugins/source/file_source/*.cpp + ) + +INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../include) diff --git a/engine/foundation/blocking_queue.h b/engine/foundation/blocking_queue.h new file mode 100644 index 00000000..104d3612 --- /dev/null +++ b/engine/foundation/blocking_queue.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_BLOCKING_QUEUE_H +#define HISTREAMER_FOUNDATION_BLOCKING_QUEUE_H + +#include +#include +#include +#include "constants.h" +#include "foundation/log.h" +#include "osal/thread/condition_variable.h" +#include "osal/thread/mutex.h" +#include "osal/thread/scoped_lock.h" + +namespace OHOS { +namespace Media { +template +class BlockingQueue { +public: + explicit BlockingQueue(const std::string& name, size_t capacity = DEFAULT_QUEUE_SIZE) + : name_(name), capacity_(capacity), isActive(true) + { + } + ~BlockingQueue() = default; + size_t Size() + { + OSAL::ScopedLock lock(mutex_); + return que_.size(); + } + size_t Capacity() + { + return capacity_; + } + size_t Empty() + { + OSAL::ScopedLock lock(mutex_); + return que_.empty(); + } + bool Push(const T& value) + { + OSAL::ScopedLock lock(mutex_); + if (!isActive) { + MEDIA_LOG_D("blocking queue %s is inactive for Push.", name_.c_str()); + return false; + } + if (que_.size() >= capacity_) { + MEDIA_LOG_D("blocking queue %s is full, waiting for pop.", name_.c_str()); + cvFull_.Wait(lock, [this] { return !isActive || que_.size() < capacity_; }); + } + if (!isActive) { + MEDIA_LOG_D("blocking queue %s: inactive: %d, isFull: %d", name_.c_str(), isActive.load(), + que_.size() < capacity_); + return false; + } + que_.push(value); + cvEmpty_.NotifyAll(); + MEDIA_LOG_D("blocking queue %s Push succeed.", name_.c_str()); + return true; + } + bool Push(const T& value, int timeoutMs) + { + OSAL::ScopedLock lock(mutex_); + if (!isActive) { + MEDIA_LOG_D("blocking queue %s is inactive for Push.", name_.c_str()); + return false; + } + if (que_.size() >= capacity_) { + MEDIA_LOG_D("blocking queue is full, waiting for pop..."); + cvFull_.WaitFor(lock, timeoutMs, [this] { return !isActive || que_.size() < capacity_; }); + } + if (!isActive || (que_.size() == capacity_)) { + MEDIA_LOG_D("blocking queue: inactive: %d, isFull: %d", isActive, que_.size() < capacity_); + return false; + } + que_.push(value); + cvEmpty_.NotifyAll(); + return true; + } + T Pop() + { + MEDIA_LOG_D("blocking queue %s Pop enter.", name_.c_str()); + OSAL::ScopedLock lock(mutex_); + if (!isActive) { + MEDIA_LOG_D("blocking queue %s is inactive.", name_.c_str()); + return {}; + } + if (que_.empty()) { + MEDIA_LOG_D("blocking queue %s is empty, waiting for push", name_.c_str()); + cvEmpty_.Wait(lock, [this] { return !isActive || !que_.empty(); }); + } + if (!isActive) { + return {}; + } + T el = que_.front(); + que_.pop(); + cvFull_.NotifyOne(); + MEDIA_LOG_D("blocking queue %s Pop succeed.", name_.c_str()); + return el; + } + T Pop(int timeoutMs) + { + OSAL::ScopedLock lock(mutex_); + if (!isActive) { + MEDIA_LOG_D("blocking queue %s is inactive.", name_.c_str()); + return {}; + } + if (que_.empty()) { + MEDIA_LOG_D("blocking queue %s is empty, waiting for push", name_.c_str()); + cvEmpty_.WaitFor(lock, timeoutMs, [this] { return !isActive || !que_.empty(); }); + } + if (!isActive || que_.empty()) { + return {}; + } + T el = que_.front(); + que_.pop(); + cvFull_.NotifyOne(); + return el; + } + void Clear() + { + OSAL::ScopedLock lock(mutex_); + ClearUnprotected(); + } + void SetActive(bool active) + { + OSAL::ScopedLock lock(mutex_); + MEDIA_LOG_D("SetActive for %s: %d.", name_.c_str(), active); + isActive = active; + if (!active) { + ClearUnprotected(); + cvEmpty_.NotifyOne(); + } + } + +private: + void ClearUnprotected() + { + if (que_.empty()) { + return; + } + bool needNotify = que_.size() == capacity_; + std::queue().swap(que_); + if (needNotify) { + cvFull_.NotifyOne(); + } + } + + OSAL::Mutex mutex_; + OSAL::ConditionVariable cvFull_; + OSAL::ConditionVariable cvEmpty_; + + std::string name_; + std::queue que_; + const size_t capacity_; + std::atomic isActive; +}; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_BLOCKING_QUEUE_H diff --git a/engine/foundation/buffer_pool.h b/engine/foundation/buffer_pool.h new file mode 100644 index 00000000..8174bd45 --- /dev/null +++ b/engine/foundation/buffer_pool.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_BUFFER_POOL_H +#define HISTREAMER_FOUNDATION_BUFFER_POOL_H + +#include +#include +#include +#include "foundation/constants.h" +#include "osal/thread/condition_variable.h" +#include "osal/thread/mutex.h" +#include "osal/thread/scoped_lock.h" +#include "plugin/common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +template +class BufferPool : public std::enable_shared_from_this> { +public: + explicit BufferPool(size_t poolSize) : poolSize_(poolSize), isActive_(true), allocInProgress(false) + { + } + + BufferPool(const BufferPool&) = delete; + + BufferPool& operator=(const BufferPool&) = delete; + + ~BufferPool() + { + isActive_ = false; + { + OSAL::ScopedLock lock(mutex_); + cv_.NotifyOne(); + } + FinishAllocInProgress(); + } + + static std::shared_ptr> Create(size_t poolSize) + { + return std::make_shared>(poolSize); + } + + void Init(size_t msgSize = DEFAULT_FRAME_SIZE, Plugin::BufferMetaType type = Plugin::BufferMetaType::AUDIO, + size_t align = 0) + { + if (msgSize_ == msgSize && align == align_) { + return; + } + msgSize_ = msgSize; + align_ = align; + freeBuffers_.clear(); + for (size_t i = 0; i < poolSize_; ++i) { + auto buf = new Plugin::Buffer(type); + buf->AllocMemory(nullptr, msgSize); + freeBuffers_.emplace_back(std::unique_ptr(buf)); + } + } + + bool Append(std::unique_ptr buffer) + { + OSAL::ScopedLock lock(mutex_); + if (!isActive_ || freeBuffers_.size() >= poolSize_) { + return false; + } + freeBuffers_.emplace_back(std::move(buffer)); + cv_.NotifyOne(); + return true; + } + + void SetActive(bool active) + { + OSAL::ScopedLock lock(mutex_); + if (!active) { + cv_.NotifyOne(); + } + isActive_ = active; + } + + void RecycleBuffer(std::unique_ptr buffer) const + { + OSAL::ScopedLock lock(mutex_); + freeBuffers_.emplace_back(std::move(buffer)); + cv_.NotifyOne(); + } + + std::shared_ptr AllocateBuffer() + { + OSAL::ScopedLock lock(mutex_); + allocInProgress = true; + if (freeBuffers_.empty() && isActive_) { + cv_.Wait(lock, [this] { return !isActive_ || !freeBuffers_.empty(); }); + } + std::shared_ptr buffer; + if (isActive_) { + buffer = AllocateBufferUnprotected(); + } + allocInProgress = false; + cvFinishAlloc_.NotifyOne(); + return buffer; + } + + std::shared_ptr AllocateBufferNonBlocking() + { + OSAL::ScopedLock lock(mutex_); + if (freeBuffers_.empty() || !isActive_) { + return nullptr; + } + return AllocateBufferUnprotected(); + } + + size_t Size() const + { + OSAL::ScopedLock lock(mutex_); + return freeBuffers_.size(); + } + + size_t Capacity() const + { + return poolSize_; + } + + bool Empty() const + { + OSAL::ScopedLock lock(mutex_); + return freeBuffers_.empty(); + } + +private: + std::shared_ptr AllocateBufferUnprotected() + { + std::weak_ptr> weakRef(this->shared_from_this()); + std::shared_ptr sptr(freeBuffers_.front().release(), [weakRef](T* ptr) { + auto pool = weakRef.lock(); + if (pool) { + pool->RecycleBuffer(std::unique_ptr(ptr)); + } else { + delete ptr; + } + }); + freeBuffers_.pop_front(); + return sptr; + } + + void FinishAllocInProgress() + { + OSAL::ScopedLock lock(mutex_); + if (allocInProgress.load()) { + cvFinishAlloc_.Wait(lock, [this] { return !allocInProgress.load(); }); + } + } + + mutable OSAL::Mutex mutex_; + mutable OSAL::ConditionVariable cv_; + mutable OSAL::ConditionVariable cvFinishAlloc_; + mutable std::list> freeBuffers_; + size_t poolSize_ {0}; + size_t msgSize_ {0}; + size_t align_ {0}; // 0: use default alignment. + std::atomic isActive_; + std::atomic allocInProgress; +}; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_BUFFER_POOL_H diff --git a/engine/foundation/constants.cpp b/engine/foundation/constants.cpp new file mode 100644 index 00000000..bd129c8c --- /dev/null +++ b/engine/foundation/constants.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "constants.h" +#include + +namespace OHOS { +namespace Media { +const char* const PORT_NAME_DEFAULT = "default"; + +const char* const MEDIA_MIME_AUDIO_MPEG = "audio/mpeg"; +const char* const MEDIA_MIME_AUDIO_MP3 = "audio/mp3"; +const char* const MEDIA_MIME_AUDIO_FLAC = "audio/flac"; +const char* const MEDIA_MIME_AUDIO_RAW = "audio/raw"; +const char* const MEDIA_MIME_AUDIO_APE = "audio/ape"; +const char* const MEDIA_MIME_AUDIO_WAV = "audio/wav"; +const char* const MEDIA_MIME_AUDIO_AAC = "audio/aac"; +const char* const MEDIA_MIME_AUDIO_AAC_ADTS = "audio/aac-adts"; +const char* const MEDIA_MIME_AUDIO_AAC_LATM = "audio/aac-latm"; +const char* const MEDIA_MIME_AUDIO_VORBIS = "audio/vorbis"; +const char* const MEDIA_MIME_AUDIO_OPUS = "audio/opus"; +const char* const MEDIA_MIME_AUDIO_AC3 = "audio/ac3"; +const char* const MEDIA_MIME_AUDIO_EAC3 = "audio/eac3"; +const char* const MEDIA_MIME_AUDIO_EAC3_JOC = "audio/eac3-joc"; +const char* const MEDIA_MIME_AUDIO_AC4 = "audio/ac4"; +const char* const MEDIA_MIME_AUDIO_WMA = "audio/x-ms-wma"; +const char* const MEDIA_MIME_AUDIO_AMR_NB = "audio/3gpp"; +const char* const MEDIA_MIME_AUDIO_AMR_WB = "audio/amr-wb"; + +const char* const MEDIA_MIME_VIDEO_RAW = "video/raw"; +const char* const MEDIA_MIME_VIDEO_AVC = "video/h264"; + +bool IsAudioMime(const std::string& mime) +{ + return mime.find("audio/") == 0; +} +bool IsVideoMime(const std::string& mime) +{ + return mime.find("video/") == 0; +} + +bool IsRawAudio(const std::string& mime) +{ + return mime == MEDIA_MIME_AUDIO_RAW; +} +} // namespace Media +} // namespace OHOS diff --git a/engine/foundation/constants.h b/engine/foundation/constants.h new file mode 100644 index 00000000..b5144b52 --- /dev/null +++ b/engine/foundation/constants.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_CONSTANTS_H +#define HISTREAMER_FOUNDATION_CONSTANTS_H + +#include + +namespace OHOS { +namespace Media { +constexpr size_t DEFAULT_QUEUE_SIZE = 10; +constexpr int DEFAULT_FRAME_SIZE = 2 * 1024; // 2kb +constexpr size_t DEFAULT_POOL_SIZE = 10; +constexpr uint32_t MAX_PORT_NUMBER = 1; // max in/out port in filter +constexpr uint32_t MAX_ROUTE_NUMBER = 1; // max route in filter +extern const char* const PORT_NAME_DEFAULT; + +// audio mime +extern const char* const MEDIA_MIME_AUDIO_MPEG; +extern const char* const MEDIA_MIME_AUDIO_MP3; +extern const char* const MEDIA_MIME_AUDIO_FLAC; +extern const char* const MEDIA_MIME_AUDIO_RAW; +extern const char* const MEDIA_MIME_AUDIO_APE; +extern const char* const MEDIA_MIME_AUDIO_WAV; +extern const char* const MEDIA_MIME_AUDIO_AAC; +extern const char* const MEDIA_MIME_AUDIO_AAC_ADTS; +extern const char* const MEDIA_MIME_AUDIO_AAC_LATM; +extern const char* const MEDIA_MIME_AUDIO_VORBIS; +extern const char* const MEDIA_MIME_AUDIO_OPUS; +extern const char* const MEDIA_MIME_AUDIO_AC3; +extern const char* const MEDIA_MIME_AUDIO_EAC3; +extern const char* const MEDIA_MIME_AUDIO_EAC3_JOC; +extern const char* const MEDIA_MIME_AUDIO_AC4; +extern const char* const MEDIA_MIME_AUDIO_WMA; +extern const char* const MEDIA_MIME_AUDIO_AMR_NB; +extern const char* const MEDIA_MIME_AUDIO_AMR_WB; + +// video mime +extern const char* const MEDIA_MIME_VIDEO_RAW; +extern const char* const MEDIA_MIME_VIDEO_AVC; + +bool IsAudioMime(const std::string& mime); +bool IsVideoMime(const std::string& mime); +bool IsRawAudio(const std::string& mime); +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_CONSTANTS_H diff --git a/engine/foundation/error_code.h b/engine/foundation/error_code.h new file mode 100644 index 00000000..83451292 --- /dev/null +++ b/engine/foundation/error_code.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_ERROR_CODE_H +#define HISTREAMER_FOUNDATION_ERROR_CODE_H + +namespace OHOS { +namespace Media { +enum ErrorCode { + SUCCESS = 0, + UNKNOWN_ERROR, + UNIMPLEMENT, + BUSY, + NOT_FOUND, + INVALID_PARAM_TYPE, + INVALID_PARAM_VALUE, + INVALID_OPERATION, + INVALID_SOURCE, + INVALID_STREAM_INDEX, + INITIALIZATION_FAILURE, + NULL_POINTER_ERROR, + ACTIVATE_ERROR, + NEGOTIATE_ERROR, + LOAD_URI_FAILED, + END_OF_STREAM, + PLUGIN_NOT_FOUND, + PORT_NOT_FOUND, + PORT_UNEXPECTED, + ASYNC, + ERROR_STATE, + PARSE_META_FAILED, + SEEK_FAILURE, + ALREADY_EXISTS, + ERROR_TIMEOUT +}; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_ERROR_CODE_H diff --git a/engine/foundation/event.h b/engine/foundation/event.h new file mode 100644 index 00000000..386d4a5d --- /dev/null +++ b/engine/foundation/event.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_EVENT_H +#define HISTREAMER_FOUNDATION_EVENT_H + +#include "common/any.h" + +namespace OHOS { +namespace Media { +class Meta; + +// 各个组件向Pipeline报告的事件类型 +enum EventType { + EVENT_READY = 0, + EVENT_PROGRESS, + EVENT_COMPLETE, + EVENT_ERROR, + EVENT_BUFFERING, + EVENT_BUFFER_PROGRESS, + EVENT_DECODER_ERROR, +}; + +struct Event { + EventType type; + Plugin::Any param; +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/foundation/log.h b/engine/foundation/log.h new file mode 100644 index 00000000..89917948 --- /dev/null +++ b/engine/foundation/log.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_LOG_H +#define HISTREAMER_FOUNDATION_LOG_H + +#include +#include +#include +#include + +#include "error_code.h" + +inline std::string MediaGetFileName(std::string file) +{ + if (file == "") { + return "Unknown File"; + } + + return file.substr(file.find_last_of("/\\") + 1); +} + +#ifndef LOG_TAG +#define LOG_TAG "NULL" +#endif + +// control of logd and logi. If these to function is need, use #define LOG_NDEBUG 0 at header of file +#ifndef LOG_NDEBUG +#define LOG_NDEBUG 1 +#endif + +#define MEDIA_LOG_MESSAGE(level, msg, ...) \ + do { \ + std::string file(__FILE__); \ + std::string bareFile = MediaGetFileName(file); \ + printf("%lu " LOG_TAG " " level " (%s, %d) : Func(%s) " msg "\n", pthread_self(), bareFile.c_str(), __LINE__, \ + __FUNCTION__, ##__VA_ARGS__); \ + fflush(stdout); \ + } while (0) + +#if LOG_NDEBUG +#define MEDIA_LOG_I(msg, ...) ((void)0) +#define MEDIA_LOG_D(msg, ...) ((void)0) +#endif + +#define MEDIA_LOG_E(msg, ...) MEDIA_LOG_MESSAGE("ERROR", msg, ##__VA_ARGS__) +#define MEDIA_LOG_W(msg, ...) MEDIA_LOG_MESSAGE("WARN", msg, ##__VA_ARGS__) +#if !LOG_NDEBUG +#define MEDIA_LOG_I(msg, ...) MEDIA_LOG_MESSAGE("INFO", msg, ##__VA_ARGS__) +#define MEDIA_LOG_D(msg, ...) MEDIA_LOG_MESSAGE("DEBUG", msg, ##__VA_ARGS__) +#endif + +#ifndef FAIL_RETURN +#define FAIL_RETURN(exec) \ + do { \ + ErrorCode ret = (exec); \ + if (ret != SUCCESS) { \ + MEDIA_LOG_E("FAIL_RETURN on ErrorCode(%d).", ret); \ + return ret; \ + } \ + } while (0) +#endif + +#ifndef FAIL_LOG +#define FAIL_LOG(exec) \ + do { \ + ErrorCode ret = (exec); \ + if (ret != SUCCESS) { \ + MEDIA_LOG_E("FAIL_LOG on ErrorCode(%d).", ret); \ + } \ + } while (0) +#endif + +#ifndef FALSE_RETURN +#define FALSE_RETURN(exec) \ + do { \ + bool ret = (exec); \ + if (!ret) { \ + MEDIA_LOG_E("FALSE_RETURN " #exec); \ + return; \ + } \ + } while (0) +#endif + +#ifndef FALSE_RETURN_V +#define FALSE_RETURN_V(exec, ret) \ + do { \ + bool value = (exec); \ + if (!value) { \ + MEDIA_LOG_E("FALSE_RETURN_V " #exec); \ + return ret; \ + } \ + } while (0) +#endif + +#ifndef ASSERT_CONDITION +#define ASSERT_CONDITION(exec, msg) \ + do { \ + bool value = (exec); \ + if (!value) { \ + MEDIA_LOG_E("ASSERT_CONDITION(msg:%s) " #exec, msg); \ + } \ + } while (0) +#endif + +#ifndef RETURN_ERROR_IF_NULL +#define RETURN_ERROR_IF_NULL(ptr) \ + do { \ + if ((ptr) == nullptr) { \ + MEDIA_LOG_E("Null pointer error: " #ptr); \ + return ErrorCode::NULL_POINTER_ERROR; \ + } \ + } while (0) +#endif + +#define RETURN_TARGET_ERR_MESSAGE_LOG_IF_FAIL(err, returnErr, msg) \ + if ((err) != SUCCESS) { \ + MEDIA_LOG_E(msg); \ + return returnErr; \ + } + +#define RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, msg) RETURN_TARGET_ERR_MESSAGE_LOG_IF_FAIL(err, err, msg) + +#endif // HISTREAMER_FOUNDATION_LOG_H \ No newline at end of file diff --git a/engine/foundation/memory_helper.h b/engine/foundation/memory_helper.h new file mode 100644 index 00000000..fb68664f --- /dev/null +++ b/engine/foundation/memory_helper.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_MEMORY_HELPER_H +#define HISTREAMER_FOUNDATION_MEMORY_HELPER_H + +#include + +namespace OHOS { +namespace Media { +namespace MemoryHelper { +template struct _unique_ptr_if { + typedef std::unique_ptr _single_object; +}; + +template struct _unique_ptr_if { + typedef std::unique_ptr _unknown_bound_array; +}; + +template struct _unique_ptr_if { + typedef void _known_bound_array; // not supported +}; + +template +typename _unique_ptr_if::_single_object make_unique(U&&... params) +{ + return std::unique_ptr(new T(std::forward(params)...)); +} + +template +typename _unique_ptr_if::_unknown_bound_array make_unique(size_t n) +{ + typedef typename std::remove_extent::type U; + return std::unique_ptr(new U[n]()); +} + +template +typename _unique_ptr_if::_known_bound_array make_unique(Args&&...) = delete; +} +} +} +#endif // HISTREAMER_FOUNDATION_MEMORY_HELPER_H diff --git a/engine/foundation/meta.cpp b/engine/foundation/meta.cpp new file mode 100644 index 00000000..41569743 --- /dev/null +++ b/engine/foundation/meta.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "meta.h" + +#include +#include + +namespace OHOS { +namespace Media { +using PointerPair = std::pair, size_t>; +Meta::~Meta() +{ + Clear(); +} + +bool Meta::Empty() const +{ + return items_.empty(); +} + +bool Meta::SetString(Plugin::MetaID id, const std::string& value) +{ + return SetData(id, value); +} + +bool Meta::SetInt32(Plugin::MetaID id, int32_t value) +{ + return SetData(id, value); +} + +bool Meta::SetUint32(Plugin::MetaID id, uint32_t value) +{ + return SetData(id, value); +} + +bool Meta::SetInt64(Plugin::MetaID id, int64_t value) +{ + return SetData(id, value); +} + +bool Meta::SetUint64(Plugin::MetaID id, uint64_t value) +{ + return SetData(id, value); +} + +bool Meta::SetFloat(Plugin::MetaID id, float value) +{ + return SetData(id, value); +} + +bool Meta::GetString(Plugin::MetaID id, std::string& value) const +{ + auto ite = items_.find(id); + if (ite == items_.end()) { + return false; + } + const std::type_info& type = ite->second.Type(); + if (type == typeid(const char*)) { + value = Plugin::AnyCast(ite->second); + } else if (type == typeid(std::string)) { + value = Plugin::AnyCast(ite->second); + } else if (type == typeid(char*)) { + value = Plugin::AnyCast(ite->second); + } else { + return false; + } + return true; +} + +bool Meta::GetInt32(Plugin::MetaID id, int32_t& value) const +{ + return GetData(id, value); +} + +bool Meta::GetUint32(Plugin::MetaID id, uint32_t& value) const +{ + return GetData(id, value); +} + +bool Meta::GetInt64(Plugin::MetaID id, int64_t& value) const +{ + return GetData(id, value); +} + +bool Meta::GetUint64(Plugin::MetaID id, uint64_t& value) const +{ + return GetData(id, value); +} + +bool Meta::GetFloat(Plugin::MetaID id, float& value) const +{ + return GetData(id, value); +} + +void Meta::Clear() +{ + items_.clear(); +} + +bool Meta::Remove(Plugin::MetaID id) +{ + auto ite = items_.find(id); + if (ite == items_.end()) { + return false; + } + items_.erase(ite); + return true; +} + +void Meta::Update(const Meta& meta) +{ + for (auto& ptr : meta.items_) { + // we need to copy memory for pointers + if (typeid(ptr.second) == typeid(PointerPair)) { + auto pointerPair = Plugin::AnyCast(ptr.second); + SetPointer(ptr.first, pointerPair.first.get(), pointerPair.second); + } else { + items_[ptr.first] = ptr.second; + } + } +} + +bool Meta::SetPointer(Plugin::MetaID id, const void* ptr, size_t size) +{ + auto tmp = new (std::nothrow) uint8_t[size]; + if (tmp == nullptr) { + return false; + } + std::shared_ptr savePtr(tmp, std::default_delete()); + if (memcpy_s(savePtr.get(), size, ptr, size) != EOK) { + delete[] tmp; + return false; + } + return SetData, size_t>>(id, std::make_pair(savePtr, size)); +} + +bool Meta::GetPointer(Plugin::MetaID id, void** ptr, size_t& size) const +{ + std::pair, size_t> item; + if (GetData, size_t>>(id, item)) { + size = item.second; + auto tmp = new (std::nothrow) uint8_t[size]; + if (tmp == nullptr) { + return false; + } + if (memcpy_s(tmp, size, item.first.get(), size) != EOK) { + delete[] tmp; + return false; + } + *ptr = tmp; + return true; + } else { + return false; + } +} +} // namespace Media +} // namespace OHOS diff --git a/engine/foundation/meta.h b/engine/foundation/meta.h new file mode 100644 index 00000000..f4eb6e07 --- /dev/null +++ b/engine/foundation/meta.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_META_H +#define HISTREAMER_FOUNDATION_META_H + +#include +#include +#include + +#include "plugin/common/plugin_types.h" +#include "plugin/core/plugin_meta.h" + +namespace OHOS { +namespace Media { +class Meta { +public: + explicit Meta()= default; + ~Meta(); + bool Empty() const; + bool SetString(Plugin::MetaID, const std::string &); + bool SetInt32(Plugin::MetaID, int32_t); + bool SetUint32(Plugin::MetaID, uint32_t); + bool SetInt64(Plugin::MetaID, int64_t); + bool SetUint64(Plugin::MetaID, uint64_t); + bool SetFloat(Plugin::MetaID, float); + + /** + * this function will copy from ptr with size. + * @param ptr pointer + * @param size size + * @return + */ + bool SetPointer(Plugin::MetaID, const void* ptr, size_t size); + + bool GetString(Plugin::MetaID, std::string &) const; + bool GetInt32(Plugin::MetaID, int32_t &) const; + bool GetUint32(Plugin::MetaID, uint32_t &) const; + bool GetInt64(Plugin::MetaID, int64_t &) const; + bool GetUint64(Plugin::MetaID, uint64_t &) const; + bool GetFloat(Plugin::MetaID, float &) const; + + /** + * this function will copy from inner storage with size if exists. The user should delete the memory when it's not + * needed. + * @param ptr pointer + * @param size size + * @return + */ + bool GetPointer(Plugin::MetaID, void **ptr, size_t &size) const; + + template + bool GetData(Plugin::MetaID id, T &value) const + { + auto ite = items_.find(id); + if (ite == items_.end() || typeid(T) != ite->second.Type()) { + return false; + } + value = Plugin::AnyCast(ite->second); + return true; + } + + void Clear(); + + /** + * remove item with the input name + * @param name target name + * @return true if the target exists; or false. + */ + bool Remove(Plugin::MetaID id); + + void Update(const Meta& meta); + + /** + * user should always use like SetData to set data, otherwise it may be wrong to GetData. + * e.g. for string if use SetData(id, "abc") then GetData(id, value) cannot work. + * for uint64 if use SetData(id, 1), then GetData(id, value) cannot work. + * + * @tparam T + * @param id + * @param value + * @return + */ + template + bool SetData(Plugin::MetaID id, const T &value) + { + items_[id] = value; + return true; + } + +private: + std::map items_ {}; +}; +} +} +#endif // HISTREAMER_FOUNDATION_META_H diff --git a/engine/foundation/osal/base/common.h b/engine/foundation/osal/base/common.h new file mode 100644 index 00000000..b822b7d5 --- /dev/null +++ b/engine/foundation/osal/base/common.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_BASE_COMMON_H +#define HISTREAMER_FOUNDATION_OSAL_BASE_COMMON_H + +#include +#include + +namespace OHOS { +namespace Media { +namespace OSAL { +inline void Assert(const char* file, int line) +{ + fprintf(stderr, "assertion failed for: %s, at line: %d", file, line); + abort(); +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS + +#ifdef NDEBUG +#define ASSERT(_expr) ((void)0) +#else +#define ASSERT(_expr) ((_expr) ? ((void)0) : OHOS::Media::OSAL::Assert(__FILE__, __LINE__)) +#endif + +#endif // HISTREAMER_FOUNDATION_OSAL_BASE_COMMON_H diff --git a/engine/foundation/osal/base/synchronizer.h b/engine/foundation/osal/base/synchronizer.h new file mode 100644 index 00000000..9a6b1621 --- /dev/null +++ b/engine/foundation/osal/base/synchronizer.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_BASE_SYNCHRONIZER_H +#define HISTREAMER_FOUNDATION_OSAL_BASE_SYNCHRONIZER_H + +#include +#include +#include + +#include "foundation/log.h" +#include "thread/condition_variable.h" +#include "thread/mutex.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +template +class Synchronizer { +public: + explicit Synchronizer(std::string name) : name_(std::move(name)) + { + } + + Synchronizer(const Synchronizer&) = delete; + + Synchronizer& operator=(const Synchronizer&) = delete; + + virtual ~Synchronizer() = default; + + void Wait(SyncIdType syncId) + { + MEDIA_LOG_I("Synchronizer %s Wait for %d", name_.c_str(), static_cast(syncId)); + OSAL::ScopedLock lock(mutex_); + waitSet_.insert(syncId); + cv_.Wait(lock, [this, syncId] { return syncMap_.find(syncId) != syncMap_.end(); }); + syncMap_.erase(syncId); + } + + bool WaitFor(SyncIdType syncId, int timeoutMs) + { + MEDIA_LOG_I("Synchronizer %s Wait for %d, timeout: %d", name_.c_str(), static_cast(syncId), timeoutMs); + OSAL::ScopedLock lock(mutex_); + waitSet_.insert(syncId); + auto rtv = cv_.WaitFor(lock, timeoutMs, [this, syncId] { return syncMap_.find(syncId) != syncMap_.end(); }); + if (rtv) { + syncMap_.erase(syncId); + } else { + waitSet_.erase(syncId); + } + return rtv; + } + + void Wait(SyncIdType syncId, ResultType& result) + { + MEDIA_LOG_I("Synchronizer %s Wait for %d", name_.c_str(), static_cast(syncId)); + OSAL::ScopedLock lock(mutex_); + waitSet_.insert(syncId); + cv_.Wait(lock, [this, syncId] { return syncMap_.find(syncId) != syncMap_.end(); }); + result = syncMap_[syncId]; + syncMap_.erase(syncId); + } + + bool WaitFor(SyncIdType syncId, int timeoutMs, ResultType& result) + { + MEDIA_LOG_I("Synchronizer %s Wait for %d, timeout: %d", name_.c_str(), static_cast(syncId), timeoutMs); + OSAL::ScopedLock lock(mutex_); + waitSet_.insert(syncId); + auto rtv = cv_.WaitFor(lock, timeoutMs, [this, syncId] { return syncMap_.find(syncId) != syncMap_.end(); }); + if (rtv) { + result = syncMap_[syncId]; + syncMap_.erase(syncId); + } else { + waitSet_.erase(syncId); + } + return rtv; + } + + void Notify(SyncIdType syncId, ResultType result = ResultType()) + { + MEDIA_LOG_I("Synchronizer %s Notify: %d", name_.c_str(), static_cast(syncId)); + OSAL::ScopedLock lock(mutex_); + if (waitSet_.find(syncId) != waitSet_.end()) { + waitSet_.erase(syncId); + syncMap_.insert({syncId, result}); + cv_.NotifyAll(); + } + } + +private: + Mutex mutex_; + ConditionVariable cv_; + std::string name_; + std::map syncMap_; + std::set waitSet_; +}; +} // namespace OSAL +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_OSAL_BASE_SYNCHRONIZER_H diff --git a/engine/foundation/osal/osal_config.h b/engine/foundation/osal/osal_config.h new file mode 100644 index 00000000..03e7fdcf --- /dev/null +++ b/engine/foundation/osal/osal_config.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_CONFIG_H +#define HISTREAMER_FOUNDATION_OSAL_CONFIG_H + +#ifdef OSAL_OHOS +#include "platform/ohos/ohos_config.h" +#elif OSAL_LINUX +#error "OSAL not support linux." +#else +#error "unknown OSAL platform" +#endif + +#endif // HISTREAMER_FOUNDATION_OSAL_CONFIG_H diff --git a/engine/foundation/osal/platform/ohos/ohos_config.h b/engine/foundation/osal/platform/ohos/ohos_config.h new file mode 100644 index 00000000..7906b324 --- /dev/null +++ b/engine/foundation/osal/platform/ohos/ohos_config.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_PLATFORM_OHOS_OHOS_CONFIG_H +#define HISTREAMER_FOUNDATION_OSAL_PLATFORM_OHOS_OHOS_CONFIG_H + +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +using ThreadObject = pthread_t; +using MutexObject = pthread_mutex_t; +using SemaphoreObject = sem_t; +using ConditionObject = pthread_cond_t; +} +} +#endif // HISTREAMER_FOUNDATION_OSAL_PLATFORM_OHOS_OHOS_CONFIG_H diff --git a/engine/foundation/osal/thread/condition_variable.cpp b/engine/foundation/osal/thread/condition_variable.cpp new file mode 100644 index 00000000..7f0d03c6 --- /dev/null +++ b/engine/foundation/osal/thread/condition_variable.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "ConditionVariable" + +#include "condition_variable.h" +#include "foundation/log.h" + +namespace { +constexpr int32_t TIME_SCALE = 1000; +} + +namespace OHOS { +namespace Media { +namespace OSAL { +ConditionVariable::ConditionVariable() noexcept : condInited_(true) +{ + pthread_condattr_t attr; + pthread_condattr_init(&attr); +#ifdef USING_CLOCK_REALTIME + pthread_condattr_setclock(&attr, CLOCK_REALTIME); +#else + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); +#endif + int rtv = pthread_cond_init(&cond_, &attr); + if (rtv != 0) { + condInited_ = false; + MEDIA_LOG_E("failed to init pthread cond variable"); + } + pthread_condattr_destroy(&attr); +} + +ConditionVariable::~ConditionVariable() noexcept +{ + if (condInited_) { + pthread_cond_destroy(&cond_); + } +} + +void ConditionVariable::NotifyOne() noexcept +{ + pthread_cond_signal(&cond_); +} + +void ConditionVariable::NotifyAll() noexcept +{ + pthread_cond_broadcast(&cond_); +} + +void ConditionVariable::Wait(ScopedLock& lock) noexcept +{ + pthread_cond_wait(&cond_, const_cast(lock.GetMutex()->GetNativeHandle())); +} + +bool ConditionVariable::WaitFor(ScopedLock& lock, int timeoutMs) +{ + if (timeoutMs < 0) { + MEDIA_LOG_E("ConditionVariable WaitUntil invalid timeoutMs: %d", timeoutMs); + return false; + } + struct timespec timeout = {0, 0}; +#ifdef USING_CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &timeout); +#else + clock_gettime(CLOCK_MONOTONIC, &timeout); +#endif + timeout.tv_sec += timeoutMs / TIME_SCALE; + timeout.tv_nsec += (timeoutMs % TIME_SCALE) * TIME_SCALE * TIME_SCALE; + return pthread_cond_timedwait(&cond_, const_cast(lock.GetMutex()->GetNativeHandle()), + &timeout) == 0; +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/foundation/osal/thread/condition_variable.h b/engine/foundation/osal/thread/condition_variable.h new file mode 100644 index 00000000..b6c2eb33 --- /dev/null +++ b/engine/foundation/osal/thread/condition_variable.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_CONDITION_VARIABLE_H +#define HISTREAMER_FOUNDATION_OSAL_CONDITION_VARIABLE_H + +#include + +#include "scoped_lock.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +class ConditionVariable { +public: + ConditionVariable() noexcept; + + virtual ~ConditionVariable() noexcept; + + ConditionVariable(const ConditionVariable& other) = delete; + + ConditionVariable operator=(const ConditionVariable& other) = delete; + + void NotifyOne() noexcept; + + void NotifyAll() noexcept; + + void Wait(ScopedLock& lock) noexcept; + + template + void Wait(ScopedLock& lock, Predicate pred) noexcept + { + while (!pred()) { + Wait(lock); + } + } + + bool WaitFor(ScopedLock& lock, int timeoutMs); + + template + bool WaitFor(ScopedLock& lock, int timeoutMs, Predicate pred) + { + if (timeoutMs < 0) { + return false; + } + struct timespec timeout = {0, 0}; +#ifdef USING_CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &timeout); +#else + clock_gettime(CLOCK_MONOTONIC, &timeout); +#endif + timeout.tv_sec += timeoutMs / 1000; // 1000 + timeout.tv_nsec += (timeoutMs % 1000) * 1000000; // 1000 1000000 + int status = 0; + while (!pred() && (status == 0)) { + status = pthread_cond_timedwait(&cond_, const_cast(lock.GetMutex()->GetNativeHandle()), + &timeout); + if (status == ETIMEDOUT) { + return pred(); + } + } + return status == 0; + } + +private: + bool condInited_; + pthread_cond_t cond_ {}; +}; +} // namespace OSAL +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_FOUNDATION_OSAL_CONDITION_VARIABLE_H diff --git a/engine/foundation/osal/thread/mutex.cpp b/engine/foundation/osal/thread/mutex.cpp new file mode 100644 index 00000000..e73db15d --- /dev/null +++ b/engine/foundation/osal/thread/mutex.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "Mutex" + +#include "mutex.h" + +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +Mutex::Mutex() : created_(true) +{ + int rtv = pthread_mutex_init(&nativeHandle_, nullptr); + if (rtv != 0) { + created_ = false; + MEDIA_LOG_E("failed to init pthread mutex"); + } +} + +Mutex::~Mutex() +{ + if (created_) { + pthread_mutex_destroy(&nativeHandle_); + } +} + +const pthread_mutex_t* Mutex::GetNativeHandle() const +{ + return &nativeHandle_; +} + +void Mutex::Lock() +{ + if (!created_) { + MEDIA_LOG_E("Lock uninitialized pthread mutex!"); + return; + } + pthread_mutex_lock(&nativeHandle_); +} + +bool Mutex::TryLock() +{ + if (!created_) { + MEDIA_LOG_E("TryLock uninitialized pthread mutex."); + return false; + } + int ret = pthread_mutex_trylock(&nativeHandle_); + if (ret != 0) { + MEDIA_LOG_E("TryLock failed with ret = %d", ret); + } + return ret == 0; +} + +void Mutex::Unlock() +{ + if (!created_) { + MEDIA_LOG_E("Unlock uninitialized pthread mutex!"); + return; + } + pthread_mutex_unlock(&nativeHandle_); +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/foundation/osal/thread/mutex.h b/engine/foundation/osal/thread/mutex.h new file mode 100644 index 00000000..a411b539 --- /dev/null +++ b/engine/foundation/osal/thread/mutex.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_MUTEX_H +#define HISTREAMER_FOUNDATION_OSAL_MUTEX_H + +#include "osal/osal_config.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +class Mutex { +public: + Mutex(); + + virtual ~Mutex(); + + Mutex(const Mutex&) = delete; + + Mutex operator=(const Mutex&) = delete; + + const pthread_mutex_t* GetNativeHandle() const; + + void Lock(); + + bool TryLock(); + + void Unlock(); + +private: + pthread_mutex_t nativeHandle_ {}; + bool created_; +}; +} // namespace OSAL +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_OSAL_MUTEX_H diff --git a/engine/foundation/osal/thread/scoped_lock.cpp b/engine/foundation/osal/thread/scoped_lock.cpp new file mode 100644 index 00000000..14dac935 --- /dev/null +++ b/engine/foundation/osal/thread/scoped_lock.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "ScopedLock" + +#include "scoped_lock.h" + +#include +#include "base/common.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +ScopedLock::ScopedLock() : mutex_(nullptr) +{ +} + +ScopedLock::ScopedLock(Mutex& mutex) : mutex_(&mutex) +{ + mutex_->Lock(); +} + +ScopedLock::~ScopedLock() +{ + if (mutex_ != nullptr) { + mutex_->Unlock(); + mutex_ = nullptr; + } +} + +ScopedLock::ScopedLock(ScopedLock&& other) noexcept +{ + mutex_ = nullptr; + *this = std::move(other); +} + +ScopedLock& ScopedLock::operator=(ScopedLock&& other) noexcept +{ + if (this != &other) { + if (mutex_) { + mutex_->Unlock(); + } + mutex_ = other.mutex_; + other.mutex_ = nullptr; + } + return *this; +} + +void ScopedLock::Lock() +{ + if (mutex_ != nullptr) { + mutex_->Lock(); + } +} + +bool ScopedLock::TryLock() +{ + bool ret = false; + if (mutex_ != nullptr) { + ret = mutex_->TryLock(); + } + return ret; +} + +void ScopedLock::Unlock() +{ + if (mutex_ != nullptr) { + mutex_->Unlock(); + } +} + +const Mutex* ScopedLock::GetMutex() const +{ + return mutex_; +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/foundation/osal/thread/scoped_lock.h b/engine/foundation/osal/thread/scoped_lock.h new file mode 100644 index 00000000..884baace --- /dev/null +++ b/engine/foundation/osal/thread/scoped_lock.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_SCOPED_LOCK_H +#define HISTREAMER_FOUNDATION_OSAL_SCOPED_LOCK_H + +#include "mutex.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +class ScopedLock { +public: + ScopedLock(); + + explicit ScopedLock(Mutex& mutex); + + ScopedLock(ScopedLock&& other) noexcept; + + ScopedLock& operator=(ScopedLock&& other) noexcept; + + virtual ~ScopedLock(); + + void Lock(); + + bool TryLock(); + + void Unlock(); + + const Mutex* GetMutex() const; + +private: + Mutex* mutex_; +}; +} // namespace OSAL +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_OSAL_SCOPED_LOCK_H diff --git a/engine/foundation/osal/thread/task.cpp b/engine/foundation/osal/thread/task.cpp new file mode 100644 index 00000000..8f28cf11 --- /dev/null +++ b/engine/foundation/osal/thread/task.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "Task" + +#include "task.h" + +#include "foundation/log.h" +#include "utils/util.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +Task::Task(std::string name) + : name_(std::move(name)), loop_([this] { Run(); }), runningState_(PAUSED), pauseDone_(false), workInProgress_(false) +{ + MEDIA_LOG_D("task %s ctor called", name_.c_str()); + loop_.SetName(name_); +} + +Task::Task(std::string name, std::function handler) : Task(std::move(name)) +{ + MEDIA_LOG_D("task %s ctor called", name_.c_str()); + handler_ = std::move(handler); +} + +Task::~Task() +{ + MEDIA_LOG_D("task %s dtor called", name_.c_str()); + runningState_ = STOPPED; + cv_.NotifyOne(); +} + +void Task::Start() +{ +#ifndef START_FAKE_TASK + OSAL::ScopedLock lock(stateMutex_); + runningState_ = STARTED; + cv_.NotifyOne(); + MEDIA_LOG_D("task %s start called", name_.c_str()); +#endif +} + +void Task::Stop() +{ + OSAL::ScopedLock lock(stateMutex_); + runningState_ = STOPPED; + cv_.NotifyOne(); + while (workInProgress_.load()) {} + MEDIA_LOG_D("task %s stop called", name_.c_str()); +} + +void Task::StopAsync() +{ + OSAL::ScopedLock lock(stateMutex_); + runningState_ = STOPPED; + cv_.NotifyOne(); + MEDIA_LOG_D("task %s stop called", name_.c_str()); +} + +void Task::Pause() +{ + if (runningState_.load() != STARTED) { + return; + } + OSAL::ScopedLock lock(stateMutex_); + MEDIA_LOG_D("task %s Pause called", name_.c_str()); + if (runningState_.load() == STARTED) { + pauseDone_ = false; + runningState_ = PAUSED; + while (!pauseDone_.load()) {} + } + MEDIA_LOG_D("task %s Pause done.", name_.c_str()); +} + +void Task::PauseAsync() +{ + if (runningState_.load() != STARTED) { + return; + } + OSAL::ScopedLock lock(stateMutex_); + MEDIA_LOG_D("task %s Pause called", name_.c_str()); + runningState_ = PAUSED; +} + +void Task::RegisterHandler(std::function handler) +{ + MEDIA_LOG_D("task %s RegisterHandler called", name_.c_str()); + handler_ = std::move(handler); +} + +void Task::DoTask() +{ + MEDIA_LOG_D("task %s not override DoTask...", name_.c_str()); +} + +void Task::Run() +{ + for (;;) { + if (runningState_.load() == STARTED) { + workInProgress_ = true; + handler_(); + workInProgress_ = false; + continue; + } + if (runningState_.load() == STOPPED) { + MEDIA_LOG_D("task %s stopped, exit task", name_.c_str()); + break; + } + if (runningState_.load() == PAUSED) { + OSAL::ScopedLock lock(cvMutex_); + pauseDone_ = true; + cv_.Wait(lock, [this] { return runningState_.load() != PAUSED; }); + } + } +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS diff --git a/engine/foundation/osal/thread/task.h b/engine/foundation/osal/thread/task.h new file mode 100644 index 00000000..9ac3c089 --- /dev/null +++ b/engine/foundation/osal/thread/task.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_TASK_H +#define HISTREAMER_FOUNDATION_OSAL_TASK_H + +#include +#include +#include + +#include "osal/thread/condition_variable.h" +#include "osal/thread/mutex.h" +#include "osal/thread/scoped_lock.h" +#include "osal/thread/thread.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +class Task { +public: + explicit Task(std::string name); + + explicit Task(std::string name, std::function handler); + + virtual ~Task(); + + virtual void Start(); + + virtual void Stop(); + + virtual void StopAsync(); + + virtual void Pause(); + + virtual void PauseAsync(); + + virtual void DoTask(); + + void RegisterHandler(std::function handler); + +private: + enum RunningState { + STARTED, + PAUSED, + STOPPED, + }; + + void Run(); + + const std::string name_; + OSAL::Thread loop_; + std::atomic runningState_ {PAUSED}; + std::function handler_ = [this] { DoTask(); }; + + OSAL::Mutex stateMutex_ {}; + OSAL::Mutex cvMutex_ {}; + OSAL::ConditionVariable cv_ {}; + std::atomic pauseDone_ {}; + std::atomic workInProgress_ {}; +}; +} // namespace OSAL +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_OSAL_TASK_H diff --git a/engine/foundation/osal/thread/thread.cpp b/engine/foundation/osal/thread/thread.cpp new file mode 100644 index 00000000..c3b4c779 --- /dev/null +++ b/engine/foundation/osal/thread/thread.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "Thread" + +#include "thread.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +Thread::Thread() noexcept : id_(), name_(), state_() +{ +} +Thread::Thread(Thread&& other) noexcept +{ + *this = std::move(other); +} +Thread::Thread(const std::function &func) +{ + CreateThread(func); +} +Thread& Thread::operator=(Thread&& other) noexcept +{ + if (this != &other) { + id_ = other.id_; + name_ = std::move(other.name_); + state_ = std::move(other.state_); + } + return *this; +} +Thread::~Thread() noexcept +{ + if (state_) { + pthread_join(id_, nullptr); + } +} +void Thread::SetName(const std::string& name) +{ +#ifndef OSAL_OHOS + constexpr size_t threadNameMaxSize = 15; + name_ = (name.size() > threadNameMaxSize) ? name.substr(0, threadNameMaxSize).c_str() : name; + if (name.size() > threadNameMaxSize) { + MEDIA_LOG_W("task name %s exceed max size: %d", name.c_str(), threadNameMaxSize); + } + pthread_setname_np(id_, name_.c_str()); +#endif +} +void Thread::CreateThread(const std::function& func) +{ + state_ = std::unique_ptr(new State); + state_->func_ = func; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int rtv = pthread_create(&id_, &attr, Thread::Run, state_.get()); + if (rtv == 0) { + MEDIA_LOG_I("thread create succ"); + } else { + state_.reset(); + MEDIA_LOG_E("thread create failed"); + } +} +void* Thread::Run(void* arg) +{ + auto state = static_cast(arg); + if (state && state->func_) { + state->func_(); + } + MEDIA_LOG_I("Thread::Run exited..."); + return nullptr; +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS diff --git a/engine/foundation/osal/thread/thread.h b/engine/foundation/osal/thread/thread.h new file mode 100644 index 00000000..47b6424c --- /dev/null +++ b/engine/foundation/osal/thread/thread.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_THREAD_H +#define HISTREAMER_FOUNDATION_OSAL_THREAD_H + +#include +#include +#include +#include +#include + +#include "osal/osal_config.h" + +namespace OHOS { +namespace Media { +namespace OSAL { +class Thread { +public: + Thread() noexcept; + + Thread(const Thread&) = delete; + + Thread& operator=(const Thread&) = delete; + + Thread(Thread&& other) noexcept; + + explicit Thread(const std::function& func); + + Thread& operator=(Thread&& other) noexcept; + + virtual ~Thread() noexcept; + + void SetName(const std::string& name); + +private: + struct State { + virtual ~State() = default; + std::function func_ {}; + }; + + void CreateThread(const std::function& func); + + static void* Run(void* arg); + + pthread_t id_ {}; + std::string name_; + std::unique_ptr state_ {}; +}; +} // namespace OSAL +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_OSAL_THREAD_H diff --git a/engine/foundation/osal/utils/util.cpp b/engine/foundation/osal/utils/util.cpp new file mode 100644 index 00000000..3943a376 --- /dev/null +++ b/engine/foundation/osal/utils/util.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "util.h" + +#ifdef OSAL_OHOS +#if !defined(_MSC_VER) && !defined(__clang__) +#include +#endif +#else +#include +#include +#endif + +#ifdef _MSC_VER +#include + +void usleep(__int64 usec) +{ + HANDLE timer = CreateWaitableTimer(nullptr, TRUE, nullptr); + // set interval to 100 nanosecond, using relative time + LARGE_INTEGER dueTime; + dueTime.QuadPart = -(10 * usec); // 10 + SetWaitableTimer(timer, &dueTime, 0, nullptr, nullptr, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); +} +#else +extern "C" int usleep(unsigned); +#endif + +namespace OHOS { +namespace Media { +namespace OSAL { +void SleepFor(unsigned ms) +{ + constexpr int factor = 1000; +#ifdef OSAL_OHOS + usleep(ms * factor); +#else + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +#endif +} +} // namespace OSAL +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/foundation/osal/utils/util.h b/engine/foundation/osal/utils/util.h new file mode 100644 index 00000000..327e2bcc --- /dev/null +++ b/engine/foundation/osal/utils/util.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_OSAL_UTILS_UTIL_H +#define HISTREAMER_FOUNDATION_OSAL_UTILS_UTIL_H + +namespace OHOS { +namespace Media { +namespace OSAL { +void SleepFor(unsigned ms); +} // OSAL +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_FOUNDATION_OSAL_UTILS_UTIL_H diff --git a/engine/foundation/singleton.h b/engine/foundation/singleton.h new file mode 100644 index 00000000..1e82f322 --- /dev/null +++ b/engine/foundation/singleton.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_SINGLETON_H +#define HISTREAMER_FOUNDATION_SINGLETON_H + +namespace OHOS { +namespace Media { +template +class Singleton : public T { +public: + static T& Instance() + { + static T instance; + return instance; + } + ~Singleton(); + Singleton(const Singleton&) = delete; + Singleton operator=(const Singleton&) = delete; + +private: + Singleton() = default; +}; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_SINGLETON_H diff --git a/engine/foundation/type_define.h b/engine/foundation/type_define.h new file mode 100644 index 00000000..ceacf8a1 --- /dev/null +++ b/engine/foundation/type_define.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_TYPE_DEFINE_H +#define HISTREAMER_FOUNDATION_TYPE_DEFINE_H + +#include + +#include "buffer_pool.h" +#include "plugin/common/plugin_caps.h" +#include "plugin/common/plugin_tags.h" +#include "plugin/core/plugin_meta.h" +#include "plugin/common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +using Capability = Plugin::Capability; +using CapabilitySet = Plugin::CapabilitySet; +using MetaID = Plugin::MetaID; +using CapabilityID = Plugin::Capability::Key; +using Tag = Plugin::Tag; +using Allocator = Plugin::Allocator; +using AVBuffer = Plugin::Buffer; +using AVBufferPtr = std::shared_ptr; +using AVBufferPool = BufferPool; +using AVBufferPoolPtr = std::shared_ptr>; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FOUNDATION_TYPE_DEFINE_H diff --git a/engine/foundation/utils.h b/engine/foundation/utils.h new file mode 100644 index 00000000..ab2c62be --- /dev/null +++ b/engine/foundation/utils.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FOUNDATION_UTILS_H +#define HISTREAMER_FOUNDATION_UTILS_H + +#include + +#define CALL_PTR_FUNC(ptr, func, param) \ + if ((ptr)) { \ + (ptr)->func(param); \ + } else { \ + MEDIA_LOG_E("Call weakPtr " #func " error." ); \ + } +namespace OHOS { +namespace Media { +inline bool StringStartsWith(const std::string& input, const std::string& prefix) +{ + return input.rfind(prefix, 0) == 0; +} + +template +constexpr typename std::underlying_type::type to_underlying(E e) noexcept +{ + return static_cast::type>(e); +} +} +} +#endif // HISTREAMER_FOUNDATION_UTILS_H diff --git a/engine/pipeline/core/compatible_check.cpp b/engine/pipeline/core/compatible_check.cpp new file mode 100644 index 00000000..4412d757 --- /dev/null +++ b/engine/pipeline/core/compatible_check.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "Compatible_Check" + +#include "compatible_check.h" + +#include +#include +#include + +#include "foundation/log.h" +#include "plugin/common/plugin_audio_tags.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +static constexpr uint8_t ALLOW_FIXED = 1 << 0; +static constexpr uint8_t ALLOW_INTERVAL = 1 << 1; +static constexpr uint8_t ALLOW_DISCRETE = 1 << 2; + +static inline bool IsFixedAllowed(uint8_t flags) +{ + return ALLOW_FIXED & flags; +} + +static inline bool IsIntervalAllowed(uint8_t flags) +{ + return ALLOW_INTERVAL & flags; +} + +static inline bool IsDiscreteAllowed(uint8_t flags) +{ + return ALLOW_DISCRETE & flags; +} + +__attribute__((unused)) static bool StringCapabilityCheck(const std::pair& tagEntry, + const Meta& meta, uint8_t flags); +template +bool NumericalCapabilityCheck(const std::pair& tagEntry, const Meta& meta, + uint8_t flags, std::function compareFunc); + +template +bool FixInvalDiscNumericalCheck(const std::pair& capability, const Meta& meta) +{ + return NumericalCapabilityCheck(capability, meta, ALLOW_FIXED | ALLOW_INTERVAL | ALLOW_DISCRETE, + [](T a, T b) { return a - b; }); +} + +template +bool FixDiscNumericalCheck(const std::pair& capability, const Meta& meta) +{ + return NumericalCapabilityCheck(capability, meta, ALLOW_FIXED | ALLOW_DISCRETE, + [](T a, T b) { return static_cast(a) - static_cast(b); }); +} + +static std::map&, const Meta&)>> + g_capabilityCheckMap = { + {CapabilityID::AUDIO_CHANNELS, FixInvalDiscNumericalCheck}, + {CapabilityID::AUDIO_SAMPLE_RATE, FixInvalDiscNumericalCheck}, + {CapabilityID::AUDIO_MPEG_VERSION, FixInvalDiscNumericalCheck}, + {CapabilityID::AUDIO_MPEG_LAYER, FixInvalDiscNumericalCheck}, + {CapabilityID::AUDIO_CHANNEL_LAYOUT, FixDiscNumericalCheck}, + {CapabilityID::AUDIO_SAMPLE_FORMAT, FixDiscNumericalCheck}, + {CapabilityID::AUDIO_AAC_PROFILE, FixDiscNumericalCheck}, + {CapabilityID::AUDIO_AAC_LEVEL, FixDiscNumericalCheck}, + {CapabilityID::AUDIO_AAC_STREAM_FORMAT, FixDiscNumericalCheck}, +}; + +static bool StringEqIgnoreCase(const std::string& s1, const std::string& s2) +{ + if (s1.length() == s2.length()) { + return std::equal(s1.begin(), s1.end(), s2.begin(), [](char a, char b) { return tolower(a) == tolower(b); }); + } + return false; +} + +bool CompatibleWith(const Capability& capability, const Meta& meta) +{ + // first check mime + std::string mimeInMeta; + if (!meta.GetString(MetaID::MIME, mimeInMeta)) { + MEDIA_LOG_E("mime is not found in meta when check compatible"); + return false; + } + + size_t devLinePosInMeta = mimeInMeta.find_first_of('/'); + if (devLinePosInMeta == 0 || devLinePosInMeta == std::string::npos) { + MEDIA_LOG_E("wrong format of meta mime, must be xx/xxx"); + return false; + } + + if (capability.mime == "*") { + return true; + } + + size_t devLinePosInCap = capability.mime.find_first_of('/'); + if (devLinePosInCap == 0 || devLinePosInCap == std::string::npos) { + MEDIA_LOG_E("wrong format of capability mime, must be * or xx/* or xx/xxx"); + return false; + } + + // if media type is not the same, return false + if (!StringEqIgnoreCase(mimeInMeta.substr(0, devLinePosInMeta), capability.mime.substr(0, devLinePosInCap))) { + return false; + } + // if media type of capability is like audio/* video/* image/* etc. always return true + if (capability.mime.substr(devLinePosInCap + 1) == "*") { + return true; + } + // left mime string compare + if (!StringEqIgnoreCase(capability.mime.substr(devLinePosInCap + 1), mimeInMeta.substr(devLinePosInMeta + 1))) { + return false; + } + + for (const auto& keyEntry : capability.keys) { + auto ite = g_capabilityCheckMap.find(keyEntry.first); + if (ite == g_capabilityCheckMap.end()) { + MEDIA_LOG_E("found one capability which cannot be checked"); + return false; + } + if (!ite->second(keyEntry, meta)) { + return false; + } + } + return true; +} + +bool CompatibleWith(const CapabilitySet& capability, const Meta& meta) +{ + for (const auto& cap : capability) { + if (CompatibleWith(cap, meta)) { + return true; + } + } + return false; +} + +static bool StringCapabilityCheck(const std::pair& tagEntry, const Meta& meta, + uint8_t flags) +{ + std::string metaValue; + if (!meta.GetString(static_cast(tagEntry.first), metaValue) || IsIntervalAllowed(flags)) { + return false; + } + if (IsFixedAllowed(flags)) { + if (tagEntry.second.Type() == typeid(const char*)) { + auto capabilityValue = Plugin::AnyCast(tagEntry.second); + return metaValue == capabilityValue; + } else if (tagEntry.second.Type() == typeid(char*)) { + auto capabilityValue = Plugin::AnyCast(tagEntry.second); + return metaValue == capabilityValue; + } else if (tagEntry.second.Type() == typeid(std::string)) { + auto capabilityValue = Plugin::AnyCast(tagEntry.second); + return metaValue == capabilityValue; + } else if (tagEntry.second.Type() == typeid(Plugin::DiscreteCapability)) { // 列表 + auto capabilityValues = Plugin::AnyCast>(tagEntry.second); + return std::any_of(capabilityValues.begin(), capabilityValues.end(), + [&metaValue](const char* cap) { return metaValue == cap; }); + } + } + if (IsDiscreteAllowed(flags)) { + if (tagEntry.second.Type() == typeid(Plugin::DiscreteCapability)) { // 列表 + auto capabilityValues = Plugin::AnyCast>(tagEntry.second); + return std::any_of(capabilityValues.begin(), capabilityValues.end(), + [&metaValue](const char* cap) { return metaValue == cap; }); + } else if (tagEntry.second.Type() == typeid(Plugin::DiscreteCapability)) { + auto capabilityValues = Plugin::AnyCast>(tagEntry.second); + return std::any_of(capabilityValues.begin(), capabilityValues.end(), + [&metaValue](const std::string& cap) { return metaValue == cap; }); + } + } + return false; +} + +template +bool NumericalCapabilityCheck(const std::pair& tagEntry, const Meta& meta, + uint8_t flags, std::function compareFunc) +{ + T metaValue; + if (!meta.GetData(static_cast(tagEntry.first), metaValue)) { + return false; + } + if (IsFixedAllowed(flags) && tagEntry.second.Type() == typeid(T)) { + auto capabilityValue = Plugin::AnyCast(tagEntry.second); + return metaValue == capabilityValue; + } + if (IsIntervalAllowed(flags) && tagEntry.second.Type() == typeid(Plugin::IntervalCapability)) { + auto capabilityValueRange = Plugin::AnyCast>(tagEntry.second); + T max = std::max(capabilityValueRange.first, capabilityValueRange.second); + T min = std::min(capabilityValueRange.first, capabilityValueRange.second); + return compareFunc(metaValue, min) >= 0 && compareFunc(metaValue, max) <= 0; + } + if (IsDiscreteAllowed(flags) && tagEntry.second.Type() == typeid(Plugin::DiscreteCapability)) { + auto capabilityValues = Plugin::AnyCast>(tagEntry.second); + for (const auto& cap : capabilityValues) { + if (compareFunc(metaValue, cap) == 0) { + return true; + } + } + } + return false; +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/core/compatible_check.h b/engine/pipeline/core/compatible_check.h new file mode 100644 index 00000000..ec38ee36 --- /dev/null +++ b/engine/pipeline/core/compatible_check.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_COMPATIBLE_CHECK_H +#define HISTREAMER_PIPELINE_CORE_COMPATIBLE_CHECK_H + +#include "common/plugin_caps.h" +#include "foundation/meta.h" +#include "foundation/type_define.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +bool CompatibleWith(const Capability &capability, const Meta& meta); +bool CompatibleWith(const CapabilitySet &capability, const Meta& meta); +} +} +} +#endif // HISTREAMER_PIPELINE_CORE_COMPATIBLE_CHECK_H diff --git a/engine/pipeline/core/filter.h b/engine/pipeline/core/filter.h new file mode 100644 index 00000000..f615238b --- /dev/null +++ b/engine/pipeline/core/filter.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_FILTER_H +#define HISTREAMER_PIPELINE_CORE_FILTER_H +#include +#include +#include + +#include "filter_callback.h" +#include "foundation/constants.h" +#include "foundation/error_code.h" +#include "foundation/event.h" +#include "foundation/utils.h" +#include "parameter.h" +#include "port.h" + +namespace OHOS { +namespace Media { +class Meta; + +namespace Pipeline { +class Filter; + +using PFilter = std::shared_ptr; +using PPort = std::shared_ptr; +using PInPort = std::shared_ptr; +using POutPort = std::shared_ptr; +using PairPort = std::pair; + +enum class FilterState { + CREATED, // Filter created + INITIALIZED, // Init called + PREPARING, // Prepare called + READY, // Ready Event reported + RUNNING, // Start called + PAUSED, // Pause called +}; + +// EventReceiver: +// 1. Port使用此接口传递事件给Filter +// 2. Filter使用此接口传递事件给Pipeline +// 3. Sub Filter使用此接口传递事件给 Parent Filter +class EventReceiver { +public: + virtual ~EventReceiver() = default; + virtual void OnEvent(Event event) = 0; +}; + +// InfoTransfer: +// Port使用此接口从Filter获取信息 或 传递信息给Filter +class InfoTransfer : public EventReceiver { +public: + virtual const std::string& GetName() = 0; + virtual std::vector GetWorkModes() = 0; // OutPort调用 + + // InPort调用此接口确定是否要继续往后协商 + virtual bool Negotiate(const std::string& inPort, const std::shared_ptr& inMeta, CapabilitySet& outCaps) + { + return false; + } + virtual ErrorCode PushData(const std::string& inPort, AVBufferPtr buffer) = 0; // InPort调用 + virtual ErrorCode PullData(const std::string& outPort, uint64_t offset, size_t size, + AVBufferPtr& data) = 0; // OutPort调用 + virtual const EventReceiver* GetOwnerPipeline() const = 0; +}; + +class Filter : public InfoTransfer { +public: + ~Filter() override = default; + virtual void Init(EventReceiver* receiver, FilterCallback* callback) = 0; + virtual PInPort GetInPort(const std::string& name) = 0; + virtual POutPort GetOutPort(const std::string& name) = 0; + + virtual ErrorCode Prepare() = 0; + virtual ErrorCode Start() = 0; + virtual ErrorCode Pause() = 0; + virtual ErrorCode Resume() = 0; + virtual ErrorCode Stop() = 0; + + // 清除缓存 + virtual void FlushStart() = 0; + virtual void FlushEnd() = 0; + + // Filter的使用者可以直接调用Filter的SetParameter接口设置参数 + virtual ErrorCode SetParameter(int32_t key, const Plugin::Any& value) = 0; + virtual ErrorCode GetParameter(int32_t key, Plugin::Any& value) = 0; + + virtual void UnlinkPrevFilters() = 0; + virtual std::vector GetNextFilters() = 0; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PIPELINE_CORE_FILTER_H diff --git a/engine/pipeline/core/filter_base.cpp b/engine/pipeline/core/filter_base.cpp new file mode 100644 index 00000000..5292abf8 --- /dev/null +++ b/engine/pipeline/core/filter_base.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "FilterBase" + +#include "filter_base.h" +#include +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +FilterBase::FilterBase(std::string name) + : name_(std::move(name)), state_(FilterState::CREATED), eventReceiver_(nullptr), callback_(nullptr) +{ + inPorts_.reserve(MAX_PORT_NUMBER); + outPorts_.reserve(MAX_PORT_NUMBER); + routeMap_.reserve(MAX_ROUTE_NUMBER); +} + +void FilterBase::Init(EventReceiver* receiver, FilterCallback* callback) +{ + this->eventReceiver_ = receiver; + this->callback_ = callback; + InitPorts(); + state_ = FilterState::INITIALIZED; +} + +std::shared_ptr FilterBase::GetInPort(const std::string& name) +{ + auto port = FindPort(inPorts_, name); + FALSE_RETURN_V(port != nullptr, EmptyInPort::GetInstance()); + return port; +} + +std::shared_ptr FilterBase::GetOutPort(const std::string& name) +{ + auto port = FindPort(outPorts_, name); + FALSE_RETURN_V(port != nullptr, EmptyOutPort::GetInstance()); + return port; +} + +ErrorCode FilterBase::Prepare() +{ + state_ = FilterState::PREPARING; + + // Filter默认InPort按Push方式获取数据 + // 只有 Demuxer 的 Prepare() 需要覆写此实现, Demuxer的 InPort可能按 Pull 或 Push 方式获取数据 + WorkMode mode; + return GetInPort(PORT_NAME_DEFAULT)->Activate({WorkMode::PUSH}, mode); +} + +ErrorCode FilterBase::Start() +{ + state_ = FilterState::RUNNING; + return SUCCESS; +} + +ErrorCode FilterBase::Pause() +{ + state_ = FilterState::PAUSED; + return SUCCESS; +} + +ErrorCode FilterBase::Stop() +{ + state_ = FilterState::INITIALIZED; + mediaTypeCntMap_.clear(); + return SUCCESS; +} + +void FilterBase::UnlinkPrevFilters() +{ + for (auto&& inPort : inPorts_) { + auto peer = inPort->GetPeerPort(); + inPort->Disconnect(); + if (peer) { + peer->Disconnect(); + } + } +} + +std::vector FilterBase::GetNextFilters() +{ + std::vector nextFilters; + nextFilters.reserve(outPorts_.size()); + for (auto&& outPort : outPorts_) { + auto peerPort = outPort->GetPeerPort(); + if (!peerPort) { + MEDIA_LOG_E("Filter %s outport %s has no peer port (%lu).", name_.c_str(), outPort->GetName().c_str(), + outPorts_.size()); + continue; + } + auto filter = const_cast(dynamic_cast(peerPort->GetOwnerFilter())); + if (filter) { + nextFilters.emplace_back(filter); + } + } + return nextFilters; +} + +ErrorCode FilterBase::PushData(const std::string& inPort, AVBufferPtr buffer) +{ + return SUCCESS; +} + +ErrorCode FilterBase::PullData(const std::string& outPort, uint64_t offset, size_t size, AVBufferPtr& data) +{ + return SUCCESS; +} + +void FilterBase::OnEvent(Event event) +{ + // Receive event from port, pass it to pipeline + if (eventReceiver_) { + eventReceiver_->OnEvent(event); + } +} + +void FilterBase::InitPorts() +{ + inPorts_.clear(); + outPorts_.clear(); + + auto inPort = std::make_shared(this); + inPorts_.push_back(inPort); + + auto outPort = std::make_shared(this); + outPorts_.push_back(outPort); + routeMap_.emplace_back(inPort->GetName(), outPort->GetName()); +} + +template +T FilterBase::FindPort(const std::vector& list, const std::string& name) +{ + auto find = std::find_if(list.begin(), list.end(), [name](const T& item) { + if (item == nullptr) { + return false; + } + return name == item->GetName(); + }); + if (find != list.end()) { + return *find; + } + MEDIA_LOG_E("Find port(%s) failed.", name.c_str()); + return nullptr; +} + +std::string FilterBase::NamePort(const std::string& mime) +{ + auto type = mime.substr(0, mime.find_first_of('/')); + if (type.empty()) { + type = "default"; + } + auto cnt = ++(mediaTypeCntMap_[name_ + type]); + auto portName = type + "_" + std::to_string(cnt); + return portName; +} + +PInPort FilterBase::GetRouteInPort(const std::string& outPortName) +{ + auto ite = std::find_if(routeMap_.begin(), routeMap_.end(), + [&outPortName](const PairPort& pp) { return outPortName == pp.second; }); + if (ite == routeMap_.end()) { + MEDIA_LOG_W("out port %s has no route map port", outPortName.c_str()); + return nullptr; + } + return GetInPort(ite->first); +} + +POutPort FilterBase::GetRouteOutPort(const std::string& inPortName) +{ + auto ite = std::find_if(routeMap_.begin(), routeMap_.end(), + [&inPortName](const PairPort& pp) { return inPortName == pp.first; }); + if (ite == routeMap_.end()) { + MEDIA_LOG_W("in port %s has no route map port", inPortName.c_str()); + return nullptr; + } + return GetOutPort(ite->second); +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/core/filter_base.h b/engine/pipeline/core/filter_base.h new file mode 100644 index 00000000..ebb5a431 --- /dev/null +++ b/engine/pipeline/core/filter_base.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_FILTER_BASE_H +#define HISTREAMER_PIPELINE_CORE_FILTER_BASE_H +#include +#include +#include +#include + +#include "filter.h" +#include "foundation/constants.h" +#include "foundation/error_code.h" +#include "foundation/event.h" +#include "foundation/utils.h" +#include "port.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class FilterBase : public Filter { +public: + explicit FilterBase(std::string name); + ~FilterBase() override = default; + void Init(EventReceiver* receiver, FilterCallback* callback) override; + PInPort GetInPort(const std::string& name) override; + POutPort GetOutPort(const std::string& name) override; + const std::string& GetName() override + { + return name_; + } + const EventReceiver* GetOwnerPipeline() const override + { + return eventReceiver_; + } + + ErrorCode Prepare() override; + ErrorCode Start() override; + ErrorCode Pause() override; + ErrorCode Stop() override; + ErrorCode Resume() override + { + return Start(); + } + void FlushStart() override + { + } + void FlushEnd() override + { + } + ErrorCode SetParameter(int32_t key, const Plugin::Any& value) override + { + return UNIMPLEMENT; + } + ErrorCode GetParameter(int32_t key, Plugin::Any& value) override + { + return UNIMPLEMENT; + } + + void UnlinkPrevFilters() override; + + std::vector GetNextFilters() override; + + ErrorCode PushData(const std::string& inPort, AVBufferPtr buffer) override; + ErrorCode PullData(const std::string& outPort, uint64_t offset, size_t size, AVBufferPtr& data) override; + std::vector GetWorkModes() override + { + return {WorkMode::PUSH}; + } + + // Port调用此方法向Filter报告事件 + void OnEvent(Event event) override; + +protected: + virtual void InitPorts(); + + std::string NamePort(const std::string& mime); + + /** + * 获取routemap中 outPortName对应的inport + * + * @param outPortName outport name + * @return null if not exists + */ + PInPort GetRouteInPort(const std::string& outPortName); + + /** + * 获取routemap中 inPortName对应的outport + * + * @param inPortName inport name + * @return null if not exists + */ + POutPort GetRouteOutPort(const std::string& inPortName); + +protected: + std::string name_; + std::atomic state_; + EventReceiver* eventReceiver_; + FilterCallback* callback_; + std::vector children_ {}; + std::vector inPorts_ {}; + std::vector outPorts_ {}; + std::vector routeMap_ {}; // inport -> outport + + std::map mediaTypeCntMap_ {}; + +private: + template + static T FindPort(const std::vector& list, const std::string& name); +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/pipeline/core/filter_callback.h b/engine/pipeline/core/filter_callback.h new file mode 100644 index 00000000..280552e9 --- /dev/null +++ b/engine/pipeline/core/filter_callback.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_FILTER_CALLBACK_H +#define HISTREAMER_PIPELINE_CORE_FILTER_CALLBACK_H + +#include +#include + +#include "common/any.h" +#include "foundation/error_code.h" +#include "foundation/utils.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class Filter; + +// FilterCallback用于同步处理完再返回的场景 +// Filter Callback Types +enum class FilterCallbackType { PORT_ADDED, PORT_REMOVE }; + +enum class PortType { IN, OUT }; + +struct PortDesc { + std::string name; + bool isPcm; +}; + +struct PortInfo { + PortType type; + std::vector ports; +}; + +class FilterCallback { +public: + virtual ~FilterCallback() = default; + virtual ErrorCode OnCallback(const FilterCallbackType& type, Filter* filter, const Plugin::Any& parameter) = 0; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_PIPELINE_CORE_FILTER_CALLBACK_H diff --git a/engine/pipeline/core/parameter.h b/engine/pipeline/core/parameter.h new file mode 100644 index 00000000..2b6eb182 --- /dev/null +++ b/engine/pipeline/core/parameter.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_PARAMETER_H +#define HISTREAMER_PIPELINE_CORE_PARAMETER_H + +#include + +namespace OHOS { +namespace Media { +namespace Pipeline { +// Parameter用于扩展不常用功能,下面的定义仅仅只是一些例子,不会真正使用 + +const int32_t KEY_PARAMETER_START = 0x00000000; +// source parameter start from 0x00010000 +const int32_t KEY_SOURCE_PARAMETER_START = 0x00010000; +// demuxer parameter start from 0x00020000 +const int32_t KEY_DEMUXER_PARAMETER_START = 0x00020000; +// codec parameter start from 0x000300000 +const int32_t KEY_CODEC_PARAMETER_START = 0x00030000; + +// sink parameter start from 0x00400000 +const int32_t KEY_SINK_PARAMETER_START = 0x00040000; + +const int32_t KEY_CODEC_DRIVE_MODE = KEY_CODEC_PARAMETER_START + 2; // ThreadDrivingMode + +enum class ThreadDrivingMode { + SYNC, + ASYNC, +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_PIPELINE_CORE_PARAMETER_H diff --git a/engine/pipeline/core/pipeline.h b/engine/pipeline/core/pipeline.h new file mode 100644 index 00000000..9c375136 --- /dev/null +++ b/engine/pipeline/core/pipeline.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_PIPELINE_H +#define HISTREAMER_PIPELINE_CORE_PIPELINE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "filter.h" +#include "foundation/error_code.h" +#include "foundation/utils.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class Pipeline : public Filter { +public: + virtual ~Pipeline() = default; + + /** + * 添加Filter到Pipeline + * + * 添加后的Filter,才会被初始化,才能使用。 + * + * @param filters 要添加的filter + * @return + */ + virtual ErrorCode AddFilters(std::initializer_list filters) = 0; + + virtual ErrorCode RemoveFilter(Filter* filter) = 0; + + virtual ErrorCode RemoveFilterChain(Filter* firstFilter) = 0; + + /** + * 连接Filter的默认端口 + * + * 前一个Filter的默认输出端口,与后一个Filter的默认输入端口连接 + * + * @param filters 要连接的filter + * @return + */ + virtual ErrorCode LinkFilters(std::initializer_list filters) = 0; + + /** + * 连接两个端口 + * + * 端口所属的Filter必须已经添加到Pipeline,否则报错 + * + * @param port1 前一个节点的输出端口 + * @return + */ + virtual ErrorCode LinkPorts(std::shared_ptr port1, std::shared_ptr port2) = 0; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_PIPELINE_CORE_PIPELINE_H \ No newline at end of file diff --git a/engine/pipeline/core/pipeline_core.cpp b/engine/pipeline/core/pipeline_core.cpp new file mode 100644 index 00000000..d8bf3c76 --- /dev/null +++ b/engine/pipeline/core/pipeline_core.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "pipeline_core.h" +#include +#include "foundation/log.h" +#include "osal/thread/scoped_lock.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +std::shared_ptr OHOS::Media::Pipeline::MetaBundle::GetStreamMeta(int32_t streamIndex) +{ + for (auto& ptr : streamMeta_) { + uint32_t found = 0; + if (ptr->GetUint32(Plugin::MetaID::STREAM_INDEX, found) && found == streamIndex) { + return ptr; + } + } + return nullptr; +} + +void MetaBundle::UpdateGlobalMeta(const Meta& meta) +{ + if (globalMeta_ == nullptr) { + globalMeta_ = std::make_shared(); + } + globalMeta_->Update(meta); +} + +void MetaBundle::UpdateStreamMeta(const Meta& meta) +{ + uint32_t streamIndex = 0; + if (!meta.GetUint32(Plugin::MetaID::STREAM_INDEX, streamIndex)) { + MEDIA_LOG_W("update stream meta with invalid meta, which contains no stream index, will ignore this meta"); + return; + } + for (const auto& tmp : streamMeta_) { + uint32_t stIndex = 0; + if (tmp->GetUint32(Plugin::MetaID::STREAM_INDEX, stIndex) && streamIndex == stIndex) { + tmp->Update(meta); + return; + } + } + auto ptr = std::make_shared(); + ptr->Update(meta); + streamMeta_.emplace_back(ptr); +} + +PipelineCore::PipelineCore(const std::string& name) + : name_(name), eventReceiver_(nullptr), filterCallback_(nullptr), metaBundle_(std::make_shared()) +{ +} + +const std::string& PipelineCore::GetName() +{ + return name_; +} + +const EventReceiver* PipelineCore::GetOwnerPipeline() const +{ + return eventReceiver_; +} + +void PipelineCore::Init(EventReceiver* receiver, FilterCallback* callback) +{ + eventReceiver_ = receiver; + filterCallback_ = callback; + state_ = FilterState::INITIALIZED; +} + +ErrorCode PipelineCore::Prepare() +{ + state_ = FilterState::PREPARING; + ErrorCode rtv = SUCCESS; + OSAL::ScopedLock lock(mutex_); + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + auto& filterPtr = *it; + if (filterPtr) { + if ((rtv = filterPtr->Prepare()) != SUCCESS) { + break; + } + } else { + MEDIA_LOG_E("invalid pointer in filters."); + } + } + return rtv; +} + +ErrorCode PipelineCore::Start() +{ + state_ = FilterState::RUNNING; + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + auto rtv = (*it)->Start(); + FALSE_RETURN_V(rtv == SUCCESS, rtv); + } + return SUCCESS; +} + +ErrorCode PipelineCore::Pause() +{ + if (state_ == FilterState::PAUSED) { + return SUCCESS; + } + if (state_ != FilterState::READY && state_ != FilterState::RUNNING) { + return ERROR_STATE; + } + state_ = FilterState::PAUSED; + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + if ((*it)->Pause() != SUCCESS) { + MEDIA_LOG_I("pause filter: %s", (*it)->GetName().c_str()); + } + } + return SUCCESS; +} + +ErrorCode PipelineCore::Resume() +{ + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + MEDIA_LOG_I("Resume filter: %s", (*it)->GetName().c_str()); + auto rtv = (*it)->Resume(); + FALSE_RETURN_V(rtv == SUCCESS, rtv); + } + state_ = FilterState::RUNNING; + return SUCCESS; +} + +ErrorCode PipelineCore::Stop() +{ + readyEventCnt_ = 0; + state_ = FilterState::INITIALIZED; + filtersToRemove_.clear(); + filtersToRemove_.reserve(filters_.size()); + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + if (*it == nullptr) { + MEDIA_LOG_E("PipelineCore error: %ld", filters_.size()); + continue; + } + auto filterName = (*it)->GetName(); + auto rtv = (*it)->Stop(); + FALSE_RETURN_V(rtv == SUCCESS, rtv); + } + for (const auto& filter : filtersToRemove_) { + RemoveFilter(filter); + } + MEDIA_LOG_I("Stop finished, filter number: %lu", filters_.size()); + return SUCCESS; +} + +void PipelineCore::FlushStart() +{ + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + (*it)->FlushStart(); + } +} + +void PipelineCore::FlushEnd() +{ + for (auto it = filters_.rbegin(); it != filters_.rend(); ++it) { + (*it)->FlushEnd(); + } +} + +FilterState PipelineCore::GetState() +{ + return state_; +} + +ErrorCode PipelineCore::AddFilters(std::initializer_list filtersIn) +{ + std::vector filtersToAdd; + for (auto& filterIn : filtersIn) { + bool matched = false; + for (auto& filter : filters_) { + if (filterIn == filter) { + matched = true; + break; + } + } + if (!matched) { + filtersToAdd.push_back(filterIn); + } + } + if (filtersToAdd.empty()) { + return ALREADY_EXISTS; + } + { + OSAL::ScopedLock lock(mutex_); + this->filters_.insert(this->filters_.end(), filtersToAdd.begin(), filtersToAdd.end()); + } + InitFilters(filtersToAdd); + return SUCCESS; +} + +ErrorCode PipelineCore::RemoveFilter(Filter* filter) +{ + auto it = std::find_if(filters_.begin(), filters_.end(), + [&filter](const Filter* filterPtr) { return filterPtr == filter; }); + ErrorCode rtv = INVALID_PARAM_VALUE; + if (it != filters_.end()) { + MEDIA_LOG_I("RemoveFilter %s", (*it)->GetName().c_str()); + filters_.erase(it); + rtv = SUCCESS; + } + return rtv; +} + +ErrorCode PipelineCore::RemoveFilterChain(Filter* firstFilter) +{ + if (!firstFilter) { + return NULL_POINTER_ERROR; + } + std::queue levelFilters; + levelFilters.push(firstFilter); + while (!levelFilters.empty()) { + auto filter = levelFilters.front(); + levelFilters.pop(); + filter->UnlinkPrevFilters(); + filtersToRemove_.push_back(filter); + for (auto&& nextFilter : filter->GetNextFilters()) { + levelFilters.push(nextFilter); + } + } + return SUCCESS; +} + +ErrorCode PipelineCore::LinkFilters(std::initializer_list filters) +{ + std::vector filtersToLink; + std::vector(filters).swap(filtersToLink); + int count = std::max((int)(filtersToLink.size()) - 1, 0); + for (int i = 0; i < count; i++) { + filtersToLink[i]->GetOutPort(PORT_NAME_DEFAULT)->Connect(filtersToLink[i + 1]->GetInPort(PORT_NAME_DEFAULT)); + filtersToLink[i + 1]->GetInPort(PORT_NAME_DEFAULT)->Connect(filtersToLink[i]->GetOutPort(PORT_NAME_DEFAULT)); + } + return SUCCESS; +} + +ErrorCode PipelineCore::LinkPorts(std::shared_ptr port1, std::shared_ptr port2) +{ + FAIL_RETURN(port1->Connect(port2)); + FAIL_RETURN(port2->Connect(port1)); + return SUCCESS; +} + +void PipelineCore::OnEvent(Event event) +{ + if (event.type != EVENT_READY) { + CALL_PTR_FUNC(eventReceiver_, OnEvent, event); + return; + } + + readyEventCnt_++; + MEDIA_LOG_I("OnEvent readyCnt: %lu / %lu", readyEventCnt_, filters_.size()); + if (readyEventCnt_ == filters_.size()) { + CALL_PTR_FUNC(eventReceiver_, OnEvent, event); + readyEventCnt_ = 0; + } +} + +void PipelineCore::InitFilters(const std::vector& filters) +{ + for (auto& filter : filters) { + filter->Init(this, filterCallback_); + } +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/core/pipeline_core.h b/engine/pipeline/core/pipeline_core.h new file mode 100644 index 00000000..436f2fed --- /dev/null +++ b/engine/pipeline/core/pipeline_core.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_PIPELINE_CORE_H +#define HISTREAMER_PIPELINE_CORE_PIPELINE_CORE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "filter_base.h" +#include "foundation/error_code.h" +#include "foundation/meta.h" +#include "foundation/utils.h" +#include "pipeline.h" +#include "plugin/common/plugin_types.h" +#include "thread/mutex.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class MetaBundle { +public: + MetaBundle() = default; + ~MetaBundle() = default; + std::shared_ptr GetGlobalMeta() + { + return globalMeta_; + } + + std::shared_ptr GetStreamMeta(int32_t streamIndex); + + void UpdateGlobalMeta(const Meta& meta); + + void UpdateStreamMeta(const Meta& meta); + +private: + std::shared_ptr globalMeta_; + std::vector> streamMeta_; +}; + +class PipelineCore : public Pipeline { +public: + explicit PipelineCore(const std::string& name = "pipeline_core"); + + ~PipelineCore() override = default; + + const std::string& GetName() override; + + const EventReceiver* GetOwnerPipeline() const override; + + void Init(EventReceiver* receiver, FilterCallback* callback) override; + + ErrorCode Prepare() override; + + ErrorCode Start() override; + + ErrorCode Pause() override; + + ErrorCode Resume() override; + + ErrorCode Stop() override; + + void FlushStart() override; + + void FlushEnd() override; + + FilterState GetState(); + + ErrorCode AddFilters(std::initializer_list filters) override; + ErrorCode RemoveFilter(Filter* filter) override; + ErrorCode RemoveFilterChain(Filter* firstFilter) override; + ErrorCode LinkFilters(std::initializer_list filters) override; + ErrorCode LinkPorts(std::shared_ptr port1, std::shared_ptr port2) override; + + void OnEvent(Event event) override; + + void UnlinkPrevFilters() override + { + } + std::vector GetNextFilters() override + { + return {}; + } + ErrorCode PushData(const std::string& inPort, AVBufferPtr buffer) override + { + return UNIMPLEMENT; + } + ErrorCode PullData(const std::string& outPort, uint64_t offset, size_t size, AVBufferPtr& data) override + { + return UNIMPLEMENT; + } + std::vector GetWorkModes() override + { + return {WorkMode::PUSH}; + } + + PInPort GetInPort(const std::string& name) override + { + return nullptr; + } + POutPort GetOutPort(const std::string& name) override + { + return nullptr; + } + + ErrorCode SetParameter(int32_t key, const Plugin::Any& value) override + { + return UNIMPLEMENT; + } + ErrorCode GetParameter(int32_t key, Plugin::Any& value) override + { + return UNIMPLEMENT; + } + + void InitFilters(const std::vector& filters); + + std::shared_ptr GetMetaBundle() + { + return metaBundle_; + } + +private: + std::string name_; + size_t readyEventCnt_ {0}; + FilterState state_ {FilterState::CREATED}; + OSAL::Mutex mutex_ {}; + std::vector filters_ {}; + EventReceiver* eventReceiver_; + FilterCallback* filterCallback_; + std::shared_ptr metaBundle_; + std::vector filtersToRemove_ {}; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PIPELINE_CORE_PIPELINE_CORE_H diff --git a/engine/pipeline/core/port.cpp b/engine/pipeline/core/port.cpp new file mode 100644 index 00000000..0cf1821a --- /dev/null +++ b/engine/pipeline/core/port.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "FilterPort" + +#include "port.h" +#include +#include "filter.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +std::shared_ptr EmptyInPort::port = std::make_shared(); +std::shared_ptr EmptyOutPort::port = std::make_shared(); + +const std::string& Port::GetName() +{ + return name; +} + +const InfoTransfer* Port::GetOwnerFilter() const +{ + return filter; +} + +std::shared_ptr Port::GetPeerPort() +{ + return nullptr; +} + +ErrorCode InPort::Connect(std::shared_ptr port) +{ + prevPort = port; + return SUCCESS; +} + +ErrorCode InPort::Disconnect() +{ + prevPort.reset(); + return SUCCESS; +} + +ErrorCode InPort::Activate(const std::vector& modes, WorkMode& outMode) +{ + if (auto ptr = prevPort.lock()) { + FAIL_RETURN(ptr->Activate(modes, workMode)); + outMode = workMode; + return SUCCESS; + } + MEDIA_LOG_E("[Filter %s] InPort %s Activate error: prevPort destructed", filter->GetName().c_str(), name.c_str()); + return NULL_POINTER_ERROR; +} + +std::shared_ptr InPort::GetPeerPort() +{ + return prevPort.lock(); +} + +bool InPort::Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) +{ + return filter && filter->Negotiate(name, inMeta, outCaps); +} + +void InPort::PushData(AVBufferPtr buffer) +{ + if (filter) { + filter->PushData(name, buffer); + } else { + MEDIA_LOG_E("filter destructed"); + } +} + +ErrorCode InPort::PullData(uint64_t offset, size_t size, AVBufferPtr& data) +{ + if (auto ptr = prevPort.lock()) { + return ptr->PullData(offset, size, data); + } + MEDIA_LOG_E("prevPort destructed"); + return NULL_POINTER_ERROR; +} + +ErrorCode OutPort::Connect(std::shared_ptr port) +{ + if (InSamePipeline(port)) { + nextPort = port; + return SUCCESS; + } + MEDIA_LOG_E("Connect filters that are not in the same pipeline."); + return INVALID_PARAM_VALUE; +} + +ErrorCode OutPort::Disconnect() +{ + nextPort.reset(); + return SUCCESS; +} + +bool OutPort::InSamePipeline(std::shared_ptr port) const +{ + auto filter1 = GetOwnerFilter(); + FALSE_RETURN_V(filter1 != nullptr, false); + auto filter2 = port->GetOwnerFilter(); + FALSE_RETURN_V(filter2 != nullptr, false); + auto pipeline1 = filter1->GetOwnerPipeline(); + FALSE_RETURN_V(pipeline1 != nullptr, false); + auto pipeline2 = filter2->GetOwnerPipeline(); + FALSE_RETURN_V(pipeline2 != nullptr, false); + return pipeline1 == pipeline2; +} + +ErrorCode OutPort::Activate(const std::vector& modes, WorkMode& outMode) +{ + if (filter) { + auto supportedModes = filter->GetWorkModes(); + for (auto mode : modes) { + auto found = std::find(supportedModes.cbegin(), supportedModes.cend(), mode); + if (found != supportedModes.cend()) { + outMode = mode; + workMode = mode; + return SUCCESS; // 最先找到的兼容的mode,作为最后结果 + } + } + } else { + MEDIA_LOG_E("filter destructed"); + } + return NEGOTIATE_ERROR; +} + +std::shared_ptr OutPort::GetPeerPort() +{ + return nextPort; +} + +bool OutPort::Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) +{ + return nextPort->Negotiate(inMeta, outCaps); +} + +void OutPort::PushData(AVBufferPtr buffer) +{ + nextPort->PushData(buffer); +} + +ErrorCode OutPort::PullData(uint64_t offset, size_t size, AVBufferPtr& data) +{ + if (filter) { + return filter->PullData(name, offset, size, data); + } + MEDIA_LOG_E("filter destructed"); + return NULL_POINTER_ERROR; +} + +ErrorCode EmptyInPort::Connect(std::shared_ptr port) +{ + MEDIA_LOG_E("Connect in EmptyInPort"); + return SUCCESS; +} +ErrorCode EmptyInPort::Activate(const std::vector& modes, WorkMode& outMode) +{ + MEDIA_LOG_E("Activate in EmptyInPort"); + return SUCCESS; +} +bool EmptyInPort::Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) +{ + MEDIA_LOG_E("Negotiate in EmptyInPort"); + return false; +} +void EmptyInPort::PushData(AVBufferPtr buffer) +{ + MEDIA_LOG_E("PushData in EmptyInPort"); +} +ErrorCode EmptyInPort::PullData(uint64_t offset, size_t size, AVBufferPtr& data) +{ + MEDIA_LOG_E("PullData in EmptyInPort"); + return UNIMPLEMENT; +} + +ErrorCode EmptyOutPort::Connect(std::shared_ptr port) +{ + MEDIA_LOG_E("Connect in EmptyOutPort"); + return SUCCESS; +} +ErrorCode EmptyOutPort::Activate(const std::vector& modes, WorkMode& outMode) +{ + MEDIA_LOG_E("Activate in EmptyOutPort"); + return SUCCESS; +} +bool EmptyOutPort::Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) +{ + MEDIA_LOG_E("Negotiate in EmptyOutPort"); + return false; +} +void EmptyOutPort::PushData(AVBufferPtr buffer) +{ + MEDIA_LOG_E("PushData in EmptyOutPort"); +} +ErrorCode EmptyOutPort::PullData(uint64_t offset, size_t size, AVBufferPtr& data) +{ + MEDIA_LOG_E("PullData in EmptyOutPort"); + return UNIMPLEMENT; +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/core/port.h b/engine/pipeline/core/port.h new file mode 100644 index 00000000..d607fad8 --- /dev/null +++ b/engine/pipeline/core/port.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_CORE_PORT_H +#define HISTREAMER_PIPELINE_CORE_PORT_H + +#include +#include +#include + +#include "error_code.h" +#include "foundation/constants.h" +#include "foundation/type_define.h" +#include "foundation/utils.h" + +namespace OHOS { +namespace Media { +class Meta; +} +} // namespace OHOS + +namespace OHOS { +namespace Media { +namespace Pipeline { +class InfoTransfer; + +enum class WorkMode { PUSH, PULL }; + +class Port { +public: + Port(InfoTransfer* ownerFilter, std::string portName, bool ownerNegotiate) + : filter(ownerFilter), name(std::move(portName)), useFilterNegotiate(ownerNegotiate) {} + virtual ~Port() = default; + const std::string& GetName(); + const InfoTransfer* GetOwnerFilter() const; + virtual std::shared_ptr GetPeerPort(); + WorkMode GetWorkMode() + { + return workMode; + } + virtual ErrorCode Connect(std::shared_ptr port) = 0; + virtual ErrorCode Disconnect() = 0; + virtual ErrorCode Activate(const std::vector& modes, WorkMode& outMode) = 0; + virtual bool Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) = 0; + virtual void PushData(AVBufferPtr buffer) = 0; + virtual ErrorCode PullData(uint64_t offset, size_t size, AVBufferPtr& data) = 0; + +protected: + InfoTransfer* filter; + WorkMode workMode {WorkMode::PUSH}; + const std::string name; + bool useFilterNegotiate; +}; + +class OutPort; + +class InPort : public Port { +public: + explicit InPort(InfoTransfer* filter, std::string name = PORT_NAME_DEFAULT, bool ownerNegotiate = false) + : Port(filter, std::move(name), ownerNegotiate) {} + ~InPort() override = default; + ErrorCode Connect(std::shared_ptr port) override; + ErrorCode Disconnect() override; + ErrorCode Activate(const std::vector& modes, WorkMode& outMode) override; + std::shared_ptr GetPeerPort() override; + bool Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) override; + void PushData(AVBufferPtr buffer) override; + ErrorCode PullData(uint64_t offset, size_t size, AVBufferPtr& data) override; + +private: + std::weak_ptr prevPort; +}; + +class OutPort : public Port { +public: + explicit OutPort(InfoTransfer* filter, std::string name = PORT_NAME_DEFAULT, bool ownerNegotiate = false) + : Port(filter, std::move(name), ownerNegotiate) {} + ~OutPort() override = default; + ErrorCode Connect(std::shared_ptr port) override; + ErrorCode Disconnect() override; + ErrorCode Activate(const std::vector& modes, WorkMode& outMode) override; + std::shared_ptr GetPeerPort() override; + bool Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) override; + void PushData(AVBufferPtr buffer) override; + ErrorCode PullData(uint64_t offset, size_t size, AVBufferPtr& data) override; + +private: + bool InSamePipeline(std::shared_ptr port) const; + +private: + std::shared_ptr nextPort; +}; + +class EmptyInPort : public InPort { +public: + static std::shared_ptr GetInstance() + { + return port; + } + EmptyInPort() : InPort(nullptr, "emptyInPort", false) {} + ~EmptyInPort() override = default; + ErrorCode Connect(std::shared_ptr port) override; + ErrorCode Activate(const std::vector& modes, WorkMode& outMode) override; + bool Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) override; + void PushData(AVBufferPtr buffer) override; + ErrorCode PullData(uint64_t offset, size_t size, AVBufferPtr& data) override; + +private: + static std::shared_ptr port; +}; + +class EmptyOutPort : public OutPort { +public: + static std::shared_ptr GetInstance() + { + return port; + } + EmptyOutPort() : OutPort(nullptr, "emptyOutPort", false) {} + ~EmptyOutPort() override = default; + ErrorCode Connect(std::shared_ptr port) override; + ErrorCode Activate(const std::vector& modes, WorkMode& outMode) override; + bool Negotiate(const std::shared_ptr& inMeta, CapabilitySet& outCaps) override; + void PushData(AVBufferPtr buffer) override; + ErrorCode PullData(uint64_t offset, size_t size, AVBufferPtr& data) override; + +private: + static std::shared_ptr port; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PIPELINE_CORE_PORT_H diff --git a/engine/pipeline/factory/filter_factory.cpp b/engine/pipeline/factory/filter_factory.cpp new file mode 100644 index 00000000..a3ce1909 --- /dev/null +++ b/engine/pipeline/factory/filter_factory.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "filter_factory.h" + +namespace OHOS { +namespace Media { +FilterFactory& FilterFactory::Instance() +{ + static FilterFactory instance; + return instance; +} + +void FilterFactory::Init() +{ +} + +void FilterFactory::RegisterGenerator(const std::string& name, const InstanceGenerator& generator) +{ + auto result = generators.emplace(name, generator); + if (!result.second) { + result.first->second = generator; + } +} + +std::shared_ptr FilterFactory::CreateFilter(const std::string& filterName, + const std::string& aliasName) +{ + auto it = generators.find(filterName); + if (it != generators.end()) { + return it->second(aliasName); + } + return nullptr; +} +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/factory/filter_factory.h b/engine/pipeline/factory/filter_factory.h new file mode 100644 index 00000000..d9443eec --- /dev/null +++ b/engine/pipeline/factory/filter_factory.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_FILTER_FACTORY_H +#define HISTREAMER_PIPELINE_FILTER_FACTORY_H + +#include +#include +#include +#include + +#include "pipeline/core/filter.h" + +namespace OHOS { +namespace Media { +class FilterFactory { + using InstanceGenerator = std::function(const std::string&)>; + +public: + ~FilterFactory() = default; + + FilterFactory(const FilterFactory&) = delete; + + FilterFactory operator=(const FilterFactory&) = delete; + + static FilterFactory& Instance(); + + void Init(); + + void RegisterGenerator(const std::string& name, const InstanceGenerator& generator); + + std::shared_ptr CreateFilter(const std::string& filterName, const std::string& aliasName); + + template + std::shared_ptr CreateFilterWithType(const std::string& filterName, const std::string& aliasName) + { + auto filter = CreateFilter(filterName, aliasName); + auto typedFilter = std::dynamic_pointer_cast(filter); + return typedFilter; + } + + template + void RegisterFilter(const std::string& name) + { + RegisterGenerator(name, [](const std::string& aliaName) { return std::make_shared(aliaName); }); + } + +private: + FilterFactory() = default; + + std::unordered_map generators; +}; + +template +class AutoRegisterFilter { +public: + explicit AutoRegisterFilter(const std::string& name) + { + FilterFactory::Instance().RegisterFilter(name); + } + ~AutoRegisterFilter() = default; +}; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PIPELINE_FILTER_FACTORY_H diff --git a/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp new file mode 100644 index 00000000..b1f207b9 --- /dev/null +++ b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "AudioDecoderFilter" + +#include "audio_decoder_filter.h" + +#include "common/plugin_utils.h" +#include "factory/filter_factory.h" +#include "foundation/constants.h" +#include "foundation/memory_helper.h" +#include "osal/utils/util.h" +#include "plugin/common/plugin_audio_tags.h" + +namespace { +constexpr int32_t DEFAULT_OUT_BUFFER_POOL_SIZE = 5; +constexpr int32_t DEFAULT_IN_BUFFER_POOL_SIZE = 5; +constexpr int32_t MAX_OUT_DECODED_DATA_SIZE_PER_FRAME = 20 * 1024; // 20kB +constexpr int32_t AF_64BIT_BYTES = 8; +constexpr int32_t AF_32BIT_BYTES = 4; +constexpr int32_t AF_16BIT_BYTES = 2; +constexpr int32_t AF_8BIT_BYTES = 1; + +int32_t CalculateBufferSize(const std::shared_ptr &meta) +{ + using namespace OHOS::Media; + int32_t samplesPerFrame; + if (!meta->GetInt32(MetaID::AUDIO_SAMPLE_PRE_FRAME, samplesPerFrame)) { + return 0; + } + int32_t channels; + if (!meta->GetInt32(MetaID::AUDIO_CHANNELS, channels)) { + return 0; + } + int32_t bytesPerSample = 0; + Plugin::AudioSampleFormat format; + if (!meta->GetData(MetaID::AUDIO_SAMPLE_FORMAT, format)) { + return 0; + } + switch (format) { + case Plugin::AudioSampleFormat::S64: + case Plugin::AudioSampleFormat::S64P: + case Plugin::AudioSampleFormat::U64: + case Plugin::AudioSampleFormat::U64P: + case Plugin::AudioSampleFormat::F64: + case Plugin::AudioSampleFormat::F64P: + bytesPerSample = AF_64BIT_BYTES; + break; + case Plugin::AudioSampleFormat::F32: + case Plugin::AudioSampleFormat::F32P: + case Plugin::AudioSampleFormat::S32: + case Plugin::AudioSampleFormat::S32P: + case Plugin::AudioSampleFormat::U32: + case Plugin::AudioSampleFormat::U32P: + bytesPerSample = AF_32BIT_BYTES; + break; + case Plugin::AudioSampleFormat::S16: + case Plugin::AudioSampleFormat::S16P: + case Plugin::AudioSampleFormat::U16: + case Plugin::AudioSampleFormat::U16P: + bytesPerSample = AF_16BIT_BYTES; + break; + case Plugin::AudioSampleFormat::S8: + case Plugin::AudioSampleFormat::U8: + bytesPerSample = AF_8BIT_BYTES; + break; + default: + bytesPerSample = 0; + break; + } + return bytesPerSample * samplesPerFrame * channels; +} +}; + +namespace OHOS { +namespace Media { +namespace Pipeline { +static AutoRegisterFilter g_registerFilterHelper("builtin.player.audiodecoder"); + +class AudioDecoderFilter::DataCallbackImpl : public Plugin::DataCallbackHelper { +public: + explicit DataCallbackImpl(AudioDecoderFilter& filter): decoderFilter_(filter){} + + ~DataCallbackImpl() override = default; + + void OnInputBufferDone(const std::shared_ptr &input) override + { + decoderFilter_.OnInputBufferDone(input); + } + + void OnOutputBufferDone(const std::shared_ptr &output) override + { + decoderFilter_.OnOutputBufferDone(output); + } + +private: + AudioDecoderFilter& decoderFilter_; +}; + +AudioDecoderFilter::AudioDecoderFilter(const std::string &name): DecoderFilterBase(name), + dataCallback_(std::make_shared(*this)) +{ +} + +AudioDecoderFilter::~AudioDecoderFilter() +{ + MEDIA_LOG_I("AudioDecoderFilter dtor called..."); + Release(); + if (inBufferQ_) { + inBufferQ_->SetActive(false); + } + if (outBufferQ_) { + outBufferQ_->SetActive(false); + } +} + +ErrorCode AudioDecoderFilter::QueueAllBufferInPoolToPluginLocked() +{ + ErrorCode err = SUCCESS; + while (!outBufferPool_->Empty()) { + auto ptr = outBufferPool_->AllocateBuffer(); + if (ptr == nullptr) { + MEDIA_LOG_W("cannot allocate buffer in buffer pool"); + continue; + } + err = TranslatePluginStatus(plugin_->QueueOutputBuffer(ptr, -1)); + if (err != SUCCESS) { + MEDIA_LOG_E("queue output buffer error"); + } + } + return err; +} + +ErrorCode AudioDecoderFilter::Start() +{ + MEDIA_LOG_D("Start called"); + if (state_ != FilterState::READY && state_ != FilterState::PAUSED) { + MEDIA_LOG_W("call decoder start() when state is not ready or working"); + return ERROR_STATE; + } + return FilterBase::Start(); +} + +ErrorCode AudioDecoderFilter::Prepare() +{ + if (state_ != FilterState::INITIALIZED) { + MEDIA_LOG_W("decoder filter is not in init state"); + return ERROR_STATE; + } + if (!outBufferQ_) { + outBufferQ_ = std::make_shared>("decoderOutBuffQueue", DEFAULT_OUT_BUFFER_POOL_SIZE); + } else { + outBufferQ_->SetActive(true); + } + if (!pushTask_) { + pushTask_ = std::make_shared("decPushThread"); + pushTask_->RegisterHandler([this] { FinishFrame(); }); + } + if (drivingMode_ == ThreadDrivingMode::ASYNC) { + if (!inBufferQ_) { + inBufferQ_ = std::make_shared>("decoderFilterInBufQue", + DEFAULT_IN_BUFFER_POOL_SIZE); + } else { + inBufferQ_->SetActive(true); + } + if (!handleFrameTask_) { + handleFrameTask_ = std::make_shared("decHandleFrameThread"); + handleFrameTask_->RegisterHandler([this] { HandleFrame(); }); + } + } else { + if (inBufferQ_) { + inBufferQ_->SetActive(false); + inBufferQ_.reset(); + } + if (handleFrameTask_) { + handleFrameTask_->Stop(); + handleFrameTask_.reset(); + } + } + auto err = FilterBase::Prepare(); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "Audio Decoder prepare error because of filter base prepare error"); + return SUCCESS; +} + +bool AudioDecoderFilter::Negotiate(const std::string& inPort, const std::shared_ptr &inMeta, + CapabilitySet& outCaps) +{ + if (state_ != FilterState::PREPARING) { + MEDIA_LOG_W("decoder filter is not in preparing when negotiate"); + return false; + } + auto creator = [] (const std::string& pluginName) { + return Plugin::PluginManager::Instance().CreateCodecPlugin(pluginName); + }; + ErrorCode err = FindPluginAndUpdate(inMeta, Plugin::PluginType::CODEC, plugin_, + targetPluginInfo_, creator); + + RETURN_TARGET_ERR_MESSAGE_LOG_IF_FAIL(err, false, "cannot find matched plugin"); + outCaps = targetPluginInfo_->inCaps; + auto targetOutPort = GetRouteOutPort(inPort); + if (targetOutPort == nullptr) { + MEDIA_LOG_E("decoder out port is not found"); + return false; + } + // todo how to decide pcm caps + std::shared_ptr pcmMeta = std::make_shared(); + // shall we avoid copy + pcmMeta->Update(*inMeta); + pcmMeta->SetString(Media::Plugin::MetaID::MIME, MEDIA_MIME_AUDIO_RAW); + CapabilitySet sinkCaps; + if (!targetOutPort->Negotiate(pcmMeta, sinkCaps)) { + MEDIA_LOG_E("negotiate with sink failed"); + return false; + } + err = ConfigureToStartPluginLocked(inMeta); + if (err != SUCCESS) { + MEDIA_LOG_E("decoder configure error"); + OnEvent({EVENT_ERROR, err}); + return false; + } + + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_ != nullptr) { + handleFrameTask_->Start(); + } + pushTask_->Start(); + state_ = FilterState::READY; + OnEvent({EVENT_READY}); + return true; +} + +ErrorCode AudioDecoderFilter::ConfigureWithMetaLocked(const std::shared_ptr &meta) +{ + uint32_t channels; + if (meta->GetUint32(MetaID::AUDIO_CHANNELS, channels)) { + MEDIA_LOG_D("found audio channel meta"); + SetPluginParameterLocked(Tag::AUDIO_CHANNELS, channels); + } + uint32_t sampleRate; + if (meta->GetUint32(MetaID::AUDIO_SAMPLE_RATE, sampleRate)) { + MEDIA_LOG_D("found audio sample rate meta"); + SetPluginParameterLocked(Tag::AUDIO_SAMPLE_RATE, sampleRate); + } + int64_t bitRate; + if (meta->GetInt64(MetaID::MEDIA_BITRATE, bitRate)) { + MEDIA_LOG_D("found audio bit rate meta"); + SetPluginParameterLocked(Tag::MEDIA_BITRATE, bitRate); + } + auto audioFormat = Plugin::AudioSampleFormat::U8; + if (meta->GetData(MetaID::AUDIO_SAMPLE_FORMAT, audioFormat)) { + SetPluginParameterLocked(Tag::AUDIO_SAMPLE_FORMAT, audioFormat); + } + std::vector codecConfig; + if (meta->GetData>(MetaID::MEDIA_CODEC_CONFIG, codecConfig)) { + SetPluginParameterLocked(Tag::MEDIA_CODEC_CONFIG, std::move(codecConfig)); + } + return SUCCESS; +} + +ErrorCode AudioDecoderFilter::ConfigureToStartPluginLocked(const std::shared_ptr& meta) +{ + auto err = TranslatePluginStatus(plugin_->SetDataCallback(dataCallback_)); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "set decoder plugin callback failed"); + err = TranslatePluginStatus(plugin_->Init()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "decoder plugin init error"); + + err = ConfigureWithMetaLocked(meta); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "configure decoder plugin error"); + + uint32_t bufferCnt = 0; + if (GetPluginParameterLocked(Tag::REQUIRED_OUT_BUFFER_CNT, bufferCnt) != SUCCESS) { + bufferCnt = DEFAULT_OUT_BUFFER_POOL_SIZE; + } + + // 每次重新创建bufferPool + outBufferPool_ = std::make_shared>(bufferCnt); + + int32_t bufferSize = CalculateBufferSize(meta); + if (bufferSize == 0) { + bufferSize = MAX_OUT_DECODED_DATA_SIZE_PER_FRAME; + } + auto outAllocator = plugin_->GetAllocator(); + if (outAllocator == nullptr) { + MEDIA_LOG_I("plugin doest not support out allocator, using framework allocator"); + outBufferPool_->Init(bufferSize); + } else { + MEDIA_LOG_I("using plugin output allocator"); + for (size_t cnt = 0; cnt < bufferCnt; cnt++) { + auto buf = MemoryHelper::make_unique(); + buf->AllocMemory(outAllocator, bufferSize); + outBufferPool_->Append(std::move(buf)); + } + } + + err = TranslatePluginStatus(plugin_->Prepare()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "decoder prepare failed"); + err = QueueAllBufferInPoolToPluginLocked(); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "queue out buffer to plugin failed"); + + err = TranslatePluginStatus(plugin_->Start()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "decoder start failed"); + + return SUCCESS; +} + +ErrorCode AudioDecoderFilter::PushData(const std::string &inPort, AVBufferPtr buffer) +{ + if (state_ != FilterState::READY && state_ != FilterState::PAUSED && state_ != FilterState::RUNNING) { + MEDIA_LOG_W("pushing data to decoder when state is %d", static_cast(state_.load())); + return ERROR_STATE; + } + if (isFlushing_) { + MEDIA_LOG_I("decoder is flushing, discarding this data from port %s", inPort.c_str()); + return SUCCESS; + } + // async + if (drivingMode_ == ThreadDrivingMode::ASYNC) { + inBufferQ_->Push(buffer); + return SUCCESS; + } + // sync + HandleOneFrame(buffer); + return SUCCESS; +} + +void AudioDecoderFilter::FlushStart() +{ + MEDIA_LOG_I("FlushStart entered."); + isFlushing_ = true; + if (inBufferQ_) { + inBufferQ_->SetActive(false); + } + handleFrameTask_->PauseAsync(); + if (outBufferQ_) { + outBufferQ_->SetActive(false); + } + pushTask_->PauseAsync(); + if (plugin_ != nullptr) { + auto err = TranslatePluginStatus(plugin_->Flush()); + if (err != SUCCESS) { + MEDIA_LOG_E("decoder plugin flush error"); + } + } + MEDIA_LOG_I("FlushStart exit."); +} + +void AudioDecoderFilter::FlushEnd() +{ + MEDIA_LOG_I("FlushEnd entered"); + isFlushing_ = false; + if (inBufferQ_) { + inBufferQ_->SetActive(true); + } + handleFrameTask_->Start(); + if (outBufferQ_) { + outBufferQ_->SetActive(true); + } + pushTask_->Start(); + if (plugin_ != nullptr) { + QueueAllBufferInPoolToPluginLocked(); + } +} + +ErrorCode AudioDecoderFilter::Stop() +{ + MEDIA_LOG_I("AudioDecoderFilter stop start."); + // 先改变底层状态 然后停掉上层线程 否则会产生死锁 + auto err = TranslatePluginStatus(plugin_->Flush()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "decoder flush error"); + err = TranslatePluginStatus(plugin_->Stop()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "decoder stop error"); + outBufferQ_->SetActive(false); + pushTask_->Pause(); + + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_ != nullptr) { + inBufferQ_->SetActive(false); + handleFrameTask_->Pause(); + } + + outBufferPool_.reset(); + MEDIA_LOG_I("AudioDecoderFilter stop end."); + return FilterBase::Stop(); +} + +ErrorCode AudioDecoderFilter::Release() +{ + if (plugin_) { + plugin_->Stop(); + plugin_->Deinit(); + } + + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_ != nullptr) { + handleFrameTask_->Stop(); + handleFrameTask_.reset(); + } + + if (inBufferQ_ != nullptr) { + inBufferQ_->SetActive(false); + inBufferQ_.reset(); + } + // 先停止线程 然后释放bufferQ 如果顺序反过来 可能导致线程访问已经释放的锁 + if (pushTask_ != nullptr) { + pushTask_->Stop(); + pushTask_.reset(); + } + + if (outBufferQ_ != nullptr) { + outBufferQ_->SetActive(false); + outBufferQ_.reset(); + } + return SUCCESS; +} + +void AudioDecoderFilter::HandleFrame() +{ + MEDIA_LOG_D("HandleFrame called"); + auto oneBuffer = inBufferQ_->Pop(); + if (oneBuffer == nullptr) { + MEDIA_LOG_W("decoder find nullptr in esBufferQ"); + return; + } + HandleOneFrame(oneBuffer); + MEDIA_LOG_D("HandleFrame finished"); +} + +void AudioDecoderFilter::HandleOneFrame(const std::shared_ptr& data) +{ + MEDIA_LOG_D("HandleOneFrame called"); + Plugin::Status status = Plugin::Status::OK; + status = plugin_->QueueInputBuffer(data, -1); + if (status != Plugin::Status::OK) { + MEDIA_LOG_W("send data to plugin error"); + } + MEDIA_LOG_D("HandleOneFrame finished"); +} + +void AudioDecoderFilter::FinishFrame() +{ + MEDIA_LOG_D("begin finish frame"); + // get buffer from plugin + auto ptr = outBufferQ_->Pop(); + if (ptr) { + // push to port + auto oPort = outPorts_[0]; + if (oPort->GetWorkMode() == WorkMode::PUSH) { + oPort->PushData(ptr); + } else { + MEDIA_LOG_W("decoder out port works in pull mode"); + } + ptr.reset(); // 释放buffer 如果没有被缓存使其回到buffer pool 如果被sink缓存 则从buffer pool拿其他的buffer + auto oPtr = outBufferPool_->AllocateBuffer(); + if (oPtr != nullptr) { + oPtr->Reset(); + plugin_->QueueOutputBuffer(oPtr, -1); + } + } + MEDIA_LOG_D("end finish frame"); +} + +void AudioDecoderFilter::OnInputBufferDone(const std::shared_ptr &buffer) +{ + // do nothing since we has no input buffer pool +} + +void AudioDecoderFilter::OnOutputBufferDone(const std::shared_ptr &buffer) +{ + outBufferQ_->Push(buffer); +} +} +} +} diff --git a/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.h b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.h new file mode 100644 index 00000000..a17b3cf4 --- /dev/null +++ b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_FILTER_AUDIO_DECODER_H +#define HISTREAMER_PIPELINE_FILTER_AUDIO_DECODER_H + +#include "filters/codec/decoder_filter_base.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class AudioDecoderFilter : public DecoderFilterBase { +public: + explicit AudioDecoderFilter(const std::string &name); + ~AudioDecoderFilter() override; + + ErrorCode Start() override; + + ErrorCode Stop() override; + + ErrorCode Prepare() override; + + bool Negotiate(const std::string &inPort, const std::shared_ptr &inMeta, + CapabilitySet &outCaps) override; + + ErrorCode PushData(const std::string &inPort, AVBufferPtr buffer) override; + + void FlushStart() override; + + void FlushEnd() override; + +private: + class DataCallbackImpl; + + ErrorCode ConfigureToStartPluginLocked(const std::shared_ptr &meta); + + ErrorCode ConfigureWithMetaLocked(const std::shared_ptr &meta); + + void HandleFrame(); + + void HandleOneFrame(const std::shared_ptr &data); + + void FinishFrame(); + + ErrorCode Release(); + + // callbacks + void OnInputBufferDone(const std::shared_ptr &buffer); + + void OnOutputBufferDone(const std::shared_ptr &buffer); + +private: + ErrorCode QueueAllBufferInPoolToPluginLocked(); + + std::shared_ptr> inBufferQ_; + std::shared_ptr> outBufferQ_; // PCM data + std::shared_ptr handleFrameTask_ {}; // dequeue from es bufferQ then enqueue to plugin + // this task will dequeue from the plugin and then push to downstream + std::shared_ptr pushTask_ {nullptr}; + std::shared_ptr> outBufferPool_ {}; + bool isFlushing_ {false}; + + std::shared_ptr dataCallback_ {nullptr}; +}; +} +} +} +#endif // HISTREAMER_PIPELINE_FILTER_AUDIO_DECODER_H diff --git a/engine/pipeline/filters/codec/decoder_filter_base.cpp b/engine/pipeline/filters/codec/decoder_filter_base.cpp new file mode 100644 index 00000000..e53740ac --- /dev/null +++ b/engine/pipeline/filters/codec/decoder_filter_base.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "AudioDecoderFilter" + +#include "decoder_filter_base.h" + +#include "common/plugin_utils.h" +#include "factory/filter_factory.h" +#include "foundation/constants.h" +#include "foundation/memory_helper.h" +#include "osal/utils/util.h" +#include "plugin/common/plugin_audio_tags.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +DecoderFilterBase::DecoderFilterBase(const std::string &name): FilterBase(name) {} + +DecoderFilterBase::~DecoderFilterBase(){} + +ErrorCode DecoderFilterBase::SetPluginParameterLocked(Tag tag, const Plugin::ValueType &value) +{ + return TranslatePluginStatus(plugin_->SetParameter(tag, value)); +} + +ErrorCode DecoderFilterBase::SetParameter(int32_t key, const Plugin::Any& value) +{ + if (state_.load() == FilterState::CREATED) { + return ERROR_STATE; + } + switch (key) { + case KEY_CODEC_DRIVE_MODE: { + if (state_ == FilterState::READY || state_ == FilterState::RUNNING || state_ == FilterState::PAUSED) { + MEDIA_LOG_W("decoder cannot set parameter KEY_CODEC_DRIVE_MODE in this state"); + return ERROR_STATE; + } + if (value.Type() != typeid(ThreadDrivingMode)) { + return INVALID_PARAM_TYPE; + } + drivingMode_ = Plugin::AnyCast(value); + return SUCCESS; + } + default: { + Tag tag = Tag::INVALID; + if (!TranslateIntoParameter(key, tag)) { + MEDIA_LOG_I("SetParameter key %d is out of boundary", key); + return INVALID_PARAM_VALUE; + } + RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin_); + return SetPluginParameterLocked(tag, value); + } + } +} + +ErrorCode DecoderFilterBase::GetParameter(int32_t key, Plugin::Any& value) +{ + if (state_.load() == FilterState::CREATED) { + return ERROR_STATE; + } + switch (key) { + case KEY_CODEC_DRIVE_MODE: + value = drivingMode_; + return SUCCESS; + default: { + Tag tag = Tag::INVALID; + if (!TranslateIntoParameter(key, tag)) { + MEDIA_LOG_I("GetParameter key %d is out of boundary", key); + return INVALID_PARAM_VALUE; + } + RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin_); + return TranslatePluginStatus(plugin_->GetParameter(tag, value)); + } + } +} +} +} +} diff --git a/engine/pipeline/filters/codec/decoder_filter_base.h b/engine/pipeline/filters/codec/decoder_filter_base.h new file mode 100644 index 00000000..e514a5a0 --- /dev/null +++ b/engine/pipeline/filters/codec/decoder_filter_base.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_FILTER_DECODER_BASE_H +#define HISTREAMER_PIPELINE_FILTER_DECODER_BASE_H + +#include + +#include "foundation/blocking_queue.h" +#include "foundation/buffer_pool.h" +#include "foundation/type_define.h" +#include "foundation/utils.h" +#include "osal/thread/task.h" +#include "plugin/common/plugin_tags.h" +#include "plugin/core/codec.h" +#include "pipeline/core/filter_base.h" +#include "plugin/core/plugin_info.h" +#include "common/plugin_utils.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class DecoderFilterBase : public FilterBase { +public: + explicit DecoderFilterBase(const std::string &name); + ~DecoderFilterBase() override; + + ErrorCode SetParameter(int32_t key, const Plugin::Any &value) override; + + ErrorCode GetParameter(int32_t key, Plugin::Any &value) override; + +protected: + ErrorCode SetPluginParameterLocked(Tag tag, const Plugin::ValueType &value); + + template + ErrorCode GetPluginParameterLocked(Tag tag, T& value) + { + Plugin::Any tmp; + auto err = TranslatePluginStatus(plugin_->GetParameter(tag, tmp)); + if (err == SUCCESS && tmp.Type() == typeid(T)) { + value = Plugin::AnyCast(tmp); + } + return err; + } + + ThreadDrivingMode drivingMode_ {ThreadDrivingMode::ASYNC}; + + // plugin + std::shared_ptr plugin_ {nullptr}; + std::shared_ptr targetPluginInfo_ {nullptr}; +}; +} +} +} +#endif // HISTREAMER_PIPELINE_FILTER_DECODER_BASE_H diff --git a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp new file mode 100644 index 00000000..912e7d8b --- /dev/null +++ b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#define LOG_TAG "VideoDecoderFilter" + +#include "video_decoder_filter.h" +#include +#include "foundation/constants.h" +#include "factory/filter_factory.h" +#include "plugin/common/plugin_video_tags.h" +#include "foundation/memory_helper.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +const uint32_t DEFAULT_IN_BUFFER_POOL_SIZE = 8; +const uint32_t DEFAULT_OUT_BUFFER_POOL_SIZE = 8; +const float VIDEO_PIX_DEPTH = 1.5; +static uint32_t VIDEO_ALIGN_SIZE = 16; + +template +static constexpr T AlignUp(T num, U alignment) +{ + return alignment > 0 ? ((num + static_cast(alignment) - 1) & (~(static_cast(alignment) - 1))) : num; +} + +static AutoRegisterFilter g_registerFilterHelper("builtin.player.videodecoder"); + +class VideoDecoderFilter::DataCallbackImpl : public Plugin::DataCallbackHelper { +public: + explicit DataCallbackImpl(VideoDecoderFilter& filter): decFilter_(filter) + { + } + + ~DataCallbackImpl() override = default; + + void OnInputBufferDone(const std::shared_ptr &input) override + { + decFilter_.OnInputBufferDone(input); + } + + void OnOutputBufferDone(const std::shared_ptr &output) override + { + decFilter_.OnOutputBufferDone(output); + } + +private: + VideoDecoderFilter& decFilter_; +}; + +VideoDecoderFilter::VideoDecoderFilter(const std::string &name): DecoderFilterBase(name), + dataCallback_(std::make_shared(*this)) +{ + MEDIA_LOG_I("VideoDecoderFilter ctor called..."); + vdecFormat_.width = 0; + vdecFormat_.height = 0; + vdecFormat_.bitRate = -1; +} + +VideoDecoderFilter::~VideoDecoderFilter() +{ + MEDIA_LOG_I("VideoDecoderFilter dtor called..."); + if (plugin_) { + plugin_->Stop(); + plugin_->Deinit(); + } + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_ != nullptr) { + handleFrameTask_->Stop(); + handleFrameTask_.reset(); + } + if (inBufQue_ != nullptr) { + inBufQue_->SetActive(false); + inBufQue_.reset(); + } + if (pushTask_ != nullptr) { + pushTask_->Stop(); + pushTask_.reset(); + } + if (outBufQue_ != nullptr) { + outBufQue_->SetActive(false); + outBufQue_.reset(); + } +} + +ErrorCode VideoDecoderFilter::Start() +{ + MEDIA_LOG_D("Start called"); + if (state_ != FilterState::READY && state_ != FilterState::PAUSED) { + MEDIA_LOG_W("call decoder start() when state_ is not ready or working"); + return ERROR_STATE; + } + return FilterBase::Start(); +} + +ErrorCode VideoDecoderFilter::Prepare() +{ + MEDIA_LOG_D("Prepare called"); + if (state_ != FilterState::INITIALIZED) { + MEDIA_LOG_W("decoder filter is not in init state_"); + return ERROR_STATE; + } + if (!outBufQue_) { + outBufQue_ = std::make_shared>("decoderFilterOutBufQue", + DEFAULT_IN_BUFFER_POOL_SIZE); + } else { + outBufQue_->SetActive(true); + } + if (!pushTask_) { + pushTask_ = std::make_shared("decPushThread"); + pushTask_->RegisterHandler([this] { FinishFrame(); }); + } + if (!inBufQue_) { + inBufQue_ = std::make_shared>("decoderFilterInBufQue", + DEFAULT_OUT_BUFFER_POOL_SIZE); + } else { + inBufQue_->SetActive(true); + } + if (!handleFrameTask_) { + handleFrameTask_ = std::make_shared("decHandleFrameThread"); + handleFrameTask_->RegisterHandler([this] { HandleFrame(); }); + } + return FilterBase::Prepare(); +} + +bool VideoDecoderFilter::Negotiate(const std::string& inPort, const std::shared_ptr &inMeta, + CapabilitySet& outCaps) +{ + if (state_ != FilterState::PREPARING) { + MEDIA_LOG_W("decoder filter is not in preparing when negotiate"); + return false; + } + + MEDIA_LOG_D("Negotiate called"); + auto creator = [] (const std::string& pluginName) { + return Plugin::PluginManager::Instance().CreateCodecPlugin(pluginName); + }; + ErrorCode err = FindPluginAndUpdate(inMeta, Plugin::PluginType::CODEC, plugin_, + targetPluginInfo_, creator); + RETURN_TARGET_ERR_MESSAGE_LOG_IF_FAIL(err, false, "cannot find matched plugin"); + outCaps = targetPluginInfo_->inCaps; + auto targetOutPort = GetRouteOutPort(inPort); + if (targetOutPort == nullptr) { + MEDIA_LOG_E("decoder out port is not found"); + return false; + } + if (Configure(inMeta) != ErrorCode::SUCCESS) { + MEDIA_LOG_E("decoder configure error"); + Event event{ + .type = EVENT_ERROR, + .param = err, + }; + OnEvent(event); + return false; + } + std::shared_ptr videoMeta = std::make_shared(); + videoMeta->Update(*inMeta); + videoMeta->SetString(Media::Plugin::MetaID::MIME, MEDIA_MIME_VIDEO_RAW); + videoMeta->SetUint32(Media::Plugin::MetaID::VIDEO_PIXEL_FORMAT, vdecFormat_.format); + CapabilitySet sinkCaps; + if (!targetOutPort->Negotiate(videoMeta, sinkCaps)) { + MEDIA_LOG_E("negotiate with sink failed"); + return false; + } + return true; +} + +ErrorCode VideoDecoderFilter::AllocateOutputBuffers() +{ + uint32_t bufferCnt = 0; + if (GetPluginParameterLocked(Tag::REQUIRED_OUT_BUFFER_CNT, bufferCnt) != ErrorCode::SUCCESS) { + bufferCnt = DEFAULT_OUT_BUFFER_POOL_SIZE; + } + outBufPool_ = std::make_shared>(bufferCnt); + // YUV420: size = stride * height * 1.5 + uint32_t bufferSize = 0; + uint32_t stride = AlignUp(vdecFormat_.width, VIDEO_ALIGN_SIZE); + if ((vdecFormat_.format == static_cast(Plugin::VideoPixelFormat::YUV420P)) || + (vdecFormat_.format == static_cast(Plugin::VideoPixelFormat::NV21)) || + (vdecFormat_.format == static_cast(Plugin::VideoPixelFormat::NV12))) { + bufferSize = static_cast(AlignUp(stride, VIDEO_ALIGN_SIZE) * + AlignUp(vdecFormat_.height, VIDEO_ALIGN_SIZE) * VIDEO_PIX_DEPTH); + MEDIA_LOG_D("Output buffer size: %u", bufferSize); + } else { + // TODO: need to check video sink support and calc buffer size + MEDIA_LOG_E("Unsupported video pixel format: %d", vdecFormat_.format); + return ErrorCode::UNIMPLEMENT; + } + auto outAllocator = plugin_->GetAllocator(); // zero copy need change to use sink allocator + if (outAllocator == nullptr) { + MEDIA_LOG_I("plugin doest not support out allocator, using framework allocator"); + outBufPool_->Init(bufferSize, Plugin::BufferMetaType::VIDEO); + } else { + MEDIA_LOG_I("using plugin output allocator"); + for (size_t cnt = 0; cnt < bufferCnt; cnt++) { + auto buf = MemoryHelper::make_unique(Plugin::BufferMetaType::VIDEO); + buf->AllocMemory(outAllocator, bufferSize); + outBufPool_->Append(std::move(buf)); + } + } + return ErrorCode::SUCCESS; +} + +ErrorCode VideoDecoderFilter::SetVideoDecoderFormat(const std::shared_ptr& meta) +{ + vdecFormat_.format = static_cast(Plugin::VideoPixelFormat::NV12); + if (!meta->GetString(MetaID::MIME, vdecFormat_.mime)) { + return ErrorCode::INVALID_PARAM_VALUE; + } + if (!meta->GetUint32(MetaID::VIDEO_WIDTH, vdecFormat_.width)) { + return ErrorCode::INVALID_PARAM_VALUE; + } + if (!meta->GetUint32(MetaID::VIDEO_HEIGHT, vdecFormat_.height)) { + return ErrorCode::INVALID_PARAM_VALUE; + } + if (!meta->GetInt64(MetaID::MEDIA_BITRATE, vdecFormat_.bitRate)) { + MEDIA_LOG_D("Do not have codec bit rate"); + } + // Optional: codec extra data + if (!meta->GetData>(MetaID::MEDIA_CODEC_CONFIG, vdecFormat_.codecConfig)) { + MEDIA_LOG_D("Do not have codec extra data"); + } + return ErrorCode::SUCCESS; +} + +ErrorCode VideoDecoderFilter::ConfigurePluginParams() +{ + if (SetPluginParameterLocked(Tag::MIME, vdecFormat_.mime) != ErrorCode::SUCCESS) { + MEDIA_LOG_W("Set mime to plugin fail"); + return ErrorCode::UNKNOWN_ERROR; + } + if (SetPluginParameterLocked(Tag::VIDEO_WIDTH, vdecFormat_.width) != ErrorCode::SUCCESS) { + MEDIA_LOG_W("Set width to plugin fail"); + return ErrorCode::UNKNOWN_ERROR; + } + if (SetPluginParameterLocked(Tag::VIDEO_HEIGHT, vdecFormat_.height) != ErrorCode::SUCCESS) { + MEDIA_LOG_W("Set height to plugin fail"); + return ErrorCode::UNKNOWN_ERROR; + } + if (SetPluginParameterLocked(Tag::VIDEO_PIXEL_FORMAT, vdecFormat_.format) != ErrorCode::SUCCESS) { + MEDIA_LOG_W("Set pixel format to plugin fail"); + return ErrorCode::UNKNOWN_ERROR; + } + if (vdecFormat_.bitRate != -1) { + if (SetPluginParameterLocked(Tag::MEDIA_BITRATE, vdecFormat_.bitRate) != ErrorCode::SUCCESS) { + MEDIA_LOG_W("Set bitrate to plugin fail"); + } + } + // Optional: codec extra data + if (vdecFormat_.codecConfig.size() > 0) { + if (SetPluginParameterLocked(Tag::MEDIA_CODEC_CONFIG, std::move(vdecFormat_.codecConfig)) != + ErrorCode::SUCCESS) { + MEDIA_LOG_W("Set bitrate to plugin fail"); + } + } + MEDIA_LOG_D("ConfigurePluginParams success, mime: %s, width: %u, height: %u, format: %u, bitRate: %u", + vdecFormat_.mime.c_str(), vdecFormat_.width, vdecFormat_.height, vdecFormat_.format, + vdecFormat_.bitRate); + return ErrorCode::SUCCESS; +} + +ErrorCode VideoDecoderFilter::ConfigurePluginOutputBuffers() +{ + ErrorCode err = ErrorCode::SUCCESS; + while (!outBufPool_->Empty()) { + auto ptr = outBufPool_->AllocateBuffer(); + if (ptr == nullptr) { + MEDIA_LOG_W("cannot allocate buffer in buffer pool"); + continue; + } + err = TranslatePluginStatus(plugin_->QueueOutputBuffer(ptr, -1)); + if (err != ErrorCode::SUCCESS) { + MEDIA_LOG_E("queue output buffer error"); + } + } + return err; +} + +ErrorCode VideoDecoderFilter::InitPlugin() +{ + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->SetDataCallback(dataCallback_)), + "Set plugin callback fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Init()), "Init plugin error"); + return ErrorCode::SUCCESS; +} + +ErrorCode VideoDecoderFilter::ConfigurePlugin() +{ + RETURN_ERR_MESSAGE_LOG_IF_FAIL(ConfigurePluginParams(), "Configure plugin params error"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(ConfigurePluginOutputBuffers(), "Configure plugin output buffers error"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Prepare()), "Prepare plugin fail"); + return TranslatePluginStatus(plugin_->Start()); +} + +ErrorCode VideoDecoderFilter::Configure(const std::shared_ptr& meta) +{ + MEDIA_LOG_D("Configure called"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(InitPlugin(), "Init plugin fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(SetVideoDecoderFormat(meta), "Set video decoder format fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(AllocateOutputBuffers(), "Alloc output buffers fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(ConfigurePlugin(), "Config plugin fail"); + if (handleFrameTask_ != nullptr) { + handleFrameTask_->Start(); + } + pushTask_->Start(); + + state_ = FilterState::READY; + Event event{ + .type = EVENT_READY, + }; + OnEvent(event); + return ErrorCode::SUCCESS; +} + +ErrorCode VideoDecoderFilter::PushData(const std::string& inPort, AVBufferPtr buffer) +{ + if (state_ != FilterState::READY && state_ != FilterState::PAUSED && state_ != FilterState::RUNNING) { + MEDIA_LOG_W("pushing data to decoder when state_ is %d", static_cast(state_.load())); + return ErrorCode::ERROR_STATE; + } + if (isFlushing_) { + MEDIA_LOG_I("decoder is flushing, discarding this data from port %s", inPort.c_str()); + return ErrorCode::SUCCESS; + } + inBufQue_->Push(buffer); + return ErrorCode::SUCCESS; +} + +void VideoDecoderFilter::FlushStart() +{ + MEDIA_LOG_I("FlushStart entered"); + isFlushing_ = true; + if (inBufQue_) { + inBufQue_->SetActive(false); + } + handleFrameTask_->PauseAsync(); + if (outBufQue_) { + outBufQue_->SetActive(false); + } + pushTask_->PauseAsync(); + if (plugin_ != nullptr) { + auto err = TranslatePluginStatus(plugin_->Flush()); + if (err != SUCCESS) { + MEDIA_LOG_E("decoder plugin flush error"); + } + } +} + +void VideoDecoderFilter::FlushEnd() +{ + MEDIA_LOG_I("FlushEnd entered"); + isFlushing_ = false; + if (inBufQue_) { + inBufQue_->SetActive(true); + } + handleFrameTask_->Start(); + if (outBufQue_) { + outBufQue_->SetActive(true); + } + pushTask_->Start(); + if (plugin_ != nullptr) { + ConfigurePluginOutputBuffers(); + } +} + +ErrorCode VideoDecoderFilter::Stop() +{ + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Flush()), "Flush plugin fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Stop()), "Stop plugin fail"); + outBufQue_->SetActive(false); + pushTask_->Pause(); + inBufQue_->SetActive(false); + handleFrameTask_->Pause(); + outBufPool_.reset(); + MEDIA_LOG_I("Stop success"); + return FilterBase::Stop(); +} + +void VideoDecoderFilter::HandleFrame() +{ + MEDIA_LOG_D("HandleFrame called"); + auto oneBuffer = inBufQue_->Pop(); + if (oneBuffer == nullptr) { + MEDIA_LOG_W("decoder find nullptr in esBufferQ"); + return; + } + HandleOneFrame(oneBuffer); +} + +void VideoDecoderFilter::HandleOneFrame(const std::shared_ptr& data) +{ + MEDIA_LOG_D("HandleOneFrame called"); + auto ret = plugin_->QueueInputBuffer(data, -1); + if (ret != Plugin::Status::OK) { + MEDIA_LOG_W("Send data to plugin error: %d", ret); + } +} + +void VideoDecoderFilter::FinishFrame() +{ + MEDIA_LOG_D("begin finish frame"); + auto ptr = outBufQue_->Pop(); + if (ptr) { + auto oPort = outPorts_[0]; + if (oPort->GetWorkMode() == WorkMode::PUSH) { + oPort->PushData(ptr); + } else { + MEDIA_LOG_W("decoder out port works in pull mode"); + } + ptr.reset(); + auto oPtr = outBufPool_->AllocateBuffer(); + if (oPtr != nullptr) { + oPtr->Reset(); + plugin_->QueueOutputBuffer(oPtr, 0); + } + } + MEDIA_LOG_D("end finish frame"); +} + +void VideoDecoderFilter::OnInputBufferDone(const std::shared_ptr &buffer) +{ + // do nothing since we has no input buffer pool +} + +void VideoDecoderFilter::OnOutputBufferDone(const std::shared_ptr &buffer) +{ + outBufQue_->Push(buffer); +} +} +} +} +#endif \ No newline at end of file diff --git a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.h b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.h new file mode 100644 index 00000000..c1d33d7d --- /dev/null +++ b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#ifndef MEDIA_PIPELINE_VIDEO_DECODER_FILTER_H +#define MEDIA_PIPELINE_VIDEO_DECODER_FILTER_H + +#include "type_define.h" +#include "filters/codec/decoder_filter_base.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class VideoDecoderFilter : public DecoderFilterBase { +public: + explicit VideoDecoderFilter(const std::string &name); + ~VideoDecoderFilter() override; + + ErrorCode Prepare() override; + + ErrorCode Start() override; + + ErrorCode Stop() override; + + void FlushStart() override; + + void FlushEnd() override; + + bool Negotiate(const std::string &inPort, const std::shared_ptr &inMeta, + CapabilitySet &outCaps) override; + + ErrorCode PushData(const std::string &inPort, AVBufferPtr buffer) override; + + void OnInputBufferDone(const std::shared_ptr &buffer); + + void OnOutputBufferDone(const std::shared_ptr &buffer); + +private: + class DataCallbackImpl; + + struct VideoDecoderFormat { + std::string mime; + uint32_t width; + uint32_t height; + int64_t bitRate; + uint32_t format; + std::vector codecConfig; + }; + + ErrorCode SetVideoDecoderFormat(const std::shared_ptr& meta); + + ErrorCode AllocateOutputBuffers(); + + ErrorCode InitPlugin(); + + ErrorCode ConfigurePluginOutputBuffers(); + + ErrorCode ConfigurePluginParams(); + + ErrorCode ConfigurePlugin(); + + ErrorCode Configure(const std::shared_ptr &meta); + + void HandleFrame(); + + void HandleOneFrame(const std::shared_ptr &data); + + void FinishFrame(); + + bool isFlushing_ {false}; + VideoDecoderFormat vdecFormat_; + std::shared_ptr dataCallback_ {nullptr}; + + std::shared_ptr handleFrameTask_ {nullptr}; + std::shared_ptr pushTask_ {nullptr}; + std::shared_ptr> outBufPool_ {nullptr}; + std::shared_ptr> inBufQue_ {nullptr}; + std::shared_ptr> outBufQue_ {nullptr}; +}; +} +} +} +#endif // MEDIA_PIPELINE_VIDEO_DECODER_FILTER_H +#endif \ No newline at end of file diff --git a/engine/pipeline/filters/common/plugin_utils.cpp b/engine/pipeline/filters/common/plugin_utils.cpp new file mode 100644 index 00000000..b190c186 --- /dev/null +++ b/engine/pipeline/filters/common/plugin_utils.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "plugin_utils.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +/** + * translate plugin error into pipeline error code + * @param pluginError + * @return + */ +OHOS::Media::ErrorCode TranslatePluginStatus(Plugin::Status pluginError) +{ + if (pluginError != OHOS::Media::Plugin::Status::OK) { + return OHOS::Media::ErrorCode::UNKNOWN_ERROR; + } + return OHOS::Media::ErrorCode::SUCCESS; +} +bool TranslateIntoParameter(const int &key, OHOS::Media::Plugin::Tag &tag) +{ + if (key < static_cast(OHOS::Media::Plugin::Tag::INVALID)) { + return false; + } + tag = static_cast(key); + return true; +} +} +} +} diff --git a/engine/pipeline/filters/common/plugin_utils.h b/engine/pipeline/filters/common/plugin_utils.h new file mode 100644 index 00000000..aab1070a --- /dev/null +++ b/engine/pipeline/filters/common/plugin_utils.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PIPELINE_FILTER_PLUGIN_UTILS_H +#define HISTREAMER_PIPELINE_FILTER_PLUGIN_UTILS_H + +#include +#include + +#include "foundation/meta.h" +#include "foundation/type_define.h" +#include "foundation/error_code.h" +#include "pipeline/core/compatible_check.h" +#include "plugin/common/plugin_types.h" +#include "plugin/common/plugin_tags.h" +#include "plugin/common/plugin_buffer.h" +#include "plugin/core/plugin_info.h" +#include "plugin/core/plugin_manager.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +#define RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin) \ + if ((plugin) == nullptr) { \ + return PLUGIN_NOT_FOUND; \ + } + +/** + * translate plugin error into pipeline error code + * @param pluginError + * @return + */ +ErrorCode TranslatePluginStatus(Plugin::Status pluginError); + +bool TranslateIntoParameter(const int &key, OHOS::Media::Plugin::Tag &tag); + +template +inline ErrorCode FindPluginAndUpdate(const std::shared_ptr &inMeta, + Plugin::PluginType pluginType, std::shared_ptr& plugin, std::shared_ptr& pluginInfo, + std::function(const std::string&)> pluginCreator) +{ + uint32_t maxRank = 0; + std::shared_ptr info; + auto pluginNames = Plugin::PluginManager::Instance().ListPlugins(pluginType); + for (const auto &name:pluginNames) { + auto tmpInfo = Plugin::PluginManager::Instance().GetPluginInfo(pluginType, name); + if (CompatibleWith(tmpInfo->inCaps, *inMeta) && tmpInfo->rank > maxRank) { + info = tmpInfo; + } + } + if (info == nullptr) { + return PLUGIN_NOT_FOUND; + } + + // try to reuse the plugin if their name are the same + if (plugin != nullptr && pluginInfo != nullptr) { + if (info->name == pluginInfo->name) { + if (TranslatePluginStatus(plugin->Reset()) == SUCCESS) { + return SUCCESS; + } + } + plugin->Deinit(); + } + plugin = pluginCreator(info->name); + if (plugin == nullptr) { + return PLUGIN_NOT_FOUND; + } + pluginInfo = info; + return SUCCESS; +} +} +} +} +#endif // HISTREAMER_PIPELINE_FILTER_PLUGIN_UTILS_H diff --git a/engine/pipeline/filters/demux/data_packer.cpp b/engine/pipeline/filters/demux/data_packer.cpp new file mode 100644 index 00000000..68b4d48d --- /dev/null +++ b/engine/pipeline/filters/demux/data_packer.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "data_packer.h" +#include +#include "foundation/log.h" +#include "plugin/common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +#define LOG_WARN_IF_FAIL(errorNo, errorMsg) \ + do { \ + if ((errorNo) != EOK) { \ + MEDIA_LOG_W(errorMsg); \ + } \ + } while (0) + +#define RETURN_FALSE_IF_NULL(ptr) \ + do { \ + if ((ptr) == nullptr) { \ + return false; \ + } \ + } while (0) + +DataPacker::DataPacker() : mutex_(), que_(), assembler_(), size_(0), bufferOffset_(0), pts_(0), dts_(0) +{ + MEDIA_LOG_I("DataPacker ctor..."); +} + +DataPacker::~DataPacker() +{ + MEDIA_LOG_I("DataPacker dtor..."); +} + +inline static size_t AudioBufferSize(AVBufferPtr& ptr) +{ + return ptr->GetMemory()->GetSize(); +} + +inline static size_t AudioBufferCapacity(AVBufferPtr& ptr) +{ + return ptr->GetMemory()->GetCapacity(); +} + +inline static uint8_t* AudioBufferWritableData(AVBufferPtr& ptr, size_t size, size_t position = 0) +{ + return ptr->GetMemory()->GetWritableData(size, position); +} + +inline static const uint8_t* AudioBufferReadOnlyData(AVBufferPtr& ptr) +{ + return ptr->GetMemory()->GetReadOnlyData(); +} + +void DataPacker::PushData(AVBufferPtr bufferPtr) +{ + MEDIA_LOG_D("DataPacker PushData..."); + OSAL::ScopedLock lock(mutex_); + size_ += AudioBufferSize(bufferPtr); + if (que_.size() == 1) { + bufferOffset_ = 0; + dts_ = bufferPtr->dts; + pts_ = bufferPtr->pts; + } + que_.emplace_back(std::move(bufferPtr)); +} + +bool DataPacker::IsDataAvailable(uint64_t offset, uint32_t size) +{ + MEDIA_LOG_D("DataPacker IsDataAvailable..."); + OSAL::ScopedLock lock(mutex_); + auto curOffset = bufferOffset_; + if (que_.empty() || offset < curOffset) { + return false; + } + int bufCnt = que_.size(); + auto offsetEnd = offset + size; + auto curOffsetEnd = AudioBufferSize(que_.front()); + if (bufCnt == 1) { + return offsetEnd <= curOffsetEnd; + } + auto preOffsetEnd = curOffsetEnd; + for (auto i = 1; i < bufCnt; ++i) { + auto bufferOffset = 0; + if (preOffsetEnd != bufferOffset) { + return false; + } + curOffsetEnd = bufferOffset + AudioBufferSize(que_[i]); + if (curOffsetEnd >= offsetEnd) { + return true; + } else { + preOffsetEnd = curOffsetEnd; + } + } + return false; +} + +// 在调用当前接口前需要先调用IsDataAvailable() +bool DataPacker::PeekRange(uint64_t offset, uint32_t size, AVBufferPtr& bufferPtr) +{ + MEDIA_LOG_D("DataPacker PeekRange(offset, size) = (%" PRIu64 ", %ul)...", offset, size); + OSAL::ScopedLock lock(mutex_); + uint8_t* dstPtr = nullptr; + if (bufferPtr && AudioBufferCapacity(bufferPtr) < size) { + return false; + } else { + assembler_.resize(size); + dstPtr = assembler_.data(); + } + auto offsetEnd = offset + size; + auto curOffsetEnd = AudioBufferSize(que_[0]); + if (offsetEnd <= curOffsetEnd) { + dstPtr = AudioBufferWritableData(bufferPtr, size); + RETURN_FALSE_IF_NULL(dstPtr); + LOG_WARN_IF_FAIL(memcpy_s(dstPtr, size, AudioBufferReadOnlyData(que_[0]), size), "failed to memcpy"); + } else { + auto srcSize = AudioBufferSize(que_[0]) - bufferOffset_; + dstPtr = AudioBufferWritableData(bufferPtr, srcSize); + RETURN_FALSE_IF_NULL(dstPtr); + LOG_WARN_IF_FAIL(memcpy_s(dstPtr, srcSize, AudioBufferReadOnlyData(que_[0]) + bufferOffset_, srcSize), + "failed to memcpy"); + dstPtr += srcSize; + size -= srcSize; + for (size_t i = 1; i < que_.size(); ++i) { + curOffsetEnd = AudioBufferSize(que_[i]); + if (curOffsetEnd >= offsetEnd) { + dstPtr = AudioBufferWritableData(bufferPtr, size); + RETURN_FALSE_IF_NULL(dstPtr); + LOG_WARN_IF_FAIL(memcpy_s(dstPtr, size, AudioBufferReadOnlyData(que_[i]), size), "failed to memcpy"); + break; + } else { + srcSize = AudioBufferSize(que_[i]); + dstPtr = AudioBufferWritableData(bufferPtr, srcSize); + RETURN_FALSE_IF_NULL(dstPtr); + LOG_WARN_IF_FAIL(memcpy_s(dstPtr, srcSize, AudioBufferReadOnlyData(que_[i]), srcSize), + "failed to memcpy"); + dstPtr += srcSize; + size -= srcSize; + } + } + } + if (!bufferPtr) { + WrapAssemblerBuffer(offset).swap(bufferPtr); + } + bufferPtr->pts = pts_; + bufferPtr->dts = dts_; + return true; +} + +// 单个buffer不足以存储请求的数据,需要合并多个buffer从而组成目标buffer +bool DataPacker::RepackBuffers(uint64_t offset, uint32_t size, AVBufferPtr& bufferPtr) +{ + if (!PeekRange(offset, size, bufferPtr)) { + return false; + } + auto offsetEnd = offset + size; + size_ -= size; + while (!que_.empty()) { + auto& buf = que_.front(); + auto curOffsetEnd = AudioBufferSize(buf); + if (curOffsetEnd < offsetEnd) { + que_.pop_front(); + continue; + } else if (curOffsetEnd == offsetEnd) { + que_.pop_front(); + bufferOffset_ = 0; + if (!que_.empty()) { + dts_ = que_.front()->dts; + pts_ = que_.front()->pts; + } + } else { + bufferOffset_ = AudioBufferSize(buf) - (curOffsetEnd - offsetEnd); + dts_ = buf->dts; + pts_ = buf->pts; + } + break; + } + return true; +} + +// 在调用当前接口前需要先调用IsDataAvailable() +AVBufferPtr DataPacker::GetRange(uint64_t offset, uint32_t size) +{ + MEDIA_LOG_D("DataPacker GetRange(offset, size) = (%" PRIu64 ", %ul)...", offset, size); + OSAL::ScopedLock lock(mutex_); + AVBufferPtr bufferPtr = nullptr; + if (AudioBufferCapacity(que_[0]) < size) { + RepackBuffers(offset, size, bufferPtr); + return bufferPtr; + } + size_ -= size; + + auto offsetEnd = offset + size; + auto curOffsetEnd = AudioBufferSize(que_[0]); + if (offsetEnd < curOffsetEnd) { + bufferOffset_ = static_cast(offset); + bufferPtr = MakeAliasBuffer(que_[0], bufferOffset_, size); + bufferOffset_ += size; + } else if (offsetEnd == curOffsetEnd) { + bufferPtr = que_.front(); + que_.pop_front(); + bufferOffset_ = 0; + if (!que_.empty()) { + dts_ = que_.front()->dts; + pts_ = que_.front()->pts; + } + } else { + bufferPtr = que_.front(); + que_.pop_front(); + auto remainBytes = AudioBufferSize(bufferPtr) - bufferOffset_; + + auto dataPtr = AudioBufferWritableData(bufferPtr, remainBytes); + if (dataPtr) { + LOG_WARN_IF_FAIL(memmove_s(dataPtr, remainBytes, dataPtr + bufferOffset_, remainBytes), + "failed to memmove"); + dataPtr = AudioBufferWritableData(bufferPtr, size); + LOG_WARN_IF_FAIL(memcpy_s(dataPtr + remainBytes, size - remainBytes, AudioBufferReadOnlyData(que_.front()), + size - remainBytes), "failed to memcpy"); + bufferOffset_ = size - remainBytes; + dts_ = que_.front()->dts; + pts_ = que_.front()->pts; + } else { + dataPtr = nullptr; + } + } + return bufferPtr; +} + +bool DataPacker::GetRange(uint64_t offset, uint32_t size, AVBufferPtr& bufferPtr) +{ + if (bufferPtr) { + if (!RepackBuffers(offset, size, bufferPtr)) { + return false; + } + } else { + GetRange(offset, size).swap(bufferPtr); + } + return true; +} + +AVBufferPtr DataPacker::MakeAliasBuffer(AVBufferPtr bufferPtr, uint32_t offset, uint32_t size) +{ + MEDIA_LOG_D("DataPacker MakeAliasBuffer(offset, size) = (%uld, %ul)...", offset, size); + AudioBufferWritableData(bufferPtr, size); + return bufferPtr; +} + +AVBufferPtr DataPacker::WrapAssemblerBuffer(uint64_t offset) +{ + MEDIA_LOG_D("DataPacker WrapAssemblerBuffer, offset = %" PRIu64, offset); + auto bufferPtr = std::make_shared(); + auto dataPtr = std::shared_ptr(assembler_.data(), [this](void* ptr) { assembler_.resize(0); }); + auto bufferData = bufferPtr->WrapMemoryPtr(dataPtr, assembler_.size(), assembler_.size()); + bufferPtr->dts = dts_; + bufferPtr->pts = pts_; + return bufferPtr; +} + +void DataPacker::Flush() +{ + MEDIA_LOG_D("DataPacker Flush called."); + OSAL::ScopedLock lock(mutex_); + que_.clear(); + size_ = 0; + bufferOffset_ = 0; + dts_ = 0; + pts_ = 0; +} +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/filters/demux/data_packer.h b/engine/pipeline/filters/demux/data_packer.h new file mode 100644 index 00000000..3d8b3c46 --- /dev/null +++ b/engine/pipeline/filters/demux/data_packer.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_DATA_PACKER_H +#define HISTREAMER_DATA_PACKER_H + +#include +#include +#include + +#include "thread/mutex.h" +#include "thread/scoped_lock.h" +#include "foundation/type_define.h" + +namespace OHOS { +namespace Media { +class DataPacker { +public: + DataPacker(); + + ~DataPacker(); + + DataPacker(const DataPacker& other) = delete; + + DataPacker& operator=(const DataPacker& other) = delete; + + void PushData(AVBufferPtr bufferPtr); + + bool IsDataAvailable(uint64_t offset, uint32_t size); + + bool PeekRange(uint64_t offset, uint32_t size, AVBufferPtr &bufferPtr); + + bool GetRange(uint64_t offset, uint32_t size, AVBufferPtr &bufferPtr); + + AVBufferPtr GetRange(uint64_t offset, uint32_t size); + + void Flush(); + +private: + static AVBufferPtr MakeAliasBuffer(AVBufferPtr bufferPtr, uint32_t offset, uint32_t size); + + AVBufferPtr WrapAssemblerBuffer(uint64_t offset); + + bool RepackBuffers(uint64_t offset, uint32_t size, AVBufferPtr &bufferPtr); + + OSAL::Mutex mutex_; + std::deque que_; + std::vector assembler_; + std::atomic size_; + uint32_t bufferOffset_; + uint64_t pts_; + uint64_t dts_; +}; +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_DATA_PACKER_H diff --git a/engine/pipeline/filters/demux/demuxer_filter.cpp b/engine/pipeline/filters/demux/demuxer_filter.cpp new file mode 100644 index 00000000..e063157d --- /dev/null +++ b/engine/pipeline/filters/demux/demuxer_filter.cpp @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "DemuxerFilter" + +#include "demuxer_filter.h" +#include +#include "factory/filter_factory.h" +#include "foundation/constants.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +static AutoRegisterFilter g_registerFilterHelper("builtin.player.demuxer"); + +class DemuxerFilter::DataSourceImpl : public Plugin::DataSourceHelper { +public: + explicit DataSourceImpl(const DemuxerFilter& filter); + ~DataSourceImpl() override = default; + Plugin::Status ReadAt(int64_t offset, std::shared_ptr& buffer, size_t expectedLen) override; + Plugin::Status GetSize(size_t& size) override; + +private: + const DemuxerFilter& filter; +}; + +DemuxerFilter::DataSourceImpl::DataSourceImpl(const DemuxerFilter& filter) : filter(filter) +{ +} + +/** + * ReadAt Plugin::DataSource::ReadAt implementation. + * @param offset offset in media stream. + * @param buffer caller allocate real buffer. + * @param expectedLen buffer size wanted to read. + * @return read result. + */ +Plugin::Status DemuxerFilter::DataSourceImpl::ReadAt(int64_t offset, std::shared_ptr& buffer, + size_t expectedLen) +{ + if (!buffer || buffer->IsEmpty() || expectedLen == 0 || !filter.IsOffsetValid(offset)) { + MEDIA_LOG_E("ReadAt failed, buffer empty: %d, expectedLen: %d, offset: %ld", !buffer, + static_cast(expectedLen), offset); + return Plugin::Status::ERROR_UNKNOWN; + } + Plugin::Status rtv = Plugin::Status::OK; + switch (filter.pluginState_.load()) { + case DEMUXER_STATE_NULL: + rtv = Plugin::Status::ERROR_WRONG_STATE; + MEDIA_LOG_E("ReadAt error due to DEMUXER_STATE_NULL"); + break; + case DEMUXER_STATE_PARSE_HEADER: { + if (!filter.peekRange_(static_cast(offset), expectedLen, buffer)) { + rtv = Plugin::Status::ERROR_NOT_ENOUGH_DATA; + } + break; + } + case DEMUXER_STATE_PARSE_FRAME: { + if (!filter.getRange_(static_cast(offset), expectedLen, buffer)) { + rtv = Plugin::Status::END_OF_STREAM; + } + break; + } + default: + break; + } + return rtv; +} + +Plugin::Status DemuxerFilter::DataSourceImpl::GetSize(size_t& size) +{ + size = filter.mediaDataSize_; + return (filter.mediaDataSize_ > 0) ? Plugin::Status::OK : Plugin::Status::ERROR_WRONG_STATE; +} + +DemuxerFilter::DemuxerFilter(std::string name) + : FilterBase(std::move(name)), + uriSuffix_(), + mediaDataSize_(0), + task_(nullptr), + typeFinder_(nullptr), + dataPacker_(nullptr), + pluginName_(), + plugin_(nullptr), + pluginState_(DEMUXER_STATE_NULL), + pluginAllocator_(nullptr), + dataSource_(std::make_shared(*this)), + mediaMetaData_(), + curTimeUs_(0) +{ + MEDIA_LOG_D("ctor called"); +} + +DemuxerFilter::~DemuxerFilter() +{ + MEDIA_LOG_D("dtor called"); + if (task_) { + task_->Stop(); + } + if (plugin_) { + plugin_->Deinit(); + } +} + +void DemuxerFilter::Init(EventReceiver* receiver, FilterCallback* callback) +{ + this->eventReceiver_ = receiver; + this->callback_ = callback; + inPorts_.clear(); + outPorts_.clear(); + inPorts_.push_back(std::make_shared(this, PORT_NAME_DEFAULT, true)); + state_ = FilterState::INITIALIZED; +} + +ErrorCode DemuxerFilter::Start() +{ + if (task_) { + task_->Start(); + } + return FilterBase::Start(); +} + +ErrorCode DemuxerFilter::Stop() +{ + MEDIA_LOG_I("Stop called."); + if (task_) { + task_->Pause(); + } + Reset(); + if (!outPorts_.empty()) { + PortInfo portInfo; + portInfo.type = PortType::OUT; + portInfo.ports.reserve(outPorts_.size()); + for (const auto& outPort : outPorts_) { + portInfo.ports.push_back({outPort->GetName(), false}); + } + if (callback_) { + callback_->OnCallback(FilterCallbackType::PORT_REMOVE, static_cast(this), portInfo); + } + } + return FilterBase::Stop(); +} + +ErrorCode DemuxerFilter::Pause() +{ + MEDIA_LOG_D("Pause called"); + return FilterBase::Pause(); +} + +void DemuxerFilter::FlushStart() +{ + MEDIA_LOG_D("FlushStart entered"); + if (dataPacker_) { + dataPacker_->Flush(); + } + if (task_) { + task_->Pause(); + } +} + +void DemuxerFilter::FlushEnd() +{ + MEDIA_LOG_D("FlushEnd entered"); +} + +ErrorCode DemuxerFilter::Prepare() +{ + MEDIA_LOG_D("Prepare called"); + pluginState_ = DEMUXER_STATE_NULL; + Pipeline::WorkMode mode; + GetInPort(PORT_NAME_DEFAULT)->Activate({Pipeline::WorkMode::PULL, Pipeline::WorkMode::PUSH}, mode); + if (mode == Pipeline::WorkMode::PULL) { + ActivatePullMode(); + } else { + ActivatePushMode(); + } + curTimeUs_ = 0; + state_ = FilterState::PREPARING; + return SUCCESS; +} + +ErrorCode DemuxerFilter::PushData(const std::string& inPort, AVBufferPtr buffer) +{ + MEDIA_LOG_D("PushData for port: %s", inPort.c_str()); + if (dataPacker_) { + dataPacker_->PushData(std::move(buffer)); + } + return SUCCESS; +} + +bool DemuxerFilter::Negotiate(const std::string&, const std::shared_ptr& inMeta, CapabilitySet&) +{ + return inMeta->GetString(Media::Plugin::MetaID::MEDIA_FILE_EXTENSION, uriSuffix_) && + inMeta->GetUint64(Media::Plugin::MetaID::MEDIA_FILE_SIZE, mediaDataSize_); +} + +ErrorCode DemuxerFilter::SeekTo(int64_t msec) +{ + if (!plugin_) { + MEDIA_LOG_E("SeekTo failed due to no valid plugin"); + return NULL_POINTER_ERROR; + } + ErrorCode rtv = SUCCESS; + auto ret = plugin_->SeekTo(-1, msec * 1000, Plugin::SeekMode::BACKWARD); // 1000 + if (ret == Plugin::Status::OK) { + if (task_) { + task_->Start(); + } + } else { + MEDIA_LOG_E("SeekTo failed with return value: %d", static_cast(ret)); + rtv = SEEK_FAILURE; + } + return rtv; +} + +std::vector> DemuxerFilter::GetStreamMetaInfo() const +{ + return mediaMetaData_.trackMetas; +} + +std::shared_ptr DemuxerFilter::GetGlobalMetaInfo() const +{ + return mediaMetaData_.globalMeta; +} + +ErrorCode DemuxerFilter::GetCurrentTime(int64_t& time) const +{ + time = curTimeUs_ / 1000; // time in milliseconds + return SUCCESS; +} + +void DemuxerFilter::Reset() +{ + curTimeUs_ = 0; + mediaMetaData_.globalMeta.reset(); + mediaMetaData_.trackMetas.clear(); + mediaMetaData_.trackInfos.clear(); +} + +void DemuxerFilter::InitTypeFinder() +{ + if (!typeFinder_) { + typeFinder_ = std::make_shared(); + } +} + +bool DemuxerFilter::InitPlugin(std::string pluginName) +{ + if (pluginName.empty()) { + return false; + } + if (!(pluginName_ == pluginName)) { + if (plugin_) { + plugin_->Deinit(); + } + plugin_ = Plugin::PluginManager::Instance().CreateDemuxerPlugin(pluginName); + pluginAllocator_ = plugin_->GetAllocator(); + pluginName_.swap(pluginName); + } + if (plugin_->Init() != Plugin::Status::OK) { + MEDIA_LOG_E("InitPlugin for %s failed due to plugin not found.", pluginName.c_str()); + return false; + } + MEDIA_LOG_W("InitPlugin, %s used.", pluginName_.c_str()); + plugin_->SetDataSource(std::dynamic_pointer_cast(dataSource_)); + pluginState_ = DEMUXER_STATE_PARSE_HEADER; + plugin_->Prepare(); + return true; +} + +void DemuxerFilter::ActivatePullMode() +{ + MEDIA_LOG_D("ActivatePullMode called"); + InitTypeFinder(); + if (!task_) { + task_ = std::make_shared("DemuxerFilter"); + } + task_->RegisterHandler([this] { DemuxerLoop(); }); + checkRange_ = [this](uint64_t offset, uint32_t size) { + return (offset < mediaDataSize_) && (size <= mediaDataSize_) && (offset <= (mediaDataSize_ - size)); + }; + peekRange_ = [this](uint64_t offset, size_t size, AVBufferPtr& bufferPtr) -> bool { + return inPorts_.front()->PullData(offset, size, bufferPtr) == SUCCESS; + }; + getRange_ = peekRange_; + typeFinder_->Init(uriSuffix_, mediaDataSize_, checkRange_, peekRange_); + MediaTypeFound(typeFinder_->FindMediaType()); +} + +void DemuxerFilter::ActivatePushMode() +{ + MEDIA_LOG_D("ActivatePushMode called"); + InitTypeFinder(); + if (!dataPacker_) { + dataPacker_ = std::make_shared(); + } + checkRange_ = [this](uint64_t offset, uint32_t size) { return dataPacker_->IsDataAvailable(offset, size); }; + peekRange_ = [this](uint64_t offset, size_t size, AVBufferPtr& bufferPtr) -> bool { + return dataPacker_->PeekRange(offset, size, bufferPtr); + }; + getRange_ = [this](uint64_t offset, size_t size, AVBufferPtr& bufferPtr) -> bool { + return dataPacker_->GetRange(offset, size, bufferPtr); + }; + typeFinder_->Init(uriSuffix_, mediaDataSize_, checkRange_, peekRange_); + typeFinder_->FindMediaTypeAsync([this](std::string pluginName) { MediaTypeFound(std::move(pluginName)); }); +} + +void DemuxerFilter::MediaTypeFound(std::string pluginName) +{ + if (InitPlugin(std::move(pluginName))) { + task_->Start(); + } else { + OnEvent({EVENT_ERROR, PLUGIN_NOT_FOUND}); + } +} + +void DemuxerFilter::InitMediaMetaData(const Plugin::MediaInfoHelper& mediaInfo) +{ + mediaMetaData_.globalMeta = std::make_shared(mediaInfo.globalMeta); + mediaMetaData_.trackMetas.clear(); + int trackCnt = 0; + for (auto& trackMeta : mediaInfo.streamMeta) { + mediaMetaData_.trackMetas.push_back(std::make_shared(trackMeta)); + if (!trackMeta.Empty()) { + ++trackCnt; + } + } + mediaMetaData_.trackInfos.reserve(trackCnt); +} + +bool DemuxerFilter::IsOffsetValid(int64_t offset) const +{ + return mediaDataSize_ == 0 || offset <= static_cast(mediaDataSize_); +} + +bool DemuxerFilter::PrepareStreams(const Plugin::MediaInfoHelper& mediaInfo) +{ + MEDIA_LOG_D("PrepareStreams called"); + InitMediaMetaData(mediaInfo); + outPorts_.clear(); + int streamCnt = mediaInfo.streamMeta.size(); + PortInfo portInfo; + portInfo.type = PortType::OUT; + portInfo.ports.reserve(streamCnt); + int audioTrackCnt = 0; + for (int i = 0; i < streamCnt; ++i) { + if (mediaInfo.streamMeta[i].Empty()) { + MEDIA_LOG_E("PrepareStreams, unsupported stream with streamIdx = %d", i); + continue; + } + std::string mime; + uint32_t streamIdx = 0; + if (!mediaInfo.streamMeta[i].GetString(MetaID::MIME, mime) || + !mediaInfo.streamMeta[i].GetUint32(MetaID::STREAM_INDEX, streamIdx)) { + MEDIA_LOG_E("PrepareStreams failed to extract mime or streamIdx."); + continue; + } + if (IsAudioMime(mime)) { + MEDIA_LOG_D("PrepareStreams, audio stream with streamIdx = %u.", streamIdx); + if (audioTrackCnt == 1) { + MEDIA_LOG_E("PrepareStreams, discard audio stream: %d.", streamIdx); + continue; + } + ++audioTrackCnt; + } + auto port = std::make_shared(this, NamePort(mime)); + MEDIA_LOG_I("PrepareStreams, streamIdx: %d, portName: %s", i, port->GetName().c_str()); + outPorts_.push_back(port); + portInfo.ports.push_back({port->GetName(), IsRawAudio(mime)}); + mediaMetaData_.trackInfos.emplace_back(streamIdx, std::move(port), true); + } + if (portInfo.ports.empty()) { + MEDIA_LOG_E("PrepareStreams failed due to no valid port."); + return false; + } + ErrorCode ret = SUCCESS; + if (callback_) { + ret = callback_->OnCallback(FilterCallbackType::PORT_ADDED, static_cast(this), portInfo); + } + return ret == SUCCESS; +} + +int DemuxerFilter::ReadFrame(AVBuffer& buffer, uint32_t& streamIndex) +{ + MEDIA_LOG_D("ReadFrame called"); + ErrorCode result = UNKNOWN_ERROR; + auto rtv = plugin_->ReadFrame(buffer, 0); + if (rtv == Plugin::Status::OK) { + streamIndex = buffer.streamID; + curTimeUs_ = buffer.pts; + result = SUCCESS; + } + MEDIA_LOG_D("ReadFrame return with rtv = %d", static_cast(rtv)); + return (rtv != Plugin::Status::END_OF_STREAM) ? result : END_OF_STREAM; +} + +std::shared_ptr DemuxerFilter::GetStreamMeta(uint32_t streamIndex) +{ + return (streamIndex < mediaMetaData_.trackMetas.size()) ? mediaMetaData_.trackMetas[streamIndex] : nullptr; +} + +void DemuxerFilter::SendEventEos() +{ + MEDIA_LOG_D("SendEventEos called"); + AVBufferPtr bufferPtr = std::make_shared(); + bufferPtr->flag = BUFFER_FLAG_EOS; + for (const auto& stream : mediaMetaData_.trackInfos) { + stream.port->PushData(bufferPtr); + } +} + +void DemuxerFilter::HandleFrame(const AVBufferPtr& bufferPtr, uint32_t streamIndex) +{ + for (auto& stream : mediaMetaData_.trackInfos) { + if (stream.streamIdx != streamIndex) { + continue; + } + if (stream.needNegoCaps) { + CapabilitySet caps; + if (stream.port->Negotiate(GetStreamMeta(stream.streamIdx), caps)) { + stream.needNegoCaps = false; + } else { + task_->PauseAsync(); + OnEvent({EVENT_ERROR, PLUGIN_NOT_FOUND}); + } + } + stream.port->PushData(bufferPtr); + break; + } +} + +void DemuxerFilter::DemuxerLoop() +{ + if (pluginState_.load() == DEMUXER_STATE_PARSE_FRAME) { + AVBufferPtr bufferPtr = std::make_shared(); + uint32_t streamIndex = 0; + auto rtv = ReadFrame(*bufferPtr, streamIndex); + if (rtv == SUCCESS) { + HandleFrame(bufferPtr, streamIndex); + } else { + SendEventEos(); + task_->PauseAsync(); + if (rtv != END_OF_STREAM) { + MEDIA_LOG_E("ReadFrame failed with rtv = %d", rtv); + } + } + } else { + Plugin::MediaInfoHelper mediaInfo; + if (plugin_->GetMediaInfo(mediaInfo) == Plugin::Status::OK && PrepareStreams(mediaInfo)) { + pluginState_ = DEMUXER_STATE_PARSE_FRAME; + state_ = FilterState::READY; + OnEvent({EVENT_READY, {}}); + } else { + task_->PauseAsync(); + OnEvent({EVENT_ERROR, PARSE_META_FAILED}); + } + } +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/filters/demux/demuxer_filter.h b/engine/pipeline/filters/demux/demuxer_filter.h new file mode 100644 index 00000000..e6158528 --- /dev/null +++ b/engine/pipeline/filters/demux/demuxer_filter.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 MEDIA_PIPELINE_DEMUXER_FILTER_H +#define MEDIA_PIPELINE_DEMUXER_FILTER_H + +#include +#include +#include "common/plugin_types.h" +#include "core/filter_base.h" +#include "data_packer.h" +#include "foundation/meta.h" +#include "foundation/type_define.h" +#include "foundation/utils.h" +#include "plugin/core/demuxer.h" +#include "thread/task.h" +#include "type_finder.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class DemuxerFilter : public FilterBase { +public: + explicit DemuxerFilter(std::string name); + + ~DemuxerFilter() override; + + void Init(EventReceiver* receiver, FilterCallback* callback) override; + + ErrorCode Prepare() override; + + ErrorCode Start() override; + + ErrorCode Stop() override; + + ErrorCode Pause() override; + + void FlushStart() override; + + void FlushEnd() override; + + ErrorCode PushData(const std::string& inPort, AVBufferPtr buffer) override; + + bool Negotiate(const std::string& inPort, const std::shared_ptr& inMeta, + CapabilitySet& peerCaps) override; + + ErrorCode SeekTo(int64_t msec); + + std::vector> GetStreamMetaInfo() const; + + std::shared_ptr GetGlobalMetaInfo() const; + + ErrorCode GetCurrentTime(int64_t& time) const; + +private: + class DataSourceImpl; + + enum DemuxerState { DEMUXER_STATE_NULL, DEMUXER_STATE_PARSE_HEADER, DEMUXER_STATE_PARSE_FRAME }; + + struct StreamTrackInfo { + uint32_t streamIdx = 0; + std::shared_ptr port = nullptr; + bool needNegoCaps = false; + + StreamTrackInfo(uint32_t streamIdx, std::shared_ptr port, bool needNegoCaps) + : streamIdx(streamIdx), port(std::move(port)), needNegoCaps(needNegoCaps) + { + } + }; + + struct MediaMetaData { + std::vector trackInfos; + std::vector> trackMetas; + std::shared_ptr globalMeta; + }; + + void Reset(); + + void InitTypeFinder(); + + bool InitPlugin(std::string pluginName); + + void ActivatePullMode(); + + void ActivatePushMode(); + + void MediaTypeFound(std::string pluginName); + + void InitMediaMetaData(const Plugin::MediaInfoHelper& mediaInfo); + + bool IsOffsetValid(int64_t offset) const; + + bool PrepareStreams(const Plugin::MediaInfoHelper& mediaInfo); + + int ReadFrame(AVBuffer& buffer, uint32_t& streamIndex); + + std::shared_ptr GetStreamMeta(uint32_t streamIndex); + + void SendEventEos(); + + void HandleFrame(const AVBufferPtr& buffer, uint32_t streamIndex); + + void DemuxerLoop(); + + std::string uriSuffix_; + uint64_t mediaDataSize_; + std::shared_ptr task_; + std::shared_ptr typeFinder_; + std::shared_ptr dataPacker_; + + std::string pluginName_; + std::shared_ptr plugin_; + std::atomic pluginState_; + std::shared_ptr pluginAllocator_; + std::shared_ptr dataSource_; + MediaMetaData mediaMetaData_; + + std::function checkRange_; + std::function peekRange_; + std::function getRange_; + + std::atomic curTimeUs_; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS + +#endif // MEDIA_PIPELINE_DEMUXER_FILTER_H diff --git a/engine/pipeline/filters/demux/type_finder.cpp b/engine/pipeline/filters/demux/type_finder.cpp new file mode 100644 index 00000000..5db4aa91 --- /dev/null +++ b/engine/pipeline/filters/demux/type_finder.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "TypeFinder" + +#include "type_finder.h" +#include +#include "foundation/log.h" +#include "utils/util.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +namespace { +bool IsPluginSupportedExtension(Plugin::PluginInfo& pluginInfo, const std::string& extension) +{ + if (pluginInfo.pluginType != Plugin::PluginType::DEMUXER) { + return false; + } + bool rtv = false; + auto info = pluginInfo.extra[PLUGIN_INFO_EXTRA_EXTENSIONS]; + if (info.HasValue() && info.Type() == typeid(std::vector)) { + for (const auto& ext : Plugin::AnyCast&>(info)) { + if (ext == extension) { + rtv = true; + break; + } + } + } + return rtv; +} + +void ToLower(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), [](unsigned char ch) { return std::tolower(ch); }); +} + +// lowercase suffix +std::vector g_findTypeNeededForSameSuffix = {"aac"}; +} // namespace + +TypeFinder::TypeFinder() + : sniffNeeded_(true), + uriSuffix_(), + mediaDataSize_(0), + pluginName_(), + plugins_(), + pluginRegistryChanged_(true), + task_(nullptr), + checkRange_(), + peekRange_(), + typeFound_() +{ + MEDIA_LOG_I("TypeFinder ctor called..."); +} + +TypeFinder::~TypeFinder() +{ + MEDIA_LOG_I("TypeFinder dtor called..."); + if (task_) { + task_->Stop(); + } +} + +bool TypeFinder::IsSniffNeeded(std::string suffix) +{ + ToLower(suffix); + bool suffixChanged = uriSuffix_ != suffix; + if (suffixChanged || pluginRegistryChanged_) { + return true; + } + return std::any_of(g_findTypeNeededForSameSuffix.begin(), g_findTypeNeededForSameSuffix.end(), + [&suffix](const std::string& uriSuffix) { return suffix == uriSuffix; }); +} + +void TypeFinder::Init(std::string uriSuffix, size_t mediaDataSize, std::function checkRange, + std::function peekRange) +{ + mediaDataSize_ = mediaDataSize; + checkRange_ = std::move(checkRange); + peekRange_ = std::move(peekRange); + sniffNeeded_ = IsSniffNeeded(uriSuffix); + if (sniffNeeded_) { + uriSuffix_.swap(uriSuffix); + pluginName_.clear(); + if (GetPlugins()) { + SortPlugins(uriSuffix_); + } else { + MEDIA_LOG_E("TypeFinder Init failed due to no demuxer plugins..."); + } + } +} + +/** + * FindMediaType for seekable source, is a sync interface. + * @return plugin names for the found media type. + */ +std::string TypeFinder::FindMediaType() +{ + if (sniffNeeded_) { + pluginName_ = SniffMediaType(); + if (pluginName_.empty()) { + pluginName_ = GuessMediaType(); + } + sniffNeeded_ = false; + } + return pluginName_; +} + +/** + * FindMediaTypeAsync for non-seekable source + * @param typeFound is a callback called when media type found. + */ +void TypeFinder::FindMediaTypeAsync(std::function typeFound) +{ + typeFound_ = std::move(typeFound); + task_ = std::make_shared("TypeFinder"); + task_->RegisterHandler([this]() { DoTask(); }); + task_->Start(); +} + +Plugin::Status TypeFinder::ReadAt(int64_t offset, std::shared_ptr& buffer, size_t expectedLen) +{ + if (!buffer || expectedLen == 0 || !IsOffsetValid(offset)) { + MEDIA_LOG_E("ReadAt failed, buffer empty: %d, expectedLen: %zu, offset: %ld", !buffer, expectedLen, offset); + return Plugin::Status::ERROR_INVALID_PARAMETER; + } + const int maxTryTimes = 3; + int i = 0; + while (!checkRange_(offset, expectedLen) && (i++ < maxTryTimes)) { + OSAL::SleepFor(5); // 5 ms + } + if (i == maxTryTimes) { + MEDIA_LOG_E("ReadAt exceed maximum allowed try times and failed."); + return Plugin::Status::ERROR_NOT_ENOUGH_DATA; + } + ASSERT_CONDITION(peekRange_(static_cast(offset), expectedLen, buffer), "peekRange failed."); + return Plugin::Status::OK; +} + +Plugin::Status TypeFinder::GetSize(size_t& size) +{ + size = mediaDataSize_; + return (mediaDataSize_ > 0) ? Plugin::Status::OK : Plugin::Status::ERROR_UNKNOWN; +} + +void TypeFinder::DoTask() +{ + if (sniffNeeded_) { + pluginName_ = SniffMediaType(); + if (pluginName_.empty()) { + pluginName_ = GuessMediaType(); + } + sniffNeeded_ = false; + } + task_->Pause(); + typeFound_(pluginName_); +} + +std::string TypeFinder::SniffMediaType() +{ + constexpr int probThresh = 50; // valid range [0, 100] + std::string pluginName; + int maxProb = 0; + auto dataSource = shared_from_this(); + for (const auto& plugin : plugins_) { + auto prob = Plugin::PluginManager::Instance().Sniffer(plugin->name, dataSource); + if (prob > probThresh) { + pluginName = plugin->name; + break; + } + if (prob > maxProb) { + maxProb = prob; + pluginName = plugin->name; + } + } + return pluginName; +} + +std::string TypeFinder::GuessMediaType() const +{ + std::string pluginName; + for (const auto& pluginInfo : plugins_) { + if (IsPluginSupportedExtension(*pluginInfo, uriSuffix_)) { + pluginName = pluginInfo->name; + break; + } + } + return pluginName; +} + +bool TypeFinder::IsOffsetValid(int64_t offset) const +{ + return (mediaDataSize_ == 0) || offset < mediaDataSize_; +} + +bool TypeFinder::GetPlugins() +{ + MEDIA_LOG_I("TypeFinder::GetPlugins : %d, empty: %d", (pluginRegistryChanged_ == true), plugins_.empty()); + if (pluginRegistryChanged_) { + pluginRegistryChanged_ = false; + auto pluginNames = Plugin::PluginManager::Instance().ListPlugins(Plugin::PluginType::DEMUXER); + for (auto& pluginName : pluginNames) { + auto pluginInfo = Plugin::PluginManager::Instance().GetPluginInfo(Plugin::PluginType::DEMUXER, pluginName); + if (!pluginInfo) { + MEDIA_LOG_E("GetPlugins failed for plugin: %s", pluginName.c_str()); + continue; + } + plugins_.emplace_back(std::move(pluginInfo)); + } + } + return !plugins_.empty(); +} + +void TypeFinder::SortPlugins(const std::string& suffix) +{ + if (suffix.empty()) { + return; + } + std::stable_sort( + plugins_.begin(), plugins_.end(), + [&suffix](const std::shared_ptr& lhs, const std::shared_ptr& rhs) { + if (IsPluginSupportedExtension(*lhs, suffix)) { + return (lhs->rank >= rhs->rank) || !IsPluginSupportedExtension(*rhs, suffix); + } else { + return (lhs->rank >= rhs->rank) && !IsPluginSupportedExtension(*rhs, suffix); + } + }); +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/filters/demux/type_finder.h b/engine/pipeline/filters/demux/type_finder.h new file mode 100644 index 00000000..1dab9c65 --- /dev/null +++ b/engine/pipeline/filters/demux/type_finder.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_TYPE_FINDER_H +#define HISTREAMER_TYPE_FINDER_H + +#include +#include +#include +#include +#include "core/plugin_manager.h" +#include "foundation/type_define.h" +#include "interface/plugin_base.h" +#include "thread/task.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class TypeFinder : public std::enable_shared_from_this, public Plugin::DataSourceHelper { +public: + TypeFinder(); + + ~TypeFinder() override; + + void Init(std::string uriSuffix, size_t mediaDataSize, std::function checkRange, + std::function peekRange); + + std::string FindMediaType(); + + void FindMediaTypeAsync(std::function typeFound); + + Plugin::Status ReadAt(int64_t offset, std::shared_ptr& buffer, size_t expectedLen) override; + + Plugin::Status GetSize(size_t& size) override; + +private: + void DoTask(); + + std::string SniffMediaType(); + + std::string GuessMediaType() const; + + bool IsOffsetValid(int64_t offset) const; + + bool IsSniffNeeded(std::string suffix); + + bool GetPlugins(); + + void SortPlugins(const std::string& uriSuffix); + + bool sniffNeeded_; + std::string uriSuffix_; + size_t mediaDataSize_; + std::string pluginName_; + std::vector> plugins_; + std::atomic pluginRegistryChanged_; + std::shared_ptr task_; + std::function checkRange_; + std::function peekRange_; + std::function typeFound_; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_TYPE_FINDER_H diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp new file mode 100644 index 00000000..602feb41 --- /dev/null +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "AudioSinkFilter" + +#include "audio_sink_filter.h" +#include "common/plugin_utils.h" +#include "factory/filter_factory.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +static AutoRegisterFilter g_registerFilterHelper("builtin.player.audiosink"); + +AudioSinkFilter::AudioSinkFilter(const std::string& name) : FilterBase(name), curPos_(0) +{ + MEDIA_LOG_I("AudioSinkFilter ctor"); +} +AudioSinkFilter::~AudioSinkFilter() +{ + MEDIA_LOG_I("AudioSinkFilter deCtor."); + if (plugin_) { + plugin_->Stop(); + plugin_->Deinit(); + } +} + +void AudioSinkFilter::Init(EventReceiver* receiver, FilterCallback* callback) +{ + FilterBase::Init(receiver, callback); + outPorts_.clear(); +} + +ErrorCode AudioSinkFilter::SetPluginParameter(Tag tag, const Plugin::ValueType& value) +{ + return TranslatePluginStatus(plugin_->SetParameter(tag, value)); +} + +ErrorCode AudioSinkFilter::SetParameter(int32_t key, const Plugin::Any& value) +{ + if (state_.load() == FilterState::CREATED) { + return ERROR_STATE; + } + Tag tag = Tag::INVALID; + if (!TranslateIntoParameter(key, tag)) { + MEDIA_LOG_I("SetParameter key %d is out of boundary", key); + return INVALID_PARAM_VALUE; + } + RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin_); + return SetPluginParameter(tag, value); +} + +template +ErrorCode AudioSinkFilter::GetPluginParameter(Tag tag, T& value) +{ + Plugin::Any tmp; + auto err = TranslatePluginStatus(plugin_->GetParameter(tag, tmp)); + if (err == SUCCESS && tmp.Type() == typeid(T)) { + value = Plugin::AnyCast(tmp); + } + return err; +} + +ErrorCode AudioSinkFilter::GetParameter(int32_t key, Plugin::Any& value) +{ + if (state_.load() == FilterState::CREATED) { + return ERROR_STATE; + } + Tag tag = Tag::INVALID; + if (!TranslateIntoParameter(key, tag)) { + MEDIA_LOG_I("GetParameter key %d is out of boundary", key); + return INVALID_PARAM_VALUE; + } + RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin_); + return TranslatePluginStatus(plugin_->GetParameter(tag, value)); +} + +bool AudioSinkFilter::Negotiate(const std::string& inPort, const std::shared_ptr& inMeta, + CapabilitySet& outCaps) +{ + MEDIA_LOG_D("sink negotiate started"); + if (state_ != FilterState::PREPARING) { + MEDIA_LOG_W("sink filter is not in preparing when negotiate"); + return false; + } + + auto creator = [](const std::string& pluginName) { + return Plugin::PluginManager::Instance().CreateAudioSinkPlugin(pluginName); + }; + auto err = FindPluginAndUpdate(inMeta, Plugin::PluginType::AUDIO_SINK, plugin_, + targetPluginInfo_, creator); + RETURN_TARGET_ERR_MESSAGE_LOG_IF_FAIL(err, false, "cannot find matched plugin"); + + outCaps = targetPluginInfo_->inCaps; + + err = ConfigureToPreparePlugin(inMeta); + if (err != SUCCESS) { + MEDIA_LOG_E("sink configure error"); + OnEvent({EVENT_ERROR, err}); + return false; + } + return true; +} + +ErrorCode AudioSinkFilter::GetCurrentTime(int64_t& time) const +{ + time = curPos_; + return SUCCESS; +} +ErrorCode AudioSinkFilter::ConfigureWithMeta(const std::shared_ptr& meta) +{ + uint32_t channels; + if (meta->GetUint32(MetaID::AUDIO_CHANNELS, channels)) { + MEDIA_LOG_D("found audio channel meta"); + SetPluginParameter(Tag::AUDIO_CHANNELS, channels); + } + uint32_t sampleRate; + if (meta->GetUint32(MetaID::AUDIO_SAMPLE_RATE, sampleRate)) { + MEDIA_LOG_D("found audio sample rate meta"); + SetPluginParameter(Tag::AUDIO_SAMPLE_RATE, sampleRate); + } + int64_t bitRate; + if (meta->GetInt64(MetaID::MEDIA_BITRATE, bitRate)) { + MEDIA_LOG_D("found audio bit rate meta"); + SetPluginParameter(Tag::MEDIA_BITRATE, bitRate); + } + + auto audioFormat = Plugin::AudioSampleFormat::U8; + if (meta->GetData(MetaID::AUDIO_SAMPLE_FORMAT, audioFormat)) { + SetPluginParameter(Tag::AUDIO_SAMPLE_FORMAT, audioFormat); + } + + auto audioChannelLayout = Plugin::AudioChannelLayout::STEREO; + if (meta->GetData(MetaID::AUDIO_CHANNEL_LAYOUT, audioChannelLayout)) { + SetPluginParameter(Tag::AUDIO_CHANNEL_LAYOUT, audioChannelLayout); + } + + uint32_t samplePerFrame = 0; + if (meta->GetUint32(MetaID::AUDIO_SAMPLE_PRE_FRAME, samplePerFrame)) { + SetPluginParameter(Tag::AUDIO_SAMPLE_PRE_FRAME, samplePerFrame); + } + return SUCCESS; +} +ErrorCode AudioSinkFilter::ConfigureToPreparePlugin(const std::shared_ptr& meta) +{ + auto err = TranslatePluginStatus(plugin_->Init()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "sink plugin init error."); + err = ConfigureWithMeta(meta); + if (err != SUCCESS) { + MEDIA_LOG_E("sink configuration failed "); + return err; + } + err = TranslatePluginStatus(plugin_->Prepare()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "sink prepare failed"); + + return SUCCESS; +} + +ErrorCode AudioSinkFilter::PushData(const std::string& inPort, AVBufferPtr buffer) +{ + MEDIA_LOG_D("audio sink push data started, state: %d", state_.load()); + if (state_ == FilterState::PREPARING) { + state_ = FilterState::READY; + OnEvent({EVENT_READY}); + } + + if (isFlushing || state_.load() == FilterState::INITIALIZED) { + MEDIA_LOG_I("audio sink is flushing ignore this buffer"); + return SUCCESS; + } + if (state_.load() != FilterState::RUNNING) { + pushThreadIsBlocking = true; + OSAL::ScopedLock lock(mutex_); + startWorkingCondition_.Wait(lock, [this] { + return state_ == FilterState::RUNNING || state_ == FilterState::INITIALIZED || isFlushing; + }); + pushThreadIsBlocking = false; + } + if (isFlushing || state_.load() == FilterState::INITIALIZED) { + MEDIA_LOG_I("PushData return due to: isFlushing = %d, state = %d", isFlushing, static_cast(state_.load())); + return SUCCESS; + } + + if (buffer->GetMemory()->GetSize() == 0) { + Event event { + .type = EVENT_COMPLETE, + }; + MEDIA_LOG_D("audio sink push data send event_complete"); + OnEvent(event); + MEDIA_LOG_D("audio sink push data end"); + return SUCCESS; + } + curPos_ = buffer->pts; + auto err = TranslatePluginStatus(plugin_->Write(buffer)); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "audio sink write failed"); + MEDIA_LOG_D("audio sink push data end"); + return err; +} + +ErrorCode AudioSinkFilter::Start() +{ + MEDIA_LOG_D("start called"); + if (state_ != FilterState::READY && state_ != FilterState::PAUSED) { + MEDIA_LOG_W("sink is not ready when start, state: %d", state_.load()); + return ERROR_STATE; + } + auto err = FilterBase::Start(); + if (err != SUCCESS) { + MEDIA_LOG_E("audio sink filter start error"); + return err; + } + err = TranslatePluginStatus(plugin_->Start()); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "audio sink plugin start failed"); + if (pushThreadIsBlocking.load()) { + startWorkingCondition_.NotifyOne(); + } + return SUCCESS; +} + +ErrorCode AudioSinkFilter::Stop() +{ + MEDIA_LOG_I("AudioSinkFilter stop called."); + FilterBase::Stop(); + plugin_->Stop(); + if (pushThreadIsBlocking.load()) { + startWorkingCondition_.NotifyOne(); + } + MEDIA_LOG_I("AudioSinkFilter stop finished."); + return SUCCESS; +} + +ErrorCode AudioSinkFilter::Pause() +{ + MEDIA_LOG_D("audio sink filter pause start"); + // only worked when state is working + if (state_ != FilterState::READY && state_ != FilterState::RUNNING) { + MEDIA_LOG_W("audio sink cannot pause when not working"); + return ERROR_STATE; + } + auto err = FilterBase::Pause(); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "audio sink pause failed"); + err = TranslatePluginStatus(plugin_->Pause()); + MEDIA_LOG_D("audio sink filter pause end"); + return err; +} +ErrorCode AudioSinkFilter::Resume() +{ + MEDIA_LOG_D("audio sink filter resume"); + // only worked when state is paused + if (state_ == FilterState::PAUSED) { + state_ = FilterState::RUNNING; + if (pushThreadIsBlocking) { + startWorkingCondition_.NotifyOne(); + } + return TranslatePluginStatus(plugin_->Resume()); + } + return SUCCESS; +} + +void AudioSinkFilter::FlushStart() +{ + MEDIA_LOG_D("FlushStart entered"); + isFlushing = true; + if (pushThreadIsBlocking) { + startWorkingCondition_.NotifyOne(); + } + plugin_->Flush(); +} + +void AudioSinkFilter::FlushEnd() +{ + MEDIA_LOG_D("FlushEnd entered"); + isFlushing = false; +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h new file mode 100644 index 00000000..1cf8142f --- /dev/null +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 MEDIA_PIPELINE_AUDIO_SINK_FILTER_H +#define MEDIA_PIPELINE_AUDIO_SINK_FILTER_H + +#include + +#include "foundation/error_code.h" +#include "foundation/utils.h" +#include "osal/thread/condition_variable.h" +#include "osal/thread/mutex.h" +#include "pipeline/core/filter_base.h" +#include "plugin/core/audio_sink.h" +#include "plugin/core/plugin_info.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class AudioSinkFilter : public FilterBase { +public: + explicit AudioSinkFilter(const std::string& name); + ~AudioSinkFilter() override; + + void Init(EventReceiver*, FilterCallback* callback) override; + + ErrorCode SetParameter(int32_t key, const Plugin::Any& value) override; + + ErrorCode GetParameter(int32_t key, Plugin::Any& value) override; + + bool Negotiate(const std::string& inPort, const std::shared_ptr& inMeta, + CapabilitySet& outCaps) override; + + ErrorCode PushData(const std::string& inPort, AVBufferPtr buffer) override; + + ErrorCode Start() override; + ErrorCode Stop() override; + + ErrorCode Pause() override; + ErrorCode Resume() override; + + void FlushStart() override; + void FlushEnd() override; + + ErrorCode GetCurrentTime(int64_t& time) const; + +private: + ErrorCode SetPluginParameter(Tag tag, const Plugin::ValueType& value); + ErrorCode ConfigureToPreparePlugin(const std::shared_ptr& meta); + ErrorCode ConfigureWithMeta(const std::shared_ptr& meta); + template + ErrorCode GetPluginParameter(Tag tag, T& value); + + std::atomic pushThreadIsBlocking {false}; + bool isFlushing {false}; + OSAL::ConditionVariable startWorkingCondition_ {}; + OSAL::Mutex mutex_ {}; + std::atomic curPos_; + + std::shared_ptr plugin_ {nullptr}; + std::shared_ptr targetPluginInfo_ {}; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS +#endif \ No newline at end of file diff --git a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp new file mode 100644 index 00000000..9901eada --- /dev/null +++ b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#define LOG_TAG "VideoSinkFilter" + +#include "video_sink_filter.h" + +#include "factory/filter_factory.h" +#include "common/plugin_utils.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +static AutoRegisterFilter g_registerFilterHelper("builtin.player.videosink"); + +const uint32_t VSINK_DEFAULT_BUFFER_NUM = 8; + +VideoSinkFilter::VideoSinkFilter(const std::string &name) : FilterBase(name), curPos_(0) +{ + MEDIA_LOG_I("VideoSinkFilter ctor called..."); +} + +VideoSinkFilter::~VideoSinkFilter() +{ + MEDIA_LOG_D("VideoSinkFilter deCtor."); + if (plugin_) { + plugin_->Stop(); + plugin_->Deinit(); + } + if (inBufQueue_ != nullptr) { + inBufQueue_->SetActive(false); + inBufQueue_.reset(); + } +} + +void VideoSinkFilter::Init(EventReceiver* receiver, FilterCallback* callback) +{ + FilterBase::Init(receiver, callback); + outPorts_.clear(); + if (inBufQueue_ == nullptr) { + inBufQueue_ = std::make_shared>("VideoSinkInBufQue", VSINK_DEFAULT_BUFFER_NUM); + } + if (renderTask_ == nullptr) { + renderTask_ = std::make_shared("VideoSinkRenderThread"); + renderTask_->RegisterHandler([this] { RenderFrame(); }); + } +} + +ErrorCode VideoSinkFilter::SetParameter(int32_t key, const Plugin::Any& value) +{ + if (state_.load() == FilterState::CREATED) { + return ERROR_STATE; + } + Tag tag = Tag::INVALID; + if (!TranslateIntoParameter(key, tag)) { + MEDIA_LOG_I("SetParameter key %d is out of boundary", key); + return INVALID_PARAM_VALUE; + } + RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin_); + return TranslatePluginStatus(plugin_->SetParameter(tag, value)); +} + +ErrorCode VideoSinkFilter::GetParameter(int32_t key, Plugin::Any& value) +{ + if (state_.load() == FilterState::CREATED) { + return ERROR_STATE; + } + Tag tag = Tag::INVALID; + if (!TranslateIntoParameter(key, tag)) { + MEDIA_LOG_I("GetParameter key %d is out of boundary", key); + return INVALID_PARAM_VALUE; + } + RETURN_PLUGIN_NOT_FOUND_IF_NULL(plugin_); + return TranslatePluginStatus(plugin_->GetParameter(tag, value)); +} + +bool VideoSinkFilter::Negotiate(const std::string &inPort, const std::shared_ptr &inMeta, CapabilitySet &outCaps) +{ + MEDIA_LOG_D("video sink negotiate started"); + if (state_ != FilterState::PREPARING) { + MEDIA_LOG_W("Video sink filter is not in preparing when negotiate"); + return false; + } + auto creator = [](const std::string& pluginName) { + return Plugin::PluginManager::Instance().CreateVideoSinkPlugin(pluginName); + }; + auto err = FindPluginAndUpdate(inMeta, Plugin::PluginType::VIDEO_SINK, plugin_, + pluginInfo_, creator); + RETURN_TARGET_ERR_MESSAGE_LOG_IF_FAIL (err, false, "cannot find matched video sink plugin"); + outCaps = pluginInfo_->inCaps; + err = ConfigureLocked(inMeta); + if (err != ErrorCode::SUCCESS) { + MEDIA_LOG_E("sink configure error"); + Event event{ + .type = EventType::EVENT_ERROR, + .param = err, + }; + OnEvent(event); + return false; + } + return true; +} + +ErrorCode VideoSinkFilter::GetCurrentTime(uint64_t &time) const +{ + time = curPos_; + return ErrorCode::SUCCESS; +} + +ErrorCode VideoSinkFilter::ConfigurePluginParams(const std::shared_ptr &meta) +{ + auto err = ErrorCode::SUCCESS; + uint32_t width; + if (meta->GetUint32(MetaID::VIDEO_WIDTH, width)) { + err = TranslatePluginStatus(plugin_->SetParameter(Tag::VIDEO_WIDTH, width)); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "Set plugin width fail"); + } + uint32_t height; + if (meta->GetUint32(MetaID::VIDEO_HEIGHT, height)) { + err = TranslatePluginStatus(plugin_->SetParameter(Tag::VIDEO_HEIGHT, height)); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "Set plugin height fail"); + } + uint32_t pixelFormat; + if (meta->GetUint32(MetaID::VIDEO_PIXEL_FORMAT, pixelFormat)) { + err = TranslatePluginStatus(plugin_->SetParameter(Tag::VIDEO_PIXEL_FORMAT, pixelFormat)); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "Set plugin pixel format fail"); + } + MEDIA_LOG_D("width: %u, height: %u, pixelFormat: %u", width, height, pixelFormat); + return err; +} + +ErrorCode VideoSinkFilter::ConfigureLocked(const std::shared_ptr &meta) +{ + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Init()), "Init plugin error"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(ConfigurePluginParams(meta), "Configure plugin params fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Prepare()), "Prepare plugin fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Start()), "Start plugin fail"); + return ErrorCode::SUCCESS; +} + +bool VideoSinkFilter::DoSync() +{ + // Add av sync handle here + return true; +} + +void VideoSinkFilter::RenderFrame() +{ + MEDIA_LOG_D("RenderFrame called"); + auto oneBuffer = inBufQueue_->Pop(); + if (oneBuffer == nullptr) { + MEDIA_LOG_W("Video sink find nullptr in esBufferQ"); + return; + } + if (DoSync() == false) { + return; + } + auto err = plugin_->Write(oneBuffer); + if (err != Plugin::Status::OK) { + MEDIA_LOG_E("Video sink write failed"); + return; + } +} + +ErrorCode VideoSinkFilter::PushData(const std::string &inPort, AVBufferPtr buffer) +{ + MEDIA_LOG_D("video sink push data started, state_: %d", state_.load()); + if (state_ == FilterState::PREPARING) { + state_ = FilterState::READY; + Event event { + .type = EVENT_READY, + }; + OnEvent(event); + } + + if (isFlushing_ || state_.load() == FilterState::INITIALIZED) { + MEDIA_LOG_I("video sink is flushing ignore this buffer"); + return ErrorCode::SUCCESS; + } + if (state_.load() != FilterState::RUNNING) { + pushThreadIsBlocking_ = true; + OSAL::ScopedLock lock(mutex_); + startWorkingCondition_.Wait(lock, [this] { + return state_ == FilterState::RUNNING || state_ == FilterState::INITIALIZED || isFlushing_; + }); + pushThreadIsBlocking_ = false; + } + if (isFlushing_ || state_.load() == FilterState::INITIALIZED) { + MEDIA_LOG_I("PushData return due to: isFlushing_ = %d, state_ = %d", + isFlushing_, static_cast(state_.load())); + return ErrorCode::SUCCESS; + } + + if (buffer->GetMemory()->GetSize() == 0) { + Event event{ + .type = EVENT_COMPLETE, + }; + MEDIA_LOG_D("video sink push data send event_complete"); + OnEvent(event); + MEDIA_LOG_D("video sink push data end"); + return ErrorCode::SUCCESS; + } + curPos_ = buffer->pts; + inBufQueue_->Push(buffer); + MEDIA_LOG_D("video sink push data end"); + return ErrorCode::SUCCESS; +} + +ErrorCode VideoSinkFilter::Start() +{ + MEDIA_LOG_D("start called"); + if (state_ != FilterState::READY && state_ != FilterState::PAUSED) { + MEDIA_LOG_W("sink is not ready when start, state_: %d", state_.load()); + return ERROR_STATE; + } + inBufQueue_->SetActive(true); + renderTask_->Start(); + auto err = FilterBase::Start(); + if (err != ErrorCode::SUCCESS) { + MEDIA_LOG_E("Video sink filter start error"); + return err; + } + plugin_->Start(); + if (pushThreadIsBlocking_.load()) { + startWorkingCondition_.NotifyOne(); + } + return ErrorCode::SUCCESS; +} + +ErrorCode VideoSinkFilter::Stop() +{ + MEDIA_LOG_I("VideoSinkFilter stop called."); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(FilterBase::Stop(), "Video sink stop fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Stop()), "Stop plugin fail"); + if (pushThreadIsBlocking_.load()) { + startWorkingCondition_.NotifyOne(); + } + inBufQueue_->SetActive(false); + renderTask_->Pause(); + return ErrorCode::SUCCESS; +} + +ErrorCode VideoSinkFilter::Pause() +{ + MEDIA_LOG_D("Video sink filter pause start"); + if (state_ != FilterState::READY && state_ != FilterState::RUNNING) { + MEDIA_LOG_W("video sink cannot pause when not working"); + return ERROR_STATE; + } + RETURN_ERR_MESSAGE_LOG_IF_FAIL(FilterBase::Pause(), "Video sink pause fail"); + RETURN_ERR_MESSAGE_LOG_IF_FAIL(TranslatePluginStatus(plugin_->Pause()), "Pause plugin fail"); + inBufQueue_->SetActive(false); + renderTask_->Pause(); + MEDIA_LOG_D("Video sink filter pause end"); + return ErrorCode::SUCCESS; +} + +ErrorCode VideoSinkFilter::Resume() +{ + MEDIA_LOG_D("Video sink filter resume"); + // only worked when state_ is paused + if (state_ == FilterState::PAUSED) { + state_ = FilterState::RUNNING; + if (pushThreadIsBlocking_) { + startWorkingCondition_.NotifyOne(); + } + auto err = TranslatePluginStatus(plugin_->Resume()); + if (err != ErrorCode::SUCCESS) { + return err; + } + inBufQueue_->SetActive(true); + renderTask_->Start(); + } + return ErrorCode::SUCCESS; +} + +void VideoSinkFilter::FlushStart() +{ + MEDIA_LOG_D("FlushStart entered"); + isFlushing_ = true; + if (pushThreadIsBlocking_) { + startWorkingCondition_.NotifyOne(); + } + if (inBufQueue_) { + inBufQueue_->SetActive(false); + } + auto err = TranslatePluginStatus(plugin_->Flush()); + if (err != ErrorCode::SUCCESS) { + MEDIA_LOG_W("Video sink filter flush end"); + } +} + +void VideoSinkFilter::FlushEnd() +{ + MEDIA_LOG_D("FlushEnd entered"); + isFlushing_ = false; + if (inBufQueue_) { + inBufQueue_->SetActive(true); + } +} +} +} +} +#endif \ No newline at end of file diff --git a/engine/pipeline/filters/sink/video_sink/video_sink_filter.h b/engine/pipeline/filters/sink/video_sink/video_sink_filter.h new file mode 100644 index 00000000..6a00308c --- /dev/null +++ b/engine/pipeline/filters/sink/video_sink/video_sink_filter.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#ifndef MEDIA_PIPELINE_VIDEO_SINK_FILTER_H +#define MEDIA_PIPELINE_VIDEO_SINK_FILTER_H + +#include +#include "osal/thread/task.h" +#include "foundation/utils.h" +#include "foundation/error_code.h" +#include "pipeline/core/filter_base.h" +#include "foundation/blocking_queue.h" +#include "osal/thread/mutex.h" +#include "osal/thread/condition_variable.h" +#include "plugin/core/video_sink.h" +#include "plugin/core/plugin_info.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +class VideoSinkAdapter; +class VideoSinkFilter : public FilterBase { +public: + explicit VideoSinkFilter(const std::string &name); + ~VideoSinkFilter() override; + + void Init(EventReceiver*, FilterCallback* callback) override; + + ErrorCode SetParameter(int32_t key, const Plugin::Any& value) override; + + ErrorCode GetParameter(int32_t key, Plugin::Any& value) override; + + bool Negotiate(const std::string &inPort, const std::shared_ptr &inMeta, + CapabilitySet &outCaps) override; + + ErrorCode PushData(const std::string &inPort, AVBufferPtr buffer) override; + + ErrorCode Start() override; + ErrorCode Stop() override; + + ErrorCode Pause() override; + ErrorCode Resume() override; + + void FlushStart() override; + void FlushEnd() override; + + ErrorCode GetCurrentTime(uint64_t &time) const; + +private: + ErrorCode ConfigurePluginParams(const std::shared_ptr &meta); + ErrorCode ConfigureLocked(const std::shared_ptr &meta); + void RenderFrame(); + bool DoSync(); + + std::shared_ptr> inBufQueue_; + std::shared_ptr renderTask_; + std::shared_ptr adapter_; + std::atomic pushThreadIsBlocking_ {false}; + bool isFlushing_ {false}; + OSAL::ConditionVariable startWorkingCondition_ {}; + OSAL::Mutex mutex_; + std::atomic curPos_; + + std::shared_ptr plugin_ {nullptr}; + std::shared_ptr pluginInfo_ {nullptr}; +}; +} +} +} + +#endif // MEDIA_PIPELINE_VIDEO_SINK_FILTER_H + +#endif \ No newline at end of file diff --git a/engine/pipeline/filters/source/media_source_filter.cpp b/engine/pipeline/filters/source/media_source_filter.cpp new file mode 100644 index 00000000..9c227c45 --- /dev/null +++ b/engine/pipeline/filters/source/media_source_filter.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "MediaSourceFilter" + +#include "media_source_filter.h" +#include "factory/filter_factory.h" +#include "foundation/meta.h" +#include "foundation/type_define.h" +#include "plugin/interface/source_plugin.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +using namespace Plugin; + +namespace { +AutoRegisterFilter g_registerFilterHelper("builtin.player.mediasource"); + +ErrorCode TranslateError(Status err) +{ + ErrorCode ret = UNKNOWN_ERROR; + switch (err) { + case Status::OK: + ret = SUCCESS; + break; + case Status::END_OF_STREAM: + ret = END_OF_STREAM; + default: + break; + } + return ret; +} +} // namespace + +MediaSourceFilter::MediaSourceFilter(const std::string& name) + : FilterBase(name), + taskPtr_(nullptr), + protocol_(), + uri_(), + isSeekable_(false), + position_(0), + bufferSize_(DEFAULT_FRAME_SIZE), + plugin_(nullptr), + pluginAllocator_(nullptr), + pluginInfo_(nullptr) +{ + MEDIA_LOG_D("ctor called"); +} + +MediaSourceFilter::~MediaSourceFilter() +{ + MEDIA_LOG_D("dtor called"); + if (taskPtr_) { + taskPtr_->Stop(); + } + if (plugin_) { + plugin_->Deinit(); + } +} + +ErrorCode MediaSourceFilter::SetSource(std::shared_ptr source) +{ + MEDIA_LOG_D("IN"); + if (source == nullptr) { + MEDIA_LOG_E("Invalid source"); + return ErrorCode::INVALID_SOURCE; + } + ErrorCode err = FindPlugin(source); + if (err != ErrorCode::SUCCESS) { + return err; + } + err = InitPlugin(source); + if (err != ErrorCode::SUCCESS) { + return err; + } + ActivateMode(); + return DoNegotiate(source); +} + +ErrorCode MediaSourceFilter::InitPlugin(std::shared_ptr source) +{ + MEDIA_LOG_D("IN"); + ErrorCode err = TranslateError(plugin_->Init()); + if (err != ErrorCode::SUCCESS) { + return err; + } + pluginAllocator_ = plugin_->GetAllocator(); + SourceType srcType = source->GetSourceType(); + MEDIA_LOG_D("sourceType = %d", OHOS::Media::to_underlying(srcType)); + if (srcType == SourceType::SOURCE_TYPE_STREAM) { + auto params = std::make_shared>(); + std::string key = "StreamSource"; + ValueType value = source; + params->insert(std::pair(key, value)); + } else { + // network protocl need to add params + err = TranslateError(plugin_->SetSource(uri_, nullptr)); + } + return err; +} + +ErrorCode MediaSourceFilter::SetBufferSize(size_t size) +{ + MEDIA_LOG_D("IN, size: %zu", size); + bufferSize_ = size; + return ErrorCode::SUCCESS; +} + +bool MediaSourceFilter::IsSeekable() const +{ + MEDIA_LOG_D("IN, isSeekable_: %d", static_cast(isSeekable_)); + return isSeekable_; +} + +std::vector MediaSourceFilter::GetWorkModes() +{ + MEDIA_LOG_D("IN, isSeekable_: %d", static_cast(isSeekable_)); + if (isSeekable_) { + return {WorkMode::PUSH, WorkMode::PULL}; + } else { + return {WorkMode::PUSH}; + } +} + +ErrorCode MediaSourceFilter::Prepare() +{ + MEDIA_LOG_D("IN"); + if (plugin_ == nullptr) { + return ErrorCode::PLUGIN_NOT_FOUND; + } + auto err = TranslateError(plugin_->Prepare()); + if (err == ErrorCode::SUCCESS) { + MEDIA_LOG_D("Send READY event to player"); + OnEvent(Event{EVENT_READY, {}}); + } + return err; +} + +ErrorCode MediaSourceFilter::Start() +{ + MEDIA_LOG_D("IN"); + if (taskPtr_) { + taskPtr_->Start(); + } + return plugin_ ? TranslateError(plugin_->Start()) : PLUGIN_NOT_FOUND; +} + +ErrorCode MediaSourceFilter::PullData(const std::string& outPort, uint64_t offset, size_t size, AVBufferPtr& data) +{ + MEDIA_LOG_D("IN, offset: %zu, size: %zu", offset, size); + if (!plugin_) { + return ErrorCode::PLUGIN_NOT_FOUND; + } + ErrorCode err; + if (isSeekable_) { + size_t totalSize = 0; + if ((plugin_->GetSize(totalSize) == Status::OK) && (totalSize != 0)) { + if (offset >= totalSize) { + MEDIA_LOG_W("offset: %zu is larger than totalSize: %zu", offset, totalSize); + return ErrorCode::END_OF_STREAM; + } + if ((offset + size) > totalSize) { + size = totalSize - offset; + } + auto realSize = data->GetMemory()->GetCapacity(); + if (size > realSize) { + size = realSize; + } + MEDIA_LOG_D("totalSize_: %zu", totalSize); + } + if (position_ != offset) { + err = TranslateError(plugin_->SeekTo(offset)); + if (err != ErrorCode::SUCCESS) { + MEDIA_LOG_E("Seek to %zu fail", offset); + return err; + } + position_ = offset; + } + } + if (data == nullptr) { + data = std::make_shared(); + } + err = TranslateError(plugin_->Read(data, size)); + if (err == ErrorCode::SUCCESS) { + position_ += data->GetMemory()->GetSize(); + } + return err; +} + +ErrorCode MediaSourceFilter::Stop() +{ + MEDIA_LOG_D("IN"); + if (taskPtr_) { + taskPtr_->Stop(); + } + protocol_.clear(); + uri_.clear(); + ErrorCode ret = PLUGIN_NOT_FOUND; + if (plugin_) { + ret = TranslateError(plugin_->Stop()); + } + return ret; +} + +void MediaSourceFilter::FlushStart() +{ + MEDIA_LOG_D("FlushStart entered."); +} + +void MediaSourceFilter::FlushEnd() +{ + MEDIA_LOG_D("FlushEnd entered."); +} + +void MediaSourceFilter::InitPorts() +{ + MEDIA_LOG_D("IN"); + auto outPort = std::make_shared(this); + outPorts_.push_back(outPort); +} + +void MediaSourceFilter::ActivateMode() +{ + MEDIA_LOG_D("IN"); + isSeekable_ = plugin_ && plugin_->IsSeekable(); + if (!isSeekable_) { + taskPtr_ = std::make_shared("DataReader"); + taskPtr_->RegisterHandler(std::bind(&MediaSourceFilter::ReadLoop, this)); + } +} + +ErrorCode MediaSourceFilter::DoNegotiate(const std::shared_ptr& source) +{ + MEDIA_LOG_D("IN"); + SourceType sourceType = source->GetSourceType(); + if (sourceType == SourceType::SOURCE_TYPE_URI) { + std::string suffix = GetUriSuffix(source->GetSourceUri()); + if (!suffix.empty()) { + std::shared_ptr suffixMeta = std::make_shared(); + suffixMeta->SetString(Media::Plugin::MetaID::MEDIA_FILE_EXTENSION, suffix); + size_t fileSize = 0; + if ((plugin_->GetSize(fileSize) == Status::OK) && (fileSize != 0)) { + suffixMeta->SetUint64(Media::Plugin::MetaID::MEDIA_FILE_SIZE, fileSize); + } + CapabilitySet peerCaps; + if (!GetOutPort(PORT_NAME_DEFAULT)->Negotiate(suffixMeta, peerCaps)) { + MEDIA_LOG_E("Negotiate fail!"); + return ErrorCode::INVALID_PARAM_VALUE; + } + } + } + return ErrorCode::SUCCESS; +} + +std::string MediaSourceFilter::GetUriSuffix(const std::string& uri) +{ + MEDIA_LOG_D("IN"); + std::string suffix; + auto const pos = uri.find_last_of('.'); + if (pos != std::string::npos) { + suffix = uri.substr(pos + 1); + } + return suffix; +} + +void MediaSourceFilter::ReadLoop() +{ + MEDIA_LOG_D("IN"); + AVBufferPtr bufferPtr = std::make_shared(); + ErrorCode ret = TranslateError(plugin_->Read(bufferPtr, 4096)); // 4096: default push data size + if (ret == END_OF_STREAM) { + Stop(); + OnEvent({EVENT_COMPLETE, {}}); + return; + } + outPorts_[0]->PushData(bufferPtr); +} + +void MediaSourceFilter::ParseProtocol(const std::shared_ptr& source) +{ + OHOS::Media::SourceType srcType = source->GetSourceType(); + MEDIA_LOG_D("sourceType = %d", OHOS::Media::to_underlying(srcType)); + if (srcType == OHOS::Media::SourceType::SOURCE_TYPE_URI) { + uri_ = source->GetSourceUri(); + auto const pos = uri_.find("://"); + if (pos != std::string::npos) { + auto prefix = uri_.substr(0, pos); + protocol_.append(prefix); + } else { + protocol_.append("file"); + } + } else if (srcType == SourceType::SOURCE_TYPE_FD) { + protocol_.append("fd"); + uri_ = source->GetSourceUri(); + } else if (srcType == SourceType::SOURCE_TYPE_STREAM) { + protocol_.append("stream"); + uri_.append("stream://"); + } + MEDIA_LOG_D("protocol: %s, uri: %s", protocol_.c_str(), uri_.c_str()); +} + +ErrorCode MediaSourceFilter::CreatePlugin(const std::shared_ptr& info, const std::string& name, + PluginManager& manager) +{ + if ((plugin_ != nullptr) && (pluginInfo_ != nullptr)) { + if (info->name == pluginInfo_->name && TranslateError(plugin_->Reset()) == ErrorCode::SUCCESS) { + MEDIA_LOG_I("Reuse last plugin: %s", name.c_str()); + return ErrorCode::SUCCESS; + } + if (TranslateError(plugin_->Deinit()) != ErrorCode::SUCCESS) { + MEDIA_LOG_E("Deinit last plugin: %s error", pluginInfo_->name.c_str()); + } + } + plugin_ = manager.CreateSourcePlugin(name); + if (plugin_ == nullptr) { + MEDIA_LOG_E("PluginManager CreatePlugin %s fail", name.c_str()); + return ErrorCode::UNKNOWN_ERROR; + } + pluginInfo_ = info; + MEDIA_LOG_I("Create new plugin: \"%s\" success", pluginInfo_->name.c_str()); + return ErrorCode::SUCCESS; +} + +ErrorCode MediaSourceFilter::FindPlugin(const std::shared_ptr& source) +{ + ParseProtocol(source); + if (protocol_.empty()) { + MEDIA_LOG_E("protocol_ is empty"); + return ErrorCode::NULL_POINTER_ERROR; + } + PluginManager& pluginManager = PluginManager::Instance(); + std::set nameList = pluginManager.ListPlugins(PluginType::SOURCE); + for (const std::string& name : nameList) { + std::shared_ptr info = pluginManager.GetPluginInfo(PluginType::SOURCE, name); + MEDIA_LOG_I("name: %s, info->name: %s", name.c_str(), info->name.c_str()); + auto val = info->extra[PLUGIN_INFO_EXTRA_PROTOCOL]; + if (val.Type() == typeid(std::string)) { + auto supportProtocol = OHOS::Media::Plugin::AnyCast(val); + MEDIA_LOG_D("supportProtocol: %s", supportProtocol.c_str()); + if (protocol_ == supportProtocol && CreatePlugin(info, name, pluginManager) == ErrorCode::SUCCESS) { + MEDIA_LOG_I("CreatePlugin %s success", name_.c_str()); + return ErrorCode::SUCCESS; + } + } + } + MEDIA_LOG_I("Cannot find any plugin"); + return ErrorCode::PLUGIN_NOT_FOUND; +} +} // namespace Pipeline +} // namespace Media +} // namespace OHOS diff --git a/engine/pipeline/filters/source/media_source_filter.h b/engine/pipeline/filters/source/media_source_filter.h new file mode 100644 index 00000000..ae896160 --- /dev/null +++ b/engine/pipeline/filters/source/media_source_filter.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 MEDIA_PIPELINE_MEDIA_SOURCE_FILTER_H +#define MEDIA_PIPELINE_MEDIA_SOURCE_FILTER_H + +#include +#include + +#include "source.h" + +#include "foundation/blocking_queue.h" +#include "foundation/constants.h" +#include "foundation/error_code.h" +#include "foundation/type_define.h" +#include "foundation/utils.h" +#include "osal/thread/task.h" +#include "pipeline/core/filter_base.h" +#include "plugin/core/plugin_manager.h" +#include "plugin/interface/source_plugin.h" + +namespace OHOS { +namespace Media { +namespace Pipeline { +using SourceType = OHOS::Media::SourceType; +using MediaSource = OHOS::Media::Source; + +class MediaSourceFilter : public FilterBase { +public: + explicit MediaSourceFilter(const std::string& name); + ~MediaSourceFilter() override; + + std::vector GetWorkModes() override; + ErrorCode PullData(const std::string& outPort, uint64_t offset, size_t size, AVBufferPtr& data) override; + virtual ErrorCode SetSource(std::shared_ptr source); + ErrorCode InitPlugin(std::shared_ptr source); + virtual ErrorCode SetBufferSize(size_t size); + bool IsSeekable() const; + ErrorCode Prepare() override; + ErrorCode Start() override; + ErrorCode Stop() override; + void FlushStart() override; + void FlushEnd() override; + +private: + void InitPorts() override; + void ActivateMode(); + static std::string GetUriSuffix(const std::string& uri); + ErrorCode DoNegotiate(const std::shared_ptr& source); + void ReadLoop(); + void ParseProtocol(const std::shared_ptr& source); + ErrorCode CreatePlugin(const std::shared_ptr& info, const std::string& name, + Plugin::PluginManager& manager); + ErrorCode FindPlugin(const std::shared_ptr& source); + + std::shared_ptr taskPtr_; + std::string protocol_; + std::string uri_; + bool isSeekable_; + uint64_t position_; + size_t bufferSize_; + std::shared_ptr plugin_; + std::shared_ptr pluginAllocator_; + std::shared_ptr pluginInfo_; +}; +} // namespace Pipeline +} // namespace Media +} // namespace OHOS + +#endif diff --git a/engine/player/hiplayer.cpp b/engine/player/hiplayer.cpp new file mode 100644 index 00000000..8212db9d --- /dev/null +++ b/engine/player/hiplayer.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "HiPlayer" + +#include "histreamer/hiplayer.h" + +#include +#include "hiplayer_impl.h" + +namespace OHOS { +namespace Media { +HiPlayer::HiPlayer() + : player_(HiPlayerImpl::CreateHiPlayerImpl()), + curState_(Media::PlayerStates::PLAYER_IDLE), + isInited_(false), + isSingleLoop_(false), + audioStreamType_(-1) +{ + MEDIA_LOG_I("ctor entered."); +} + +HiPlayer::~HiPlayer() +{ + MEDIA_LOG_I("dtor entered."); + player_ = nullptr; + isInited_ = false; + isSingleLoop_ = false; +} + +int32_t HiPlayer::SetSource(const Media::Source& source) +{ + MEDIA_LOG_I("SetSource entered."); + Init(); + auto src = std::make_shared(source); + int ret = -1; + if (player_ && player_->SetSource(src) == SUCCESS) { + ret = 0; + } + return ret; +} + +int32_t HiPlayer::Prepare() +{ + MEDIA_LOG_I("Prepare entered."); + curState_ = Media::PlayerStates::PLAYER_PREPARED; + return 0; +} + +int32_t HiPlayer::Play() +{ + MEDIA_LOG_I("Play entered."); + int ret = -1; + if (curState_ == Media::PlayerStates::PLAYER_PAUSED) { + if (player_ && player_->Resume() == SUCCESS) { + ret = 0; + } + } else if (player_ && player_->Play() == SUCCESS) { + curState_ = Media::PlayerStates::PLAYER_STARTED; + ret = 0; + } + return ret; +} + +bool HiPlayer::IsPlaying() +{ + return player_ && curState_ == Media::PlayerStates::PLAYER_STARTED; +} + +int32_t HiPlayer::Pause() +{ + MEDIA_LOG_I("Pause entered."); + int ret = -1; + if (player_ && player_->Pause() == SUCCESS) { + curState_ = Media::PlayerStates::PLAYER_PAUSED; + ret = 0; + } + return ret; +} + +int32_t HiPlayer::Stop() +{ + MEDIA_LOG_I("Stop entered."); + int ret = -1; + if (player_ && player_->Stop() == SUCCESS) { + curState_ = Media::PlayerStates::PLAYER_STOPPED; + ret = 0; + } + return ret; +} + +int32_t HiPlayer::Rewind(int64_t mSeconds, int32_t mode) +{ + MEDIA_LOG_I("Rewind entered."); + int ret = -1; + size_t pos = 0; + if (player_ && player_->Seek(mSeconds, pos) == SUCCESS) { + ret = 0; + } + return ret; +} + +int32_t HiPlayer::SetVolume(float leftVolume, float rightVolume) +{ + return 0; +} + +int32_t HiPlayer::SetSurface(Surface* surface) +{ + return 0; +} + +bool HiPlayer::IsSingleLooping() +{ + return isSingleLoop_; +} + +int32_t HiPlayer::GetPlayerState(int32_t& state) +{ + if (!player_) { + return -1; + } + state = static_cast(curState_); + return 0; +} + +int32_t HiPlayer::GetCurrentPosition(int64_t& currentPosition) +{ + int ret = -1; + if (player_ && player_->GetCurrentTime(currentPosition) == SUCCESS) { + ret = 0; + } + return ret; +} + +int32_t HiPlayer::GetDuration(int64_t& duration) +{ + int ret = -1; + size_t durationTime = 0; + if (player_ && player_->GetDuration(durationTime) == SUCCESS) { + duration = static_cast(durationTime); + ret = 0; + } + return ret; +} + +int32_t HiPlayer::GetVideoWidth(int32_t& videoWidth) +{ + return -1; +} + +int32_t HiPlayer::GetVideoHeight(int32_t& videoHeight) +{ + return -1; +} + +int32_t HiPlayer::SetPlaybackSpeed(float speed) +{ + return 0; +} + +int32_t HiPlayer::GetPlaybackSpeed(float& speed) +{ + speed = 1.0; + return 0; +} + +int32_t HiPlayer::SetAudioStreamType(int32_t type) +{ + if (curState_ != Media::PlayerStates::PLAYER_IDLE && curState_ != Media::PlayerStates::PLAYER_INITIALIZED) { + return -1; + } + audioStreamType_ = type; + return 0; +} + +void HiPlayer::GetAudioStreamType(int32_t& type) +{ + type = audioStreamType_; +} + +int32_t HiPlayer::Reset() +{ + int ret = -1; + if (player_ && player_->Stop() == SUCCESS) { + ret = 0; + } + return ret; +} + +int32_t HiPlayer::Release() +{ + return Reset(); +} + +int32_t HiPlayer::SetLoop(bool loop) +{ + MEDIA_LOG_I("SetLoop entered."); + int ret = -1; + if (player_ && player_->SetSingleLoop(loop) == SUCCESS) { + isSingleLoop_ = loop; + ret = 0; + } + return ret; +} + +void HiPlayer::SetPlayerCallback(const std::shared_ptr& cb) +{ +} + +int32_t HiPlayer::Init() +{ + MEDIA_LOG_I("Init entered."); + if (isInited_) { + return 0; + } + if (player_) { + player_->Init(); + } + curState_ = Media::PlayerStates::PLAYER_INITIALIZED; + return 0; +} + +int32_t HiPlayer::DeInit() +{ + return 0; +} + +int32_t HiPlayer::SetParameter(const Media::Format& params) +{ + return 0; +} + +std::shared_ptr CreateHiPlayer() +{ + return std::shared_ptr(new (std::nothrow) HiPlayer()); +} +} // namespace Media +} // namespace OHOS diff --git a/engine/player/hiplayer_impl.cpp b/engine/player/hiplayer_impl.cpp new file mode 100644 index 00000000..8eebe11d --- /dev/null +++ b/engine/player/hiplayer_impl.cpp @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "HiPlayerImpl" + +#include "hiplayer_impl.h" +#include +#include "foundation/log.h" +#include "foundation/meta.h" +#include "foundation/utils.h" +#include "pipeline/factory/filter_factory.h" + +namespace OHOS { +namespace Media { +using namespace Pipeline; + +HiPlayer::HiPlayerImpl::HiPlayerImpl() : fsm_(*this) +{ + MEDIA_LOG_I("hiPlayerImpl ctor"); + FilterFactory::Instance().Init(); + + audioSource = + FilterFactory::Instance().CreateFilterWithType("builtin.player.mediasource", "mediaSource"); + +#ifdef UNIT_TEST + demuxer = FilterFactory::Instance().CreateFilterWithType("builtin.player.demuxer", "demuxer"); + audioDecoder = FilterFactory::Instance().CreateFilterWithType("builtin.player.audiodecoder", + "audiodecoder"); + audioSink = + FilterFactory::Instance().CreateFilterWithType("builtin.player.audiosink", "audiosink"); +#else + demuxer = FilterFactory::Instance().CreateFilterWithType("builtin.player.demuxer", "demuxer"); + audioSink = + FilterFactory::Instance().CreateFilterWithType("builtin.player.audiosink", "audioSink"); +#ifdef VIDEO_SUPPORT + videoSink = + FilterFactory::Instance().CreateFilterWithType("builtin.player.videosink", "videoSink"); + FALSE_RETURN(videoSink != nullptr); +#endif +#endif + FALSE_RETURN(audioSource != nullptr); + FALSE_RETURN(demuxer != nullptr); + FALSE_RETURN(audioSink != nullptr); + + pipeline = std::make_shared(); +} + +HiPlayer::HiPlayerImpl::~HiPlayerImpl() +{ + fsm_.Stop(); + MEDIA_LOG_W("dtor called."); +} + +std::shared_ptr HiPlayer::HiPlayerImpl::CreateHiPlayerImpl() +{ + return std::shared_ptr(new (std::nothrow) HiPlayer::HiPlayerImpl()); +} + +void HiPlayer::HiPlayerImpl::Init() +{ + if (initialized) { + return; + } + initialized = true; + fsm_.SetStateCallback(this); + fsm_.Start(); + pipeline->Init(this, this); + +#ifdef UNIT_TEST + pipeline->AddFilters({audioSource.get(), demuxer.get(), audioSink.get()}); + pipeline->LinkFilters({audioSource.get(), demuxer.get(), audioSink.get()}); +#else + pipeline->AddFilters({audioSource.get(), demuxer.get()}); + pipeline->LinkFilters({audioSource.get(), demuxer.get()}); +#endif +} + +PFilter HiPlayer::HiPlayerImpl::CreateAudioDecoder(const std::string& desc) +{ + if (!audioDecoderMap[desc]) { + audioDecoderMap[desc] = FilterFactory::Instance().CreateFilterWithType( + "builtin.player.audiodecoder", "audiodecoder-" + desc); + // set parameters to decoder. + } + return audioDecoderMap[desc]; +} + +ErrorCode HiPlayer::HiPlayerImpl::Prepare() +{ + // Do nothing, because after SetSource, it enters PreparingState, will call filters.Prepare() + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::Play() +{ + return fsm_.SendEvent(PLAY); +} + +ErrorCode HiPlayer::HiPlayerImpl::Pause() +{ + return fsm_.SendEvent(PAUSE); +} + +ErrorCode HiPlayer::HiPlayerImpl::Resume() +{ + return fsm_.SendEvent(RESUME); +} + +ErrorCode HiPlayer::HiPlayerImpl::Stop() +{ + return fsm_.SendEvent(STOP); +} + +ErrorCode HiPlayer::HiPlayerImpl::StopAsync() +{ + return fsm_.SendEventAsync(STOP); +} + +ErrorCode HiPlayer::HiPlayerImpl::SetSource(std::shared_ptr source) +{ + return fsm_.SendEvent(SET_SOURCE, source); +} + +ErrorCode HiPlayer::HiPlayerImpl::SetBufferSize(size_t size) +{ + return audioSource->SetBufferSize(size); +} + +void HiPlayer::HiPlayerImpl::OnEvent(Event event) +{ + MEDIA_LOG_D("[HiStreamer] OnEvent (%d)", event.type); + switch (event.type) { + case EVENT_ERROR: { + fsm_.SendEventAsync(NOTIFY_ERROR, event.param); + break; + } + case EVENT_READY: + fsm_.SendEventAsync(NOTIFY_READY); + break; + case EVENT_COMPLETE: + fsm_.SendEventAsync(NOTIFY_COMPLETE); + break; + default: + MEDIA_LOG_E("Unknown event(%d)", event.type); + } +} + +ErrorCode HiPlayer::HiPlayerImpl::SetSingleLoop(bool loop) +{ + singleLoop = loop; + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::Seek(size_t time, size_t& position) +{ + auto rtv = fsm_.SendEvent(SEEK, static_cast(time)); + if (rtv == SUCCESS) { + int64_t pos = 0; + rtv = GetCurrentTime(pos); + position = static_cast(pos); + } + return rtv; +} + +ErrorCode HiPlayer::HiPlayerImpl::DoSetSource(const std::shared_ptr& source) const +{ + return audioSource->SetSource(source); +} + +ErrorCode HiPlayer::HiPlayerImpl::PrepareFilters() +{ + return pipeline->Prepare(); +} + +ErrorCode HiPlayer::HiPlayerImpl::DoPlay() +{ + return pipeline->Start(); +} + +ErrorCode HiPlayer::HiPlayerImpl::DoPause() +{ + return pipeline->Pause(); +} + +ErrorCode HiPlayer::HiPlayerImpl::DoResume() +{ + return pipeline->Resume(); +} + +ErrorCode HiPlayer::HiPlayerImpl::DoStop() +{ + return pipeline->Stop(); +} + +ErrorCode HiPlayer::HiPlayerImpl::DoSeek(int64_t msec) +{ + pipeline->FlushStart(); + pipeline->FlushEnd(); + return demuxer->SeekTo(msec); +} + +ErrorCode HiPlayer::HiPlayerImpl::DoOnReady() +{ + sourceMeta_ = demuxer->GetGlobalMetaInfo(); + streamMeta_.clear(); + for (auto& streamMeta : demuxer->GetStreamMetaInfo()) { + streamMeta_.push_back(streamMeta); + } + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::DoOnComplete() +{ + MEDIA_LOG_W("OnComplete looping: %d.", singleLoop.load()); + if (!singleLoop) { + StopAsync(); + } else { + fsm_.SendEventAsync(SEEK, static_cast(0)); + } + auto ptr = callback_.lock(); + if (ptr != nullptr) { + ptr->OnCompleted(); + } + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::DoOnError(ErrorCode errorCode) +{ + // fixme do we need to callback here to notify registered callback + auto ptr = callback_.lock(); + if (ptr != nullptr) { + ptr->OnError(errorCode); + } + return SUCCESS; +} + +bool HiPlayer::HiPlayerImpl::IsSingleLooping() +{ + return singleLoop; +} + +ErrorCode HiPlayer::HiPlayerImpl::GetCurrentTime(int64_t& time) const +{ + return demuxer->GetCurrentTime(time); +} + +ErrorCode HiPlayer::HiPlayerImpl::GetDuration(size_t& time) const +{ + uint64_t duration = 0; + auto sourceMeta = sourceMeta_.lock(); + if (sourceMeta == nullptr) { + time = 0; + return NOT_FOUND; + } + if (sourceMeta->GetUint64(Media::Plugin::MetaID::MEDIA_DURATION, duration)) { + time = duration; + return SUCCESS; + } + // use max stream duration as whole source duration if source meta does not contains the duration meta + uint64_t tmp = 0; + bool found = false; + for (const auto& streamMeta : streamMeta_) { + auto ptr = streamMeta.lock(); + if (ptr != nullptr && ptr->GetUint64(Media::Plugin::MetaID::MEDIA_DURATION, tmp)) { + found = true; + duration = std::max(duration, tmp); + } + } + if (found) { + time = duration; + return SUCCESS; + } + return NOT_FOUND; +} + +ErrorCode HiPlayer::HiPlayerImpl::SetVolume(float leftVolume, float rightVolume) +{ + return UNIMPLEMENT; +} + +ErrorCode HiPlayer::HiPlayerImpl::SetCallback(const shared_ptr& callback) +{ + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::SetCallback(const shared_ptr& callback) +{ + // todo this interface should be protected by mutex in case of multi-thread runtime + callback_ = callback; + return NULL_POINTER_ERROR; +} + +void HiPlayer::HiPlayerImpl::OnStateChanged(std::string state) +{ + if (auto callback = callback_.lock()) { + callback->OnStateChanged(state); + } +} + +ErrorCode HiPlayer::HiPlayerImpl::OnCallback(const FilterCallbackType& type, Filter* filter, + const Plugin::Any& parameter) +{ + ErrorCode ret = ErrorCode::SUCCESS; + switch (type) { + case FilterCallbackType::PORT_ADDED: +#ifndef VIDEO_SUPPORT + ret = NewAudioPortFound(filter, parameter); +#else + ret = NewVideoPortFound(filter, parameter); +#endif + break; + case FilterCallbackType::PORT_REMOVE: + ret = RemoveFilterChains(filter, parameter); + break; + default: + break; + } + return ret; +} + +ErrorCode HiPlayer::HiPlayerImpl::GetStreamCnt(size_t& cnt) const +{ + cnt = streamMeta_.size(); + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::GetSourceMeta(shared_ptr& meta) const +{ + meta = sourceMeta_.lock(); + return meta ? SUCCESS : NOT_FOUND; +} + +ErrorCode HiPlayer::HiPlayerImpl::GetStreamMeta(size_t index, shared_ptr& meta) const +{ + if (index > streamMeta_.size() || index < 0) { + return INVALID_PARAM_VALUE; + } + meta = streamMeta_[index].lock(); + if (meta == nullptr) { + return NOT_FOUND; + } + return SUCCESS; +} + +ErrorCode HiPlayer::HiPlayerImpl::NewAudioPortFound(Filter* filter, const Plugin::Any& parameter) +{ + ErrorCode rtv = PORT_UNEXPECTED; + auto param = Plugin::AnyCast(parameter); + if (filter == demuxer.get() && param.type == PortType::OUT) { + MEDIA_LOG_I("new port found on demuxer %lu", param.ports.size()); + for (const auto& portDesc : param.ports) { + if (!StringStartsWith(portDesc.name, "audio")) { + MEDIA_LOG_W("NewAudioPortFound, discard non-audio port: %s", portDesc.name.c_str()); + continue; + } + MEDIA_LOG_I("port name %s", portDesc.name.c_str()); + auto fromPort = filter->GetOutPort(portDesc.name); + if (portDesc.isPcm) { + pipeline->AddFilters({audioSink.get()}); + FAIL_LOG(pipeline->LinkPorts(fromPort, audioSink->GetInPort(PORT_NAME_DEFAULT))); + ActiveFilters({audioSink.get()}); + } else { + auto newAudioDecoder = CreateAudioDecoder(portDesc.name); + pipeline->AddFilters({newAudioDecoder.get(), audioSink.get()}); + FAIL_LOG(pipeline->LinkPorts(fromPort, newAudioDecoder->GetInPort(PORT_NAME_DEFAULT))); + FAIL_LOG(pipeline->LinkPorts(newAudioDecoder->GetOutPort(PORT_NAME_DEFAULT), + audioSink->GetInPort(PORT_NAME_DEFAULT))); + ActiveFilters({newAudioDecoder.get(), audioSink.get()}); + } + rtv = SUCCESS; + break; + } + } + return rtv; +} + +#ifdef VIDEO_SUPPORT +ErrorCode HiPlayer::HiPlayerImpl::NewVideoPortFound(Filter* filter, const Plugin::Any& parameter) +{ + auto param = Plugin::AnyCast(parameter); + if (filter != demuxer.get() || param.type != PortType::OUT) { + return PORT_UNEXPECTED; + } + MEDIA_LOG_I("new port found on demuxer %lu", param.ports.size()); + std::vector newFilters; + for (const auto& portDesc : param.ports) { + MEDIA_LOG_I("port name %s", portDesc.name.c_str()); + if (StringStartsWith(portDesc.name, "video")) { + videoDecoder = FilterFactory::Instance().CreateFilterWithType( + "builtin.player.videodecoder", "videodecoder-" + portDesc.name); + if (pipeline->AddFilters({videoDecoder.get()}) != ALREADY_EXISTS) { + // link demuxer and video decoder + auto fromPort = filter->GetOutPort(portDesc.name); + auto toPort = videoDecoder->GetInPort(PORT_NAME_DEFAULT); + FAIL_LOG(pipeline->LinkPorts(fromPort, toPort)); // link ports + newFilters.emplace_back(videoDecoder.get()); + + // link video decoder and video sink + if (pipeline->AddFilters({videoSink.get()}) != ALREADY_EXISTS) { + fromPort = videoDecoder->GetOutPort(PORT_NAME_DEFAULT); + toPort = videoSink->GetInPort(PORT_NAME_DEFAULT); + FAIL_LOG(pipeline->LinkPorts(fromPort, toPort)); // link ports + newFilters.push_back(videoSink.get()); + } + } + break; + } + } + if (!newFilters.empty()) { + ActiveFilters(newFilters); + } + return SUCCESS; +} +#endif + +ErrorCode HiPlayer::HiPlayerImpl::RemoveFilterChains(Filter* filter, const Plugin::Any& parameter) +{ + ErrorCode ret = SUCCESS; + auto param = Plugin::AnyCast(parameter); + if (filter != demuxer.get() || param.type != PortType::OUT) { + return ret; + } + for (const auto& portDesc : param.ports) { + MEDIA_LOG_I("remove filter chain for port: %s", portDesc.name.c_str()); + auto peerPort = filter->GetOutPort(portDesc.name)->GetPeerPort(); + if (peerPort) { + auto nextFilter = const_cast(dynamic_cast(peerPort->GetOwnerFilter())); + if (nextFilter) { + pipeline->RemoveFilterChain(nextFilter); + } + } + } + return ret; +} + +void HiPlayer::HiPlayerImpl::ActiveFilters(const std::vector& filters) +{ + for (auto it = filters.rbegin(); it != filters.rend(); ++it) { + (*it)->Prepare(); + } +} +} // namespace Media +} // namespace OHOS diff --git a/engine/player/hiplayer_impl.h b/engine/player/hiplayer_impl.h new file mode 100644 index 00000000..55f5bdd3 --- /dev/null +++ b/engine/player/hiplayer_impl.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_IMPL_H +#define HISTREAMER_HIPLAYER_IMPL_H + +#include +#include + +#include "common/any.h" +#ifdef VIDEO_SUPPORT +#include "filters/sink/video_sink/video_sink_filter.h" +#include "filters/codec/video_decoder/video_decoder_filter.h" +#endif +#include "filters/demux/demuxer_filter.h" +#include "filters/source/media_source_filter.h" +#include "foundation/error_code.h" +#include "foundation/utils.h" +#include "histreamer/hiplayer.h" +#include "internal/state_machine.h" +#include "pipeline/core/filter_callback.h" +#include "pipeline/core/pipeline.h" +#include "pipeline/core/pipeline_core.h" +#include "pipeline/filters/codec/audio_decoder/audio_decoder_filter.h" +#include "pipeline/filters/sink/audio_sink/audio_sink_filter.h" +#include "play_executor.h" + +namespace OHOS { +namespace Media { +class PlayerCallbackInner { +public: + virtual void OnCompleted() = 0; + virtual void OnError(ErrorCode errorCode) = 0; + virtual void OnSeekCompleted(int32_t position) = 0; + virtual void OnStateChanged(std::string state) = 0; +}; + +class HiPlayer::HiPlayerImpl : public Pipeline::EventReceiver, + public PlayExecutor, + public StateChangeCallback, + public Pipeline::FilterCallback { + friend class StateMachine; + +public: + ~HiPlayerImpl() override; + + static std::shared_ptr CreateHiPlayerImpl(); + + void Init(); + void OnEvent(Event event) override; + + ErrorCode Prepare(); + ErrorCode Play(); + ErrorCode Pause(); + ErrorCode Resume(); + ErrorCode Stop(); + + // interface from MediaSource + ErrorCode SetSource(std::shared_ptr source); + ErrorCode SetBufferSize(size_t size); + ErrorCode Seek(size_t time, size_t& position); + ErrorCode SetSingleLoop(bool loop); + bool IsSingleLooping(); + + /** + * get duration in milliseconds + * + * @param time milliseconds + * @return + */ + ErrorCode GetDuration(size_t& time) const; + ErrorCode GetCurrentTime(int64_t& time) const; + ErrorCode GetSourceMeta(std::shared_ptr& meta) const; + ErrorCode GetStreamCnt(size_t& cnt) const; + ErrorCode GetStreamMeta(size_t index, std::shared_ptr& meta) const; + + ErrorCode SetVolume(float leftVolume, float rightVolume); + + ErrorCode SetCallback(const shared_ptr& callback); + + ErrorCode SetCallback(const shared_ptr& callback); + + void OnStateChanged(std::string state) override; + + ErrorCode OnCallback(const Pipeline::FilterCallbackType& type, Pipeline::Filter* filter, + const Plugin::Any& parameter) override; + + // interface from PlayExecutor + ErrorCode DoSetSource(const std::shared_ptr& source) const override; + ErrorCode PrepareFilters() override; + ErrorCode DoPlay() override; + ErrorCode DoPause() override; + ErrorCode DoResume() override; + ErrorCode DoStop() override; + ErrorCode DoSeek(int64_t msec) override; + ErrorCode DoOnReady() override; + ErrorCode DoOnComplete() override; + ErrorCode DoOnError(ErrorCode) override; + +private: + HiPlayerImpl(); + HiPlayerImpl(const HiPlayerImpl& other); + HiPlayerImpl& operator=(const HiPlayerImpl& other); + ErrorCode StopAsync(); + + Pipeline::PFilter CreateAudioDecoder(const std::string& desc); + + ErrorCode NewAudioPortFound(Pipeline::Filter* filter, const Plugin::Any& parameter); +#ifdef VIDEO_SUPPORT + ErrorCode NewVideoPortFound(Pipeline::Filter* filter, const Plugin::Any& parameter); +#endif + + ErrorCode RemoveFilterChains(Pipeline::Filter* filter, const Plugin::Any& parameter); + + void ActiveFilters(const std::vector& filters); + +private: + StateMachine fsm_; + std::shared_ptr pipeline; + + std::shared_ptr audioSource; + std::shared_ptr demuxer; + std::shared_ptr audioDecoder; + std::shared_ptr audioSink; +#ifdef VIDEO_SUPPORT + std::shared_ptr videoDecoder; + std::shared_ptr videoSink; +#endif + + std::unordered_map> audioDecoderMap; + + std::weak_ptr sourceMeta_; + std::vector> streamMeta_; + + std::atomic singleLoop {false}; + bool initialized = false; + + std::weak_ptr callback_; +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/init_state.h b/engine/player/internal/init_state.h new file mode 100644 index 00000000..e5985067 --- /dev/null +++ b/engine/player/internal/init_state.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_INIT_STATE_H +#define HISTREAMER_HIPLAYER_INIT_STATE_H + +#include + +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "play_executor.h" +#include "thread/mutex.h" + +#include "state.h" + +namespace OHOS { +namespace Media { +class InitState : public State { +public: + explicit InitState(PlayExecutor& executor, const std::string& name = "InitState") : State(executor, name) + { + } + + ~InitState() override = default; + + std::tuple SetSource(const Plugin::Any& param) override + { + OSAL::ScopedLock lock(mutex_); + std::shared_ptr source; + if (param.Type() != typeid(std::shared_ptr) || + !(source = Plugin::AnyCast>(param))) { + return {INVALID_SOURCE, ACTION_BUTT}; + } + auto ret = executor_.DoSetSource(source); + return {ret, TRANS_TO_PREPARING}; + } + + std::tuple Stop() override + { + return {SUCCESS, TRANS_TO_INIT}; + } + + std::tuple Enter(Intent) override + { + OSAL::ScopedLock lock(mutex_); + auto ret = executor_.DoStop(); + return {ret, ACTION_BUTT}; + } + +private: + OSAL::Mutex mutex_ {}; +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/pause_state.h b/engine/player/internal/pause_state.h new file mode 100644 index 00000000..f65a6f91 --- /dev/null +++ b/engine/player/internal/pause_state.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_PAUSE_STATE_H +#define HISTREAMER_HIPLAYER_PAUSE_STATE_H + +#include + +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "play_executor.h" + +#include "state.h" + +namespace OHOS { +namespace Media { +class PauseState : public State { +public: + explicit PauseState(PlayExecutor& executor, const std::string& name = "PauseState") : State(executor, name) + { + } + + ~PauseState() override = default; + + std::tuple Enter(Intent) override + { + MEDIA_LOG_D("Pause state entered."); + auto ret = executor_.DoPause(); + return {ret, ACTION_BUTT}; + } + + std::tuple Play() override + { + MEDIA_LOG_D("Play in pause state."); + return {SUCCESS, TRANS_TO_PLAYING}; + } + + std::tuple Seek(const Plugin::Any& param) override + { + MEDIA_LOG_D("Seek in pause state."); + if (param.Type() != typeid(int64_t)) { + return {INVALID_PARAM_VALUE, ACTION_BUTT}; + } + auto timeMs = Plugin::AnyCast(param); + auto ret = executor_.DoSeek(timeMs); + return {ret, ACTION_BUTT}; + } + + std::tuple Resume() override + { + MEDIA_LOG_D("Resume in pause state."); + return {SUCCESS, TRANS_TO_PLAYING}; + } + + std::tuple Stop() override + { + MEDIA_LOG_D("Stop called in pause state."); + return {SUCCESS, TRANS_TO_INIT}; + } +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/playing_state.h b/engine/player/internal/playing_state.h new file mode 100644 index 00000000..a9df76fb --- /dev/null +++ b/engine/player/internal/playing_state.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_PLAYING_STATE_H +#define HISTREAMER_HIPLAYER_PLAYING_STATE_H + +#include + +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "play_executor.h" + +#include "state.h" + +namespace OHOS { +namespace Media { +class PlayingState : public State { +public: + explicit PlayingState(PlayExecutor& executor, const std::string& name = "PlayingState") : State(executor, name) + { + } + + ~PlayingState() override = default; + + std::tuple Enter(Intent intent) override + { + MEDIA_LOG_D("Enter state: %s", name_.c_str()); + ErrorCode ret; + if (intent == RESUME) { + ret = executor_.DoResume(); + } else { + ret = executor_.DoPlay(); + } + return {ret, ACTION_BUTT}; + } + + std::tuple Play() override + { + return {SUCCESS, ACTION_BUTT}; + } + + std::tuple Seek(const Plugin::Any& param) override + { + MEDIA_LOG_D("Seek in playing state."); + if (param.Type() != typeid(int64_t)) { + return {INVALID_PARAM_VALUE, ACTION_BUTT}; + } + auto timeMs = Plugin::AnyCast(param); + auto ret = executor_.DoSeek(timeMs); + return {ret, ACTION_BUTT}; + } + + std::tuple Pause() override + { + return {SUCCESS, TRANS_TO_PAUSE}; + } + + std::tuple Stop() override + { + return {SUCCESS, TRANS_TO_INIT}; + } + + std::tuple OnComplete() override + { + auto ret = executor_.DoOnComplete(); + return {ret, ACTION_BUTT}; + } +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/prepare_state.h b/engine/player/internal/prepare_state.h new file mode 100644 index 00000000..12c62f58 --- /dev/null +++ b/engine/player/internal/prepare_state.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_PREPARE_STATE_H +#define HISTREAMER_HIPLAYER_PREPARE_STATE_H + +#include + +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "play_executor.h" + +#include "state.h" + +namespace OHOS { +namespace Media { +class PreparingState : public State { +public: + explicit PreparingState(PlayExecutor& executor, const std::string& name = "PreparingState") : State(executor, name) + { + } + + ~PreparingState() override = default; + + std::tuple Enter(Intent) override + { + MEDIA_LOG_D("Enter state: %s", name_.c_str()); + Action nextAction = ACTION_BUTT; + auto rtv = executor_.PrepareFilters(); + if (rtv != SUCCESS) { + nextAction = TRANS_TO_INIT; + } + return {rtv, nextAction}; + } + + std::tuple Play() override + { + MEDIA_LOG_W("Play received in preparing state."); + return {SUCCESS, ACTION_PENDING}; + } + + std::tuple Seek(const Plugin::Any& param) override + { + MEDIA_LOG_D("Seek in preparing state."); + if (param.Type() != typeid(int64_t)) { + return {INVALID_PARAM_VALUE, ACTION_BUTT}; + } + auto timeMs = Plugin::AnyCast(param); + auto ret = executor_.DoSeek(timeMs); + return {ret, ACTION_BUTT}; + } + + std::tuple Stop() override + { + return {SUCCESS, TRANS_TO_INIT}; + } + + std::tuple OnReady() override + { + return {SUCCESS, TRANS_TO_READY}; + } +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/ready_state.h b/engine/player/internal/ready_state.h new file mode 100644 index 00000000..6603d531 --- /dev/null +++ b/engine/player/internal/ready_state.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLAYER_READY_STATE_H +#define HISTREAMER_PLAYER_READY_STATE_H + +#include + +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "play_executor.h" + +#include "state.h" + +namespace OHOS { +namespace Media { +class ReadyState : public State { +public: + explicit ReadyState(PlayExecutor& executor, const std::string& name = "ReadyState") : State(executor, name) + { + } + + ~ReadyState() override = default; + + std::tuple Enter(Intent) override + { + auto rtv = executor_.DoOnReady(); + return {rtv, ACTION_BUTT}; + } + + std::tuple Seek(const Plugin::Any& param) override + { + MEDIA_LOG_D("Seek in ready state."); + if (param.Type() != typeid(int64_t)) { + return {INVALID_PARAM_VALUE, ACTION_BUTT}; + } + auto timeMs = Plugin::AnyCast(param); + auto ret = executor_.DoSeek(timeMs); + return {ret, ACTION_BUTT}; + } + + std::tuple Play() override + { + MEDIA_LOG_D("Play in ready state."); + return {SUCCESS, TRANS_TO_PLAYING}; + } + + std::tuple Stop() override + { + MEDIA_LOG_D("Stop in ready state."); + return {SUCCESS, TRANS_TO_INIT}; + } +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/state.cpp b/engine/player/internal/state.cpp new file mode 100644 index 00000000..c9ae2d1e --- /dev/null +++ b/engine/player/internal/state.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "State" + +#include "state.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +State::State(PlayExecutor& executor, std::string name) : executor_(executor), name_(std::move(name)) +{ +} +std::tuple State::Enter(Intent) +{ + MEDIA_LOG_D("Enter state: %s", name_.c_str()); + return {SUCCESS, ACTION_BUTT}; +} +void State::Exit() +{ + MEDIA_LOG_D("Exit state: %s", name_.c_str()); +} +std::tuple State::Execute(Intent intent, const Plugin::Any& param) +{ + return DispatchIntent(intent, param); +} +const std::string& State::GetName() +{ + return name_; +} +std::tuple State::SetSource(const Plugin::Any&) +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::Play() +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::Stop() +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::Pause() +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::Resume() +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::Seek(const Plugin::Any&) +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::SetAttribute() +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::OnReady() +{ + return {INVALID_OPERATION, ACTION_BUTT}; +} +std::tuple State::OnError(const Plugin::Any& param) +{ + ErrorCode errorCode = UNKNOWN_ERROR; + if (param.Type() == typeid(ErrorCode)) { + errorCode = Plugin::AnyCast(param); + } + executor_.DoOnError(errorCode); + return {SUCCESS, TRANS_TO_INIT}; +} +std::tuple State::OnComplete() +{ + return {SUCCESS, ACTION_BUTT}; +} +std::tuple State::DispatchIntent(Intent intent, const Plugin::Any& param) +{ + ErrorCode rtv = SUCCESS; + Action nextAction = ACTION_BUTT; + switch (intent) { + case SET_SOURCE: + std::tie(rtv, nextAction) = SetSource(param); + break; + case SEEK: + std::tie(rtv, nextAction) = Seek(param); + break; + case PLAY: + std::tie(rtv, nextAction) = Play(); + break; + case PAUSE: + std::tie(rtv, nextAction) = Pause(); + break; + case RESUME: + std::tie(rtv, nextAction) = Resume(); + break; + case STOP: + std::tie(rtv, nextAction) = Stop(); + break; + case SET_ATTRIBUTE: + std::tie(rtv, nextAction) = SetAttribute(); + break; + case NOTIFY_READY: + std::tie(rtv, nextAction) = OnReady(); + break; + case NOTIFY_COMPLETE: + std::tie(rtv, nextAction) = OnComplete(); + break; + case NOTIFY_ERROR: + std::tie(rtv, nextAction) = OnError(param); + break; + default: + break; + } + MEDIA_LOG_D("DispatchIntent %s, curState: %s, nextState: %s", intentDesc_.at(intent).c_str(), name_.c_str(), + actionDesc_.at(nextAction).c_str()); + return {rtv, nextAction}; +} +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/player/internal/state.h b/engine/player/internal/state.h new file mode 100644 index 00000000..23d7cb94 --- /dev/null +++ b/engine/player/internal/state.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_STATE_H +#define HISTREAMER_HIPLAYER_STATE_H + +#include +#include +#include +#include + +#include "common/any.h" +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "play_executor.h" + +namespace OHOS { +namespace Media { +enum Intent { + SET_SOURCE, + SEEK, + PLAY, + PAUSE, + RESUME, + STOP, + SET_ATTRIBUTE, + NOTIFY_READY, + NOTIFY_COMPLETE, + NOTIFY_ERROR, + INTENT_BUTT +}; + +enum Action { + TRANS_TO_INIT, + TRANS_TO_PREPARING, + TRANS_TO_READY, + TRANS_TO_PLAYING, + TRANS_TO_PAUSE, + ACTION_PENDING, + ACTION_BUTT +}; + +class State { +public: + State(PlayExecutor& executor, std::string name); + virtual ~State() = default; + virtual std::tuple Enter(Intent intent); + virtual void Exit(); + std::tuple Execute(Intent intent, const Plugin::Any& param); + const std::string& GetName(); + virtual std::tuple SetSource(const Plugin::Any& param); + virtual std::tuple Play(); + virtual std::tuple Stop(); + virtual std::tuple Pause(); + virtual std::tuple Resume(); + virtual std::tuple Seek(const Plugin::Any&); + virtual std::tuple SetAttribute(); + virtual std::tuple OnReady(); + virtual std::tuple OnError(const Plugin::Any&) final; + virtual std::tuple OnComplete(); + +protected: + std::tuple DispatchIntent(Intent intent, const Plugin::Any& param); + + PlayExecutor& executor_; + const std::string name_; + const std::map intentDesc_ = { + { SET_SOURCE, "SET_SOURCE" }, + { SEEK, "SEEK" }, + { PLAY, "PLAY" }, + { PAUSE, "PAUSE" }, + { RESUME, "RESUME" }, + { STOP, "STOP" }, + { SET_ATTRIBUTE, "SET_ATTRIBUTE" }, + { NOTIFY_READY, "NOTIFY_READY" }, + { NOTIFY_COMPLETE, "NOTIFY_COMPLETE" }, + { NOTIFY_ERROR, "NOTIFY_ERROR" }, + { INTENT_BUTT, "INTENT_BUTT" } + }; + const std::map actionDesc_ = { + { TRANS_TO_INIT, "TRANS_TO_INIT" }, + { TRANS_TO_PREPARING, "TRANS_TO_PREPARING" }, + { TRANS_TO_READY, "TRANS_TO_READY" }, + { TRANS_TO_PLAYING, "TRANS_TO_PLAYING" }, + { TRANS_TO_PAUSE, "TRANS_TO_PAUSE" }, + { ACTION_PENDING, "ACTION_PENDING" }, + { ACTION_BUTT, "ACTION_BUTT" } + }; +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/internal/state_machine.cpp b/engine/player/internal/state_machine.cpp new file mode 100644 index 00000000..9607e203 --- /dev/null +++ b/engine/player/internal/state_machine.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "StateMachine" + +#include "state_machine.h" + +namespace OHOS { +namespace Media { +StateMachine::StateMachine(PlayExecutor& executor) + : Task("StateMachine"), intentSync_("fsmSync"), + curState_ (std::make_shared(executor, "InitState")), jobs_("StateMachineJobQue") +{ + AddState(curState_); + AddState(std::make_shared(executor, "PreparingState")); + AddState(std::make_shared(executor, "ReadyState")); + AddState(std::make_shared(executor, "PlayingState")); + AddState(std::make_shared(executor, "PauseState")); +} + +void StateMachine::Stop() +{ + MEDIA_LOG_D("Stop called."); + jobs_.SetActive(false); + Task::Stop(); +} + +void StateMachine::SetStateCallback(StateChangeCallback* callback) +{ + callback_ = callback; +} + +const std::string& StateMachine::GetCurrentState() const +{ + return curState_->GetName(); +} + +ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param) const +{ + return const_cast(this)->SendEvent(intent, param); +} + +ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param) +{ + SendEventAsync(intent, param); + constexpr int timeoutMs = 5000; + ErrorCode errorCode = ERROR_TIMEOUT; + if (!intentSync_.WaitFor(intent, timeoutMs, errorCode)) { + MEDIA_LOG_E("SendEvent timeout, intent: %d", static_cast(intent)); + } + return errorCode; +} + +ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param) const +{ + return const_cast(this)->SendEventAsync(intent, param); +} + +ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param) +{ + MEDIA_LOG_D("SendEventAsync, intent: %d", static_cast(intent)); + jobs_.Push([this, intent, param]() -> Action { return ProcessIntent(intent, param); }); + return SUCCESS; +} + +Action StateMachine::ProcessIntent(Intent intent, const Plugin::Any& param) +{ + MEDIA_LOG_D("ProcessIntent, curState: %s, intent: %d.", curState_->GetName().c_str(), intent); + OSAL::ScopedLock lock(mutex_); + lastIntent = intent; + ErrorCode rtv = SUCCESS; + Action nextAction = ACTION_BUTT; + std::tie(rtv, nextAction) = curState_->Execute(intent, param); + if (rtv == SUCCESS) { + rtv = ProcAction(nextAction); + } + OnIntentExecuted(intent, nextAction, rtv); + return (rtv == SUCCESS) ? nextAction : ACTION_BUTT; +} + +void StateMachine::DoTask() +{ + constexpr int timeoutMs = 100; + auto job = jobs_.Pop(timeoutMs); + if (!job) { + return; + } + auto action = job(); + switch (action) { + case ACTION_PENDING: + pendingJobs_.push(job); + break; + case TRANS_TO_INIT: + case TRANS_TO_READY: + case TRANS_TO_PREPARING: + case TRANS_TO_PLAYING: + case TRANS_TO_PAUSE: { + if (!pendingJobs_.empty()) { + job = pendingJobs_.front(); + pendingJobs_.pop(); + action = job(); + if (action == ACTION_PENDING) { + pendingJobs_.push(job); + } + } + break; + } + case ACTION_BUTT: + // fall through + default: + break; + } +} + +void StateMachine::AddState(const std::shared_ptr& state) +{ + states_[state->GetName()] = state; +} + +ErrorCode StateMachine::ProcAction(Action nextAction) +{ + std::shared_ptr nextState = nullptr; + switch (nextAction) { + case TRANS_TO_INIT: + nextState = states_["InitState"]; + break; + case TRANS_TO_PREPARING: + nextState = states_["PreparingState"]; + break; + case TRANS_TO_READY: + nextState = states_["ReadyState"]; + break; + case TRANS_TO_PLAYING: + nextState = states_["PlayingState"]; + break; + case TRANS_TO_PAUSE: + nextState = states_["PauseState"]; + break; + default: + break; + } + ErrorCode ret = SUCCESS; + if (nextState) { + ret = TransitionTo(nextState); + } + return ret; +} + +ErrorCode StateMachine::TransitionTo(const std::shared_ptr& state) +{ + if (state == nullptr) { + MEDIA_LOG_E("TransitionTo, nullptr for state"); + return NULL_POINTER_ERROR; + } + ErrorCode rtv = SUCCESS; + if (state != curState_) { + curState_->Exit(); + curState_ = state; + Action nextAction; + std::tie(rtv, nextAction) = curState_->Enter(lastIntent); + if (rtv == SUCCESS) { + rtv = ProcAction(nextAction); + } + if (callback_) { + callback_->OnStateChanged(curState_->GetName()); + } + } + return rtv; +} + +void StateMachine::OnIntentExecuted(Intent intent, Action action, ErrorCode result) +{ + MEDIA_LOG_D("OnIntentExecuted, curState: %s, intent: %d, action: %d, result: %d", curState_->GetName().c_str(), + static_cast(intent), static_cast(action), static_cast(result)); + if (action == ACTION_PENDING) { + return; + } + if (intent == PLAY) { + if (action == TRANS_TO_PLAYING) { + intentSync_.Notify(PLAY, result); + } + } else { + if (intent == NOTIFY_READY && action == TRANS_TO_PLAYING) { + intentSync_.Notify(PLAY, result); + } else { + intentSync_.Notify(intent, result); + } + } +} +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/player/internal/state_machine.h b/engine/player/internal/state_machine.h new file mode 100644 index 00000000..3821f7cc --- /dev/null +++ b/engine/player/internal/state_machine.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_STATE_MACHINE_H +#define HISTREAMER_HIPLAYER_STATE_MACHINE_H + +#include +#include +#include +#include + +#include "common/any.h" +#include "foundation/blocking_queue.h" +#include "foundation/error_code.h" +#include "foundation/log.h" +#include "osal/base/synchronizer.h" +#include "osal/thread/mutex.h" +#include "osal/thread/task.h" + +#include "play_executor.h" + +#include "init_state.h" +#include "pause_state.h" +#include "playing_state.h" +#include "prepare_state.h" +#include "ready_state.h" + +namespace OHOS { +namespace Media { +class StateChangeCallback { +public: + virtual ~StateChangeCallback() = default; + virtual void OnStateChanged(std::string state) = 0; +}; + +class StateMachine final : public OSAL::Task { +public: + explicit StateMachine(PlayExecutor& executor); + + ~StateMachine() override = default; + + void Stop() override; + + void SetStateCallback(StateChangeCallback* callback); + + const std::string& GetCurrentState() const; + + ErrorCode SendEvent(Intent intent, const Plugin::Any& param = {}); + + ErrorCode SendEvent(Intent intent, const Plugin::Any& param = {}) const; + + ErrorCode SendEventAsync(Intent intent, const Plugin::Any& param = {}); + + ErrorCode SendEventAsync(Intent intent, const Plugin::Any& param = {}) const; + +private: + using Job = std::function; + + Action ProcessIntent(Intent intent, const Plugin::Any& param); + + void DoTask() override; + + void AddState(const std::shared_ptr& state); + + ErrorCode ProcAction(Action nextAction); + + ErrorCode TransitionTo(const std::shared_ptr& state); + + void OnIntentExecuted(Intent intent, Action action, ErrorCode result); + + mutable OSAL::Mutex mutex_ {}; + mutable OSAL::Synchronizer intentSync_; + mutable std::shared_ptr curState_ {nullptr}; + mutable Intent lastIntent {INTENT_BUTT}; + std::map> states_ {}; + Media::BlockingQueue jobs_; + std::queue pendingJobs_ {}; + StateChangeCallback* callback_ {nullptr}; +}; +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/player/play_executor.h b/engine/player/play_executor.h new file mode 100644 index 00000000..1f31d91d --- /dev/null +++ b/engine/player/play_executor.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 MEDIA_PIPELINE_PLAYER_EXECUTOR_H +#define MEDIA_PIPELINE_PLAYER_EXECUTOR_H + +#include +#include "foundation/error_code.h" +#include "source.h" + +namespace OHOS { +namespace Media { +using MediaSource = OHOS::Media::Source; + +class PlayExecutor { +public: + virtual ~PlayExecutor() + { + } + + virtual ErrorCode PrepareFilters() + { + return SUCCESS; + } + + virtual ErrorCode DoSetSource(const std::shared_ptr&) const + { + return SUCCESS; + } + + virtual ErrorCode DoPlay() + { + return SUCCESS; + } + + virtual ErrorCode DoPause() + { + return SUCCESS; + } + + virtual ErrorCode DoResume() + { + return SUCCESS; + } + + virtual ErrorCode DoStop() + { + return SUCCESS; + } + + virtual ErrorCode DoSeek(int64_t msec) + { + (void)msec; + return SUCCESS; + } + + virtual ErrorCode DoOnReady() + { + return SUCCESS; + } + + virtual ErrorCode DoOnComplete() + { + return SUCCESS; + } + + virtual ErrorCode DoOnError(ErrorCode) + { + return SUCCESS; + } +}; +} // namespace Media +} // namespace OHOS + +#endif diff --git a/engine/plugin/common/any.h b/engine/plugin/common/any.h new file mode 100644 index 00000000..e3ca6779 --- /dev/null +++ b/engine/plugin/common/any.h @@ -0,0 +1,605 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_COMMON_ANY_H +#define HISTREAMER_PLUGIN_COMMON_ANY_H + +#if defined(__clang__) || defined(__GNUC__) +#define CPP_STANDARD __cplusplus +#elif defined(_MSC_VER) +#define CPP_STANDARD _MSVC_LANG +#endif + +#if CPP_STANDARD >= 201103L + +#include +#include +#include +#include + +#include "securec.h" + +namespace { +template +using decay_t = typename std::decay::type; + +template +using enable_if_t = typename std::enable_if::type; + +template +using conditional_t = typename std::conditional::type; + +template +using remove_cv_t = typename std::remove_cv::type; + +template +using remove_reference_t = typename std::remove_reference::type; +constexpr size_t STACK_STORAGE_SIZE = 2 * sizeof(void*); + +template +struct IsTrivialStackStorable { + static constexpr bool value = + alignof(T) <= alignof(max_align_t) && std::is_trivially_copyable::value && sizeof(T) <= STACK_STORAGE_SIZE; +}; + +template +struct IsStackStorable { + static constexpr bool value = alignof(T) <= alignof(max_align_t) && std::is_nothrow_move_constructible::value && + sizeof(T) <= STACK_STORAGE_SIZE; +}; + +template +struct IsValidCast { + static constexpr bool value = std::is_reference::value || std::is_copy_constructible::value; +}; +} // namespace +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief BadAnyCast exception, which is thrown when error occurs in AnyCast + * + * @since 1.0 + * @version 1.0 + */ +class BadAnyCast : public std::bad_cast { +public: + const char* what() const noexcept override + { + return "bad any cast"; + } +}; + +/** + * @brief This class describes a type-safe container for arbitrary type values which are copy constructible. + * + * @since 1.0 + * @version 1.0 + */ +class Any final { +public: + constexpr Any() noexcept + { + } + + Any(const Any& other) : functionTable_(other.functionTable_) + { + if (other.HasValue()) { + functionTable_->copy(storage_, other.storage_); + } + } + + Any(Any&& other) noexcept : functionTable_(other.functionTable_) + { + if (other.HasValue()) { + functionTable_->move(storage_, other.storage_); + other.functionTable_ = nullptr; + } + } + + /** + * constructor from right reference value with type of ValueType. + * + * @tparam Type ValueType is not the same as Any itself. The decay type of ValueType must be copy constructible. + * @param value content + */ + template , Any>::value && + std::is_copy_constructible>::value, + bool> = true> + Any(ValueType&& value) + { + DoEmplace>(std::forward(value)); + } + + Any& operator=(const Any& other) + { + *this = Any(other); + return *this; + } + + Any& operator=(Any&& other) noexcept + { + Reset(); + MoveFrom(std::forward(other)); + return *this; + } + + /** + * Assigns contents to Any. + * + * @tparam ValueType Type ValueType is not the same as Any itself. The decay type of ValueType must be copy + * constructible. + * @param value content + * @return + */ + template , Any>::value && + std::is_copy_constructible>::value, + bool> = true> + Any& operator=(ValueType&& value) + { + *this = Any(std::forward(value)); + return *this; + } + + ~Any() + { + Reset(); + } + + /** + * Emplace one content with type of ValueType into object. The content is constructed by args. + * + * @tparam ValueType The decay type of ValueType must be constructible from args and copy constructible. + * @tparam Args args type + * @param args args + * @return content with type of decay ValueType + */ + template , Args...>::value && + std::is_copy_constructible>::value, + bool> = true> + decay_t& Emplace(Args&&... args) + { + Reset(); + return DoEmplace>(std::forward(args)...); + } + + /** + * Emplace one content with type of ValueType into object. The content is constructed by il and args. + * + * @tparam ValueType type of conetent. The decay type of ValueType must be constructible from il and args and copy + * constructible + * @tparam U type of initializer list. + * @tparam Args type of other args + * @param il initializer list + * @param args args + * @return content with type of decay ValueType + */ + template , std::initializer_list&, Args...>::value && + std::is_copy_constructible>::value, + bool> = true> + decay_t& Emplace(std::initializer_list il, Args&&... args) + { + Reset(); + return DoEmplace>(il, std::forward(args)...); + } + + /** + * Destroy the inner content if exists. + */ + void Reset() noexcept + { + if (HasValue()) { + functionTable_->destroy(storage_); + storage_.trivialStack_.fill(0); + } + functionTable_ = nullptr; + } + + /** + * swap contents of two any objects + * + * @param other object to swap with + */ + void Swap(Any& other) noexcept + { + Any tmp(std::move(*this)); + *this = std::move(other); + other = std::move(tmp); + } + + /** + * Checks whether the object has one content. + * + * @return true if object has one content, otherwise false. + */ + bool HasValue() const noexcept + { + return IsFunctionTableValid(); + } + + /** + * Get tye type_info of object + * + * @return type info of object + */ + const std::type_info& Type() const noexcept + { + if (!HasValue()) { + return typeid(void); + } + return functionTable_->type(); + } + +private: + template + friend const T* AnyCast(const Any* operand) noexcept; + template + friend T* AnyCast(Any* operand) noexcept; + + union Storage { + using Stack = std::aligned_storage::value>::type; + using Heap = void*; + + std::array trivialStack_; + Stack nonTrivialStack_; + Heap heap_; + }; + + struct FunctionTable { + const std::type_info& (*type)() noexcept; + void (*destroy)(Storage&) noexcept; + void (*copy)(Storage&, const Storage&) noexcept; + void (*move)(Storage&, Storage&) noexcept; + void* (*getPtr)(Storage&) noexcept; + const void* (*getConstPtr)(const Storage&) noexcept; + }; + + template + struct TrivialStackFunctionTable { + static const std::type_info& Type() noexcept + { + return typeid(T); + } + + static void Destroy(Storage& storage) noexcept + { + reinterpret_cast(storage.trivialStack_.data())->~T(); + } + + static void Copy(Storage& dest, const Storage& source) noexcept + { + (void)memcpy_s(GetPtr(dest), STACK_STORAGE_SIZE, GetConstPtr(source), STACK_STORAGE_SIZE); + } + + static void Move(Storage& dest, Storage& source) noexcept + { + Copy(dest, source); + source.trivialStack_.fill(0); + } + + static const void* GetConstPtr(const Storage& storage) noexcept + { + return reinterpret_cast(storage.trivialStack_.data()); + } + + static void* GetPtr(Storage& storage) noexcept + { + return reinterpret_cast(storage.trivialStack_.data()); + } + }; + + template + struct StackFunctionTable { + static const std::type_info& Type() noexcept + { + return typeid(T); + } + + static void Destroy(Storage& storage) noexcept + { + reinterpret_cast(GetPtr(storage))->~T(); + } + + static void Copy(Storage& dest, const Storage& source) noexcept + { + new (reinterpret_cast(GetPtr(dest))) T(*reinterpret_cast(GetConstPtr(source))); + } + + static void Move(Storage& dest, Storage& source) noexcept + { + new (reinterpret_cast(GetPtr(dest))) T(std::move(*reinterpret_cast(GetPtr(source)))); + Destroy(source); + } + + static const void* GetConstPtr(const Storage& storage) noexcept + { + return reinterpret_cast(&storage.nonTrivialStack_); + } + + static void* GetPtr(Storage& storage) noexcept + { + return reinterpret_cast(&storage.nonTrivialStack_); + } + }; + + template + struct HeapFunctionTable { + static const std::type_info& Type() noexcept + { + return typeid(T); + } + + static void Destroy(Storage& storage) noexcept + { + delete reinterpret_cast(storage.heap_); + storage.heap_ = nullptr; + } + static void Copy(Storage& dest, const Storage& source) noexcept + { + dest.heap_ = new T(*reinterpret_cast(source.heap_)); + } + static void Move(Storage& dest, Storage& source) noexcept + { + dest.heap_ = source.heap_; + source.heap_ = nullptr; + } + static const void* GetConstPtr(const Storage& storage) noexcept + { + return storage.heap_; + } + static void* GetPtr(Storage& storage) noexcept + { + return storage.heap_; + } + }; + + template + static FunctionTable* GetFunctionTable() + { + using DecayedValueType = decay_t; + using DetailFunctionTable = + conditional_t::value, + TrivialStackFunctionTable, + conditional_t::value, + StackFunctionTable, HeapFunctionTable>>; + static FunctionTable table = { + .type = DetailFunctionTable::Type, + .destroy = DetailFunctionTable::Destroy, + .copy = DetailFunctionTable::Copy, + .move = DetailFunctionTable::Move, + .getPtr = DetailFunctionTable::GetPtr, + .getConstPtr = DetailFunctionTable::GetConstPtr, + }; + return &table; + } + + bool IsFunctionTableValid() const noexcept + { + return functionTable_ != nullptr; + } + + template + DecayedValueType& DoEmplace(Args&&... args) + { + functionTable_ = GetFunctionTable(); + DecayedValueType* ptr = nullptr; + if (IsTrivialStackStorable::value || IsStackStorable::value) { + ptr = reinterpret_cast(functionTable_->getPtr(storage_)); + new (ptr) DecayedValueType(std::forward(args)...); + } else { + storage_.heap_ = new DecayedValueType(std::forward(args)...); + ptr = reinterpret_cast(storage_.heap_); + } + return *ptr; + } + + void MoveFrom(Any&& other) noexcept + { + if (other.HasValue()) { + functionTable_ = other.functionTable_; + functionTable_->move(storage_, other.storage_); + other.Reset(); + } + } + + template + ValueType* Cast() noexcept + { + using DecayedValueType = decay_t; + if (!IsFunctionTableValid() || functionTable_->type() != typeid(DecayedValueType)) { + return nullptr; + } + return IsTrivialStackStorable::value + ? reinterpret_cast(storage_.trivialStack_.data()) + : (IsStackStorable::value + ? reinterpret_cast(&storage_.nonTrivialStack_) + : reinterpret_cast(storage_.heap_)); + } + template + const ValueType* Cast() const noexcept + { + using DecayedValueType = decay_t; + if (!IsFunctionTableValid() || functionTable_->type() != typeid(DecayedValueType)) { + return nullptr; + } + return IsTrivialStackStorable::value + ? reinterpret_cast(storage_.trivialStack_.data()) + : (IsStackStorable::value + ? reinterpret_cast(&storage_.nonTrivialStack_) + : reinterpret_cast(storage_.heap_)); + } + +private: + Storage storage_ {}; + FunctionTable* functionTable_ {nullptr}; +}; + +/** + * cast one Any pointer into ValueType pointer + * + * @tparam ValueType target value type + * @param operand any object + * @return nullptr if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, a pointer to the + * const value contained by operand. + */ +template +const ValueType* AnyCast(const Any* operand) noexcept +{ + static_assert(!std::is_void::value, "ValueType of any_cast must not be void"); + if (std::is_function::value || std::is_array::value || operand == nullptr) { + return nullptr; + } + return operand->Cast(); +} + +/** + * cast one Any pointer into ValueType pointer + * + * @tparam ValueType target value type + * @param operand any object + * @return nullptr if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, a pointer to the + * value contained by operand. + */ +template +ValueType* AnyCast(Any* operand) noexcept +{ + static_assert(!std::is_void::value, "ValueType of any_cast must not be void"); + if (std::is_function::value || std::is_array::value || operand == nullptr) { + return nullptr; + } + return operand->Cast(); +} + +/** + * cast one Any object into ValueType object + * + * @tparam ValueType target value type. It must match both conditions: + * 1. ValueType must be reference or constructible + * 2. Let U be remove_cv_t>, then std::is_constructible must be true + * @param operand any object + * @return throws BadAnyCast exception if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, + * one object of ValueType contained in Any. + */ +template +ValueType AnyCast(const Any& other) +{ + using U = remove_cv_t>; + static_assert(IsValidCast::value, "template argument must be a reference or has copy constructors"); + static_assert(std::is_constructible::value, + "any_cast(const any&) requires ValueType constructable from const " + "remove_cv_t>&"); + auto ptr = AnyCast(&other); + if (ptr == nullptr) { + throw BadAnyCast(); + } + return static_cast(*ptr); +} + +/** + * cast one Any object into ValueType object + * + * @tparam ValueType target value type. It must match both conditions: + * 1. ValueType must be reference or constructible + * 2. Let U be remove_cv_t>, then std::is_constructible must be true + * @param operand any object + * @return throws BadAnyCast exception if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, + * one object of ValueType contained in Any. + */ +template +ValueType AnyCast(Any& other) +{ + using U = remove_cv_t>; + static_assert(IsValidCast::value, "template argument must be a reference or has copy constructors"); + static_assert(std::is_constructible::value, + "any_cast(const any&) requires ValueType constructable from " + "remove_cv_t>&"); + auto ptr = AnyCast(&other); + if (ptr == nullptr) { + throw BadAnyCast(); + } + return static_cast(*ptr); +} + +/** + * cast one Any object into ValueType object + * + * @tparam ValueType target value type. It must match both conditions: + * 1. ValueType must be reference or constructible + * 2. Let U be remove_cv_t>, then std::is_constructible must be true + * @param operand any object + * @return throws BadAnyCast exception if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, + * one object of ValueType contained in Any. + */ +template +ValueType AnyCast(Any&& other) +{ + using U = remove_cv_t>; + static_assert(IsValidCast::value, "template argument must be a reference or has copy constructors"); + static_assert(std::is_constructible::value, + "any_cast(const any&) requires ValueType constructable from " + "remove_cv_t>"); + auto ptr = AnyCast(&other); + if (ptr == nullptr) { + throw BadAnyCast(); + } + return static_cast(std::move(*ptr)); +} + +/** + * Constructs Any object, whose content is constructed by args. The content type is T. + * + * @tparam T type of Any's content + * @tparam Args type of args + * @param args args used to construct the content + * @return Any object + */ +template +Any MakeAny(Args&&... args) +{ + Any tmp; + tmp.Emplace(std::forward(args)...); + return tmp; +} + +/** + * Constructs Any object, whose content is constructed by il and args. The content type is T. + * + * @tparam T type of Any's content + * @tparam U type of initializer list + * @tparam Args type of args + * @param il initializer list + * @param args args + * @return Any object + */ +template +Any MakeAny(std::initializer_list il, Args&&... args) +{ + Any tmp; + tmp.Emplace(il, std::forward(args)...); + return tmp; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif +namespace std { +inline void swap(OHOS::Media::Plugin::Any& lhs, OHOS::Media::Plugin::Any& rhs) noexcept +{ + lhs.Swap(rhs); +} +} // namespace std +#endif // HISTREAMER_PLUGIN_COMMON_ANY_H diff --git a/engine/plugin/common/plugin_audio_tags.h b/engine/plugin/common/plugin_audio_tags.h new file mode 100644 index 00000000..ef98a9a8 --- /dev/null +++ b/engine/plugin/common/plugin_audio_tags.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_COMMON_AUDIO_TAGS_H +#define HISTREAMER_PLUGIN_COMMON_AUDIO_TAGS_H + +#include + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @enum Audio Channel Masks + * + * A 64-bit integer with bits set for each channel. + * + * @since 1.0 + * @version 1.0 + */ +enum AudioChannelMasks : uint64_t { + FRONT_LEFT = 1ULL << 0U, + FRONT_RIGHT = 1ULL << 1U, + FRONT_CENTER = 1ULL << 2U, + LOW_FREQUENCY = 1ULL << 3U, + BACK_LEFT = 1ULL << 4U, + BACK_RIGHT = 1ULL << 5U, + FRONT_LEFT_OF_CENTER = 1ULL << 6U, + FRONT_RIGHT_OF_CENTER = 1ULL << 7U, + BACK_CENTER = 1ULL << 8U, + SIDE_LEFT = 1ULL << 9U, + SIDE_RIGHT = 1ULL << 10U, + TOP_CENTER = 1ULL << 11U, + TOP_FRONT_LEFT = 1ULL << 12U, + TOP_FRONT_CENTER = 1ULL << 13U, + TOP_FRONT_RIGHT = 1ULL << 14U, + TOP_BACK_LEFT = 1ULL << 15U, + TOP_BACK_CENTER = 1ULL << 16U, + TOP_BACK_RIGHT = 1ULL << 17U, + STEREO_LEFT = 1ULL << 29U, + STEREO_RIGHT = 1ULL << 30U, + WIDE_LEFT = 1ULL << 31U, + WIDE_RIGHT = 1ULL << 32U, +}; + + +/** + * @enum Audio Channel Layout + * + * Indicates that the channel order in which the user requests decoder output + * is the native codec channel order. + * + * @since 1.0 + * @version 1.0 + */ +enum struct AudioChannelLayout : uint64_t { + MONO = (AudioChannelMasks::FRONT_CENTER), + STEREO = (AudioChannelMasks::FRONT_LEFT | AudioChannelMasks::FRONT_RIGHT), + CH_2POINT1 = (STEREO | AudioChannelMasks::LOW_FREQUENCY), + CH_2_1 = (STEREO | AudioChannelMasks::BACK_CENTER), + SURROUND = (STEREO | AudioChannelMasks::FRONT_CENTER), + CH_3POINT1 = (SURROUND | AudioChannelMasks::LOW_FREQUENCY), + CH_4POINT0 = (SURROUND | AudioChannelMasks::BACK_CENTER), + CH_4POINT1 = (CH_4POINT0 | AudioChannelMasks::LOW_FREQUENCY), + CH_2_2 = (STEREO | AudioChannelMasks::SIDE_LEFT | AudioChannelMasks::SIDE_RIGHT), + QUAD = (STEREO | AudioChannelMasks::BACK_LEFT | AudioChannelMasks::BACK_RIGHT), + CH_5POINT0 = (SURROUND | AudioChannelMasks::SIDE_LEFT | AudioChannelMasks::SIDE_RIGHT), + CH_5POINT1 = (CH_5POINT0 | AudioChannelMasks::LOW_FREQUENCY), + CH_5POINT0_BACK = (SURROUND | AudioChannelMasks::BACK_LEFT | AudioChannelMasks::BACK_RIGHT), + CH_5POINT1_BACK = (CH_5POINT0_BACK | AudioChannelMasks::LOW_FREQUENCY), + CH_6POINT0 = (CH_5POINT0 | AudioChannelMasks::BACK_CENTER), + CH_6POINT0_FRONT = (CH_2_2 | AudioChannelMasks::FRONT_LEFT_OF_CENTER | + AudioChannelMasks::FRONT_RIGHT_OF_CENTER), + HEXAGONAL = (CH_5POINT0_BACK | AudioChannelMasks::BACK_CENTER), + CH_6POINT1 = (CH_5POINT1 | AudioChannelMasks::BACK_CENTER), + CH_6POINT1_BACK = (CH_5POINT1_BACK | AudioChannelMasks::BACK_CENTER), + CH_6POINT1_FRONT = (CH_6POINT0_FRONT | AudioChannelMasks::LOW_FREQUENCY), + CH_7POINT0 = (CH_5POINT0 | AudioChannelMasks::BACK_LEFT | AudioChannelMasks::BACK_RIGHT), + CH_7POINT0_FRONT = (CH_5POINT0 | AudioChannelMasks::FRONT_LEFT_OF_CENTER | + AudioChannelMasks::FRONT_RIGHT_OF_CENTER), + CH_7POINT1 = (CH_5POINT1 | AudioChannelMasks::BACK_LEFT | AudioChannelMasks::BACK_RIGHT), + CH_7POINT1_WIDE = (CH_5POINT1 | AudioChannelMasks::FRONT_LEFT_OF_CENTER | + AudioChannelMasks::FRONT_RIGHT_OF_CENTER), + CH_7POINT1_WIDE_BACK = (CH_5POINT1_BACK | AudioChannelMasks::FRONT_LEFT_OF_CENTER | + AudioChannelMasks::FRONT_RIGHT_OF_CENTER), + OCTAGONAL = (CH_5POINT0 | AudioChannelMasks::BACK_LEFT | AudioChannelMasks::BACK_CENTER | + AudioChannelMasks::BACK_RIGHT), + HEXADECAGONAL = (OCTAGONAL | AudioChannelMasks::WIDE_LEFT | AudioChannelMasks::WIDE_RIGHT | + AudioChannelMasks::TOP_BACK_LEFT | AudioChannelMasks::TOP_BACK_RIGHT | + AudioChannelMasks::TOP_BACK_CENTER | AudioChannelMasks::TOP_FRONT_CENTER | + AudioChannelMasks::TOP_FRONT_LEFT | AudioChannelMasks::TOP_FRONT_RIGHT), + STEREO_DOWNMIX = (AudioChannelMasks::STEREO_LEFT | AudioChannelMasks::STEREO_RIGHT), +}; + +/** + * @enum Audio sample formats + * + * 'S' is signed, 'U' is unsigned, and 'F' is a floating-point number. + * 'P' is planes, default is interleaved. + * + * @since 1.0 + * @version 1.0 + */ +enum struct AudioSampleFormat : uint8_t { + /* 8 bit */ + S8, U8, S8P, U8P, + /* 16 bit */ + S16, U16, S16P, U16P, + /* 32 bit */ + S32, U32, S32P, U32P, + /* 64 bit */ + S64, U64, S64P, U64P, + /* float double */ + F32, F32P, F64, F64P, +}; + +/** + * @enum Audio AAC Profile。 + * + * AAC mode type. Note that the term profile is used with the MPEG-2 + * standard and the term object type and profile is used with MPEG-4 + * + * @since 1.0 + * @version 1.0 + */ +enum struct AudioAacProfile : uint8_t { + NONE = 0, ///< Null, not used + MAIN = 1, ///< AAC Main object + LC, ///< AAC Low Complexity object (AAC profile) + SSR, ///< AAC Scalable Sample Rate object + LTP, ///< AAC Long Term Prediction object + HE, ///< AAC High Efficiency (object type SBR, HE-AAC profile) + SCALABLE, ///< AAC Scalable object + ERLC = 17, ///< ER AAC Low Complexity object (Error Resilient AAC-LC) + ER_SCALABLE = 20, ///< ER AAC scalable object + LD = 23, ///< AAC Low Delay object (Error Resilient) + HE_PS = 29, ///< AAC High Efficiency with Parametric Stereo coding (HE-AAC v2, object type PS) + ELD = 39, ///< AAC Enhanced Low Delay. NOTE: Pending Khronos standardization + XHE = 42, ///< extended High Efficiency AAC. NOTE: Pending Khronos standardization +}; + +/** + * @enum Audio AAC Stream Format + * + * @since 1.0 + * @version 1.0 + */ +enum struct AudioAacStreamFormat : uint8_t { + MP2ADTS = 0, ///< AAC Audio Data Transport Stream 2 format + MP4ADTS, ///< AAC Audio Data Transport Stream 4 format + MP4LOAS, ///< AAC Low Overhead Audio Stream format + MP4LATM, ///< AAC Low overhead Audio Transport Multiplex + ADIF, ///< AAC Audio Data Interchange Format + MP4FF, ///< AAC inside MPEG-4/ISO File Format + RAW, ///< AAC Raw Format +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_COMMON_AUDIO_TAGS_H diff --git a/engine/plugin/common/plugin_buffer.cpp b/engine/plugin/common/plugin_buffer.cpp new file mode 100644 index 00000000..dcae1870 --- /dev/null +++ b/engine/plugin/common/plugin_buffer.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +template +static constexpr T AlignUp(T num, U alignment) +{ + return (alignment > 0) ? ((num + static_cast(alignment) - 1) & (~(static_cast(alignment) - 1))) : num; +} + +Memory::Memory(size_t capacity, std::shared_ptr bufData, size_t align) + : capacity(capacity), alignment(align), offset(0), size(0), allocator(nullptr), addr(std::move(bufData)) +{ +} + +Memory::Memory(size_t capacity, std::shared_ptr allocator, size_t align) + : capacity(capacity), alignment(align), size(0), allocator(std::move(allocator)), addr(nullptr) +{ + size_t allocSize = align ? (capacity + align - 1) : capacity; + if (this->allocator) { + addr = std::shared_ptr(static_cast(this->allocator->Alloc(allocSize)), + [this](uint8_t* ptr) { this->allocator->Free((void*)ptr); }); + } else { + addr = std::shared_ptr(new uint8_t[allocSize], std::default_delete()); + } + offset = static_cast(AlignUp((uintptr_t)addr.get(), (uintptr_t)align) - (uintptr_t)addr.get()); +} + +size_t Memory::GetCapacity() +{ + return capacity; +} + +void Memory::Reset() +{ + this->size = 0; +} + +size_t Memory::Write(const uint8_t* in, size_t writeSize, size_t position) +{ + size_t start = 0; + if (position == std::string::npos) { + start = size; + } else { + start = std::min(position, capacity); + } + size_t length = std::min(writeSize, capacity - start); + if (memcpy_s(GetRealAddr() + start, length, in, length) != EOK) { + return 0; + } + size = start + length; + return length; +} + +size_t Memory::Read(uint8_t* out, size_t readSize, size_t position) +{ + size_t start = 0; + if (position != std::string::npos) { + start = std::min(position, capacity); + } + size_t length = std::min(readSize, size); + if (memcpy_s(out, length, GetRealAddr() + start, length) != EOK) { + return 0; + } + return length; +} + +const uint8_t* Memory::GetReadOnlyData(size_t position) +{ + if (position > capacity) { + return nullptr; + } + return GetRealAddr() + position; +} + +uint8_t* Memory::GetWritableData(size_t writeSize, size_t position) +{ + if (position + writeSize > capacity) { + return nullptr; + } + uint8_t* ptr = GetRealAddr() + position; + size = (writeSize + position); + return ptr; +} + +size_t Memory::GetSize() +{ + return size; +} + +uint8_t* Memory::GetRealAddr() const +{ + return addr.get() + offset; +} + +BufferMeta::BufferMeta(BufferMetaType type) : type(type) +{ +} + +ValueType BufferMeta::GetMeta(Tag tag) +{ + if (tags) { + return (*tags.get())[tag]; + } + return ValueType(); +} + +void BufferMeta::SetMeta(Tag tag, ValueType value) +{ + if (!tags) { + tags = std::make_shared(); + } + (*tags.get())[tag] = value; +} + +BufferMetaType BufferMeta::GetType() const +{ + return type; +} + +Buffer::Buffer(BufferMetaType type) : streamID(0), pts(0), dts(0), duration(0), flag (0), meta() +{ + if (type == BufferMetaType::AUDIO) { + meta = std::shared_ptr(new AudioBufferMeta()); + } else if (type == BufferMetaType::VIDEO) { + meta = std::shared_ptr(new VideoBufferMeta()); + } +} + +std::shared_ptr Buffer::CreateDefaultBuffer(BufferMetaType type, size_t capacity, + std::shared_ptr allocator, size_t align) +{ + auto buffer = std::make_shared(type); + std::shared_ptr memory = std::shared_ptr(new Memory(capacity, allocator, align)); + buffer->data.push_back(memory); + return buffer; +} + +std::shared_ptr Buffer::WrapMemory(uint8_t* data, size_t capacity, size_t size) +{ + auto memory = std::shared_ptr(new Memory(capacity, std::shared_ptr(data, [](void* ptr) {}))); + memory->size = size; + this->data.push_back(memory); + return memory; +} + +std::shared_ptr Buffer::WrapMemoryPtr(std::shared_ptr data, size_t capacity, size_t size) +{ + auto memory = std::shared_ptr(new Memory(capacity, data)); + memory->size = size; + this->data.push_back(memory); + return memory; +} + +std::shared_ptr Buffer::AllocMemory(std::shared_ptr allocator, size_t capacity, size_t align) +{ + std::shared_ptr memory = std::shared_ptr(new Memory(capacity, allocator, align)); + data.push_back(memory); + return memory; +} + +uint32_t Buffer::GetMemoryCount() +{ + return data.size(); +} + +std::shared_ptr Buffer::GetMemory(uint32_t index) +{ + if (data.size() <= index) { + return nullptr; + } + return data[index]; +} + +std::shared_ptr Buffer::GetBufferMeta() +{ + return meta; +} + +bool Buffer::IsEmpty() +{ + return data.empty(); +} + +void Buffer::Reset() +{ + data[0]->Reset(); + streamID = 0; + pts = 0; + dts = 0; + duration = 0; + flag = 0; + BufferMetaType type = meta->GetType(); + meta.reset(); + if (type == BufferMetaType::AUDIO) { + meta = std::shared_ptr(new AudioBufferMeta()); + } else if (type == BufferMetaType::VIDEO) { + meta = std::shared_ptr(new VideoBufferMeta()); + } +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/common/plugin_buffer.h b/engine/plugin/common/plugin_buffer.h new file mode 100644 index 00000000..736aff86 --- /dev/null +++ b/engine/plugin/common/plugin_buffer.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_COMMON_BUFFER_H +#define HISTREAMER_PLUGIN_COMMON_BUFFER_H + +#include +#include + +#include "plugin_tags.h" +#include "plugin_types.h" +#include "plugin_audio_tags.h" +#include "plugin_video_tags.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/// End of Stream Buffer Flag +#define BUFFER_FLAG_EOS 0x00000001 + +/** + * @brief Memory allocator, which is provided by the plugin implementer. + * + * @since 1.0 + * @version 1.0 + */ +struct Allocator { + virtual ~Allocator() = default; + /** + * @brief Allocates a buffer using the specified size + * . + * @param size Allocation parameters. + * @return Pointer of the allocated buffer. + */ + virtual void* Alloc(size_t size) = 0; + + /** + * @brief Frees a buffer. + * Buffer handles returned by Alloc() must be freed with this function when no longer needed. + * + * @param ptr Pointer of the allocated buffer. + */ + virtual void Free(void* ptr) = 0; +}; + +/** + * @enum Buffer Meta Type + * + * @since 1.0 + * @version 1.0 + */ +enum struct BufferMetaType : uint32_t { + AUDIO, ///< Meta used to describe audio data + VIDEO, ///< Meta used to describe video data +}; + +/** +* @brief Memory description. Only manager the basic memory information. +* +* here is the memory layout. +* | capacity | +* |------------------------------------------| +* | buffer size | +* |-------------------------| +* addr offset buffer end +* +---------+-------------------------+----------------+ +* |*********| used buffer | unused buffer | +* +---------+-------------------------+----------------+ +* +* operate position: +* position +* +----------------+ +* +* @since 1.0 +* @version 1.0 +*/ +class Memory { +public: + /// Destructor + ~Memory() = default; + + // Todo: Add the documentation description. + size_t GetCapacity(); + + size_t GetSize(); + + const uint8_t* GetReadOnlyData(size_t position = 0); + + uint8_t *GetWritableData(size_t size, size_t position = 0); + + size_t Write(const uint8_t* in, size_t size, size_t position = std::string::npos); + + size_t Read(uint8_t* out, size_t size, size_t position = std::string::npos); + + void Reset(); + +private: + /** + * Create objects based on the external memory, use shared pointers, + * the allocation and release of memory are managed externally. + * + * @param capacity Allocated memory size. + * @param bufData External memory. + * @param align The alignment of the memory. + */ + Memory(size_t capacity, std::shared_ptr bufData, size_t align = 1); + + /** + * Allocates memory by the specified allocator. + * Allocation and release are the responsibility of the external allocator. + * + * @param capacity Allocated memory size. + * @param allocator External allocator. + * @param align The alignment of the memory. + */ + explicit Memory(size_t capacity, std::shared_ptr allocator = nullptr, size_t align = 1); + + /** + * Get real memory address, it is addr + offset, the offset is calculated according to alignment. + */ + uint8_t *GetRealAddr() const; + +private: + /// Allocated memory size. + size_t capacity; + + /// The alignment of the memory. + __attribute__((unused)) size_t alignment; + + /// Offset of the buffer address to make sure access acording to alignment. + size_t offset {0}; + + /// Valid data size + size_t size; + + /// Externally specified allocator, optional. + std::shared_ptr allocator; + + /// Allocated memory address. + std::shared_ptr addr; + + friend class Buffer; +}; + +/** + * @brief Buffer Meta. + * Base class that describes various media metadata. + * + * @since 1.0 + * @version 1.0 + */ +class BufferMeta { +public: + /// Destructor + virtual ~BufferMeta() = default; + + ValueType GetMeta(Tag tag); + + void SetMeta(Tag tag, ValueType value); + + BufferMetaType GetType() const; + +protected: + /// Constructor + explicit BufferMeta(BufferMetaType type); + +private: + BufferMetaType type; + + /// Buffer metadata information of the buffer, which is represented by the key-value pair of the tag. + std::shared_ptr tags {}; +}; + +/** + * @brief Audio buffer metadata. + * + * Buffer metadata describing how data is laid out inside the buffer. + * + * @since 1.0 + * @version 1.0 + */ +class AudioBufferMeta : public BufferMeta { +public: + /// Destructor + ~AudioBufferMeta() = default; + + /// the number of valid samples in the buffer + size_t samples {0}; + + /// Audio sample formats + AudioSampleFormat sampleFormat {AudioSampleFormat::S8}; + + /// the audio sample rate + uint32_t sampleRate {0}; + + /// the number of channels + uint32_t channels {0}; + + /// the number bytes for one frame, this is the size of one sample * channels + uint32_t bytesPreFrame {0}; + + /// Indicates that the channel order. + AudioChannelLayout channelLayout {AudioChannelLayout::MONO}; + + /// the offsets (in bytes) where each channel plane starts in the buffer. + std::vector offsets {}; + +private: + /// Constructor + AudioBufferMeta() : BufferMeta(BufferMetaType::AUDIO) {} + + friend class Buffer; +}; + +/** + * @brief Video buffer metadata. + * + * Extra buffer metadata describing video properties. + * + * @since 1.0 + * @version 1.0 + */ +class VideoBufferMeta : public BufferMeta { +public: + /// Destructor + ~VideoBufferMeta() = default; + + /// describing video formats. + VideoPixelFormat videoPixelFormat {VideoPixelFormat::UNKNOWN}; + + /// identifier of the frame。 + uint32_t id {0}; + + /// the video width. + uint32_t width {0}; + + /// the video height. + uint32_t height {0}; + + /// the number of planes in the image. + uint32_t planes {0}; + + /// array of strides for the planes. + std::vector stride {}; + + /// array of offsets for the planes. + std::vector offset {}; + +private: + /// Constructor + VideoBufferMeta() : BufferMeta(BufferMetaType::VIDEO) {} + + friend class Buffer; +}; + +/** +* @brief Buffer base class. +* Contains the data storage and metadata information of the buffer (buffer description information). +* +* @since 1.0 +* @version 1.0 +*/ +class Buffer { +public: + /// Construct an empty buffer. + explicit Buffer(BufferMetaType type = BufferMetaType::AUDIO); + + /// Destructor + ~Buffer() = default; + + static std::shared_ptr CreateDefaultBuffer(BufferMetaType type, size_t capacity, + std::shared_ptr allocator = nullptr, + size_t align = 1); + + std::shared_ptr WrapMemory(uint8_t* data, size_t capacity, size_t size); + + std::shared_ptr WrapMemoryPtr(std::shared_ptr data, size_t capacity, size_t size); + + std::shared_ptr AllocMemory(std::shared_ptr allocator, size_t capacity, size_t align = 1); + + uint32_t GetMemoryCount(); + + std::shared_ptr GetMemory(uint32_t index = 0); + + std::shared_ptr GetBufferMeta(); + + void Reset(); + + /// no memory in the buffer. + bool IsEmpty(); + + /// stream index. + uint32_t streamID; + + /// presentation timestamp of the buffer. + uint64_t pts; + + /// decoding timestamp of the buffer. + uint64_t dts; + + /// duration in time of the buffer data. + uint64_t duration; + + /// flag of the buffer, which is used to record extra information. + /// @see BUFFER_FLAG_EOS + uint64_t flag; + +private: + /// Data described by this buffer. + std::vector> data {}; + + /// The audio buffer meta information. + std::shared_ptr meta; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_COMMON_BUFFER_H diff --git a/engine/plugin/common/plugin_caps.h b/engine/plugin/common/plugin_caps.h new file mode 100644 index 00000000..b10cc704 --- /dev/null +++ b/engine/plugin/common/plugin_caps.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_COMMON_CAPS_H +#define HISTREAMER_PLUGIN_COMMON_CAPS_H + +#include +#include +#include "plugin_tags.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/// Indicates that the available capability type is an fixed value. +template using FixedCapability = T; + +/// Indicates that the available capability type is an interval value. +template using IntervalCapability = std::pair; + +/// Indicates that the available capability types are discrete values. +template using DiscreteCapability = std::vector; + +/** + * @brief The Capability describes the input and output capabilities of the plugin. + * + * It is basically a set of tags attached to the mime-type in order to + * describe the mime-type more closely. + * + * @since 1.0 + * @version 1.0 + */ +struct Capability { + /** + * @enum Capability ID is used to describe plugin capabilities or support capability matching. + * All Capability ID must come from Tag. + * + * For details about the definition and usage, to see enum Tag in file plugin_tags.h. + * + * @since 1.0 + * @version 1.0 + */ + enum struct Key : uint32_t { + AUDIO_SAMPLE_RATE = static_cast(Tag::AUDIO_SAMPLE_RATE), + AUDIO_CHANNELS = static_cast(Tag::AUDIO_CHANNELS), + AUDIO_CHANNEL_LAYOUT = static_cast(Tag::AUDIO_CHANNEL_LAYOUT), + AUDIO_SAMPLE_FORMAT = static_cast(Tag::AUDIO_SAMPLE_FORMAT), + AUDIO_MPEG_VERSION = static_cast(Tag::AUDIO_MPEG_VERSION), + AUDIO_MPEG_LAYER = static_cast(Tag::AUDIO_MPEG_LAYER), + AUDIO_AAC_PROFILE = static_cast(Tag::AUDIO_AAC_PROFILE), + AUDIO_AAC_LEVEL = static_cast(Tag::AUDIO_AAC_LEVEL), + AUDIO_AAC_STREAM_FORMAT = static_cast(Tag::AUDIO_AAC_STREAM_FORMAT), + }; + + /// Used to store the capability in the key-value format. + using KeyMap = std::map; + + /// default constructor + Capability() = default; + + /** + * @brief constructor one capability with mime of m + * + * @param m mime string + */ + explicit Capability(std::string m):mime(std::move(m)){} + + /** + * @brief Append one fix key into KeyMap + * + * @tparam T type of value + * @param key Capability::Key + * @param val value + * @return reference of object + */ + template + Capability& AppendFixedKey(Key key, const T& val) + { + keys[key] = val; + return *this; + } + + /** + * @brief Append one interval key i.e. [rangeStart, rangeEnd] into KeyMap + * + * @tparam T type of value + * @param key Capability::Key + * @param rangeStart range start + * @param rangeEnd rang end + * @return reference of object + */ + template + Capability& AppendIntervalKey(Key key, const T& rangeStart, const T& rangeEnd) + { + keys[key] = std::make_pair(rangeStart, rangeEnd); + return *this; + } + + /** + * @brief Append one discrete key i.e. {val1, val2, ....} into KeyMap + * + * @tparam T type of value + * @param key Capability::Key + * @param discreteValues values + * @return reference of object + */ + template + Capability& AppendDiscreteKeys(Key key, DiscreteCapability discreteValues) + { + keys[key] = std::move(discreteValues); + return *this; + } + + /** + * @brief set mime of this capability + * + * @param val mime value + * @return reference of object + */ + Capability& SetMime(std::string val) + { + mime = std::move(val); + return *this; + } + + /// mime of capability + std::string mime; + + /// storage map of Capability::Key and values + KeyMap keys; +}; + +/// A collection of multiple capabilities +using CapabilitySet = std::vector; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_COMMON_CAPS_H diff --git a/engine/plugin/common/plugin_tags.h b/engine/plugin/common/plugin_tags.h new file mode 100644 index 00000000..7343900b --- /dev/null +++ b/engine/plugin/common/plugin_tags.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_COMMON_TAGS_H +#define HISTREAMER_PLUGIN_COMMON_TAGS_H + +#include +#include +#include "any.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +enum TagSection : uint8_t { + REGULAR = 1, + MEDIA = 2, + AUDIO_UNIVERSAL = 3, + AUDIO_SPECIFIC = 4, + VIDEO_UNIVERSAL = 5, + VIDEO_SPECIFIC = 6, + MAX_SECTION = 64 +}; + +enum AudioFormat : uint8_t { + MPEG = 1, + AAC, +}; + +enum VideoFormat : uint8_t { + H264 = 1, +}; + +#define MAKE_AUDIO_SPECIFIC_START(format) (SECTION_AUDIO_SPECIFIC_START | ((format) << 8U)) + +/** + * @brief Tag is a key-value pair used to settings or transfer information. + * + * The "key" member:An uint32_t index, defined as an enumerated type. + * Tag Index consisting of the following fragments: + * - reserved field + * - vendor extensions + * - section (regular, audio, video ...) + * - addition index + * + * layout: + * +----------+---------+--------+----------------+ + * | reserved | section | vendor | addition index | + * +----------+---------+--------+----------------+ + * bit: 31 ... 22 21 ... 16 15 14 ............ 0 + * + * The "value" member: Different tag have different value types, + * which is defined in the plug-in interface. + * + * @since 1.0 + * @version 1.0 + */ +enum struct Tag : uint32_t { + INVALID = 0, + SECTION_REGULAR_START = TagSection::REGULAR << 16U, // regular tag + SECTION_MEDIA_START = TagSection::MEDIA << 16U, // media tag + SECTION_AUDIO_UNIVERSAL_START = TagSection::AUDIO_UNIVERSAL << 16U, // audio universal tag + SECTION_AUDIO_SPECIFIC_START = TagSection::AUDIO_SPECIFIC << 16U, // audio specific tag + SECTION_VIDEO_UNIVERSAL_START = TagSection::VIDEO_UNIVERSAL << 16U, // video universal tag + SECTION_VIDEO_SPECIFIC_START = TagSection::VIDEO_SPECIFIC << 16U, // video specific tag + + /* -------------------- regular tag -------------------- */ + MIME = SECTION_REGULAR_START + 1, // string + STREAM_INDEX, // uint32_t + REQUIRED_OUT_BUFFER_CNT, // uint32_t required buffer count of plugin; read only tag + + /* -------------------- media tag -------------------- */ + MEDIA_TITLE = SECTION_MEDIA_START + 1, // string + MEDIA_ARTIST, // string + MEDIA_LYRICIST, // string + MEDIA_ALBUM, // string + MEDIA_ALBUM_ARTIST, // string + MEDIA_DATE, // string, format:YYYY-MM-DD + MEDIA_COMMENT, // string + MEDIA_GENRE, // string + MEDIA_COPYRIGHT, // string + MEDIA_LANGUAGE, // string + MEDIA_DESCRIPTION, // string + MEDIA_LYRICS, // string + MEDIA_DURATION, // uint64_t + MEDIA_FILE_SIZE, // uint64_t + MEDIA_BITRATE, // int64_t + MEDIA_FILE_EXTENSION, // string + MEDIA_CODEC_CONFIG, // vector, e.g. AudioSpecificConfig for mp4 + MEDIA_POSITION, ///< uint64_t : The byte position within media stream/file + + /* -------------------- audio universal tag -------------------- */ + AUDIO_CHANNELS = SECTION_AUDIO_UNIVERSAL_START + 1, // uint32_t + AUDIO_CHANNEL_LAYOUT, // AudioChannelLayout + AUDIO_SAMPLE_RATE, // uint32_t + AUDIO_SAMPLE_FORMAT, // AudioSampleFormat + AUDIO_SAMPLE_PRE_FRAME, // uint32_t + + /* -------------------- audio specific tag -------------------- */ + AUDIO_SPECIFIC_MPEG_START = MAKE_AUDIO_SPECIFIC_START(AudioFormat::MPEG), + AUDIO_MPEG_VERSION, // uint32_t + AUDIO_MPEG_LAYER, // uint32_t + + AUDIO_SPECIFIC_AAC_START = MAKE_AUDIO_SPECIFIC_START(AudioFormat::AAC), + AUDIO_AAC_PROFILE, // AudioAacProfile + AUDIO_AAC_LEVEL, // uint32_t + AUDIO_AAC_STREAM_FORMAT, // AudioAacStreamFormat + + /* -------------------- video universal tag -------------------- */ + VIDEO_WIDTH = SECTION_VIDEO_UNIVERSAL_START + 1, // uint32_t + VIDEO_HEIGHT, // uint32_t + VIDEO_PIXEL_FORMAT, // uint32_t +}; + +using ValueType = Any; + +/** + * The tag content is stored in key-value format. + */ +using TagMap = std::map; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_COMMON_TAGS_H diff --git a/engine/plugin/common/plugin_types.h b/engine/plugin/common/plugin_types.h new file mode 100644 index 00000000..1c047cd2 --- /dev/null +++ b/engine/plugin/common/plugin_types.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_TYPES_H +#define HISTREAMER_PLUGIN_TYPES_H + +#include +#include +#include +#include +#include "common/any.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @enum Plugin running state. + * + * @since 1.0 + * @version 1.0 + */ +enum struct State : int32_t { + CREATED = 0, ///< Indicates the status of the plugin when it is constructed. + ///< The plug-in will not be restored in the entire life cycle. + INITIALIZED = 1, ///< Plugin global resource initialization completion status. + PREPARED = 2, ///< Status of parameters required for plugin running. + RUNNING = 3, ///< The system enters the running state after call start(). + PAUSED = 4, ///< Plugin temporarily stops processing data. This state is optional. + DESTROYED = -1, ///< Plugin destruction state. In this state, all resources are released. + INVALID = -2, ///< An error occurs in any state and the plugin enters the invalid state. +}; + +/** + * @enum Enumerates types of Seek Mode. + * + * @brief Seek modes, Options that SeekTo() behaviour. + * + * @since 1.0 + * @version 1.0 + */ +enum struct SeekMode : uint32_t { + FORWARD, ///< Seeks forwards for the keyframe closest to specified position + BACKWARD, ///< Seeks backwards for the keyframe closest to specified position + BYTE, ///< Seeks based on position in bytes + ANY, ///< Seeks to any frame, even non-keyframes + FRAME, ///< Seeks seeking based on frame number +}; + +/** + * @enum Api Return Status. + * + * @since 1.0 + * @version 1.0 + */ +enum struct Status : int32_t { + END_OF_STREAM = 1, ///< Read source when end of stream + OK = 0, ///< The execution result is correct. + NO_ERROR = OK, ///< Same as Status::OK + ERROR_UNKNOWN = -1, ///< An unknown error occurred. + ERROR_ALREADY_EXISTS = -2, ///< The plugin already exists, usually occurs when in plugin registered. + ERROR_INCOMPATIBLE_VERSION = + -3, ///< Incompatible version, may occur during plugin registration or function calling. + ERROR_NO_MEMORY = -4, ///< The system memory is insufficient. + ERROR_WRONG_STATE = -5, ///< The function is called in an invalid state. + ERROR_UNIMPLEMENTED = -6, ///< This method or interface is not implemented. + ERROR_INVALID_PARAMETER = -7, ///< The plugin does not support this parameter. + ERROR_INVALID_DATA = -8, ///< The value is not in the valid range. + ERROR_MISMATCHED_TYPE = -9, ///< Mismatched data type + ERROR_TIMED_OUT = -10, ///< Operation timeout. + ERROR_UNSUPPORTED_FORMAT = -11, ///< The plugin not support this format/name. + ERROR_NOT_ENOUGH_DATA = -12, ///< Not enough data when read from source. +}; + +/** + * @enum Plugin Type. + * + * @since 1.0 + * @version 1.0 + */ +enum struct PluginType : int32_t { + INVALID_TYPE = -1, ///< Invalid plugin + SOURCE = 1, ///< reference SourcePlugin + DEMUXER, ///< reference DemuxerPlugin + CODEC, ///< reference CodecPlugin + AUDIO_SINK, ///< reference AudioSinkPlugin + VIDEO_SINK, ///< reference VideoSinkPlugin +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_TYPES_H diff --git a/engine/plugin/common/plugin_video_tags.h b/engine/plugin/common/plugin_video_tags.h new file mode 100644 index 00000000..9f2c604f --- /dev/null +++ b/engine/plugin/common/plugin_video_tags.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_COMMON_VIDEO_TAGS_H +#define HISTREAMER_PLUGIN_COMMON_VIDEO_TAGS_H + +#include + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @enum Video Pixel Format. + * + * @since 1.0 + * @version 1.0 + */ +enum struct VideoPixelFormat : uint32_t { + UNKNOWN, + YUV410P, ///< planar YUV 4:1:0, 1 Cr & Cb sample per 4x4 Y samples + YUV411P, ///< planar YUV 4:1:1, 1 Cr & Cb sample per 4x1 Y samples + YUV420P, ///< planar YUV 4:2:0, 1 Cr & Cb sample per 2x2 Y samples + NV12, ///< semi-planar YUV 4:2:0, UVUV... + NV21, ///< semi-planar YUV 4:2:0, VUVU... + YUYV422, ///< packed YUV 4:2:2, Y0 Cb Y1 Cr + YUV422P, ///< planar YUV 4:2:2, 1 Cr & Cb sample per 2x1 Y samples + YUV444P, ///< planar YUV 4:4:4, 1 Cr & Cb sample per 1x1 Y samples + RGB24, ///< packed RGB 8:8:8, RGBRGB... + BGR24, ///< packed RGB 8:8:8, BGRBGR... + PAL8, ///< 8 bit with AV_PIX_FMT_RGB32 palette + GRAY8, ///< Y + MONOWHITE, ///< Y, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb + MONOBLACK, ///< Y, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb + YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG) + YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG) + YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG) +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_COMMON_AUDIO_TAGS_H diff --git a/engine/plugin/core/all_plugin_static.h b/engine/plugin/core/all_plugin_static.h new file mode 100644 index 00000000..cd3734c6 --- /dev/null +++ b/engine/plugin/core/all_plugin_static.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_ALL_PLUGIN_STATIC_H +#define HISTREAMER_PLUGIN_ALL_PLUGIN_STATIC_H + +#include "interface/plugin_base.h" + +#define PLUGIN_REGISTER_STATIC_DECLARE(name) \ + extern "C" OHOS::Media::Plugin::Status PLUGIN_PASTE(register_, \ + name)(std::shared_ptr reg); \ + extern "C" OHOS::Media::Plugin::Status PLUGIN_PASTE(unregister_, name)(); + +#define REGISTER_STATIC(name, reg) PLUGIN_PASTE(register_, name)(reg) +#define UNREGISTER_STATIC(name) PLUGIN_PASTE(unregister_, name)() + +PLUGIN_REGISTER_STATIC_DECLARE(FileSource); +PLUGIN_REGISTER_STATIC_DECLARE(StreamSource); +PLUGIN_REGISTER_STATIC_DECLARE(FFmpegDemuxer) +PLUGIN_REGISTER_STATIC_DECLARE(FFmpegAudioDecoders); +#ifdef VIDEO_SUPPORT +PLUGIN_REGISTER_STATIC_DECLARE(FFmpegVideoDecoders); +#endif +#ifdef HI3516DV300 +PLUGIN_REGISTER_STATIC_DECLARE(HdiAuSink); +#else +PLUGIN_REGISTER_STATIC_DECLARE(SdlAudioSink); +#ifdef VIDEO_SUPPORT +PLUGIN_REGISTER_STATIC_DECLARE(SdlVideoSink); +#endif +#endif + +void RegisterPluginStatic(std::shared_ptr reg) +{ + REGISTER_STATIC(FileSource, reg); +#ifndef UNIT_TEST + REGISTER_STATIC(StreamSource, reg); + REGISTER_STATIC(FFmpegDemuxer, reg); + REGISTER_STATIC(FFmpegAudioDecoders, reg); +#ifdef VIDEO_SUPPORT + REGISTER_STATIC(FFmpegVideoDecoders, reg); +#endif +#ifdef HI3516DV300 + REGISTER_STATIC(HdiAuSink, reg); +#else + REGISTER_STATIC(SdlAudioSink, reg); +#ifdef VIDEO_SUPPORT + REGISTER_STATIC(SdlVideoSink, reg); +#endif +#endif +#endif +} + +void UnregisterPluginStatic() +{ + UNREGISTER_STATIC(FileSource); +#ifndef UNIT_TEST + UNREGISTER_STATIC(StreamSource); + UNREGISTER_STATIC(FFmpegDemuxer); + UNREGISTER_STATIC(FFmpegAudioDecoders); +#ifdef HI3516DV300 + UNREGISTER_STATIC(HdiAuSink); +#else + UNREGISTER_STATIC(SdlAudioSink); +#endif +#endif +} +#endif // HISTREAMER_PLUGIN_ALL_PLUGIN_STATIC_H diff --git a/engine/plugin/core/audio_sink.cpp b/engine/plugin/core/audio_sink.cpp new file mode 100644 index 00000000..6c396ac2 --- /dev/null +++ b/engine/plugin/core/audio_sink.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "audio_sink.h" + +#include "interface/audio_sink_plugin.h" + +using namespace OHOS::Media::Plugin; + +AudioSink::AudioSink(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin) + : Base(pkgVer, apiVer, plugin), audioSink(std::move(plugin)) +{ +} + +Status AudioSink::Pause() +{ + return audioSink->Pause(); +} + +Status AudioSink::Resume() +{ + return audioSink->Resume(); +} + +Status AudioSink::Flush() +{ + return audioSink->Flush(); +} + +Status AudioSink::Write(const std::shared_ptr& input) +{ + return audioSink->Write(input); +} diff --git a/engine/plugin/core/audio_sink.h b/engine/plugin/core/audio_sink.h new file mode 100644 index 00000000..608b701e --- /dev/null +++ b/engine/plugin/core/audio_sink.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_AUDIO_SINK_H +#define HISTREAMER_PLUGIN_CORE_AUDIO_SINK_H + +#include +#include + +#include "base.h" +#include "common/plugin_types.h" +#include "common/plugin_tags.h" +#include "common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct AudioSinkPlugin; + +class AudioSink : public Base { +public: + AudioSink(const AudioSink &) = delete; + AudioSink operator=(const AudioSink &) = delete; + ~AudioSink() override = default; + + Status Pause(); + + Status Resume(); + + Status Flush(); + + Status Write(const std::shared_ptr &input); + +private: + friend class PluginManager; + + AudioSink(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); + +private: + std::shared_ptr audioSink; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_AUDIO_SINK_H diff --git a/engine/plugin/core/base.cpp b/engine/plugin/core/base.cpp new file mode 100644 index 00000000..839dc204 --- /dev/null +++ b/engine/plugin/core/base.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "base.h" +#include "plugin/interface/plugin_base.h" + +using namespace OHOS::Media::Plugin; + +Base::Base(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr base) + : pkgVersion(pkgVer), apiVersion(apiVer), plugin(std::move(base)) +{ +} + +Status Base::Init() +{ + return plugin->Init(); +} + +Status Base::Deinit() +{ + return plugin->Deinit(); +} + +Status Base::Prepare() +{ + return plugin->Prepare(); +} + +Status Base::Reset() +{ + return plugin->Reset(); +} + +Status Base::Start() +{ + return plugin->Start(); +} + +Status Base::Stop() +{ + return plugin->Stop(); +} + +bool Base::IsParameterSupported(Tag tag) +{ + return plugin->IsParameterSupported(tag); +} + +Status Base::GetParameter(Tag tag, ValueType& value) +{ + return plugin->GetParameter(tag, value); +} + +Status Base::SetParameter(Tag tag, const ValueType& value) +{ + return plugin->SetParameter(tag, value); +} + +Status Base::GetState(State &state) +{ + return plugin->GetState(state); +} + +std::shared_ptr Base::GetAllocator() +{ + return plugin->GetAllocator(); +} diff --git a/engine/plugin/core/base.h b/engine/plugin/core/base.h new file mode 100644 index 00000000..60539746 --- /dev/null +++ b/engine/plugin/core/base.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_PLUGIN_H +#define HISTREAMER_PLUGIN_CORE_PLUGIN_H + +#include + +#include "common/plugin_types.h" +#include "common/plugin_tags.h" +#include "common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct PluginBase; + +class Base { +public: + Base(const Base &) = delete; + + Base operator=(const Base &) = delete; + + virtual ~Base() = default; + + virtual Status Init(); + + virtual Status Deinit(); + + virtual Status Prepare(); + + virtual Status Reset(); + + virtual Status Start(); + + virtual Status Stop(); + + virtual bool IsParameterSupported(Tag tag); + + virtual Status GetParameter(Tag tag, ValueType &value); + + virtual Status SetParameter(Tag tag, const ValueType &value); + + virtual Status GetState(State &state); + + virtual std::shared_ptr GetAllocator(); + +protected: + friend class PluginManager; + + Base(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); + +protected: + const uint32_t pkgVersion; + + const uint32_t apiVersion; + + std::shared_ptr plugin; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_PLUGIN_H diff --git a/engine/plugin/core/codec.cpp b/engine/plugin/core/codec.cpp new file mode 100644 index 00000000..d2018840 --- /dev/null +++ b/engine/plugin/core/codec.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "codec.h" + +#include +#include "interface/codec_plugin.h" + +using namespace OHOS::Media::Plugin; + +Codec::Codec(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin) + : Base(pkgVer, apiVer, plugin), codec_(std::move(plugin)) +{ +} + +Status Codec::QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) +{ + return codec_->QueueInputBuffer(inputBuffer, timeoutMs); +} + +Status Codec::QueueOutputBuffer(const std::shared_ptr& outputBuffers, int32_t timeoutMs) +{ + return codec_->QueueOutputBuffer(outputBuffers, timeoutMs); +} + +Status Codec::Flush() +{ + return codec_->Flush(); +} + +struct DataCallbackWrapper : DataCallback { + DataCallbackWrapper(uint32_t pkgVersion, std::weak_ptr dataCallback) + : version(pkgVersion), helper(dataCallback) + { + } + + ~DataCallbackWrapper() override = default; + + void OnInputBufferDone(const std::shared_ptr& input) override + { + auto callback = helper.lock(); + if (callback) { + callback->OnInputBufferDone(input); + } + } + + void OnOutputBufferDone(const std::shared_ptr& output) override + { + auto callback = helper.lock(); + if (callback) { + callback->OnOutputBufferDone(output); + } + } + +private: + __attribute__((unused)) uint32_t version; + std::weak_ptr helper; +}; + +Status Codec::SetDataCallback(const std::weak_ptr& helper) +{ + dataCallback_ = std::make_shared(pkgVersion, helper); + return codec_->SetDataCallback(dataCallback_); +} diff --git a/engine/plugin/core/codec.h b/engine/plugin/core/codec.h new file mode 100644 index 00000000..e544f7cc --- /dev/null +++ b/engine/plugin/core/codec.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_CODEC_H +#define HISTREAMER_PLUGIN_CORE_CODEC_H + +#include + +#include "base.h" +#include "common/plugin_types.h" +#include "common/plugin_tags.h" +#include "common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct DataCallbackHelper { + virtual ~DataCallbackHelper() = default; + virtual void OnInputBufferDone(const std::shared_ptr &input) = 0; + virtual void OnOutputBufferDone(const std::shared_ptr &output) = 0; +}; + +struct CodecPlugin; + +struct DataCallback; + +class Codec : public Base { +public: + Codec(const Codec &) = delete; + + Codec operator=(const Codec &) = delete; + + ~Codec() override = default; + + Status QueueInputBuffer(const std::shared_ptr &inputBuffer, int32_t timeoutMs); + + Status QueueOutputBuffer(const std::shared_ptr &outputBuffers, int32_t timeoutMs); + + Status Flush(); + + Status SetDataCallback(const std::weak_ptr &dataCallback); + +private: + friend class PluginManager; + + Codec(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); + +private: + std::shared_ptr codec_; + + std::shared_ptr dataCallback_; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_CODEC_H diff --git a/engine/plugin/core/demuxer.cpp b/engine/plugin/core/demuxer.cpp new file mode 100644 index 00000000..a34e084a --- /dev/null +++ b/engine/plugin/core/demuxer.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "demuxer.h" +#include "interface/demuxer_plugin.h" +#include "interface/plugin_definition.h" + +#include "plugin_wrapper.h" + +using namespace OHOS::Media::Plugin; + +Demuxer::Demuxer(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin) + : Base(pkgVer, apiVer, plugin), demuxer_(std::move(plugin)) +{ +} + +Status Demuxer::SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) +{ + return demuxer_->SeekTo(trackId, timeStampUs, mode); +} + +Status Demuxer::SetDataSource(const std::shared_ptr& source) +{ + return demuxer_->SetDataSource(std::make_shared(pkgVersion, source)); +} + +Status Demuxer::GetMediaInfo(MediaInfoHelper& mediaInfo) +{ + MediaInfo info; + demuxer_->GetMediaInfo(info); + ConvertToMediaInfoHelper(pkgVersion, info, mediaInfo); + return Status::OK; +} + +Status Demuxer::ReadFrame(Buffer& info, int32_t timeOutMs) +{ + return demuxer_->ReadFrame(info, timeOutMs); +} diff --git a/engine/plugin/core/demuxer.h b/engine/plugin/core/demuxer.h new file mode 100644 index 00000000..b28bdedc --- /dev/null +++ b/engine/plugin/core/demuxer.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_DEMUXER_H +#define HISTREAMER_PLUGIN_CORE_DEMUXER_H + +#include + +#include "base.h" +#include "foundation/meta.h" +#include "common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +using AllocatorHelper = Allocator; + +struct MediaInfoHelper { + Meta globalMeta; + std::vector streamMeta; +}; + +struct DataSourceHelper { + virtual ~DataSourceHelper() = default; + virtual Status ReadAt(int64_t offset, std::shared_ptr &buffer, size_t expectedLen) = 0; + virtual Status GetSize(size_t &size) = 0; +}; + +struct DemuxerPlugin; + +class Demuxer : public Base { +public: + Demuxer(const Demuxer &) = delete; + Demuxer operator=(const Demuxer &) = delete; + ~Demuxer() override = default; + + Status SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode); + + Status SetDataSource(const std::shared_ptr &source); + + Status GetMediaInfo(MediaInfoHelper &mediaInfo); + + Status ReadFrame(Buffer &info, int32_t timeOutMs); + +private: + friend class PluginManager; + + Demuxer(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); + +private: + std::shared_ptr demuxer_; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_DEMUXER_H diff --git a/engine/plugin/core/plugin_info.h b/engine/plugin/core/plugin_info.h new file mode 100644 index 00000000..8d055b26 --- /dev/null +++ b/engine/plugin/core/plugin_info.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INFO_H +#define HISTREAMER_PLUGIN_INFO_H + +#include "interface/plugin_definition.h" +#include "common/plugin_tags.h" +#include "common/plugin_caps.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * PluginInfo, which describes static information for a plugin, including basic plugin information, + * such as the type, name, rank, and input and output capabilities. + * + * Different types of plugins have their own extra information, + * which is described in the "extra" field in the form of key-value. + * + * Note that the type, name, rating, and extra information describes the plugin as a whole; + * + * Typically, plugin have inputs and outputs, those capabilities are described by inCaps and outCaps. + * (The Source plugin has only output capability, and the Sink plugin has only input capability.) + * The input/output capability describes the data processing capability by mime-type. + * To describe mime-type more closely, a detailed tag may be attached to the mime-type. + * + */ +struct PluginInfo { + uint32_t apiVersion; + PluginType pluginType; + std::string name; + std::string description; + uint32_t rank; + CapabilitySet inCaps; + CapabilitySet outCaps; + std::map extra; +}; + +/** + * Extra information about the plugin. + * Describes the protocol types supported by the Source plugin. + */ +#define PLUGIN_INFO_EXTRA_PROTOCOL "protocol" + +/** + * Extra information about the plugin. + * Describes the extensions supported by the Demuxer plugin. + */ +#define PLUGIN_INFO_EXTRA_EXTENSIONS "extensions" + +/** + * Extra information about the plugin. + * Describes the codec type supported by the Codec plugin. + * + * ValueType: enum Plugin::CodecType + */ +#define PLUGIN_INFO_EXTRA_CODEC_TYPE "codec_type" +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INFO_H diff --git a/engine/plugin/core/plugin_loader.cpp b/engine/plugin/core/plugin_loader.cpp new file mode 100644 index 00000000..e52b29eb --- /dev/null +++ b/engine/plugin/core/plugin_loader.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "plugin_loader.h" + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +#include +#include +#include +#else +#include +#endif + +using namespace OHOS::Media::Plugin; + +PluginLoader::PluginLoader(void* handler, std::string name) : handler_(handler), name_(std::move(name)) +{ +} + +PluginLoader::~PluginLoader() +{ + UnLoadPluginFile(); +} + +std::shared_ptr PluginLoader::Create(const std::string& name, const std::string& path) +{ + if (name.empty() || path.empty()) { + return std::shared_ptr(); + } + return CheckSymbol(LoadPluginFile(path), name); +} + +RegisterFunc PluginLoader::FetchRegisterFunction() +{ + return registerFunc_; +} + +UnregisterFunc PluginLoader::FetchUnregisterFunction() +{ + return unregisterFunc_; +} + +void* PluginLoader::LoadPluginFile(const std::string& path) +{ +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) + return ::LoadLibraryA(path.c_str()); +#else + auto pathStr = path.c_str(); + if (pathStr) { + return ::dlopen(pathStr, RTLD_NOW); + } + return nullptr; +#endif +} + +std::shared_ptr PluginLoader::CheckSymbol(void* handler, const std::string& name) +{ + if (handler) { + std::string registerFuncName = "register_" + name; + std::string unregisterFuncName = "unregister_" + name; + RegisterFunc registerFunc = nullptr; + UnregisterFunc unregisterFunc = nullptr; +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) + registerFunc = (RegisterFunc)(::GetProcAddress((HMODULE)handler, registerFuncName.c_str())); + unregisterFunc = (UnregisterFunc)(::GetProcAddress((HMODULE)handler, unregisterFuncName.c_str())); +#else + registerFunc = (RegisterFunc)(::dlsym(handler, registerFuncName.c_str())); + unregisterFunc = (UnregisterFunc)(::dlsym(handler, unregisterFuncName.c_str())); +#endif + if (registerFunc && unregisterFunc) { + std::shared_ptr loader(new PluginLoader(handler, name)); + loader->registerFunc_ = registerFunc; + loader->unregisterFunc_ = unregisterFunc; + return loader; + } + } + return std::shared_ptr(); +} + +void PluginLoader::UnLoadPluginFile() +{ + if (handler_) { +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) + ::FreeLibrary((HMODULE)handler); +#else + ::dlclose(const_cast(handler_)); +#endif + } +} diff --git a/engine/plugin/core/plugin_loader.h b/engine/plugin/core/plugin_loader.h new file mode 100644 index 00000000..8003c292 --- /dev/null +++ b/engine/plugin/core/plugin_loader.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_LOADER_H +#define HISTREAMER_PLUGIN_LOADER_H + +#include "interface/plugin_base.h" +#include "interface/plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +class PluginLoader { +public: + PluginLoader(const PluginLoader &) = delete; + + PluginLoader operator=(const PluginLoader &) = delete; + + ~PluginLoader(); + + static std::shared_ptr Create(const std::string &name, const std::string &path); + + RegisterFunc FetchRegisterFunction(); + + UnregisterFunc FetchUnregisterFunction(); + +private: + PluginLoader(void* handler, std::string name); + + static void* LoadPluginFile(const std::string &path); + + static std::shared_ptr CheckSymbol(void* handler, const std::string &name); + + void UnLoadPluginFile(); + +private: + const void *handler_; + const std::string name_; + RegisterFunc registerFunc_ {nullptr}; + UnregisterFunc unregisterFunc_ {nullptr}; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_LOADER_H diff --git a/engine/plugin/core/plugin_manager.cpp b/engine/plugin/core/plugin_manager.cpp new file mode 100644 index 00000000..46e99ba7 --- /dev/null +++ b/engine/plugin/core/plugin_manager.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "plugin_manager.h" +#include "interface/audio_sink_plugin.h" +#include "interface/codec_plugin.h" +#include "interface/demuxer_plugin.h" +#include "interface/source_plugin.h" +#include "interface/video_sink_plugin.h" +#include "plugin_register.h" +#include "plugin_wrapper.h" + +#include + +using namespace OHOS::Media::Plugin; + +PluginManager::PluginManager() +{ + Init(); +} + +PluginManager::~PluginManager() +{ +} + +std::set PluginManager::ListPlugins(PluginType type) +{ + return pluginRegister->ListPlugins(type); +} + +std::shared_ptr PluginManager::GetPluginInfo(PluginType type, const std::string& name) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(type, name); + if (regInfo && regInfo->info && regInfo->info->pluginType == type) { + return regInfo->info; + } + return std::shared_ptr(); +} + +int32_t PluginManager::Sniffer(const std::string& name, std::shared_ptr source) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(PluginType::DEMUXER, name); + if (!regInfo) { + return 0; + } + if (regInfo->info->pluginType == PluginType::DEMUXER) { + return regInfo->sniffer(name, std::make_shared(regInfo->packageDef->pkgVersion, source)); + } + return 0; +} + +void PluginManager::Init() +{ + pluginRegister = std::make_shared(); + pluginRegister->RegisterPlugins(); +} + +std::shared_ptr PluginManager::CreateDemuxerPlugin(const std::string& name) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(PluginType::DEMUXER, name); + if (!regInfo) { + return std::shared_ptr(); + } + std::shared_ptr demuxer = std::dynamic_pointer_cast(regInfo->creator(name)); + if (!demuxer) { + return std::shared_ptr(); + } + return std::shared_ptr(new Demuxer(regInfo->packageDef->pkgVersion, regInfo->info->apiVersion, demuxer)); +} + +std::shared_ptr PluginManager::CreateSourcePlugin(const std::string& name) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(PluginType::SOURCE, name); + if (!regInfo) { + return std::shared_ptr(); + } + std::shared_ptr source = std::dynamic_pointer_cast(regInfo->creator(name)); + if (!source) { + return std::shared_ptr(); + } + return std::shared_ptr(new Source(regInfo->packageDef->pkgVersion, regInfo->info->apiVersion, source)); +} + +std::shared_ptr PluginManager::CreateCodecPlugin(const std::string& name) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(PluginType::CODEC, name); + if (!regInfo) { + return std::shared_ptr(); + } + std::shared_ptr codec = std::dynamic_pointer_cast(regInfo->creator(name)); + if (!codec) { + return std::shared_ptr(); + } + return std::shared_ptr(new Codec(regInfo->packageDef->pkgVersion, regInfo->info->apiVersion, codec)); +} + +std::shared_ptr PluginManager::CreateAudioSinkPlugin(const std::string& name) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(PluginType::AUDIO_SINK, name); + if (!regInfo) { + return std::shared_ptr(); + } + std::shared_ptr audioSink = std::dynamic_pointer_cast(regInfo->creator(name)); + if (!audioSink) { + return std::shared_ptr(); + } + return std::shared_ptr( + new AudioSink(regInfo->packageDef->pkgVersion, regInfo->info->apiVersion, audioSink)); +} + +std::shared_ptr PluginManager::CreateVideoSinkPlugin(const std::string& name) +{ + std::shared_ptr regInfo = pluginRegister->GetPluginRegInfo(PluginType::VIDEO_SINK, name); + if (!regInfo) { + return std::shared_ptr(); + } + std::shared_ptr videoSink = std::dynamic_pointer_cast(regInfo->creator(name)); + if (!videoSink) { + return std::shared_ptr(); + } + return std::shared_ptr( + new VideoSink(regInfo->packageDef->pkgVersion, regInfo->info->apiVersion, videoSink)); +} \ No newline at end of file diff --git a/engine/plugin/core/plugin_manager.h b/engine/plugin/core/plugin_manager.h new file mode 100644 index 00000000..3ae32a6d --- /dev/null +++ b/engine/plugin/core/plugin_manager.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_MANAGER_H +#define HISTREAMER_PLUGIN_MANAGER_H + +#include "plugin_info.h" +#include "plugin_register.h" + +#include "audio_sink.h" +#include "codec.h" +#include "demuxer.h" +#include "source.h" +#include "video_sink.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +class PluginManager { +public: + PluginManager(const PluginManager&) = delete; + PluginManager operator=(const PluginManager&) = delete; + ~PluginManager(); + static PluginManager& Instance() + { + static PluginManager impl; + return impl; + } + + std::set ListPlugins(PluginType type); + + std::shared_ptr GetPluginInfo(PluginType type, const std::string& name); + + std::shared_ptr CreateSourcePlugin(const std::string& name); + + std::shared_ptr CreateDemuxerPlugin(const std::string& name); + + std::shared_ptr CreateCodecPlugin(const std::string& name); + + std::shared_ptr CreateAudioSinkPlugin(const std::string& name); + + std::shared_ptr CreateVideoSinkPlugin(const std::string& name); + + int32_t Sniffer(const std::string& name, std::shared_ptr source); + +private: + PluginManager(); + + void Init(); + +private: + std::shared_ptr pluginRegister; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_MANAGER_H diff --git a/engine/plugin/core/plugin_meta.h b/engine/plugin/core/plugin_meta.h new file mode 100644 index 00000000..dca584f2 --- /dev/null +++ b/engine/plugin/core/plugin_meta.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_META_H +#define HISTREAMER_PLUGIN_META_H + +#include "common/plugin_tags.h" +#include "common/plugin_types.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +template +constexpr typename std::underlying_type::type to_underlying(E e) noexcept +{ + return static_cast::type>(e); +} + +/** + * Meta ID is used to describe the metadata of media files or data streams. + * All Meta ID must come from Tag. + * + * For details about the definition and usage, to see enum Tag in file plugin_tags.h. + */ +enum struct MetaID : uint32_t { + MIME = to_underlying(Tag::MIME), + STREAM_INDEX = to_underlying(Tag::STREAM_INDEX), + MEDIA_CODEC_CONFIG = to_underlying(Tag::MEDIA_CODEC_CONFIG), + + AUDIO_CHANNELS = to_underlying(Tag::AUDIO_CHANNELS), + AUDIO_SAMPLE_RATE = to_underlying(Tag::AUDIO_SAMPLE_RATE), + AUDIO_SAMPLE_FORMAT = to_underlying(Tag::AUDIO_SAMPLE_FORMAT), + AUDIO_SAMPLE_PRE_FRAME = to_underlying(Tag::AUDIO_SAMPLE_PRE_FRAME), + AUDIO_CHANNEL_LAYOUT = to_underlying(Tag::AUDIO_CHANNEL_LAYOUT), + + MEDIA_TITLE = to_underlying(Tag::MEDIA_TITLE), + MEDIA_ARTIST = to_underlying(Tag::MEDIA_ARTIST), + MEDIA_LYRICIST = to_underlying(Tag::MEDIA_LYRICIST), + MEDIA_ALBUM = to_underlying(Tag::MEDIA_ALBUM), + MEDIA_ALBUM_ARTIST = to_underlying(Tag::MEDIA_ALBUM_ARTIST), + MEDIA_DATE = to_underlying(Tag::MEDIA_DATE), + MEDIA_COMMENT = to_underlying(Tag::MEDIA_COMMENT), + MEDIA_GENRE = to_underlying(Tag::MEDIA_GENRE), + MEDIA_DESCRIPTION = to_underlying(Tag::MEDIA_DESCRIPTION), + MEDIA_COPYRIGHT = to_underlying(Tag::MEDIA_COPYRIGHT), + MEDIA_LANGUAGE = to_underlying(Tag::MEDIA_LANGUAGE), + MEDIA_LYRICS = to_underlying(Tag::MEDIA_LYRICS), + MEDIA_DURATION = to_underlying(Tag::MEDIA_DURATION), + MEDIA_BITRATE = to_underlying(Tag::MEDIA_BITRATE), + MEDIA_FILE_EXTENSION = to_underlying(Tag::MEDIA_FILE_EXTENSION), + MEDIA_FILE_SIZE = to_underlying(Tag::MEDIA_FILE_SIZE), + + AUDIO_MPEG_VERSION = to_underlying(Tag::AUDIO_MPEG_VERSION), + AUDIO_MPEG_LAYER = to_underlying(Tag::AUDIO_MPEG_LAYER), + AUDIO_AAC_PROFILE = to_underlying(Tag::AUDIO_AAC_PROFILE), + AUDIO_AAC_LEVEL = to_underlying(Tag::AUDIO_AAC_LEVEL), + AUDIO_AAC_STREAM_FORMAT = to_underlying(Tag::AUDIO_AAC_STREAM_FORMAT), + + VIDEO_WIDTH = to_underlying(Tag::VIDEO_WIDTH), + VIDEO_HEIGHT = to_underlying(Tag::VIDEO_HEIGHT), + VIDEO_PIXEL_FORMAT = to_underlying(Tag::VIDEO_PIXEL_FORMAT), +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_META_H diff --git a/engine/plugin/core/plugin_register.cpp b/engine/plugin/core/plugin_register.cpp new file mode 100644 index 00000000..c14ec659 --- /dev/null +++ b/engine/plugin/core/plugin_register.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "plugin_register.h" +#include "interface/audio_sink_plugin.h" +#include "interface/codec_plugin.h" +#include "interface/demuxer_plugin.h" +#include "interface/source_plugin.h" +#include "interface/video_sink_plugin.h" + +#include "all_plugin_static.h" + +#include + +using namespace OHOS::Media::Plugin; + +static std::map g_apiVersionMap = { + {PluginType::SOURCE, SOURCE_API_VERSION}, + {PluginType::DEMUXER, DEMUXER_API_VERSION}, + {PluginType::CODEC, CODEC_API_VERSION}, + {PluginType::AUDIO_SINK, AUDIO_SINK_API_VERSION}, + {PluginType::VIDEO_SINK, VIDEO_SINK_API_VERSION}, +}; + +static std::string g_libFileHead = "libplugin_"; + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +static std::string g_libFileTail = ".dll"; +static std::string g_fileSeparator = "\\"; +#else +static std::string g_libFileTail = ".so"; +static std::string g_fileSeparator = "/"; +#endif + +PluginRegister::~PluginRegister() +{ + UnregisterAllPlugins(); + registerData->registerNames.clear(); + registerData->registerTable.clear(); +} + +Status PluginRegister::RegisterImpl::AddPackage(const PackageDef& def) +{ + return SetPackageDef(def); +} + +Status PluginRegister::RegisterImpl::SetPackageDef(const PackageDef& def) +{ + packageDef->name = def.name; + packageDef->licenseType = def.licenseType; + packageDef->pkgVersion = def.pkgVersion; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::AddPlugin(const PluginDefBase& def) +{ + if (!Verification(def)) { + // 插件定义参数校验不合法 + return Status::ERROR_INVALID_DATA; + } + if (!VersionMatched(def)) { + // 版本不匹配,不给注册 + return Status::ERROR_UNKNOWN; + } + if (registerData->IsPluginExist(def.pluginType, def.name)) { + if (MoreAcceptable(registerData->registerTable[def.pluginType][def.name], def)) { + registerData->registerTable[def.pluginType].erase(def.name); + } else { + // 重复注册,且有更合适的版本存在 + return Status::ERROR_ALREADY_EXISTS; + } + } + registerData->registerNames[def.pluginType].insert(def.name); + registerData->registerTable[def.pluginType][def.name] = BuildRegInfo(def); + return Status::OK; +} + +std::shared_ptr PluginRegister::RegisterImpl::BuildRegInfo(const PluginDefBase& def) +{ + std::shared_ptr regInfo = std::make_shared(); + regInfo->packageDef = packageDef; + switch (def.pluginType) { + case PluginType::SOURCE: { + InitSourceInfo(regInfo, def); + break; + } + case PluginType::DEMUXER: { + InitDemuxerInfo(regInfo, def); + break; + } + case PluginType::CODEC: { + InitCodecInfo(regInfo, def); + break; + } + case PluginType::AUDIO_SINK: { + InitAudioSinkInfo(regInfo, def); + break; + } + case PluginType::VIDEO_SINK: { + InitVideoSinkInfo(regInfo, def); + break; + } + default: + return std::shared_ptr(); + } + regInfo->loader = std::move(pluginLoader); + return regInfo; +} + +bool PluginRegister::RegisterImpl::Verification(const PluginDefBase& definition) +{ + if (definition.rank < 0 || definition.rank > 100) { + return false; + } + return (definition.pluginType != PluginType::INVALID_TYPE); +} + +bool PluginRegister::RegisterImpl::VersionMatched(const PluginDefBase& definition) +{ + int major = (definition.apiVersion >> 16) & 0xFFFF; // 16 + int minor = definition.apiVersion & 0xFFFF; + uint32_t version = g_apiVersionMap[definition.pluginType]; + int coreMajor = (version >> 16) & 0xFFFF; // 16 + int coreMinor = version & 0xFFFF; + return (major == coreMajor) && (minor <= coreMinor); +} + +bool PluginRegister::RegisterImpl::MoreAcceptable(std::shared_ptr& reg, const PluginDefBase& def) +{ + return false; +} + +void PluginRegister::RegisterImpl::SetPluginInfo(std::shared_ptr& info, const PluginDefBase& def) +{ + info->apiVersion = def.apiVersion; + info->pluginType = def.pluginType; + info->name = def.name; + info->description = def.description; + info->rank = def.rank; +} + +Status PluginRegister::RegisterImpl::InitSourceInfo(std::shared_ptr& reg, const PluginDefBase& def) +{ + auto& base = (SourcePluginDef&)def; + reg->creator = reinterpret_cast>(base.creator); + std::shared_ptr info = std::make_shared(); + SetPluginInfo(info, def); + info->extra.insert({PLUGIN_INFO_EXTRA_PROTOCOL, base.protocol}); + SourceCapabilityConvert(info, def); + reg->info = info; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::InitDemuxerInfo(std::shared_ptr& reg, const PluginDefBase& def) +{ + auto& base = (DemuxerPluginDef&)def; + reg->creator = reinterpret_cast>(base.creator); + reg->sniffer = base.sniffer; + std::shared_ptr info = std::make_shared(); + SetPluginInfo(info, def); + info->extra.insert({PLUGIN_INFO_EXTRA_EXTENSIONS, base.extensions}); + DemuxerCapabilityConvert(info, def); + reg->info = info; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::InitCodecInfo(std::shared_ptr& reg, const PluginDefBase& def) +{ + auto& base = (CodecPluginDef&)def; + reg->creator = reinterpret_cast>(base.creator); + std::shared_ptr info = std::make_shared(); + SetPluginInfo(info, def); + info->extra.insert({PLUGIN_INFO_EXTRA_CODEC_TYPE, base.codecType}); + CodecCapabilityConvert(info, def); + reg->info = info; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::InitAudioSinkInfo(std::shared_ptr& reg, const PluginDefBase& def) +{ + reg->creator = reinterpret_cast>(((AudioSinkPluginDef&)def).creator); + std::shared_ptr info = std::make_shared(); + SetPluginInfo(info, def); + AudioSinkCapabilityConvert(info, def); + reg->info = info; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::InitVideoSinkInfo(std::shared_ptr& reg, const PluginDefBase& def) +{ + reg->creator = reinterpret_cast>(((VideoSinkPluginDef&)def).creator); + std::shared_ptr info = std::make_shared(); + SetPluginInfo(info, def); + VideoSinkCapabilityConvert(info, def); + reg->info = info; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::SourceCapabilityConvert(std::shared_ptr& info, + const PluginDefBase& def) +{ + auto& base = (SourcePluginDef&)def; + info->outCaps = base.outCaps; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::DemuxerCapabilityConvert(std::shared_ptr& info, + const PluginDefBase& def) +{ + auto& base = (DemuxerPluginDef&)def; + info->inCaps = base.inCaps; + info->outCaps = base.outCaps; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::CodecCapabilityConvert(std::shared_ptr& info, const PluginDefBase& def) +{ + auto& base = (CodecPluginDef&)def; + info->inCaps = base.inCaps; + info->outCaps = base.outCaps; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::AudioSinkCapabilityConvert(std::shared_ptr& info, + const PluginDefBase& def) +{ + auto& base = (AudioSinkPluginDef&)def; + info->inCaps = base.inCaps; + return Status::OK; +} + +Status PluginRegister::RegisterImpl::VideoSinkCapabilityConvert(std::shared_ptr& info, + const PluginDefBase& def) +{ + auto& base = (VideoSinkPluginDef&)def; + info->inCaps = base.inCaps; + return Status::OK; +} + +std::set PluginRegister::ListPlugins(PluginType type) +{ + return registerData->registerNames[type]; +} + +std::shared_ptr PluginRegister::GetPluginRegInfo(PluginType type, const std::string& name) +{ + if (registerData->IsPluginExist(type, name)) { + return registerData->registerTable[type][name]; + } + return std::shared_ptr(); +} + +void PluginRegister::RegisterPlugins() +{ + RegisterStaticPlugins(); + RegisterDynamicPlugins(); +} + +void PluginRegister::RegisterStaticPlugins() +{ + RegisterPluginStatic(std::make_shared(registerData)); +} + +void PluginRegister::RegisterDynamicPlugins() +{ +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) + RegisterPluginsFromPath("."); + RegisterPluginsFromPath(".\\plugins"); + RegisterPluginsFromPath("..\\src\\plugin\\plugins\\source\\file_source"); +#endif +} + +void PluginRegister::RegisterPluginsFromPath(const char* libDirPath) +{ + DIR* libDir = opendir(libDirPath); + if (libDir) { + struct dirent* lib = nullptr; + std::shared_ptr loader = nullptr; + while ((lib = readdir(libDir))) { + if (lib->d_name[0] == '.') { + continue; + } + std::string libName = lib->d_name; + if (libName.find(g_libFileHead) || + libName.compare(libName.size() - g_libFileTail.size(), g_libFileTail.size(), g_libFileTail)) { + continue; + } + std::string pluginName = + libName.substr(g_libFileHead.size(), libName.size() - g_libFileHead.size() - g_libFileTail.size()); + std::string libPath = libDirPath + g_fileSeparator + lib->d_name; + loader = PluginLoader::Create(pluginName, libPath); + if (loader) { + loader->FetchRegisterFunction()(std::make_shared(registerData, loader)); + registeredLoaders.push_back(loader); + } + } + closedir(libDir); + } +} + +void PluginRegister::UnregisterAllPlugins() +{ + UnregisterPluginStatic(); + for (auto& loader : registeredLoaders) { + EraseRegisteredPlugins(loader); + loader->FetchUnregisterFunction()(); + loader.reset(); + } + registeredLoaders.clear(); +} + +void PluginRegister::EraseRegisteredPlugins(std::shared_ptr loader) +{ + for (auto& it : registerData->registerTable) { + PluginType type = it.first; + auto plugins = it.second; + for (auto info = plugins.begin(); info != plugins.end();) { + if (info->second->loader == loader) { + registerData->registerNames[type].erase(info->first); + info = plugins.erase(info); + } else { + info++; + } + } + } +} + +bool PluginRegister::RegisterData::IsPluginExist(PluginType type, const std::string& name) +{ + return (registerTable.find(type) != registerTable.end() && + registerTable[type].find(name) != registerTable[type].end()); +} diff --git a/engine/plugin/core/plugin_register.h b/engine/plugin/core/plugin_register.h new file mode 100644 index 00000000..13d563a3 --- /dev/null +++ b/engine/plugin/core/plugin_register.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_REGISTER_H +#define HISTREAMER_PLUGIN_REGISTER_H + +#include +#include +#include +#include +#include "common/any.h" +#include "interface/audio_sink_plugin.h" +#include "interface/codec_plugin.h" +#include "interface/demuxer_plugin.h" +#include "interface/plugin_base.h" +#include "interface/source_plugin.h" +#include "plugin_loader.h" + +#include "plugin_info.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct PluginRegInfo { + std::shared_ptr packageDef; + std::shared_ptr info; + PluginCreatorFunc creator; + DemuxerPluginSnifferFunc sniffer; + std::shared_ptr loader; +}; + +class PluginRegister { +public: + PluginRegister() = default; + PluginRegister(const PluginRegister&) = delete; + PluginRegister operator=(const PluginRegister&) = delete; + ~PluginRegister(); + + std::set ListPlugins(PluginType type); + + std::shared_ptr GetPluginRegInfo(PluginType type, const std::string& name); + + void RegisterPlugins(); + +private: + void RegisterStaticPlugins(); + void RegisterDynamicPlugins(); + void RegisterPluginsFromPath(const char* libDirPath); + void UnregisterAllPlugins(); + void EraseRegisteredPlugins(std::shared_ptr loader); + +private: + using REGISTERED_TABLE = std::map>>; + + struct RegisterData { + std::map> registerNames; + REGISTERED_TABLE registerTable; + bool IsPluginExist(PluginType type, const std::string& name); + }; + + struct RegisterImpl : PackageRegister { + RegisterImpl(std::shared_ptr data, std::shared_ptr loader = nullptr) + : pluginLoader(std::move(loader)), registerData(std::move(data)) {} + + ~RegisterImpl() override = default; + + Status AddPlugin(const PluginDefBase& def) override; + + Status AddPackage(const PackageDef& def) override; + + Status SetPackageDef(const PackageDef& def); + + std::shared_ptr BuildRegInfo(const PluginDefBase& definition); + + void SetPluginInfo(std::shared_ptr& pluginInfo, const PluginDefBase& definition); + + Status InitSourceInfo(std::shared_ptr& reg, const PluginDefBase& definition); + + static Status SourceCapabilityConvert(std::shared_ptr& info, const PluginDefBase& def); + + Status InitDemuxerInfo(std::shared_ptr& reg, const PluginDefBase& definition); + + static Status DemuxerCapabilityConvert(std::shared_ptr& info, const PluginDefBase& def); + + Status InitCodecInfo(std::shared_ptr& reg, const PluginDefBase& definition); + + static Status CodecCapabilityConvert(std::shared_ptr& info, const PluginDefBase& def); + + Status InitAudioSinkInfo(std::shared_ptr& reg, const PluginDefBase& definition); + + static Status AudioSinkCapabilityConvert(std::shared_ptr& info, const PluginDefBase& def); + + Status InitVideoSinkInfo(std::shared_ptr& reg, const PluginDefBase& definition); + + static Status VideoSinkCapabilityConvert(std::shared_ptr& info, const PluginDefBase& def); + + bool Verification(const PluginDefBase& definition); + + bool VersionMatched(const PluginDefBase& definition); + + bool MoreAcceptable(std::shared_ptr& regInfo, const PluginDefBase& definition); + + std::shared_ptr pluginLoader; + std::shared_ptr registerData; + std::shared_ptr packageDef = std::make_shared(); + }; + + std::shared_ptr registerData = std::make_shared(); + std::vector> registeredLoaders; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_REGISTER_H diff --git a/engine/plugin/core/plugin_wrapper.cpp b/engine/plugin/core/plugin_wrapper.cpp new file mode 100644 index 00000000..8e6659e7 --- /dev/null +++ b/engine/plugin/core/plugin_wrapper.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "plugin_wrapper.h" + +namespace { +std::set g_metaIdSet = { + OHOS::Media::Plugin::MetaID::MIME, + OHOS::Media::Plugin::MetaID::STREAM_INDEX, + OHOS::Media::Plugin::MetaID::MEDIA_CODEC_CONFIG, + OHOS::Media::Plugin::MetaID::MEDIA_BITRATE, + OHOS::Media::Plugin::MetaID::AUDIO_CHANNELS, + OHOS::Media::Plugin::MetaID::AUDIO_SAMPLE_RATE, + OHOS::Media::Plugin::MetaID::AUDIO_SAMPLE_FORMAT, + OHOS::Media::Plugin::MetaID::AUDIO_SAMPLE_PRE_FRAME, + OHOS::Media::Plugin::MetaID::AUDIO_CHANNEL_LAYOUT, + OHOS::Media::Plugin::MetaID::MEDIA_TITLE, + OHOS::Media::Plugin::MetaID::MEDIA_ARTIST, + OHOS::Media::Plugin::MetaID::MEDIA_LYRICIST, + OHOS::Media::Plugin::MetaID::MEDIA_ALBUM, + OHOS::Media::Plugin::MetaID::MEDIA_ALBUM_ARTIST, + OHOS::Media::Plugin::MetaID::MEDIA_DATE, + OHOS::Media::Plugin::MetaID::MEDIA_COMMENT, + OHOS::Media::Plugin::MetaID::MEDIA_GENRE, + OHOS::Media::Plugin::MetaID::MEDIA_DESCRIPTION, + OHOS::Media::Plugin::MetaID::MEDIA_COPYRIGHT, + OHOS::Media::Plugin::MetaID::MEDIA_LANGUAGE, + OHOS::Media::Plugin::MetaID::MEDIA_LYRICS, + OHOS::Media::Plugin::MetaID::MEDIA_DURATION, + OHOS::Media::Plugin::MetaID::MEDIA_FILE_EXTENSION, + OHOS::Media::Plugin::MetaID::MEDIA_FILE_SIZE, + OHOS::Media::Plugin::MetaID::AUDIO_MPEG_VERSION, + OHOS::Media::Plugin::MetaID::AUDIO_MPEG_LAYER, + OHOS::Media::Plugin::MetaID::AUDIO_AAC_PROFILE, + OHOS::Media::Plugin::MetaID::AUDIO_AAC_LEVEL, + OHOS::Media::Plugin::MetaID::AUDIO_AAC_STREAM_FORMAT, + OHOS::Media::Plugin::MetaID::VIDEO_WIDTH, + OHOS::Media::Plugin::MetaID::VIDEO_HEIGHT, + OHOS::Media::Plugin::MetaID::VIDEO_PIXEL_FORMAT, +}; +} + +namespace OHOS { +namespace Media { +namespace Plugin { +void ConvertToMediaInfoHelper(uint32_t pkgVersion, const MediaInfo& src, MediaInfoHelper& dest) +{ + for (auto const& global : src.general) { + dest.globalMeta.SetData(MetaID(global.first), global.second); + } + size_t streamSize = src.tracks.size(); + if (streamSize <= 0) { + return; + } + dest.streamMeta.resize(streamSize); + for (size_t i = 0; i < streamSize; ++i) { + for (auto const& meta : src.tracks[i]) { + if (g_metaIdSet.count(MetaID(meta.first))) { + dest.streamMeta[i].SetData(MetaID(meta.first), meta.second); + } + } + } +} +} +} +} + diff --git a/engine/plugin/core/plugin_wrapper.h b/engine/plugin/core/plugin_wrapper.h new file mode 100644 index 00000000..afc0d2dd --- /dev/null +++ b/engine/plugin/core/plugin_wrapper.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_WRAPPER_H +#define HISTREAMER_PLUGIN_CORE_WRAPPER_H + +#include "demuxer.h" +#include "interface/demuxer_plugin.h" +#include "interface/plugin_base.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct DataSourceWrapper : DataSource { + DataSourceWrapper(uint32_t pkgVersion, std::shared_ptr dataSource) + : version(pkgVersion), helper(std::move(dataSource)) + { + } + + ~DataSourceWrapper() override = default; + + Status ReadAt(int64_t offset, std::shared_ptr& buffer, size_t expectedLen) override + { + return helper->ReadAt(offset, buffer, expectedLen); + } + + Status GetSize(size_t& size) override + { + return helper->GetSize(size); + } + +private: + __attribute__((unused)) uint32_t version; + std::shared_ptr helper; +}; + +struct AllocatorHelperWrapper : AllocatorHelper { + AllocatorHelperWrapper(uint32_t pkgVersion, std::shared_ptr alloc) + : version_(pkgVersion), allocator_(std::move(alloc)) {} + + ~AllocatorHelperWrapper() override = default; + + void* Alloc(size_t size) override + { + return allocator_->Alloc(size); + } + + void Free(void* ptr) override + { + allocator_->Free(ptr); + } + +private: + __attribute__((unused)) uint32_t version_; + std::shared_ptr allocator_; +}; + +void ConvertToMediaInfoHelper(uint32_t pkgVersion, const MediaInfo& src, MediaInfoHelper& dest); +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_WRAPPER_H diff --git a/engine/plugin/core/source.cpp b/engine/plugin/core/source.cpp new file mode 100644 index 00000000..64f617c1 --- /dev/null +++ b/engine/plugin/core/source.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "source.h" +#include "interface/source_plugin.h" + +using namespace OHOS::Media::Plugin; + +Source::Source(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin) + : Base(pkgVer, apiVer, plugin), source_(std::move(plugin)) +{ +} + +Status Source::SetSource(std::string& uri, std::shared_ptr> params) +{ + return source_->SetSource(uri, params); +} + +Status Source::Read(std::shared_ptr& buffer, size_t expectedLen) +{ + return source_->Read(buffer, expectedLen); +} + +Status Source::GetSize(size_t& size) +{ + return source_->GetSize(size); +} + +bool Source::IsSeekable() +{ + return source_->IsSeekable(); +} + +Status Source::SeekTo(uint64_t offset) +{ + return source_->SeekTo(offset); +} diff --git a/engine/plugin/core/source.h b/engine/plugin/core/source.h new file mode 100644 index 00000000..a15c6f4b --- /dev/null +++ b/engine/plugin/core/source.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_SOURCE_H +#define HISTREAMER_PLUGIN_CORE_SOURCE_H + +#include +#include + +#include "base.h" +#include "common/plugin_types.h" +#include "common/plugin_tags.h" +#include "common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct SourcePlugin; + +class Source : public Base { +public: + Source(const Source &) = delete; + Source operator=(const Source &) = delete; + ~Source() override = default; + + Status SetSource(std::string& uri, std::shared_ptr> params = nullptr); + + Status Read(std::shared_ptr &buffer, size_t expectedLen); + + Status GetSize(size_t& size); + + bool IsSeekable(); + + Status SeekTo(uint64_t offset); + +private: + friend class PluginManager; + + Source(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); + +private: + std::shared_ptr source_; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_SOURCE_H diff --git a/engine/plugin/core/video_sink.cpp b/engine/plugin/core/video_sink.cpp new file mode 100644 index 00000000..a0ecd4c6 --- /dev/null +++ b/engine/plugin/core/video_sink.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "video_sink.h" + +#include "interface/video_sink_plugin.h" + +using namespace OHOS::Media::Plugin; + +VideoSink::VideoSink(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin) + : Base(pkgVer, apiVer, plugin), videoSink(std::move(plugin)) +{ +} + +Status VideoSink::Pause() +{ + return videoSink->Pause(); +} + +Status VideoSink::Resume() +{ + return videoSink->Resume(); +} + +Status VideoSink::Flush() +{ + return videoSink->Flush(); +} + +Status VideoSink::Write(const std::shared_ptr& input) +{ + return videoSink->Write(input); +} diff --git a/engine/plugin/core/video_sink.h b/engine/plugin/core/video_sink.h new file mode 100644 index 00000000..76c3f12c --- /dev/null +++ b/engine/plugin/core/video_sink.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_VIDEO_SINK_H +#define HISTREAMER_PLUGIN_CORE_VIDEO_SINK_H + +#include +#include "base.h" +#include "common/plugin_buffer.h" +#include "common/plugin_tags.h" +#include "common/plugin_types.h" +#include "memory" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct VideoSinkPlugin; + +class VideoSink : public Base { +public: + VideoSink(const VideoSink&) = delete; + VideoSink operator=(const VideoSink&) = delete; + ~VideoSink() override = default; + + Status Pause(); + + Status Resume(); + + Status Flush(); + + Status Write(const std::shared_ptr& input); + +private: + friend class PluginManager; + + VideoSink(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); + +private: + std::shared_ptr videoSink; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_CORE_VIDEO_SINK_H diff --git a/engine/plugin/interface/audio_sink_plugin.h b/engine/plugin/interface/audio_sink_plugin.h new file mode 100644 index 00000000..18ffdd51 --- /dev/null +++ b/engine/plugin/interface/audio_sink_plugin.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_AUDIO_SINK_PLUGIN_H +#define HISTREAMER_PLUGIN_INTF_AUDIO_SINK_PLUGIN_H + +#include "common/plugin_buffer.h" +#include "common/plugin_caps.h" +#include "plugin_base.h" +#include "plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief Audio Sink Plugin. + * + * Component that receives media streams. + * + * @since 1.0 + * @version 1.0 + */ +struct AudioSinkPlugin : public PluginBase { + /** + * @brief Get the mute operation set for the audio. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param mute Indicates the mute operation set for the audio. + * Value true means that the audio is muted, and false means the opposite. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status GetMute(bool& mute) = 0; + + /** + * @brief Set the mute operation for the audio. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param mute Indicates the mute operation set for the audio. + * Value true means that the audio is muted, and false means the opposite. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status SetMute(bool mute) = 0; + + /** + * @brief Get the audio volume. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param volume Indicates the volume to set. The value ranges from 0.0 to 1.0. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status GetVolume(float& volume) = 0; + + /** + * @brief Set the audio volume. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param volume Indicates the volume to set. The value ranges from 0.0 to 1.0. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + * @retval ERROR_INVALID_DATA: The value is not in the valid range. + */ + virtual Status SetVolume(float volume) = 0; + + /** + * @brief Get the current audio rendering speed. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param speed Indicates the pointer to the current rendering speed to obtain. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status GetSpeed(float& speed) = 0; + + /** + * @brief Set the audio rendering speed. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param speed speed Indicates the pointer to the current rendering speed to obtain. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + * @retval ERROR_INVALID_DATA: The value is not in the valid range. + */ + virtual Status SetSpeed(float speed) = 0; + + /** + * @brief Pauses audio rendering + * + * The function is valid only in the RUNNING state. If the pause is successful, + * the plugin enters the PAUSED state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Pause() = 0; + + /** + * @brief Resumes audio rendering + * + * The function is valid only in the PAUSED state. If the resume is successful, + * the plugin enters the RUNNING state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Resume() = 0; + + /** + * @brief Get the estimated latency of the audio device driver. + * + * The function is valid only in the after PREPARED state. + * + * @param ms Indicates the pointer to the latency (in milliseconds) to be obtained. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status GetLatency(uint64_t& ms) = 0; + + /** + * @brief Get the audio frame size, that is, the length (in bytes) of a frame. + * + * The function is valid only in the RUNNING state. + * + * @param size size Indicates the pointer to the audio frame size (in bytes). + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status GetFrameSize(size_t& size) = 0; + + /** + * @brief Get the number of audio frames in the audio buffer. + * + * The function is valid only in the RUNNING state. + * + * @param count Indicates the pointer to the number of audio frames in the audio buffer. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status GetFrameCount(uint32_t& count) = 0; + + /** + * @brief Writes a frame of output data into the audio driver for rendering. + * + * The function is valid only in the RUNNING state. + * + * @param input Indicates the pointer to the frame to write. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Write(const std::shared_ptr& input) = 0; + + /** + * @brief Flushes data in the audio buffer. + * + * The function is valid only in after RUNNING state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Flush() = 0; +}; + +/// Audio sink plugin api major number. +#define AUDIO_SINK_API_VERSION_MAJOR (1) + +/// Audio sink plugin api minor number +#define AUDIO_SINK_API_VERSION_MINOR (0) + +/// Audio sink plugin version +#define AUDIO_SINK_API_VERSION MAKE_VERSION(AUDIO_SINK_API_VERSION_MAJOR, AUDIO_SINK_API_VERSION_MINOR) + +/** + * @brief Describes the audio sink plugin information. + * + * @since 1.0 + * @version 1.0 + */ +struct AudioSinkPluginDef : public PluginDefBase { + CapabilitySet inCaps {}; ///< Plug-in input capability, For details, @see Capability. + PluginCreatorFunc creator {nullptr}; ///< Audio sink plugin create function. + AudioSinkPluginDef() + { + apiVersion = AUDIO_SINK_API_VERSION; ///< Audio sink plugin version. + pluginType = PluginType::AUDIO_SINK; ///< Plugin type, MUST be AUDIO_SINK. + } +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_AUDIO_SINK_PLUGIN_H diff --git a/engine/plugin/interface/codec_plugin.h b/engine/plugin/interface/codec_plugin.h new file mode 100644 index 00000000..d648d8c6 --- /dev/null +++ b/engine/plugin/interface/codec_plugin.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_CODEC_PLUGIN_H +#define HISTREAMER_PLUGIN_INTF_CODEC_PLUGIN_H + +#include +#include "common/plugin_caps.h" +#include "plugin_base.h" +#include "plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief Plugin data callback interface. + * + * @since 1.0 + * @version 1.0 + */ +struct DataCallback { + virtual ~DataCallback() = default; + + /** + * @brief When the input buffer has been consumed inside the plugin. + * + * This function works with QueueInputBuffer to implement input data transmission. + * + * @param input Indicates the pointer to the input data. + */ + virtual void OnInputBufferDone(const std::shared_ptr& input) = 0; + + /** + * @brief When the out buffer has been produced inside the plugin. + * + * This function works with QueueOutputBuffer to implement out data transmission. + * + * @param output Indicates the pointer to the output data. + */ + virtual void OnOutputBufferDone(const std::shared_ptr& output) = 0; +}; + +/** + * @brief Codec Plugin Interface. + * + * Used for audio and video encoding and decoding. + * + * @since 1.0 + * @version 1.0 + */ +struct CodecPlugin : public PluginBase { + /** + * @brief Queues input data + * + * This function works with DataCallback::OnInputBufferDone to implement input data transmission. + * + * The function is valid only in the RUNNING state. + * + * @param inputBuffer Indicates the pointer to the input data. + * @param timeoutMs Indicates the timeout duration. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_INVALID_DATA: The input buffer is invalid. + * @retval ERROR_TIMED_OUT: Operation timeout. + */ + virtual Status QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) = 0; + /** + * @brief Queues output data + * This function works with DataCallback::OnOutputBufferDone to implement output data transmission. + * + * The function is valid only in the RUNNING state. + * + * @param outputBuffers Indicates the pointer to the output data. + * @param timeoutMs Indicates the timeout duration. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_INVALID_DATA: The output buffer is invalid. + * @retval ERROR_TIMED_OUT: Operation timeout. + */ + virtual Status QueueOutputBuffer(const std::shared_ptr& outputBuffers, int32_t timeoutMs) = 0; + + /** + * @brief Flushes data in the audio buffer. + * + * The function is valid only in after RUNNING state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status Flush() = 0; + + /** + * @brief Sets the plugin callback data to notify the plugin user. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param cb Data callback, NULL callback listening is canceled. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status SetDataCallback(const std::weak_ptr& dataCallback) = 0; +}; + +/// Codec plugin api major number. +#define CODEC_API_VERSION_MAJOR (1) + +/// Codec plugin api minor number +#define CODEC_API_VERSION_MINOR (0) + +/// Codec plugin version +#define CODEC_API_VERSION MAKE_VERSION(CODEC_API_VERSION_MAJOR, CODEC_API_VERSION_MINOR) + +/** + * @brief The Codec Type. + * + * @since 1.0 + * @version 1.0 + */ +enum struct CodecType { + AUDIO_DECODER, ///< Audio decoder + AUDIO_ENCODER, ///< Audio encoder + VIDEO_DECODER, ///< video decoder + VIDEO_ENCODER, ///< video encoder +}; + +/** + * @brief Describes the codec plugin information. + * + * @since 1.0 + * @version 1.0 + */ +struct CodecPluginDef : public PluginDefBase { + CodecType codecType {}; + CapabilitySet inCaps {}; ///< Plug-in input capability, For details, @see Capability. + CapabilitySet outCaps {}; ///< Plug-in output capability, For details, @see Capability. + PluginCreatorFunc creator {nullptr}; ///< Codec plugin create function. + CodecPluginDef() + { + apiVersion = CODEC_API_VERSION; ///< Codec plugin version + pluginType = PluginType::CODEC; ///< Plugin type, MUST be CODEC. + } +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_CODEC_PLUGIN_H diff --git a/engine/plugin/interface/demuxer_plugin.h b/engine/plugin/interface/demuxer_plugin.h new file mode 100644 index 00000000..19f5334e --- /dev/null +++ b/engine/plugin/interface/demuxer_plugin.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_DEMUXER_PLUGIN_H +#define HISTREAMER_PLUGIN_INTF_DEMUXER_PLUGIN_H + +#include +#include +#include "common/plugin_caps.h" +#include "plugin_base.h" +#include "plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief MediaInfo is a facilitate unified display of the relevant technical and + * tag data for video and audio files. + * + * MediaInfo reveals information such as: + * - General: artist, album, author, copyright, date, duration, etc. + * - Tracks: such as codec, channel, bitrate, etc. + * @see Tag + * + * @since 1.0 + * @version 1.0 + */ +struct MediaInfo { + TagMap general; ///< General information + std::vector tracks; ///< Media tracks, include audio, video and text +}; + +/** + * @brief Data source operation interface. + * + * @since 1.0 + * @version 1.0 + */ +struct DataSource { + /// Destructor + virtual ~DataSource() = default; + + /** + * @brief Read data from data source. + * + * @param offset Offset of read position + * @param buffer Storage of the read data + * @param expectedLen Expected data size to be read + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_NOT_ENOUGH_DATA: Data not enough + * @retval END_OF_STREAM: End of stream + */ + virtual Status ReadAt(int64_t offset, std::shared_ptr& buffer, size_t expectedLen) = 0; + + /** + * @brief Get data source size. + * + * @param size data source size. + * @return Execution status return. + * @retval OK: Plugin reset succeeded. + */ + virtual Status GetSize(size_t& size) = 0; +}; + +/** + * @brief Demuxer Plugin Interface. + * + * Used for audio and video media file parse. + * + * @since 1.0 + * @version 1.0 + */ +struct DemuxerPlugin : public PluginBase { + /** + * @brief Set the data source to demuxer component. + * + * The function is valid only in the CREATED state. + * + * @param source Data source where data read from. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status SetDataSource(const std::shared_ptr& source) = 0; + + /** + * @brief Get the attributes of a media file. + * + * The attributes contain file and stream attributes. + * The function is valid only after INITIALIZED state. + * + * @param mediaInfo Indicates the pointer to the source attributes + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status GetMediaInfo(MediaInfo& mediaInfo) = 0; + + /** + * @brief Get the stack count from the data source. + * + * The function is valid only after INITIALIZED state. + * + * @return number of tracks. + */ + virtual size_t GetTrackCount() = 0; + + /** + * @brief Select a specified media track. + * + * The function is valid only after RUNNING state. + * + * @param trackId Identifies the media track. If an invalid value is passed, the default media track specified. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status SelectTrack(int32_t trackId) = 0; + + /** + * @brief Unselect a specified media track from which the demuxer reads data frames. + * + * The function is valid only after RUNNING state. + * + * @param trackId Identifies the media track. ignore the invalid value is passed. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status UnselectTrack(int32_t trackId) = 0; + + /** + * @brief Get the ID of the media track selected by the demuxer for output. + * + * The function is valid only after RUNNING state. + * + * @param trackIds Identifies the array of selected media tracks. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status GetSelectedTracks(std::vector& trackIds) = 0; + + /** + * @brief Reads data frames. + * + * The function is valid only after RUNNING state. + * + * @param buffer Indicates the pointer to the data buffer. + * @param timeOutMs Indicates the time required for waiting data frame read. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_TIMED_OUT: Operation timeout. + */ + virtual Status ReadFrame(Buffer& buffer, int32_t timeOutMs) = 0; + + /** + * @brief Seeks for a specified position for the demuxer. + * + * After being started, the demuxer seeks for a specified position to read data frames. + * + * The function is valid only after RUNNING state. + * + * @param trackId Identifies the stream in the media file. + * @param timeStampUs Indicates the target position, in microseconds. + * @param mode Indicates the seek mode. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_INVALID_DATA: The input data is invalid. + */ + virtual Status SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) = 0; +}; + +/// Demuxer plugin api major number. +#define DEMUXER_API_VERSION_MAJOR (1) + +/// Demuxer plugin api minor number +#define DEMUXER_API_VERSION_MINOR (0) + +/// Demuxer plugin version +#define DEMUXER_API_VERSION MAKE_VERSION(DEMUXER_API_VERSION_MAJOR, DEMUXER_API_VERSION_MINOR) + +/// Demuxer sniff function +using DemuxerPluginSnifferFunc = int (*)(const std::string& name, std::shared_ptr dataSource); + +/** + * @brief Describes the demuxer plugin information. + * + * @since 1.0 + * @version 1.0 + */ +struct DemuxerPluginDef : public PluginDefBase { + std::vector extensions; ///< File extensions supported by demuxer + CapabilitySet inCaps; ///< Plug-in input capability, For details, @see Capability. + CapabilitySet outCaps; ///< Plug-in output capability, For details, @see Capability. + PluginCreatorFunc creator {nullptr}; ///< Demuxer plugin create function. + DemuxerPluginSnifferFunc sniffer {nullptr}; ///< Demuxer plugin sniff function. + DemuxerPluginDef() + { + apiVersion = DEMUXER_API_VERSION; ///< Demuxer plugin version. + pluginType = PluginType::DEMUXER; ///< Plugin type, MUST be DEMUXER. + } +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_DEMUXER_PLUGIN_H diff --git a/engine/plugin/interface/plugin_base.h b/engine/plugin/interface/plugin_base.h new file mode 100644 index 00000000..4b15ed00 --- /dev/null +++ b/engine/plugin/interface/plugin_base.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_PLUGIN_BASE_H +#define HISTREAMER_PLUGIN_INTF_PLUGIN_BASE_H + +#include +#include "common/plugin_tags.h" +#include "common/plugin_types.h" +#include "common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief Plugin status callback interface. + * + * @since 1.0 + * @version 1.0 + */ +struct Callback { + /// Destructor + virtual ~Callback() = default; + + /** + * @brief When asynchronous time occurs during plugin running, + * the plugin implementer invokes this interface to notify the plugin user. + * + * @note Reserved Interface, Not used yet. + * + * @param event Event ID. + */ + virtual void onEvent(int32_t event) = 0; + + /** + * @brief When an error occurs during plugin running, + * the plugin implementer invokes this interface to notify the plugin user. + * + * @param errorType Error type, For details, @see Status + */ + virtual void onError(Status errorType) = 0; +}; + +/** + * @brief Base class of a plugin. All plugins of different types inherit this interface. + * + * @details The base class contains only common operation methods and defines basic operation processes. + * Different operations are valid only in the corresponding states. Some operations also change the plugin status. + * For details, see the description of each function. + * + * @since 1.0 + * @version 1.0 + */ +struct PluginBase { + /// Destructor + virtual ~PluginBase() = default; + + /** + * @brief Plugin initialization, which is used to load external resources or plugin common resources. + * + * The function is valid only in the CREATED state. If the initialization is successful, + * the plugin enters the INITIALIZED state. + * + * @return Execution status return + * @retval OK: Plugin initialization succeeded. + * @retval ERROR_NO_MEMORY: Memory allocation or external resource loading error caused by insufficient memory. + * @retval ERROR_WRONG_STATE: Call this function in non CREATED state + */ + virtual Status Init() = 0; + + /** + * @brief Plugin deinitialize to release resources. + * + * This function can be invoked in any state. + * After the function is invoked, the plugin will no longer be available. + * + * @return Execution status return + * @retval OK: Plugin deinitialize succeeded. + */ + virtual Status Deinit() = 0; + + /** + * @brief Preparing parameters required or allocate the memory for plugin running. + * + * The function is valid only in the INITIALIZED state. If the prepare is successful, + * the plugin enters the PREPARED state. + * + * @return Execution status return + * @retval OK: Plugin deinitialize succeeded. + * @retval ERROR_NO_MEMORY: Memory allocation error caused by insufficient memory. + * @retval ERROR_WRONG_STATE: Call this function in non INITIALIZED state + */ + virtual Status Prepare() = 0; + + /** + * @brief Reset the plugin, reset the plugin running status and parameters before Prepare. + * + * The function is valid only in the PREPARED/RUNNING/PAUSED state. If the reset is successful, + * the plugin enters the INITIALIZED state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in wrong state + * @retval ERROR_UNIMPLEMENTED: This method is not implemented and cannot respond to reset. + */ + virtual Status Reset() = 0; + + /** + * @brief The plugin enters the running state and can process data. + * + * The function is valid only in the PREPARED state. If the start is successful, + * the plugin enters the RUNNING state. If an error occurs during the running, + * the plu-in status can be changed through asynchronous callback. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non PREPARED state + */ + virtual Status Start() = 0; + + /** + * @brief The plugin enters the stopped state and stops processing data. + * + * The function is valid only in the RUNNING state. If the stop is successful, + * the plugin enters the PREPARED state. Temporary data generated during the operation will be cleared. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non RUNNING state + */ + virtual Status Stop() = 0; + + /** + * @brief Determines whether the current plugin supports the specified parameter. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param tag Plugin parameter type, which is described by tag. + * @return true is supported, otherwise, false. + */ + virtual bool IsParameterSupported(Tag tag) = 0; + + /** + * @brief Get the value of a specified parameter. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param tag Plugin parameter type, which is described by tag. + * @param value Plugin parameter value. which is described by Any type. Need check the real type in tag. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + * @retval ERROR_INVALID_PARAMETER: The plugin does not support this parameter. + */ + virtual Status GetParameter(Tag tag, ValueType &value) = 0; + + /** + * @brief Set the specified parameter. The value must be within the valid range of the parameter. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param tag Plugin parameter type, which is described by tag. + * @param value Plugin parameter value. which is described by Any type. Need check the real type in tag. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + * @retval ERROR_INVALID_PARAMETER: The plugin does not support this parameter. + * @retval ERROR_INVALID_DATA: The value is not in the valid range. + * @retval ERROR_MISMATCHED_TYPE: The data type is mismatched. + */ + virtual Status SetParameter(Tag tag, const ValueType &value) = 0; + + /** + * @brief Get the plugin running state. + * + * @param state Plugin running state. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + */ + virtual Status GetState(State &state) = 0; + + /** + * @brief Get the allocator specified by the plugin. + * The allocator can allocate memory types that meet the plugin requirements. + * + * @return Obtains the allocator object or NULL if the plugin does not have requirements for memory. + */ + virtual std::shared_ptr GetAllocator() = 0; + + /** + * @brief Sets the plugin callback message to notify the plugin user. + * + * This function can be called in any state except DESTROYED and INVALID. + * + * @param cb Message callback, NULL callback listening is canceled. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status SetCallback(const std::shared_ptr &cb) = 0; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_PLUGIN_BASE_H diff --git a/engine/plugin/interface/plugin_definition.h b/engine/plugin/interface/plugin_definition.h new file mode 100644 index 00000000..5ae62470 --- /dev/null +++ b/engine/plugin/interface/plugin_definition.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_PLUGIN_DEFINITION_H +#define HISTREAMER_PLUGIN_INTF_PLUGIN_DEFINITION_H + +#include +#include +#include "common/plugin_types.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief Macro definition, creating the version information. + * + * @details The versioning is the process of assigning a unique version number to a unique state + * of plugin interface. Within a given version number category (major, minor), these numbers are + * usually assigned in ascending order and correspond to new developments in the plugin. + * + * Given a version number MAJOR.MINOR: + * - MAJOR: When you make incompatible API changes. + * - MINOR: When you add features in a backwards-compatible manner or do backwards-compatible bug fixes. + */ +#define MAKE_VERSION(MAJOR, MINOR) ((((MAJOR)&0xFFFF) << 16) | ((MINOR)&0xFFFF)) + +/// Plugin interface major number +#define PLUGIN_INTERFACE_VERSION_MAJOR (1) + +/// Plugin interface minor number +#define PLUGIN_INTERFACE_VERSION_MINOR (0) + +/// Plugin interface version +#define PLUGIN_INTERFACE_VERSION MAKE_VERSION(PLUGIN_INTERFACE_VERSION_MAJOR, PLUGIN_INTERFACE_VERSION_MINOR) + +/** + * @enum License Type. + * an official permission or permit. + * + * @since 1.0 + * @version 1.0 + */ +enum struct LicenseType : uint8_t { + APACHE_V2, ///< The Apache License 2.0 + LGPL, ///< The GNU Lesser General Public License +}; + +/** + * @brief Definition of plugin packaging information. + * + * @since 1.0 + * @version 1.0 + */ +struct PackageDef { + uint32_t pkgVersion; ///< Package information version, which indicates the latest plug-in interface version + ///< used by the plugin in the package. The default value is PLUGIN_INTERFACE_VERSION. + + std::string name; ///< Package name. The plugin framework registers the plugin using this name. + ///< If the plugins are packaged as a dynamic library, the name of library + ///< must be in the format of "libplugin_.so". + + LicenseType + licenseType; ///< The License information of the plugin in the package. + ///< The different plugins must be the same. + ///< The plugin framework processing in the plugin running state based on different license. +}; + +/// Plugin create function. All plugins must implement this function. +template +using PluginCreatorFunc = std::shared_ptr(*)(const std::string& name); + +/** + * @brief Describes the basic information about the plugin. + * + * @since 1.0 + * @version 1.0 + */ +struct PluginDefBase { + uint32_t apiVersion; ///< Versions of different plugins. Different types of plugin have their own versions. + + PluginType pluginType = PluginType::INVALID_TYPE; ///< Describe the plugin type, e.g. 'source', 'codec'. + + std::string name; ///< Indicates the name of a plugin. The name of the same type plugins must be unique. + ///< Plugins with the same name may fail to be registered. + + std::string description; ///< Detailed description of the plugin. + + uint32_t rank; ///< Plugin score. The plugin with a high score may be preferred. You can evaluate the + ///< plugin score in terms of performance, version support, and license. Range: 0 to 100. +}; + +/** + * @brief The plugin registration interface. + * The plugin framework will provide the implementation. + * Developers only need to invoke the API to register the plugin. + * + * @since 1.0 + * @version 1.0 + */ +struct Register { + virtual ~Register() = default; + /** + * @brief Register the plugin. + * + * @param def Basic information about the plugin + * @return Registration status return + * @retval OK: The plugin is registered succeed. + * @retval ERROR_ALREADY_EXISTS: The plugin already exists in plugin registered. + * @retval ERROR_INCOMPATIBLE_VERSION: Incompatible version during plugin registration. + */ + virtual Status AddPlugin(const PluginDefBase& def) = 0; +}; + +/** + * @brief The package registration interface. + * The plugin framework will provide the implementation and auto invoke the API to + * finish the package registration when plugin framework first time be initialized. + * + * @since 1.0 + * @version 1.0 + */ +struct PackageRegister : Register { + ~PackageRegister() override = default; + + /** + * @brief Register the package. + * During package registration, all plugins in the package are automatically registered. + * + * @param def plugin packaging information. + * @return Registration status return + * @retval OK: The package is registered succeed without any errors. + * @retval ERROR_ALREADY_EXISTS: The package or plugins already exists. + * @retval ERROR_INCOMPATIBLE_VERSION: Incompatible plugin interface version or api version. + */ + virtual Status AddPackage(const PackageDef& def) = 0; +}; + +/// Plugin registration function, all plugins must be implemented. +using RegisterFunc = Status (*)(std::shared_ptr reg); + +/// Plugin deregister function, all plugins must be implemented. +using UnregisterFunc = void (*)(); + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +#define PLUGIN_EXPORT extern "C" __declspec(dllexport) +#else +#if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) +#define PLUGIN_EXPORT extern "C" __attribute__((visibility("default"))) +#else +#define PLUGIN_EXPORT +#endif +#endif + +/// Macro definition, string concatenation +#define PLUGIN_PASTE_ARGS(str1, str2) str1##str2 + +/// Macro definition, string concatenation +#define PLUGIN_PASTE(str1, str2) PLUGIN_PASTE_ARGS(str1, str2) + +/// Macro definition, stringify +#define PLUGIN_STRINGIFY_ARG(str) #str + +/// Macro definition, stringify +#define PLUGIN_STRINGIFY(str) PLUGIN_STRINGIFY_ARG(str) + +/** + * @brief Macro definition, Defines basic plugin information. + * Which is invoked during plugin package registration. All plugin packages must be implemented. + * + * @param name Package name. For details, @see PackageDef::name + * @param license Package License, For details, @see PackageDef::licenseType + * @param registerFunc Plugin registration function, MUST NOT be NULL. + * @param unregisterFunc Plugin deregister function,MUST NOT be NULL. + */ +#define PLUGIN_DEFINITION(name, license, registerFunc, unregisterFunc) \ + PLUGIN_EXPORT const OHOS::Media::Plugin::Status PLUGIN_PASTE(register_, name)( \ + std::shared_ptr reg) \ + { \ + std::dynamic_pointer_cast(reg)->AddPackage( \ + {PLUGIN_INTERFACE_VERSION, PLUGIN_STRINGIFY(name), license}); \ + return registerFunc(reg); \ + } \ + PLUGIN_EXPORT const void PLUGIN_PASTE(unregister_, name)() \ + { \ + unregisterFunc(); \ + } +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_PLUGIN_DEFINITION_H diff --git a/engine/plugin/interface/source_plugin.h b/engine/plugin/interface/source_plugin.h new file mode 100644 index 00000000..3eae469d --- /dev/null +++ b/engine/plugin/interface/source_plugin.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_SOURCE_PLUGIN_H +#define HISTREAMER_PLUGIN_INTF_SOURCE_PLUGIN_H + +#include +#include +#include "common/plugin_caps.h" +#include "plugin_base.h" +#include "plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief Source Plugin Interface. + * + * The data source may be network push or active read. + * + * @since 1.0 + * @version 1.0 + */ +struct SourcePlugin : public PluginBase { + /** + * @brief Set the data source to demuxer component. + * + * The function is valid only in the CREATED state. + * + * @param uri data uri + * @param params Special data, which is used to assist in obtaining data in the URI + * or to read the detailed information required by the data. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_INVALID_DATA: Uri is not supported. + */ + virtual Status SetSource(std::string& uri, std::shared_ptr> params = nullptr) = 0; + + /** + * @brief Read data from data source. + * + * The function is valid only after RUNNING state. + * + * @param buffer Storage of the read data + * @param expectedLen Expected data size to be read + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_NOT_ENOUGH_DATA: Data not enough + * @retval END_OF_STREAM: End of stream + */ + virtual Status Read(std::shared_ptr& buffer, size_t expectedLen) = 0; + + /** + * @brief Get data source size. + * + * The function is valid only after INITIALIZED state. + * + * @param size data source size. + * @return Execution status return. + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual Status GetSize(size_t& size) = 0; + + /** + * @brief Indicates that the current source can be seek. + * + * The function is valid only after INITIALIZED state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + */ + virtual bool IsSeekable() = 0; + + /** + * @brief Seeks for a specified position for the source. + * + * After being started, the source seeks for a specified position to read data frames. + * + * The function is valid only after RUNNING state. + * + * @param offset position to read data frames + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state + * @retval ERROR_INVALID_DATA: The offset is invalid. + */ + virtual Status SeekTo(uint64_t offset) = 0; +}; + +/// Source plugin api major number. +#define SOURCE_API_VERSION_MAJOR (1) + +/// Source plugin api minor number +#define SOURCE_API_VERSION_MINOR (0) + +/// Source plugin version +#define SOURCE_API_VERSION MAKE_VERSION(SOURCE_API_VERSION_MAJOR, SOURCE_API_VERSION_MINOR) + +/** + * @brief Describes the source plugin information. + * + * @since 1.0 + * @version 1.0 + */ +struct SourcePluginDef : public PluginDefBase { + std::string protocol; ///< Protocols supported by demuxer + CapabilitySet outCaps; ///< Plug-in output capability, For details, @see Capability. + PluginCreatorFunc creator {nullptr}; ///< Source plugin create function. + SourcePluginDef() + { + apiVersion = SOURCE_API_VERSION; ///< Source plugin version. + pluginType = PluginType::SOURCE; ///< Plugin type, MUST be SOURCE. + } +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_SOURCE_PLUGIN_H diff --git a/engine/plugin/interface/video_sink_plugin.h b/engine/plugin/interface/video_sink_plugin.h new file mode 100644 index 00000000..86552d50 --- /dev/null +++ b/engine/plugin/interface/video_sink_plugin.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_INTF_VIDEO_SINK_PLUGIN_H +#define HISTREAMER_PLUGIN_INTF_VIDEO_SINK_PLUGIN_H + +#include "common/plugin_buffer.h" +#include "common/plugin_caps.h" +#include "plugin_base.h" +#include "plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +/** + * @brief Video Sink Plugin. + * + * Component that receives media streams. + * + * @since 1.0 + * @version 1.0 + */ +struct VideoSinkPlugin : public PluginBase { + /** + * @brief Pauses video rendering + * + * The function is valid only in the RUNNING state. If the pause is successful, + * the plugin enters the PAUSED state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Pause() = 0; + + /** + * @brief Resumes video rendering + * + * The function is valid only in the PAUSED state. If the resume is successful, + * the plugin enters the RUNNING state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Resume() = 0; + + /** + * @brief Writes a frame of output data into the video dispaly device for rendering. + * + * The function is valid only in the RUNNING state. + * + * @param input Indicates the pointer to the frame to write. + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Write(const std::shared_ptr& input) = 0; + + /** + * @brief Flushes data in the video buffer. + * + * The function is valid only in after RUNNING state. + * + * @return Execution status return + * @retval OK: Plugin reset succeeded. + * @retval ERROR_WRONG_STATE: Call this function in non wrong state. + */ + virtual Status Flush() = 0; +}; + +/// Video sink plugin api major number. +#define VIDEO_SINK_API_VERSION_MAJOR (1) + +/// Video sink plugin api minor number +#define VIDEO_SINK_API_VERSION_MINOR (0) + +/// Video sink plugin version +#define VIDEO_SINK_API_VERSION MAKE_VERSION(VIDEO_SINK_API_VERSION_MAJOR, VIDEO_SINK_API_VERSION_MINOR) + +/** + * @brief Describes the video sink plugin information. + * + * @since 1.0 + * @version 1.0 + */ +struct VideoSinkPluginDef : public PluginDefBase { + CapabilitySet inCaps; ///< Plug-in input capability, For details, @see Capability. + PluginCreatorFunc creator; ///< Video sink plugin create function. + VideoSinkPluginDef() + { + apiVersion = VIDEO_SINK_API_VERSION; ///< Video sink plugin version. + pluginType = PluginType::VIDEO_SINK; ///< Plugin type, MUST be VIDEO_SINK. + } +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_PLUGIN_INTF_VIDEO_SINK_PLUGIN_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp new file mode 100644 index 00000000..e1a05018 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "Ffmpeg_Au_Decoder" + +#include "audio_ffmpeg_decoder_plugin.h" +#include +#include +#include +#include "foundation/constants.h" +#include "foundation/memory_helper.h" + +#include "plugin/common/plugin_audio_tags.h" +#include "plugin/common/plugin_buffer.h" +#include "plugin/interface/codec_plugin.h" +#include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" + +namespace { +// register plugins +using namespace OHOS::Media::Plugin; +void UpdatePluginDefinition(const AVCodec* codec, CodecPluginDef& definition); + +std::map> codecMap; + +const size_t BUFFER_QUEUE_SIZE = 6; + +std::set g_supportedCodec = {AV_CODEC_ID_MP3, AV_CODEC_ID_FLAC, AV_CODEC_ID_AAC, AV_CODEC_ID_AAC_LATM}; + +std::shared_ptr AuFfmpegDecoderCreator(const std::string& name) +{ + return std::make_shared(name); +} + +Status RegisterAudioDecoderPlugins(const std::shared_ptr& reg) +{ + const AVCodec* codec = nullptr; + void* ite = nullptr; + MEDIA_LOG_I("registering audio decoders"); + while ((codec = av_codec_iterate(&ite))) { + if (!av_codec_is_decoder(codec) || codec->type != AVMEDIA_TYPE_AUDIO) { + continue; + } + if (g_supportedCodec.find(codec->id) == g_supportedCodec.end()) { + MEDIA_LOG_W("codec %s(%s) is not supported right now", codec->name, codec->long_name); + continue; + } + CodecPluginDef definition; + definition.name = "audecoder_" + std::string(codec->name); + definition.codecType = CodecType::AUDIO_DECODER; + definition.rank = 100; // 100 + definition.creator = AuFfmpegDecoderCreator, UpdatePluginDefinition(codec, definition); + // do not delete the codec in the deleter + codecMap[definition.name] = std::shared_ptr(const_cast(codec), [](void* ptr) {}); + if (reg->AddPlugin(definition) != Status::OK) { + MEDIA_LOG_W("register plugin %s(%s) failed", codec->name, codec->long_name); + } + } + return Status::OK; +} + +void UnRegisterAudioDecoderPlugin() +{ + codecMap.clear(); +} + +std::map g_formatMap = { + {AV_SAMPLE_FMT_U8, AudioSampleFormat::U8}, {AV_SAMPLE_FMT_U8P, AudioSampleFormat::U8P}, + {AV_SAMPLE_FMT_S16, AudioSampleFormat::S16}, {AV_SAMPLE_FMT_S16P, AudioSampleFormat::S16P}, + {AV_SAMPLE_FMT_S32, AudioSampleFormat::S32}, {AV_SAMPLE_FMT_S32P, AudioSampleFormat::S32P}, + {AV_SAMPLE_FMT_FLT, AudioSampleFormat::F32}, {AV_SAMPLE_FMT_FLTP, AudioSampleFormat::F32P}, + {AV_SAMPLE_FMT_DBL, AudioSampleFormat::F64}, {AV_SAMPLE_FMT_DBLP, AudioSampleFormat::F64P}, +}; + +std::map g_reverseFormatMap = { + {AudioSampleFormat::U8, AV_SAMPLE_FMT_U8}, {AudioSampleFormat::U8P, AV_SAMPLE_FMT_U8P}, + {AudioSampleFormat::S16, AV_SAMPLE_FMT_S16}, {AudioSampleFormat::S16P, AV_SAMPLE_FMT_S16P}, + {AudioSampleFormat::S32, AV_SAMPLE_FMT_S32}, {AudioSampleFormat::S32P, AV_SAMPLE_FMT_S32P}, + {AudioSampleFormat::F32, AV_SAMPLE_FMT_FLT}, {AudioSampleFormat::F32P, AV_SAMPLE_FMT_FLTP}, + {AudioSampleFormat::F64, AV_SAMPLE_FMT_DBL}, {AudioSampleFormat::F64P, AV_SAMPLE_FMT_DBLP}, +}; + +void UpdatePluginDefinition(const AVCodec* codec, CodecPluginDef& definition) +{ + Capability cap("audio/unknown"); + switch (codec->id) { + case AV_CODEC_ID_MP3: + cap.SetMime(OHOS::Media::MEDIA_MIME_AUDIO_MPEG) + .AppendFixedKey(Capability::Key::AUDIO_MPEG_VERSION, 1) + .AppendIntervalKey(Capability::Key::AUDIO_MPEG_LAYER, 1, 3); // 3 + break; + case AV_CODEC_ID_FLAC: + cap.SetMime(OHOS::Media::MEDIA_MIME_AUDIO_FLAC); + break; + case AV_CODEC_ID_AAC: + cap.SetMime(OHOS::Media::MEDIA_MIME_AUDIO_AAC); + break; + case AV_CODEC_ID_AAC_LATM: + cap.SetMime(OHOS::Media::MEDIA_MIME_AUDIO_AAC_LATM); + break; + default: + MEDIA_LOG_I("codec is not supported right now"); + } + + size_t index = 0; + if (codec->supported_samplerates != nullptr) { + DiscreteCapability values; + for (index = 0; codec->supported_samplerates[index] != 0; ++index) { + values.push_back(codec->supported_samplerates[index]); + } + if (index) { + cap.AppendDiscreteKeys(Capability::Key::AUDIO_SAMPLE_RATE, values); + } + } + + if (codec->channel_layouts != nullptr) { + DiscreteCapability values; + for (index = 0; codec->channel_layouts[index] != 0; ++index) { + values.push_back(AudioChannelLayout(codec->channel_layouts[index])); + } + if (index) { + cap.AppendDiscreteKeys(Capability::Key::AUDIO_CHANNEL_LAYOUT, values); + } + } + + if (codec->sample_fmts != nullptr) { + DiscreteCapability values; + for (index = 0; codec->sample_fmts[index] != AV_SAMPLE_FMT_NONE; ++index) { + values.push_back(g_formatMap[codec->sample_fmts[index]]); + } + if (index) { + cap.AppendDiscreteKeys(Capability::Key::AUDIO_SAMPLE_FORMAT, values); + } + } + definition.inCaps.push_back(cap); +} +uint32_t GetWidth(AVSampleFormat sampleFormat) +{ + switch (sampleFormat) { + case AV_SAMPLE_FMT_U8P: + case AV_SAMPLE_FMT_U8: + return 1; + case AV_SAMPLE_FMT_S16P: + case AV_SAMPLE_FMT_S16: + return 2; // 2 + case AV_SAMPLE_FMT_S32P: + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_FLTP: + case AV_SAMPLE_FMT_FLT: + return 4; // 4 + case AV_SAMPLE_FMT_DBLP: + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_S64P: + case AV_SAMPLE_FMT_S64: + return 8; // 8 + default: + MEDIA_LOG_W("not supported right now"); + return 1; + } +} +} // namespace +PLUGIN_DEFINITION(FFmpegAudioDecoders, LicenseType::LGPL, RegisterAudioDecoderPlugins, UnRegisterAudioDecoderPlugin); + +namespace OHOS { +namespace Media { +namespace Plugin { +AudioFfmpegDecoderPlugin::AudioFfmpegDecoderPlugin(std::string name) + : name_(std::move(name)), outBufferQ_("decoderPluginQueue", BUFFER_QUEUE_SIZE) +{ +} + +Status AudioFfmpegDecoderPlugin::Init() +{ + OSAL::ScopedLock l(lock_); + auto ite = codecMap.find(name_); + if (ite == codecMap.end()) { + MEDIA_LOG_W("cannot find codec with name %s", name_.c_str()); + return Status::ERROR_UNSUPPORTED_FORMAT; + } + avCodec_ = ite->second; + cachedFrame_ = std::shared_ptr(av_frame_alloc(), [](AVFrame* fp) { av_frame_free(&fp); }); + state_ = State::INITIALIZED; + audioParameter_[Tag::REQUIRED_OUT_BUFFER_CNT] = (uint32_t)BUFFER_QUEUE_SIZE; + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::Deinit() +{ + OSAL::ScopedLock l(lock_); + avCodec_.reset(); + cachedFrame_.reset(); + ResetLocked(); + state_ = State::DESTROYED; + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::SetParameter(Tag tag, const ValueType& value) +{ + OSAL::ScopedLock l(lock_); + audioParameter_.insert(std::make_pair(tag, value)); + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::GetParameter(Tag tag, ValueType& value) +{ + OSAL::ScopedLock l(lock_); + auto res = audioParameter_.find(tag); + if (res != audioParameter_.end()) { + value = res->second; + return Status::OK; + } + return Status::ERROR_INVALID_PARAMETER; +} + +template +bool AudioFfmpegDecoderPlugin::FindInParameterMapThenAssignLocked(Tag tag, T& assign) +{ + auto ite = audioParameter_.find(tag); + if (ite != audioParameter_.end() && typeid(T) == ite->second.Type()) { + assign = Plugin::AnyCast(ite->second); + return true; + } else { + MEDIA_LOG_W("parameter %d is not found or type mismatch", static_cast(tag)); + return false; + } +} + +Status AudioFfmpegDecoderPlugin::Prepare() +{ + { + OSAL::ScopedLock l(lock_); + if (state_ != State::INITIALIZED && state_ != State::PREPARED) { + return Status::ERROR_WRONG_STATE; + } + auto context = avcodec_alloc_context3(avCodec_.get()); + if (context == nullptr) { + MEDIA_LOG_E("cannot allocate codec context"); + return Status::ERROR_UNKNOWN; + } + avCodecContext_ = std::shared_ptr(context, [](AVCodecContext* ptr) { + if (ptr != nullptr) { + if (ptr->extradata) { + av_free(ptr->extradata); + ptr->extradata = nullptr; + } + avcodec_free_context(&ptr); + } + }); + uint32_t tmp = 0; + if (FindInParameterMapThenAssignLocked(Tag::AUDIO_CHANNELS, tmp)) { + avCodecContext_->channels = tmp; + } + if (FindInParameterMapThenAssignLocked(Tag::AUDIO_SAMPLE_RATE, tmp)) { + avCodecContext_->sample_rate = tmp; + } + int64_t bitRate = 0; + if (FindInParameterMapThenAssignLocked(Tag::MEDIA_BITRATE, bitRate)) { + avCodecContext_->bit_rate = bitRate; + } + AudioSampleFormat audioSampleFormat = AudioSampleFormat::S8; + if (FindInParameterMapThenAssignLocked(Tag::AUDIO_SAMPLE_FORMAT, audioSampleFormat)) { + auto ite = g_reverseFormatMap.find(audioSampleFormat); + if (ite != g_reverseFormatMap.end()) { + avCodecContext_->sample_fmt = ite->second; + } + } + + InitCodecContextExtraData(); + + avCodecContext_->workaround_bugs = + static_cast(avCodecContext_->workaround_bugs) | static_cast(FF_BUG_AUTODETECT); + avCodecContext_->err_recognition = 1; + + state_ = State::PREPARED; + } + outBufferQ_.SetActive(true); + return Status::OK; +} + +void AudioFfmpegDecoderPlugin::InitCodecContextExtraData() +{ + if (!avCodecContext_) { + return; + } + auto it = audioParameter_.find(Tag::MEDIA_CODEC_CONFIG); + if (it == audioParameter_.end() || it->second.Type() != typeid(std::vector)) { + return; + } + auto codecConfig = Plugin::AnyCast>(it->second); + int configSize = codecConfig.size(); + if (configSize > 0) { + auto allocSize = AlignUp(configSize + AV_INPUT_BUFFER_PADDING_SIZE, 16); // 16 + avCodecContext_->extradata = static_cast(av_mallocz(allocSize)); + if (memcpy_s(avCodecContext_->extradata, configSize, codecConfig.data(), configSize) != EOK) { + MEDIA_LOG_E("init codec context extradata error"); + // todo should report error to fwk using callback + return; + } + avCodecContext_->extradata_size = configSize; + } +} + +Status AudioFfmpegDecoderPlugin::ResetLocked() +{ + audioParameter_.clear(); + avCodecContext_.reset(); + outBufferQ_.Clear(); + state_ = State::INITIALIZED; + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::Reset() +{ + OSAL::ScopedLock l(lock_); + return ResetLocked(); +} + +Status AudioFfmpegDecoderPlugin::Start() +{ + { + OSAL::ScopedLock l(lock_); + if (state_ != State::PREPARED) { + return Status::ERROR_WRONG_STATE; + } + auto res = avcodec_open2(avCodecContext_.get(), avCodec_.get(), nullptr); + if (res != 0) { + MEDIA_LOG_E("avcodec open error %s when start decoder ", AVStrError(res).c_str()); + return Status::ERROR_UNKNOWN; + } + state_ = State::RUNNING; + } + outBufferQ_.SetActive(true); + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::Stop() +{ + Status ret = Status::OK; + { + OSAL::ScopedLock l(lock_); + if (avCodecContext_ != nullptr) { + auto res = avcodec_close(avCodecContext_.get()); + if (res != 0) { + MEDIA_LOG_E("avcodec close error %s when stop decoder", AVStrError(res).c_str()); + ret = Status::ERROR_UNKNOWN; + } + avCodecContext_.reset(); + } + state_ = State::INITIALIZED; + } + outBufferQ_.SetActive(false); + return ret; +} + +Status AudioFfmpegDecoderPlugin::QueueOutputBuffer(const std::shared_ptr& outputBuffer, int32_t timeoutMs) +{ + MEDIA_LOG_I("queue out put"); + outBufferQ_.Push(outputBuffer); + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::Flush() +{ + MEDIA_LOG_I("Flush entered."); + OSAL::ScopedLock l(lock_); + if (avCodecContext_ != nullptr) { + avcodec_flush_buffers(avCodecContext_.get()); + } + MEDIA_LOG_I("Flush exit."); + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) +{ + MEDIA_LOG_D("queue input buffer"); + Status status = SendBuffer(inputBuffer); + if (status != Status::OK) { + return status; + } + bool receiveOneFrame = false; + do { + receiveOneFrame = ReceiveBuffer(status); + if (status != Status::OK) { + break; + } + } while (receiveOneFrame); + return status; +} + +Status AudioFfmpegDecoderPlugin::SendBufferLocked(const std::shared_ptr& inputBuffer) +{ + if (state_ != State::RUNNING) { + MEDIA_LOG_W("queue input buffer in wrong state"); + return Status::ERROR_WRONG_STATE; + } + AVPacket packet; + size_t bufferLength = 0; + bool eos = false; + if (inputBuffer == nullptr || (inputBuffer->flag & BUFFER_FLAG_EOS) != 0) { + // eos buffer + eos = true; + } else { + auto inputMemory = inputBuffer->GetMemory(); + const uint8_t* ptr = inputMemory->GetReadOnlyData(); + bufferLength = inputMemory->GetSize(); + // pad to data if needed + if (bufferLength % AV_INPUT_BUFFER_PADDING_SIZE != 0) { + if (paddedBufferSize_ < bufferLength + AV_INPUT_BUFFER_PADDING_SIZE) { + paddedBufferSize_ = bufferLength + AV_INPUT_BUFFER_PADDING_SIZE; + paddedBuffer_.reserve(paddedBufferSize_); + MEDIA_LOG_I("increase padded buffer size to %zu", paddedBufferSize_); + } + paddedBuffer_.assign(ptr, ptr + bufferLength); + paddedBuffer_.insert(paddedBuffer_.end(), AV_INPUT_BUFFER_PADDING_SIZE, 0); + ptr = paddedBuffer_.data(); + } + av_init_packet(&packet); + packet.data = const_cast(ptr); + packet.size = bufferLength; + packet.pts = inputBuffer->pts; + } + AVPacket* packetPtr = nullptr; + if (!eos) { + packetPtr = &packet; + } + auto ret = avcodec_send_packet(avCodecContext_.get(), packetPtr); + if (ret < 0) { + MEDIA_LOG_E("send buffer error %s", AVStrError(ret).c_str()); + } + return Status::OK; +} + +Status AudioFfmpegDecoderPlugin::SendBuffer(const std::shared_ptr& inputBuffer) +{ + if (inputBuffer->IsEmpty() && !(inputBuffer->flag & BUFFER_FLAG_EOS)) { + MEDIA_LOG_E("decoder does not support fd buffer"); + return Status::ERROR_INVALID_DATA; + } + Status ret = Status::OK; + { + OSAL::ScopedLock l(lock_); + ret = SendBufferLocked(inputBuffer); + } + NotifyInputBufferDone(inputBuffer); + return ret; +} + +void AudioFfmpegDecoderPlugin::ReceiveFrameSucc(const std::shared_ptr& ioInfo, Status& status, + bool& receiveOneFrame, bool& notifyBufferDone) +{ + int32_t channels = cachedFrame_->channels; + int32_t samples = cachedFrame_->nb_samples; + auto sampleFormat = static_cast(cachedFrame_->format); + int32_t bytePerSample = GetWidth(sampleFormat); + int32_t outputSize = samples * bytePerSample * channels; + auto ioInfoMem = ioInfo->GetMemory(); + if (ioInfoMem->GetCapacity() < outputSize) { + MEDIA_LOG_W("output buffer size is not enough"); + receiveOneFrame = false; + notifyBufferDone = false; + } else { + if (av_sample_fmt_is_planar(avCodecContext_->sample_fmt)) { + int32_t planarSize = outputSize / channels; + for (size_t idx = 0; idx < channels; idx++) { + ioInfoMem->Write(cachedFrame_->extended_data[idx], planarSize); + } + } else { + ioInfoMem->Write(cachedFrame_->data[0], outputSize); + } + ioInfo->pts = static_cast(cachedFrame_->pts); + notifyBufferDone = true; + receiveOneFrame = true; + status = Status::OK; + } +} + +void AudioFfmpegDecoderPlugin::ReceiveBufferLocked(Status& status, const std::shared_ptr& ioInfo, + bool& receiveOneFrame, bool& notifyBufferDone) +{ + if (state_ != State::RUNNING) { + MEDIA_LOG_W("queue input buffer in wrong state"); + status = Status::ERROR_WRONG_STATE; + receiveOneFrame = false; + notifyBufferDone = false; + return; + } + auto ret = avcodec_receive_frame(avCodecContext_.get(), cachedFrame_.get()); + if (ret >= 0) { + MEDIA_LOG_D("receive one frame"); + ReceiveFrameSucc(ioInfo, status, receiveOneFrame, notifyBufferDone); + } else if (ret == AVERROR_EOF) { + MEDIA_LOG_I("eos received"); + ioInfo->GetMemory()->Reset(); + notifyBufferDone = true; + receiveOneFrame = false; + status = Status::END_OF_STREAM; + } else { + MEDIA_LOG_I("audio decoder receive error: %s", AVStrError(ret).c_str()); + notifyBufferDone = false; + receiveOneFrame = false; + status = Status::OK; + } + av_frame_unref(cachedFrame_.get()); +} +bool AudioFfmpegDecoderPlugin::ReceiveBuffer(Status& status) +{ + bool notifyBufferDone = false; + bool receiveOneFrame = false; + std::shared_ptr ioInfo = outBufferQ_.Pop(); + if (ioInfo == nullptr || ioInfo->IsEmpty()) { + MEDIA_LOG_W("cannot fetch valid buffer to output"); + return false; + } + if (ioInfo->GetBufferMeta()->GetType() != BufferMetaType::AUDIO) { + MEDIA_LOG_W("cannot process with non-audio buffer"); + receiveOneFrame = false; + } else { + OSAL::ScopedLock l(lock_); + ReceiveBufferLocked(status, ioInfo, receiveOneFrame, notifyBufferDone); + } + if (notifyBufferDone) { + NotifyOutputBufferDone(ioInfo); + } else { + outBufferQ_.Push(ioInfo); + } + return receiveOneFrame; +} + +void AudioFfmpegDecoderPlugin::NotifyInputBufferDone(const std::shared_ptr& input) +{ + auto ptr = dataCb_.lock(); + if (ptr != nullptr) { + ptr->OnInputBufferDone(input); + } +} + +void AudioFfmpegDecoderPlugin::NotifyOutputBufferDone(const std::shared_ptr& output) +{ + auto ptr = dataCb_.lock(); + if (ptr != nullptr) { + ptr->OnOutputBufferDone(output); + } +} + +std::shared_ptr AudioFfmpegDecoderPlugin::GetAllocator() +{ + return nullptr; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h new file mode 100644 index 00000000..ae9107d8 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_AUDIO_FFMPEG_DECODER_PLUGIN_H +#define HISTREAMER_AUDIO_FFMPEG_DECODER_PLUGIN_H + +#include +#include +#include "foundation/blocking_queue.h" +#include "plugin/interface/codec_plugin.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libavcodec/avcodec.h" +#ifdef __cplusplus +}; +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +class AudioFfmpegDecoderPlugin : public CodecPlugin { +public: + explicit AudioFfmpegDecoderPlugin(std::string name); + + ~AudioFfmpegDecoderPlugin() override = default; + + Status Init() override; + + Status Deinit() override; + + Status Prepare() override; + + Status Reset() override; + + Status Start() override; + + Status Stop() override; + + bool IsParameterSupported(Tag tag) override + { + return true; + } + + Status GetParameter(Tag tag, ValueType& value) override; + + Status SetParameter(Tag tag, const ValueType& value) override; + + Status GetState(State& state) override + { + return Status::ERROR_UNIMPLEMENTED; + } + + std::shared_ptr GetAllocator() override; + + Status SetCallback(const std::shared_ptr& cb) override + { + return Status::OK; + } + + Status QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) override; + + Status QueueOutputBuffer(const std::shared_ptr& outputBuffers, int32_t timeoutMs) override; + + Status Flush() override; + + Status SetDataCallback(const std::weak_ptr& dataCallback) override + { + dataCb_ = dataCallback; + return Status::OK; + } + +private: + void InitCodecContextExtraData(); + + Status ResetLocked(); + + template + bool FindInParameterMapThenAssignLocked(Tag tag, T& assign); + + Status SendBuffer(const std::shared_ptr& inputBuffer); + + Status SendBufferLocked(const std::shared_ptr& inputBuffer); + + void ReceiveFrameSucc(const std::shared_ptr& ioInfo, Status& status, + bool& receiveOneFrame, bool& notifyBufferDone); + + bool ReceiveBuffer(Status& err); + void ReceiveBufferLocked(Status& status, const std::shared_ptr& ioInfo, bool& receiveOneFrame, + bool& notifyBufferDone); + + void NotifyInputBufferDone(const std::shared_ptr& input); + + void NotifyOutputBufferDone(const std::shared_ptr& output); + + std::string name_; + std::shared_ptr avCodec_ {}; + std::map audioParameter_ {}; + std::vector paddedBuffer_ {}; + size_t paddedBufferSize_ {0}; + std::shared_ptr cachedFrame_ {}; + std::weak_ptr dataCb_ {}; + + mutable OSAL::Mutex lock_ {}; + State state_ {State::CREATED}; + std::shared_ptr avCodecContext_ {}; + + // outBufferQ有自己的锁保护 不要和lock_同时混用 否则可能导致死锁 + OHOS::Media::BlockingQueue> outBufferQ_; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_AUDIO_FFMPEG_DECODER_PLUGIN_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp new file mode 100644 index 00000000..79285981 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "FFmpegDemuxerPlugin" + +#include "ffmpeg_demuxer_plugin.h" +#include +#include +#include +#include +#include "core/plugin_manager.h" +#include "ffmpeg_track_meta.h" +#include "foundation/log.h" +#include "foundation/memory_helper.h" +#include "osal/thread/scoped_lock.h" +#include "plugin/common/plugin_buffer.h" +#include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" + +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 78, 0) and LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 64, 100) +#include "libavformat/internal.h" +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +namespace { +const std::map TAG_MAP = { + {"title", MetaID::MEDIA_TITLE}, + {"artist", MetaID::MEDIA_ARTIST}, + {"lyricist", MetaID::MEDIA_LYRICIST}, + {"album", MetaID::MEDIA_ALBUM}, + {"album-artist", MetaID::MEDIA_ALBUM_ARTIST}, + {"date", MetaID::MEDIA_DATE}, + {"comment", MetaID::MEDIA_COMMENT}, + {"genre", MetaID::MEDIA_GENRE}, + {"copyright", MetaID::MEDIA_COPYRIGHT}, + {"language", MetaID::MEDIA_LANGUAGE}, + {"description", MetaID::MEDIA_DESCRIPTION}, + {"lyrics", MetaID::MEDIA_LYRICS}, +}; + +std::map> g_pluginInputFormat; + +int Sniff(const std::string& name, std::shared_ptr dataSource); + +Status RegisterPlugins(const std::shared_ptr& reg); + +int ConvertSeekModeToFFmpeg(SeekMode mode); +} // namespace + +void* FFmpegDemuxerPlugin::DemuxerPluginAllocator::Alloc(size_t size) +{ + return static_cast(new (std::nothrow) uint8_t[size]); +} + +void FFmpegDemuxerPlugin::DemuxerPluginAllocator::Free(void* ptr) +{ + if (ptr) { + auto data = static_cast(ptr); + delete[] data; + } +} + +FFmpegDemuxerPlugin::FFmpegDemuxerPlugin(std::string name) + : name_(std::move(name)), + ioContext_(), + callback_(nullptr), + pluginImpl_(nullptr), + formatContext_(nullptr), + allocator_(std::make_shared()), + mediaInfo_(nullptr), + selectedTrackIds_() +{ + MEDIA_LOG_I("ctor called, plugin name: %s", name_.c_str()); +} + +FFmpegDemuxerPlugin::~FFmpegDemuxerPlugin() +{ + MEDIA_LOG_I("dtor called."); + pluginImpl_ = nullptr; +} + +Status FFmpegDemuxerPlugin::Init() +{ + MEDIA_LOG_I("Init called."); + Reset(); + pluginImpl_ = g_pluginInputFormat[name_]; + + return pluginImpl_ ? Status::OK : Status::ERROR_UNSUPPORTED_FORMAT; +} + +Status FFmpegDemuxerPlugin::Deinit() +{ + return Status::OK; +} + +Status FFmpegDemuxerPlugin::Prepare() +{ + InitAVFormatContext(); + if (formatContext_ == nullptr) { + MEDIA_LOG_E("prepare failed due to formatContext init error"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status FFmpegDemuxerPlugin::Reset() +{ + mediaInfo_.reset(); + ioContext_.offset = 0; + ioContext_.eos = false; + selectedTrackIds_.clear(); + return Status::OK; +} + +Status FFmpegDemuxerPlugin::Start() +{ + return Status::OK; +} + +Status FFmpegDemuxerPlugin::Stop() +{ + return Status::OK; +} + +/** + * IsParameterSupported no need supported by demuxer + * @return return false always. + */ +bool FFmpegDemuxerPlugin::IsParameterSupported(Tag) +{ + return false; +} + +/** + * GetParameter no need supported by demuxer + * @return return ERROR_UNIMPLEMENTED always. + */ +Status FFmpegDemuxerPlugin::GetParameter(Tag, ValueType&) +{ + return Status::ERROR_UNIMPLEMENTED; +} + +/** + * SetParameter no need supported by demuxer + * @return return ERROR_UNIMPLEMENTED always. + */ +Status FFmpegDemuxerPlugin::SetParameter(Tag, const ValueType&) +{ + return Status::ERROR_UNIMPLEMENTED; +} + +Status FFmpegDemuxerPlugin::GetState(State&) +{ + return Status::ERROR_UNIMPLEMENTED; +} + +std::shared_ptr FFmpegDemuxerPlugin::GetAllocator() +{ + return allocator_; +} + +Status FFmpegDemuxerPlugin::SetCallback(const std::shared_ptr& cb) +{ + callback_ = cb; + return Status::OK; +} + +Status FFmpegDemuxerPlugin::SetDataSource(const std::shared_ptr& source) +{ + ioContext_.dataSource = source; + return Status::OK; +} + +Status FFmpegDemuxerPlugin::GetMediaInfo(MediaInfo& mediaInfo) +{ + if (!mediaInfo_ && !ParseMediaData()) { + return Status::ERROR_INVALID_PARAMETER; + } + mediaInfo = *mediaInfo_; + return Status::OK; +} + +size_t FFmpegDemuxerPlugin::GetTrackCount() +{ + size_t trackCnt = 0; + if (mediaInfo_) { + trackCnt = mediaInfo_->tracks.size(); + } + return trackCnt; +} + +Status FFmpegDemuxerPlugin::SelectTrack(int32_t trackId) +{ + if (!mediaInfo_) { + MEDIA_LOG_E("SelectTrack called before GetMediaInfo()..."); + return Status::ERROR_INVALID_DATA; + } + if (trackId < 0 || trackId >= static_cast(mediaInfo_->tracks.size())) { + MEDIA_LOG_E("SelectTrack called with invalid trackId: %d, number of tracks: %d", trackId, + static_cast(mediaInfo_->tracks.size())); + return Status::ERROR_INVALID_DATA; + } + OSAL::ScopedLock lock(mutex_); + auto it = std::find_if(selectedTrackIds_.begin(), selectedTrackIds_.end(), + [trackId](int32_t streamId) { return trackId == streamId; }); + if (it == selectedTrackIds_.end()) { + selectedTrackIds_.push_back(trackId); + } + return Status::OK; +} + +Status FFmpegDemuxerPlugin::UnselectTrack(int32_t trackId) +{ + OSAL::ScopedLock lock(mutex_); + auto it = std::find_if(selectedTrackIds_.begin(), selectedTrackIds_.end(), + [trackId](int32_t streamId) { return trackId == streamId; }); + if (it != selectedTrackIds_.end()) { + selectedTrackIds_.erase(it); + } + return Status::OK; +} + +Status FFmpegDemuxerPlugin::GetSelectedTracks(std::vector& trackIds) +{ + OSAL::ScopedLock lock(mutex_); + trackIds = selectedTrackIds_; + return Status::OK; +} + +bool FFmpegDemuxerPlugin::ConvertAVPacketToFrameInfo(const AVStream& avStream, const AVPacket& pkt, Buffer& frameInfo) +{ + frameInfo.streamID = static_cast(pkt.stream_index); + int64_t pts = (pkt.pts > 0) ? pkt.pts : 0; + frameInfo.pts = ConvertTimeFromFFmpeg(pts, avStream.time_base); + frameInfo.dts = static_cast(pkt.dts); + frameInfo.duration = ConvertTimeFromFFmpeg(pkt.duration, avStream.time_base); + frameInfo.GetBufferMeta()->SetMeta(Tag::MEDIA_POSITION, static_cast(pkt.pos)); + + int frameSize = 0; + if (avStream.codecpar->codec_type == AVMEDIA_TYPE_VIDEO && avStream.codecpar->codec_id == AV_CODEC_ID_RAWVIDEO) { +#ifdef VIDEO_SUPPORT + frameSize = pkt.size; +#else + MEDIA_LOG_W("video unsupported..."); + return false; +#endif + } else { + frameSize = pkt.size; + } + auto data = frameInfo.AllocMemory(allocator_, frameSize); + if (data) { + size_t writeSize = data->Write(pkt.data, frameSize); + ASSERT_CONDITION(writeSize == frameSize, "Copy data failed."); + } + return data != nullptr; +} + +Status FFmpegDemuxerPlugin::ReadFrame(Buffer& info, int32_t timeOutMs) +{ + (void)timeOutMs; + AVPacket pkt; + int res = 0; + do { + res = av_read_frame(formatContext_.get(), &pkt); + } while (res >= 0 && !selectedTrackIds_.empty() && !IsSelectedTrack(pkt.stream_index)); + Status result = Status::ERROR_UNKNOWN; + if (res == 0 && ConvertAVPacketToFrameInfo(*(formatContext_->streams[pkt.stream_index]), pkt, info)) { + result = Status::OK; + } else { + MEDIA_LOG_W("ReadFrame failed, rtv = %s", AVStrError(res).c_str()); + } + av_packet_unref(&pkt); + return (res != AVERROR_EOF) ? result : Status::END_OF_STREAM; +} + +/** + * SeekTo seek operation + * @param trackId -1 for unspecified, >= 0 for specific trackid + * @param timeStampUs + * @param mode + * @return operation result. + */ +Status FFmpegDemuxerPlugin::SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) +{ + if (trackId == -1) { + trackId = av_find_default_stream_index(formatContext_.get()); + } + if (trackId < 0 || trackId >= static_cast(formatContext_->nb_streams)) { + MEDIA_LOG_E("SeekTo called with invalid trackid = %d, nb_streams = %d.", trackId, formatContext_->nb_streams); + return Status::ERROR_INVALID_PARAMETER; + } + + auto avStream = formatContext_->streams[trackId]; + int64_t ffTime = ConvertTimeToFFmpeg(timeStampUs, avStream->time_base); + if (avStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + int keyFrameIdx = av_index_search_timestamp(avStream, ffTime, ConvertSeekModeToFFmpeg(mode)); + MEDIA_LOG_I("SeekTo %ld, ffTime: %ld, key frame index: %d", timeStampUs, ffTime, keyFrameIdx); + if (keyFrameIdx >= 0) { +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 78, 0) + ffTime = avformat_index_get_entry(avStream, keyFrameIdx)->timestamp; +#elif LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(58, 64, 100) + ffTime = avStream->internal->index_entries[keyFrameIdx].timestamp; +#else + ffTime = avStream->index_entries[keyFrameIdx].timestamp; +#endif + } + } + auto newTime = ConvertTimeFromFFmpeg(ffTime, avStream->time_base); + MEDIA_LOG_W("SeekTo %lu / %ld, ffTime: %ld", newTime, timeStampUs, ffTime); + auto rtv = av_seek_frame(formatContext_.get(), trackId, ffTime, ConvertSeekModeToFFmpeg(mode)); + if (rtv < 0) { + MEDIA_LOG_E("seek failed, return value: %d", rtv); + } + return (rtv >= 0) ? Status::OK : Status::ERROR_UNKNOWN; +} + +void FFmpegDemuxerPlugin::InitAVFormatContext() +{ + AVFormatContext* formatContext = avformat_alloc_context(); + if (formatContext == nullptr) { + return; + } + formatContext->pb = AllocAVIOContext(AVIO_FLAG_READ); + formatContext->flags = static_cast(formatContext->flags) | static_cast(AVFMT_FLAG_CUSTOM_IO); + formatContext_ = std::shared_ptr(formatContext, [](AVFormatContext* ptr) { + if (ptr) { + auto ctx = ptr->pb; + if (ctx) { + av_freep(&ctx->buffer); + av_free(ctx); + } + avformat_close_input(&ptr); + } + }); +} + +std::shared_ptr FFmpegDemuxerPlugin::InitCodecContext(const AVStream& avStream) +{ + auto codecContext = std::shared_ptr(avcodec_alloc_context3(nullptr), [](AVCodecContext* p) { + if (p) { + avcodec_free_context(&p); + } + }); + if (codecContext == nullptr) { + MEDIA_LOG_E("cannot create ffmpeg codecContext"); + return nullptr; + } + int ret = avcodec_parameters_to_context(codecContext.get(), avStream.codecpar); + if (ret < 0) { + MEDIA_LOG_E("avcodec_parameters_to_context failed with return = %s", AVStrError(ret).c_str()); + return nullptr; + } + codecContext->workaround_bugs = static_cast(codecContext->workaround_bugs) | FF_BUG_AUTODETECT; + codecContext->err_recognition = 1; + return codecContext; +} + +AVIOContext* FFmpegDemuxerPlugin::AllocAVIOContext(int flags) +{ + constexpr int bufferSize = 4096; + auto buffer = static_cast(av_malloc(bufferSize)); + if (buffer == nullptr) { + MEDIA_LOG_E("AllocAVIOContext failed to av_malloc..."); + return nullptr; + } + AVIOContext* avioContext = avio_alloc_context(buffer, bufferSize, flags, static_cast(&ioContext_), + AVReadPacket, AVWritePacket, AVSeek); + if (avioContext == nullptr) { + MEDIA_LOG_E("AllocAVIOContext failed to avio_alloc_context..."); + av_free(buffer); + return nullptr; + } + avioContext->seekable = AVIO_SEEKABLE_NORMAL; + if (!(static_cast(flags) & static_cast(AVIO_FLAG_WRITE))) { + avioContext->buf_ptr = avioContext->buf_end; + avioContext->write_flag = 0; + } + return avioContext; +} + +bool FFmpegDemuxerPlugin::IsSelectedTrack(int32_t trackId) +{ + bool rtv = false; + for (const auto& id : selectedTrackIds_) { + if (id == trackId) { + rtv = true; + break; + } + } + return rtv; +} + +void FFmpegDemuxerPlugin::SaveFileInfoToMetaInfo(TagMap& meta) +{ + meta.clear(); + AVDictionaryEntry* tag = nullptr; + while ((tag = av_dict_get(formatContext_->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + auto it = TAG_MAP.find(tag->key); + if (it == TAG_MAP.end()) { + continue; + } + if (it->second != Media::Plugin::MetaID::MEDIA_DATE) { + meta.insert({Tag(it->second), std::string(tag->value)}); + } else { + uint32_t year = 0; + uint32_t month = 0; + uint32_t day = 0; + if (sscanf_s(tag->value, "%04u-%02u-%02u", &year, &month, &day) == 3) { // 3 + meta.insert({Tag::MEDIA_DATE, RemoveDelimiter(tag->value, '-')}); + } + } + } + int64_t msec = formatContext_->duration / 1000; // 1000 + meta.insert({Tag::MEDIA_DURATION, static_cast(msec)}); +} + +bool FFmpegDemuxerPlugin::ParseMediaData() +{ + auto formatContext = formatContext_.get(); + int ret = avformat_open_input(&formatContext, nullptr, pluginImpl_.get(), nullptr); + if (ret != 0) { + MEDIA_LOG_E("avformat_open_input using plugin %s failed with return = %s", pluginImpl_->name, + AVStrError(ret).c_str()); + return false; + } + // retrieve stream information + avformat_find_stream_info(formatContext, nullptr); + av_dump_format(formatContext, 0, nullptr, false); + + MemoryHelper::make_unique().swap(mediaInfo_); + size_t streamCnt = formatContext_->nb_streams; + mediaInfo_->general.clear(); + mediaInfo_->tracks.resize(streamCnt); + for (size_t i = 0; i < streamCnt; ++i) { + auto& avStream = *formatContext_->streams[i]; + auto codecContext = InitCodecContext(avStream); + if (codecContext == nullptr) { + continue; + } + ConvertAVStreamToMetaInfo(avStream, codecContext, mediaInfo_->tracks[i]); + } + SaveFileInfoToMetaInfo(mediaInfo_->general); + return true; +} + +// ffmpeg provide buf, we write data +int FFmpegDemuxerPlugin::AVReadPacket(void* opaque, uint8_t* buf, int bufSize) +{ + int rtv = -1; + auto ioContext = static_cast(opaque); + if (ioContext && ioContext->dataSource) { + auto buffer = std::make_shared(); + auto bufData = buffer->WrapMemory(buf, bufSize, bufSize); + auto result = ioContext->dataSource->ReadAt(ioContext->offset, buffer, static_cast(bufSize)); + MEDIA_LOG_D("AVReadPacket read data size = %d", static_cast(bufData->GetSize())); + if (result == Status::OK) { + ioContext->offset += buffer->GetMemory()->GetSize(); + rtv = buffer->GetMemory()->GetSize(); + } else if (result == Status::END_OF_STREAM) { + ioContext->eos = true; + rtv = AVERROR_EOF; + } else { + MEDIA_LOG_E("AVReadPacket failed with rtv = %d", static_cast(result)); + } + } + return rtv; +} + +/** + * write packet unimplemented. + * @return 0 + */ +int FFmpegDemuxerPlugin::AVWritePacket(void*, uint8_t*, int) +{ + return 0; +} + +int64_t FFmpegDemuxerPlugin::AVSeek(void* opaque, int64_t offset, int whence) +{ + auto ioContext = static_cast(opaque); + uint64_t newPos = 0; + switch (whence) { + case SEEK_SET: + newPos = static_cast(offset); + ioContext->offset = newPos; + MEDIA_LOG_I("AVSeek whence: %d, pos = %ld, newPos = %ld", whence, offset, newPos); + break; + case SEEK_CUR: + newPos = ioContext->offset + offset; + MEDIA_LOG_I("AVSeek whence: %d, pos = %ld, newPos = %ld", whence, offset, newPos); + break; + case SEEK_END: + case AVSEEK_SIZE: { + size_t mediaDataSize = 0; + if (ioContext->dataSource->GetSize(mediaDataSize) == Status::OK) { + newPos = mediaDataSize + offset; + MEDIA_LOG_I("AVSeek seek end whence: %d, pos = %ld", whence, offset); + } + break; + } + default: + MEDIA_LOG_E("AVSeek unexpected whence: %d", whence); + break; + } + if (whence != AVSEEK_SIZE) { + ioContext->offset = newPos; + } + MEDIA_LOG_I("current offset: %ld, new pos: %ld", ioContext->offset, newPos); + return newPos; +} + +namespace { +int ConvertSeekModeToFFmpeg(SeekMode mode) +{ + int seekFlag = AVSEEK_FLAG_BACKWARD; + switch (mode) { + case SeekMode::BACKWARD: + seekFlag = AVSEEK_FLAG_BACKWARD; + break; + case SeekMode::FORWARD: + seekFlag = 0; + break; + case SeekMode::FRAME: + seekFlag = AVSEEK_FLAG_FRAME; + break; + case SeekMode::ANY: + seekFlag = AVSEEK_FLAG_ANY; + break; + default: + MEDIA_LOG_W("unsupported seekmode: %d, using backward mode instead.", static_cast(mode)); + break; + } + return seekFlag; +} + +int Sniff(const std::string& name, std::shared_ptr dataSource) +{ + auto pluginInfo = PluginManager::Instance().GetPluginInfo(PluginType::DEMUXER, name); + if (!pluginInfo || !dataSource) { + MEDIA_LOG_E("Sniff failed due to plugin not found or dataSource invalid for %s.", name.c_str()); + return 0; + } + auto plugin = g_pluginInputFormat[pluginInfo->name]; + if (!plugin || !plugin->read_probe) { + MEDIA_LOG_E("Sniff failed due to invalid plugin for %s.", name.c_str()); + return 0; + } + size_t bufferSize = 4096; + size_t fileSize = 0; + if (dataSource->GetSize(fileSize) == Status::OK) { + bufferSize = (bufferSize < fileSize) ? bufferSize : fileSize; + } + std::vector buff(bufferSize); + auto bufferInfo = std::make_shared(); + auto bufData = bufferInfo->WrapMemory(buff.data(), bufferSize, bufferSize); + int confidence = 0; + if (dataSource->ReadAt(0, bufferInfo, bufferSize) == Status::OK) { + AVProbeData probeData{"", buff.data(), static_cast(bufferInfo->GetMemory()->GetSize()), ""}; + confidence = plugin->read_probe(&probeData); + } + MEDIA_LOG_D("Sniff: plugin name = %s, probability = %d / 100 ...", plugin->name, confidence); + return confidence; +} + +bool IsInputFormatSupported(const char* name) +{ + if (!strcmp(name, "audio_device") || !strncmp(name, "image", 5) || // 5 + !strcmp(name, "mjpeg") || !strcmp(name, "redir") || !strncmp(name, "u8", 2) || // 2 + !strncmp(name, "u16", 3) || !strncmp(name, "u24", 3) || // 3 + !strncmp(name, "u32", 3) || // 3 + !strncmp(name, "s8", 2) || !strncmp(name, "s16", 3) || // 2 3 + !strncmp(name, "s24", 3) || // 3 + !strncmp(name, "s32", 3) || !strncmp(name, "f32", 3) || // 3 + !strncmp(name, "f64", 3) || // 3 + !strcmp(name, "mulaw") || !strcmp(name, "alaw")) { + return false; + } + + /* no network demuxers */ + if (!strcmp(name, "sdp") || !strcmp(name, "rtsp") || !strcmp(name, "applehttp")) { + return false; + } + return true; +} + +Status RegisterPlugins(const std::shared_ptr& reg) +{ + MEDIA_LOG_D("RegisterPlugins called."); + if (!reg) { + MEDIA_LOG_E("RegisterPlugins failed due to null pointer for reg."); + return Status::ERROR_INVALID_PARAMETER; + } + const AVInputFormat* plugin = nullptr; + void* i = nullptr; + while ((plugin = av_demuxer_iterate(&i))) { + MEDIA_LOG_D("Attempting to handle libav demuxer plugin %s [%s]", plugin->name, plugin->long_name); + /* no emulators */ + if (plugin->long_name != nullptr) { + if (!strncmp(plugin->long_name, "pcm ", 4)) { // 4 + continue; + } + } + + if (!IsInputFormatSupported(plugin->name)) { + continue; + } + + std::string pluginName = "avdemux_" + std::string(plugin->name); + ReplaceDelimiter(".,|-<> ", '_', pluginName); + + DemuxerPluginDef regInfo; + regInfo.name = pluginName; + regInfo.description = "adapter for ffmpeg demuxer plugin"; + regInfo.rank = 100; // 100 + SplitString(plugin->extensions, ',').swap(regInfo.extensions); + g_pluginInputFormat[pluginName] = + std::shared_ptr(const_cast(plugin), [](void*) {}); + regInfo.creator = [](const std::string& name) -> std::shared_ptr { + return std::make_shared(name); + }; + regInfo.sniffer = Sniff; + auto rtv = reg->AddPlugin(regInfo); + if (rtv != Status::OK) { + MEDIA_LOG_E("RegisterPlugins AddPlugin failed with return %d", static_cast(rtv)); + } + } + return Status::OK; +} +} // namespace + +PLUGIN_DEFINITION(FFmpegDemuxer, LicenseType::LGPL, RegisterPlugins, [] { g_pluginInputFormat.clear(); }); +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h new file mode 100644 index 00000000..495de894 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FFMPEG_DEMUXER_PLUGIN_H +#define HISTREAMER_FFMPEG_DEMUXER_PLUGIN_H + +#include +#include +#include + +#include "foundation/type_define.h" +#include "core/plugin_register.h" +#include "interface/demuxer_plugin.h" +#include "thread/mutex.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libavformat/avformat.h" +#ifdef __cplusplus +}; +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +class FFmpegDemuxerPlugin : public DemuxerPlugin { +public: + explicit FFmpegDemuxerPlugin(std::string name); + ~FFmpegDemuxerPlugin() override; + + Status Init() override; + Status Deinit() override; + Status Prepare() override; + Status Reset() override; + Status Start() override; + Status Stop() override; + bool IsParameterSupported(Tag tag) override; + Status GetParameter(Tag tag, ValueType& value) override; + Status SetParameter(Tag tag, const ValueType& value) override; + Status GetState(State& state) override; + std::shared_ptr GetAllocator() override; + Status SetCallback(const std::shared_ptr& cb) override; + + Status SetDataSource(const std::shared_ptr& source) override; + Status GetMediaInfo(MediaInfo& mediaInfo) override; + size_t GetTrackCount() override; + Status SelectTrack(int32_t trackId) override; + Status UnselectTrack(int32_t trackId) override; + Status GetSelectedTracks(std::vector& trackIds) override; + Status ReadFrame(Buffer& info, int32_t timeOutMs) override; + Status SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) override; + +private: + class DemuxerPluginAllocator : public Allocator { + public: + DemuxerPluginAllocator() = default; + ~DemuxerPluginAllocator() override = default; + void* Alloc(size_t size) override; + void Free(void* ptr) override; + }; + + struct IOContext { + std::shared_ptr dataSource {nullptr}; + int64_t offset {0}; + bool eos {false}; + }; + + void InitAVFormatContext(); + + static std::shared_ptr InitCodecContext(const AVStream& avStream); + + AVIOContext* AllocAVIOContext(int flags); + + bool IsSelectedTrack(int32_t trackId); + + void SaveFileInfoToMetaInfo(TagMap &meta); + + bool ParseMediaData(); + + bool ConvertAVPacketToFrameInfo(const AVStream& avStream, const AVPacket& pkt, Buffer& frameInfo); + + static int AVReadPacket(void* opaque, uint8_t* buf, int bufSize); + + static int AVWritePacket(void* opaque, uint8_t* buf, int bufSize); + + static int64_t AVSeek(void* opaque, int64_t offset, int whence); + + std::string name_; + IOContext ioContext_; + std::shared_ptr callback_ {}; + std::shared_ptr pluginImpl_; + std::shared_ptr formatContext_; + std::shared_ptr allocator_; + std::unique_ptr mediaInfo_; + std::vector selectedTrackIds_; + OSAL::Mutex mutex_ {}; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_FFMPEG_DEMUXER_PLUGIN_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp new file mode 100644 index 00000000..094d31c4 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "ffmpeg_track_meta.h" +#include "foundation/constants.h" +#include "foundation/log.h" +#include "foundation/type_define.h" +#include "plugins/ffmpeg_adapter/utils/aac_audio_config_parser.h" +#include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" +#ifdef VIDEO_SUPPORT +#include "plugins/ffmpeg_adapter/utils/avc_config_data_parser.h" +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +namespace { +using ConvertFunc = void (*)(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); + +struct StreamConvertor { + AVCodecID codecId; + ConvertFunc convertor; +}; + +StreamConvertor g_streamConvertors[] = {{AV_CODEC_ID_PCM_S16LE, ConvertRawAudioStreamToMetaInfo}, + {AV_CODEC_ID_PCM_S16BE, ConvertRawAudioStreamToMetaInfo}, + {AV_CODEC_ID_PCM_U16LE, ConvertRawAudioStreamToMetaInfo}, + {AV_CODEC_ID_PCM_U16BE, ConvertRawAudioStreamToMetaInfo}, + {AV_CODEC_ID_PCM_S8, ConvertRawAudioStreamToMetaInfo}, + {AV_CODEC_ID_PCM_U8, ConvertRawAudioStreamToMetaInfo}, + {AV_CODEC_ID_MP1, ConvertMP1StreamToMetaInfo}, + {AV_CODEC_ID_MP2, ConvertMP2StreamToMetaInfo}, + {AV_CODEC_ID_MP3, ConvertMP3StreamToMetaInfo}, + {AV_CODEC_ID_AAC, ConvertAACStreamToMetaInfo}, + {AV_CODEC_ID_AAC_LATM, ConvertAACLatmStreamToMetaInfo}, +#ifdef VIDEO_SUPPORT + {AV_CODEC_ID_H264, ConvertAVCStreamToMetaInfo} +#endif +}; + +bool IsPcmStream(const AVStream& avStream) +{ + auto codecId = avStream.codecpar->codec_id; + return codecId == AV_CODEC_ID_PCM_S16LE || codecId == AV_CODEC_ID_PCM_S16BE || codecId == AV_CODEC_ID_PCM_U16LE || + codecId == AV_CODEC_ID_PCM_U16BE || codecId == AV_CODEC_ID_PCM_S8 || codecId == AV_CODEC_ID_PCM_U8; +} + +void ConvertCommonAudioStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, + TagMap& meta) +{ + meta.insert({Tag::STREAM_INDEX, static_cast(avStream.index)}); + if (context->channels != -1) { + meta.insert({Tag::AUDIO_SAMPLE_RATE, static_cast(context->sample_rate)}); + meta.insert({Tag::AUDIO_CHANNELS, static_cast(context->channels)}); + meta.insert( + {Tag::AUDIO_CHANNEL_LAYOUT, ConvertChannelLayoutFromFFmpeg(context->channels, context->channel_layout)}); + // ffmpeg defaults to 1024 samples per frame for planar PCM in each buffer (one for each channel). + uint32_t samplesPerFrame = 1024; + if (!IsPcmStream(avStream)) { + samplesPerFrame = static_cast(context->frame_size); + } + meta.insert({Tag::AUDIO_SAMPLE_PRE_FRAME, samplesPerFrame}); + meta.insert({Tag::AUDIO_SAMPLE_FORMAT, Trans2Format(context->sample_fmt)}); + meta.insert({Tag::MEDIA_BITRATE, static_cast(context->bit_rate)}); + } +} +} // namespace + +void ConvertRawAudioStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, + TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_AUDIO_RAW)}); + ConvertCommonAudioStreamToMetaInfo(avStream, context, meta); +} + +void ConvertMP1StreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_AUDIO_MPEG)}); + ConvertCommonAudioStreamToMetaInfo(avStream, context, meta); + meta.insert({Tag::AUDIO_MPEG_VERSION, static_cast(1)}); + meta.insert({Tag::AUDIO_MPEG_LAYER, static_cast(1)}); +} + +void ConvertMP2StreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_AUDIO_MPEG)}); + ConvertCommonAudioStreamToMetaInfo(avStream, context, meta); + meta.insert({Tag::AUDIO_MPEG_VERSION, static_cast(1)}); + meta.insert({Tag::AUDIO_MPEG_LAYER, static_cast(2)}); // 2 +} + +void ConvertMP3StreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_AUDIO_MPEG)}); + ConvertCommonAudioStreamToMetaInfo(avStream, context, meta); + meta.insert({Tag::AUDIO_MPEG_VERSION, static_cast(1)}); + meta.insert({Tag::AUDIO_MPEG_LAYER, static_cast(3)}); // 3 +} + +void ConvertAACStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_AUDIO_AAC)}); + ConvertCommonAudioStreamToMetaInfo(avStream, context, meta); + meta.insert({Tag::AUDIO_MPEG_VERSION, static_cast(4)}); // 4 + meta.insert({Tag::AUDIO_AAC_PROFILE, AudioAacProfile::LC}); + if (context->extradata_size > 0) { + std::vector codecConfig; + codecConfig.assign(context->extradata, context->extradata + context->extradata_size); + meta.insert({Tag::MEDIA_CODEC_CONFIG, std::move(codecConfig)}); + AACAudioConfigParser parser(context->extradata, context->extradata_size); + if (!parser.ParseConfigs()) { + return; + } + meta.insert({Tag::AUDIO_AAC_LEVEL, parser.GetLevel()}); + auto profile = parser.GetProfile(); + if (profile != AudioAacProfile::NONE) { + meta.insert({Tag::AUDIO_AAC_PROFILE, profile}); + } + } else { + meta.insert({Tag::AUDIO_AAC_STREAM_FORMAT, AudioAacStreamFormat::MP4ADTS}); + } +} + +void ConvertAACLatmStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, + TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_AUDIO_AAC_LATM)}); + meta.insert({Tag::STREAM_INDEX, static_cast(avStream.index)}); + if (context->channels != -1) { + meta.insert({Tag::AUDIO_SAMPLE_RATE, static_cast(context->sample_rate)}); + meta.insert({Tag::AUDIO_CHANNELS, static_cast(context->channels)}); + meta.insert({Tag::AUDIO_SAMPLE_FORMAT, Trans2Format(context->sample_fmt)}); + meta.insert( + {Tag::AUDIO_CHANNEL_LAYOUT, ConvertChannelLayoutFromFFmpeg(context->channels, context->channel_layout)}); + meta.insert({Tag::AUDIO_SAMPLE_PRE_FRAME, static_cast(context->frame_size)}); + meta.insert({Tag::MEDIA_BITRATE, static_cast(context->bit_rate)}); + } + meta.insert({Tag::AUDIO_MPEG_VERSION, static_cast(4)}); // 4 + meta.insert({Tag::AUDIO_AAC_STREAM_FORMAT, AudioAacStreamFormat::MP4LOAS}); +} + +#ifdef VIDEO_SUPPORT +void ConvertAVCStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta) +{ + meta.insert({Tag::MIME, std::string(MEDIA_MIME_VIDEO_AVC)}); + meta.insert({Tag::STREAM_INDEX, static_cast(avStream.index)}); + meta.insert({Tag::MEDIA_BITRATE, context->bit_rate}); + meta.insert({Tag::VIDEO_WIDTH, static_cast(context->width)}); + meta.insert({Tag::VIDEO_HEIGHT, static_cast(context->height)}); + if (context->extradata_size > 0) { + AVCConfigDataParser parser(context->extradata, context->extradata_size); + if (!parser.ParseConfigData()) { + return; + } + std::shared_ptr cfgData = nullptr; + size_t cfgDataSize = 0; + if (parser.GetNewConfigData(cfgData, cfgDataSize) && (cfgData != nullptr) && (cfgDataSize != 0)) { + std::vector codecConfig; + codecConfig.assign(cfgData.get(), cfgData.get() + cfgDataSize); + meta.insert({Tag::MEDIA_CODEC_CONFIG, std::move(codecConfig)}); + } + } +} +#endif + +void ConvertAVStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta) +{ + meta.clear(); + auto codecId = avStream.codecpar->codec_id; + for (auto& streamConvertor : g_streamConvertors) { + if (streamConvertor.codecId == codecId) { + streamConvertor.convertor(avStream, context, meta); + return; + } + } + MEDIA_LOG_E("unsupported codec type: %d", codecId); +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h new file mode 100644 index 00000000..21c21a68 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FFMPEG_TRACK_META_H +#define HISTREAMER_FFMPEG_TRACK_META_H + +#include +#include +#include "plugin/common/plugin_tags.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libavformat/avformat.h" +#ifdef __cplusplus +}; +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +void ConvertRawAudioStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, + TagMap& meta); + +void ConvertMP1StreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); + +void ConvertMP2StreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); + +void ConvertMP3StreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); + +void ConvertAACStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); + +void ConvertAACLatmStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, + TagMap& meta); + +#ifdef VIDEO_SUPPORT +void ConvertAVCStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); +#endif + +void ConvertAVStreamToMetaInfo(const AVStream& avStream, const std::shared_ptr& context, TagMap& meta); +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FFMPEG_TRACK_META_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp b/engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp new file mode 100644 index 00000000..9548ce74 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "AACAudioConfigParser" + +#include "aac_audio_config_parser.h" +#include "bit_reader.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +namespace { +const uint32_t SAMPLE_RATES[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350 +}; +} + +AACAudioConfigParser::AACAudioConfigParser(const uint8_t* audioConfig, size_t len) + : bitReader_(audioConfig, len), isConfigValid_(false) +{ +} + +bool AACAudioConfigParser::ParseConfigs() +{ + isConfigValid_ = ParseObjectTypeFull(); + isConfigValid_ = isConfigValid_ && ParseLevel(); + isConfigValid_ = isConfigValid_ && ParseProfile(); + if (!isConfigValid_) { + Reset(); + MEDIA_LOG_E("ParseAudioObjectTypeFull failed"); + } + return isConfigValid_; +} + +uint32_t AACAudioConfigParser::GetLevel() const +{ + return audioConfig_.level; +} + +AudioAacProfile AACAudioConfigParser::GetProfile() const +{ + return audioConfig_.profile; +} + +void AACAudioConfigParser::Reset() +{ + audioConfig_.audioObjectType = 0xFF; // 0xFF + audioConfig_.channelConfig = 0xFF; // 0xFF + audioConfig_.sampleRate = 0; + audioConfig_.level = 0; + audioConfig_.profile = AudioAacProfile::NONE; +} + +bool AACAudioConfigParser::ParseObjectType() +{ + uint8_t audioObjectType = 0; + if (!bitReader_.ReadBits(5, audioObjectType)) { // 5 + return false; + } + if (audioObjectType == 31) { // 31 + if (!bitReader_.ReadBits(6, audioObjectType)) { // 6 + return false; + } + audioObjectType += 32; // 32 + } + audioConfig_.audioObjectType = audioObjectType; + return true; +} + +bool AACAudioConfigParser::ParseSampleRate() +{ + uint8_t sampleFreqIndex = 0; + uint32_t sampleRate = 0; + if (!bitReader_.ReadBits(4, sampleFreqIndex)) { // 4 + return false; + } + if (sampleFreqIndex == 0xf) { + if (!bitReader_.ReadBits(24, sampleRate)) { // 24 + return false; + } + } else { + if (sampleFreqIndex < sizeof(SAMPLE_RATES) / sizeof(uint32_t)) { + sampleRate = SAMPLE_RATES[sampleFreqIndex]; + } else { + return false; + } + } + audioConfig_.sampleRate = sampleRate; + return true; +} + +bool AACAudioConfigParser::ParseChannel() +{ + return bitReader_.ReadBits(4, audioConfig_.channelConfig); // 4 +} + +/** + * object type, sample rate, channel need to be parsed in the following order. + * @return TRUE if parse successfully, False otherwise. + */ +bool AACAudioConfigParser::ParseObjectTypeFull() +{ + if (!ParseObjectType() || !ParseSampleRate() || !ParseChannel()) { + return false; + } + // 5 indicates SBR extension (i.e. HE-AAC), 29 indicates PS extension + if (audioConfig_.audioObjectType == 5 || audioConfig_.audioObjectType == 29) { // 5 29 + return ParseSampleRate() && ParseObjectType(); + } + return true; +} + +/** + * Extract Number of single channel elements, channel pair elements, low frequency elements, + * independently switched coupling channel elements, and dependently switched coupling channel + * elements. + * Note: The 2 CCE types are ignored for now as they require us to actually + * parse the first frame, and they are rarely found in actual streams. + * @param sceCnt number of single channel elements + * @param cpeCnt number of channel pair elements + * @param lfeCnt number of low frequency elements + * @param indepCce number of independently switched coupling channel elements + * @param depCce number of dependently switched coupling channel elements + */ +bool AACAudioConfigParser::ExtractChannelElements(int& sceCnt, int& cpeCnt, int& lfeCnt, int& indepCce, int& depCce) +{ + indepCce = 0; + depCce = 0; + switch (audioConfig_.channelConfig) { + case 0: + MEDIA_LOG_W("Found a stream with channel configuration in the " + "AudioSpecificConfig. Please file a bug with a link to the media if " + "possible."); + return false; + case 1: /* front center */ + sceCnt = 1; + break; + case 2: // 2 front left and right + cpeCnt = 1; + break; + case 3: // 3 front left, right, and center + sceCnt = 1; + cpeCnt = 1; + break; + case 4: // 4 front left, right, and center; rear surround + sceCnt = 2; // 2 + cpeCnt = 1; + break; + case 5: // 5 front left, right, and center; rear left and right surround + sceCnt = 1; + cpeCnt = 2; // 2 + break; + case 6: // 6 front left, right, center and LFE; rear left and right surround + sceCnt = 1; + cpeCnt = 2; // 2 + break; + case 7: // 7 + case 12: // 12 + case 14: // 14 + /* front left, right, center and LFE; outside front left and right; + * rear left and right surround */ + sceCnt = 1; + cpeCnt = 3; // 3 + lfeCnt = 1; + break; + case 11: // 11 + sceCnt = 2; // 2 + cpeCnt = 2; // 2 + lfeCnt = 1; + break; + default: + MEDIA_LOG_W("Unknown channel config in header: %d", audioConfig_.channelConfig); + return false; + } + return true; +} + +/** + * Extract Processor and RAM Complexity Units (calculated and "reference" for single channel) + * @param refPcu + * @param refRcu + * @return + */ +bool AACAudioConfigParser::ExtractReferencePRCU(int& refPcu, int& refRcu) +{ + switch (audioConfig_.audioObjectType) { + case 0: /* NULL */ + MEDIA_LOG_W("profile 0 is not a valid profile"); + return false; + case 2: // 2, LC + refPcu = 3; // 3 + refRcu = 3; // 3 + break; + case 3: // 3, SSR + refPcu = 4; // 4 + refRcu = 3; // 3 + break; + case 4: // 4, LTP + refPcu = 4; // 4 + refRcu = 4; // 4 + break; + case 1: /* Main */ + default: + /* Other than a couple of ER profiles, Main is the worst-case */ + refPcu = 5; // 5 + refRcu = 5; // 5 + break; + } + return true; +} + +/** + * calculate profile from channelcnt, pcu, and rcu. + * @param channelCnt number of channels + * @param pcu processor complexity unit + * @param rcu ram complexity unit + * @return profile calculated result, true or false. + */ +bool AACAudioConfigParser::CalculateProfile(int channelCnt, int pcu, int rcu) +{ +#define AAC_PROFILE_VALUE_RANGE(maxChannelCnt, maxSampleRate, maxPcu, maxRcu) \ + channelCnt <= (maxChannelCnt) && audioConfig_.sampleRate <= (maxSampleRate) && pcu <= (maxPcu) && rcu <= (maxRcu) + + int ret = -1; // -1 + if (audioConfig_.audioObjectType == 2) { // 2 + /* AAC LC => return the level as per the 'AAC Profile' */ + if (AAC_PROFILE_VALUE_RANGE(2, 24000, 3, 5)) { // 2 24000 3 5 + ret = 1; // 1 + } else if (AAC_PROFILE_VALUE_RANGE(2, 48000, 6, 5)) { // 2 48000 6 5 + ret = 2; // 2 + } else if (AAC_PROFILE_VALUE_RANGE(5, 48000, 19, 15)) { // 5 48000 19 15 + ret = 4; // 4 + } else if (AAC_PROFILE_VALUE_RANGE(5, 96000, 38, 15)) { // 5 96000 38 15 + ret = 5; // 5 + } else if (AAC_PROFILE_VALUE_RANGE(7, 48000, 25, 19)) { // 7 48000 25 19 + ret = 6; // 6 + } else if (AAC_PROFILE_VALUE_RANGE(7, 96000, 50, 19)) { // 7 96000 50 19 + ret = 7; // 7 + } + } else { + /* Return the level as per the 'Main Profile' */ + if (pcu < 40 && rcu < 20) { // 40 20 + ret = 1; + } else if (pcu < 80 && rcu < 64) { // 80 64 + ret = 2; // 2 + } else if (pcu < 160 && rcu < 128) { // 160 128 + ret = 3; // 3 + } else if (pcu < 320 && rcu < 256) { // 320 256 + ret = 4; // 4 + } + } + MEDIA_LOG_W("determined level: profile=%u, sampleRate=%u, channel_config=%u, pcu=%d,rcu=%d", + audioConfig_.audioObjectType, audioConfig_.sampleRate, audioConfig_.channelConfig, pcu, rcu); + audioConfig_.level = static_cast(ret); + return ret != -1; +} + +/** + * Determines the level of a stream as defined in ISO/IEC 14496-3. For AAC LC + * streams, the constraints from the AAC audio profile are applied. For AAC + * Main, LTP, SSR and others, the Main profile is used. + * + * The @audioConfig parameter follows the following format, starting from the + * most significant bit of the first byte: + * + * * Bit 0:4 contains the AudioObjectType (if this is 0x5, then the + * real AudioObjectType is carried after the rate and channel data) + * * Bit 5:8 contains the sample frequency index (if this is 0xf, then the + * next 24 bits define the actual sample frequency, and subsequent + * fields are appropriately shifted). + * * Bit 9:12 contains the channel configuration + * + * Returns: TRUE if level can be set, FALSE otherwise. + */ +bool AACAudioConfigParser::ParseLevel() +{ + int sceCnt = 0; + int cpeCnt = 0; + int lfeCnt = 0; + int indepCce = 0; + int depCce = 0; + if (!ExtractChannelElements(sceCnt, cpeCnt, lfeCnt, indepCce, depCce)) { + return false; + } + int refPcu = 0; + int refRcu = 0; + if (!ExtractReferencePRCU(refPcu, refRcu)) { + return false; + } + int tmp = (static_cast(audioConfig_.sampleRate) / 48000) * refPcu; // 48000 + int pcu = tmp * ((2 * cpeCnt) + sceCnt + lfeCnt + indepCce + (0.3 * depCce)); // 48000 2 0.3 + int rcu = (static_cast(refRcu)) * (sceCnt + (0.5 * lfeCnt) + (0.5 * indepCce) + (0.4 * depCce)); // 0.5 0.4 + if (cpeCnt < 2) { // 2 + rcu += (refRcu + (refRcu - 1)) * cpeCnt; + } else { + rcu += (refRcu + (refRcu - 1) * ((2 * cpeCnt) - 1)); // 2 + } + int channelCnt = sceCnt + (2 * cpeCnt); // 2 + return CalculateProfile(channelCnt, pcu, rcu); +} + +bool AACAudioConfigParser::ParseProfile() +{ + bool ret = true; + switch (audioConfig_.audioObjectType) { + case 1: + audioConfig_.profile = AudioAacProfile::MAIN; + break; + case 2: // 2 + audioConfig_.profile = AudioAacProfile::LC; + break; + case 3: // 3 + audioConfig_.profile = AudioAacProfile::SSR; + break; + case 4: // 4 + audioConfig_.profile = AudioAacProfile::LTP; + break; + default: + audioConfig_.profile = AudioAacProfile::NONE; + MEDIA_LOG_W("ParseProfile failed due to invalid profile index: %u", audioConfig_.audioObjectType); + break; + } + return ret; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.h b/engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.h new file mode 100644 index 00000000..4bb6f18c --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_AAC_AUDIO_CONFIG_PARSER_H +#define HISTREAMER_AAC_AUDIO_CONFIG_PARSER_H + +#include +#include +#include "bit_reader.h" +#include "common/plugin_audio_tags.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +class AACAudioConfigParser final { +public: + AACAudioConfigParser(const uint8_t* audioConfig, size_t len); + + ~AACAudioConfigParser() = default; + + bool ParseConfigs(); + + uint32_t GetLevel() const; + + AudioAacProfile GetProfile() const; + +private: + struct AudioSpecificConfig { + uint8_t audioObjectType {0xFF}; + uint8_t channelConfig {0xFF}; + uint32_t sampleRate {0}; + uint32_t level {0}; + AudioAacProfile profile {AudioAacProfile::NONE}; + }; + + void Reset(); + + bool ParseObjectType(); + + bool ParseSampleRate(); + + bool ParseChannel(); + + bool ParseObjectTypeFull(); + + bool ExtractChannelElements(int& sceCnt, int& cpeCnt, int& lfeCnt, int& indepCce, int& depCce); + + bool ExtractReferencePRCU(int& refPcu, int& refRcu); + + bool CalculateProfile(int channelCnt, int pcu, int rcu); + + bool ParseLevel(); + + bool ParseProfile(); + + BitReader bitReader_; + bool isConfigValid_; + AudioSpecificConfig audioConfig_; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_AAC_AUDIO_CONFIG_PARSER_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.cpp b/engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.cpp new file mode 100644 index 00000000..18ae453e --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#define LOG_TAG "AVCConfigDataParser" + +#include "avc_config_data_parser.h" +#include +#include +#include "bit_reader.h" +#include "foundation/log.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +#define AVC_MIN_CONFIG_DATA_SIZE 7 +#define AVC_NAL_HEADER_LEN 4 + +// refer to ISO/IEC 14496-15: AVC NAL size bytes should be 1, 2, 4 +enum AVC_NAL_SIZE_LEN : int32_t { + AVC_NAL_SIZE_LEN_1 = 1, + AVC_NAL_SIZE_LEN_2 = 2, + AVC_NAL_SIZE_LEN_4 = 4, +}; + +AVCConfigDataParser::AVCConfigDataParser(const uint8_t* cfgData, const size_t cfgDataSize) + : bitReader_(cfgData, cfgDataSize), + cfgData_(cfgData), + cfgDataSize_(cfgDataSize), + newCfgData_(nullptr), + newCfgDataSize_(0), + nalUnitLen_(AVC_NAL_SIZE_LEN_4), + version_(1), + profile_(0), + profile_compat_(0), + level_(0), + needAddFrameHeader_(false) +{ + cfgSet.count = 0; +} + +AVCConfigDataParser::~AVCConfigDataParser() +{ + ClearConfigSet(); +} + +bool AVCConfigDataParser::ParseConfigData() +{ + if (!ParseNal()) { + if ((cfgDataSize_ < 3) || !((version_ == 0) && (profile_ == 0) && (profile_compat_ <= 1))) { // 3 + needAddFrameHeader_ = true; + } + if (cfgDataSize_ == 0) { + return false; + } + newCfgDataSize_ = cfgDataSize_; + newCfgData_ = std::shared_ptr(new uint8_t[newCfgDataSize_], std::default_delete()); + if (newCfgData_ == nullptr) { + MEDIA_LOG_E("Alloc new config data memory fail"); + return false; + } + (void)memcpy_s(newCfgData_.get(), cfgDataSize_, cfgData_, cfgDataSize_); + return true; + } + + needAddFrameHeader_ = true; + if (cfgSet.count == 0) { + return false; + } + for (auto i = 0; i < cfgSet.count; ++i) { + newCfgDataSize_ += (cfgSet.items[i]->len & 0xFFF); + } + newCfgData_ = std::shared_ptr(new uint8_t[newCfgDataSize_], std::default_delete()); + if (newCfgData_ == nullptr) { + MEDIA_LOG_E("Alloc new config data memory fail"); + return false; + } + uint32_t usedLen = 0; + for (auto i = 0; i < cfgSet.count; ++i) { + (void)memcpy_s(newCfgData_.get() + usedLen, cfgSet.items[i]->len, cfgSet.items[i]->SpsOrPps, + cfgSet.items[i]->len); + usedLen += cfgSet.items[i]->len; + } + return true; +} + +bool AVCConfigDataParser::IsNeedAddFrameHeader() +{ + return needAddFrameHeader_; // We can add this flag as specific header in newCfgData_, do it later +} + +bool AVCConfigDataParser::GetNewConfigData(std::shared_ptr& newCfgData, size_t& newCfgDataSize) +{ + if (newCfgData_ != nullptr) { + newCfgData = newCfgData_; + newCfgDataSize = newCfgDataSize_; + return true; + } + + return false; +} + +void AVCConfigDataParser::ClearConfigSet() +{ + for (auto i = 0; i < cfgSet.count; i++) { + if (cfgSet.items[i]) { + delete[] reinterpret_cast(cfgSet.items[i]); + } + } +} + +bool AVCConfigDataParser::ParseNalUnitSizeLen() +{ + uint8_t sizeLen_ = 0; + if (!bitReader_.ReadBits(1, sizeLen_)) { + return false; + } + + nalUnitLen_ = (sizeLen_ & 0x3) + 1; // lengthSize Minus One + if ((nalUnitLen_ != AVC_NAL_SIZE_LEN_1) && (nalUnitLen_ != AVC_NAL_SIZE_LEN_2) && + (nalUnitLen_ != AVC_NAL_SIZE_LEN_4)) { + MEDIA_LOG_I("Unsupported config data, nalUnitLen_: %u", nalUnitLen_); + return false; + } + + return true; +} + +bool AVCConfigDataParser::GetSpsOrPpsLen(uint32_t& len) +{ + uint8_t tmp1, tmp2; + if (!bitReader_.ReadBits(1, tmp1) || !bitReader_.ReadBits(1, tmp2)) { + return false; + } + len = ((tmp1 << 8) | tmp2) & 0xFFFF; // 8 + if (len > bitReader_.GetAvailableBits()) { + MEDIA_LOG_E("len: %u is too large", len); + return false; + } + return true; +} + +bool AVCConfigDataParser::ParseNalHeader() +{ + if (bitReader_.GetAvailableBits() < AVC_MIN_CONFIG_DATA_SIZE) { + MEDIA_LOG_E("Config data size is smaller than MIN: %d", static_cast(AVC_MIN_CONFIG_DATA_SIZE)); + return false; + } + auto ret = bitReader_.ReadBits(1, version_); // configurationVersion = 1 + if ((ret == false) || (version_ != 1)) { + // Some parser has parser config data, so just return + MEDIA_LOG_I("Unsupported config data, version: %u", version_); + return false; + } + if (!bitReader_.ReadBits(1, profile_)) { // AVCProfileIndication + return false; + } + if (!bitReader_.ReadBits(1, profile_compat_)) { // profile_compatibility + return false; + } + if (!bitReader_.ReadBits(1, level_)) { // AVCLevelIndication + return false; + } + if (!ParseNalUnitSizeLen()) { + return false; + } + return true; +} + +bool AVCConfigDataParser::CreateConfigSetItem(const uint32_t len) +{ + uint32_t itemLen = static_cast(sizeof(NalConfigItem)) + AVC_NAL_HEADER_LEN + len; + cfgSet.items[cfgSet.count] = reinterpret_cast(new (std::nothrow) uint8_t[itemLen]); + if (cfgSet.items[cfgSet.count] == nullptr) { + MEDIA_LOG_E("Alloc config set item memory fail"); + return false; + } + auto& item = cfgSet.items[cfgSet.count]; + // Add start code: 0x00000001 + item->SpsOrPps[0] = 0; + item->SpsOrPps[1] = 0; + item->SpsOrPps[2] = 0; // 2 + item->SpsOrPps[3] = 1; // 3 + item->len = AVC_NAL_HEADER_LEN + len; + return true; +} + +bool AVCConfigDataParser::ParseSpsOrPps(const uint32_t mask) +{ + uint32_t setCount = 0; + if (!bitReader_.ReadBits(1, setCount)) { + return false; + } + setCount &= mask; + for (auto idx = 0; idx < setCount; ++idx) { + if (bitReader_.GetAvailableBits() < 2) { // 2 + MEDIA_LOG_E("Sps data err"); + return false; + } + uint32_t len = 0; + if (!GetSpsOrPpsLen(len)) { + MEDIA_LOG_E("Get sps/pps len fail"); + return false; + } + if (cfgSet.count >= AVC_MAX_CONFIG_ITEM) { + MEDIA_LOG_E("config set count is larger than: %d", static_cast(AVC_MAX_CONFIG_ITEM)); + return false; + } + if (!CreateConfigSetItem(len)) { + MEDIA_LOG_E("Create config set item fail"); + return false; + } + (void)memcpy_s(&cfgSet.items[cfgSet.count]->SpsOrPps[AVC_NAL_HEADER_LEN], static_cast(len), + bitReader_.GetCurrentPtr(), static_cast(len)); + cfgSet.count++; + bitReader_.SkipBits(len); + } + return true; +} + +bool AVCConfigDataParser::ParseNal() +{ + if (!ParseNalHeader()) { + MEDIA_LOG_D("Parse NAL header fail"); + return false; + } + // Parse SPS + if (!ParseSpsOrPps(0x1F)) { + MEDIA_LOG_E("Parse SPS fail"); + return false; + } + if (bitReader_.GetAvailableBits() < 1) { + return false; + } + // Parse PPS + if (!ParseSpsOrPps(0xFF)) { + MEDIA_LOG_E("Parse PPS fail"); + return false; + } + return true; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.h b/engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.h new file mode 100644 index 00000000..de384cbb --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/avc_config_data_parser.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#ifndef HISTREAMER_AVC_CONFIG_DATA_PARSER_H +#define HISTREAMER_AVC_CONFIG_DATA_PARSER_H + +#include +#include +#include +#include "bit_reader.h" +#include "common/plugin_audio_tags.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +#define AVC_MAX_CONFIG_ITEM 256 + +class AVCConfigDataParser final { +public: + AVCConfigDataParser(const uint8_t* cfgData, const size_t cfgDataSize); + ~AVCConfigDataParser(); + + bool ParseConfigData(); + bool IsNeedAddFrameHeader(); + bool GetNewConfigData(std::shared_ptr& newCfgData, size_t& newCfgDataSize); + +private: + // sps or pps + struct NalConfigItem { + uint32_t len; + uint8_t SpsOrPps[0]; + }; + + struct NalConfigList { + uint32_t count; + NalConfigItem *items[AVC_MAX_CONFIG_ITEM]; + }; + + NalConfigList cfgSet; + + BitReader bitReader_; + const uint8_t* cfgData_; + const size_t cfgDataSize_; + + uint8_t nalUnitLen_; + uint8_t version_; + uint8_t profile_; + uint8_t profile_compat_; + uint8_t level_; + bool needAddFrameHeader_; + + std::shared_ptr newCfgData_; + size_t newCfgDataSize_; + + void ClearConfigSet(); + bool ParseNalUnitSizeLen(); + bool GetSpsOrPpsLen(uint32_t& len); + bool ParseNalHeader(); + bool CreateConfigSetItem(const uint32_t len); + bool ParseSpsOrPps(const uint32_t mask); + bool ParseNal(); +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_AVC_CONFIG_DATA_PARSER_H +#endif diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp b/engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp new file mode 100644 index 00000000..5d8d08d5 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "bit_reader.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +BitReader::BitReader() : begin_(nullptr), cur_(nullptr), end_(nullptr) +{ +} + +BitReader::BitReader(const uint8_t* buffer, size_t bufferSize) : begin_(buffer), cur_(buffer), end_(buffer + bufferSize) +{ +} + +BitReader::BitReader(const uint8_t* begin, const uint8_t* end) : begin_(begin), cur_(begin), end_(end) +{ +} + +BitReader::~BitReader() +{ + begin_ = nullptr; + cur_ = nullptr; + end_ = nullptr; + availBits_ = 8; // 8 +} + +size_t BitReader::GetAvailableBits() const +{ + return (cur_ != end_) ? static_cast(((end_ - cur_ - 1) * 8) + availBits_) : 0; // 8 +} + +const uint8_t* BitReader::GetCurrentPtr() const +{ + return cur_; +} + +void BitReader::SkipBits(size_t bits) +{ + if (bits <= availBits_) { + availBits_ -= static_cast(bits); + return; + } + cur_ += (1 + (bits -= availBits_) / 8); // 8 + if (cur_ >= end_) { + cur_ = end_; + availBits_ = 0; + } else { + availBits_ = 8 - bits % 8; // 8 + } +} + +bool BitReader::SeekTo(size_t bitPos) +{ + size_t bytePos = bitPos / 8; // 8 + if (begin_ + bytePos >= end_) { + return false; + } + cur_ = begin_ + bytePos; + uint8_t skipBits = bitPos % 8; // 8 + availBits_ = 8 - skipBits; // 8 + SkipBits(skipBits); + return true; +} + +void BitReader::Reset(const uint8_t* begin, const uint8_t* end) +{ + begin_ = begin; + cur_ = begin; + end_ = end; + availBits_ = 8; // 8 +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.h b/engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.h new file mode 100644 index 00000000..c1751ad3 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_BIT_READER_H +#define HISTREAMER_BIT_READER_H + +#include +#include + +namespace OHOS { +namespace Media { +namespace Plugin { +class BitReader { +public: + BitReader(); + + BitReader(const uint8_t* buffer, size_t bufferSize); + + BitReader(const uint8_t* begin, const uint8_t* end); + + template + BitReader(const T* buffer, size_t bufferSize) : BitReader(buffer, buffer + bufferSize) + { + } + + template + BitReader(const T* begin, const T* end) + : BitReader(reinterpret_cast(begin), reinterpret_cast(end)) + { + } + + ~BitReader(); + + template ::value, bool>::type = true> + bool ReadBits(uint8_t bits, T& val) + { + if (GetAvailableBits() < bits) { + return false; + } + val = 0; + for (uint8_t toRead; bits; bits -= toRead) { + if (availBits_ == 0) { + ++cur_; + availBits_ = 8; // 8 + } + toRead = std::min(bits, availBits_); + uint8_t shift = availBits_ - toRead; + uint64_t mask = 0xFF >> (0x08 - toRead); + val = static_cast((val << toRead) | static_cast(((*cur_) >> shift) & mask)); + availBits_ -= toRead; + } + return true; + } + + size_t GetAvailableBits() const; + + const uint8_t *GetCurrentPtr() const; + + void SkipBits(size_t bits); + + bool SeekTo(size_t bitPos); + + template + bool PeekBits(uint8_t bits, T& val) + { + auto tmp = *this; + return tmp.ReadBits(bits, val); + } + + template + void Reset(const T* buffer, size_t bufferSize) + { + Reset(buffer, buffer + bufferSize); + } + + template + void Reset(const T* begin, const T* end) + { + Reset(reinterpret_cast(begin), reinterpret_cast(end)); + } + +private: + void Reset(const uint8_t* begin, const uint8_t* end); + + const uint8_t* begin_; + const uint8_t* cur_; + const uint8_t* end_; + uint8_t availBits_ {8}; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_BIT_READER_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp b/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp new file mode 100644 index 00000000..648afba3 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "ffmpeg_utils.h" +#include +#include "common/plugin_audio_tags.h" +#include "foundation/log.h" +#include "libavutil/channel_layout.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +// Internal definitions +namespace { +// Histreamer channel layout to ffmpeg channel layout +std::map g_toFFMPEGChannelLayout = { + {AudioChannelLayout::MONO, AV_CH_FRONT_LEFT}, + {AudioChannelLayout::STEREO, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT}, + {AudioChannelLayout::CH_2POINT1, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_LOW_FREQUENCY}, + {AudioChannelLayout::CH_2_1, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_BACK_CENTER}, + {AudioChannelLayout::SURROUND, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER}, + {AudioChannelLayout::CH_3POINT1, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_LOW_FREQUENCY}, + {AudioChannelLayout::CH_4POINT0, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_CENTER}, + {AudioChannelLayout::CH_4POINT1, + AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_CENTER | AV_CH_LOW_FREQUENCY}, + {AudioChannelLayout::CH_2_2, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT}, + {AudioChannelLayout::QUAD, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT}, + {AudioChannelLayout::CH_5POINT0, + AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT}, + {AudioChannelLayout::CH_5POINT1, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_LOW_FREQUENCY}, + {AudioChannelLayout::CH_5POINT0_BACK, + AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT}, + {AudioChannelLayout::CH_5POINT1_BACK, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_LEFT | + AV_CH_BACK_RIGHT | AV_CH_LOW_FREQUENCY}, + {AudioChannelLayout::CH_6POINT0, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_LEFT | + AV_CH_BACK_RIGHT | AV_CH_BACK_CENTER}, + {AudioChannelLayout::CH_6POINT0_FRONT, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT | + AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER}, + {AudioChannelLayout::HEXAGONAL, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_LEFT | + AV_CH_BACK_RIGHT | AV_CH_BACK_CENTER}, + {AudioChannelLayout::CH_6POINT1, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_LOW_FREQUENCY | AV_CH_BACK_CENTER}, + {AudioChannelLayout::CH_6POINT1_BACK, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_LEFT | + AV_CH_BACK_RIGHT | AV_CH_LOW_FREQUENCY | AV_CH_BACK_CENTER}, + {AudioChannelLayout::CH_6POINT1_FRONT, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT | + AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER | + AV_CH_LOW_FREQUENCY}, + {AudioChannelLayout::CH_7POINT0, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT}, + {AudioChannelLayout::CH_7POINT0_FRONT, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_FRONT_LEFT_OF_CENTER | + AV_CH_FRONT_RIGHT_OF_CENTER}, + {AudioChannelLayout::CH_7POINT1, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_LOW_FREQUENCY | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT}, + {AudioChannelLayout::CH_7POINT1_WIDE, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_LOW_FREQUENCY | AV_CH_FRONT_LEFT_OF_CENTER | + AV_CH_FRONT_RIGHT_OF_CENTER}, + {AudioChannelLayout::CH_7POINT1_WIDE_BACK, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | + AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT | AV_CH_LOW_FREQUENCY | + AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER}, + {AudioChannelLayout::OCTAGONAL, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_BACK_LEFT | AV_CH_BACK_CENTER | AV_CH_BACK_RIGHT}, + {AudioChannelLayout::HEXADECAGONAL, AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_SIDE_LEFT | + AV_CH_SIDE_RIGHT | AV_CH_BACK_LEFT | AV_CH_BACK_CENTER | AV_CH_BACK_RIGHT | + AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT | AV_CH_TOP_BACK_LEFT | + AV_CH_TOP_BACK_CENTER | AV_CH_TOP_BACK_RIGHT | AV_CH_TOP_FRONT_LEFT | + AV_CH_TOP_FRONT_CENTER | AV_CH_TOP_FRONT_RIGHT}, + {AudioChannelLayout::STEREO_DOWNMIX, AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT}, +}; + +// ffmpeg channel layout to histreamer channel layout +std::map g_fromFFMPEGChannelLayout = { + {AV_CH_FRONT_LEFT, AudioChannelMasks::FRONT_LEFT}, + {AV_CH_FRONT_RIGHT, AudioChannelMasks::FRONT_RIGHT}, + {AV_CH_FRONT_CENTER, AudioChannelMasks::FRONT_CENTER}, + {AV_CH_LOW_FREQUENCY, AudioChannelMasks::LOW_FREQUENCY}, + {AV_CH_BACK_LEFT, AudioChannelMasks::BACK_LEFT}, + {AV_CH_BACK_RIGHT, AudioChannelMasks::BACK_RIGHT}, + {AV_CH_FRONT_LEFT_OF_CENTER, AudioChannelMasks::FRONT_LEFT_OF_CENTER}, + {AV_CH_FRONT_RIGHT_OF_CENTER, AudioChannelMasks::FRONT_RIGHT_OF_CENTER}, + {AV_CH_BACK_CENTER, AudioChannelMasks::BACK_CENTER}, + {AV_CH_SIDE_LEFT, AudioChannelMasks::SIDE_LEFT}, + {AV_CH_SIDE_RIGHT, AudioChannelMasks::SIDE_RIGHT}, + {AV_CH_TOP_CENTER, AudioChannelMasks::TOP_CENTER}, + {AV_CH_TOP_FRONT_LEFT, AudioChannelMasks::TOP_FRONT_LEFT}, + {AV_CH_TOP_FRONT_CENTER, AudioChannelMasks::TOP_FRONT_CENTER}, + {AV_CH_TOP_FRONT_RIGHT, AudioChannelMasks::TOP_FRONT_RIGHT}, + {AV_CH_TOP_BACK_LEFT, AudioChannelMasks::TOP_BACK_LEFT}, + {AV_CH_TOP_BACK_CENTER, AudioChannelMasks::TOP_BACK_CENTER}, + {AV_CH_TOP_BACK_RIGHT, AudioChannelMasks::TOP_BACK_RIGHT}, + {AV_CH_STEREO_LEFT, AudioChannelMasks::STEREO_LEFT}, + {AV_CH_STEREO_RIGHT, AudioChannelMasks::STEREO_RIGHT}, +}; +} // namespace + +std::string AVStrError(int errnum) +{ + char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); + return std::string(errbuf); +} + +uint64_t ConvertTimeFromFFmpeg(int64_t pts, AVRational base) +{ + uint64_t out; + if (pts == AV_NOPTS_VALUE) { + out = static_cast(-1); + } else { + constexpr int timeScale = 1000000; + AVRational bq = {1, timeScale}; + out = av_rescale_q(pts, base, bq); + } + return out; +} + +int64_t ConvertTimeToFFmpeg(int64_t timestampUs, AVRational base) +{ + int64_t result; + if (base.num == 0) { + result = AV_NOPTS_VALUE; + } else { + constexpr int timeScale = 1000000; + AVRational bq = {1, timeScale}; + result = av_rescale_q(timestampUs, bq, base); + } + return result; +} + +int FillAVPicture(AVFrame* picture, uint8_t* ptr, enum AVPixelFormat pixFmt, int width, int height) +{ + return 0; +} + +int GetAVPictureSize(int pixFmt, int width, int height) +{ + AVFrame dummy; + return FillAVPicture(&dummy, nullptr, static_cast(pixFmt), width, height); +} + +std::string RemoveDelimiter(const char* str, char delimiter) +{ + std::string tmp(str); + RemoveDelimiter(delimiter, tmp); + return tmp; +} + +void RemoveDelimiter(char delimiter, std::string& str) +{ + for (auto it = std::find(str.begin(), str.end(), delimiter); it != str.end();) { + it = str.erase(it); + if (*it != delimiter) { + it = std::find(it, str.end(), delimiter); + } + } +} + +void ReplaceDelimiter(const std::string& delmiters, char newDelimiter, std::string& str) +{ + for (auto it = str.begin(); it != str.end(); ++it) { + if (delmiters.find(newDelimiter) != std::string::npos) { + *it = newDelimiter; + } + } +} + +std::vector SplitString(const char* str, char delimiter) +{ + std::vector rtv; + if (str) { + SplitString(std::string(str), delimiter).swap(rtv); + } + return rtv; +} + +std::vector SplitString(const std::string& str, char delimiter) +{ + if (str.empty()) { + return {}; + } + std::vector rtv; + std::string::size_type startPos = 0; + std::string::size_type endPos = str.find_first_of(delimiter, startPos); + while (startPos != endPos) { + rtv.emplace_back(str.substr(startPos, endPos - startPos)); + if (endPos == std::string::npos) { + break; + } + startPos = endPos + 1; + endPos = str.find_first_of(delimiter, startPos); + } + return rtv; +} + +AudioSampleFormat Trans2Format(AVSampleFormat sampleFormat) +{ + switch (sampleFormat) { + case AV_SAMPLE_FMT_U8: + return AudioSampleFormat::U8; + case AV_SAMPLE_FMT_U8P: + return AudioSampleFormat::U8P; + case AV_SAMPLE_FMT_S16: + return AudioSampleFormat::S16; + case AV_SAMPLE_FMT_S16P: + return AudioSampleFormat::S16P; + case AV_SAMPLE_FMT_S32: + return AudioSampleFormat::S32; + case AV_SAMPLE_FMT_S32P: + return AudioSampleFormat::S32P; + case AV_SAMPLE_FMT_FLT: + return AudioSampleFormat::F32; + case AV_SAMPLE_FMT_FLTP: + return AudioSampleFormat::F32P; + case AV_SAMPLE_FMT_DBL: + return AudioSampleFormat::F64; + case AV_SAMPLE_FMT_DBLP: + return AudioSampleFormat::F64P; + default: + return AudioSampleFormat::U8; + } +} + +AVSampleFormat Trans2FFmepgFormat(AudioSampleFormat sampleFormat) +{ + switch (sampleFormat) { + case AudioSampleFormat::U8: + return AVSampleFormat::AV_SAMPLE_FMT_U8; + case AudioSampleFormat::U8P: + return AVSampleFormat::AV_SAMPLE_FMT_U8P; + case AudioSampleFormat::S16: + return AVSampleFormat::AV_SAMPLE_FMT_S16; + case AudioSampleFormat::S16P: + return AVSampleFormat::AV_SAMPLE_FMT_S16P; + case AudioSampleFormat::S32: + return AVSampleFormat::AV_SAMPLE_FMT_S32; + case AudioSampleFormat::S32P: + return AVSampleFormat::AV_SAMPLE_FMT_S32P; + case AudioSampleFormat::F32: + return AVSampleFormat::AV_SAMPLE_FMT_FLT; + case AudioSampleFormat::F32P: + return AVSampleFormat::AV_SAMPLE_FMT_FLTP; + case AudioSampleFormat::F64: + return AVSampleFormat::AV_SAMPLE_FMT_DBL; + case AudioSampleFormat::F64P: + return AVSampleFormat::AV_SAMPLE_FMT_DBLP; + default: + return AV_SAMPLE_FMT_NONE; + } +} + +AudioChannelLayout ConvertChannelLayoutFromFFmpeg(int channels, uint64_t ffChannelLayout) +{ + uint64_t channelLayout = 0; + uint64_t mask = 1; + for (uint8_t bitPos = 0, channelNum = 0; (bitPos < 64) && (channelNum < channels); ++bitPos) { // 64 + mask = 1ULL << bitPos; + if (!(mask & ffChannelLayout)) { + continue; + } + channelNum++; + auto it = g_fromFFMPEGChannelLayout.find(mask); + if (it != g_fromFFMPEGChannelLayout.end()) { + channelLayout |= static_cast(it->second); + } else { + MEDIA_LOG_W("unsupported audio channel layout: %lu", mask); + } + } + auto ret = static_cast(channelLayout); + if (ffChannelLayout == 0) { + if (channels == 1) { + ret = AudioChannelLayout::MONO; + } + if (channels == 2) { // 2 + ret = AudioChannelLayout::STEREO; + } + } + return ret; +} + +uint64_t ConvertChannelLayoutToFFmpeg(AudioChannelLayout channelLayout) +{ + auto it = g_toFFMPEGChannelLayout.find(channelLayout); + if (it == g_toFFMPEGChannelLayout.end()) { + MEDIA_LOG_E("ConvertChannelLayoutToFFmpeg, unexpected audio channel layout: %lu", + static_cast(channelLayout)); + return 0; + } + return it->second; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h b/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h new file mode 100644 index 00000000..5d31db3e --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_FFMPEG_UTILS_H +#define HISTREAMER_FFMPEG_UTILS_H + +#include +#include +#include "foundation/type_define.h" +#include "plugin/common/plugin_audio_tags.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libavutil/error.h" +#include "libavutil/frame.h" +#ifdef __cplusplus +}; +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +template +using MakeUnsigned = typename std::make_unsigned::type; + +template +static constexpr T AlignUp(T num, U alignment) +{ + return alignment > 0 ? (static_cast((num + static_cast>(alignment) - 1)) & + static_cast((~(static_cast>(alignment) - 1)))) : + num; +} + +std::string AVStrError(int errnum); + +/** + * Convert time from ffmpeg to time in us. + * @param pts ffmpeg time + * @param base ffmpeg time_base + * @return time in milliseconds + */ +uint64_t ConvertTimeFromFFmpeg(int64_t pts, AVRational base); + +/** + * Convert time in milliseconds to ffmpeg time. + * @param time time in milliseconds + * @param base ffmpeg time_base + * @return time in ffmpeg. + */ +int64_t ConvertTimeToFFmpeg(int64_t timestampUs, AVRational base); + +/* + * Fill in pointers in an AVFrame, aligned by 4 (required by X). + */ +int FillAVPicture(AVFrame* picture, uint8_t* ptr, enum AVPixelFormat pixFmt, int width, int height); + +/* + * Get the size of an picture + */ +int GetAVPictureSize(int pixFmt, int width, int height); + +void RemoveDelimiter(char delimiter, std::string& str); + +std::string RemoveDelimiter(const char* str, char delimiter); + +void ReplaceDelimiter(const std::string& delmiters, char newDelimiter, std::string& str); + +std::vector SplitString(const char* str, char delimiter); + +std::vector SplitString(const std::string& string, char delimiter); + +AudioSampleFormat Trans2Format(AVSampleFormat sampleFormat); + +AVSampleFormat Trans2FFmepgFormat(AudioSampleFormat sampleFormat, bool interleaved); + +AudioChannelLayout ConvertChannelLayoutFromFFmpeg(int channels, uint64_t ffChannelLayout); + +uint64_t ConvertChannelLayoutToFFmpeg(AudioChannelLayout channelLayout); +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif // HISTREAMER_FFMPEG_UTILS_H diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp new file mode 100644 index 00000000..da5e8ab2 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp @@ -0,0 +1,658 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#define LOG_TAG "Ffmpeg_Video_Decoder" + +#include "video_ffmpeg_decoder_plugin.h" +#include +#include +#include +#include "foundation/log.h" +#include "foundation/constants.h" +#include "foundation/memory_helper.h" +#include "plugin/common/plugin_buffer.h" +#include "plugin/common/plugin_video_tags.h" +#include "plugin/interface/codec_plugin.h" +#include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" + +namespace { +// register plugins +using namespace OHOS::Media::Plugin; +void UpdatePluginDefinition(const AVCodec* codec, CodecPluginDef& definition); + +std::map> codecMap; + +const size_t BUFFER_QUEUE_SIZE = 6; + +std::set supportedCodec = {AV_CODEC_ID_H264}; + +std::shared_ptr VideoFfmpegDecoderCreator(const std::string& name) +{ + return std::make_shared(name); +} + +Status RegisterVideoDecoderPlugins(const std::shared_ptr ®) +{ + const AVCodec* codec = nullptr; + void* iter = nullptr; + MEDIA_LOG_I("registering video decoders"); + while ((codec = av_codec_iterate(&iter))) { + if (!av_codec_is_decoder(codec) || codec->type != AVMEDIA_TYPE_VIDEO) { + continue; + } + if (supportedCodec.find(codec->id) == supportedCodec.end()) { + MEDIA_LOG_W("codec %s(%s) is not supported right now", codec->name, codec->long_name); + continue; + } + CodecPluginDef definition; + definition.name = "videodecoder_" + std::string(codec->name); + definition.codecType = CodecType::VIDEO_DECODER; + definition.rank = 100; // 100 + definition.creator = VideoFfmpegDecoderCreator, UpdatePluginDefinition(codec, definition); + // do not delete the codec in the deleter + codecMap[definition.name] = std::shared_ptr(const_cast(codec), [](void* ptr) {}); + if (reg->AddPlugin(definition) != Status::OK) { + MEDIA_LOG_W("register plugin %s(%s) failed", codec->name, codec->long_name); + } + } + return Status::OK; +} + +void UnRegisterVideoDecoderPlugins() +{ + codecMap.clear(); +} + +void UpdatePluginDefinition(const AVCodec* codec, CodecPluginDef& definition) +{ + Capability cap("video/unknown"); + switch (codec->id) { + case AV_CODEC_ID_H264: + cap.SetMime(OHOS::Media::MEDIA_MIME_VIDEO_AVC); + break; + default: + MEDIA_LOG_I("codec is not supported right now"); + break; + } + definition.inCaps.push_back(cap); +} +} // namespace + +PLUGIN_DEFINITION(FFmpegVideoDecoders, LicenseType::LGPL, RegisterVideoDecoderPlugins, UnRegisterVideoDecoderPlugins); + +namespace OHOS { +namespace Media { +namespace Plugin { +namespace { +struct PixelFormatMap { + AVPixelFormat avPixelFormat; + VideoPixelFormat videoPixelFormat; +}; + +const PixelFormatMap g_pixelFormatMap[] = { + {AV_PIX_FMT_YUV420P, VideoPixelFormat::YUV420P}, {AV_PIX_FMT_YUYV422, VideoPixelFormat::YUV420P}, + {AV_PIX_FMT_RGB24, VideoPixelFormat::RGB24}, {AV_PIX_FMT_BGR24, VideoPixelFormat::BGR24}, + {AV_PIX_FMT_YUV422P, VideoPixelFormat::YUV422P}, {AV_PIX_FMT_YUV444P, VideoPixelFormat::YUV444P}, + {AV_PIX_FMT_YUV410P, VideoPixelFormat::YUV410P}, {AV_PIX_FMT_YUV411P, VideoPixelFormat::YUV411P}, + {AV_PIX_FMT_GRAY8, VideoPixelFormat::GRAY8}, {AV_PIX_FMT_MONOWHITE, VideoPixelFormat::MONOWHITE}, + {AV_PIX_FMT_MONOBLACK, VideoPixelFormat::MONOBLACK}, {AV_PIX_FMT_PAL8, VideoPixelFormat::PAL8}, + {AV_PIX_FMT_YUVJ420P, VideoPixelFormat::YUVJ420P}, {AV_PIX_FMT_YUVJ422P, VideoPixelFormat::YUVJ422P}, + {AV_PIX_FMT_YUVJ444P, VideoPixelFormat::YUVJ444P}, {AV_PIX_FMT_NV12, VideoPixelFormat::NV12}, + {AV_PIX_FMT_NV21, VideoPixelFormat::NV21}, +}; + +VideoPixelFormat TranslatePixelFormat(int32_t pixelFormat) +{ + auto newFormat = VideoPixelFormat::UNKNOWN; + for (const auto& pixelMap : g_pixelFormatMap) { + if (pixelMap.avPixelFormat == pixelFormat) { + newFormat = pixelMap.videoPixelFormat; + break; + } + } + MEDIA_LOG_D("pixelFormat: %d, newFormat: %u", pixelFormat, newFormat); + return newFormat; +} +} // namespace + +VideoFfmpegDecoderPlugin::VideoFfmpegDecoderPlugin(std::string name) + : name_(std::move(name)), outBufferQ_("decoderPluginQueue", BUFFER_QUEUE_SIZE) +{ +} + +Status VideoFfmpegDecoderPlugin::Init() +{ + OSAL::ScopedLock l(lock_); + auto iter = codecMap.find(name_); + if (iter == codecMap.end()) { + MEDIA_LOG_W("cannot find codec with name %s", name_.c_str()); + return Status::ERROR_UNSUPPORTED_FORMAT; + } + avCodec_ = iter->second; + cachedFrame_ = std::shared_ptr(av_frame_alloc(), [](AVFrame* fp) { av_frame_free(&fp); }); + state_ = State::INITIALIZED; + videoDecParams_[Tag::REQUIRED_OUT_BUFFER_CNT] = (uint32_t)BUFFER_QUEUE_SIZE; + MEDIA_LOG_I("Init success"); + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::Deinit() +{ + OSAL::ScopedLock l(lock_); + avCodec_.reset(); + cachedFrame_.reset(); + ResetLocked(); + state_ = State::DESTROYED; + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::SetParameter(Tag tag, const ValueType& value) +{ + OSAL::ScopedLock l(lock_); + videoDecParams_.insert(std::make_pair(tag, value)); + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::GetParameter(Tag tag, ValueType& value) +{ + OSAL::ScopedLock l(lock_); + auto res = videoDecParams_.find(tag); + if (res != videoDecParams_.end()) { + value = res->second; + return Status::OK; + } + return Status::ERROR_INVALID_PARAMETER; +} + +template +void VideoFfmpegDecoderPlugin::FindInParameterMapThenAssignLocked(Tag tag, T& assign) +{ + auto iter = videoDecParams_.find(tag); + if (iter != videoDecParams_.end() && typeid(T) == iter->second.Type()) { + assign = Plugin::AnyCast(iter->second); + } else { + MEDIA_LOG_W("parameter %d is not found or type mismatch", static_cast(tag)); + } +} + +Status VideoFfmpegDecoderPlugin::CreateCodecContext() +{ + auto context = avcodec_alloc_context3(avCodec_.get()); + if (context == nullptr) { + MEDIA_LOG_E("cannot allocate codec context"); + return Status::ERROR_UNKNOWN; + } + avCodecContext_ = std::shared_ptr(context, [](AVCodecContext* ptr) { + if (ptr != nullptr) { + if (ptr->extradata) { + av_free(ptr->extradata); + ptr->extradata = nullptr; + } + avcodec_free_context(&ptr); + } + }); + MEDIA_LOG_I("Create ffmpeg codec context success"); + return Status::OK; +} + +void VideoFfmpegDecoderPlugin::ConfigCodecId() +{ + std::string mine; + FindInParameterMapThenAssignLocked(Tag::MIME, mine); + if (!mine.compare("video/h264")) { + avCodecContext_->codec_id = AV_CODEC_ID_H264; + MEDIA_LOG_I("Ffmpeg codec id: %d", static_cast(avCodecContext_->codec_id)); + } else { + // Support more codec id later + MEDIA_LOG_I("Unsupported mine: %s yet", mine.c_str()); + } +} + +void VideoFfmpegDecoderPlugin::InitCodecContext() +{ + avCodecContext_->codec_type = AVMEDIA_TYPE_VIDEO; + ConfigCodecId(); + FindInParameterMapThenAssignLocked(Tag::MEDIA_BITRATE, avCodecContext_->bit_rate); + FindInParameterMapThenAssignLocked(Tag::VIDEO_WIDTH, width_); + FindInParameterMapThenAssignLocked(Tag::VIDEO_HEIGHT, height_); + uint32_t format; + FindInParameterMapThenAssignLocked(Tag::VIDEO_PIXEL_FORMAT, format); + pixelFormat_ = static_cast(format); + MEDIA_LOG_D("bitRate: %" PRId64 ", width: %u, height: %u, pixelFormat: %u", avCodecContext_->bit_rate, width_, + height_, pixelFormat_); + SetCodecExtraData(); + // Reset coded_width/_height to prevent it being reused from last time when + // the codec is opened again, causing a mismatch and possible segfault/corruption. + avCodecContext_->coded_width = 0; + avCodecContext_->coded_height = 0; + avCodecContext_->workaround_bugs |= FF_BUG_AUTODETECT; + avCodecContext_->err_recognition = 1; +} + +void VideoFfmpegDecoderPlugin::DeinitCodecContext() +{ + if (avCodecContext_ == nullptr) { + return; + } + if (avCodecContext_->extradata) { + av_free(avCodecContext_->extradata); + avCodecContext_->extradata = nullptr; + } + avCodecContext_->extradata_size = 0; + avCodecContext_->opaque = nullptr; + avCodecContext_->width = 0; + avCodecContext_->height = 0; + avCodecContext_->coded_width = 0; + avCodecContext_->coded_height = 0; + avCodecContext_->time_base.den = 0; + avCodecContext_->time_base.num = 0; + avCodecContext_->ticks_per_frame = 0; + avCodecContext_->sample_aspect_ratio.num = 0; + avCodecContext_->sample_aspect_ratio.den = 0; + avCodecContext_->get_buffer2 = nullptr; +} + +void VideoFfmpegDecoderPlugin::SetCodecExtraData() +{ + auto iter = videoDecParams_.find(Tag::MEDIA_CODEC_CONFIG); + if (iter == videoDecParams_.end() || iter->second.Type() != typeid(std::vector)) { + return; + } + auto codecConfig = Plugin::AnyCast>(iter->second); + int configSize = codecConfig.size(); + if (configSize > 0) { + auto allocSize = AlignUp(configSize + AV_INPUT_BUFFER_PADDING_SIZE, 16); // 16 + avCodecContext_->extradata = static_cast(av_mallocz(allocSize)); + (void)memcpy_s(avCodecContext_->extradata, configSize, codecConfig.data(), configSize); + avCodecContext_->extradata_size = configSize; + MEDIA_LOG_I("Set Codec Extra Data success"); + } +} + +Status VideoFfmpegDecoderPlugin::OpenCodecContext() +{ + AVCodec* vdec = avcodec_find_decoder(avCodecContext_->codec_id); + if (vdec == nullptr) { + MEDIA_LOG_E("Codec: %d is not found", static_cast(avCodecContext_->codec_id)); + DeinitCodecContext(); + return Status::ERROR_INVALID_PARAMETER; + } + auto res = avcodec_open2(avCodecContext_.get(), avCodec_.get(), nullptr); + if (res != 0) { + MEDIA_LOG_E("avcodec open error %s when start decoder ", AVStrError(res).c_str()); + DeinitCodecContext(); + return Status::ERROR_UNKNOWN; + } + MEDIA_LOG_I("Open ffmpeg codec context success"); + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::CloseCodecContext() +{ + Status ret = Status::OK; + if (avCodecContext_ != nullptr) { + auto res = avcodec_close(avCodecContext_.get()); + if (res != 0) { + DeinitCodecContext(); + MEDIA_LOG_E("avcodec close error %s when stop decoder", AVStrError(res).c_str()); + ret = Status::ERROR_UNKNOWN; + } + avCodecContext_.reset(); + } + return ret; +} + +Status VideoFfmpegDecoderPlugin::Prepare() +{ + { + OSAL::ScopedLock l(lock_); + if (state_ != State::INITIALIZED && state_ != State::PREPARED) { + return Status::ERROR_WRONG_STATE; + } + if (CreateCodecContext() != Status::OK) { + MEDIA_LOG_E("Create codec context fail"); + return Status::ERROR_UNKNOWN; + } + InitCodecContext(); +#ifdef DUMP_RAW_DATA + dumpData_.open("./vdec_out.dat", std::ios::out | std::ios::binary); +#endif + state_ = State::PREPARED; + } + outBufferQ_.SetActive(true); + MEDIA_LOG_I("Prepare success"); + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::ResetLocked() +{ + videoDecParams_.clear(); + avCodecContext_.reset(); + outBufferQ_.Clear(); +#ifdef DUMP_RAW_DATA + dumpData_.close(); +#endif + state_ = State::INITIALIZED; + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::Reset() +{ + OSAL::ScopedLock l(lock_); + return ResetLocked(); +} + +Status VideoFfmpegDecoderPlugin::Start() +{ + { + OSAL::ScopedLock l(lock_); + if (state_ != State::PREPARED) { + return Status::ERROR_WRONG_STATE; + } + if (OpenCodecContext() != Status::OK) { + MEDIA_LOG_E("Open codec context fail"); + return Status::ERROR_UNKNOWN; + } + state_ = State::RUNNING; + } + outBufferQ_.SetActive(true); + MEDIA_LOG_I("Start success"); + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::Stop() +{ + Status ret = Status::OK; + { + OSAL::ScopedLock l(lock_); + ret = CloseCodecContext(); +#ifdef DUMP_RAW_DATA + dumpData_.close(); +#endif + state_ = State::INITIALIZED; + } + outBufferQ_.SetActive(false); + return ret; +} + +Status VideoFfmpegDecoderPlugin::QueueOutputBuffer(const std::shared_ptr& outputBuffer, int32_t timeoutMs) +{ + MEDIA_LOG_I("queue out put"); + outBufferQ_.Push(outputBuffer); + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::Flush() +{ + OSAL::ScopedLock l(lock_); + if (avCodecContext_ != nullptr) { + avcodec_flush_buffers(avCodecContext_.get()); + } + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) +{ + MEDIA_LOG_D("queue input buffer"); + Status status = SendBuffer(inputBuffer); + if (status != Status::OK) { + return status; + } + bool receiveOneFrame = false; + do { + receiveOneFrame = ReceiveBuffer(status); + if (status != Status::OK) { + break; + } + } while (receiveOneFrame); + return status; +} + +Status VideoFfmpegDecoderPlugin::SendBufferLocked(const std::shared_ptr& inputBuffer) +{ + if (state_ != State::RUNNING) { + MEDIA_LOG_W("queue input buffer in wrong state"); + return Status::ERROR_WRONG_STATE; + } + AVPacket packet; + size_t bufferLength = 0; + bool eos = false; + if (inputBuffer == nullptr || (inputBuffer->flag & BUFFER_FLAG_EOS) != 0) { + // eos buffer + eos = true; + } else { + auto inputMemory = inputBuffer->GetMemory(); + const uint8_t* ptr = inputMemory->GetReadOnlyData(); + bufferLength = inputMemory->GetSize(); + size_t bufferEnd = bufferLength; + // pad to data if needed + if ((bufferLength % AV_INPUT_BUFFER_PADDING_SIZE != 0) && + (bufferLength - bufferEnd + bufferLength % AV_INPUT_BUFFER_PADDING_SIZE < AV_INPUT_BUFFER_PADDING_SIZE)) { + if (paddedBufferSize_ < bufferLength + AV_INPUT_BUFFER_PADDING_SIZE) { + paddedBufferSize_ = bufferLength + AV_INPUT_BUFFER_PADDING_SIZE; + paddedBuffer_.reserve(paddedBufferSize_); + MEDIA_LOG_I("increase padded buffer size to %zu", paddedBufferSize_); + } + paddedBuffer_.assign(ptr, ptr + bufferLength); + paddedBuffer_.insert(paddedBuffer_.end(), AV_INPUT_BUFFER_PADDING_SIZE, 0); + ptr = paddedBuffer_.data(); + } + av_init_packet(&packet); + packet.data = const_cast(ptr); + packet.size = static_cast(bufferLength); + packet.pts = static_cast(inputBuffer->pts); + } + AVPacket* packetPtr = nullptr; + if (!eos) { + packetPtr = &packet; + } + auto ret = avcodec_send_packet(avCodecContext_.get(), packetPtr); + if (ret < 0) { + MEDIA_LOG_E("send buffer error %s", AVStrError(ret).c_str()); + } + return Status::OK; +} + +Status VideoFfmpegDecoderPlugin::SendBuffer(const std::shared_ptr& inputBuffer) +{ + if (inputBuffer->IsEmpty() && !(inputBuffer->flag & BUFFER_FLAG_EOS)) { + MEDIA_LOG_E("decoder does not support fd buffer"); + return Status::ERROR_INVALID_DATA; + } + Status ret = Status::OK; + { + OSAL::ScopedLock l(lock_); + ret = SendBufferLocked(inputBuffer); + } + NotifyInputBufferDone(inputBuffer); + return ret; +} + +void VideoFfmpegDecoderPlugin::CheckResolutionChange() +{ + if ((width_ > 0) && (height_ > 0) && ((cachedFrame_->width != width_) || (cachedFrame_->height != height_))) { + MEDIA_LOG_W("Demuxer's W&H&S: [%u %u] diff from FFMPEG's [%d %d]", width_, height_, cachedFrame_->width, + cachedFrame_->height); + // need to reallocte output buffers + } +} + +#ifdef DUMP_RAW_DATA +void VideoFfmpegDecoderPlugin::DumpVideoRawOutData() +{ + if (cachedFrame_->format == AV_PIX_FMT_YUV420P) { + if (cachedFrame_->data[0] != nullptr && cachedFrame_->linesize[0] != 0) { + dumpData_.write((char*)cachedFrame_->data[0], cachedFrame_->linesize[0] * cachedFrame_->height); + } + if (cachedFrame_->data[1] != nullptr && cachedFrame_->linesize[1] != 0) { + dumpData_.write((char*)cachedFrame_->data[1], cachedFrame_->linesize[1] * cachedFrame_->height / 2); // 2 + } + if (cachedFrame_->data[2] != nullptr && cachedFrame_->linesize[2] != 0) { // 2 + dumpData_.write((char*)cachedFrame_->data[2], cachedFrame_->linesize[2] * cachedFrame_->height / 2); // 2 + } + } else if (cachedFrame_->format == AV_PIX_FMT_NV12 || cachedFrame_->format == AV_PIX_FMT_NV21) { + if (cachedFrame_->data[0] != nullptr && cachedFrame_->linesize[0] != 0) { + dumpData_.write((char*)cachedFrame_->data[0], cachedFrame_->linesize[0] * cachedFrame_->height); + } + if (cachedFrame_->data[1] != nullptr && cachedFrame_->linesize[1] != 0) { + dumpData_.write((char*)cachedFrame_->data[1], cachedFrame_->linesize[1] * cachedFrame_->height / 2); // 2 + } + } +} +#endif + +void VideoFfmpegDecoderPlugin::CalculateFrameSizes(size_t& ySize, size_t& uvSize, size_t& frameSize) +{ + ySize = static_cast(cachedFrame_->linesize[0] * AlignUp(cachedFrame_->height, 16)); // 16 + // AV_PIX_FMT_YUV420P: linesize[0] = linesize[1] * 2, AV_PIX_FMT_NV12: linesize[0] = linesize[1] + uvSize = static_cast(cachedFrame_->linesize[1] * AlignUp(cachedFrame_->height, 16) / 2); // 2 16 + frameSize = 0; + if (cachedFrame_->format == AV_PIX_FMT_YUV420P) { + frameSize = ySize + (uvSize * 2); // 2 + } else if (cachedFrame_->format == AV_PIX_FMT_NV12 || cachedFrame_->format == AV_PIX_FMT_NV21) { + frameSize = ySize + uvSize; + } +} + +Status VideoFfmpegDecoderPlugin::FillFrameBuffer(const std::shared_ptr& frameBuffer, bool& receiveOneFrame, + bool& notifyBufferDone) +{ + MEDIA_LOG_D("receive one frame: %d, picture type: %d, pixel format: %d, packet size: %d", cachedFrame_->key_frame, + static_cast(cachedFrame_->pict_type), static_cast(cachedFrame_->format), + cachedFrame_->pkt_size); + if ((cachedFrame_->flags & AV_FRAME_FLAG_CORRUPT) || (TranslatePixelFormat(cachedFrame_->format) != pixelFormat_)) { + MEDIA_LOG_W("format: %d unsupported, pixelFormat_: %u", cachedFrame_->format, pixelFormat_); + return Status::ERROR_INVALID_DATA; + } + CheckResolutionChange(); +#ifdef DUMP_RAW_DATA + DumpVideoRawOutData(); +#endif + MEDIA_LOG_D("linesize: %d, %d, %d", cachedFrame_->linesize[0], cachedFrame_->linesize[1], + cachedFrame_->linesize[2]); // 2 + auto bufferMeta = frameBuffer->GetBufferMeta(); + if (bufferMeta != nullptr && bufferMeta->GetType() == BufferMetaType::VIDEO) { + std::shared_ptr videoMeta = std::dynamic_pointer_cast(bufferMeta); + videoMeta->videoPixelFormat = TranslatePixelFormat(cachedFrame_->format); + videoMeta->height = cachedFrame_->height; + videoMeta->width = cachedFrame_->width; + for (int i = 0; cachedFrame_->linesize[i] > 0; ++i) { + videoMeta->stride.emplace_back(cachedFrame_->linesize[i]); + } + videoMeta->planes = videoMeta->stride.size(); + } + size_t ySize = 0; + size_t uvSize = 0; + size_t frameSize = 0; + CalculateFrameSizes(ySize, uvSize, frameSize); + auto frameBufferMem = frameBuffer->GetMemory(); + if (frameBufferMem->GetCapacity() < frameSize) { + MEDIA_LOG_W("output buffer size is not enough: real[%zu], need[%zu]", frameBufferMem->GetCapacity(), frameSize); + receiveOneFrame = false; + notifyBufferDone = false; + return Status::ERROR_NO_MEMORY; + } + if (cachedFrame_->format == AV_PIX_FMT_YUV420P) { + frameBufferMem->Write(cachedFrame_->data[0], ySize); + frameBufferMem->Write(cachedFrame_->data[1], uvSize); + frameBufferMem->Write(cachedFrame_->data[2], uvSize); // 2 + } else if ((cachedFrame_->format == AV_PIX_FMT_NV12) || (cachedFrame_->format == AV_PIX_FMT_NV21)) { + frameBufferMem->Write(cachedFrame_->data[0], ySize); + frameBufferMem->Write(cachedFrame_->data[1], uvSize); + } + frameBuffer->pts = static_cast(cachedFrame_->pts); + notifyBufferDone = true; + receiveOneFrame = true; + return Status::OK; +} + +void VideoFfmpegDecoderPlugin::ReceiveBufferLocked(Status& status, const std::shared_ptr& frameBuffer, + bool& receiveOneFrame, bool& notifyBufferDone) +{ + if (state_ != State::RUNNING) { + MEDIA_LOG_W("queue input buffer in wrong state"); + status = Status::ERROR_WRONG_STATE; + receiveOneFrame = false; + notifyBufferDone = false; + return; + } + auto ret = avcodec_receive_frame(avCodecContext_.get(), cachedFrame_.get()); + if (ret >= 0) { + status = FillFrameBuffer(frameBuffer, receiveOneFrame, notifyBufferDone); + } else if (ret == AVERROR_EOF) { + MEDIA_LOG_I("eos received"); + frameBuffer->GetMemory()->Reset(); + notifyBufferDone = true; + receiveOneFrame = false; + status = Status::END_OF_STREAM; + } else { + MEDIA_LOG_I("video decoder receive error: %s", AVStrError(ret).c_str()); + notifyBufferDone = false; + receiveOneFrame = false; + status = Status::OK; + } + av_frame_unref(cachedFrame_.get()); +} + +bool VideoFfmpegDecoderPlugin::ReceiveBuffer(Status& status) +{ + bool notifyBufferDone = false; + bool receiveOneFrame = false; + std::shared_ptr frameBuffer = outBufferQ_.Pop(); + if (frameBuffer == nullptr || frameBuffer->IsEmpty()) { + MEDIA_LOG_W("cannot fetch valid buffer to output"); + return false; + } + if (frameBuffer->GetBufferMeta()->GetType() != BufferMetaType::VIDEO) { + MEDIA_LOG_W("Cannot handle non-video buffer"); + receiveOneFrame = false; + } else { + OSAL::ScopedLock l(lock_); + ReceiveBufferLocked(status, frameBuffer, receiveOneFrame, notifyBufferDone); + } + if (notifyBufferDone) { + NotifyOutputBufferDone(frameBuffer); + } else { + outBufferQ_.Push(frameBuffer); + } + return receiveOneFrame; +} + +void VideoFfmpegDecoderPlugin::NotifyInputBufferDone(const std::shared_ptr& input) +{ + auto ptr = dataCb_.lock(); + if (ptr != nullptr) { + ptr->OnInputBufferDone(input); + } +} + +void VideoFfmpegDecoderPlugin::NotifyOutputBufferDone(const std::shared_ptr& output) +{ + auto ptr = dataCb_.lock(); + if (ptr != nullptr) { + ptr->OnOutputBufferDone(output); + } +} + +std::shared_ptr VideoFfmpegDecoderPlugin::GetAllocator() +{ + return nullptr; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h new file mode 100644 index 00000000..0e88eb72 --- /dev/null +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#ifndef HISTREAMER_VIDEO_FFMPEG_DECODER_PLUGIN_H +#define HISTREAMER_VIDEO_FFMPEG_DECODER_PLUGIN_H + +#include +#include +#include "foundation/blocking_queue.h" +#include "plugin/interface/codec_plugin.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libavcodec/avcodec.h" +#ifdef __cplusplus +}; +#endif + +#ifdef DUMP_RAW_DATA +#include +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +class VideoFfmpegDecoderPlugin : public CodecPlugin { +public: + explicit VideoFfmpegDecoderPlugin(std::string name); + ~VideoFfmpegDecoderPlugin() override = default; + + Status Init() override; + Status Deinit() override; + Status Prepare() override; + Status Reset() override; + Status Start() override; + Status Stop() override; + + bool IsParameterSupported(Tag tag) override + { + return true; + } + + Status GetParameter(Tag tag, ValueType &value) override; + Status SetParameter(Tag tag, const ValueType &value) override; + + Status GetState(State &state) override + { + return Status::ERROR_UNIMPLEMENTED; + } + + std::shared_ptr GetAllocator() override; + + Status SetCallback(const std::shared_ptr &cb) override + { + return Status::OK; + } + + Status QueueInputBuffer(const std::shared_ptr &inputBuffer, int32_t timeoutMs) override; + + Status QueueOutputBuffer(const std::shared_ptr &outputBuffers, int32_t timeoutMs) override; + + Status Flush() override; + + Status SetDataCallback(const std::weak_ptr &dataCallback) override + { + dataCb_ = dataCallback; + return Status::OK; + } + +private: + Status CreateCodecContext(); + void InitCodecContext(); + void DeinitCodecContext(); + void SetCodecExtraData(); + Status OpenCodecContext(); + Status CloseCodecContext(); + + void ConfigCodecId(); + + Status ResetLocked(); + + template + void FindInParameterMapThenAssignLocked(Tag tag, T &assign); + + Status SendBuffer(const std::shared_ptr &inputBuffer); + Status SendBufferLocked(const std::shared_ptr &inputBuffer); + + void CalculateFrameSizes(size_t &ySize, size_t &uvSize, size_t &frameSize); + Status FillFrameBuffer(const std::shared_ptr &frameBuffer, bool &receiveOneFrame, bool ¬ifyBufferDone); + bool ReceiveBuffer(Status &err); + void ReceiveBufferLocked(Status &status, const std::shared_ptr &ioInfo, + bool &receiveOneFrame, bool ¬ifyBufferDone); + + void CheckResolutionChange(); + +#ifdef DUMP_RAW_DATA + std::ofstream dumpData_; + void DumpVideoRawOutData(); +#endif + + void CopyYUV2NativeBuffer(const std::shared_ptr &ioInfo, const uint32_t frameSize); + + void NotifyInputBufferDone(const std::shared_ptr &input); + void NotifyOutputBufferDone(const std::shared_ptr &output); + + std::string name_; + std::shared_ptr avCodec_; + std::map videoDecParams_ {}; + std::vector paddedBuffer_; + size_t paddedBufferSize_ {0}; + std::shared_ptr cachedFrame_; + std::weak_ptr dataCb_ {}; + + uint32_t width_; + uint32_t height_; + VideoPixelFormat pixelFormat_; + + mutable OSAL::Mutex lock_ {}; + State state_ {State::CREATED}; + std::shared_ptr avCodecContext_ {}; + OHOS::Media::BlockingQueue> outBufferQ_; +}; +} +} +} +#endif // HISTREAMER_VIDEO_FFMPEG_DECODER_PLUGIN_H +#endif diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp new file mode 100644 index 00000000..0f304a40 --- /dev/null +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "HdiSink" + +#include "hos_au_sink.h" + +#include +#include + +#include "securec.h" + +#include "audio_proxy_manager.h" +#include "audio_adapter.h" + +#include "foundation/constants.h" +#include "foundation/log.h" +#include "foundation/osal/thread/scoped_lock.h" +#include "foundation/osal/utils/util.h" +#include "plugin/common/plugin_audio_tags.h" +#include "plugins/hdi_adapter/utils/hdi_au_utils.h" +#include "ring_buffer.h" + +namespace { +using namespace OHOS::Media::Plugin; +constexpr int32_t MAX_RETRY_CNT = 3; +constexpr int32_t RETRY_INTERVAL = 100; // 100ms +constexpr int32_t DEFAULT_BUFFER_POOL_SIZE = 5; +constexpr int32_t HI_ERR_VI_BUF_FULL = 0xA016800F; +constexpr int32_t RANK100 = 100; +constexpr int32_t HALF = 2; +constexpr int32_t SEC_TO_MILLS = 1000; + + +Status LoadAndInitAdapter(AudioManager *proxyManager, AudioAdapterDescriptor *descriptor, AudioAdapter **adapter) +{ + if (proxyManager == nullptr) { + MEDIA_LOG_E("no audio manager when load adapter"); + return Status::ERROR_UNKNOWN; + } + if (adapter == nullptr) { + MEDIA_LOG_E("**adapter null ptr"); + return Status::ERROR_INVALID_PARAMETER; + } + if (proxyManager->LoadAdapter(proxyManager, descriptor, adapter) < 0) { + *adapter = nullptr; + MEDIA_LOG_W("failed to load adapter %s", descriptor->adapterName); + return Status::ERROR_UNSUPPORTED_FORMAT; + } + if (*adapter == nullptr) { + MEDIA_LOG_E("no available adapter after load adapter"); + return Status::ERROR_UNKNOWN; + } + + int32_t retryCnt = 0; + do { + if ((*adapter)->InitAllPorts(*adapter) != 0) { + OHOS::Media::OSAL::SleepFor(RETRY_INTERVAL); + } else { + break; + } + MEDIA_LOG_I("retry init port on adapter %s", descriptor->adapterName); + } while (++retryCnt < MAX_RETRY_CNT); + if (retryCnt >= MAX_RETRY_CNT) { + MEDIA_LOG_W("cannot init port on adapter %s after retry %d times", descriptor->adapterName, retryCnt); + proxyManager->UnloadAdapter(proxyManager, *adapter); + *adapter = nullptr; + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +std::shared_ptr AudioSinkPluginCreator(const std::string &name) +{ + return std::make_shared(name); +} + +Status RegisterHdiSinkPlugins(const std::shared_ptr& reg) +{ + auto proxyManager = GetAudioManagerFuncs(); + if (proxyManager == nullptr) { + MEDIA_LOG_E("cannot find audio manager funcs"); + return Status::ERROR_UNKNOWN; + } + int32_t adapterSize = 0; + AudioAdapterDescriptor *descriptors = nullptr; + int32_t ret = proxyManager->GetAllAdapters(proxyManager, &descriptors, &adapterSize); + if (ret != 0 || adapterSize == 0) { + MEDIA_LOG_E("cannot find available audio adapter"); + return Status::OK; + } + for (int32_t index = 0; index < adapterSize; index++) { + AudioAdapter *adapter = nullptr; + const auto &desc = descriptors[index]; + if (LoadAndInitAdapter(proxyManager, &descriptors[index], &adapter) != Status::OK) { + continue; + } + CapabilitySet adapterCapabilities; + for (uint32_t portIndex = 0; portIndex < desc.portNum; portIndex++) { + if (desc.ports[portIndex].dir != PORT_OUT) { + continue; + } + Capability capability(OHOS::Media::MEDIA_MIME_AUDIO_RAW); + adapterCapabilities.emplace_back(capability); + break; + } + if (adapterCapabilities.empty()) { + continue; + } + AudioSinkPluginDef sinkPluginDef; + sinkPluginDef.creator = AudioSinkPluginCreator; + sinkPluginDef.name = desc.adapterName; + sinkPluginDef.inCaps = adapterCapabilities; + sinkPluginDef.rank = RANK100; + proxyManager->UnloadAdapter(proxyManager, adapter); + if (reg->AddPlugin(sinkPluginDef) == Status::OK) { + MEDIA_LOG_D("register plugin %s succ.", desc.adapterName); + } else { + MEDIA_LOG_W("register plugin %s failed", desc.adapterName); + } + } + return Status::OK; +} + +template +inline Status AssignIfCastSuccess(T &lvalue, const Any& anyValue, const char* tagName) +{ + if (typeid(T) == anyValue.Type()) { + lvalue = AnyCast(anyValue); + MEDIA_LOG_I("AssignIfCastSuccess found %s", tagName); + return Status::OK; + } else { + MEDIA_LOG_W("tag:%s value type mismatch", tagName); + return Status::ERROR_MISMATCHED_TYPE; + } +} + + +int32_t CalculateBufferSize(const AudioSampleAttributes &attributes) +{ + return attributes.frameSize * attributes.period; +} + +PLUGIN_DEFINITION(HdiAuSink, LicenseType::APACHE_V2, RegisterHdiSinkPlugins, []() {}); +} +namespace OHOS { +namespace Media { +namespace HosLitePlugin { +using namespace OHOS::Media::Plugin; + +HdiSink::HdiSink(std::string name) : adapterName_(std::move(name)), audioManager_(nullptr) +{ + // default is media + sampleAttributes_.type = AUDIO_IN_MEDIA; +} + +Status HdiSink::Init() +{ + MEDIA_LOG_D("Init entered."); + if (pluginState_ == State::DESTROYED) { + MEDIA_LOG_E("plugin has been already destroyed, cannot init any more"); + return Status::ERROR_WRONG_STATE; + } + if (pluginState_ != State::CREATED) { + MEDIA_LOG_I("plugin has been already inited"); + return Status::OK; + } + audioManager_ = GetAudioManagerFuncs(); + if (audioManager_ == nullptr) { + MEDIA_LOG_E("Init error due to audioManager nullptr"); + return Status::ERROR_UNKNOWN; + } + int32_t adapterSize = 0; + AudioAdapterDescriptor *descriptors = nullptr; + int32_t ret = audioManager_->GetAllAdapters(audioManager_, &descriptors, &adapterSize); + if (ret != 0 || adapterSize == 0) { + MEDIA_LOG_E("cannot find available audio adapter"); + return Status::ERROR_UNKNOWN; + } + for (int32_t index = 0; index < adapterSize; index++) { + const auto &desc = descriptors[index]; + if (adapterName_ != desc.adapterName) { + continue; + } + + if (LoadAndInitAdapter(audioManager_, &descriptors[index], &audioAdapter_) != Status::OK) { + continue; + } + adapterDescriptor_ = descriptors[index]; + } + if (audioAdapter_ == nullptr) { + MEDIA_LOG_E("cannot find adapter with name %s", adapterName_.c_str()); + return Status::ERROR_UNKNOWN; + } + if (!renderThread_) { + renderThread_ = std::make_shared("auRenderThread"); + renderThread_->RegisterHandler([this] { DoRender(); }); + } + pluginState_ = State::INITIALIZED; + return Status::OK; +} + +Media::Plugin::Status HdiSink::ReleaseRender() +{ + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioAdapter_ != nullptr && audioRender_ != nullptr) { + audioAdapter_->DestroyRender(audioAdapter_, audioRender_); + audioRender_ = nullptr; + } + } + return Status::OK; +} + +Status HdiSink::Deinit() +{ + MEDIA_LOG_E("Deinit entered."); + if (pluginState_ == State::DESTROYED || pluginState_ == State::CREATED) { + MEDIA_LOG_I("no need to destroy"); + return Status::OK; + } + if (renderThread_ != nullptr) { + renderThread_->Stop(); + } + // release all resources + ReleaseRender(); + if (audioManager_ != nullptr) { + if (audioAdapter_ != nullptr) { + audioManager_->UnloadAdapter(audioManager_, audioAdapter_); + audioAdapter_ = nullptr; + } + audioManager_ = nullptr; + } + pluginState_ = State::DESTROYED; + return Status::OK; +} + +Status HdiSink::SetParameter(Tag tag, const ValueType &value) +{ + if (pluginState_ == State::DESTROYED || pluginState_ == State::CREATED) { + MEDIA_LOG_E("cannot set parameter in state %d", pluginState_.load()); + return Status::ERROR_WRONG_STATE; + } + switch (tag) { + case Tag::AUDIO_CHANNELS: + return AssignIfCastSuccess(sampleAttributes_.channelCount, value, "channel"); + case Tag::AUDIO_SAMPLE_RATE: + return AssignIfCastSuccess(sampleAttributes_.sampleRate, value, "sampleRate"); + case Tag::AUDIO_SAMPLE_FORMAT: { + AudioSampleFormat format; + auto ret = AssignIfCastSuccess(format, value, "audioSampleFormat"); + if (ret != Status::OK) { + return ret; + } + if (PluginAuFormat2HdiAttrs(format, sampleAttributes_)) { + return Status::OK; + } else { + MEDIA_LOG_E("audioSampleFormat mismatch"); + ret = Status::ERROR_MISMATCHED_TYPE; + break; + } + } + case Tag::AUDIO_SAMPLE_PRE_FRAME: + return AssignIfCastSuccess(sampleAttributes_.period, value, "samples per frame"); + case Tag::AUDIO_CHANNEL_LAYOUT: { + AudioChannelLayout layout; + auto ret = AssignIfCastSuccess(layout, value, "audioChannelLayout"); + if (ret != Status::OK) { + return ret; + } + if (PluginChannelLayout2HdiMask(layout, channelMask_)) { + return Status::OK; + } else { + MEDIA_LOG_E("audioChannelLayout mismatch"); + return Status::ERROR_MISMATCHED_TYPE; + } + } + default: + MEDIA_LOG_I("receive one parameter with unconcern tag, ignore it"); + } + return Status::OK; +} + +Status HdiSink::GetParameter(Tag tag, ValueType &value) +{ + return Status::ERROR_UNIMPLEMENTED; +} + +Status HdiSink::Prepare() +{ + if (pluginState_ != State::PREPARED && pluginState_ != State::INITIALIZED) { + MEDIA_LOG_E("cannot prepare in state %d", pluginState_.load()); + return Status::ERROR_WRONG_STATE; + } + if (pluginState_ == State::PREPARED) { + return Status::OK; + } + + sampleAttributes_.frameSize = GetPcmBytes(sampleAttributes_.format) * sampleAttributes_.channelCount; + sampleAttributes_.startThreshold = sampleAttributes_.period / sampleAttributes_.frameSize; + sampleAttributes_.stopThreshold = INT32_MAX; + bool foundPort = false; + for (uint32_t portIndex = 0; portIndex < adapterDescriptor_.portNum; portIndex++) { + if (adapterDescriptor_.ports[portIndex].dir == PORT_OUT) { + audioPort_ = adapterDescriptor_.ports[portIndex]; + foundPort = true; + break; + } + } + if (!foundPort) { + MEDIA_LOG_E("cannot find out port"); + return Status::ERROR_UNKNOWN; + } + + deviceDescriptor_.portId = audioPort_.portId; + deviceDescriptor_.pins = PIN_OUT_SPEAKER; + deviceDescriptor_.desc = nullptr; + + MEDIA_LOG_I("create render on adapter: %s, port: %d, with parameters: category %s, channels %d, sampleRate %d," + " audioChannelMask %x, format %d, isSignedData %d, interleaved %d, period %u, frameSize %u", + adapterDescriptor_.adapterName, deviceDescriptor_.portId, + (sampleAttributes_.type == AUDIO_IN_MEDIA) ? "media" : "communication", sampleAttributes_.channelCount, + sampleAttributes_.sampleRate, channelMask_, sampleAttributes_.format, sampleAttributes_.isSignedData, + sampleAttributes_.interleaved, sampleAttributes_.period, sampleAttributes_.frameSize); + + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + auto ret = audioAdapter_->CreateRender(audioAdapter_, &deviceDescriptor_, &sampleAttributes_, &audioRender_); + if (ret != 0) { + MEDIA_LOG_E("cannot create render with error code %lx", ret); + audioRender_ = nullptr; + return Status::ERROR_UNKNOWN; + } + } + MEDIA_LOG_I("create audio render successfully"); + ringBuffer_ = std::make_shared(DEFAULT_BUFFER_POOL_SIZE * CalculateBufferSize(sampleAttributes_)); + if (!ringBuffer_->Init()) { + MEDIA_LOG_E("cannot allocate enough buffer for ring buffer cache"); + return Status::ERROR_NO_MEMORY; + } + + pluginState_ = State::PREPARED; + return Status::OK; +} + +Status HdiSink::Reset() +{ + MEDIA_LOG_D("Reset entered."); + if (pluginState_ != State::PREPARED && pluginState_ != State::RUNNING && pluginState_ != State::PAUSED) { + MEDIA_LOG_I("cannot reset in state %d", pluginState_.load()); + return Status::ERROR_WRONG_STATE; + } + ReleaseRender(); + (void)memset_s(&audioPort_, sizeof(audioPort_), 0, sizeof(audioPort_)); + (void)memset_s(&sampleAttributes_, sizeof(sampleAttributes_), 0, sizeof(sampleAttributes_)); + (void)memset_s(&deviceDescriptor_, sizeof(deviceDescriptor_), 0, sizeof(deviceDescriptor_)); + channelMask_ = AUDIO_CHANNEL_MONO; + + return Status::OK; +} + +Status HdiSink::Start() +{ + MEDIA_LOG_D("Start entered."); + if (pluginState_ != State::PREPARED && pluginState_ != State::RUNNING && pluginState_ != State::PAUSED) { + MEDIA_LOG_E("cannot Start in state %d", pluginState_.load()); + return Status::ERROR_WRONG_STATE; + } + if (pluginState_ == State::RUNNING) { + MEDIA_LOG_I("already in running state, ignore start"); + return Status::OK; + } + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_E("no available render"); + return Status::ERROR_UNKNOWN; + } + + if (audioRender_->control.Start(audioRender_) != 0) { + MEDIA_LOG_E("audio render start error"); + return Status::ERROR_UNKNOWN; + } + } + ringBuffer_->SetActive(true); + renderThread_->Start(); + pluginState_ = State::RUNNING; + return Status::OK; +} + +Status HdiSink::Stop() +{ + MEDIA_LOG_D("Stop Entered"); + if (pluginState_ != State::RUNNING && pluginState_ != State::PAUSED) { + MEDIA_LOG_W("Stop is called when not running or paused, ignore it"); + return Status::OK; + } + ringBuffer_->SetActive(false); + renderThread_->Pause(); + pluginState_ = State::PREPARED; + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_E("no available render"); + return Status::OK; + } + if (audioRender_->control.Stop(audioRender_) != 0) { + MEDIA_LOG_E("audio render stop error"); + return Status::ERROR_UNKNOWN; + } + } + MEDIA_LOG_D("Stop Exited"); + return Status::OK; +} + +bool HdiSink::IsParameterSupported(Tag tag) +{ + return false; +} + +Status HdiSink::GetState(State &state) +{ + state = pluginState_; + return Status::OK; +} + +std::shared_ptr HdiSink::GetAllocator() +{ + return nullptr; +} + +Status HdiSink::SetCallback(const std::shared_ptr &cb) +{ + eventCallback_ = cb; + return Status::OK; +} + +Status HdiSink::GetMute(bool &mute) +{ + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, get mute must be called after prepared"); + return Status::ERROR_WRONG_STATE; + } + if (audioRender_->volume.GetMute(audioRender_, &mute) != 0) { + MEDIA_LOG_E("get mute failed"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status HdiSink::SetMute(bool mute) +{ + // todo when to set mute + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, set mute must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + if (audioRender_->volume.SetMute(audioRender_, mute) != 0) { + MEDIA_LOG_E("set mute failed"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status HdiSink::GetVolume(float &volume) +{ + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, get volume must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + if (audioRender_->volume.GetVolume(audioRender_, &volume) != 0) { + MEDIA_LOG_E("get volume failed"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status HdiSink::SetVolume(float volume) +{ + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, set volume must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + if (audioRender_->volume.SetVolume(audioRender_, volume) != 0) { + MEDIA_LOG_E("set volume failed"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status HdiSink::GetSpeed(float &speed) +{ + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, get speed must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + if (audioRender_->GetRenderSpeed(audioRender_, &speed) != 0) { + MEDIA_LOG_E("get speed failed"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status HdiSink::SetSpeed(float speed) +{ + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, set speed must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + if (audioRender_->SetRenderSpeed(audioRender_, speed) != 0) { + MEDIA_LOG_E("set speed failed"); + return Status::ERROR_UNKNOWN; + } + return Status::OK; +} + +Status HdiSink::Pause() +{ + MEDIA_LOG_D("Pause Entered"); + if (pluginState_ != State::RUNNING) { + MEDIA_LOG_I("pause in status %d, ignore pause", pluginState_.load()); + return Status::OK; + } + renderThread_->Pause(); + + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ != nullptr && audioRender_->control.Pause(audioRender_) != 0) { + MEDIA_LOG_E("pause failed"); + return Status::ERROR_UNKNOWN; + } + } + pluginState_ = State::PAUSED; + return Status::OK; +} + +Status HdiSink::Resume() +{ + MEDIA_LOG_D("Resume Entered"); + if (pluginState_ != State::PAUSED) { + MEDIA_LOG_I("resume in status %d, ignore pause", pluginState_.load()); + return Status::OK; + } + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ != nullptr && audioRender_->control.Resume(audioRender_) != 0) { + MEDIA_LOG_E("resume failed"); + return Status::ERROR_UNKNOWN; + } + } + renderThread_->Start(); + pluginState_ = State::RUNNING; + return Status::OK; +} + +Status HdiSink::GetLatency(uint64_t &ms) +{ + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_W("no render available, get latency must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + uint32_t tmp; + if (audioRender_->GetLatency(audioRender_, &tmp) != 0) { + MEDIA_LOG_E("get latency failed"); + return Status::ERROR_UNKNOWN; + } + ms = tmp; + return Status::OK; +} + +Status HdiSink::GetFrameSize(size_t &size) +{ + return Status::ERROR_UNIMPLEMENTED; +} + +Status HdiSink::GetFrameCount(uint32_t &count) +{ + return Status::ERROR_UNIMPLEMENTED; +} + +Status HdiSink::Write(const std::shared_ptr &input) +{ + MEDIA_LOG_D("Write begin."); + if (pluginState_ != State::RUNNING) { + MEDIA_LOG_E("cannot write buffer until enter running state"); + return Status::ERROR_WRONG_STATE; + } + if (input != nullptr && !input->IsEmpty()) { + ringBuffer_->WriteBuffer(input); + MEDIA_LOG_D("write to ring buffer"); + } + MEDIA_LOG_D("Write finished."); + return Status::OK; +} + +Status HdiSink::Flush() +{ + MEDIA_LOG_I("Flush Entered"); + ringBuffer_->Clear(); + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ == nullptr) { + MEDIA_LOG_E("no render available, flush must be called after prepare"); + return Status::ERROR_WRONG_STATE; + } + + if (audioRender_->control.Flush(audioRender_) != 0) { + MEDIA_LOG_E("audio render flush error"); + return Status::ERROR_UNKNOWN; + } + } + MEDIA_LOG_I("Flush Exited."); + return Status::OK; +} + +void HdiSink::DoRender() +{ + MEDIA_LOG_D("DoRender started"); + if (pluginState_ != State::RUNNING) { + return; + } + + size_t outSize = 0; + auto outFramePtr = ringBuffer_->ReadBufferWithoutAdvance(CalculateBufferSize(sampleAttributes_), outSize); + if (outFramePtr == nullptr || outSize == 0) { + return; + } + uint64_t renderSize = 0; + auto ret = 0; + { + OHOS::Media::OSAL::ScopedLock lock(renderMutex_); + if (audioRender_ != nullptr) { + ret = audioRender_->RenderFrame(audioRender_, outFramePtr.get(), outSize, &renderSize); + } + } + if (ret != 0) { + if (ret == HI_ERR_VI_BUF_FULL) { + MEDIA_LOG_I("renderFrame buffer full"); + uint32_t latency = sampleAttributes_.period * SEC_TO_MILLS / sampleAttributes_.sampleRate; + MEDIA_LOG_D("latency origin %u ms", latency); + OHOS::Media::OSAL::SleepFor(latency / HALF); + } else { + MEDIA_LOG_E("renderFrame error with code %lx", ret); + } + return; + } else { + MEDIA_LOG_D("render frame %d", renderSize); + } + + if (renderSize != 0) { + ringBuffer_->Advance(renderSize); + } +} +} +} +} diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h new file mode 100644 index 00000000..34e43ed8 --- /dev/null +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HDI_SINK_H +#define HISTREAMER_HDI_SINK_H + +#include + +#include "audio_types.h" +#include "foundation/osal/thread/mutex.h" +#include "foundation/osal/thread/condition_variable.h" +#include "foundation/osal/thread/task.h" +#include "interface/audio_sink_plugin.h" +#include "audio_manager.h" + +struct AudioAdapter; +struct AudioRender; +struct AudioPort; +namespace OHOS { +namespace Media { +namespace HosLitePlugin { +class RingBuffer; + +class HdiSink : public std::enable_shared_from_this, public OHOS::Media::Plugin::AudioSinkPlugin { +public: + explicit HdiSink(std::string name); + + ~HdiSink() override = default; + + Media::Plugin::Status Init() override; + + Media::Plugin::Status Deinit() override; + + Media::Plugin::Status Prepare() override; + + Media::Plugin::Status Reset() override; + + Media::Plugin::Status Start() override; + + Media::Plugin::Status Stop() override; + + bool IsParameterSupported(Media::Plugin::Tag tag) override; + + Media::Plugin::Status GetParameter(Media::Plugin::Tag tag, Media::Plugin::ValueType &value) override; + + Media::Plugin::Status + SetParameter(Media::Plugin::Tag tag, const Media::Plugin::ValueType &value) override; + + Media::Plugin::Status GetState(Media::Plugin::State &state) override; + + std::shared_ptr GetAllocator() override; + + Media::Plugin::Status SetCallback(const std::shared_ptr &cb) override; + + Media::Plugin::Status GetMute(bool &mute) override; + + Media::Plugin::Status SetMute(bool mute) override; + + Media::Plugin::Status GetVolume(float &volume) override; + + Media::Plugin::Status SetVolume(float volume) override; + + Media::Plugin::Status GetSpeed(float &speed) override; + + Media::Plugin::Status SetSpeed(float speed) override; + + Media::Plugin::Status Pause() override; + + Media::Plugin::Status Resume() override; + + Media::Plugin::Status GetLatency(uint64_t &ms) override; + + Media::Plugin::Status GetFrameSize(size_t &size) override; + + Media::Plugin::Status GetFrameCount(uint32_t &count) override; + + Media::Plugin::Status Write(const std::shared_ptr &input) override; + + Media::Plugin::Status Flush() override; + +private: + Media::Plugin::Status ReleaseRender(); + + void DoRender(); + +private: + const std::string adapterName_; + + std::atomic pluginState_ {OHOS::Media::Plugin::State::CREATED}; + + OHOS::Media::OSAL::Mutex renderMutex_ {}; + + AudioManager* audioManager_ {nullptr}; + AudioAdapterDescriptor adapterDescriptor_ {}; + AudioAdapter* audioAdapter_ {nullptr}; + AudioRender* audioRender_ {nullptr}; + AudioPort audioPort_ {}; + AudioDeviceDescriptor deviceDescriptor_ {}; + AudioSampleAttributes sampleAttributes_ {}; + AudioChannelMask channelMask_ {AUDIO_CHANNEL_MONO}; + + std::weak_ptr eventCallback_ {}; + + std::shared_ptr ringBuffer_ {}; + std::shared_ptr renderThread_ {}; +}; +} +} +} +#endif diff --git a/engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp b/engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp new file mode 100644 index 00000000..c69a82a4 --- /dev/null +++ b/engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "HdiRingBuffer" + +#include "ring_buffer.h" + +#include "foundation/log.h" +#include "foundation/memory_helper.h" +#include "plugin/common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace HosLitePlugin { +bool RingBuffer::Init() +{ + buffer_ = OHOS::Media::MemoryHelper::make_unique(bufferSize_); + return buffer_ != nullptr; +} +std::shared_ptr RingBuffer::ReadBufferWithoutAdvance(size_t readSize, size_t& outSize) +{ + OHOS::Media::OSAL::ScopedLock lck(writeMutex_); + std::shared_ptr ptr; + if (!isActive_) { + ptr = nullptr; + outSize = 0; + return ptr; + } + auto available = tail_ - head_; + available = (available > readSize) ? readSize : available; + size_t index = head_ % bufferSize_; + if (index + available < bufferSize_) { + ptr = std::shared_ptr(buffer_.get() + index, [](uint8_t* ptr) {}); // do not delete memory + outSize = available; + return ptr; + } + + ptr = std::shared_ptr(new (std::nothrow) uint8_t[available], std::default_delete()); + if (ptr == nullptr) { + outSize = 0; + } else { + outSize = available; + if (memcpy_s(ptr.get(), bufferSize_ - index, buffer_.get() + index, bufferSize_ - index) != EOK) { + MEDIA_LOG_E("memcpy_s failed when read buffer"); + outSize = 0; + } + if (available - (bufferSize_ - index) > 0 && memcpy_s(ptr.get() + (bufferSize_ - index), + available - (bufferSize_ - index), buffer_.get(), available - (bufferSize_ - index)) != EOK) { + MEDIA_LOG_E("memcpy_s failed when read buffer"); + outSize = 0; + } + } + return ptr; +} +void RingBuffer::Advance(size_t size) +{ + OHOS::Media::OSAL::ScopedLock lck(writeMutex_); + if (!isActive_) { + return; + } + head_ += size; + writeCondition_.NotifyAll(); +} +void RingBuffer::WriteBuffer(const std::shared_ptr& inputInfo) +{ + auto mem = inputInfo->GetMemory(); + size_t writeSize = mem->GetSize(); + auto ptr = mem->GetReadOnlyData(); + OHOS::Media::OSAL::ScopedLock lck(writeMutex_); + if (!isActive_) { + return; + } + while (writeSize + tail_ > head_ + bufferSize_) { + writeCondition_.Wait(lck); + if (!isActive_) { + return; + } + } + size_t index = tail_ % bufferSize_; + if (index + writeSize < bufferSize_) { + if (memcpy_s(buffer_.get() + index, writeSize, ptr, writeSize) != EOK) { + MEDIA_LOG_E("memcpy_s failed when write buffer"); + writeSize = 0; + } + tail_ += writeSize; + return; + } + if (memcpy_s(buffer_.get() + index, bufferSize_ - index, ptr, bufferSize_ - index) != EOK) { + MEDIA_LOG_E("memcpy_s failed when write buffer"); + return; + } + if (writeSize - (bufferSize_ - index) > 0 && memcpy_s(buffer_.get(), writeSize - (bufferSize_ - index), + ((uint8_t*)ptr) + bufferSize_ - index, writeSize - (bufferSize_ - index)) != EOK) { + MEDIA_LOG_E("memcpy_s failed when write buffer"); + return; + } + tail_ += writeSize; +} +void RingBuffer::SetActive(bool active) +{ + OHOS::Media::OSAL::ScopedLock lck(writeMutex_); + isActive_ = active; + if (!active) { + head_ = 0; + tail_ = 0; + writeCondition_.NotifyOne(); + } +} +void RingBuffer::Clear() +{ + OHOS::Media::OSAL::ScopedLock lck(writeMutex_); + head_ = 0; + tail_ = 0; + writeCondition_.NotifyOne(); +} +} +} +} \ No newline at end of file diff --git a/engine/plugin/plugins/hdi_adapter/sink/ring_buffer.h b/engine/plugin/plugins/hdi_adapter/sink/ring_buffer.h new file mode 100644 index 00000000..204868c7 --- /dev/null +++ b/engine/plugin/plugins/hdi_adapter/sink/ring_buffer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HDI_ADAPTER_RING_BUFFER_H +#define HISTREAMER_HDI_ADAPTER_RING_BUFFER_H + +#include +#include + +#include "foundation/osal/thread/condition_variable.h" +#include "foundation/osal/thread/mutex.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +class Buffer; +} +namespace HosLitePlugin { +class RingBuffer { +public: + explicit RingBuffer(size_t bufferSize) : bufferSize_(bufferSize) {} + + ~RingBuffer() = default; + + bool Init(); + + std::shared_ptr ReadBufferWithoutAdvance(size_t readSize, size_t& outSize); + + void Advance(size_t size); + + void WriteBuffer(const std::shared_ptr& inputInfo); + + void SetActive(bool active); + + void Clear(); + +private: + const size_t bufferSize_; + std::unique_ptr buffer_ {}; + size_t head_ {0}; // head + size_t tail_ {0}; // tail + OHOS::Media::OSAL::Mutex writeMutex_ {}; + OHOS::Media::OSAL::ConditionVariable writeCondition_ {}; + bool isActive_ {true}; +}; +} // namespace HosLitePlugin +} // namespace Media +} // namespace OHOS + +#endif // MEDIA_PIPELINE_RING_BUFFER_H diff --git a/engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp b/engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp new file mode 100644 index 00000000..65ac0c97 --- /dev/null +++ b/engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "HdiAuUtils" + +#include "hdi_au_utils.h" + +#include + +namespace OHOS { +namespace Media { +namespace HosLitePlugin { +namespace { +struct PluginHdiAudioFormatTable { + AudioFormat hFormat; + bool isSigned; + bool isInterleaved; + bool isBigEndian; + OHOS::Media::Plugin::AudioSampleFormat pFormat; +}; + +PluginHdiAudioFormatTable g_phft[] = { + {AUDIO_FORMAT_PCM_8_BIT, false, false, false, OHOS::Media::Plugin::AudioSampleFormat::U8}, + {AUDIO_FORMAT_PCM_8_BIT, false, true, false, OHOS::Media::Plugin::AudioSampleFormat::U8P}, + {AUDIO_FORMAT_PCM_8_BIT, true, false, false, OHOS::Media::Plugin::AudioSampleFormat::S8}, + {AUDIO_FORMAT_PCM_8_BIT, true, true, false, OHOS::Media::Plugin::AudioSampleFormat::S8P}, + {AUDIO_FORMAT_PCM_16_BIT, false, false, false, OHOS::Media::Plugin::AudioSampleFormat::U16}, + {AUDIO_FORMAT_PCM_16_BIT, false, true, false, OHOS::Media::Plugin::AudioSampleFormat::U16P}, + {AUDIO_FORMAT_PCM_16_BIT, true, false, false, OHOS::Media::Plugin::AudioSampleFormat::S16}, + {AUDIO_FORMAT_PCM_16_BIT, true, true, false, OHOS::Media::Plugin::AudioSampleFormat::S16P}, + {AUDIO_FORMAT_PCM_32_BIT, false, false, false, OHOS::Media::Plugin::AudioSampleFormat::U32}, + {AUDIO_FORMAT_PCM_32_BIT, false, true, false, OHOS::Media::Plugin::AudioSampleFormat::U32P}, + {AUDIO_FORMAT_PCM_32_BIT, true, false, false, OHOS::Media::Plugin::AudioSampleFormat::S32}, + {AUDIO_FORMAT_PCM_32_BIT, true, true, false, OHOS::Media::Plugin::AudioSampleFormat::S32P}, +}; + +std::pair g_phst[] = { + {0, AUDIO_SAMPLE_RATE_MASK_INVALID}, + {8000, AUDIO_SAMPLE_RATE_MASK_8000}, + {12000, AUDIO_SAMPLE_RATE_MASK_12000}, + {11025, AUDIO_SAMPLE_RATE_MASK_11025}, + {16000, AUDIO_SAMPLE_RATE_MASK_16000}, + {22050, AUDIO_SAMPLE_RATE_MASK_22050}, + {24000, AUDIO_SAMPLE_RATE_MASK_24000}, + {32000, AUDIO_SAMPLE_RATE_MASK_32000}, + {44100, AUDIO_SAMPLE_RATE_MASK_44100}, + {48000, AUDIO_SAMPLE_RATE_MASK_48000}, + {64000, AUDIO_SAMPLE_RATE_MASK_64000}, + {96000, AUDIO_SAMPLE_RATE_MASK_96000}, +}; + +std::pair g_phcmt[] = { + {OHOS::Media::Plugin::AudioChannelLayout::MONO, AUDIO_CHANNEL_MONO}, + {OHOS::Media::Plugin::AudioChannelLayout::STEREO, AUDIO_CHANNEL_STEREO} +}; +} // namespace + +bool PluginAuFormat2HdiAttrs(OHOS::Media::Plugin::AudioSampleFormat pFormat, AudioSampleAttributes& attrs) +{ + for (const auto& item : g_phft) { + if (item.pFormat == pFormat) { + attrs.format = item.hFormat; + attrs.isSignedData = item.isSigned; + attrs.isBigEndian = item.isBigEndian; + attrs.interleaved = item.isInterleaved; + return true; + } + } + return false; +} + +bool HdiAttrs2PluginAuFormat(AudioSampleAttributes attrs, OHOS::Media::Plugin::AudioSampleFormat& pFormat) +{ + for (const auto& item : g_phft) { + if (attrs.format == item.hFormat && attrs.isSignedData == item.isSigned && + attrs.interleaved == item.isInterleaved && attrs.isBigEndian == item.isBigEndian) { + pFormat = item.pFormat; + return true; + } + } + return false; +} + +bool PluginSampleRate2HdiRate(uint32_t pRate, AudioSampleRatesMask& mask) +{ + for (const auto& item : g_phst) { + if (item.first == pRate) { + mask = item.second; + return true; + } + } + mask = AUDIO_SAMPLE_RATE_MASK_INVALID; + return false; +} +bool HdiRate2PluginSampleRate(AudioSampleRatesMask mask, uint32_t& pRate) +{ + for (const auto& item : g_phst) { + if (item.second == mask) { + pRate = item.first; + return true; + } + } + return false; +} + +bool PluginChannelLayout2HdiMask(OHOS::Media::Plugin::AudioChannelLayout layout, AudioChannelMask& mask) +{ + for (const auto& item : g_phcmt) { + if (item.first == layout) { + mask = item.second; + return true; + } + } + return false; +} +bool HdiMask2PluginChannelLayout(AudioChannelMask mask, OHOS::Media::Plugin::AudioChannelLayout& layout) +{ + for (const auto& item : g_phcmt) { + if (item.second == mask) { + layout = item.first; + return true; + } + } + return false; +} +} // namespace HosLitePlugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.h b/engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.h new file mode 100644 index 00000000..8af99770 --- /dev/null +++ b/engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HDI_ADAPTER_AU_UTILS_H +#define HISTREAMER_HDI_ADAPTER_AU_UTILS_H + +#include "audio_types.h" +#include "plugin/common/plugin_audio_tags.h" + +namespace OHOS { +namespace Media { +namespace HosLitePlugin { +bool PluginAuFormat2HdiAttrs(OHOS::Media::Plugin::AudioSampleFormat pFormat, AudioSampleAttributes& attrs); +bool HdiAttrs2PluginAuFormat(AudioSampleAttributes attrs, OHOS::Media::Plugin::AudioSampleFormat& pFormat); + +bool PluginSampleRate2HdiRate(uint32_t pRate, AudioSampleRatesMask& mask); +bool HdiRate2PluginSampleRate(AudioSampleRatesMask mask, uint32_t& pRate); + +bool PluginChannelLayout2HdiMask(OHOS::Media::Plugin::AudioChannelLayout layout, AudioChannelMask& mask); +bool HdiMask2PluginChannelLayout(AudioChannelMask mask, OHOS::Media::Plugin::AudioChannelLayout& layout); + +inline int32_t GetPcmBytes(AudioFormat format) +{ + switch (format) { + case AUDIO_FORMAT_PCM_8_BIT: + return 1; + case AUDIO_FORMAT_PCM_16_BIT: + return 2; // 2 + case AUDIO_FORMAT_PCM_24_BIT: + return 3; // 3 + case AUDIO_FORMAT_PCM_32_BIT: + return 4; // 4 + default: + return 0; + } +} +} // namespace HosLitePlugin +} // namespace Media +} // namespace OHOS +#endif diff --git a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp new file mode 100644 index 00000000..d01e2e1a --- /dev/null +++ b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "SdlAudioSinkPlugin" + +#include "sdl_audio_sink_plugin.h" +#include +#include "foundation/constants.h" +#include "foundation/log.h" +#include "plugin/common/plugin_audio_tags.h" +#include "plugin/common/plugin_buffer.h" +#include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" + +namespace { +using namespace OHOS::Media::Plugin; + +constexpr int MAX_AUDIO_FRAME_SIZE = 192000; +std::function g_audioCallback; + +void RegisterAudioCallback(std::function callback) +{ + g_audioCallback = std::move(callback); +} +void SDLAudioCallback(void* userdata, uint8_t* stream, int len) +{ + g_audioCallback(userdata, stream, len); +} +AVSampleFormat TranslateFormat(AudioSampleFormat format) +{ + switch (format) { + case AudioSampleFormat::F32: + return AV_SAMPLE_FMT_FLT; + case AudioSampleFormat::F32P: + return AV_SAMPLE_FMT_FLTP; + case AudioSampleFormat::F64: + return AV_SAMPLE_FMT_DBL; + case AudioSampleFormat::F64P: + return AV_SAMPLE_FMT_DBLP; + case AudioSampleFormat::S32: + return AV_SAMPLE_FMT_S32; + case AudioSampleFormat::S32P: + return AV_SAMPLE_FMT_S32P; + case AudioSampleFormat::S16: + return AV_SAMPLE_FMT_S16; + case AudioSampleFormat::S16P: + return AV_SAMPLE_FMT_S16P; + default: + return AV_SAMPLE_FMT_NONE; + } +} + +bool IsPlanes(AudioSampleFormat format) +{ + switch (format) { + case AudioSampleFormat::F32P: + case AudioSampleFormat::F64P: + case AudioSampleFormat::S32P: + case AudioSampleFormat::S16P: + return true; + default: + return false; + } +} + +std::shared_ptr AudioSinkPluginCreator(const std::string& name) +{ + return std::make_shared(name); +} + +const Status SdlAudioRegister(const std::shared_ptr& reg) +{ + AudioSinkPluginDef definition; + definition.name = "sdl_audio_sink"; + definition.rank = 100; // 100 + definition.inCaps.emplace_back(Capability(OHOS::Media::MEDIA_MIME_AUDIO_RAW)); + definition.creator = AudioSinkPluginCreator; + return reg->AddPlugin(definition); +} + +PLUGIN_DEFINITION(SdlAudioSink, LicenseType::LGPL, SdlAudioRegister, [] {}); +} // namespace + +namespace OHOS { +namespace Media { +namespace Plugin { +SdlAudioSinkPlugin::SdlAudioSinkPlugin(std::string name) + : aliasName_(std::move(name)), + transformCache_((MAX_AUDIO_FRAME_SIZE * 3) / 2), // 3, 2 + mixCache_((MAX_AUDIO_FRAME_SIZE * 3) / 2) // 3, 2 +{ +} +Status SdlAudioSinkPlugin::Init() +{ + std::weak_ptr weakPtr(shared_from_this()); + RegisterAudioCallback([weakPtr](void* userdata, uint8_t* stream, int len) { + auto ptr = weakPtr.lock(); + if (ptr) { + ptr->AudioCallback(userdata, stream, len); + } + }); + return Status::OK; +} + +Status SdlAudioSinkPlugin::Deinit() +{ + return Status::OK; +} + +Status SdlAudioSinkPlugin::Prepare() +{ + AVSampleFormat outSampleFmt = AV_SAMPLE_FMT_S16; + uint64_t outChannelLayout = AV_CH_LAYOUT_STEREO; + int outChannels = av_get_channel_layout_nb_channels(outChannelLayout); + avFrameSize_ = av_samples_get_buffer_size(nullptr, outChannels, samplesPerFrame_, outSampleFmt, 1); + + rb = MemoryHelper::make_unique(avFrameSize_ * 10); // 最大缓存10帧 + rb->Init(); + + wantedSpec_.freq = sampleRate_; + wantedSpec_.format = AUDIO_S16SYS; + wantedSpec_.channels = outChannels; + wantedSpec_.samples = samplesPerFrame_; + wantedSpec_.silence = 0; + wantedSpec_.callback = SDLAudioCallback; + if (SDL_OpenAudio(&wantedSpec_, nullptr) < 0) { + MEDIA_LOG_E("sdl cannot open audio with error: %s", SDL_GetError()); + return Status::ERROR_UNKNOWN; + } + + SwrContext* swrContext = swr_alloc(); + if (swrContext == nullptr) { + MEDIA_LOG_E("cannot allocate swr context"); + return Status::ERROR_NO_MEMORY; + } + AVSampleFormat sampleFormat = TranslateFormat(audioFormat_); + MEDIA_LOG_I("configure swr with outChannelLayout 0x%x, outSampleFmt %d, outSampleRate %d inChannelLayout 0x%x, " + "inSampleFormat %d, inSampleRate %d", + outChannelLayout, outSampleFmt, sampleRate_, channelMask_, sampleFormat, sampleRate_); + swrContext = swr_alloc_set_opts(swrContext, outChannelLayout, outSampleFmt, sampleRate_, channelMask_, sampleFormat, + sampleRate_, 0, nullptr); + if (swr_init(swrContext) != 0) { + MEDIA_LOG_E("swr init error"); + return Status::ERROR_UNKNOWN; + } + swrCtx_ = std::shared_ptr(swrContext, [](SwrContext* ptr) { + if (ptr) { + swr_free(&ptr); + } + }); + return Status::OK; +} + +Status SdlAudioSinkPlugin::Reset() +{ + return Status::OK; +} + +Status SdlAudioSinkPlugin::Start() +{ + MEDIA_LOG_I("SDL SINK start..."); + SDL_PauseAudio(0); + rb->SetActive(true); + return Status::OK; +} + +Status SdlAudioSinkPlugin::Stop() +{ + SDL_PauseAudio(1); + Flush(); + SDL_CloseAudio(); + SDL_Quit(); + return Status::OK; +} + +bool SdlAudioSinkPlugin::IsParameterSupported(Tag tag) +{ + return false; +} + +Status SdlAudioSinkPlugin::GetParameter(Tag tag, ValueType& value) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::SetParameter(Tag tag, const ValueType& value) +{ +#define RETURN_ERROR_IF_CHECK_ERROR(typenames) \ + if (value.Type() != typeid(typenames)) { \ + return Status::ERROR_MISMATCHED_TYPE; \ + } + + switch (tag) { + case Tag::AUDIO_CHANNELS: { + RETURN_ERROR_IF_CHECK_ERROR(uint32_t); + channels_ = Plugin::AnyCast(value); + break; + } + case Tag::AUDIO_SAMPLE_RATE: { + RETURN_ERROR_IF_CHECK_ERROR(uint32_t); + sampleRate_ = Plugin::AnyCast(value); + break; + } + case Tag::AUDIO_SAMPLE_PRE_FRAME: { + RETURN_ERROR_IF_CHECK_ERROR(uint32_t); + samplesPerFrame_ = Plugin::AnyCast(value); + break; + } + case Tag::AUDIO_CHANNEL_LAYOUT: { + RETURN_ERROR_IF_CHECK_ERROR(AudioChannelLayout); + auto channelLayout = Plugin::AnyCast(value); + channelMask_ = ConvertChannelLayoutToFFmpeg(channelLayout); + break; + } + case Tag::AUDIO_SAMPLE_FORMAT: { + RETURN_ERROR_IF_CHECK_ERROR(AudioSampleFormat); + audioFormat_ = Plugin::AnyCast(value); + break; + } + default: + MEDIA_LOG_I("receive one parameter with unconcern key"); + break; + } + return Status::OK; +} + +Status SdlAudioSinkPlugin::GetState(State& state) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +std::shared_ptr SdlAudioSinkPlugin::GetAllocator() +{ + return nullptr; +} + +Status SdlAudioSinkPlugin::SetCallback(const std::shared_ptr& cb) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::GetMute(bool& mute) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::SetMute(bool mute) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::GetVolume(float& volume) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::SetVolume(float volume) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::GetSpeed(float& speed) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::SetSpeed(float speed) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::Pause() +{ + SDL_PauseAudio(1); + return Status::OK; +} + +Status SdlAudioSinkPlugin::Resume() +{ + SDL_PauseAudio(0); + return Status::OK; +} + +Status SdlAudioSinkPlugin::GetLatency(uint64_t& ms) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::GetFrameSize(size_t& size) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::GetFrameCount(uint32_t& count) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlAudioSinkPlugin::Write(const std::shared_ptr& inputInfo) +{ + MEDIA_LOG_D("SdlSink Write begin"); + if (inputInfo == nullptr || inputInfo->IsEmpty()) { + return Status::OK; + } + auto bufferData = inputInfo->GetMemory(); + auto dataPtr = transformCache_.data(); + std::vector input(channels_); + input[0] = static_cast(bufferData->GetReadOnlyData()); + if (IsPlanes(audioFormat_)) { + size_t planeSize = bufferData->GetSize() / channels_; + for (auto i = 1; i < channels_; ++i) { + input[i] = input[i - 1] + planeSize; + } + } + swr_convert(swrCtx_.get(), &dataPtr, MAX_AUDIO_FRAME_SIZE, (const uint8_t**)input.data(), samplesPerFrame_); + MEDIA_LOG_D("SdlSink Write before ring buffer"); + rb->WriteBuffer(transformCache_.data(), avFrameSize_); + MEDIA_LOG_D("SdlSink Write end"); + return Status::OK; +} + +Status SdlAudioSinkPlugin::Flush() +{ + SDL_ClearQueuedAudio(1); + return Status::OK; +} + +void SdlAudioSinkPlugin::AudioCallback(void* userdata, uint8_t* stream, int len) +{ + MEDIA_LOG_D("sdl audio callback begin"); + auto realLen = rb->ReadBuffer(mixCache_.data(), len); + if (realLen == 0) { + MEDIA_LOG_D("sdl audio callback end with 0"); + return; + } + SDL_memset(stream, 0, len); + SDL_MixAudio(stream, mixCache_.data(), realLen, SDL_MIX_MAXVOLUME); + SDL_PauseAudio(0); + MEDIA_LOG_D("sdl audio callback end with %zu", realLen); +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h new file mode 100644 index 00000000..21efef3f --- /dev/null +++ b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_SDL_AU_SINK_PLUGIN_H +#define HISTREAMER_SDL_AU_SINK_PLUGIN_H + +#include +#include "SDL.h" +#include "interface/audio_sink_plugin.h" +#include "plugin/common/plugin_audio_tags.h" +#include "plugin/plugins/sink/sdl/ring_buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libswresample/swresample.h" +#include "libswscale/swscale.h" +#ifdef __cplusplus +}; +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +class SdlAudioSinkPlugin : public std::enable_shared_from_this, public AudioSinkPlugin { +public: + explicit SdlAudioSinkPlugin(std::string name); + ~SdlAudioSinkPlugin() override = default; + + Status Init() override; + + Status Deinit() override; + + Status Prepare() override; + + Status Reset() override; + + Status Start() override; + + Status Stop() override; + + bool IsParameterSupported(Tag tag) override; + + Status GetParameter(Tag tag, ValueType &value) override; + + Status SetParameter(Tag tag, const ValueType &value) override; + + Status GetState(State &state) override; + + std::shared_ptr GetAllocator() override; + + Status SetCallback(const std::shared_ptr &cb) override; + + // audio sink + + Status GetMute(bool &mute) override; + + Status SetMute(bool mute) override; + + Status GetVolume(float &volume) override; + + Status SetVolume(float volume) override; + + Status GetSpeed(float &speed) override; + + Status SetSpeed(float speed) override; + + Status Pause() override; + + Status Resume() override; + + Status GetLatency(uint64_t &ms) override; + + Status GetFrameSize(size_t &size) override; + + Status GetFrameCount(uint32_t &count) override; + + Status Write(const std::shared_ptr &input) override; + + Status Flush() override; + +private: + void AudioCallback(void* userdata, uint8_t* stream, int len); + + std::string aliasName_ {}; + std::vector transformCache_ {}; + std::vector mixCache_ {}; + std::unique_ptr rb {}; + size_t avFrameSize_ {}; + SDL_AudioSpec wantedSpec_ {}; + SDL_AudioDeviceID deviceId_ {}; + int32_t channels_ {-1}; + int32_t sampleRate_ {-1}; + int32_t samplesPerFrame_ {-1}; + int64_t channelMask_ {0}; + AudioSampleFormat audioFormat_ {AudioSampleFormat::U8}; + std::shared_ptr swrCtx_ {nullptr}; +}; +} +} +} +#endif // MEDIA_PIPELINE_SDL_AU_SINK_PLUGIN_H diff --git a/engine/plugin/plugins/sink/sdl/ring_buffer.h b/engine/plugin/plugins/sink/sdl/ring_buffer.h new file mode 100644 index 00000000..40c7644a --- /dev/null +++ b/engine/plugin/plugins/sink/sdl/ring_buffer.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_RING_BUFFER_H +#define HISTREAMER_RING_BUFFER_H + +#include +#include +#include +#include "foundation/memory_helper.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +class RingBuffer { +public: + explicit RingBuffer(size_t bufferSize) : bufferSize_(bufferSize) + { + } + + ~RingBuffer() = default; + + bool Init() + { + buffer_ = MemoryHelper::make_unique(bufferSize_); + return buffer_ != nullptr; + } + + size_t ReadBuffer(void* ptr, size_t readSize) + { + std::unique_lock lck(writeMutex_); + if (!isActive_) { + return 0; + } + auto available = tail_ - head_; + available = (available > readSize) ? readSize : available; + size_t index = head_ % bufferSize_; + if (index + available < bufferSize_) { + (void)memcpy_s(ptr, available, buffer_.get() + index, available); + } else { + (void)memcpy_s(ptr, bufferSize_ - index, buffer_.get() + index, bufferSize_ - index); + (void)memcpy_s(((uint8_t*)ptr) + (bufferSize_ - index), available - (bufferSize_ - index), buffer_.get(), + available - (bufferSize_ - index)); + } + head_ += available; + writeCondition_.notify_one(); + return available; + } + + void WriteBuffer(void* ptr, size_t writeSize) + { + std::unique_lock lck(writeMutex_); + while (writeSize + tail_ > head_ + bufferSize_) { + writeCondition_.wait(lck); + if (!isActive_) { + return; + } + } + size_t index = tail_ % bufferSize_; + if (index + writeSize < bufferSize_) { + (void)memcpy_s(buffer_.get() + index, writeSize, ptr, writeSize); + } else { + (void)memcpy_s(buffer_.get() + index, bufferSize_ - index, ptr, bufferSize_ - index); + (void)memcpy_s(buffer_.get(), writeSize - (bufferSize_ - index), ((uint8_t*)ptr) + bufferSize_ - index, + writeSize - (bufferSize_ - index)); + } + tail_ += writeSize; + } + + void SetActive(bool active) + { + std::unique_lock lck(writeMutex_); + isActive_ = active; + if (!active) { + head_ = 0; + tail_ = 0; + writeCondition_.notify_one(); + } + } + +private: + const size_t bufferSize_; + std::unique_ptr buffer_; + size_t head_ {0}; // head + size_t tail_ {0}; // tail + std::mutex writeMutex_ {}; + std::condition_variable writeCondition_ {}; + bool isActive_ {true}; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS + +#endif // MEDIA_PIPELINE_RING_BUFFER_H diff --git a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp new file mode 100644 index 00000000..98cb4a8a --- /dev/null +++ b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#define LOG_TAG "SdlVideoSinkPlugin" + +#include "sdl_video_sink_plugin.h" +#include +#include "SDL_events.h" +#include "foundation/log.h" +#include "foundation/constants.h" +#include "plugin/common/plugin_buffer.h" +#include "plugin/common/plugin_video_tags.h" +#include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" + +namespace { +using namespace OHOS::Media::Plugin; + +std::shared_ptr VideoSinkPluginCreator(const std::string& name) +{ + return std::make_shared(name); +} + +const Status SdlVideoRegister(const std::shared_ptr& reg) +{ + VideoSinkPluginDef definition; + definition.name = "sdl_video_sink"; + definition.rank = 100; // 100 + definition.inCaps.emplace_back(Capability(OHOS::Media::MEDIA_MIME_VIDEO_RAW)); + definition.creator = VideoSinkPluginCreator; + return reg->AddPlugin(definition); +} + +PLUGIN_DEFINITION(SdlVideoSink, LicenseType::LGPL, SdlVideoRegister, [] {}); +} // namespace + +namespace OHOS { +namespace Media { +namespace Plugin { +const uint32_t DEFAULT_WINDOW_WIDTH = 640; +const uint32_t DEFAULT_WINDOW_HEIGHT = 480; + +static uint32_t TranslatePixelFormat(const VideoPixelFormat pixelFormat) +{ + uint32_t sdlFormat = SDL_PIXELFORMAT_UNKNOWN; + switch (pixelFormat) { + case VideoPixelFormat::YUV420P: + sdlFormat = SDL_PIXELFORMAT_IYUV; + break; + case VideoPixelFormat::YUYV422: + sdlFormat = SDL_PIXELFORMAT_YUY2; + break; + case VideoPixelFormat::RGB24: + sdlFormat = SDL_PIXELFORMAT_RGB24; + break; + case VideoPixelFormat::BGR24: + sdlFormat = SDL_PIXELFORMAT_BGR24; + break; + case VideoPixelFormat::YUV422P: + case VideoPixelFormat::YUV444P: + case VideoPixelFormat::YUV410P: + case VideoPixelFormat::YUV411P: + case VideoPixelFormat::GRAY8: + case VideoPixelFormat::MONOWHITE: + case VideoPixelFormat::MONOBLACK: + case VideoPixelFormat::PAL8: + case VideoPixelFormat::YUVJ420P: + case VideoPixelFormat::YUVJ422P: + case VideoPixelFormat::YUVJ444P: + break; + case VideoPixelFormat::NV12: + sdlFormat = SDL_PIXELFORMAT_NV12; + break; + case VideoPixelFormat::NV21: + sdlFormat = SDL_PIXELFORMAT_NV21; + break; + default: + break; + } + return sdlFormat; +} + +#define ALIGN_UP_16(value) (((value) + 15) & (~15)) + +SdlVideoSinkPlugin::SdlVideoSinkPlugin(std::string name) + : windowWidth_(DEFAULT_WINDOW_WIDTH), windowHeight_(DEFAULT_WINDOW_HEIGHT) +{ +} + +Status SdlVideoSinkPlugin::Init() +{ + std::weak_ptr weakPtr(shared_from_this()); + if (SDL_Init(SDL_INIT_VIDEO)) { + MEDIA_LOG_E("Init SDL fail: %s", SDL_GetError()); + return Status::ERROR_UNKNOWN; + } + SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE); + SDL_EventState(SDL_USEREVENT, SDL_IGNORE); +#ifdef DUMP_RAW_DATA + dumpData_.open("./vsink_out.dat", std::ios::out | std::ios::binary); +#endif + return Status::OK; +} + +Status SdlVideoSinkPlugin::Deinit() +{ + SDL_Quit(); +#ifdef DUMP_RAW_DATA + dumpData_.close(); +#endif + return Status::OK; +} + +void SdlVideoSinkPlugin::UpdateTextureRect() +{ + /* we suppose the screen has a 1/1 sample aspect ratio */ + if (windowWidth_ && windowHeight_) { + /* fit in the window */ + double_t pixelRational = static_cast(pixelWidth_) / static_cast(pixelHeight_); + double_t winRational = static_cast(windowWidth_) / static_cast(windowHeight_); + if (pixelRational > winRational) { + /* fit in width */ + textureRect_.w = static_cast(windowWidth_); + textureRect_.h = static_cast( + static_cast(static_cast(textureRect_.w) * static_cast(pixelHeight_)) / + static_cast(pixelWidth_)); + } else { + /* fit in height */ + textureRect_.h = static_cast(windowHeight_); + textureRect_.w = static_cast( + static_cast(static_cast(textureRect_.h) * static_cast(pixelWidth_)) / + static_cast(pixelHeight_)); + } + } else { + textureRect_.h = static_cast(pixelHeight_); + textureRect_.w = static_cast( + static_cast(static_cast(textureRect_.h) * static_cast(pixelWidth_)) / + static_cast(pixelHeight_)); + windowWidth_ = textureRect_.w; + windowHeight_ = textureRect_.h; + } + + textureRect_.x = (static_cast(windowWidth_) - textureRect_.w) / 2; // 2 + textureRect_.y = (static_cast(windowHeight_) - textureRect_.h) / 2; // 2 + MEDIA_LOG_D("pixelWH[%u, %u], windowWH[%u, %u], textureWH[%u, %u], textureXY[%u, %u]", pixelWidth_, pixelHeight_, + windowWidth_, windowHeight_, textureRect_.w, textureRect_.h, textureRect_.x, textureRect_.y); +} + +Status SdlVideoSinkPlugin::CreateSdlDispContext() +{ + UpdateTextureRect(); + uint32_t sdlFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + // also can call SDL_CreateWindowAndRenderer() to combine SDL_CreateWindow() and SDL_CreateRenderer() + // can use width_ and height_ to set screen_x and screen_y + SDL_Window* screen = + SDL_CreateWindow("SDL2 Video Sink", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + static_cast(windowWidth_), static_cast(windowHeight_), sdlFlags); + if (screen == nullptr) { + MEDIA_LOG_E("Create window fail: %s", SDL_GetError()); + return Status::ERROR_UNKNOWN; + } + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + window_ = std::shared_ptr(screen, [](SDL_Window* ptr) { + if (ptr) { + SDL_DestroyWindow(ptr); + } + }); + SDL_Renderer* renderer = SDL_CreateRenderer( + window_.get(), -1, + SDL_RENDERER_ACCELERATED | + SDL_RENDERER_PRESENTVSYNC); // flags: SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC + if (renderer != nullptr) { + if (!SDL_GetRendererInfo(renderer, &rendererInfo_)) { + MEDIA_LOG_I("Init %s renderer success", rendererInfo_.name); + } + } + if ((renderer == nullptr) || (!rendererInfo_.num_texture_formats)) { + MEDIA_LOG_E("Create renderer fail: %s", SDL_GetError()); + return Status::ERROR_UNKNOWN; + } + renderer_ = std::shared_ptr(renderer, [](SDL_Renderer* ptr) { + if (ptr) { + SDL_DestroyRenderer(ptr); + } + }); + return Status::OK; +} + +Status SdlVideoSinkPlugin::CreateSdlDispTexture() +{ + SDL_Texture* texture = + SDL_CreateTexture(renderer_.get(), pixelFormat_, SDL_TEXTUREACCESS_STREAMING, pixelWidth_, pixelHeight_); + if (texture == nullptr) { + MEDIA_LOG_E("Create texture fail: %s", SDL_GetError()); + return Status::ERROR_UNKNOWN; + } + texture_ = std::shared_ptr(texture, [](SDL_Texture* ptr) { + if (ptr) { + SDL_DestroyTexture(ptr); + } + }); + return Status::OK; +} + +Status SdlVideoSinkPlugin::Prepare() +{ + auto err = CreateSdlDispContext(); + if (err != Status::OK) { + return err; + } + err = CreateSdlDispTexture(); + if (err != Status::OK) { + return err; + } + return Status::OK; +} + +Status SdlVideoSinkPlugin::Reset() +{ + window_ = nullptr; + renderer_ = nullptr; + texture_ = nullptr; + SDL_memset(static_cast(&rendererInfo_), sizeof(SDL_RendererInfo), 0); +#ifdef DUMP_RAW_DATA + dumpData_.close(); +#endif + return Status::OK; +} + +Status SdlVideoSinkPlugin::Start() +{ + MEDIA_LOG_I("SDL video sink start ..."); + return Status::OK; +} + +Status SdlVideoSinkPlugin::Stop() +{ + MEDIA_LOG_I("SDL video sink stop ..."); + return Status::OK; +} + +bool SdlVideoSinkPlugin::IsParameterSupported(Tag tag) +{ + return false; +} + +Status SdlVideoSinkPlugin::GetParameter(Tag tag, ValueType& value) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlVideoSinkPlugin::SetParameter(Tag tag, const ValueType& value) +{ +#define RETURN_ERROR_IF_CHECK_ERROR(typenames) \ + if (value.Type() != typeid(typenames)) { \ + return Status::ERROR_INVALID_PARAMETER; \ + return Status::ERROR_INVALID_PARAMETER; \ + } + + switch (tag) { + case Tag::VIDEO_WIDTH: { + RETURN_ERROR_IF_CHECK_ERROR(uint32_t); + pixelWidth_ = Plugin::AnyCast(value); + MEDIA_LOG_D("pixelWidth_: %u", pixelWidth_); + break; + } + case Tag::VIDEO_HEIGHT: { + RETURN_ERROR_IF_CHECK_ERROR(uint32_t); + pixelHeight_ = Plugin::AnyCast(value); + MEDIA_LOG_D("pixelHeight_: %u", pixelHeight_); + break; + } + case Tag::VIDEO_PIXEL_FORMAT: { + RETURN_ERROR_IF_CHECK_ERROR(uint32_t); + uint32_t format = Plugin::AnyCast(value); + pixelFormat_ = TranslatePixelFormat(static_cast(format)); + MEDIA_LOG_D("SDL pixelFormat: %u", pixelFormat_); + break; + } + default: + MEDIA_LOG_I("receive one parameter with unconcern key"); + break; + } + return Status::OK; +} + +Status SdlVideoSinkPlugin::GetState(State& state) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +std::shared_ptr SdlVideoSinkPlugin::GetAllocator() +{ + return nullptr; +} + +Status SdlVideoSinkPlugin::SetCallback(const std::shared_ptr& cb) +{ + return Status::ERROR_ALREADY_EXISTS; +} + +Status SdlVideoSinkPlugin::Pause() +{ + return Status::OK; +} + +Status SdlVideoSinkPlugin::Resume() +{ + return Status::OK; +} + +bool SdlVideoSinkPlugin::IsFormatYUV() +{ + return pixelFormat_ == SDL_PIXELFORMAT_IYUV; +} + +bool SdlVideoSinkPlugin::IsFormatNV() +{ + return pixelFormat_ == SDL_PIXELFORMAT_NV12 || pixelFormat_ == SDL_PIXELFORMAT_NV21; +} + +bool SdlVideoSinkPlugin::IsFormatRGB() +{ + return pixelFormat_ == SDL_PIXELFORMAT_RGB332 || pixelFormat_ == SDL_PIXELFORMAT_RGB444 || + pixelFormat_ == SDL_PIXELFORMAT_RGB555 || pixelFormat_ == SDL_PIXELFORMAT_BGR555 || + pixelFormat_ == SDL_PIXELFORMAT_RGB565 || pixelFormat_ == SDL_PIXELFORMAT_BGR565 || + pixelFormat_ == SDL_PIXELFORMAT_RGB24 || pixelFormat_ == SDL_PIXELFORMAT_BGR24 || + pixelFormat_ == SDL_PIXELFORMAT_RGB888 || pixelFormat_ == SDL_PIXELFORMAT_RGBX8888 || + pixelFormat_ == SDL_PIXELFORMAT_BGR888 || pixelFormat_ == SDL_PIXELFORMAT_BGRX8888 || + pixelFormat_ == SDL_PIXELFORMAT_ARGB8888 || pixelFormat_ == SDL_PIXELFORMAT_RGBA8888 || + pixelFormat_ == SDL_PIXELFORMAT_ABGR8888 || pixelFormat_ == SDL_PIXELFORMAT_BGRA8888; +} + +Status SdlVideoSinkPlugin::VideoImageDisaplay(const std::shared_ptr& inputInfo) +{ + int32_t ret = -1; + const uint8_t* data[4] = {nullptr}; + int32_t lineSize[4] = {0}; + auto bufferMeta = inputInfo->GetBufferMeta(); + if (bufferMeta == nullptr || bufferMeta->GetType() != BufferMetaType::VIDEO) { + MEDIA_LOG_E("Invalid video buffer"); + return Status::ERROR_INVALID_DATA; + } + std::shared_ptr videoMeta = std::dynamic_pointer_cast(bufferMeta); + uint32_t frameFormat = TranslatePixelFormat(videoMeta->videoPixelFormat); + if (frameFormat != pixelFormat_) { + MEDIA_LOG_I("pixel format change from %u to %u", pixelFormat_, frameFormat); + pixelFormat_ = frameFormat; + } + if ((videoMeta->width != pixelWidth_) || (videoMeta->height != pixelHeight_)) { + MEDIA_LOG_E("WH[%u,%u] change to WH[%u,%u]", pixelWidth_, pixelHeight_, videoMeta->width, videoMeta->height); + // do something + } + int32_t ySize, uvSize; + auto bufferMem = inputInfo->GetMemory(); + auto ptr = bufferMem->GetReadOnlyData(); + data[0] = ptr; + lineSize[0] = static_cast(videoMeta->stride[0]); + MEDIA_LOG_D("Display one frame: WHS[%u,%u,%u]", pixelWidth_, pixelHeight_, lineSize[0]); + if (IsFormatYUV()) { + if (videoMeta->planes != 3) { // 3 + MEDIA_LOG_E("Invalid video buffer, planes: %u", videoMeta->planes); + return Status::ERROR_INVALID_DATA; + } + ret = UpdateYUVTexture(data, lineSize, videoMeta, ySize, uvSize); + } else if (IsFormatNV()) { + if (videoMeta->planes != 2) { // 2 + MEDIA_LOG_E("Invalid video buffer, planes: %u", videoMeta->planes); + return Status::ERROR_INVALID_DATA; + } + ret = UpdateNVTexture(data, lineSize, videoMeta, ySize, ptr); + } else if (IsFormatRGB()) { + ret = SDL_UpdateTexture(texture_.get(), NULL, data[0], lineSize[0]); + } else { + MEDIA_LOG_E("Unsupported pixel format"); + } + SDL_RenderClear(renderer_.get()); + SDL_RenderCopy(renderer_.get(), texture_.get(), NULL, &textureRect_); + SDL_RenderPresent(renderer_.get()); + SDL_Delay(40); // 40ms + return (ret != 0) ? Status::ERROR_UNKNOWN : Status::OK; +} + +int32_t SdlVideoSinkPlugin::UpdateNVTexture(const uint8_t** data, int32_t* lineSize, + const std::shared_ptr& videoMeta, int32_t ySize, + const uint8_t* ptr) const +{ + int32_t ret; + lineSize[1] = static_cast(videoMeta->stride[1]); + ySize = lineSize[0] * static_cast(ALIGN_UP_16(pixelHeight_)); + MEDIA_LOG_D("lineSize[0]: %d, lineSize[1]: %d, ySize: %d", lineSize[0], lineSize[1], ySize); + data[1] = ptr + ySize; +#ifdef DUMP_RAW_DATA + if (data[0] != nullptr && lineSize[0] != 0) { + dumpData_.write((char*)data[0], lineSize[0] * pixelHeight_); + } + if (data[1] != nullptr && lineSize[1] != 0) { + dumpData_.write((char*)data[1], lineSize[1] * pixelHeight_ / 2); // 2 + } +#endif + ret = SDL_UpdateTexture(texture_.get(), NULL, data[0], lineSize[0]); + return ret; +} + +int32_t SdlVideoSinkPlugin::UpdateYUVTexture(const uint8_t** data, int32_t* lineSize, + const std::shared_ptr& videoMeta, int32_t ySize, + int32_t uvSize) const +{ + int32_t ret; + lineSize[1] = static_cast(videoMeta->stride[1]); + lineSize[2] = static_cast(videoMeta->stride[2]); // 2 + ySize = lineSize[0] * static_cast(ALIGN_UP_16(pixelHeight_)); + uvSize = lineSize[1] * static_cast(ALIGN_UP_16(pixelHeight_)) / 2; // 2 + data[1] = data[0] + ySize; + data[2] = data[1] + uvSize; // 2 +#ifdef DUMP_RAW_DATA + if (data[0] != nullptr && lineSize[0] != 0) { + dumpData_.write((char*)data[0], lineSize[0] * pixelHeight_); + } + if (data[1] != nullptr && lineSize[1] != 0) { + dumpData_.write((char*)data[1], lineSize[1] * pixelHeight_ / 2); // 2 + } + if (data[2] != nullptr && lineSize[2] != 0) { // 2 + dumpData_.write((char*)data[2], lineSize[2] * pixelHeight_ / 2); // 2 + } +#endif + ret = SDL_UpdateYUVTexture(texture_.get(), NULL, data[0], lineSize[0], data[1], lineSize[1], data[2], // 2 + lineSize[2]); // 2 + return ret; +} + +bool SdlVideoSinkPlugin::HandleSdlEvent() +{ + SDL_Event event; + bool isQuit = false; + if (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + isQuit = true; + break; + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + windowWidth_ = event.window.data1; + windowHeight_ = event.window.data2; + UpdateTextureRect(); + break; + default: + break; + } + break; + default: + break; + } + } + return isQuit; +} + +Status SdlVideoSinkPlugin::Write(const std::shared_ptr& inputInfo) +{ + MEDIA_LOG_D("SDL sink write begin"); + if (inputInfo == nullptr || inputInfo->IsEmpty()) { + return Status::OK; + } + if (HandleSdlEvent() == true) { + MEDIA_LOG_W("SDL_QUIT, write nothing"); + return Status::ERROR_NOT_ENOUGH_DATA; + } + return VideoImageDisaplay(inputInfo); +} + +Status SdlVideoSinkPlugin::Flush() +{ + return Status::OK; +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h new file mode 100644 index 00000000..129a275c --- /dev/null +++ b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifdef VIDEO_SUPPORT + +#ifndef HISTREAMER_SDL_VIDEO_SINK_PLUGIN_H +#define HISTREAMER_SDL_VIDEO_SINK_PLUGIN_H + +#include +#include +#include "SDL.h" +#include "interface/video_sink_plugin.h" +#include "plugin/common/plugin_video_tags.h" + +#ifdef DUMP_RAW_DATA +#include +#endif + +namespace OHOS { +namespace Media { +namespace Plugin { +class SdlVideoSinkPlugin : public VideoSinkPlugin, public std::enable_shared_from_this { +public: + explicit SdlVideoSinkPlugin(std::string name); + ~SdlVideoSinkPlugin() override = default; + + Status Init() override; + + Status Deinit() override; + + Status Prepare() override; + + Status Reset() override; + + Status Start() override; + + Status Stop() override; + + bool IsParameterSupported(Tag tag) override; + + Status GetParameter(Tag tag, ValueType &value) override; + + Status SetParameter(Tag tag, const ValueType &value) override; + + Status GetState(State &state) override; + + std::shared_ptr GetAllocator() override; + + Status SetCallback(const std::shared_ptr &cb) override; + + Status Pause() override; + + Status Resume() override; + + Status Write(const std::shared_ptr &input) override; + + Status Flush() override; + +private: + + uint32_t windowWidth_; + uint32_t windowHeight_; + uint32_t pixelWidth_; + uint32_t pixelHeight_; + uint32_t pixelFormat_; + + std::shared_ptr window_ {nullptr}; + std::shared_ptr renderer_ {nullptr}; + SDL_RendererInfo rendererInfo_ = {0}; + std::shared_ptr texture_ {nullptr}; + SDL_Rect textureRect_ = {0}; + +#ifdef DUMP_RAW_DATA + std::ofstream dumpData_; +#endif + + void UpdateTextureRect(); + Status CreateSdlDispTexture(); + Status CreateSdlDispContext(); + bool IsFormatYUV(); + bool IsFormatNV(); + bool IsFormatRGB(); + Status VideoImageDisaplay(const std::shared_ptr &inputInfo); + bool HandleSdlEvent(); + + int32_t UpdateYUVTexture(const uint8_t **data, int32_t *lineSize, const std::shared_ptr &videoMeta, + int32_t ySize, int32_t uvSize) const; + + int32_t UpdateNVTexture(const uint8_t **data, int32_t *lineSize, const std::shared_ptr &videoMeta, + int32_t ySize, const uint8_t *ptr) const; +}; +} +} +} + +#endif // MEDIA_PIPELINE_SDL_VIDEO_SINK_PLUGIN_H + +#endif diff --git a/engine/plugin/plugins/source/file_source/CMakeLists.txt b/engine/plugin/plugins/source/file_source/CMakeLists.txt new file mode 100644 index 00000000..866e1186 --- /dev/null +++ b/engine/plugin/plugins/source/file_source/CMakeLists.txt @@ -0,0 +1,20 @@ + +CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) + +project(plugin_filesource) + +include_directories( + ${TOP_DIR}/engine + ${TOP_DIR}/engine/plugin +) + +set(BUILD_PLUGIN_FILE_SOURCE_SHARED ON) + +if (BUILD_PLUGIN_FILE_SOURCE_SHARED) + add_library(plugin_filesource SHARED ./file_source_plugin.cpp ../../../common/plugin_buffer.cpp) +else() + add_library(plugin_filesource STATIC ./file_source_plugin.cpp ../../../common/plugin_buffer.cpp) +endif() + +message("------------------ BUILD plugin_filesource ------------------") +install(TARGETS plugin_filesource DESTINATION "${CMAKE_BINARY_DIR}/bin/plugins/") diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp new file mode 100644 index 00000000..6dfea7af --- /dev/null +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "FileSourcePlugin" + +#include "foundation/log.h" + +#include "plugin/common/plugin_types.h" +#include "plugin/core/plugin_manager.h" +#include "file_source_plugin.h" +#include "plugin/common/plugin_buffer.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +std::shared_ptr FileSourcePluginCreater(const std::string &name) +{ + return std::make_shared(name); +} + +const Status FileSourceRegister(const std::shared_ptr& reg) +{ + SourcePluginDef definition; + definition.name = "FileSource"; + definition.description = "File source"; + definition.rank = 100; // 100: max rank + definition.protocol = "file"; + definition.creator = FileSourcePluginCreater; + return reg->AddPlugin(definition); +} + +PLUGIN_DEFINITION(FileSource, LicenseType::APACHE_V2, FileSourceRegister, []{}); + +void* FileSourceAllocator::Alloc(size_t size) +{ + if (size == 0) { + return nullptr; + } + return reinterpret_cast(new (std::nothrow) uint8_t[size]); +} + +void FileSourceAllocator::Free(void* ptr) +{ + if (ptr != nullptr) { + delete[] (uint8_t *)ptr; + } +} + +FileSourcePlugin::FileSourcePlugin(const std::string &name) + : name_(name), state_(State::CREATED), fileSize_(0), isSeekable_(true), position_(0) +{ + MEDIA_LOG_D("IN"); + state_ = State::CREATED; +} + +FileSourcePlugin::~FileSourcePlugin() +{ + MEDIA_LOG_D("IN"); + state_ = State::DESTROYED; +} + +Status FileSourcePlugin::Init() +{ + MEDIA_LOG_D("IN"); + mAllocator_ = std::make_shared(); + state_ = State::INITIALIZED; + return Status::OK; +} + +Status FileSourcePlugin::Deinit() +{ + MEDIA_LOG_D("IN"); + CloseFile(); + state_ = State::DESTROYED; + return Status::OK; +} + +Status FileSourcePlugin::Prepare() +{ + MEDIA_LOG_D("IN"); + state_ = State::PREPARED; + return Status::OK; +} + +Status FileSourcePlugin::Reset() +{ + MEDIA_LOG_D("IN"); + CloseFile(); + state_ = State::INITIALIZED; + return Status::OK; +} + +Status FileSourcePlugin::Start() +{ + MEDIA_LOG_D("IN"); + state_ = State::RUNNING; + return Status::OK; +} + +Status FileSourcePlugin::Stop() +{ + MEDIA_LOG_D("IN"); + state_ = State::PREPARED; + return Status::OK; +} + +bool FileSourcePlugin::IsParameterSupported(Tag tag) +{ + MEDIA_LOG_D("IN"); + return true; +} + +Status FileSourcePlugin::GetParameter(Tag tag, ValueType &value) +{ + MEDIA_LOG_D("IN"); + return Status::OK; +} + +Status FileSourcePlugin::SetParameter(Tag tag, const ValueType &value) +{ + MEDIA_LOG_D("IN"); + return Status::OK; +} + +Status FileSourcePlugin::GetState(State &state) +{ + MEDIA_LOG_D("IN"); + state = state_; + return Status::OK; +} + +std::shared_ptr FileSourcePlugin::GetAllocator() +{ + MEDIA_LOG_D("IN"); + return mAllocator_; +} + +Status FileSourcePlugin::SetCallback(const std::shared_ptr &cb) +{ + MEDIA_LOG_D("IN"); + return Status::OK; +} + +Status FileSourcePlugin::SetSource(std::string& uri, std::shared_ptr> params) +{ + MEDIA_LOG_D("IN"); + if (state_ != State::INITIALIZED) { + MEDIA_LOG_W("Wrong state: %d", state_); + return Status::ERROR_WRONG_STATE; + } + auto err = ParseFileName(uri); + if (err != Status::OK) { + MEDIA_LOG_E("Parse file name from uri fail, uri: %s", uri.c_str()); + return err; + } + return OpenFile(); +} + +Status FileSourcePlugin::Read(std::shared_ptr &buffer, size_t expectedLen) +{ + if (fin_.eof() == true) { + MEDIA_LOG_W("It is the end of file!"); + return Status::END_OF_STREAM; + } + + std::shared_ptr bufData; + + // There is no buffer, so alloc it + if (buffer->IsEmpty()) { + bufData = buffer->AllocMemory(GetAllocator(), expectedLen); + } else { + bufData = buffer->GetMemory(); + } + expectedLen = std::min(static_cast(fileSize_ - position_), expectedLen); + expectedLen = std::min(bufData->GetCapacity(), expectedLen); + + auto ptr = bufData->GetWritableData(expectedLen); + size_t offset = 0; + MEDIA_LOG_I("buffer addr %p offset %zu", ptr, offset); + fin_.read((char *)(ptr + offset), expectedLen); + bufData->GetWritableData(fin_.gcount()); + position_ += bufData->GetSize(); + MEDIA_LOG_D("position_: %" PRIu64 ", readSize: %zu", position_, bufData->GetSize()); + return Status::OK; +} + +Status FileSourcePlugin::GetSize(size_t& size) +{ + MEDIA_LOG_D("IN"); + if (!fin_.is_open()) { + MEDIA_LOG_E("Need call SetSource() to open file first"); + return Status::ERROR_WRONG_STATE; + } + size = fileSize_; + MEDIA_LOG_D("fileSize_: %zu", size); + return Status::OK; +} + +bool FileSourcePlugin::IsSeekable() +{ + MEDIA_LOG_D("IN"); + return isSeekable_; +} + +Status FileSourcePlugin::SeekTo(uint64_t offset) +{ + if (!fin_.is_open() || (offset > fileSize_) || (position_ == offset)) { + MEDIA_LOG_E("Invalid operation"); + return Status::ERROR_WRONG_STATE; + } + fin_.clear(); + fin_.seekg(offset, std::ios::beg); + if (!fin_.good() || (fin_.tellg() != offset)) { + fin_.clear(); + fin_.seekg(position_, std::ios::beg); + MEDIA_LOG_E("Seek to %" PRIu64, offset); + return Status::ERROR_UNKNOWN; + } + position_ = offset; + if (fin_.eof()) { + MEDIA_LOG_I("It is the end of file!"); + } + MEDIA_LOG_D("seek to position_: %" PRIu64 " success", position_); + return Status::OK; +} + +Status FileSourcePlugin::ParseFileName(std::string& uri) +{ + if (uri.empty()) { + MEDIA_LOG_E("uri is empty"); + return Status::ERROR_INVALID_DATA; + } + MEDIA_LOG_D("uri: %s", uri.c_str()); + if (uri.find("file:/") != std::string::npos) { + if (uri.find('#') != std::string::npos) { + MEDIA_LOG_E("Invalid file uri format: %s", uri.c_str()); + return Status::ERROR_INVALID_DATA; + } + auto pos = uri.find("file:"); + if (pos == std::string::npos) { + MEDIA_LOG_E("Invalid file uri format: %s", uri.c_str()); + return Status::ERROR_INVALID_DATA; + } + pos += 5; // 5: offset + if (uri.find("///", pos) != std::string::npos) { + pos += 3; // 3: offset + } else if (uri.find("//", pos) != std::string::npos) { + pos += 2; // 2: offset + pos = uri.find('/', pos); // skip host name + if (pos == std::string::npos) { + MEDIA_LOG_E("Invalid file uri format: %s", uri.c_str()); + return Status::ERROR_INVALID_DATA; + } + pos++; + } + fileName_ = uri.substr(pos); + } else { + fileName_ = uri; + } + MEDIA_LOG_I("fileName_: %s", fileName_.c_str()); + return Status::OK; +} + +Status FileSourcePlugin::OpenFile() +{ + MEDIA_LOG_D("IN"); + CloseFile(); + fin_.open(fileName_.c_str(), std::ios::in | std::ios::binary); + if (!fin_.is_open()) { + MEDIA_LOG_E("Fail to load file from %s", fileName_.c_str()); + return Status::ERROR_UNKNOWN; + } + fin_.seekg(0, fin_.end); + if (!fin_.good()) { + isSeekable_ = false; + MEDIA_LOG_E("Seek to end fail"); + return Status::ERROR_UNKNOWN; + } + fileSize_ = fin_.tellg(); + if (fileSize_ <= 0) { + isSeekable_ = false; + } + MEDIA_LOG_D("fileName_: %s, fileSize_: %zu", fileName_.c_str(), fileSize_); + fin_.seekg(0, fin_.beg); + return Status::OK; +} + +void FileSourcePlugin::CloseFile() +{ + if (fin_.is_open()) { + MEDIA_LOG_I("close file"); + fin_.close(); + } +} +} +} +} diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.h b/engine/plugin/plugins/source/file_source/file_source_plugin.h new file mode 100644 index 00000000..2c04e41d --- /dev/null +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 MEDIA_PIPELINE_FILE_SOURCE_PLUGIN_H +#define MEDIA_PIPELINE_FILE_SOURCE_PLUGIN_H + +#include +#include "plugin/common/plugin_types.h" +#include "plugin/interface/source_plugin.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +class FileSourceAllocator : public Allocator { +public: + FileSourceAllocator() = default; + ~FileSourceAllocator() = default; + + void* Alloc(size_t size) override; + void Free(void* ptr) override; +}; + +class FileSourcePlugin : public SourcePlugin { +public: + explicit FileSourcePlugin(const std::string &name); + ~FileSourcePlugin(); + + Status Init() override; + Status Deinit() override; + Status Prepare() override; + Status Reset() override; + Status Start() override; + Status Stop() override; + bool IsParameterSupported(Tag tag) override; + Status GetParameter(Tag tag, ValueType &value) override; + Status SetParameter(Tag tag, const ValueType &value) override; + Status GetState(State &state) override; + std::shared_ptr GetAllocator() override; + Status SetCallback(const std::shared_ptr &cb) override; + Status SetSource( + std::string& uri, std::shared_ptr> params) override; + Status Read(std::shared_ptr &buffer, size_t expectedLen) override; + Status GetSize(size_t& size) override; + bool IsSeekable() override; + Status SeekTo(uint64_t offset) override; + +private: + std::string name_; + State state_; + std::string fileName_ {}; + std::ifstream fin_; + size_t fileSize_; + bool isSeekable_; + uint64_t position_; + std::shared_ptr mAllocator_ {nullptr}; + + Status ParseFileName(std::string& uri); + Status OpenFile(); + void CloseFile(); +}; +} +} +} + +#endif // MEDIA_PIPELINE_FILE_SOURCE_PLUGIN_H diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp new file mode 100644 index 00000000..90f91323 --- /dev/null +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "StreamSourcePlugin" + +#include "stream_source_plugin.h" +#include "plugin/common/plugin_buffer.h" +#include "plugin/core/plugin_manager.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +std::shared_ptr StreamSourcePluginCreater(const std::string& name) +{ + return std::make_shared(name); +} + +const Status StreamSourceRegister(const std::shared_ptr& reg) +{ + SourcePluginDef definition; + definition.name = "StreamSource"; + definition.description = "Stream source"; + definition.rank = 100; // 100: max rank + definition.protocol = "stream"; + definition.creator = StreamSourcePluginCreater; + return reg->AddPlugin(definition); +} + +PLUGIN_DEFINITION(StreamSource, LicenseType::APACHE_V2, StreamSourceRegister, [] {}); + +void* StreamSourceAllocator::Alloc(size_t size) +{ + if (size == 0) { + return nullptr; + } + return reinterpret_cast(new (std::nothrow) uint8_t[size]); +} + +void StreamSourceAllocator::Free(void* ptr) +{ + if (ptr != nullptr) { + delete[](uint8_t*) ptr; + } +} + +StreamSourceCallback::StreamSourceCallback(std::shared_ptr dataSource, + std::shared_ptr& stream) + : dataSource_(dataSource), streamSource_(stream) +{ +} + +uint8_t* StreamSourceCallback::GetBuffer(size_t index) +{ + auto bufferPtr = dataSource_->FindBuffer(index); + return bufferPtr->GetMemory()->GetWritableData(bufferPtr->GetMemory()->GetCapacity()); +} + +void StreamSourceCallback::QueueBuffer(size_t index, size_t offset, size_t size, int64_t timestampUs, uint32_t flags) +{ + auto bufferPtr = dataSource_->FindBuffer(index); + dataSource_->EraseBuffer(index); + bufferPtr->GetMemory()->GetWritableData(size); + dataSource_->EnqueBuffer(bufferPtr); +} + +StreamSourcePlugin::StreamSourcePlugin(const std::string& name) + : bufferPool_(0), + name_(name), + state_(State::CREATED), + isSeekable_(false), + waitBuffers_(), + bufferQueue_("SourceBuffQue") +{ + MEDIA_LOG_D("ctor called"); +} + +StreamSourcePlugin::~StreamSourcePlugin() +{ + MEDIA_LOG_D("dtor called"); + state_ = State::DESTROYED; +} + +Status StreamSourcePlugin::Init() +{ + MEDIA_LOG_D("IN"); + bufferPool_.Init(DEFAULT_FRAME_SIZE); + mAllocator_ = std::make_shared(); + state_ = State::INITIALIZED; + return Status::OK; +} + +Status StreamSourcePlugin::Deinit() +{ + MEDIA_LOG_D("IN"); + state_ = State::DESTROYED; + return Status::OK; +} + +Status StreamSourcePlugin::Prepare() +{ + MEDIA_LOG_D("IN"); + state_ = State::PREPARED; + return Status::OK; +} + +Status StreamSourcePlugin::Reset() +{ + MEDIA_LOG_D("IN"); + state_ = State::INITIALIZED; + return Status::OK; +} + +Status StreamSourcePlugin::Start() +{ + MEDIA_LOG_D("IN"); + bufferPool_.SetActive(true); + taskPtr_->Start(); + state_ = State::RUNNING; + return Status::OK; +} + +Status StreamSourcePlugin::Stop() +{ + MEDIA_LOG_D("IN"); + bufferQueue_.SetActive(false); + taskPtr_->Stop(); + state_ = State::PREPARED; + return Status::OK; +} + +bool StreamSourcePlugin::IsParameterSupported(Tag tag) +{ + MEDIA_LOG_D("IN"); + return true; +} + +Status StreamSourcePlugin::GetParameter(Tag tag, ValueType& value) +{ + MEDIA_LOG_D("IN"); + return Status::OK; +} + +Status StreamSourcePlugin::SetParameter(Tag tag, const ValueType& value) +{ + MEDIA_LOG_D("IN"); + return Status::OK; +} + +Status StreamSourcePlugin::GetState(State& state) +{ + MEDIA_LOG_D("IN"); + state = state_; + return Status::OK; +} + +std::shared_ptr StreamSourcePlugin::GetAllocator() +{ + MEDIA_LOG_D("IN"); + return mAllocator_; +} + +Status StreamSourcePlugin::SetCallback(const std::shared_ptr& cb) +{ + MEDIA_LOG_D("IN"); + return Status::OK; +} + +Status StreamSourcePlugin::SetSource(std::string& uri, std::shared_ptr> params) +{ + if (uri.compare("stream://") || (params == nullptr)) { + MEDIA_LOG_E("Bad uri: %s", uri.c_str()); + return Status::ERROR_INVALID_DATA; + } + std::shared_ptr source_ = nullptr; + for (const auto& iter_ : *params) { + std::string key_ = iter_.first; + ValueType val_ = iter_.second; + if ((key_.compare("StreamSource") == 0) && (val_.Type() == typeid(std::shared_ptr))) { + source_ = Plugin::AnyCast>(val_); + break; + } + } + if (source_ == nullptr) { + MEDIA_LOG_E("Bad source"); + return Status::ERROR_INVALID_DATA; + } + std::shared_ptr stream_ = source_->GetSourceStream(); + if (stream_ == nullptr) { + MEDIA_LOG_E("Get StreamSource fail"); + return Status::ERROR_INVALID_DATA; + } + + streamCallback_ = std::make_shared(shared_from_this(), stream_); + stream_->SetStreamCallback(streamCallback_); + streamSource_ = stream_; + taskPtr_ = std::make_shared("StreamSource"); + taskPtr_->RegisterHandler(std::bind(&StreamSourcePlugin::NotifyAvilableBufferLoop, this)); + return Status::OK; +} + +Status StreamSourcePlugin::Read(std::shared_ptr& buffer, size_t expectedLen) +{ + AVBufferPtr bufPtr_ = bufferQueue_.Pop(); // the cached buffer + auto availSize = bufPtr_->GetMemory()->GetSize(); + MEDIA_LOG_D("availSize: %lu, expectedLen: %zu\n", availSize, expectedLen); + if (buffer->IsEmpty()) { // No buffer provided, use the cached buffer. + buffer = bufPtr_; + return Status::OK; + } else { // Buffer provided, copy it. + if (buffer->GetMemory()->GetCapacity() < availSize) { + MEDIA_LOG_D("buffer->length: %lu is smaller than %lu\n", buffer->GetMemory()->GetCapacity(), availSize); + return Status::ERROR_NO_MEMORY; + } + buffer->GetMemory()->Write(bufPtr_->GetMemory()->GetReadOnlyData(), availSize); + } + return Status::OK; +} + +Status StreamSourcePlugin::GetSize(size_t& size) +{ + MEDIA_LOG_D("IN"); + size = -1; + return Status::ERROR_WRONG_STATE; +} + +bool StreamSourcePlugin::IsSeekable() +{ + MEDIA_LOG_D("IN"); + return isSeekable_; +} + +Status StreamSourcePlugin::SeekTo(uint64_t offset) +{ + MEDIA_LOG_D("IN"); + return Status::ERROR_UNIMPLEMENTED; +} + +AVBufferPtr StreamSourcePlugin::AllocateBuffer() +{ + return bufferPool_.AllocateBuffer(); +} + +AVBufferPtr StreamSourcePlugin::FindBuffer(size_t idx) +{ + OSAL::ScopedLock lock(mutex_); + auto it = waitBuffers_.find(idx); + if (it != waitBuffers_.end()) { + return it->second; + } + return nullptr; +} + +void StreamSourcePlugin::EraseBuffer(size_t idx) +{ + OSAL::ScopedLock lock(mutex_); + waitBuffers_.erase(idx); +} + +void StreamSourcePlugin::EnqueBuffer(AVBufferPtr& bufferPtr) +{ + bufferQueue_.Push(bufferPtr); +} + +void StreamSourcePlugin::NotifyAvilableBufferLoop() +{ + auto bufferPtr = AllocateBuffer(); + if (bufferPtr == nullptr) { + MEDIA_LOG_E("Alloc buffer fail"); + return; + } + size_t idx = GetUniqueIdx(); + { + OSAL::ScopedLock lock(mutex_); + waitBuffers_[idx] = bufferPtr; + } + std::shared_ptr stream = streamSource_.lock(); + stream->OnBufferAvailable(idx, 0, bufferPtr->GetMemory()->GetCapacity()); +} +} // namespace Plugin +} // namespace Media +} // namespace OHOS diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.h b/engine/plugin/plugins/source/stream_source/stream_source_plugin.h new file mode 100644 index 00000000..0def850a --- /dev/null +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_STREAM_SOURCE_PLUGIN_H +#define HISTREAMER_STREAM_SOURCE_PLUGIN_H + +#include "source.h" +#include "foundation/blocking_queue.h" +#include "foundation/buffer_pool.h" +#include "foundation/constants.h" +#include "foundation/error_code.h" +#include "foundation/type_define.h" +#include "osal/thread/task.h" +#include "plugin/common/plugin_types.h" +#include "plugin/interface/source_plugin.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +using SourceType = OHOS::Media::SourceType; +using MediaSource = OHOS::Media::Source; +using StreamCallback = OHOS::Media::StreamCallback; +using StreamSource = OHOS::Media::StreamSource; + +class StreamSourcePlugin; + +class StreamSourceAllocator : public Allocator { +public: + StreamSourceAllocator() = default; + ~StreamSourceAllocator() = default; + + void* Alloc(size_t size) override; + void Free(void* ptr) override; +}; + +class StreamSourceCallback : public StreamCallback { +public: + StreamSourceCallback(std::shared_ptr dataSource, std::shared_ptr& stream); + ~StreamSourceCallback() = default; + + uint8_t* GetBuffer(size_t index); + void QueueBuffer(size_t index, size_t offset, size_t size, int64_t timestampUs, uint32_t flags); + void SetParameters(const Media::Format& params) + { + } + +private: + std::shared_ptr dataSource_; + std::weak_ptr streamSource_; +}; + +class StreamSourcePlugin : public SourcePlugin, std::enable_shared_from_this { +public: + explicit StreamSourcePlugin(const std::string& name); + ~StreamSourcePlugin(); + + Status Init() override; + Status Deinit() override; + Status Prepare() override; + Status Reset() override; + Status Start() override; + Status Stop() override; + bool IsParameterSupported(Tag tag) override; + Status GetParameter(Tag tag, ValueType& value) override; + Status SetParameter(Tag tag, const ValueType& value) override; + Status GetState(State& state) override; + std::shared_ptr GetAllocator() override; + Status SetCallback(const std::shared_ptr& cb) override; + Status SetSource(std::string& uri, std::shared_ptr> params = nullptr) override; + Status Read(std::shared_ptr& buffer, size_t expectedLen) override; + Status GetSize(size_t& size) override; + bool IsSeekable() override; + Status SeekTo(uint64_t offset) override; + + AVBufferPtr AllocateBuffer(); + AVBufferPtr FindBuffer(size_t idx); + void EraseBuffer(size_t idx); + void EnqueBuffer(AVBufferPtr& bufferPtr); + +protected: + AVBufferPool bufferPool_; + +private: + std::string name_; + State state_; + bool isSeekable_; + OSAL::Mutex mutex_ {}; + std::map waitBuffers_; + std::weak_ptr streamSource_ {}; + std::shared_ptr streamCallback_ {nullptr}; + size_t idx_ {0}; + BlockingQueue bufferQueue_; + std::shared_ptr taskPtr_ {nullptr}; + std::shared_ptr mAllocator_ {nullptr}; + + void NotifyAvilableBufferLoop(); + size_t GetUniqueIdx() + { + return ++idx_; + } +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_STREAM_SOURCE_PLUGIN_H diff --git a/interface/histreamer/hiplayer.h b/interface/histreamer/hiplayer.h new file mode 100644 index 00000000..291f297b --- /dev/null +++ b/interface/histreamer/hiplayer.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_HIPLAYER_H +#define HISTREAMER_HIPLAYER_H + +#include +#include "player_interface.h" + +namespace OHOS { +namespace Media { +class HiPlayer : public Media::PlayerInterface { +public: + HiPlayer(); + ~HiPlayer() override; + int32_t SetSource(const Media::Source& source) override; + int32_t Prepare() override; + int32_t Play() override; + bool IsPlaying() override; + int32_t Pause() override; + int32_t Stop() override; + int32_t Rewind(int64_t mSeconds, int32_t mode) override; + int32_t SetVolume(float leftVolume, float rightVolume) override; + int32_t SetSurface(Surface* surface) override; + bool IsSingleLooping() override; + int32_t GetPlayerState(int32_t& state) override; + int32_t GetCurrentPosition(int64_t& currentPosition) override; + int32_t GetDuration(int64_t& duration) override; + int32_t GetVideoWidth(int32_t& videoWidth) override; + int32_t GetVideoHeight(int32_t& videoHeight) override; + int32_t SetPlaybackSpeed(float speed) override; + int32_t GetPlaybackSpeed(float& speed) override; + int32_t SetAudioStreamType(int32_t type) override; + void GetAudioStreamType(int32_t& type) override; + int32_t Reset() override; + int32_t Release() override; + int32_t SetLoop(bool loop) override; + void SetPlayerCallback(const std::shared_ptr& cb) override; + int32_t Init() override; + int32_t DeInit() override; + int32_t SetParameter(const Media::Format& params) override; + +private: + class HiPlayerImpl; + std::shared_ptr player_; + Media::PlayerStates curState_; + bool isInited_; + bool isSingleLoop_; + int audioStreamType_; +}; + +std::shared_ptr CreateHiPlayer(); +} // namespace Media +} // namespace OHOS +#endif // MULTIMEDIA_HIPLAYER_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..67544a4f --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,16 @@ + +CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) + +#PROJECT(mediapipeline_test) + +set(CMAKE_VERBOSE_MAKEFILE ON) + +set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src) + +if(NOT MINGW) + # support findSymbol, cmake >= 3.13 + # ADD_LINK_OPTIONS( -rdynamic) +endif() + +ADD_SUBDIRECTORY(ut) diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 00000000..50fe3431 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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. + */ + + +int main(int argc, char *argv[]) +{ +} diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt new file mode 100644 index 00000000..6b9e97a6 --- /dev/null +++ b/tests/ut/CMakeLists.txt @@ -0,0 +1,93 @@ + +include(${TOP_DIR}/engine/CMakeLists.txt) + +include_directories( + ${GTEST_DIRS}/include + ${MOCKCPP_DIR}/include +) + + +if (MINGW) +include_directories( + ${TOP_DIR}/engine/demo/windows/ffmpeg/include + ${TOP_DIR}/engine/demo/windows/SDL2.0/include +) +link_directories( + ${TOP_DIR}/engine/demo/windows/ffmpeg/lib + ${TOP_DIR}/engine/demo/windows/SDL2.0/lib/x64 +) +else() +include_directories( +) +endif() + +file(GLOB UT_TEST_SRCS ./*.cpp) + +set(SRC + ${HISTREAMER_SRCS} + ${UT_TEST_SRCS} + ../main.cpp + ) + +add_executable(MediaPlayerUtTests ${SRC}) + +link_directories( + ${GTEST_DIRS}/lib/ + ${MOCKCPP_DIR}/lib/ + /usr/local/lib + ${TOP_DIR}/engine/demo/linux/ffmpeg/lib + ${TOP_DIR}/engine/demo/linux/SDL2.0/lib +) + +target_compile_definitions(MediaPlayerUtTests PRIVATE UNIT_TEST) + +if (MSVC) + target_link_libraries(MediaPlayerUtTests + ${GTEST_DIRS}/lib/gtestd.lib + ${MOCKCPP_DIR}/lib/mockcpp.lib + pthreadVC2.lib + ) +elseif (MINGW) + set(ffmpeg_path ${TOP_DIR}/engine/demo/windows/ffmpeg) + set(sdl_path ${TOP_DIR}/engine/demo/windows/SDL2.0) + target_link_libraries(MediaPlayerUtTests + # dl + ${sdl_path}/lib/x64/SDL2.lib + ${ffmpeg_path}/lib/avcodec.lib + ${ffmpeg_path}/lib/swresample.lib + ${ffmpeg_path}/lib/avformat.lib + ${ffmpeg_path}/lib/avutil.lib + ${ffmpeg_path}/lib/avdevice.lib + ${ffmpeg_path}/lib/avfilter.lib + ${ffmpeg_path}/lib/swscale.lib + ${GTEST_DIRS}/lib/libgtest.a + ${MOCKCPP_DIR}/lib/libmockcpp_mingw8.a + ) + message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") + file(GLOB ffmpeg_shared_libraries ${ffmpeg_path}/bin/*.dll) + file(GLOB sdl_shared_libraries ${sdl_path}/lib/x64/*.dll) + file(COPY ${ffmpeg_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) + file(COPY ${sdl_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) +else () + set(ffmpeg_path ${TOP_DIR}/engine/demo/linux/ffmpeg) + set(sdl_path ${TOP_DIR}/engine/demo/linux/SDL2.0) + target_link_libraries(MediaPlayerUtTests + dl + ${ffmpeg_path}/lib/libavformat.a + ${ffmpeg_path}/lib/libavcodec.a + ${ffmpeg_path}/lib/libavdevice.a + ${ffmpeg_path}/lib/libavfilter.a + ${ffmpeg_path}/lib/libavutil.a + ${ffmpeg_path}/lib/libswscale.a + ${ffmpeg_path}/lib/libswresample.a + ${ffmpeg_path}/lib/liblzma.a + m + z + ${sdl_path}/lib/libSDL2.a + ${GTEST_DIRS}/lib/libgtest.a + pthread + ${MOCKCPP_DIR}/lib/libmockcpp.a + ) +endif () +add_test(Test MediaPlayerUtTests) +enable_testing() diff --git a/tests/ut/TestAny.cpp b/tests/ut/TestAny.cpp new file mode 100644 index 00000000..34e9bfde --- /dev/null +++ b/tests/ut/TestAny.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 +#include +#include +#include +#define private public +#define protected public +#include "common/any.h" + +namespace OHOS { +namespace Media { +namespace Test { +using namespace OHOS::Media::Plugin; +bool CompareFunctionTable(const Any::FunctionTable* ft1, const Any::FunctionTable* ft2) +{ + return ft1->type == ft2->type && ft1->destroy == ft2->destroy && ft1->getPtr == ft2->getPtr && + ft1->move == ft2->move && ft1->copy == ft2->copy && ft1->getConstPtr == ft2->getConstPtr; +} + +template class U> +bool UseStorage() +{ + auto ft1 = Any::GetFunctionTable(); + Any::FunctionTable ft2 { + .type = U::Type, + .destroy = U::Destroy, + .copy = U::Copy, + .move = U::Move, + .getPtr = U::GetPtr, + .getConstPtr = U::GetConstPtr + }; + return CompareFunctionTable(ft1, &ft2); +} + +class NonTrivialCopyable { +public: + NonTrivialCopyable(uint8_t a) : a_(a) + { + } + NonTrivialCopyable(const NonTrivialCopyable& a) : a_(a.a_) + { + } + NonTrivialCopyable& operator=(const NonTrivialCopyable& other) + { + if (this != &other) { + this->a_ = other.a_; + } + return *this; + } + uint8_t a_; +}; + +class NonTrivialNonThrowMove { +public: + explicit NonTrivialNonThrowMove(uint8_t a) : a_(a) + { + } + explicit NonTrivialNonThrowMove(const NonTrivialNonThrowMove& a) : a_(a.a_) + { + } + NonTrivialNonThrowMove& operator=(const NonTrivialNonThrowMove& other) + { + if (this != &other) { + a_ = other.a_; + } + return *this; + } + NonTrivialNonThrowMove(NonTrivialNonThrowMove&& t) noexcept + { + a_ = t.a_; + } + NonTrivialNonThrowMove& operator=(NonTrivialNonThrowMove&& other) + { + a_ = other.a_; + return *this; + } + uint8_t a_; +}; + +TEST(AnyTest, testAnyUseTrivial) +{ + auto tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage(); + ASSERT_FALSE(tmp); +} + +TEST(AnyTest, testAnyUseStack) +{ + auto tmp = UseStorage(); + ASSERT_FALSE(tmp); + tmp = UseStorage(); + ASSERT_TRUE(tmp); + tmp = UseStorage, Any::StackFunctionTable>(); + ASSERT_TRUE(tmp); +} + +TEST(AnyTest, testAnyUseHeap) +{ + auto tmp = UseStorage(); + ASSERT_TRUE(tmp); +} + +TEST(AnyTest, testAnyCast) +{ + const int number = 12; + auto a = Any(number); + int* i = AnyCast(&a); + ASSERT_TRUE(*i == number); + a = std::string("hello"); + auto& ra = AnyCast(a); + ra[1] = 'o'; + const auto& refString = AnyCast(a); + ASSERT_STREQ(refString.c_str(), "hollo"); + auto copyString = AnyCast(a); + copyString[1] = 'l'; + ASSERT_STREQ(refString.c_str(), "hollo"); + ASSERT_STREQ(copyString.c_str(), "hlllo"); + + ASSERT_THROW(AnyCast("test"), BadAnyCast); + ASSERT_THROW(AnyCast("test"), BadAnyCast); +} + +class Star { + std::string name_; + int id_; + +public: + Star(std::string name, int id) : name_ {std::move(name)}, id_ {id} + { + } + + bool operator==(const Star& other) const + { + return name_ == other.name_ && id_ == other.id_; + } +}; + +TEST(AnyTest, testAnyEmplace) +{ + Any celestial; + // (1) emplace( Args&&... args ); + celestial.Emplace("Procyon", 2943); + Star star1("Procyon", 2943); + const auto* star = AnyCast(&celestial); + ASSERT_TRUE(star1 == (*star)); + + Any av; + // (2) emplace( std::initializer_list il, Args&&... args ); + av.Emplace>({'C', '+', '+', '1', '7'}); + const auto* va = AnyCast>(&av); + std::vector vector1({'C', '+', '+', '1', '7'}); + ASSERT_TRUE(vector1 == (*va)); +} + +TEST(AnyTest, testMakeAny) +{ + Star star1("Procyon", 2943); + Any celestial = MakeAny("Procyon", 2943); + ASSERT_TRUE(AnyCast(celestial) == star1); + + std::vector vector1({'C', '+', '+', '1', '7'}); + Any av = MakeAny>({'C', '+', '+', '1', '7'}); + ASSERT_TRUE(AnyCast>(av) == vector1); +} + +TEST(AnyTest, testType) +{ + Any test = "test"; + ASSERT_TRUE(test.Type() == typeid(const char*)); + ASSERT_NE(test.Type(), typeid(char*)); + ASSERT_NE(test.Type(), typeid(char*)); +} + +TEST(AnyTest, testSwap) +{ + NonTrivialNonThrowMove n1(100); + Any a1 = n1; + Any a2 = 4; + a1.Swap(a2); + ASSERT_TRUE(AnyCast(a1) == 4); + ASSERT_EQ(AnyCast(a2).a_, n1.a_); + + a2 = "test"; + const Star star("star", 100); + a1 = star; + a2.Swap(a1); + ASSERT_STREQ(AnyCast(a1), "test"); + ASSERT_TRUE(AnyCast(a2) == star); + + std::swap(a1, a2); + ASSERT_STREQ(AnyCast(a2), "test"); + ASSERT_TRUE(AnyCast(a1) == star); +} + +TEST(AnyTest, testHasValue_Reset) +{ + Any a; + ASSERT_FALSE(a.HasValue()); + a = "test"; + ASSERT_TRUE(a.HasValue()); + a.Reset(); + ASSERT_FALSE(a.HasValue()); +} +} // namespace Test +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/tests/ut/TestBitReader.cpp b/tests/ut/TestBitReader.cpp new file mode 100644 index 00000000..93cb0344 --- /dev/null +++ b/tests/ut/TestBitReader.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "common/any.h" +#include "gtest/gtest.h" +#define private public +#define protected public +#define UNIT_TEST 1 + +#include "plugin/plugins/ffmpeg_adapter/utils/bit_reader.h" + +namespace OHOS { +namespace Media { +namespace Test { +using namespace Plugin; + +class TestBitReader : public ::testing::Test { +public: + void SetUp() override + { + } + + void TearDown() override + { + } + BitReader bitReader; +}; + +TEST_F(TestBitReader, test_read_bits) +{ + const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; + bitReader.Reset(data, sizeof(data)); + int val = 0; + EXPECT_EQ(bitReader.ReadBits(4, val), true); + EXPECT_EQ(val, 0xf); + + EXPECT_EQ(bitReader.ReadBits(8, val), true); + EXPECT_EQ(val, 0xf1); + + EXPECT_EQ(bitReader.GetAvailableBits(), 20); + EXPECT_EQ(bitReader.ReadBits(20, val), true); + EXPECT_EQ(val, 0x12233); + + EXPECT_EQ(bitReader.ReadBits(4, val), false); +} + +TEST_F(TestBitReader, test_skip_bits) +{ + const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; + bitReader.Reset(data, sizeof(data)); + int val = 0; + EXPECT_EQ(bitReader.ReadBits(4, val), true); + EXPECT_EQ(val, 0xf); + bitReader.SkipBits(4); + EXPECT_EQ(bitReader.ReadBits(8, val), true); + EXPECT_EQ(val, 0x11); + bitReader.SkipBits(8); + EXPECT_EQ(bitReader.ReadBits(8, val), true); + EXPECT_EQ(val, 0x33); +} + +TEST_F(TestBitReader, test_show_bits) +{ + const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; + bitReader.Reset(data, sizeof(data)); + int val = 0; + EXPECT_EQ(bitReader.ReadBits(4, val), true); + EXPECT_EQ(val, 0xf); + EXPECT_EQ(bitReader.PeekBits(4, val), true); + EXPECT_EQ(val, 0xf); + bitReader.SkipBits(4); + EXPECT_EQ(bitReader.PeekBits(8, val), true); + EXPECT_EQ(val, 0x11); + EXPECT_EQ(bitReader.ReadBits(8, val), true); + EXPECT_EQ(val, 0x11); +} + +TEST_F(TestBitReader, test_seek_bits) +{ + const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; + bitReader.Reset(data, sizeof(data)); + int val = 0; + EXPECT_EQ(bitReader.ReadBits(4, val), true); + EXPECT_EQ(val, 0xf); + bitReader.SkipBits(20); + EXPECT_EQ(bitReader.ReadBits(8, val), true); + EXPECT_EQ(val, 0x33); + EXPECT_EQ(bitReader.GetAvailableBits(), 0); + EXPECT_EQ(bitReader.SeekTo(0), true); + EXPECT_EQ(bitReader.ReadBits(4, val), true); + EXPECT_EQ(val, 0xf); +} +} // namespace Test +} // namespace Media +} // namespace OHOS diff --git a/tests/ut/TestBufferPool.cpp b/tests/ut/TestBufferPool.cpp new file mode 100644 index 00000000..49b971af --- /dev/null +++ b/tests/ut/TestBufferPool.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "gtest/gtest.h" +#include "common/any.h" +#define private public +#define protected public +#define UNIT_TEST 1 + +#include + +#include "foundation/buffer_pool.h" +#include "foundation/constants.h" +#include "foundation/type_define.h" + +namespace OHOS::Media::Test { +class BufferPoolTest : public ::testing::Test { +public: + void SetUp() override + { + pool = std::make_shared>(DEFAULT_POOL_SIZE); + pool->Init(); + } + + void TearDown() override + { + } + + std::shared_ptr> pool; +}; + +TEST_F(BufferPoolTest, buffer_pool_init_buffer) +{ + EXPECT_EQ(DEFAULT_POOL_SIZE, pool->Size()); +} + +TEST_F(BufferPoolTest, buffer_pool_recycle_succ) +{ + EXPECT_EQ(DEFAULT_POOL_SIZE, pool->Size()); + auto buffPtr = pool->AllocateBuffer(); + EXPECT_EQ(DEFAULT_POOL_SIZE - 1, pool->Size()); + buffPtr.reset(); + EXPECT_EQ(DEFAULT_POOL_SIZE, pool->Size()); +} + +TEST_F(BufferPoolTest, buffer_pool_return_nullptr_after_buffer_exhausted) +{ + EXPECT_EQ(DEFAULT_POOL_SIZE, pool->Size()); + std::vector> buffers; + buffers.reserve(DEFAULT_POOL_SIZE); + for (size_t i = 0; i < DEFAULT_POOL_SIZE; ++i) { + buffers.emplace_back(pool->AllocateBuffer()); + } + EXPECT_EQ(true, pool->Empty()); + EXPECT_EQ(nullptr, pool->AllocateBufferNonBlocking()); +} +} // namespace \ No newline at end of file diff --git a/tests/ut/TestCompatibleCheck.cpp b/tests/ut/TestCompatibleCheck.cpp new file mode 100644 index 00000000..54840e88 --- /dev/null +++ b/tests/ut/TestCompatibleCheck.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "common/any.h" + +#include + +#define private public +#define protected public +#define UNIT_TEST 1 + +#include "core/compatible_check.h" +#include "foundation/constants.h" +#include "plugin/common/plugin_audio_tags.h" + +using namespace std; +using namespace OHOS::Media::Plugin; + +namespace OHOS::Media::Test { +TEST(TestMime, Mime_compatible) { + Capability wildcard {"*"}; + Capability audioWildcard {"audio/*"}; + Capability testWildcard {"test/*"}; + Capability wrongWildcard {"/audio*"}; + Capability wrongCapability {"wrong"}; + Capability rawMimeCapability {"audio/raw"}; + Capability mpegMimeCapability {"audio/mpeg"}; + Meta meta; + ASSERT_FALSE(Pipeline::CompatibleWith(wildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(audioWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(testWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(mpegMimeCapability, meta)); + meta.SetString(MetaID::MIME, "/TEST"); + ASSERT_FALSE(Pipeline::CompatibleWith(wildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(audioWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(testWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(mpegMimeCapability, meta)); + + meta.SetString(MetaID::MIME, MEDIA_MIME_AUDIO_RAW); + ASSERT_TRUE(Pipeline::CompatibleWith(wildcard, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(audioWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(testWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongCapability, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(rawMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(mpegMimeCapability, meta)); + + meta.SetString(MetaID::MIME, "AUDIO/RAW"); + ASSERT_TRUE(Pipeline::CompatibleWith(wildcard, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(audioWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(testWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongWildcard, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(wrongCapability, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(rawMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(mpegMimeCapability, meta)); +} + +TEST(TestAudioSampleRate, AudioSampleRate_compatible) { + Capability rawFixedMimeCapability (MEDIA_MIME_AUDIO_RAW); + rawFixedMimeCapability.AppendFixedKey(CapabilityID::AUDIO_SAMPLE_RATE, 8000); + Capability rawListMimeCapability {MEDIA_MIME_AUDIO_RAW}; + rawListMimeCapability.AppendDiscreteKeys(CapabilityID::AUDIO_SAMPLE_RATE, {8000, 32000, 48000, 44100}); + Capability rawIntervalMimeCapability {MEDIA_MIME_AUDIO_RAW}; + rawIntervalMimeCapability.AppendIntervalKey(CapabilityID::AUDIO_SAMPLE_RATE, 8000, 48000); + + + Meta meta; + meta.SetString(MetaID::MIME, MEDIA_MIME_AUDIO_RAW); + ASSERT_FALSE(Pipeline::CompatibleWith(rawFixedMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawListMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawIntervalMimeCapability, meta)); + meta.SetUint32(MetaID::AUDIO_SAMPLE_RATE, 8000); + ASSERT_TRUE(Pipeline::CompatibleWith(rawFixedMimeCapability, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(rawListMimeCapability, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(rawIntervalMimeCapability, meta)); + meta.SetUint32(MetaID::AUDIO_SAMPLE_RATE, 80000); + ASSERT_FALSE(Pipeline::CompatibleWith(rawFixedMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawListMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawIntervalMimeCapability, meta)); +} + +TEST(TestAudioChannelMask, AudioChannelLayout_compatible) { + Capability rawFixedMimeCapability {MEDIA_MIME_AUDIO_RAW}; + rawFixedMimeCapability.AppendFixedKey(CapabilityID::AUDIO_CHANNEL_LAYOUT, + AudioChannelLayout::STEREO); + + Capability rawListMimeCapability {MEDIA_MIME_AUDIO_RAW}; + rawListMimeCapability.AppendDiscreteKeys(CapabilityID::AUDIO_CHANNEL_LAYOUT, + {AudioChannelLayout::STEREO, AudioChannelLayout::SURROUND, + AudioChannelLayout::CH_5POINT1, AudioChannelLayout::CH_7POINT1}); + + Capability illFormat {MEDIA_MIME_AUDIO_RAW}; + // channel layout does not support format [a, b] + illFormat.AppendIntervalKey( + CapabilityID::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::STEREO, + AudioChannelLayout::SURROUND); + + + Meta meta; + meta.SetString(MetaID::MIME, MEDIA_MIME_AUDIO_RAW); + ASSERT_FALSE(Pipeline::CompatibleWith(rawFixedMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawListMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(illFormat, meta)); + meta.SetData(MetaID::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::STEREO); + ASSERT_TRUE(Pipeline::CompatibleWith(rawFixedMimeCapability, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(rawListMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(illFormat, meta)); + meta.SetData(MetaID::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::CH_2_1); + ASSERT_FALSE(Pipeline::CompatibleWith(rawFixedMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(rawListMimeCapability, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(illFormat, meta)); +} + +TEST(TestCapabilityListCompatible, CapabilityList_compatible) { + CapabilitySet cs0 {Capability(MEDIA_MIME_AUDIO_RAW), Capability(MEDIA_MIME_AUDIO_AAC), + Capability(MEDIA_MIME_AUDIO_APE)}; + Capability ca0; + ca0.SetMime(MEDIA_MIME_AUDIO_RAW).AppendFixedKey(CapabilityID::AUDIO_MPEG_VERSION, 1); + Capability ca1; + ca1.SetMime(MEDIA_MIME_AUDIO_RAW) + .AppendDiscreteKeys(CapabilityID::AUDIO_CHANNEL_LAYOUT, + {AudioChannelLayout::STEREO, AudioChannelLayout::SURROUND, + AudioChannelLayout::CH_5POINT1, AudioChannelLayout::CH_7POINT1}) + .AppendIntervalKey(CapabilityID::AUDIO_CHANNELS, 2, 10) + .AppendFixedKey(CapabilityID::AUDIO_SAMPLE_RATE, 44100) + .AppendIntervalKey(CapabilityID::AUDIO_MPEG_VERSION, 100, 1000); + Capability ca2; + ca2.SetMime(MEDIA_MIME_AUDIO_RAW) + .AppendDiscreteKeys(CapabilityID::AUDIO_CHANNEL_LAYOUT, + {AudioChannelLayout::STEREO, AudioChannelLayout::SURROUND}) + .AppendIntervalKey(CapabilityID::AUDIO_CHANNELS, 2, 5) + .AppendFixedKey(CapabilityID::AUDIO_SAMPLE_RATE, 48000) + .AppendIntervalKey(CapabilityID::AUDIO_MPEG_VERSION, 3000, 10000); + + Capability ca3; + ca3.SetMime(MEDIA_MIME_AUDIO_FLAC) + .AppendDiscreteKeys(CapabilityID::AUDIO_CHANNEL_LAYOUT, + {AudioChannelLayout::STEREO, AudioChannelLayout::SURROUND}) + .AppendIntervalKey(CapabilityID::AUDIO_CHANNELS, 2, 5) + .AppendFixedKey(CapabilityID::AUDIO_SAMPLE_RATE, 48000) + .AppendIntervalKey(CapabilityID::AUDIO_MPEG_VERSION, 3000, 10000); + + CapabilitySet cs1{ca0, ca1, ca2, ca3}; + + Meta meta; + meta.SetString(MetaID::MIME, MEDIA_MIME_AUDIO_RAW); + meta.SetUint32(MetaID::AUDIO_MPEG_VERSION, 1); + ASSERT_TRUE(Pipeline::CompatibleWith(cs0, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(ca0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca1, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca2, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca3, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(cs1, meta)); + + meta.SetUint32(MetaID::AUDIO_MPEG_VERSION, 300); + meta.SetData(MetaID::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::STEREO); + meta.SetUint32(MetaID::AUDIO_CHANNELS, 2); + meta.SetUint32(MetaID::AUDIO_SAMPLE_RATE, 48000); + ASSERT_TRUE(Pipeline::CompatibleWith(cs0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca1, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca2, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca3, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(cs1, meta)); + + meta.SetUint32(MetaID::AUDIO_MPEG_VERSION, 3000); + ASSERT_TRUE(Pipeline::CompatibleWith(cs0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca1, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(ca2, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca3, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(cs1, meta)); + + meta.SetString(MetaID::MIME, MEDIA_MIME_AUDIO_FLAC); + ASSERT_FALSE(Pipeline::CompatibleWith(cs0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca1, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca2, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(ca3, meta)); + ASSERT_TRUE(Pipeline::CompatibleWith(cs1, meta)); + + meta.SetString(MetaID::MIME, MEDIA_MIME_AUDIO_APE); + meta.SetData(MetaID::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::CH_2_1); + ASSERT_TRUE(Pipeline::CompatibleWith(cs0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca0, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca1, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca2, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(ca3, meta)); + ASSERT_FALSE(Pipeline::CompatibleWith(cs1, meta)); +} +} \ No newline at end of file diff --git a/tests/ut/TestFFmpegUtils.cpp b/tests/ut/TestFFmpegUtils.cpp new file mode 100644 index 00000000..5ef54929 --- /dev/null +++ b/tests/ut/TestFFmpegUtils.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "common/any.h" +#include "gtest/gtest.h" +#define private public +#define protected public + +#include "plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h" + +namespace OHOS { +namespace Media { +namespace Test { +using namespace Plugin; + +TEST(ChannelLayoutTest, test_convert_from_ffmpeg_mono) +{ + int channels = 1; + uint64_t ffChannelLayout = 0x4; + + AudioChannelLayout channelLayout = ConvertChannelLayoutFromFFmpeg(channels, ffChannelLayout); + EXPECT_EQ(AudioChannelLayout::MONO, channelLayout); +} + +TEST(ChannelLayoutTest, test_convert_from_ffmpeg_stereo) +{ + int channels = 2; + uint64_t ffChannelLayout = 0x3; + + AudioChannelLayout channelLayout = ConvertChannelLayoutFromFFmpeg(channels, ffChannelLayout); + EXPECT_EQ(AudioChannelLayout::STEREO, channelLayout); +} +} // namespace Test +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/tests/ut/TestFilter.cpp b/tests/ut/TestFilter.cpp new file mode 100644 index 00000000..5fe4c542 --- /dev/null +++ b/tests/ut/TestFilter.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 +#include "common/any.h" + +#include + +#include +#include "pipeline/core/filter_base.h" + +using namespace OHOS::Media::Pipeline; + +namespace OHOS::Media::Test { +class UtTestFilter : public ::testing::Test { +public: + std::shared_ptr port1 = std::make_shared(nullptr, "port1"); + std::shared_ptr port2 = std::make_shared(nullptr, "port2"); + std::vector list {port1, port2}; + virtual void SetUp() override + { + } + virtual void TearDown() override + { + } +}; + +TEST_F(UtTestFilter, Can_find_port_if_it_exists) +{ + ASSERT_EQ(port2, FilterBase::FindPort(list, "port2")); +} + +TEST_F(UtTestFilter, Can_not_find_port_if_it_does_not_exists) +{ + ASSERT_TRUE(nullptr == FilterBase::FindPort(list, "port3")); +} +} \ No newline at end of file diff --git a/tests/ut/TestHiPlayer.cpp b/tests/ut/TestHiPlayer.cpp new file mode 100644 index 00000000..1204d250 --- /dev/null +++ b/tests/ut/TestHiPlayer.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 +#include "common/any.h" + +#include +#include + +#include +#include "pipeline/filters/codec/audio_decoder/audio_decoder_filter.h" +#include "pipeline/filters/sink/audio_sink/audio_sink_filter.h" +#include "player/hiplayer_impl.h" + +using namespace OHOS::Media::Pipeline; + +namespace OHOS::Media::Test { +class UtTestHiPlayer : public ::testing::Test { +public: + MockObject audioSource {}; + MockObject demuxer {}; + MockObject audioDecoder {}; + MockObject audioSink {}; + + std::shared_ptr player = HiPlayer::HiPlayerImpl::CreateHiPlayerImpl(); + std::shared_ptr source = std::make_shared("./test.mp3"); + PInPort emptyInPort = EmptyInPort::GetInstance(); + POutPort emptyOutPort = EmptyOutPort::GetInstance(); + + virtual void SetUp() override + { + MOCK_METHOD(audioSource, Init).defaults(); + MOCK_METHOD(demuxer, Init).defaults(); + MOCK_METHOD(audioDecoder, Init).defaults(); + MOCK_METHOD(audioSink, Init).defaults(); + + MOCK_METHOD(audioSource, Prepare).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(demuxer, Prepare).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioDecoder, Prepare).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioSink, Prepare).defaults().will(returnValue(SUCCESS)); + + MOCK_METHOD(audioSource, Start).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(demuxer, Start).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioDecoder, Start).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioSink, Start).defaults().will(returnValue(SUCCESS)); + + MOCK_METHOD(audioSource, Stop).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(demuxer, Stop).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioDecoder, Stop).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioSink, Stop).defaults().will(returnValue(SUCCESS)); + + MOCK_METHOD(audioSource, GetInPort).defaults().will(returnValue(emptyInPort)); + MOCK_METHOD(demuxer, GetInPort).defaults().will(returnValue(emptyInPort)); + MOCK_METHOD(audioDecoder, GetInPort).defaults().will(returnValue(emptyInPort)); + MOCK_METHOD(audioSink, GetInPort).defaults().will(returnValue(emptyInPort)); + + MOCK_METHOD(audioSource, GetOutPort).defaults().will(returnValue(emptyOutPort)); + MOCK_METHOD(demuxer, GetOutPort).defaults().will(returnValue(emptyOutPort)); + MOCK_METHOD(audioDecoder, GetOutPort).defaults().will(returnValue(emptyOutPort)); + MOCK_METHOD(audioSink, GetOutPort).defaults().will(returnValue(emptyOutPort)); + + player->audioSource.reset(audioSource); + player->demuxer.reset(demuxer); + player->audioDecoder.reset(audioDecoder); + player->audioSink.reset(audioSink); + + MOCK_METHOD(audioSource, SetSource).defaults().will(returnValue(SUCCESS)); + MOCK_METHOD(audioSource, SetBufferSize).defaults().will(returnValue(SUCCESS)); + + player->Init(); + } + virtual void TearDown() override + { + audioSource.verify(); + demuxer.verify(); + audioDecoder.verify(); + audioSink.verify(); + + audioSource.reset(); + demuxer.reset(); + audioDecoder.reset(); + audioSink.reset(); + } +}; + +TEST_F(UtTestHiPlayer, Can_SetSource) +{ + MOCK_METHOD(audioSource, SetSource) + .expects(once()) + .with(source) + .will(returnValue(SUCCESS)); + ASSERT_EQ(SUCCESS, player->SetSource(source)); + player->fsm_.DoTask(); + ASSERT_EQ("PreparingState", player->fsm_.curState_->GetName()); +} + +TEST_F(UtTestHiPlayer, Can_SetBufferSize) +{ + size_t size = 100; + MOCK_METHOD(audioSource, SetBufferSize) + .expects(once()) + .with(eq(size)) + .will(returnValue(SUCCESS)); + ASSERT_EQ(SUCCESS, player->SetBufferSize(size)); +} +} + diff --git a/tests/ut/TestMeta.cpp b/tests/ut/TestMeta.cpp new file mode 100644 index 00000000..78219992 --- /dev/null +++ b/tests/ut/TestMeta.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 + +#define private public +#define protected public +#define UNIT_TEST 1 + +#include "foundation/meta.h" +#include "foundation/utils.h" + +namespace OHOS { +namespace Media { +namespace Test { +using namespace OHOS::Media::Plugin; + +TEST(TestMeta, set_then_get_uint32) +{ + Meta meta; + int32_t channels = 64; + meta.SetInt32(MetaID::AUDIO_CHANNELS, channels); + int32_t outChannels = 0; + ASSERT_TRUE(meta.GetInt32(MetaID::AUDIO_CHANNELS, outChannels)); + ASSERT_EQ(channels, outChannels); + + uint32_t canNotGet = 0; + ASSERT_FALSE(meta.GetUint32(MetaID::AUDIO_CHANNELS, canNotGet)); +} + +TEST(TestMeta, set_then_get_cstring) +{ + Meta meta; + std::string artist("abcd"); + meta.SetString(MetaID::MEDIA_TITLE, artist); + std::string outArtist; + ASSERT_TRUE(meta.GetString(MetaID::MEDIA_TITLE, outArtist)); + ASSERT_STREQ(artist.c_str(), outArtist.c_str()); +} + +TEST(TestMeta, set_then_get_float) +{ + Meta meta; + float in = 9.9999f; + meta.SetFloat(MetaID::MEDIA_ARTIST, in); // this is only for test, normally MEDIA_ARTIST should be string + float out = 0; + ASSERT_TRUE(meta.GetFloat(MetaID::MEDIA_ARTIST, out)); + ASSERT_FLOAT_EQ(in, out); +} + +TEST(TestMeta, fail_to_get_unexisted_data) +{ + Meta meta; + int32_t channels = 64; + meta.SetInt32(MetaID::AUDIO_CHANNELS, channels); + int64_t bitRate = 1876411; + ASSERT_FALSE(meta.GetInt64(MetaID::MEDIA_BITRATE, bitRate)); +} + +TEST(TestMeta, remove_data) +{ + Meta meta; + int32_t channels = 64; + meta.SetInt32(MetaID::AUDIO_CHANNELS, channels); + ASSERT_TRUE(meta.Remove(MetaID::AUDIO_CHANNELS)); + ASSERT_FALSE(meta.Remove(MetaID::MEDIA_BITRATE)); +} + +TEST(TestMeta, clear_data) +{ + Meta meta; + std::string title("title"); + std::string album("album"); + int32_t channels = 64; + meta.SetString(MetaID::MEDIA_TITLE, title); + meta.SetString(MetaID::MEDIA_ALBUM, album); + meta.SetInt32(MetaID::AUDIO_CHANNELS, channels); + meta.Clear(); + std::string out; + ASSERT_FALSE(meta.GetString(MetaID::MEDIA_TITLE, out)); + ASSERT_TRUE(out.empty()); + ASSERT_FALSE(meta.GetString(MetaID::MEDIA_ALBUM, out)); + ASSERT_TRUE(out.empty()); + int32_t oChannels = 0; + ASSERT_FALSE(meta.GetInt32(MetaID::AUDIO_CHANNELS, oChannels)); + ASSERT_EQ(0u, oChannels); +} + +TEST(TestMeta, update_meta) +{ + Meta meta; + std::string title("title"); + std::string album("album"); + int32_t channels = 64; + meta.SetString(MetaID::MEDIA_TITLE, title); + meta.SetString(MetaID::MEDIA_ALBUM, album); + meta.SetUint32(MetaID::AUDIO_CHANNELS, channels); + + Meta meta2; + int32_t channels2 = 32; + meta.SetInt32(MetaID::AUDIO_CHANNELS, channels2); + + meta.Update(meta2); + std::string out; + ASSERT_TRUE(meta.GetString(MetaID::MEDIA_TITLE, out)); + ASSERT_STREQ("title", out.c_str()); + ASSERT_TRUE(meta.GetString(MetaID::MEDIA_ALBUM, out)); + ASSERT_STREQ("album", out.c_str()); + int32_t oChannel = 0; + ASSERT_TRUE(meta.GetInt32(MetaID::AUDIO_CHANNELS, oChannel)); + ASSERT_EQ(channels2, oChannel); +} +} // namespace Test +} // namespace Media +} // namespace OHOS diff --git a/tests/ut/TestMetaBundle.cpp b/tests/ut/TestMetaBundle.cpp new file mode 100644 index 00000000..40f612e1 --- /dev/null +++ b/tests/ut/TestMetaBundle.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 +#include "foundation/utils.h" + +#define private public +#define protected public +#define UNIT_TEST 1 + +#include "pipeline/core/pipeline_core.h" + +namespace OHOS { +namespace Media { +namespace Test { +TEST(Test_meta_bundle, GetNull_before_Update) +{ + Pipeline::MetaBundle bundle; + ASSERT_TRUE(bundle.GetGlobalMeta() == nullptr); + ASSERT_TRUE(bundle.GetStreamMeta(0) == nullptr); +} + +TEST(Test_meta_bundle, GetGlobalMeta_after_Update) +{ + Meta meta; + std::string title("title"); + std::string album("album"); + int32_t channels = 64; + meta.SetString(Media::Plugin::MetaID::MEDIA_TITLE, title); + meta.SetString(Media::Plugin::MetaID::MEDIA_ALBUM, album); + meta.SetInt32(Media::Plugin::MetaID::AUDIO_CHANNELS, channels); + + Pipeline::MetaBundle bundle; + ASSERT_TRUE(bundle.GetGlobalMeta() == nullptr); + ASSERT_TRUE(bundle.GetStreamMeta(0) == nullptr); + bundle.UpdateGlobalMeta(meta); + auto out = bundle.GetGlobalMeta(); + ASSERT_TRUE(out != nullptr); + std::string outString; + ASSERT_TRUE(out->GetString(Media::Plugin::MetaID::MEDIA_TITLE, outString)); + ASSERT_STREQ("title", outString.c_str()); + ASSERT_TRUE(out->GetString(Media::Plugin::MetaID::MEDIA_ALBUM, outString)); + ASSERT_STREQ("album", outString.c_str()); + int32_t oChannel = 0; + ASSERT_TRUE(out->GetInt32(Media::Plugin::MetaID::AUDIO_CHANNELS, oChannel)); + ASSERT_EQ(channels, oChannel); +} + +TEST(Test_meta_bundle, GetStreamMeta_after_Update) +{ + Meta meta; + std::string mime("audio/mpeg"); + std::string language("zh"); + meta.SetString(Media::Plugin::MetaID::MIME, mime); + meta.SetString(Media::Plugin::MetaID::MEDIA_LANGUAGE, language); + + Pipeline::MetaBundle bundle; + ASSERT_TRUE(bundle.GetGlobalMeta() == nullptr); + ASSERT_TRUE(bundle.GetStreamMeta(0) == nullptr); + bundle.UpdateStreamMeta(meta); + auto out = bundle.GetStreamMeta(0); + ASSERT_TRUE(out == nullptr); + meta.SetUint32(Media::Plugin::MetaID::STREAM_INDEX, 1); + bundle.UpdateStreamMeta(meta); + out = bundle.GetStreamMeta(1); + std::string outString; + ASSERT_TRUE(out->GetString(Media::Plugin::MetaID::MIME, outString)); + ASSERT_STREQ("audio/mpeg", outString.c_str()); + ASSERT_TRUE(out->GetString(Media::Plugin::MetaID::MEDIA_LANGUAGE, outString)); + ASSERT_STREQ("zh", outString.c_str()); + int32_t oChannel = 0; + ASSERT_FALSE(out->GetInt32(Media::Plugin::MetaID::AUDIO_CHANNELS, oChannel)); + uint32_t sIndex = 0; + ASSERT_TRUE(out->GetUint32(Media::Plugin::MetaID::STREAM_INDEX, sIndex)); + ASSERT_EQ(sIndex, 1); +} +} // namespace Test +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/tests/ut/TestMimeDefs.cpp b/tests/ut/TestMimeDefs.cpp new file mode 100644 index 00000000..c76ac1dd --- /dev/null +++ b/tests/ut/TestMimeDefs.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 + +#define private public +#define protected public +#define UNIT_TEST 1 + +#include "foundation/constants.h" + +namespace OHOS { +namespace Media { +namespace Test { +TEST(TestDefs, check_mime_defs) +{ + ASSERT_STREQ(MEDIA_MIME_AUDIO_MP3, "audio/mp3"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_FLAC, "audio/flac"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_RAW, "audio/raw"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_APE, "audio/ape"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_WAV, "audio/wav"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_AAC, "audio/aac"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_AAC_ADTS, "audio/aac-adts"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_VORBIS, "audio/vorbis"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_OPUS, "audio/opus"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_AC3, "audio/ac3"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_EAC3, "audio/eac3"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_EAC3_JOC, "audio/eac3-joc"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_AC4, "audio/ac4"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_WMA, "audio/x-ms-wma"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_AMR_NB, "audio/3gpp"); + ASSERT_STREQ(MEDIA_MIME_AUDIO_AMR_WB, "audio/amr-wb"); +} +} // namespace Test +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/tests/ut/TestStateMachine.cpp b/tests/ut/TestStateMachine.cpp new file mode 100644 index 00000000..ae5a43f2 --- /dev/null +++ b/tests/ut/TestStateMachine.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "gtest/gtest.h" +#define private public +#define protected public + +#include "osal/utils/util.h" +#include "player/internal/state_machine.h" +#include "player/play_executor.h" + +namespace OHOS { +namespace Media { +namespace Test { +class PlayExecutorStub : public PlayExecutor { +public: + PlayExecutorStub() : isLooping_(false), stateMachine_(nullptr) + { + } + void SetLooping(bool looping) + { + isLooping_ = looping; + } + void SetStateMachine(const StateMachine* stateMachine) + { + stateMachine_ = stateMachine; + } + ErrorCode DoSetSource(const std::shared_ptr& source) const override + { + return source ? SUCCESS : INVALID_SOURCE; + } + ErrorCode DoOnReady() override + { + return SUCCESS; + } + ErrorCode DoOnComplete() override + { + if (!isLooping_ && stateMachine_) { + stateMachine_->SendEventAsync(STOP); + } + return SUCCESS; + } + ErrorCode DoOnError(ErrorCode) override + { + return SUCCESS; + } + +private: + bool isLooping_; + const StateMachine* stateMachine_; +}; + +PlayExecutorStub g_playExecutorStub; + +class TestStateMachine : public ::testing::Test { +protected: + void SetUp() override + { + g_playExecutorStub.SetStateMachine(&stateMachine); + stateMachine.Start(); + } + + void TearDown() override + { + } + + static StateMachine stateMachine; +}; + +StateMachine TestStateMachine::stateMachine(g_playExecutorStub); + +TEST_F(TestStateMachine, test_init_state) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); +} + +TEST_F(TestStateMachine, test_set_invalid_source) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); + EXPECT_EQ(INVALID_SOURCE, stateMachine.SendEvent(SET_SOURCE)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); +} + +TEST_F(TestStateMachine, test_set_valid_source) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); + auto source = std::make_shared("FakeUri"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(SET_SOURCE, source)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PreparingState"); +} + +TEST_F(TestStateMachine, test_set_notify_error_in_preparing) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "PreparingState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(NOTIFY_ERROR)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); +} + +TEST_F(TestStateMachine, test_notify_ready) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); + auto source = std::make_shared("FakeUri"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(SET_SOURCE, source)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PreparingState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(NOTIFY_READY)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "ReadyState"); +} + +TEST_F(TestStateMachine, test_play_after_ready) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "ReadyState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(PLAY)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PlayingState"); +} + +TEST_F(TestStateMachine, test_pause) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "PlayingState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(PAUSE)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PauseState"); +} + +TEST_F(TestStateMachine, test_play_after_pause) +{ + EXPECT_TRUE(stateMachine.GetCurrentState() == "PauseState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(PLAY)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PlayingState"); +} + +TEST_F(TestStateMachine, test_play_complete_looping) +{ + g_playExecutorStub.SetLooping(true); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PlayingState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(NOTIFY_COMPLETE)); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PlayingState"); +} + +TEST_F(TestStateMachine, test_play_complete_nolooping) +{ + g_playExecutorStub.SetLooping(false); + EXPECT_TRUE(stateMachine.GetCurrentState() == "PlayingState"); + EXPECT_EQ(SUCCESS, stateMachine.SendEvent(NOTIFY_COMPLETE)); + OSAL::SleepFor(100); + EXPECT_TRUE(stateMachine.GetCurrentState() == "InitState"); +} +} // namespace Test +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/tests/ut/TestSynchronizer.cpp b/tests/ut/TestSynchronizer.cpp new file mode 100644 index 00000000..b9a6d52c --- /dev/null +++ b/tests/ut/TestSynchronizer.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 "gtest/gtest.h" +#define private public +#define protected public + +#undef UNIT_TEST + +#include +#include +#include +#include +#include "osal/base/synchronizer.h" +#include "osal/thread/task.h" +#include "osal/utils/util.h" + +namespace OHOS { +namespace Media { +namespace Test { +using namespace OSAL; + +class TestSynchronizer : public ::testing::Test { +public: + void SetUp() override + { + task1 = std::make_shared("workTask1"); + task2 = std::make_shared("workTask2"); + task3 = std::make_shared("workTask3"); + isDataRcved = false; + isStarted = false; + } + + void TearDown() override + { + } + + std::shared_ptr task1; + std::shared_ptr task2; + std::shared_ptr task3; + std::atomic isDataRcved; + std::atomic isStarted; + static Synchronizer synchronizer; +}; + +Synchronizer TestSynchronizer::synchronizer("sync"); + +TEST_F(TestSynchronizer, test_waitfor_fail) +{ + int syncId = 0; + task1->RegisterHandler([this, syncId] { synchronizer.Notify(syncId, 1234); }); + // task1->Start(); + int timeoutMs = 100; + auto start = std::chrono::high_resolution_clock::now(); + auto rtv = synchronizer.WaitFor(syncId, timeoutMs); + auto end = std::chrono::high_resolution_clock::now(); + auto diff = static_cast>(end - start).count() * 1000; + EXPECT_EQ(false, rtv); + EXPECT_TRUE((std::abs(static_cast(diff) - timeoutMs) < 20) || (diff < 5)); + std::cout << "TestSynchronizer time diff: " << diff << std::endl; +} + +TEST_F(TestSynchronizer, test_waitfor_succ) +{ + int syncId = 0; + task1->RegisterHandler([this, syncId] { synchronizer.Notify(syncId, 1234); }); + task1->Start(); + int timeoutMs = 1000; + auto rtv = synchronizer.WaitFor(syncId, timeoutMs); + EXPECT_EQ(true, rtv); +} + +TEST_F(TestSynchronizer, test_waitfor_with_result_succ) +{ + int syncId = 0; + int expect = 1234; + task1->RegisterHandler([this, syncId, expect] { synchronizer.Notify(syncId, expect); }); + task1->Start(); + int timeoutMs = 1000; + int result = 0; + auto rtv = synchronizer.WaitFor(syncId, timeoutMs, result); + EXPECT_EQ(true, rtv); + EXPECT_EQ(expect, result); +} + +TEST_F(TestSynchronizer, test_wait_succ) +{ + int syncId = 0; + int result = 0; + int expect = 1234; + task1->RegisterHandler([this, syncId, &result] { + if (!isDataRcved.load()) { + synchronizer.Wait(syncId, result); + isDataRcved = true; + } + }); + task1->Start(); + task2->RegisterHandler([this, syncId, expect] { synchronizer.Notify(syncId, expect); }); + task2->Start(); + while (!isDataRcved.load()) {} + EXPECT_EQ(expect, result); +} +} // namespace Test +} // namespace Media +} // namespace OHOS \ No newline at end of file -- Gitee From 2d649c64f0aa25f49afbb4753711a42b56391379 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Sat, 25 Sep 2021 09:27:46 +0800 Subject: [PATCH 02/34] support compile and run windows demo. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 48 +++++++++---------- engine/plugin/core/plugin_loader.cpp | 2 +- .../plugins/source/file_source/CMakeLists.txt | 4 +- interface/histreamer/hiplayer.h | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 6e5d170f..febcc25f 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -29,39 +29,39 @@ ENDIF(CMAKE_CL_64) ###################################################### # include directories include_directories( - ${TOP_DIR}/interface - ${TOP_DIR}/engine - ${TOP_DIR}/engine/external - ${TOP_DIR}/engine/player - ${TOP_DIR}/engine/pipeline - ${TOP_DIR}/engine/pipeline/core - ${TOP_DIR}/engine/foundation - ${TOP_DIR}/engine/foundation/osal - ${TOP_DIR}/engine/pipeline/filters - ${TOP_DIR}/engine/pipeline/filters/player - ${TOP_DIR}/engine/plugin - ${TOP_DIR}/engine/3rdparty/curl/include + ${TOP_DIR}/histreamer/interface + ${TOP_DIR}/histreamer/engine + ${TOP_DIR}/histreamer/engine/external + ${TOP_DIR}/histreamer/engine/player + ${TOP_DIR}/histreamer/engine/pipeline + ${TOP_DIR}/histreamer/engine/pipeline/core + ${TOP_DIR}/histreamer/engine/foundation + ${TOP_DIR}/histreamer/engine/foundation/osal + ${TOP_DIR}/histreamer/engine/pipeline/filters + ${TOP_DIR}/histreamer/engine/pipeline/filters/player + ${TOP_DIR}/histreamer/engine/plugin + ${TOP_DIR}/histreamer/engine/3rdparty/curl/include ) ###################################################### #file(GLOB_RECURSE MY_SRCS ./*.cpp) # source files from all subdirectories recursely file(GLOB_RECURSE HISTREAMER_SRCS - ${TOP_DIR}/engine/pipeline/*.cpp - ${TOP_DIR}/engine/plugin/common/*.cpp - ${TOP_DIR}/engine/plugin/core/*.cpp - ${TOP_DIR}/engine/plugin/plugins/ffmpeg_adapter/*.cpp - ${TOP_DIR}/engine/plugin/plugins/sink/*.cpp - ${TOP_DIR}/engine/plugin/plugins/source/file_source/*.cpp - ${TOP_DIR}/engine/plugin/plugins/source/stream_source/*.cpp - ${TOP_DIR}/engine/plugin/types/*.cpp - ${TOP_DIR}/engine/player/*.cpp - ${TOP_DIR}/engine/foundation/*.cpp - ${TOP_DIR}/engine/external/*.cpp + ${TOP_DIR}/histreamer/engine/pipeline/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/common/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/core/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/plugins/ffmpeg_adapter/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/plugins/sink/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/plugins/source/file_source/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/plugins/source/stream_source/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/types/*.cpp + ${TOP_DIR}/histreamer/engine/player/*.cpp + ${TOP_DIR}/histreamer/engine/foundation/*.cpp + ${TOP_DIR}/histreamer/engine/external/*.cpp ) file(GLOB_RECURSE PLUGINS_STATIC_BUILD_SRCS - ${TOP_DIR}/engine/plugin/plugins/source/file_source/*.cpp + ${TOP_DIR}/histreamer/engine/plugin/plugins/source/file_source/*.cpp ) INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../include) diff --git a/engine/plugin/core/plugin_loader.cpp b/engine/plugin/core/plugin_loader.cpp index e52b29eb..af4a5f18 100644 --- a/engine/plugin/core/plugin_loader.cpp +++ b/engine/plugin/core/plugin_loader.cpp @@ -93,7 +93,7 @@ void PluginLoader::UnLoadPluginFile() { if (handler_) { #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) - ::FreeLibrary((HMODULE)handler); + ::FreeLibrary((HMODULE)handler_); #else ::dlclose(const_cast(handler_)); #endif diff --git a/engine/plugin/plugins/source/file_source/CMakeLists.txt b/engine/plugin/plugins/source/file_source/CMakeLists.txt index 866e1186..f7336d3d 100644 --- a/engine/plugin/plugins/source/file_source/CMakeLists.txt +++ b/engine/plugin/plugins/source/file_source/CMakeLists.txt @@ -4,8 +4,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) project(plugin_filesource) include_directories( - ${TOP_DIR}/engine - ${TOP_DIR}/engine/plugin + ${TOP_DIR}/histreamer/engine + ${TOP_DIR}/histreamer/engine/plugin ) set(BUILD_PLUGIN_FILE_SOURCE_SHARED ON) diff --git a/interface/histreamer/hiplayer.h b/interface/histreamer/hiplayer.h index 291f297b..4053d596 100644 --- a/interface/histreamer/hiplayer.h +++ b/interface/histreamer/hiplayer.h @@ -52,8 +52,8 @@ public: int32_t DeInit() override; int32_t SetParameter(const Media::Format& params) override; -private: class HiPlayerImpl; +private: std::shared_ptr player_; Media::PlayerStates curState_; bool isInited_; -- Gitee From 4d73b82372966cf1bdad2171c5703c65868bf0d2 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Sat, 25 Sep 2021 22:14:12 +0800 Subject: [PATCH 03/34] modify tests CMakeLists.txt Signed-off-by: Chen Guodong --- tests/CMakeLists.txt | 5 ++--- tests/ut/CMakeLists.txt | 46 ++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 67544a4f..8a63e2ea 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,12 +1,11 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) -#PROJECT(mediapipeline_test) +#PROJECT(histreamer_tests) set(CMAKE_VERBOSE_MAKEFILE ON) -set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) -set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src) +set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) if(NOT MINGW) # support findSymbol, cmake >= 3.13 diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index 6b9e97a6..df9ef3c2 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -1,5 +1,8 @@ -include(${TOP_DIR}/engine/CMakeLists.txt) +include(${TOP_DIR}/histreamer/engine/CMakeLists.txt) + +set(GTEST_DIRS ${TOP_DIR}/3rdparty/gtest) +set(MOCKCPP_DIR ${TOP_DIR}/3rdparty/mockcpp) include_directories( ${GTEST_DIRS}/include @@ -8,18 +11,21 @@ include_directories( if (MINGW) +set(ffmpeg_lib_path ${TOP_DIR}/3rdparty/ffmpeg/windows/lib) +set(ffmpeg_inc_path ${TOP_DIR}/3rdparty/ffmpeg/windows/include) +set(sdl_lib_path ${TOP_DIR}/3rdparty/SDL2.0/windows/lib/x64) +set(sdl_inc_path ${TOP_DIR}/3rdparty/SDL2.0/include) +else() +endif() + include_directories( - ${TOP_DIR}/engine/demo/windows/ffmpeg/include - ${TOP_DIR}/engine/demo/windows/SDL2.0/include + ${ffmpeg_inc_path} + ${sdl_inc_path} ) link_directories( - ${TOP_DIR}/engine/demo/windows/ffmpeg/lib - ${TOP_DIR}/engine/demo/windows/SDL2.0/lib/x64 + ${ffmpeg_lib_path} + ${sdl_lib_path} ) -else() -include_directories( -) -endif() file(GLOB UT_TEST_SRCS ./*.cpp) @@ -29,28 +35,26 @@ set(SRC ../main.cpp ) -add_executable(MediaPlayerUtTests ${SRC}) +add_executable(HiStreamerUtTests ${SRC}) link_directories( ${GTEST_DIRS}/lib/ ${MOCKCPP_DIR}/lib/ /usr/local/lib - ${TOP_DIR}/engine/demo/linux/ffmpeg/lib - ${TOP_DIR}/engine/demo/linux/SDL2.0/lib ) -target_compile_definitions(MediaPlayerUtTests PRIVATE UNIT_TEST) +target_compile_definitions(HiStreamerUtTests PRIVATE UNIT_TEST) if (MSVC) - target_link_libraries(MediaPlayerUtTests + target_link_libraries(HiStreamerUtTests ${GTEST_DIRS}/lib/gtestd.lib ${MOCKCPP_DIR}/lib/mockcpp.lib pthreadVC2.lib ) elseif (MINGW) - set(ffmpeg_path ${TOP_DIR}/engine/demo/windows/ffmpeg) - set(sdl_path ${TOP_DIR}/engine/demo/windows/SDL2.0) - target_link_libraries(MediaPlayerUtTests + set(ffmpeg_path ${TOP_DIR}/3rdparty/ffmpeg/windows) + set(sdl_path ${TOP_DIR}/3rdparty/SDL2.0/windows) + target_link_libraries(HiStreamerUtTests # dl ${sdl_path}/lib/x64/SDL2.lib ${ffmpeg_path}/lib/avcodec.lib @@ -69,9 +73,9 @@ elseif (MINGW) file(COPY ${ffmpeg_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) file(COPY ${sdl_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) else () - set(ffmpeg_path ${TOP_DIR}/engine/demo/linux/ffmpeg) - set(sdl_path ${TOP_DIR}/engine/demo/linux/SDL2.0) - target_link_libraries(MediaPlayerUtTests + set(ffmpeg_path ${TOP_DIR}/3rdparty/ffmpeg/linux) + set(sdl_path ${TOP_DIR}/3rdparty/SDL2.0/linux) + target_link_libraries(HiStreamerUtTests dl ${ffmpeg_path}/lib/libavformat.a ${ffmpeg_path}/lib/libavcodec.a @@ -89,5 +93,5 @@ else () ${MOCKCPP_DIR}/lib/libmockcpp.a ) endif () -add_test(Test MediaPlayerUtTests) +add_test(Test HiStreamerUtTests) enable_testing() -- Gitee From 5cbee8cb90fbd1b521005035d1cde9b37dbf70bf Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Sun, 26 Sep 2021 08:39:46 +0800 Subject: [PATCH 04/34] fix tests compile issue. Signed-off-by: Chen Guodong --- tests/ut/CMakeLists.txt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index df9ef3c2..6fa50471 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -9,13 +9,17 @@ include_directories( ${MOCKCPP_DIR}/include ) +set(ffmpeg_inc_path ${TOP_DIR}/3rdparty/ffmpeg/windows/include) +set(sdl_inc_path ${TOP_DIR}/3rdparty/SDL2.0/include) if (MINGW) set(ffmpeg_lib_path ${TOP_DIR}/3rdparty/ffmpeg/windows/lib) -set(ffmpeg_inc_path ${TOP_DIR}/3rdparty/ffmpeg/windows/include) set(sdl_lib_path ${TOP_DIR}/3rdparty/SDL2.0/windows/lib/x64) -set(sdl_inc_path ${TOP_DIR}/3rdparty/SDL2.0/include) +set(gtest_lib_path ${GTEST_DIRS}/mingw64/lib/) else() +set(ffmpeg_lib_path ${TOP_DIR}/3rdparty/ffmpeg/linux/lib) +set(sdl_lib_path ${TOP_DIR}/3rdparty/SDL2.0/linux/lib) +set(gtest_lib_path ${GTEST_DIRS}/linux/lib/) endif() include_directories( @@ -25,6 +29,7 @@ include_directories( link_directories( ${ffmpeg_lib_path} ${sdl_lib_path} + ${gtest_lib_path} ) file(GLOB UT_TEST_SRCS ./*.cpp) @@ -32,13 +37,13 @@ file(GLOB UT_TEST_SRCS ./*.cpp) set(SRC ${HISTREAMER_SRCS} ${UT_TEST_SRCS} + ${3RDPARTY_SRCS} ../main.cpp ) add_executable(HiStreamerUtTests ${SRC}) link_directories( - ${GTEST_DIRS}/lib/ ${MOCKCPP_DIR}/lib/ /usr/local/lib ) @@ -64,7 +69,7 @@ elseif (MINGW) ${ffmpeg_path}/lib/avdevice.lib ${ffmpeg_path}/lib/avfilter.lib ${ffmpeg_path}/lib/swscale.lib - ${GTEST_DIRS}/lib/libgtest.a + gtest ${MOCKCPP_DIR}/lib/libmockcpp_mingw8.a ) message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") @@ -86,9 +91,10 @@ else () ${ffmpeg_path}/lib/libswresample.a ${ffmpeg_path}/lib/liblzma.a m - z + #z + /lib/x86_64-linux-gnu/libz.so.1 ${sdl_path}/lib/libSDL2.a - ${GTEST_DIRS}/lib/libgtest.a + gtest pthread ${MOCKCPP_DIR}/lib/libmockcpp.a ) -- Gitee From c0d4c703d7523930476d3ad06f389c6cde81e476 Mon Sep 17 00:00:00 2001 From: huchang Date: Sun, 26 Sep 2021 10:26:44 +0800 Subject: [PATCH 05/34] fix compile problem: media_common not found Signed-off-by: Hu Chang --- BUILD.gn | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 5353cc6f..424f1576 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -13,7 +13,7 @@ # declare_args() { - compile_histreamer = false + compile_histreamer = true } if (compile_histreamer) { @@ -114,7 +114,6 @@ if (compile_histreamer) { ldflags += [ "-lavcodec" ] ldflags += [ "-lavutil" ] ldflags += [ "-lhdi_videodisplayer" ] - ldflags += [ "-lmedia_common" ] deps = [ "//foundation/graphic/surface:lite_surface", @@ -122,7 +121,7 @@ if (compile_histreamer) { "//device/hisilicon/modules/middleware:middleware_source_sdk", ] public_deps = [ - #"//foundation/multimedia/utils/lite:media_common", + "//foundation/multimedia/utils/lite:media_common", #"//kernel/liteos_m/kal/posix:posix", "//third_party/bounds_checking_function:libsec_shared", ] -- Gitee From 220b15e3f3e7c37ce3fade9284c39e4c2bc970d5 Mon Sep 17 00:00:00 2001 From: liyong12 Date: Sun, 26 Sep 2021 15:07:51 +0800 Subject: [PATCH 06/34] fix ut tests for linux. Signed-off-by: Li Yong --- CMakeLists.txt | 3 +- engine/CMakeLists.txt | 48 +++++++++---------- .../plugins/source/file_source/CMakeLists.txt | 4 +- tests/CMakeLists.txt | 9 ++-- tests/main.cpp | 3 ++ tests/ut/CMakeLists.txt | 39 +++++++-------- tests/ut/TestAny.cpp | 2 +- tests/ut/TestBitReader.cpp | 12 ++--- 8 files changed, 62 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e21b48c..3296d085 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,11 +11,12 @@ endif () set(CMAKE_CXX_STANDARD 11) -set(TOP_DIR ${CMAKE_SOURCE_DIR}) +set(TOP_DIR ${PROJECT_SOURCE_DIR}) add_definitions(-DOSAL_OHOS) # add -DVIDEO_SUPPORT to enable video play set(CMAKE_VERBOSE_MAKEFILE ON) add_subdirectory(engine/plugin/plugins/source/file_source) +add_subdirectory(engine) add_subdirectory(tests) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index febcc25f..6e5d170f 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -29,39 +29,39 @@ ENDIF(CMAKE_CL_64) ###################################################### # include directories include_directories( - ${TOP_DIR}/histreamer/interface - ${TOP_DIR}/histreamer/engine - ${TOP_DIR}/histreamer/engine/external - ${TOP_DIR}/histreamer/engine/player - ${TOP_DIR}/histreamer/engine/pipeline - ${TOP_DIR}/histreamer/engine/pipeline/core - ${TOP_DIR}/histreamer/engine/foundation - ${TOP_DIR}/histreamer/engine/foundation/osal - ${TOP_DIR}/histreamer/engine/pipeline/filters - ${TOP_DIR}/histreamer/engine/pipeline/filters/player - ${TOP_DIR}/histreamer/engine/plugin - ${TOP_DIR}/histreamer/engine/3rdparty/curl/include + ${TOP_DIR}/interface + ${TOP_DIR}/engine + ${TOP_DIR}/engine/external + ${TOP_DIR}/engine/player + ${TOP_DIR}/engine/pipeline + ${TOP_DIR}/engine/pipeline/core + ${TOP_DIR}/engine/foundation + ${TOP_DIR}/engine/foundation/osal + ${TOP_DIR}/engine/pipeline/filters + ${TOP_DIR}/engine/pipeline/filters/player + ${TOP_DIR}/engine/plugin + ${TOP_DIR}/engine/3rdparty/curl/include ) ###################################################### #file(GLOB_RECURSE MY_SRCS ./*.cpp) # source files from all subdirectories recursely file(GLOB_RECURSE HISTREAMER_SRCS - ${TOP_DIR}/histreamer/engine/pipeline/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/common/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/core/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/plugins/ffmpeg_adapter/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/plugins/sink/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/plugins/source/file_source/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/plugins/source/stream_source/*.cpp - ${TOP_DIR}/histreamer/engine/plugin/types/*.cpp - ${TOP_DIR}/histreamer/engine/player/*.cpp - ${TOP_DIR}/histreamer/engine/foundation/*.cpp - ${TOP_DIR}/histreamer/engine/external/*.cpp + ${TOP_DIR}/engine/pipeline/*.cpp + ${TOP_DIR}/engine/plugin/common/*.cpp + ${TOP_DIR}/engine/plugin/core/*.cpp + ${TOP_DIR}/engine/plugin/plugins/ffmpeg_adapter/*.cpp + ${TOP_DIR}/engine/plugin/plugins/sink/*.cpp + ${TOP_DIR}/engine/plugin/plugins/source/file_source/*.cpp + ${TOP_DIR}/engine/plugin/plugins/source/stream_source/*.cpp + ${TOP_DIR}/engine/plugin/types/*.cpp + ${TOP_DIR}/engine/player/*.cpp + ${TOP_DIR}/engine/foundation/*.cpp + ${TOP_DIR}/engine/external/*.cpp ) file(GLOB_RECURSE PLUGINS_STATIC_BUILD_SRCS - ${TOP_DIR}/histreamer/engine/plugin/plugins/source/file_source/*.cpp + ${TOP_DIR}/engine/plugin/plugins/source/file_source/*.cpp ) INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../include) diff --git a/engine/plugin/plugins/source/file_source/CMakeLists.txt b/engine/plugin/plugins/source/file_source/CMakeLists.txt index f7336d3d..866e1186 100644 --- a/engine/plugin/plugins/source/file_source/CMakeLists.txt +++ b/engine/plugin/plugins/source/file_source/CMakeLists.txt @@ -4,8 +4,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) project(plugin_filesource) include_directories( - ${TOP_DIR}/histreamer/engine - ${TOP_DIR}/histreamer/engine/plugin + ${TOP_DIR}/engine + ${TOP_DIR}/engine/plugin ) set(BUILD_PLUGIN_FILE_SOURCE_SHARED ON) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8a63e2ea..998bbe07 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,15 +1,14 @@ - CMAKE_MINIMUM_REQUIRED(VERSION 3.13...3.20) -#PROJECT(histreamer_tests) +PROJECT(histreamer_tests) set(CMAKE_VERBOSE_MAKEFILE ON) -set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) -if(NOT MINGW) +if (NOT MINGW) # support findSymbol, cmake >= 3.13 # ADD_LINK_OPTIONS( -rdynamic) -endif() +endif () ADD_SUBDIRECTORY(ut) diff --git a/tests/main.cpp b/tests/main.cpp index 50fe3431..109013b9 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -13,7 +13,10 @@ * limitations under the License. */ +#include "gtest/gtest.h" int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index 6fa50471..84cb589a 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -1,25 +1,25 @@ -include(${TOP_DIR}/histreamer/engine/CMakeLists.txt) +include(${TOP_DIR}/engine/CMakeLists.txt) -set(GTEST_DIRS ${TOP_DIR}/3rdparty/gtest) -set(MOCKCPP_DIR ${TOP_DIR}/3rdparty/mockcpp) +set(GTEST_DIRS ${THIRD_PARTY_DIR}/gtest) +set(MOCKCPP_DIR ${THIRD_PARTY_DIR}/mockcpp) include_directories( - ${GTEST_DIRS}/include + ${GTEST_ROOT_DIR}/include ${MOCKCPP_DIR}/include ) -set(ffmpeg_inc_path ${TOP_DIR}/3rdparty/ffmpeg/windows/include) -set(sdl_inc_path ${TOP_DIR}/3rdparty/SDL2.0/include) +set(ffmpeg_inc_path ${THIRD_PARTY_DIR}/ffmpeg/windows/include) +set(sdl_inc_path ${THIRD_PARTY_DIR}/SDL2.0/include) if (MINGW) -set(ffmpeg_lib_path ${TOP_DIR}/3rdparty/ffmpeg/windows/lib) -set(sdl_lib_path ${TOP_DIR}/3rdparty/SDL2.0/windows/lib/x64) -set(gtest_lib_path ${GTEST_DIRS}/mingw64/lib/) +set(ffmpeg_lib_path ${THIRD_PARTY_DIR}/ffmpeg/windows/lib) +set(sdl_lib_path ${THIRD_PARTY_DIR}/SDL2.0/windows/lib/x64) +set(gtest_lib_path ${GTEST_ROOT_DIR}/mingw64/lib/) else() -set(ffmpeg_lib_path ${TOP_DIR}/3rdparty/ffmpeg/linux/lib) -set(sdl_lib_path ${TOP_DIR}/3rdparty/SDL2.0/linux/lib) -set(gtest_lib_path ${GTEST_DIRS}/linux/lib/) +set(ffmpeg_lib_path ${THIRD_PARTY_DIR}/ffmpeg/linux/lib) +set(sdl_lib_path ${THIRD_PARTY_DIR}/SDL2.0/linux/lib) +set(gtest_lib_path ${GTEST_ROOT_DIR}/linux/lib/) endif() include_directories( @@ -52,13 +52,13 @@ target_compile_definitions(HiStreamerUtTests PRIVATE UNIT_TEST) if (MSVC) target_link_libraries(HiStreamerUtTests - ${GTEST_DIRS}/lib/gtestd.lib + ${GTEST_ROOT_DIR}/lib/gtestd.lib ${MOCKCPP_DIR}/lib/mockcpp.lib pthreadVC2.lib ) elseif (MINGW) - set(ffmpeg_path ${TOP_DIR}/3rdparty/ffmpeg/windows) - set(sdl_path ${TOP_DIR}/3rdparty/SDL2.0/windows) + set(ffmpeg_path ${THIRD_PARTY_DIR}/ffmpeg/windows) + set(sdl_path ${THIRD_PARTY_DIR}/SDL2.0/windows) target_link_libraries(HiStreamerUtTests # dl ${sdl_path}/lib/x64/SDL2.lib @@ -78,8 +78,8 @@ elseif (MINGW) file(COPY ${ffmpeg_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) file(COPY ${sdl_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) else () - set(ffmpeg_path ${TOP_DIR}/3rdparty/ffmpeg/linux) - set(sdl_path ${TOP_DIR}/3rdparty/SDL2.0/linux) + set(ffmpeg_path ${THIRD_PARTY_DIR}/ffmpeg/linux) + set(sdl_path ${THIRD_PARTY_DIR}/SDL2.0/linux) target_link_libraries(HiStreamerUtTests dl ${ffmpeg_path}/lib/libavformat.a @@ -91,10 +91,11 @@ else () ${ffmpeg_path}/lib/libswresample.a ${ffmpeg_path}/lib/liblzma.a m - #z - /lib/x86_64-linux-gnu/libz.so.1 + z +# /lib/x86_64-linux-gnu/libz.so.1 ${sdl_path}/lib/libSDL2.a gtest + gtest_main pthread ${MOCKCPP_DIR}/lib/libmockcpp.a ) diff --git a/tests/ut/TestAny.cpp b/tests/ut/TestAny.cpp index 34e9bfde..2f3db83c 100644 --- a/tests/ut/TestAny.cpp +++ b/tests/ut/TestAny.cpp @@ -14,7 +14,7 @@ */ #include -#include +#include "gtest/gtest.h" #include #include #define private public diff --git a/tests/ut/TestBitReader.cpp b/tests/ut/TestBitReader.cpp index 93cb0344..e992f7bb 100644 --- a/tests/ut/TestBitReader.cpp +++ b/tests/ut/TestBitReader.cpp @@ -42,7 +42,7 @@ TEST_F(TestBitReader, test_read_bits) { const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; bitReader.Reset(data, sizeof(data)); - int val = 0; + uint32_t val = 0; EXPECT_EQ(bitReader.ReadBits(4, val), true); EXPECT_EQ(val, 0xf); @@ -60,7 +60,7 @@ TEST_F(TestBitReader, test_skip_bits) { const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; bitReader.Reset(data, sizeof(data)); - int val = 0; + uint32_t val = 0; EXPECT_EQ(bitReader.ReadBits(4, val), true); EXPECT_EQ(val, 0xf); bitReader.SkipBits(4); @@ -75,13 +75,13 @@ TEST_F(TestBitReader, test_show_bits) { const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; bitReader.Reset(data, sizeof(data)); - int val = 0; + uint32_t val = 0; EXPECT_EQ(bitReader.ReadBits(4, val), true); EXPECT_EQ(val, 0xf); - EXPECT_EQ(bitReader.PeekBits(4, val), true); + EXPECT_EQ(bitReader.PeekBits(4, val), true); EXPECT_EQ(val, 0xf); bitReader.SkipBits(4); - EXPECT_EQ(bitReader.PeekBits(8, val), true); + EXPECT_EQ(bitReader.PeekBits(8, val), true); EXPECT_EQ(val, 0x11); EXPECT_EQ(bitReader.ReadBits(8, val), true); EXPECT_EQ(val, 0x11); @@ -91,7 +91,7 @@ TEST_F(TestBitReader, test_seek_bits) { const uint8_t data[] = {0xff, 0x11, 0x22, 0x33}; bitReader.Reset(data, sizeof(data)); - int val = 0; + uint32_t val = 0; EXPECT_EQ(bitReader.ReadBits(4, val), true); EXPECT_EQ(val, 0xf); bitReader.SkipBits(20); -- Gitee From 4a7bfdb6ad6f50aa6486b9e6301b0f7c087648ae Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Sun, 26 Sep 2021 19:50:54 +0800 Subject: [PATCH 07/34] fix copy dll to wrong binary directory issue. Signed-off-by: Chen Guodong --- tests/ut/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index 84cb589a..54cce664 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -75,8 +75,8 @@ elseif (MINGW) message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") file(GLOB ffmpeg_shared_libraries ${ffmpeg_path}/bin/*.dll) file(GLOB sdl_shared_libraries ${sdl_path}/lib/x64/*.dll) - file(COPY ${ffmpeg_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) - file(COPY ${sdl_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/tests/ut) + file(COPY ${ffmpeg_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/_deps/histreamer-build/tests/ut) + file(COPY ${sdl_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/_deps/histreamer-build/tests/ut) else () set(ffmpeg_path ${THIRD_PARTY_DIR}/ffmpeg/linux) set(sdl_path ${THIRD_PARTY_DIR}/SDL2.0/linux) -- Gitee From 375a0684127b84f914eb52ae782bf10405ffccf8 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Mon, 27 Sep 2021 15:23:07 +0800 Subject: [PATCH 08/34] restore old BUILD.gn Signed-off-by: Chen Guodong --- BUILD.gn | 178 +++++++------------------------------------- histreamer_BUILD_gn | 152 +++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 152 deletions(-) create mode 100644 histreamer_BUILD_gn diff --git a/BUILD.gn b/BUILD.gn index 424f1576..2a24a0e9 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,152 +1,26 @@ -# Copyright (c) 2021-2021 Huawei Device Co., Ltd. -# 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. -# - -declare_args() { - compile_histreamer = true -} - -if (compile_histreamer) { - import("//build/lite/config/component/lite_component.gni") - import("//build/lite/ndk/ndk.gni") - - gcc_arm_path = getenv("GCCARM") - - print("gcc_arm_path: ", gcc_arm_path) - shared_library("histreamer") { - sources = [ - "engine/foundation/osal/thread/task.cpp", - "engine/foundation/osal/thread/condition_variable.cpp", - "engine/foundation/osal/thread/mutex.cpp", - "engine/foundation/osal/thread/scoped_lock.cpp", - "engine/foundation/osal/thread/thread.cpp", - "engine/foundation/osal/utils/util.cpp", - "engine/foundation/meta.cpp", - "engine/foundation/constants.cpp", - "engine/pipeline/filters/codec/decoder_filter_base.cpp", - "engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp", - "engine/pipeline/filters/common/plugin_utils.cpp", - "engine/pipeline/filters/demux/demuxer_filter.cpp", - "engine/pipeline/filters/demux/type_finder.cpp", - "engine/pipeline/filters/demux/data_packer.cpp", - "engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp", - "engine/pipeline/filters/source/media_source_filter.cpp", - "engine/pipeline/factory/filter_factory.cpp", - "engine/pipeline/core/compatible_check.cpp", - "engine/pipeline/core/filter_base.cpp", - "engine/pipeline/core/pipeline_core.cpp", - "engine/pipeline/core/port.cpp", - "engine/player/internal/state.cpp", - "engine/player/internal/state_machine.cpp", - "engine/player/hiplayer.cpp", - "engine/player/hiplayer_impl.cpp", - "engine/plugin/common/plugin_buffer.cpp", - "engine/plugin/core/base.cpp", - "engine/plugin/core/audio_sink.cpp", - "engine/plugin/core/video_sink.cpp", - "engine/plugin/core/codec.cpp", - "engine/plugin/core/demuxer.cpp", - "engine/plugin/core/plugin_loader.cpp", - "engine/plugin/core/plugin_wrapper.cpp", - "engine/plugin/core/plugin_manager.cpp", - "engine/plugin/core/plugin_register.cpp", - "engine/plugin/core/source.cpp", - "engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp", - "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp", - "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp", - "engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp", - "engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp", - "engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp", - "engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp", - "engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp", - "engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp", - "engine/plugin/plugins/source/file_source/file_source_plugin.cpp", - "engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp", - ] - defines = [ "__NEED_wint_t", "OSAL_OHOS", "__STDC_FORMAT_MACROS", "__HOS_LITE_SINK__", "HI3516DV300" ] - cflags = [ "-O2", "-fPIC", "-Wall" ] - cflags_cc = cflags - include_dirs = [ - "interface", - "engine", - "engine/foundation", - "engine/foundation/osal", - "engine/pipeline", - "engine/player", - "engine/pipeline/filters", - "engine/pipeline/core", - "engine/plugin", - "${gcc_arm_path}/../arm-none-eabi/include", - "//foundation/multimedia/media_lite/interfaces", - "//foundation/multimedia/drivers/audio/include", - "//foundation/multimedia/drivers/codec/include", - "//foundation/multimedia/drivers/format/include", - "//foundation/multimedia/utils/lite/interfaces/kits", - "//foundation/multimedia/media_lite/interfaces/kits/player_lite", - "//base/hiviewdfx/hilog_lite/interfaces/native/kits/hilog", - "//kernel/liteos_m/kal/posix/include/", - "//kernel/liteos_m/kernel/include", - "//kernel/liteos_m/kernel/arch/include", - "//drivers/peripheral/audio/interfaces/include", - "//drivers/peripheral/base", - "//drivers/peripheral/audio/hal/hdi_binder/proxy/include", - "//drivers/peripheral/audio/interfaces/include", - "//third_party/ffmpeg", - "//third_party/bounds_checking_function/include", - ] - outdir = rebase_path("$root_out_dir") - #public_configs = [ ":player_external_library_config" ] - ldflags = [ "-L$outdir" ] - ldflags += [ "-laudio_hw" ] - ldflags += [ "-lcodec" ] - ldflags += [ "-lformat_hw" ] - ldflags += [ "-lavformat" ] - ldflags += [ "-lavcodec" ] - ldflags += [ "-lavutil" ] - ldflags += [ "-lhdi_videodisplayer" ] - - deps = [ - "//foundation/graphic/surface:lite_surface", - "//device/hisilicon/hardware:hardware_media_sdk", - "//device/hisilicon/modules/middleware:middleware_source_sdk", - ] - public_deps = [ - "//foundation/multimedia/utils/lite:media_common", - #"//kernel/liteos_m/kal/posix:posix", - "//third_party/bounds_checking_function:libsec_shared", - ] - } - - # for test only. - copy("music_files") { - sources = [ - "//foundation/multimedia/histreamer/tests/resource/dream_it_possible.mp3", - ] - outputs = ["$root_out_dir/data/{{source_file_part}}"] - } - -} else { - import("//build/ohos.gni") - - group("histreamer_packages") { - deps = [ - "//third_party/glib:glib_packages", - "//third_party/gstreamer/gst_libav:gst_libav_packages", - "//third_party/gstreamer/gstplugins_bad:gstplugins_bad_packages", - "//third_party/gstreamer/gstplugins_base:gstplugins_base_packages", - "//third_party/gstreamer/gstplugins_good:gstplugins_good_packages", - "//third_party/gstreamer/gstreamer:gstreamer_packages", - "//third_party/libffi:ffi", - ] - } -} +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") + +group("histreamer_packages") { + deps = [ + "//third_party/glib:glib_packages", + "//third_party/gstreamer/gst_libav:gst_libav_packages", + "//third_party/gstreamer/gstplugins_bad:gstplugins_bad_packages", + "//third_party/gstreamer/gstplugins_base:gstplugins_base_packages", + "//third_party/gstreamer/gstplugins_good:gstplugins_good_packages", + "//third_party/gstreamer/gstreamer:gstreamer_packages", + "//third_party/libffi:ffi", + ] +} diff --git a/histreamer_BUILD_gn b/histreamer_BUILD_gn new file mode 100644 index 00000000..424f1576 --- /dev/null +++ b/histreamer_BUILD_gn @@ -0,0 +1,152 @@ +# Copyright (c) 2021-2021 Huawei Device Co., Ltd. +# 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. +# + +declare_args() { + compile_histreamer = true +} + +if (compile_histreamer) { + import("//build/lite/config/component/lite_component.gni") + import("//build/lite/ndk/ndk.gni") + + gcc_arm_path = getenv("GCCARM") + + print("gcc_arm_path: ", gcc_arm_path) + shared_library("histreamer") { + sources = [ + "engine/foundation/osal/thread/task.cpp", + "engine/foundation/osal/thread/condition_variable.cpp", + "engine/foundation/osal/thread/mutex.cpp", + "engine/foundation/osal/thread/scoped_lock.cpp", + "engine/foundation/osal/thread/thread.cpp", + "engine/foundation/osal/utils/util.cpp", + "engine/foundation/meta.cpp", + "engine/foundation/constants.cpp", + "engine/pipeline/filters/codec/decoder_filter_base.cpp", + "engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp", + "engine/pipeline/filters/common/plugin_utils.cpp", + "engine/pipeline/filters/demux/demuxer_filter.cpp", + "engine/pipeline/filters/demux/type_finder.cpp", + "engine/pipeline/filters/demux/data_packer.cpp", + "engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp", + "engine/pipeline/filters/source/media_source_filter.cpp", + "engine/pipeline/factory/filter_factory.cpp", + "engine/pipeline/core/compatible_check.cpp", + "engine/pipeline/core/filter_base.cpp", + "engine/pipeline/core/pipeline_core.cpp", + "engine/pipeline/core/port.cpp", + "engine/player/internal/state.cpp", + "engine/player/internal/state_machine.cpp", + "engine/player/hiplayer.cpp", + "engine/player/hiplayer_impl.cpp", + "engine/plugin/common/plugin_buffer.cpp", + "engine/plugin/core/base.cpp", + "engine/plugin/core/audio_sink.cpp", + "engine/plugin/core/video_sink.cpp", + "engine/plugin/core/codec.cpp", + "engine/plugin/core/demuxer.cpp", + "engine/plugin/core/plugin_loader.cpp", + "engine/plugin/core/plugin_wrapper.cpp", + "engine/plugin/core/plugin_manager.cpp", + "engine/plugin/core/plugin_register.cpp", + "engine/plugin/core/source.cpp", + "engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp", + "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp", + "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp", + "engine/plugin/plugins/ffmpeg_adapter/utils/bit_reader.cpp", + "engine/plugin/plugins/ffmpeg_adapter/utils/aac_audio_config_parser.cpp", + "engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.cpp", + "engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp", + "engine/plugin/plugins/hdi_adapter/sink/ring_buffer.cpp", + "engine/plugin/plugins/hdi_adapter/utils/hdi_au_utils.cpp", + "engine/plugin/plugins/source/file_source/file_source_plugin.cpp", + "engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp", + ] + defines = [ "__NEED_wint_t", "OSAL_OHOS", "__STDC_FORMAT_MACROS", "__HOS_LITE_SINK__", "HI3516DV300" ] + cflags = [ "-O2", "-fPIC", "-Wall" ] + cflags_cc = cflags + include_dirs = [ + "interface", + "engine", + "engine/foundation", + "engine/foundation/osal", + "engine/pipeline", + "engine/player", + "engine/pipeline/filters", + "engine/pipeline/core", + "engine/plugin", + "${gcc_arm_path}/../arm-none-eabi/include", + "//foundation/multimedia/media_lite/interfaces", + "//foundation/multimedia/drivers/audio/include", + "//foundation/multimedia/drivers/codec/include", + "//foundation/multimedia/drivers/format/include", + "//foundation/multimedia/utils/lite/interfaces/kits", + "//foundation/multimedia/media_lite/interfaces/kits/player_lite", + "//base/hiviewdfx/hilog_lite/interfaces/native/kits/hilog", + "//kernel/liteos_m/kal/posix/include/", + "//kernel/liteos_m/kernel/include", + "//kernel/liteos_m/kernel/arch/include", + "//drivers/peripheral/audio/interfaces/include", + "//drivers/peripheral/base", + "//drivers/peripheral/audio/hal/hdi_binder/proxy/include", + "//drivers/peripheral/audio/interfaces/include", + "//third_party/ffmpeg", + "//third_party/bounds_checking_function/include", + ] + outdir = rebase_path("$root_out_dir") + #public_configs = [ ":player_external_library_config" ] + ldflags = [ "-L$outdir" ] + ldflags += [ "-laudio_hw" ] + ldflags += [ "-lcodec" ] + ldflags += [ "-lformat_hw" ] + ldflags += [ "-lavformat" ] + ldflags += [ "-lavcodec" ] + ldflags += [ "-lavutil" ] + ldflags += [ "-lhdi_videodisplayer" ] + + deps = [ + "//foundation/graphic/surface:lite_surface", + "//device/hisilicon/hardware:hardware_media_sdk", + "//device/hisilicon/modules/middleware:middleware_source_sdk", + ] + public_deps = [ + "//foundation/multimedia/utils/lite:media_common", + #"//kernel/liteos_m/kal/posix:posix", + "//third_party/bounds_checking_function:libsec_shared", + ] + } + + # for test only. + copy("music_files") { + sources = [ + "//foundation/multimedia/histreamer/tests/resource/dream_it_possible.mp3", + ] + outputs = ["$root_out_dir/data/{{source_file_part}}"] + } + +} else { + import("//build/ohos.gni") + + group("histreamer_packages") { + deps = [ + "//third_party/glib:glib_packages", + "//third_party/gstreamer/gst_libav:gst_libav_packages", + "//third_party/gstreamer/gstplugins_bad:gstplugins_bad_packages", + "//third_party/gstreamer/gstplugins_base:gstplugins_base_packages", + "//third_party/gstreamer/gstplugins_good:gstplugins_good_packages", + "//third_party/gstreamer/gstreamer:gstreamer_packages", + "//third_party/libffi:ffi", + ] + } +} -- Gitee From 19edc845d662152c1313d647034139097123621d Mon Sep 17 00:00:00 2001 From: wuyouqian Date: Mon, 27 Sep 2021 08:31:14 +0000 Subject: [PATCH 09/34] !2 clean video code Signed-off-by: wuyouqian --- .../codec/video_decoder/video_decoder_filter.cpp | 14 +++++++------- .../filters/sink/video_sink/video_sink_filter.cpp | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp index 912e7d8b..dfdcf82a 100644 --- a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp +++ b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp @@ -36,7 +36,7 @@ static uint32_t VIDEO_ALIGN_SIZE = 16; template static constexpr T AlignUp(T num, U alignment) { - return alignment > 0 ? ((num + static_cast(alignment) - 1) & (~(static_cast(alignment) - 1))) : num; + return (alignment > 0) ? ((num + static_cast(alignment) - 1) & (~(static_cast(alignment) - 1))) : num; } static AutoRegisterFilter g_registerFilterHelper("builtin.player.videodecoder"); @@ -116,7 +116,7 @@ ErrorCode VideoDecoderFilter::Prepare() } if (!outBufQue_) { outBufQue_ = std::make_shared>("decoderFilterOutBufQue", - DEFAULT_IN_BUFFER_POOL_SIZE); + DEFAULT_OUT_BUFFER_POOL_SIZE); } else { outBufQue_->SetActive(true); } @@ -126,7 +126,7 @@ ErrorCode VideoDecoderFilter::Prepare() } if (!inBufQue_) { inBufQue_ = std::make_shared>("decoderFilterInBufQue", - DEFAULT_OUT_BUFFER_POOL_SIZE); + DEFAULT_IN_BUFFER_POOL_SIZE); } else { inBufQue_->SetActive(true); } @@ -160,7 +160,7 @@ bool VideoDecoderFilter::Negotiate(const std::string& inPort, const std::shared_ } if (Configure(inMeta) != ErrorCode::SUCCESS) { MEDIA_LOG_E("decoder configure error"); - Event event{ + Event event { .type = EVENT_ERROR, .param = err, }; @@ -196,7 +196,7 @@ ErrorCode VideoDecoderFilter::AllocateOutputBuffers() AlignUp(vdecFormat_.height, VIDEO_ALIGN_SIZE) * VIDEO_PIX_DEPTH); MEDIA_LOG_D("Output buffer size: %u", bufferSize); } else { - // TODO: need to check video sink support and calc buffer size + // need to check video sink support and calc buffer size MEDIA_LOG_E("Unsupported video pixel format: %d", vdecFormat_.format); return ErrorCode::UNIMPLEMENT; } @@ -319,7 +319,7 @@ ErrorCode VideoDecoderFilter::Configure(const std::shared_ptr& meta) pushTask_->Start(); state_ = FilterState::READY; - Event event{ + Event event { .type = EVENT_READY, }; OnEvent(event); @@ -443,4 +443,4 @@ void VideoDecoderFilter::OnOutputBufferDone(const std::shared_ptr &buf } } } -#endif \ No newline at end of file +#endif diff --git a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp index 9901eada..cd6a7110 100644 --- a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp +++ b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp @@ -106,7 +106,7 @@ bool VideoSinkFilter::Negotiate(const std::string &inPort, const std::shared_ptr err = ConfigureLocked(inMeta); if (err != ErrorCode::SUCCESS) { MEDIA_LOG_E("sink configure error"); - Event event{ + Event event { .type = EventType::EVENT_ERROR, .param = err, }; @@ -207,7 +207,7 @@ ErrorCode VideoSinkFilter::PushData(const std::string &inPort, AVBufferPtr buffe } if (buffer->GetMemory()->GetSize() == 0) { - Event event{ + Event event { .type = EVENT_COMPLETE, }; MEDIA_LOG_D("video sink push data send event_complete"); -- Gitee From aafef1558089d05b8affed11b0d96be902a542be Mon Sep 17 00:00:00 2001 From: ancimoon Date: Mon, 27 Sep 2021 08:50:01 +0000 Subject: [PATCH 10/34] =?UTF-8?q?!3=20=E6=B7=BB=E5=8A=A0=E9=9F=B3=E9=87=8F?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=B9=B6=E5=B0=86=E6=97=A5=E5=BF=97=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E5=88=B0hilog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hu Change --- engine/foundation/log.h | 15 ++++++++++ .../sink/audio_sink/audio_sink_filter.cpp | 10 +++++++ .../sink/audio_sink/audio_sink_filter.h | 2 ++ engine/player/hiplayer.cpp | 29 ++++++++++++++++++- engine/player/hiplayer_impl.cpp | 12 +++++--- engine/player/hiplayer_impl.h | 6 ++-- engine/plugin/core/audio_sink.cpp | 5 ++++ engine/plugin/core/audio_sink.h | 2 ++ .../plugins/hdi_adapter/sink/hos_au_sink.cpp | 4 +++ histreamer_BUILD_gn | 5 ++-- 10 files changed, 80 insertions(+), 10 deletions(-) diff --git a/engine/foundation/log.h b/engine/foundation/log.h index 89917948..62289592 100644 --- a/engine/foundation/log.h +++ b/engine/foundation/log.h @@ -23,6 +23,11 @@ #include "error_code.h" +#ifdef HI3516DV300 +#include "hilog/log.h" +#include "media_log.h" +#endif + inline std::string MediaGetFileName(std::string file) { if (file == "") { @@ -55,12 +60,22 @@ inline std::string MediaGetFileName(std::string file) #define MEDIA_LOG_D(msg, ...) ((void)0) #endif + +#ifdef HI3516DV300 +#define MEDIA_LOG_E(msg, ...) MEDIA_ERR_LOG(msg, ##__VA_ARGS__) +#define MEDIA_LOG_W(msg, ...) MEDIA_WARNING_LOG(msg, ##__VA_ARGS__) +#if !LOG_NDEBUG +#define MEDIA_LOG_I(msg, ...) MEDIA_INFO_LOG(msg, ##__VA_ARGS__) +#define MEDIA_LOG_D(msg, ...) MEDIA_DEBUG_LOG(msg, ##__VA_ARGS__) +#endif +#else #define MEDIA_LOG_E(msg, ...) MEDIA_LOG_MESSAGE("ERROR", msg, ##__VA_ARGS__) #define MEDIA_LOG_W(msg, ...) MEDIA_LOG_MESSAGE("WARN", msg, ##__VA_ARGS__) #if !LOG_NDEBUG #define MEDIA_LOG_I(msg, ...) MEDIA_LOG_MESSAGE("INFO", msg, ##__VA_ARGS__) #define MEDIA_LOG_D(msg, ...) MEDIA_LOG_MESSAGE("DEBUG", msg, ##__VA_ARGS__) #endif +#endif #ifndef FAIL_RETURN #define FAIL_RETURN(exec) \ diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp index 602feb41..56e31704 100644 --- a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp @@ -285,6 +285,16 @@ void AudioSinkFilter::FlushEnd() MEDIA_LOG_D("FlushEnd entered"); isFlushing = false; } + +ErrorCode AudioSinkFilter::SetVolume(float volume) +{ + if (state_ != FilterState::READY && state_ != FilterState::RUNNING && state_ != FilterState::PAUSED) { + MEDIA_LOG_E("audio sink filter cannot set volume in state %d", state_.load()); + return ERROR_STATE; + } + MEDIA_LOG_W("set volume %.3f", volume); + return TranslatePluginStatus(plugin_->SetVolume(volume)); +} } // namespace Pipeline } // namespace Media } // namespace OHOS diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h index 1cf8142f..ccb9726f 100644 --- a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h @@ -56,6 +56,8 @@ public: ErrorCode GetCurrentTime(int64_t& time) const; + ErrorCode SetVolume(float volume); + private: ErrorCode SetPluginParameter(Tag tag, const Plugin::ValueType& value); ErrorCode ConfigureToPreparePlugin(const std::shared_ptr& meta); diff --git a/engine/player/hiplayer.cpp b/engine/player/hiplayer.cpp index 8212db9d..d0907b6e 100644 --- a/engine/player/hiplayer.cpp +++ b/engine/player/hiplayer.cpp @@ -20,6 +20,10 @@ #include #include "hiplayer_impl.h" +namespace { +const float MAX_MEDIA_VOLUME = 300.0f; +} + namespace OHOS { namespace Media { HiPlayer::HiPlayer() @@ -114,7 +118,30 @@ int32_t HiPlayer::Rewind(int64_t mSeconds, int32_t mode) int32_t HiPlayer::SetVolume(float leftVolume, float rightVolume) { - return 0; + if ((curState_ != Media::PlayerStates::PLAYER_STARTED) && (curState_ != Media::PlayerStates::PLAYER_PAUSED) && + (curState_ != Media::PlayerStates::PLAYER_PREPARED)) { + MEDIA_LOG_E("cannot set volume in state %d", curState_); + return -1; + } + if (leftVolume < 0 || leftVolume > MAX_MEDIA_VOLUME || rightVolume < 0 || rightVolume > MAX_MEDIA_VOLUME) { + MEDIA_LOG_E("volume not valid, should be in range [0,300]"); + return -1; + } + float volume = 0.f; + if (leftVolume < 1e-6 && rightVolume >= 1e-6) { // 1e-6 + volume = rightVolume; + } else if (rightVolume < 1e-6 && leftVolume >= 1e-6) { // 1e-6 + volume = leftVolume; + } else { + volume = (leftVolume + rightVolume) / 2; // 2 + } + volume /= MAX_MEDIA_VOLUME; // normalize to 0~1 + int ret = -1; // -1 + MEDIA_LOG_W("set volume %.3f", volume); + if (player_ && player_->SetVolume(volume) == SUCCESS) { + ret = 0; + } + return ret; } int32_t HiPlayer::SetSurface(Surface* surface) diff --git a/engine/player/hiplayer_impl.cpp b/engine/player/hiplayer_impl.cpp index 8eebe11d..3a9ecb72 100644 --- a/engine/player/hiplayer_impl.cpp +++ b/engine/player/hiplayer_impl.cpp @@ -285,17 +285,21 @@ ErrorCode HiPlayer::HiPlayerImpl::GetDuration(size_t& time) const return NOT_FOUND; } -ErrorCode HiPlayer::HiPlayerImpl::SetVolume(float leftVolume, float rightVolume) +ErrorCode HiPlayer::HiPlayerImpl::SetVolume(float volume) { - return UNIMPLEMENT; + if (audioSink != nullptr) { + return audioSink->SetVolume(volume); + } + MEDIA_LOG_W("cannot set volume while audio sink filter is null"); + return NULL_POINTER_ERROR; } -ErrorCode HiPlayer::HiPlayerImpl::SetCallback(const shared_ptr& callback) +ErrorCode HiPlayer::HiPlayerImpl::SetCallback(const std::shared_ptr& callback) { return SUCCESS; } -ErrorCode HiPlayer::HiPlayerImpl::SetCallback(const shared_ptr& callback) +ErrorCode HiPlayer::HiPlayerImpl::SetCallback(const std::shared_ptr& callback) { // todo this interface should be protected by mutex in case of multi-thread runtime callback_ = callback; diff --git a/engine/player/hiplayer_impl.h b/engine/player/hiplayer_impl.h index 55f5bdd3..fa13b38e 100644 --- a/engine/player/hiplayer_impl.h +++ b/engine/player/hiplayer_impl.h @@ -86,11 +86,11 @@ public: ErrorCode GetStreamCnt(size_t& cnt) const; ErrorCode GetStreamMeta(size_t index, std::shared_ptr& meta) const; - ErrorCode SetVolume(float leftVolume, float rightVolume); + ErrorCode SetVolume(float volume); - ErrorCode SetCallback(const shared_ptr& callback); + ErrorCode SetCallback(const std::shared_ptr& callback); - ErrorCode SetCallback(const shared_ptr& callback); + ErrorCode SetCallback(const std::shared_ptr& callback); void OnStateChanged(std::string state) override; diff --git a/engine/plugin/core/audio_sink.cpp b/engine/plugin/core/audio_sink.cpp index 6c396ac2..c3b457e1 100644 --- a/engine/plugin/core/audio_sink.cpp +++ b/engine/plugin/core/audio_sink.cpp @@ -43,3 +43,8 @@ Status AudioSink::Write(const std::shared_ptr& input) { return audioSink->Write(input); } + +Status AudioSink::SetVolume(float volume) +{ + return audioSink->SetVolume(volume); +} diff --git a/engine/plugin/core/audio_sink.h b/engine/plugin/core/audio_sink.h index 608b701e..6886d4c0 100644 --- a/engine/plugin/core/audio_sink.h +++ b/engine/plugin/core/audio_sink.h @@ -43,6 +43,8 @@ public: Status Write(const std::shared_ptr &input); + Status SetVolume(float volume); + private: friend class PluginManager; diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp index 0f304a40..47247efd 100644 --- a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp @@ -42,6 +42,7 @@ constexpr int32_t HI_ERR_VI_BUF_FULL = 0xA016800F; constexpr int32_t RANK100 = 100; constexpr int32_t HALF = 2; constexpr int32_t SEC_TO_MILLS = 1000; +constexpr float MAX_VOLUME = 300.f; Status LoadAndInitAdapter(AudioManager *proxyManager, AudioAdapterDescriptor *descriptor, AudioAdapter **adapter) @@ -487,6 +488,7 @@ Status HdiSink::GetVolume(float &volume) MEDIA_LOG_E("get volume failed"); return Status::ERROR_UNKNOWN; } + volume /= MAX_VOLUME; return Status::OK; } @@ -497,10 +499,12 @@ Status HdiSink::SetVolume(float volume) MEDIA_LOG_W("no render available, set volume must be called after prepare"); return Status::ERROR_WRONG_STATE; } + volume *= MAX_VOLUME; if (audioRender_->volume.SetVolume(audioRender_, volume) != 0) { MEDIA_LOG_E("set volume failed"); return Status::ERROR_UNKNOWN; } + MEDIA_LOG_W("set volume to %.3f", volume); return Status::OK; } diff --git a/histreamer_BUILD_gn b/histreamer_BUILD_gn index 424f1576..e80e2d41 100644 --- a/histreamer_BUILD_gn +++ b/histreamer_BUILD_gn @@ -87,13 +87,13 @@ if (compile_histreamer) { "engine/pipeline/core", "engine/plugin", "${gcc_arm_path}/../arm-none-eabi/include", + "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits", "//foundation/multimedia/media_lite/interfaces", "//foundation/multimedia/drivers/audio/include", "//foundation/multimedia/drivers/codec/include", "//foundation/multimedia/drivers/format/include", "//foundation/multimedia/utils/lite/interfaces/kits", "//foundation/multimedia/media_lite/interfaces/kits/player_lite", - "//base/hiviewdfx/hilog_lite/interfaces/native/kits/hilog", "//kernel/liteos_m/kal/posix/include/", "//kernel/liteos_m/kernel/include", "//kernel/liteos_m/kernel/arch/include", @@ -121,6 +121,7 @@ if (compile_histreamer) { "//device/hisilicon/modules/middleware:middleware_source_sdk", ] public_deps = [ + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", "//foundation/multimedia/utils/lite:media_common", #"//kernel/liteos_m/kal/posix:posix", "//third_party/bounds_checking_function:libsec_shared", @@ -149,4 +150,4 @@ if (compile_histreamer) { "//third_party/libffi:ffi", ] } -} +} \ No newline at end of file -- Gitee From 6d099c304b74d844f3eea018a2de957e3f212013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=9C=E5=A8=83?= Date: Tue, 28 Sep 2021 02:16:46 +0000 Subject: [PATCH 11/34] !4 Normalized header file reference Signed-off-by: Shihaojun --- engine/plugin/common/plugin_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/plugin/common/plugin_types.h b/engine/plugin/common/plugin_types.h index 1c047cd2..40a311bf 100644 --- a/engine/plugin/common/plugin_types.h +++ b/engine/plugin/common/plugin_types.h @@ -20,7 +20,7 @@ #include #include #include -#include "common/any.h" +#include "any.h" namespace OHOS { namespace Media { -- Gitee From 1ce94ceab7cb5be67a66f37fa63527ff4d84578d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=9C=E5=A8=83?= Date: Tue, 28 Sep 2021 08:08:26 +0000 Subject: [PATCH 12/34] =?UTF-8?q?!5=20Modify=20the=20build=20gn=EF=BC=8Cco?= =?UTF-8?q?mpile=20the=20plug-in=20interface=20and=20plug-in=20framework?= =?UTF-8?q?=20to=20a=20separate=20so?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Shihaojun --- histreamer_BUILD_gn | 69 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/histreamer_BUILD_gn b/histreamer_BUILD_gn index e80e2d41..6907770e 100644 --- a/histreamer_BUILD_gn +++ b/histreamer_BUILD_gn @@ -50,17 +50,6 @@ if (compile_histreamer) { "engine/player/internal/state_machine.cpp", "engine/player/hiplayer.cpp", "engine/player/hiplayer_impl.cpp", - "engine/plugin/common/plugin_buffer.cpp", - "engine/plugin/core/base.cpp", - "engine/plugin/core/audio_sink.cpp", - "engine/plugin/core/video_sink.cpp", - "engine/plugin/core/codec.cpp", - "engine/plugin/core/demuxer.cpp", - "engine/plugin/core/plugin_loader.cpp", - "engine/plugin/core/plugin_wrapper.cpp", - "engine/plugin/core/plugin_manager.cpp", - "engine/plugin/core/plugin_register.cpp", - "engine/plugin/core/source.cpp", "engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp", "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp", "engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.cpp", @@ -119,6 +108,8 @@ if (compile_histreamer) { "//foundation/graphic/surface:lite_surface", "//device/hisilicon/hardware:hardware_media_sdk", "//device/hisilicon/modules/middleware:middleware_source_sdk", + ":histreamer_plugin_intf", + ":histreamer_plugin_core", ] public_deps = [ "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", @@ -136,6 +127,60 @@ if (compile_histreamer) { outputs = ["$root_out_dir/data/{{source_file_part}}"] } + # lib plugin interface + shared_library("histreamer_plugin_intf") { + sources = [ + "engine/plugin/common/plugin_buffer.cpp", + ] + cflags = [ "-O2", "-fPIC", "-Wall" ] + cflags_cc = cflags + outdir = rebase_path("$root_out_dir") + ldflags = [ "-L$outdir" ] + public_deps = [ + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", + "//third_party/bounds_checking_function:libsec_shared", + ] + } + + + # lib plugin core + shared_library("histreamer_plugin_core") { + sources = [ + "engine/plugin/core/base.cpp", + "engine/plugin/core/audio_sink.cpp", + "engine/plugin/core/video_sink.cpp", + "engine/plugin/core/codec.cpp", + "engine/plugin/core/demuxer.cpp", + "engine/plugin/core/plugin_loader.cpp", + "engine/plugin/core/plugin_wrapper.cpp", + "engine/plugin/core/plugin_manager.cpp", + "engine/plugin/core/plugin_register.cpp", + "engine/plugin/core/source.cpp", + ] + defines = [ "HI3516DV300" ] + include_dirs = [ + "engine", + "engine/foundation", + "engine/foundation/osal", + "engine/pipeline", + "engine/player", + "engine/pipeline/filters", + "engine/pipeline/core", + "engine/plugin", + ] + cflags = [ "-O2", "-fPIC", "-Wall" ] + cflags_cc = cflags + outdir = rebase_path("$root_out_dir") + ldflags = [ "-L$outdir" ] + deps = [ + ":histreamer_plugin_intf", + ] + public_deps = [ + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", + "//third_party/bounds_checking_function:libsec_shared", + ] + } + } else { import("//build/ohos.gni") @@ -150,4 +195,4 @@ if (compile_histreamer) { "//third_party/libffi:ffi", ] } -} \ No newline at end of file +} -- Gitee From 46810155fabbb00ade82e2cfebafb8d9cfa7d189 Mon Sep 17 00:00:00 2001 From: leo_ysl <9693773+leo_ysl@user.noreply.gitee.com> Date: Tue, 28 Sep 2021 08:29:50 +0000 Subject: [PATCH 13/34] !6 replace atomic with mutex protected operation due to incomplete support for int64_t. Signed-off-by: Li Yong --- engine/CMakeLists.txt | 3 ++ engine/foundation/osal/thread/task.cpp | 2 +- engine/foundation/osal/thread/task.h | 2 +- .../pipeline/filters/demux/demuxer_filter.cpp | 13 ++++++-- .../pipeline/filters/demux/demuxer_filter.h | 6 +++- .../sink/audio_sink/audio_sink_filter.cpp | 8 +---- .../sink/audio_sink/audio_sink_filter.h | 4 --- .../sink/video_sink/video_sink_filter.cpp | 9 +----- .../sink/video_sink/video_sink_filter.h | 3 -- tests/ut/CMakeLists.txt | 30 +++++++++---------- 10 files changed, 37 insertions(+), 43 deletions(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 6e5d170f..92d3ab85 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -15,6 +15,7 @@ IF(UNIX AND NOT CYGWIN) ADD_DEFINITIONS( -fPIC ) +#set(CMAKE_CXX_FLAGS "-g -ggdb3 -O0 -std=c++11 -Wall -Wl,--disable-new-dtags") ENDIF(UNIX AND NOT CYGWIN) ADD_DEFINITIONS( @@ -65,3 +66,5 @@ file(GLOB_RECURSE PLUGINS_STATIC_BUILD_SRCS ) INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../include) + +add_library(${PROJECT_NAME} SHARED ${HISTREAMER_SRCS} ${PLUGINS_STATIC_BUILD_SRCS}) \ No newline at end of file diff --git a/engine/foundation/osal/thread/task.cpp b/engine/foundation/osal/thread/task.cpp index 8f28cf11..a81ee5a4 100644 --- a/engine/foundation/osal/thread/task.cpp +++ b/engine/foundation/osal/thread/task.cpp @@ -24,7 +24,7 @@ namespace OHOS { namespace Media { namespace OSAL { Task::Task(std::string name) - : name_(std::move(name)), loop_([this] { Run(); }), runningState_(PAUSED), pauseDone_(false), workInProgress_(false) + : name_(std::move(name)), runningState_(PAUSED), loop_([this] { Run(); }), pauseDone_(false), workInProgress_(false) { MEDIA_LOG_D("task %s ctor called", name_.c_str()); loop_.SetName(name_); diff --git a/engine/foundation/osal/thread/task.h b/engine/foundation/osal/thread/task.h index 9ac3c089..e13b0bfb 100644 --- a/engine/foundation/osal/thread/task.h +++ b/engine/foundation/osal/thread/task.h @@ -60,9 +60,9 @@ private: void Run(); const std::string name_; - OSAL::Thread loop_; std::atomic runningState_ {PAUSED}; std::function handler_ = [this] { DoTask(); }; + OSAL::Thread loop_; OSAL::Mutex stateMutex_ {}; OSAL::Mutex cvMutex_ {}; diff --git a/engine/pipeline/filters/demux/demuxer_filter.cpp b/engine/pipeline/filters/demux/demuxer_filter.cpp index e063157d..9a55be7e 100644 --- a/engine/pipeline/filters/demux/demuxer_filter.cpp +++ b/engine/pipeline/filters/demux/demuxer_filter.cpp @@ -187,7 +187,7 @@ ErrorCode DemuxerFilter::Prepare() } else { ActivatePushMode(); } - curTimeUs_ = 0; + SetCurrentTime(0); state_ = FilterState::PREPARING; return SUCCESS; } @@ -238,13 +238,14 @@ std::shared_ptr DemuxerFilter::GetGlobalMetaInfo() const ErrorCode DemuxerFilter::GetCurrentTime(int64_t& time) const { + OSAL::ScopedLock lock(timeMutex_); time = curTimeUs_ / 1000; // time in milliseconds return SUCCESS; } void DemuxerFilter::Reset() { - curTimeUs_ = 0; + SetCurrentTime(0); mediaMetaData_.globalMeta.reset(); mediaMetaData_.trackMetas.clear(); mediaMetaData_.trackInfos.clear(); @@ -400,7 +401,7 @@ int DemuxerFilter::ReadFrame(AVBuffer& buffer, uint32_t& streamIndex) auto rtv = plugin_->ReadFrame(buffer, 0); if (rtv == Plugin::Status::OK) { streamIndex = buffer.streamID; - curTimeUs_ = buffer.pts; + SetCurrentTime(buffer.pts); result = SUCCESS; } MEDIA_LOG_D("ReadFrame return with rtv = %d", static_cast(rtv)); @@ -469,6 +470,12 @@ void DemuxerFilter::DemuxerLoop() } } } + +void DemuxerFilter::SetCurrentTime(int64_t timestampUsec) +{ + OSAL::ScopedLock lock(timeMutex_); + curTimeUs_ = timestampUsec; +} } // namespace Pipeline } // namespace Media } // namespace OHOS diff --git a/engine/pipeline/filters/demux/demuxer_filter.h b/engine/pipeline/filters/demux/demuxer_filter.h index e6158528..d6f6eb1e 100644 --- a/engine/pipeline/filters/demux/demuxer_filter.h +++ b/engine/pipeline/filters/demux/demuxer_filter.h @@ -24,6 +24,7 @@ #include "foundation/meta.h" #include "foundation/type_define.h" #include "foundation/utils.h" +#include "foundation/osal/thread/mutex.h" #include "plugin/core/demuxer.h" #include "thread/task.h" #include "type_finder.h" @@ -114,6 +115,8 @@ private: void DemuxerLoop(); + void SetCurrentTime(int64_t timestampUsec); + std::string uriSuffix_; uint64_t mediaDataSize_; std::shared_ptr task_; @@ -131,7 +134,8 @@ private: std::function peekRange_; std::function getRange_; - std::atomic curTimeUs_; + mutable OSAL::Mutex timeMutex_; + int64_t curTimeUs_; }; } // namespace Pipeline } // namespace Media diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp index 56e31704..354ddb9a 100644 --- a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp @@ -25,7 +25,7 @@ namespace Media { namespace Pipeline { static AutoRegisterFilter g_registerFilterHelper("builtin.player.audiosink"); -AudioSinkFilter::AudioSinkFilter(const std::string& name) : FilterBase(name), curPos_(0) +AudioSinkFilter::AudioSinkFilter(const std::string& name) : FilterBase(name) { MEDIA_LOG_I("AudioSinkFilter ctor"); } @@ -115,11 +115,6 @@ bool AudioSinkFilter::Negotiate(const std::string& inPort, const std::shared_ptr return true; } -ErrorCode AudioSinkFilter::GetCurrentTime(int64_t& time) const -{ - time = curPos_; - return SUCCESS; -} ErrorCode AudioSinkFilter::ConfigureWithMeta(const std::shared_ptr& meta) { uint32_t channels; @@ -203,7 +198,6 @@ ErrorCode AudioSinkFilter::PushData(const std::string& inPort, AVBufferPtr buffe MEDIA_LOG_D("audio sink push data end"); return SUCCESS; } - curPos_ = buffer->pts; auto err = TranslatePluginStatus(plugin_->Write(buffer)); RETURN_ERR_MESSAGE_LOG_IF_FAIL(err, "audio sink write failed"); MEDIA_LOG_D("audio sink push data end"); diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h index ccb9726f..d9815a7e 100644 --- a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.h @@ -53,9 +53,6 @@ public: void FlushStart() override; void FlushEnd() override; - - ErrorCode GetCurrentTime(int64_t& time) const; - ErrorCode SetVolume(float volume); private: @@ -69,7 +66,6 @@ private: bool isFlushing {false}; OSAL::ConditionVariable startWorkingCondition_ {}; OSAL::Mutex mutex_ {}; - std::atomic curPos_; std::shared_ptr plugin_ {nullptr}; std::shared_ptr targetPluginInfo_ {}; diff --git a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp index cd6a7110..c3581117 100644 --- a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp +++ b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp @@ -30,7 +30,7 @@ static AutoRegisterFilter g_registerFilterHelper("builtin.playe const uint32_t VSINK_DEFAULT_BUFFER_NUM = 8; -VideoSinkFilter::VideoSinkFilter(const std::string &name) : FilterBase(name), curPos_(0) +VideoSinkFilter::VideoSinkFilter(const std::string &name) : FilterBase(name) { MEDIA_LOG_I("VideoSinkFilter ctor called..."); } @@ -116,12 +116,6 @@ bool VideoSinkFilter::Negotiate(const std::string &inPort, const std::shared_ptr return true; } -ErrorCode VideoSinkFilter::GetCurrentTime(uint64_t &time) const -{ - time = curPos_; - return ErrorCode::SUCCESS; -} - ErrorCode VideoSinkFilter::ConfigurePluginParams(const std::shared_ptr &meta) { auto err = ErrorCode::SUCCESS; @@ -215,7 +209,6 @@ ErrorCode VideoSinkFilter::PushData(const std::string &inPort, AVBufferPtr buffe MEDIA_LOG_D("video sink push data end"); return ErrorCode::SUCCESS; } - curPos_ = buffer->pts; inBufQueue_->Push(buffer); MEDIA_LOG_D("video sink push data end"); return ErrorCode::SUCCESS; diff --git a/engine/pipeline/filters/sink/video_sink/video_sink_filter.h b/engine/pipeline/filters/sink/video_sink/video_sink_filter.h index 6a00308c..d07eaba3 100644 --- a/engine/pipeline/filters/sink/video_sink/video_sink_filter.h +++ b/engine/pipeline/filters/sink/video_sink/video_sink_filter.h @@ -58,8 +58,6 @@ public: void FlushStart() override; void FlushEnd() override; - ErrorCode GetCurrentTime(uint64_t &time) const; - private: ErrorCode ConfigurePluginParams(const std::shared_ptr &meta); ErrorCode ConfigureLocked(const std::shared_ptr &meta); @@ -73,7 +71,6 @@ private: bool isFlushing_ {false}; OSAL::ConditionVariable startWorkingCondition_ {}; OSAL::Mutex mutex_; - std::atomic curPos_; std::shared_ptr plugin_ {nullptr}; std::shared_ptr pluginInfo_ {nullptr}; diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index 54cce664..8c8971cb 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -17,9 +17,12 @@ set(ffmpeg_lib_path ${THIRD_PARTY_DIR}/ffmpeg/windows/lib) set(sdl_lib_path ${THIRD_PARTY_DIR}/SDL2.0/windows/lib/x64) set(gtest_lib_path ${GTEST_ROOT_DIR}/mingw64/lib/) else() -set(ffmpeg_lib_path ${THIRD_PARTY_DIR}/ffmpeg/linux/lib) -set(sdl_lib_path ${THIRD_PARTY_DIR}/SDL2.0/linux/lib) -set(gtest_lib_path ${GTEST_ROOT_DIR}/linux/lib/) + set(ffmpeg_lib_path ${THIRD_PARTY_DIR}/ffmpeg/linux/lib) + set(ffmpeg_inc_path ${THIRD_PARTY_DIR}/ffmpeg/linux/include) + set(sdl_lib_path ${THIRD_PARTY_DIR}/SDL2.0/linux/lib) + set(sdl_inc_path ${THIRD_PARTY_DIR}/SDL2.0/linux/include) + set(gtest_lib_path ${THIRD_PARTY_DIR}/gtest/linux/lib) + message(STATUS "ut ${ffmpeg_lib_path}") endif() include_directories( @@ -78,22 +81,19 @@ elseif (MINGW) file(COPY ${ffmpeg_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/_deps/histreamer-build/tests/ut) file(COPY ${sdl_shared_libraries} DESTINATION ${CMAKE_BINARY_DIR}/_deps/histreamer-build/tests/ut) else () - set(ffmpeg_path ${THIRD_PARTY_DIR}/ffmpeg/linux) - set(sdl_path ${THIRD_PARTY_DIR}/SDL2.0/linux) target_link_libraries(HiStreamerUtTests dl - ${ffmpeg_path}/lib/libavformat.a - ${ffmpeg_path}/lib/libavcodec.a - ${ffmpeg_path}/lib/libavdevice.a - ${ffmpeg_path}/lib/libavfilter.a - ${ffmpeg_path}/lib/libavutil.a - ${ffmpeg_path}/lib/libswscale.a - ${ffmpeg_path}/lib/libswresample.a - ${ffmpeg_path}/lib/liblzma.a + ${ffmpeg_lib_path}/libavformat.a + ${ffmpeg_lib_path}/libavcodec.a + ${ffmpeg_lib_path}/libavdevice.a + ${ffmpeg_lib_path}/libavfilter.a + ${ffmpeg_lib_path}/libavutil.a + ${ffmpeg_lib_path}/libswscale.a + ${ffmpeg_lib_path}/libswresample.a + ${ffmpeg_lib_path}/liblzma.a m z -# /lib/x86_64-linux-gnu/libz.so.1 - ${sdl_path}/lib/libSDL2.a + ${sdl_lib_path}/libSDL2.a gtest gtest_main pthread -- Gitee From 6974e4601ceee28a26f89b0f799e0576eebaab7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=9C=E5=A8=83?= Date: Tue, 28 Sep 2021 09:15:13 +0000 Subject: [PATCH 14/34] !8 Add dynamic plugin support compile option Signed-off-by: Shihaojun --- engine/plugin/core/plugin_register.cpp | 10 +++++----- histreamer_BUILD_gn | 12 ++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/engine/plugin/core/plugin_register.cpp b/engine/plugin/core/plugin_register.cpp index c14ec659..af18b0db 100644 --- a/engine/plugin/core/plugin_register.cpp +++ b/engine/plugin/core/plugin_register.cpp @@ -275,15 +275,12 @@ void PluginRegister::RegisterStaticPlugins() void PluginRegister::RegisterDynamicPlugins() { -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) - RegisterPluginsFromPath("."); - RegisterPluginsFromPath(".\\plugins"); - RegisterPluginsFromPath("..\\src\\plugin\\plugins\\source\\file_source"); -#endif + RegisterPluginsFromPath("/usr/lib/plugins"); } void PluginRegister::RegisterPluginsFromPath(const char* libDirPath) { +#ifdef DYNAMIC_PLUGINS DIR* libDir = opendir(libDirPath); if (libDir) { struct dirent* lib = nullptr; @@ -308,16 +305,19 @@ void PluginRegister::RegisterPluginsFromPath(const char* libDirPath) } closedir(libDir); } +#endif } void PluginRegister::UnregisterAllPlugins() { UnregisterPluginStatic(); +#ifdef DYNAMIC_PLUGINS for (auto& loader : registeredLoaders) { EraseRegisteredPlugins(loader); loader->FetchUnregisterFunction()(); loader.reset(); } +#endif registeredLoaders.clear(); } diff --git a/histreamer_BUILD_gn b/histreamer_BUILD_gn index 6907770e..9845a4e9 100644 --- a/histreamer_BUILD_gn +++ b/histreamer_BUILD_gn @@ -142,6 +142,9 @@ if (compile_histreamer) { ] } + declare_args() { + plugin_dynamic_register = false + } # lib plugin core shared_library("histreamer_plugin_core") { @@ -151,13 +154,18 @@ if (compile_histreamer) { "engine/plugin/core/video_sink.cpp", "engine/plugin/core/codec.cpp", "engine/plugin/core/demuxer.cpp", - "engine/plugin/core/plugin_loader.cpp", + "engine/plugin/core/plugin_register.cpp", "engine/plugin/core/plugin_wrapper.cpp", "engine/plugin/core/plugin_manager.cpp", - "engine/plugin/core/plugin_register.cpp", "engine/plugin/core/source.cpp", ] defines = [ "HI3516DV300" ] + if (plugin_dynamic_register) { + sources += [ + "engine/plugin/core/plugin_loader.cpp", + ] + defines += [ "DYNAMIC_PLUGINS" ] + } include_dirs = [ "engine", "engine/foundation", -- Gitee From 1a6df0e308294347af4e36649bef07d9298ac75b Mon Sep 17 00:00:00 2001 From: ancimoon Date: Tue, 28 Sep 2021 09:19:24 +0000 Subject: [PATCH 15/34] !9 remove useless comment in TestAny.cpp and TestSynchronizer.cpp Signed-off-by: Hu Change --- tests/ut/TestAny.cpp | 2 -- tests/ut/TestSynchronizer.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/tests/ut/TestAny.cpp b/tests/ut/TestAny.cpp index 2f3db83c..2909f2fa 100644 --- a/tests/ut/TestAny.cpp +++ b/tests/ut/TestAny.cpp @@ -167,14 +167,12 @@ public: TEST(AnyTest, testAnyEmplace) { Any celestial; - // (1) emplace( Args&&... args ); celestial.Emplace("Procyon", 2943); Star star1("Procyon", 2943); const auto* star = AnyCast(&celestial); ASSERT_TRUE(star1 == (*star)); Any av; - // (2) emplace( std::initializer_list il, Args&&... args ); av.Emplace>({'C', '+', '+', '1', '7'}); const auto* va = AnyCast>(&av); std::vector vector1({'C', '+', '+', '1', '7'}); diff --git a/tests/ut/TestSynchronizer.cpp b/tests/ut/TestSynchronizer.cpp index b9a6d52c..41b9fac0 100644 --- a/tests/ut/TestSynchronizer.cpp +++ b/tests/ut/TestSynchronizer.cpp @@ -61,7 +61,6 @@ TEST_F(TestSynchronizer, test_waitfor_fail) { int syncId = 0; task1->RegisterHandler([this, syncId] { synchronizer.Notify(syncId, 1234); }); - // task1->Start(); int timeoutMs = 100; auto start = std::chrono::high_resolution_clock::now(); auto rtv = synchronizer.WaitFor(syncId, timeoutMs); -- Gitee From a15e0b55ddc201cd91879461734240985e7c2a92 Mon Sep 17 00:00:00 2001 From: leo_ysl <9693773+leo_ysl@user.noreply.gitee.com> Date: Wed, 29 Sep 2021 06:50:37 +0000 Subject: [PATCH 16/34] !12 remove code for creating thread in a constructor. Signed-off-by: Li Yong --- engine/foundation/osal/thread/task.cpp | 8 +++-- engine/foundation/osal/thread/thread.cpp | 42 ++++++++++++++++-------- engine/foundation/osal/thread/thread.h | 9 ++--- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/engine/foundation/osal/thread/task.cpp b/engine/foundation/osal/thread/task.cpp index a81ee5a4..bd876be4 100644 --- a/engine/foundation/osal/thread/task.cpp +++ b/engine/foundation/osal/thread/task.cpp @@ -24,7 +24,7 @@ namespace OHOS { namespace Media { namespace OSAL { Task::Task(std::string name) - : name_(std::move(name)), runningState_(PAUSED), loop_([this] { Run(); }), pauseDone_(false), workInProgress_(false) + : name_(std::move(name)), runningState_(PAUSED), loop_(), pauseDone_(false), workInProgress_(false) { MEDIA_LOG_D("task %s ctor called", name_.c_str()); loop_.SetName(name_); @@ -48,7 +48,11 @@ void Task::Start() #ifndef START_FAKE_TASK OSAL::ScopedLock lock(stateMutex_); runningState_ = STARTED; - cv_.NotifyOne(); + if (!loop_ && !loop_.CreateThread([this] { Run(); })) { + MEDIA_LOG_E("task %s create failed", name_.c_str()); + } else { + cv_.NotifyOne(); + } MEDIA_LOG_D("task %s start called", name_.c_str()); #endif } diff --git a/engine/foundation/osal/thread/thread.cpp b/engine/foundation/osal/thread/thread.cpp index c3b4c779..0654a76a 100644 --- a/engine/foundation/osal/thread/thread.cpp +++ b/engine/foundation/osal/thread/thread.cpp @@ -24,14 +24,12 @@ namespace OSAL { Thread::Thread() noexcept : id_(), name_(), state_() { } + Thread::Thread(Thread&& other) noexcept { *this = std::move(other); } -Thread::Thread(const std::function &func) -{ - CreateThread(func); -} + Thread& Thread::operator=(Thread&& other) noexcept { if (this != &other) { @@ -41,24 +39,25 @@ Thread& Thread::operator=(Thread&& other) noexcept } return *this; } + Thread::~Thread() noexcept { if (state_) { pthread_join(id_, nullptr); } } + +Thread::operator bool() const noexcept +{ + return state_ != nullptr; +} + void Thread::SetName(const std::string& name) { -#ifndef OSAL_OHOS - constexpr size_t threadNameMaxSize = 15; - name_ = (name.size() > threadNameMaxSize) ? name.substr(0, threadNameMaxSize).c_str() : name; - if (name.size() > threadNameMaxSize) { - MEDIA_LOG_W("task name %s exceed max size: %d", name.c_str(), threadNameMaxSize); - } - pthread_setname_np(id_, name_.c_str()); -#endif + name_ = name; } -void Thread::CreateThread(const std::function& func) + +bool Thread::CreateThread(const std::function& func) { state_ = std::unique_ptr(new State); state_->func_ = func; @@ -68,11 +67,28 @@ void Thread::CreateThread(const std::function& func) int rtv = pthread_create(&id_, &attr, Thread::Run, state_.get()); if (rtv == 0) { MEDIA_LOG_I("thread create succ"); + SetNameInternal(); } else { state_.reset(); MEDIA_LOG_E("thread create failed"); } + return rtv == 0; +} + +void Thread::SetNameInternal() +{ +#ifndef OSAL_OHOS + if (state_ && !name_.empty()) { + constexpr size_t threadNameMaxSize = 15; + if (name_.size() > threadNameMaxSize) { + MEDIA_LOG_W("task name %s exceed max size: %d", name_.c_str(), threadNameMaxSize); + name_ = name_.substr(0, threadNameMaxSize); + } + pthread_setname_np(id_, name_.c_str()); + } +#endif } + void* Thread::Run(void* arg) { auto state = static_cast(arg); diff --git a/engine/foundation/osal/thread/thread.h b/engine/foundation/osal/thread/thread.h index 47b6424c..21db5db5 100644 --- a/engine/foundation/osal/thread/thread.h +++ b/engine/foundation/osal/thread/thread.h @@ -37,22 +37,23 @@ public: Thread(Thread&& other) noexcept; - explicit Thread(const std::function& func); - Thread& operator=(Thread&& other) noexcept; virtual ~Thread() noexcept; + explicit operator bool() const noexcept; + void SetName(const std::string& name); + bool CreateThread(const std::function& func); + private: struct State { virtual ~State() = default; std::function func_ {}; }; - void CreateThread(const std::function& func); - + void SetNameInternal(); static void* Run(void* arg); pthread_t id_ {}; -- Gitee From e92237fce40245df7181e82108f1ce5d543c1cbf Mon Sep 17 00:00:00 2001 From: ancimoon Date: Wed, 29 Sep 2021 06:56:07 +0000 Subject: [PATCH 17/34] !11 rm GetState interface of PluginBase, and add default impl for all functions in PluginBase Signed-off-by: Hu Change --- engine/plugin/core/base.cpp | 3 +- engine/plugin/core/base.h | 2 + engine/plugin/interface/plugin_base.h | 54 ++++++++++++------- .../audio_ffmpeg_decoder_plugin.h | 5 -- .../demuxer/ffmpeg_demuxer_plugin.cpp | 5 -- .../demuxer/ffmpeg_demuxer_plugin.h | 1 - .../video_ffmpeg_decoder_plugin.h | 5 -- .../plugins/hdi_adapter/sink/hos_au_sink.cpp | 6 --- .../plugins/hdi_adapter/sink/hos_au_sink.h | 2 - .../sdl/audio_sink/sdl_audio_sink_plugin.cpp | 5 -- .../sdl/audio_sink/sdl_audio_sink_plugin.h | 2 - .../sdl/video_sink/sdl_video_sink_plugin.cpp | 5 -- .../sdl/video_sink/sdl_video_sink_plugin.h | 2 - .../source/file_source/file_source_plugin.cpp | 7 --- .../source/file_source/file_source_plugin.h | 1 - .../stream_source/stream_source_plugin.cpp | 7 --- .../stream_source/stream_source_plugin.h | 1 - 17 files changed, 40 insertions(+), 73 deletions(-) diff --git a/engine/plugin/core/base.cpp b/engine/plugin/core/base.cpp index 839dc204..4a563b96 100644 --- a/engine/plugin/core/base.cpp +++ b/engine/plugin/core/base.cpp @@ -70,7 +70,8 @@ Status Base::SetParameter(Tag tag, const ValueType& value) Status Base::GetState(State &state) { - return plugin->GetState(state); + state = pluginState_; + return Status::OK; } std::shared_ptr Base::GetAllocator() diff --git a/engine/plugin/core/base.h b/engine/plugin/core/base.h index 60539746..36761221 100644 --- a/engine/plugin/core/base.h +++ b/engine/plugin/core/base.h @@ -68,6 +68,8 @@ protected: const uint32_t apiVersion; std::shared_ptr plugin; + + State pluginState_ {State::CREATED}; }; } // namespace Plugin } // namespace Media diff --git a/engine/plugin/interface/plugin_base.h b/engine/plugin/interface/plugin_base.h index 4b15ed00..1f616f26 100644 --- a/engine/plugin/interface/plugin_base.h +++ b/engine/plugin/interface/plugin_base.h @@ -78,7 +78,10 @@ struct PluginBase { * @retval ERROR_NO_MEMORY: Memory allocation or external resource loading error caused by insufficient memory. * @retval ERROR_WRONG_STATE: Call this function in non CREATED state */ - virtual Status Init() = 0; + virtual Status Init() + { + return Status::OK; + } /** * @brief Plugin deinitialize to release resources. @@ -89,7 +92,10 @@ struct PluginBase { * @return Execution status return * @retval OK: Plugin deinitialize succeeded. */ - virtual Status Deinit() = 0; + virtual Status Deinit() + { + return Status::OK; + } /** * @brief Preparing parameters required or allocate the memory for plugin running. @@ -102,7 +108,10 @@ struct PluginBase { * @retval ERROR_NO_MEMORY: Memory allocation error caused by insufficient memory. * @retval ERROR_WRONG_STATE: Call this function in non INITIALIZED state */ - virtual Status Prepare() = 0; + virtual Status Prepare() + { + return Status::OK; + } /** * @brief Reset the plugin, reset the plugin running status and parameters before Prepare. @@ -115,7 +124,10 @@ struct PluginBase { * @retval ERROR_WRONG_STATE: Call this function in wrong state * @retval ERROR_UNIMPLEMENTED: This method is not implemented and cannot respond to reset. */ - virtual Status Reset() = 0; + virtual Status Reset() + { + return Status::OK; + } /** * @brief The plugin enters the running state and can process data. @@ -128,7 +140,10 @@ struct PluginBase { * @retval OK: Plugin reset succeeded. * @retval ERROR_WRONG_STATE: Call this function in non PREPARED state */ - virtual Status Start() = 0; + virtual Status Start() + { + return Status::OK; + } /** * @brief The plugin enters the stopped state and stops processing data. @@ -140,7 +155,10 @@ struct PluginBase { * @retval OK: Plugin reset succeeded. * @retval ERROR_WRONG_STATE: Call this function in non RUNNING state */ - virtual Status Stop() = 0; + virtual Status Stop() + { + return Status::OK; + } /** * @brief Determines whether the current plugin supports the specified parameter. @@ -150,7 +168,10 @@ struct PluginBase { * @param tag Plugin parameter type, which is described by tag. * @return true is supported, otherwise, false. */ - virtual bool IsParameterSupported(Tag tag) = 0; + virtual bool IsParameterSupported(Tag tag) + { + return false; + } /** * @brief Get the value of a specified parameter. @@ -164,7 +185,10 @@ struct PluginBase { * @retval ERROR_WRONG_STATE: Call this function in non wrong state. * @retval ERROR_INVALID_PARAMETER: The plugin does not support this parameter. */ - virtual Status GetParameter(Tag tag, ValueType &value) = 0; + virtual Status GetParameter(Tag tag, ValueType &value) + { + return Status::ERROR_UNIMPLEMENTED; + } /** * @brief Set the specified parameter. The value must be within the valid range of the parameter. @@ -180,16 +204,10 @@ struct PluginBase { * @retval ERROR_INVALID_DATA: The value is not in the valid range. * @retval ERROR_MISMATCHED_TYPE: The data type is mismatched. */ - virtual Status SetParameter(Tag tag, const ValueType &value) = 0; - - /** - * @brief Get the plugin running state. - * - * @param state Plugin running state. - * @return Execution status return - * @retval OK: Plugin reset succeeded. - */ - virtual Status GetState(State &state) = 0; + virtual Status SetParameter(Tag tag, const ValueType &value) + { + return Status::ERROR_UNIMPLEMENTED; + } /** * @brief Get the allocator specified by the plugin. diff --git a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h index ae9107d8..c7d04e73 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h @@ -59,11 +59,6 @@ public: Status SetParameter(Tag tag, const ValueType& value) override; - Status GetState(State& state) override - { - return Status::ERROR_UNIMPLEMENTED; - } - std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr& cb) override diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp index 79285981..ca370825 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp @@ -162,11 +162,6 @@ Status FFmpegDemuxerPlugin::SetParameter(Tag, const ValueType&) return Status::ERROR_UNIMPLEMENTED; } -Status FFmpegDemuxerPlugin::GetState(State&) -{ - return Status::ERROR_UNIMPLEMENTED; -} - std::shared_ptr FFmpegDemuxerPlugin::GetAllocator() { return allocator_; diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h index 495de894..1b71a654 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h @@ -50,7 +50,6 @@ public: bool IsParameterSupported(Tag tag) override; Status GetParameter(Tag tag, ValueType& value) override; Status SetParameter(Tag tag, const ValueType& value) override; - Status GetState(State& state) override; std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr& cb) override; diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h index 0e88eb72..155bafae 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h @@ -58,11 +58,6 @@ public: Status GetParameter(Tag tag, ValueType &value) override; Status SetParameter(Tag tag, const ValueType &value) override; - Status GetState(State &state) override - { - return Status::ERROR_UNIMPLEMENTED; - } - std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr &cb) override diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp index 47247efd..c10f20d3 100644 --- a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp @@ -431,12 +431,6 @@ bool HdiSink::IsParameterSupported(Tag tag) return false; } -Status HdiSink::GetState(State &state) -{ - state = pluginState_; - return Status::OK; -} - std::shared_ptr HdiSink::GetAllocator() { return nullptr; diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h index 34e43ed8..7dd5a6b6 100644 --- a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h @@ -58,8 +58,6 @@ public: Media::Plugin::Status SetParameter(Media::Plugin::Tag tag, const Media::Plugin::ValueType &value) override; - Media::Plugin::Status GetState(Media::Plugin::State &state) override; - std::shared_ptr GetAllocator() override; Media::Plugin::Status SetCallback(const std::shared_ptr &cb) override; diff --git a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp index d01e2e1a..2cefd34e 100644 --- a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp +++ b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp @@ -235,11 +235,6 @@ Status SdlAudioSinkPlugin::SetParameter(Tag tag, const ValueType& value) return Status::OK; } -Status SdlAudioSinkPlugin::GetState(State& state) -{ - return Status::ERROR_ALREADY_EXISTS; -} - std::shared_ptr SdlAudioSinkPlugin::GetAllocator() { return nullptr; diff --git a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h index 21efef3f..adeb5076 100644 --- a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h +++ b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h @@ -57,8 +57,6 @@ public: Status SetParameter(Tag tag, const ValueType &value) override; - Status GetState(State &state) override; - std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr &cb) override; diff --git a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp index 98cb4a8a..e02a3894 100644 --- a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp +++ b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp @@ -297,11 +297,6 @@ Status SdlVideoSinkPlugin::SetParameter(Tag tag, const ValueType& value) return Status::OK; } -Status SdlVideoSinkPlugin::GetState(State& state) -{ - return Status::ERROR_ALREADY_EXISTS; -} - std::shared_ptr SdlVideoSinkPlugin::GetAllocator() { return nullptr; diff --git a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h index 129a275c..62e9ae69 100644 --- a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h +++ b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.h @@ -54,8 +54,6 @@ public: Status SetParameter(Tag tag, const ValueType &value) override; - Status GetState(State &state) override; - std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr &cb) override; diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp index 6dfea7af..7d179b01 100644 --- a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp @@ -134,13 +134,6 @@ Status FileSourcePlugin::SetParameter(Tag tag, const ValueType &value) return Status::OK; } -Status FileSourcePlugin::GetState(State &state) -{ - MEDIA_LOG_D("IN"); - state = state_; - return Status::OK; -} - std::shared_ptr FileSourcePlugin::GetAllocator() { MEDIA_LOG_D("IN"); diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.h b/engine/plugin/plugins/source/file_source/file_source_plugin.h index 2c04e41d..521e3949 100644 --- a/engine/plugin/plugins/source/file_source/file_source_plugin.h +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.h @@ -46,7 +46,6 @@ public: bool IsParameterSupported(Tag tag) override; Status GetParameter(Tag tag, ValueType &value) override; Status SetParameter(Tag tag, const ValueType &value) override; - Status GetState(State &state) override; std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr &cb) override; Status SetSource( diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp index 90f91323..0087ac5a 100644 --- a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp @@ -158,13 +158,6 @@ Status StreamSourcePlugin::SetParameter(Tag tag, const ValueType& value) return Status::OK; } -Status StreamSourcePlugin::GetState(State& state) -{ - MEDIA_LOG_D("IN"); - state = state_; - return Status::OK; -} - std::shared_ptr StreamSourcePlugin::GetAllocator() { MEDIA_LOG_D("IN"); diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.h b/engine/plugin/plugins/source/stream_source/stream_source_plugin.h index 0def850a..5eec3191 100644 --- a/engine/plugin/plugins/source/stream_source/stream_source_plugin.h +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.h @@ -75,7 +75,6 @@ public: bool IsParameterSupported(Tag tag) override; Status GetParameter(Tag tag, ValueType& value) override; Status SetParameter(Tag tag, const ValueType& value) override; - Status GetState(State& state) override; std::shared_ptr GetAllocator() override; Status SetCallback(const std::shared_ptr& cb) override; Status SetSource(std::string& uri, std::shared_ptr> params = nullptr) override; -- Gitee From e4cea2ba4f328546216a309128098fe73873b2b2 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Wed, 29 Sep 2021 16:07:15 +0800 Subject: [PATCH 18/34] fix compile error on windows. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 21 ++++++++++++++++++++- interface/histreamer/hiplayer.h | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 92d3ab85..b2c1e9a7 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -67,4 +67,23 @@ file(GLOB_RECURSE PLUGINS_STATIC_BUILD_SRCS INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../include) -add_library(${PROJECT_NAME} SHARED ${HISTREAMER_SRCS} ${PLUGINS_STATIC_BUILD_SRCS}) \ No newline at end of file +link_directories( + ${ffmpeg_lib_path} + ${sdl_lib_path} +) + +if (WIN32) + link_libraries( + pthread + m + avcodec + avformat + avutil + avfilter + swresample + SDL2 + ) +endif () + +add_library(${PROJECT_NAME} SHARED ${HISTREAMER_SRCS} ${PLUGINS_STATIC_BUILD_SRCS} + ${3RDPARTY_SRCS}) \ No newline at end of file diff --git a/interface/histreamer/hiplayer.h b/interface/histreamer/hiplayer.h index 4053d596..ed186282 100644 --- a/interface/histreamer/hiplayer.h +++ b/interface/histreamer/hiplayer.h @@ -61,6 +61,9 @@ private: int audioStreamType_; }; +#if defined(WIN32) +__declspec(dllexport) +#endif std::shared_ptr CreateHiPlayer(); } // namespace Media } // namespace OHOS -- Gitee From 258e403bea28422d099ce99b6b3d51fc30588ede Mon Sep 17 00:00:00 2001 From: wuyouqian Date: Wed, 29 Sep 2021 09:25:25 +0000 Subject: [PATCH 19/34] !13 support av play together Signed-off-by: wuyouqian --- .../audio_decoder/audio_decoder_filter.cpp | 15 ++-- .../video_decoder/video_decoder_filter.cpp | 85 ++++++++++++------- .../pipeline/filters/demux/demuxer_filter.cpp | 12 ++- .../pipeline/filters/demux/demuxer_filter.h | 2 + .../sink/audio_sink/audio_sink_filter.cpp | 27 +++--- .../filters/source/media_source_filter.cpp | 2 +- engine/player/hiplayer_impl.cpp | 7 +- .../audio_ffmpeg_decoder_plugin.cpp | 2 +- .../demuxer/ffmpeg_demuxer_plugin.cpp | 7 +- .../video_ffmpeg_decoder_plugin.cpp | 2 +- .../sdl/video_sink/sdl_video_sink_plugin.cpp | 2 +- 11 files changed, 95 insertions(+), 68 deletions(-) diff --git a/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp index b1f207b9..6ab7b3a0 100644 --- a/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp +++ b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp @@ -112,11 +112,12 @@ private: AudioDecoderFilter::AudioDecoderFilter(const std::string &name): DecoderFilterBase(name), dataCallback_(std::make_shared(*this)) { + MEDIA_LOG_D("audio decoder ctor called"); } AudioDecoderFilter::~AudioDecoderFilter() { - MEDIA_LOG_I("AudioDecoderFilter dtor called..."); + MEDIA_LOG_D("audio decoder dtor called"); Release(); if (inBufferQ_) { inBufferQ_->SetActive(false); @@ -145,7 +146,7 @@ ErrorCode AudioDecoderFilter::QueueAllBufferInPoolToPluginLocked() ErrorCode AudioDecoderFilter::Start() { - MEDIA_LOG_D("Start called"); + MEDIA_LOG_D("audio decoder start called"); if (state_ != FilterState::READY && state_ != FilterState::PAUSED) { MEDIA_LOG_W("call decoder start() when state is not ready or working"); return ERROR_STATE; @@ -160,23 +161,24 @@ ErrorCode AudioDecoderFilter::Prepare() return ERROR_STATE; } if (!outBufferQ_) { - outBufferQ_ = std::make_shared>("decoderOutBuffQueue", DEFAULT_OUT_BUFFER_POOL_SIZE); + outBufferQ_ = std::make_shared>("adecOutBuffQueue", + DEFAULT_OUT_BUFFER_POOL_SIZE); } else { outBufferQ_->SetActive(true); } if (!pushTask_) { - pushTask_ = std::make_shared("decPushThread"); + pushTask_ = std::make_shared("adecPushThread"); pushTask_->RegisterHandler([this] { FinishFrame(); }); } if (drivingMode_ == ThreadDrivingMode::ASYNC) { if (!inBufferQ_) { - inBufferQ_ = std::make_shared>("decoderFilterInBufQue", + inBufferQ_ = std::make_shared>("adecFilterInBufQue", DEFAULT_IN_BUFFER_POOL_SIZE); } else { inBufferQ_->SetActive(true); } if (!handleFrameTask_) { - handleFrameTask_ = std::make_shared("decHandleFrameThread"); + handleFrameTask_ = std::make_shared("adecHandleFrameThread"); handleFrameTask_->RegisterHandler([this] { HandleFrame(); }); } } else { @@ -237,6 +239,7 @@ bool AudioDecoderFilter::Negotiate(const std::string& inPort, const std::shared_ pushTask_->Start(); state_ = FilterState::READY; OnEvent({EVENT_READY}); + MEDIA_LOG_I("audio decoder send EVENT_READY"); return true; } diff --git a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp index dfdcf82a..8c5eeb1d 100644 --- a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp +++ b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "VideoDecoderFilter" #include "video_decoder_filter.h" -#include +#include "utils/util.h" #include "foundation/constants.h" #include "factory/filter_factory.h" #include "plugin/common/plugin_video_tags.h" @@ -28,7 +28,7 @@ namespace OHOS { namespace Media { namespace Pipeline { -const uint32_t DEFAULT_IN_BUFFER_POOL_SIZE = 8; +const uint32_t DEFAULT_IN_BUFFER_POOL_SIZE = 200; const uint32_t DEFAULT_OUT_BUFFER_POOL_SIZE = 8; const float VIDEO_PIX_DEPTH = 1.5; static uint32_t VIDEO_ALIGN_SIZE = 16; @@ -66,7 +66,7 @@ private: VideoDecoderFilter::VideoDecoderFilter(const std::string &name): DecoderFilterBase(name), dataCallback_(std::make_shared(*this)) { - MEDIA_LOG_I("VideoDecoderFilter ctor called..."); + MEDIA_LOG_I("video decoder ctor called"); vdecFormat_.width = 0; vdecFormat_.height = 0; vdecFormat_.bitRate = -1; @@ -74,24 +74,24 @@ VideoDecoderFilter::VideoDecoderFilter(const std::string &name): DecoderFilterBa VideoDecoderFilter::~VideoDecoderFilter() { - MEDIA_LOG_I("VideoDecoderFilter dtor called..."); + MEDIA_LOG_I("video decoder dtor called"); if (plugin_) { plugin_->Stop(); plugin_->Deinit(); } - if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_ != nullptr) { + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_) { handleFrameTask_->Stop(); handleFrameTask_.reset(); } - if (inBufQue_ != nullptr) { + if (inBufQue_) { inBufQue_->SetActive(false); inBufQue_.reset(); } - if (pushTask_ != nullptr) { + if (pushTask_) { pushTask_->Stop(); pushTask_.reset(); } - if (outBufQue_ != nullptr) { + if (outBufQue_) { outBufQue_->SetActive(false); outBufQue_.reset(); } @@ -99,7 +99,7 @@ VideoDecoderFilter::~VideoDecoderFilter() ErrorCode VideoDecoderFilter::Start() { - MEDIA_LOG_D("Start called"); + MEDIA_LOG_D("video decoder start called"); if (state_ != FilterState::READY && state_ != FilterState::PAUSED) { MEDIA_LOG_W("call decoder start() when state_ is not ready or working"); return ERROR_STATE; @@ -109,28 +109,28 @@ ErrorCode VideoDecoderFilter::Start() ErrorCode VideoDecoderFilter::Prepare() { - MEDIA_LOG_D("Prepare called"); + MEDIA_LOG_D("video decoder prepare called"); if (state_ != FilterState::INITIALIZED) { MEDIA_LOG_W("decoder filter is not in init state_"); return ERROR_STATE; } if (!outBufQue_) { - outBufQue_ = std::make_shared>("decoderFilterOutBufQue", + outBufQue_ = std::make_shared>("vdecFilterOutBufQue", DEFAULT_OUT_BUFFER_POOL_SIZE); } else { outBufQue_->SetActive(true); } if (!pushTask_) { - pushTask_ = std::make_shared("decPushThread"); + pushTask_ = std::make_shared("vdecPushThread"); pushTask_->RegisterHandler([this] { FinishFrame(); }); } if (!inBufQue_) { - inBufQue_ = std::make_shared>("decoderFilterInBufQue", + inBufQue_ = std::make_shared>("vdecFilterInBufQue", DEFAULT_IN_BUFFER_POOL_SIZE); } else { inBufQue_->SetActive(true); } - if (!handleFrameTask_) { + if (drivingMode_ == ThreadDrivingMode::ASYNC && !handleFrameTask_) { handleFrameTask_ = std::make_shared("decHandleFrameThread"); handleFrameTask_->RegisterHandler([this] { HandleFrame(); }); } @@ -145,7 +145,7 @@ bool VideoDecoderFilter::Negotiate(const std::string& inPort, const std::shared_ return false; } - MEDIA_LOG_D("Negotiate called"); + MEDIA_LOG_D("video decoder negotiate called"); auto creator = [] (const std::string& pluginName) { return Plugin::PluginManager::Instance().CreateCodecPlugin(pluginName); }; @@ -161,8 +161,8 @@ bool VideoDecoderFilter::Negotiate(const std::string& inPort, const std::shared_ if (Configure(inMeta) != ErrorCode::SUCCESS) { MEDIA_LOG_E("decoder configure error"); Event event { - .type = EVENT_ERROR, - .param = err, + .type = EVENT_ERROR, + .param = err, }; OnEvent(event); return false; @@ -308,21 +308,23 @@ ErrorCode VideoDecoderFilter::ConfigurePlugin() ErrorCode VideoDecoderFilter::Configure(const std::shared_ptr& meta) { - MEDIA_LOG_D("Configure called"); + MEDIA_LOG_D("video decoder configure called"); RETURN_ERR_MESSAGE_LOG_IF_FAIL(InitPlugin(), "Init plugin fail"); RETURN_ERR_MESSAGE_LOG_IF_FAIL(SetVideoDecoderFormat(meta), "Set video decoder format fail"); RETURN_ERR_MESSAGE_LOG_IF_FAIL(AllocateOutputBuffers(), "Alloc output buffers fail"); RETURN_ERR_MESSAGE_LOG_IF_FAIL(ConfigurePlugin(), "Config plugin fail"); - if (handleFrameTask_ != nullptr) { + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_) { handleFrameTask_->Start(); } - pushTask_->Start(); - + if (pushTask_) { + pushTask_->Start(); + } state_ = FilterState::READY; Event event { .type = EVENT_READY, }; OnEvent(event); + MEDIA_LOG_I("video decoder send EVENT_READY"); return ErrorCode::SUCCESS; } @@ -336,7 +338,11 @@ ErrorCode VideoDecoderFilter::PushData(const std::string& inPort, AVBufferPtr bu MEDIA_LOG_I("decoder is flushing, discarding this data from port %s", inPort.c_str()); return ErrorCode::SUCCESS; } - inBufQue_->Push(buffer); + if (drivingMode_ == ThreadDrivingMode::ASYNC) { + inBufQue_->Push(buffer); + return ErrorCode::SUCCESS; + } + HandleOneFrame(buffer); return ErrorCode::SUCCESS; } @@ -347,11 +353,15 @@ void VideoDecoderFilter::FlushStart() if (inBufQue_) { inBufQue_->SetActive(false); } - handleFrameTask_->PauseAsync(); + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_) { + handleFrameTask_->PauseAsync(); + } if (outBufQue_) { outBufQue_->SetActive(false); } - pushTask_->PauseAsync(); + if (pushTask_) { + pushTask_->PauseAsync(); + } if (plugin_ != nullptr) { auto err = TranslatePluginStatus(plugin_->Flush()); if (err != SUCCESS) { @@ -367,12 +377,16 @@ void VideoDecoderFilter::FlushEnd() if (inBufQue_) { inBufQue_->SetActive(true); } - handleFrameTask_->Start(); + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_) { + handleFrameTask_->Start(); + } if (outBufQue_) { outBufQue_->SetActive(true); } - pushTask_->Start(); - if (plugin_ != nullptr) { + if (pushTask_) { + pushTask_->Start(); + } + if (plugin_) { ConfigurePluginOutputBuffers(); } } @@ -384,7 +398,9 @@ ErrorCode VideoDecoderFilter::Stop() outBufQue_->SetActive(false); pushTask_->Pause(); inBufQue_->SetActive(false); - handleFrameTask_->Pause(); + if (drivingMode_ == ThreadDrivingMode::ASYNC && handleFrameTask_) { + handleFrameTask_->Pause(); + } outBufPool_.reset(); MEDIA_LOG_I("Stop success"); return FilterBase::Stop(); @@ -404,10 +420,15 @@ void VideoDecoderFilter::HandleFrame() void VideoDecoderFilter::HandleOneFrame(const std::shared_ptr& data) { MEDIA_LOG_D("HandleOneFrame called"); - auto ret = plugin_->QueueInputBuffer(data, -1); - if (ret != Plugin::Status::OK) { - MEDIA_LOG_W("Send data to plugin error: %d", ret); - } + Plugin::Status ret; + do { + ret = plugin_->QueueInputBuffer(data, -1); + if (ret == Plugin::Status::OK) { + break; + } + MEDIA_LOG_D("Send data to plugin error: %d", ret); + OSAL::SleepFor(10); + } while (1); } void VideoDecoderFilter::FinishFrame() diff --git a/engine/pipeline/filters/demux/demuxer_filter.cpp b/engine/pipeline/filters/demux/demuxer_filter.cpp index 9a55be7e..4a7f2aef 100644 --- a/engine/pipeline/filters/demux/demuxer_filter.cpp +++ b/engine/pipeline/filters/demux/demuxer_filter.cpp @@ -429,8 +429,17 @@ void DemuxerFilter::HandleFrame(const AVBufferPtr& bufferPtr, uint32_t streamInd if (stream.streamIdx != streamIndex) { continue; } + stream.port->PushData(bufferPtr); + break; + } +} + +void DemuxerFilter::NegotiateDownstream() +{ + for (auto& stream : mediaMetaData_.trackInfos) { if (stream.needNegoCaps) { CapabilitySet caps; + MEDIA_LOG_I("demuxer negotiate with streamIdx: %u", stream.streamIdx); if (stream.port->Negotiate(GetStreamMeta(stream.streamIdx), caps)) { stream.needNegoCaps = false; } else { @@ -438,8 +447,6 @@ void DemuxerFilter::HandleFrame(const AVBufferPtr& bufferPtr, uint32_t streamInd OnEvent({EVENT_ERROR, PLUGIN_NOT_FOUND}); } } - stream.port->PushData(bufferPtr); - break; } } @@ -461,6 +468,7 @@ void DemuxerFilter::DemuxerLoop() } else { Plugin::MediaInfoHelper mediaInfo; if (plugin_->GetMediaInfo(mediaInfo) == Plugin::Status::OK && PrepareStreams(mediaInfo)) { + NegotiateDownstream(); pluginState_ = DEMUXER_STATE_PARSE_FRAME; state_ = FilterState::READY; OnEvent({EVENT_READY, {}}); diff --git a/engine/pipeline/filters/demux/demuxer_filter.h b/engine/pipeline/filters/demux/demuxer_filter.h index d6f6eb1e..029b305d 100644 --- a/engine/pipeline/filters/demux/demuxer_filter.h +++ b/engine/pipeline/filters/demux/demuxer_filter.h @@ -113,6 +113,8 @@ private: void HandleFrame(const AVBufferPtr& buffer, uint32_t streamIndex); + void NegotiateDownstream(); + void DemuxerLoop(); void SetCurrentTime(int64_t timestampUsec); diff --git a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp index 354ddb9a..7bbe8216 100644 --- a/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp +++ b/engine/pipeline/filters/sink/audio_sink/audio_sink_filter.cpp @@ -27,11 +27,11 @@ static AutoRegisterFilter g_registerFilterHelper("builtin.playe AudioSinkFilter::AudioSinkFilter(const std::string& name) : FilterBase(name) { - MEDIA_LOG_I("AudioSinkFilter ctor"); + MEDIA_LOG_I("audio sink ctor called"); } AudioSinkFilter::~AudioSinkFilter() { - MEDIA_LOG_I("AudioSinkFilter deCtor."); + MEDIA_LOG_I("audio sink dtor called"); if (plugin_) { plugin_->Stop(); plugin_->Deinit(); @@ -91,12 +91,7 @@ ErrorCode AudioSinkFilter::GetParameter(int32_t key, Plugin::Any& value) bool AudioSinkFilter::Negotiate(const std::string& inPort, const std::shared_ptr& inMeta, CapabilitySet& outCaps) { - MEDIA_LOG_D("sink negotiate started"); - if (state_ != FilterState::PREPARING) { - MEDIA_LOG_W("sink filter is not in preparing when negotiate"); - return false; - } - + MEDIA_LOG_D("audio sink negotiate started"); auto creator = [](const std::string& pluginName) { return Plugin::PluginManager::Instance().CreateAudioSinkPlugin(pluginName); }; @@ -112,6 +107,9 @@ bool AudioSinkFilter::Negotiate(const std::string& inPort, const std::shared_ptr OnEvent({EVENT_ERROR, err}); return false; } + state_ = FilterState::READY; + OnEvent({EVENT_READY}); + MEDIA_LOG_I("audio sink send EVENT_READY"); return true; } @@ -167,11 +165,6 @@ ErrorCode AudioSinkFilter::ConfigureToPreparePlugin(const std::shared_ptrStop(); if (pushThreadIsBlocking.load()) { startWorkingCondition_.NotifyOne(); } - MEDIA_LOG_I("AudioSinkFilter stop finished."); + MEDIA_LOG_I("audio sink stop finish"); return SUCCESS; } @@ -266,7 +259,7 @@ ErrorCode AudioSinkFilter::Resume() void AudioSinkFilter::FlushStart() { - MEDIA_LOG_D("FlushStart entered"); + MEDIA_LOG_D("audio sink flush start entered"); isFlushing = true; if (pushThreadIsBlocking) { startWorkingCondition_.NotifyOne(); @@ -276,7 +269,7 @@ void AudioSinkFilter::FlushStart() void AudioSinkFilter::FlushEnd() { - MEDIA_LOG_D("FlushEnd entered"); + MEDIA_LOG_D("audio sink flush end entered"); isFlushing = false; } diff --git a/engine/pipeline/filters/source/media_source_filter.cpp b/engine/pipeline/filters/source/media_source_filter.cpp index 9c227c45..29cfb252 100644 --- a/engine/pipeline/filters/source/media_source_filter.cpp +++ b/engine/pipeline/filters/source/media_source_filter.cpp @@ -143,7 +143,7 @@ ErrorCode MediaSourceFilter::Prepare() } auto err = TranslateError(plugin_->Prepare()); if (err == ErrorCode::SUCCESS) { - MEDIA_LOG_D("Send READY event to player"); + MEDIA_LOG_D("media source send EVENT_READY"); OnEvent(Event{EVENT_READY, {}}); } return err; diff --git a/engine/player/hiplayer_impl.cpp b/engine/player/hiplayer_impl.cpp index 3a9ecb72..9b74fbfa 100644 --- a/engine/player/hiplayer_impl.cpp +++ b/engine/player/hiplayer_impl.cpp @@ -319,9 +319,8 @@ ErrorCode HiPlayer::HiPlayerImpl::OnCallback(const FilterCallbackType& type, Fil ErrorCode ret = ErrorCode::SUCCESS; switch (type) { case FilterCallbackType::PORT_ADDED: -#ifndef VIDEO_SUPPORT ret = NewAudioPortFound(filter, parameter); -#else +#ifdef VIDEO_SUPPORT ret = NewVideoPortFound(filter, parameter); #endif break; @@ -366,7 +365,6 @@ ErrorCode HiPlayer::HiPlayerImpl::NewAudioPortFound(Filter* filter, const Plugin MEDIA_LOG_I("new port found on demuxer %lu", param.ports.size()); for (const auto& portDesc : param.ports) { if (!StringStartsWith(portDesc.name, "audio")) { - MEDIA_LOG_W("NewAudioPortFound, discard non-audio port: %s", portDesc.name.c_str()); continue; } MEDIA_LOG_I("port name %s", portDesc.name.c_str()); @@ -397,11 +395,10 @@ ErrorCode HiPlayer::HiPlayerImpl::NewVideoPortFound(Filter* filter, const Plugin if (filter != demuxer.get() || param.type != PortType::OUT) { return PORT_UNEXPECTED; } - MEDIA_LOG_I("new port found on demuxer %lu", param.ports.size()); std::vector newFilters; for (const auto& portDesc : param.ports) { - MEDIA_LOG_I("port name %s", portDesc.name.c_str()); if (StringStartsWith(portDesc.name, "video")) { + MEDIA_LOG_I("port name %s", portDesc.name.c_str()); videoDecoder = FilterFactory::Instance().CreateFilterWithType( "builtin.player.videodecoder", "videodecoder-" + portDesc.name); if (pipeline->AddFilters({videoDecoder.get()}) != ALREADY_EXISTS) { diff --git a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp index e1a05018..815f95b3 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp @@ -176,7 +176,7 @@ namespace OHOS { namespace Media { namespace Plugin { AudioFfmpegDecoderPlugin::AudioFfmpegDecoderPlugin(std::string name) - : name_(std::move(name)), outBufferQ_("decoderPluginQueue", BUFFER_QUEUE_SIZE) + : name_(std::move(name)), outBufferQ_("adecPluginQueue", BUFFER_QUEUE_SIZE) { } diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp index ca370825..95ca4287 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp @@ -245,7 +245,9 @@ bool FFmpegDemuxerPlugin::ConvertAVPacketToFrameInfo(const AVStream& avStream, c frameInfo.GetBufferMeta()->SetMeta(Tag::MEDIA_POSITION, static_cast(pkt.pos)); int frameSize = 0; - if (avStream.codecpar->codec_type == AVMEDIA_TYPE_VIDEO && avStream.codecpar->codec_id == AV_CODEC_ID_RAWVIDEO) { + if (avStream.codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + frameSize = pkt.size; + } else if (avStream.codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { #ifdef VIDEO_SUPPORT frameSize = pkt.size; #else @@ -253,7 +255,8 @@ bool FFmpegDemuxerPlugin::ConvertAVPacketToFrameInfo(const AVStream& avStream, c return false; #endif } else { - frameSize = pkt.size; + MEDIA_LOG_W("unsupported codec..."); + return false; } auto data = frameInfo.AllocMemory(allocator_, frameSize); if (data) { diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp index da5e8ab2..b2df0b55 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp @@ -130,7 +130,7 @@ VideoPixelFormat TranslatePixelFormat(int32_t pixelFormat) } // namespace VideoFfmpegDecoderPlugin::VideoFfmpegDecoderPlugin(std::string name) - : name_(std::move(name)), outBufferQ_("decoderPluginQueue", BUFFER_QUEUE_SIZE) + : name_(std::move(name)), outBufferQ_("vdecPluginQueue", BUFFER_QUEUE_SIZE) { } diff --git a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp index e02a3894..73c95ac2 100644 --- a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp +++ b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp @@ -385,7 +385,7 @@ Status SdlVideoSinkPlugin::VideoImageDisaplay(const std::shared_ptr& inp SDL_RenderClear(renderer_.get()); SDL_RenderCopy(renderer_.get(), texture_.get(), NULL, &textureRect_); SDL_RenderPresent(renderer_.get()); - SDL_Delay(40); // 40ms + SDL_Delay(16); // 16ms return (ret != 0) ? Status::ERROR_UNKNOWN : Status::OK; } -- Gitee From aaa2b4121eeed1c69fdaa550d6af8822140e45c2 Mon Sep 17 00:00:00 2001 From: wuyouqian Date: Wed, 29 Sep 2021 12:13:12 +0000 Subject: [PATCH 20/34] !15 move video sink send ready event flow Signed-off-by: wuyouqian --- .../filters/sink/video_sink/video_sink_filter.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp index c3581117..ac3d2937 100644 --- a/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp +++ b/engine/pipeline/filters/sink/video_sink/video_sink_filter.cpp @@ -113,6 +113,12 @@ bool VideoSinkFilter::Negotiate(const std::string &inPort, const std::shared_ptr OnEvent(event); return false; } + state_ = FilterState::READY; + Event event { + .type = EVENT_READY, + }; + OnEvent(event); + MEDIA_LOG_I("video sink send EVENT_READY"); return true; } @@ -174,14 +180,6 @@ void VideoSinkFilter::RenderFrame() ErrorCode VideoSinkFilter::PushData(const std::string &inPort, AVBufferPtr buffer) { MEDIA_LOG_D("video sink push data started, state_: %d", state_.load()); - if (state_ == FilterState::PREPARING) { - state_ = FilterState::READY; - Event event { - .type = EVENT_READY, - }; - OnEvent(event); - } - if (isFlushing_ || state_.load() == FilterState::INITIALIZED) { MEDIA_LOG_I("video sink is flushing ignore this buffer"); return ErrorCode::SUCCESS; -- Gitee From d6276c4c58f9f1aeb25d9198734f5ea61530ca22 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Thu, 30 Sep 2021 08:31:57 +0800 Subject: [PATCH 21/34] set library output directory. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index b2c1e9a7..3a93c477 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -85,5 +85,7 @@ if (WIN32) ) endif () +SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + add_library(${PROJECT_NAME} SHARED ${HISTREAMER_SRCS} ${PLUGINS_STATIC_BUILD_SRCS} - ${3RDPARTY_SRCS}) \ No newline at end of file + ${3RDPARTY_SRCS}) -- Gitee From d7383ffe79e55b07154191c1d2341dfff04017aa Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Thu, 30 Sep 2021 08:37:19 +0800 Subject: [PATCH 22/34] Generate test executable file to bin directory. Signed-off-by: Chen Guodong --- tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 998bbe07..812a9992 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,4 +11,6 @@ if (NOT MINGW) # ADD_LINK_OPTIONS( -rdynamic) endif () +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + ADD_SUBDIRECTORY(ut) -- Gitee From c959f9de22a8d163bb72160188883319b52fdb17 Mon Sep 17 00:00:00 2001 From: ancimoon Date: Thu, 30 Sep 2021 00:42:07 +0000 Subject: [PATCH 23/34] !16 bugfix:retry queueInput when timeout Signed-off-by: Hu Change --- .../audio_decoder/audio_decoder_filter.cpp | 19 +++++++++++++++++-- engine/plugin/common/plugin_buffer.h | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp index 6ab7b3a0..769d25ea 100644 --- a/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp +++ b/engine/pipeline/filters/codec/audio_decoder/audio_decoder_filter.cpp @@ -32,6 +32,8 @@ constexpr int32_t AF_64BIT_BYTES = 8; constexpr int32_t AF_32BIT_BYTES = 4; constexpr int32_t AF_16BIT_BYTES = 2; constexpr int32_t AF_8BIT_BYTES = 1; +constexpr int32_t RETRY_TIMES = 3; +constexpr int32_t RETRY_DELAY = 10; //ms int32_t CalculateBufferSize(const std::shared_ptr &meta) { @@ -441,9 +443,22 @@ void AudioDecoderFilter::HandleOneFrame(const std::shared_ptr& data) { MEDIA_LOG_D("HandleOneFrame called"); Plugin::Status status = Plugin::Status::OK; - status = plugin_->QueueInputBuffer(data, -1); + int32_t retryCnt = 0; + do { + status = plugin_->QueueInputBuffer(data, 0); + if (status != Plugin::Status::ERROR_TIMED_OUT) { + break; + } + MEDIA_LOG_I("queue input buffer timeout, will retry"); + retryCnt++; + if (retryCnt <= RETRY_TIMES) { + OSAL::SleepFor(RETRY_DELAY); + } else { + break; + } + } while (true); if (status != Plugin::Status::OK) { - MEDIA_LOG_W("send data to plugin error"); + MEDIA_LOG_W("queue input buffer with error %d, ignore this buffer", status); } MEDIA_LOG_D("HandleOneFrame finished"); } diff --git a/engine/plugin/common/plugin_buffer.h b/engine/plugin/common/plugin_buffer.h index 736aff86..eeaa44c6 100644 --- a/engine/plugin/common/plugin_buffer.h +++ b/engine/plugin/common/plugin_buffer.h @@ -137,7 +137,7 @@ private: size_t capacity; /// The alignment of the memory. - __attribute__((unused)) size_t alignment; + size_t alignment; /// Offset of the buffer address to make sure access acording to alignment. size_t offset {0}; -- Gitee From acc34235bdf84326b6e98336bfb5d78d0da5ca8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=9C=E5=A8=83?= Date: Thu, 30 Sep 2021 03:19:48 +0000 Subject: [PATCH 24/34] !17 Modular compilation Signed-off-by: Shihaojun --- engine/plugin/BUILD.gn | 80 ++++++++++++++++++++++++++++++++++++++++++ histreamer_BUILD_gn | 71 +++++-------------------------------- 2 files changed, 88 insertions(+), 63 deletions(-) create mode 100644 engine/plugin/BUILD.gn diff --git a/engine/plugin/BUILD.gn b/engine/plugin/BUILD.gn new file mode 100644 index 00000000..e3172d48 --- /dev/null +++ b/engine/plugin/BUILD.gn @@ -0,0 +1,80 @@ +# Copyright (c) 2021-2021 Huawei Device Co., Ltd. +# 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. +# + + +if (defined(ohos_lite)) { + import("//build/lite/config/component/lite_component.gni") + if (ohos_kernel_type == "liteos_m") { + print("static_library") + } else { + # build shared library + + declare_args() { + plugin_dynamic_register = false + } + + # lib plugin interface + shared_library("histreamer_plugin_intf") { + sources = [ + "common/plugin_buffer.cpp", + ] + cflags = [ "-O2", "-fPIC", "-Wall" ] + cflags_cc = cflags + outdir = rebase_path("$root_out_dir") + ldflags = [ "-L$outdir" ] + public_deps = [ + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", + "//third_party/bounds_checking_function:libsec_shared", + ] + } + + # lib plugin core + shared_library("histreamer_plugin_core") { + sources = [ + "core/base.cpp", + "core/audio_sink.cpp", + "core/video_sink.cpp", + "core/codec.cpp", + "core/demuxer.cpp", + "core/plugin_register.cpp", + "core/plugin_wrapper.cpp", + "core/plugin_manager.cpp", + "core/source.cpp", + ] + defines = [ "HI3516DV300" ] + if (plugin_dynamic_register) { + sources += [ + "engine/plugin/core/plugin_loader.cpp", + ] + defines += [ "DYNAMIC_PLUGINS" ] + } + include_dirs = [ + ".", + "../", + ] + cflags = [ "-O2", "-fPIC", "-Wall" ] + cflags_cc = cflags + outdir = rebase_path("$root_out_dir") + ldflags = [ "-L$outdir" ] + deps = [ + ":histreamer_plugin_intf", + ] + public_deps = [ + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", + "//third_party/bounds_checking_function:libsec_shared", + ] + } + } +} + diff --git a/histreamer_BUILD_gn b/histreamer_BUILD_gn index 9845a4e9..6e3293f3 100644 --- a/histreamer_BUILD_gn +++ b/histreamer_BUILD_gn @@ -108,8 +108,8 @@ if (compile_histreamer) { "//foundation/graphic/surface:lite_surface", "//device/hisilicon/hardware:hardware_media_sdk", "//device/hisilicon/modules/middleware:middleware_source_sdk", - ":histreamer_plugin_intf", - ":histreamer_plugin_core", + "engine/plugin:histreamer_plugin_intf", + "engine/plugin:histreamer_plugin_core", ] public_deps = [ "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", @@ -127,68 +127,13 @@ if (compile_histreamer) { outputs = ["$root_out_dir/data/{{source_file_part}}"] } - # lib plugin interface - shared_library("histreamer_plugin_intf") { - sources = [ - "engine/plugin/common/plugin_buffer.cpp", - ] - cflags = [ "-O2", "-fPIC", "-Wall" ] - cflags_cc = cflags - outdir = rebase_path("$root_out_dir") - ldflags = [ "-L$outdir" ] - public_deps = [ - "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", - "//third_party/bounds_checking_function:libsec_shared", - ] + lite_component("media_histreamer") { + features = [ + ":histreamer", + "engine/plugin:histreamer_plugin_intf", + "engine/plugin:histreamer_plugin_core", + ] } - - declare_args() { - plugin_dynamic_register = false - } - - # lib plugin core - shared_library("histreamer_plugin_core") { - sources = [ - "engine/plugin/core/base.cpp", - "engine/plugin/core/audio_sink.cpp", - "engine/plugin/core/video_sink.cpp", - "engine/plugin/core/codec.cpp", - "engine/plugin/core/demuxer.cpp", - "engine/plugin/core/plugin_register.cpp", - "engine/plugin/core/plugin_wrapper.cpp", - "engine/plugin/core/plugin_manager.cpp", - "engine/plugin/core/source.cpp", - ] - defines = [ "HI3516DV300" ] - if (plugin_dynamic_register) { - sources += [ - "engine/plugin/core/plugin_loader.cpp", - ] - defines += [ "DYNAMIC_PLUGINS" ] - } - include_dirs = [ - "engine", - "engine/foundation", - "engine/foundation/osal", - "engine/pipeline", - "engine/player", - "engine/pipeline/filters", - "engine/pipeline/core", - "engine/plugin", - ] - cflags = [ "-O2", "-fPIC", "-Wall" ] - cflags_cc = cflags - outdir = rebase_path("$root_out_dir") - ldflags = [ "-L$outdir" ] - deps = [ - ":histreamer_plugin_intf", - ] - public_deps = [ - "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", - "//third_party/bounds_checking_function:libsec_shared", - ] - } - } else { import("//build/ohos.gni") -- Gitee From 260d0e21b0bcb01c44ffdda08216903d5179f58d Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Thu, 30 Sep 2021 16:18:35 +0800 Subject: [PATCH 25/34] fix compile error and no log issue. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 8 +++++++- engine/foundation/log.h | 7 +------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 3a93c477..65f60eaf 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -41,7 +41,6 @@ include_directories( ${TOP_DIR}/engine/pipeline/filters ${TOP_DIR}/engine/pipeline/filters/player ${TOP_DIR}/engine/plugin - ${TOP_DIR}/engine/3rdparty/curl/include ) ###################################################### @@ -83,6 +82,13 @@ if (WIN32) swresample SDL2 ) +elseif (UNIX) +else () + link_libraries( + log + FFmpeg + SDL2 + ) endif () SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) diff --git a/engine/foundation/log.h b/engine/foundation/log.h index 62289592..abc124c5 100644 --- a/engine/foundation/log.h +++ b/engine/foundation/log.h @@ -69,12 +69,7 @@ inline std::string MediaGetFileName(std::string file) #define MEDIA_LOG_D(msg, ...) MEDIA_DEBUG_LOG(msg, ##__VA_ARGS__) #endif #else -#define MEDIA_LOG_E(msg, ...) MEDIA_LOG_MESSAGE("ERROR", msg, ##__VA_ARGS__) -#define MEDIA_LOG_W(msg, ...) MEDIA_LOG_MESSAGE("WARN", msg, ##__VA_ARGS__) -#if !LOG_NDEBUG -#define MEDIA_LOG_I(msg, ...) MEDIA_LOG_MESSAGE("INFO", msg, ##__VA_ARGS__) -#define MEDIA_LOG_D(msg, ...) MEDIA_LOG_MESSAGE("DEBUG", msg, ##__VA_ARGS__) -#endif +#include "log_adapter.h" #endif #ifndef FAIL_RETURN -- Gitee From f15fa5199a3226501d32aafc74a270e67c905244 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Thu, 30 Sep 2021 17:23:52 +0800 Subject: [PATCH 26/34] fix link error. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 65f60eaf..3b2391bc 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -82,7 +82,6 @@ if (WIN32) swresample SDL2 ) -elseif (UNIX) else () link_libraries( log -- Gitee From ae9b5bf312216f99b8079ca15e2bfd633a839ea3 Mon Sep 17 00:00:00 2001 From: wuyouqian Date: Thu, 30 Sep 2021 11:25:47 +0000 Subject: [PATCH 27/34] !19 vdec ffmpeg plugin add decode thread Signed-off-by: wuyouqian --- .../video_decoder/video_decoder_filter.cpp | 7 +- .../filters/source/media_source_filter.cpp | 11 ++- engine/plugin/common/plugin_buffer.cpp | 6 -- engine/plugin/common/plugin_buffer.h | 12 +++ .../ffmpeg_adapter/utils/ffmpeg_utils.h | 11 --- .../video_ffmpeg_decoder_plugin.cpp | 98 ++++++++----------- .../video_ffmpeg_decoder_plugin.h | 15 +-- .../source/file_source/file_source_plugin.cpp | 3 +- .../stream_source/stream_source_plugin.cpp | 2 +- 9 files changed, 69 insertions(+), 96 deletions(-) diff --git a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp index 8c5eeb1d..49692b2a 100644 --- a/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp +++ b/engine/pipeline/filters/codec/video_decoder/video_decoder_filter.cpp @@ -22,6 +22,7 @@ #include "foundation/constants.h" #include "factory/filter_factory.h" #include "plugin/common/plugin_video_tags.h" +#include "plugin/common/plugin_buffer.h" #include "foundation/memory_helper.h" #include "foundation/log.h" @@ -33,12 +34,6 @@ const uint32_t DEFAULT_OUT_BUFFER_POOL_SIZE = 8; const float VIDEO_PIX_DEPTH = 1.5; static uint32_t VIDEO_ALIGN_SIZE = 16; -template -static constexpr T AlignUp(T num, U alignment) -{ - return (alignment > 0) ? ((num + static_cast(alignment) - 1) & (~(static_cast(alignment) - 1))) : num; -} - static AutoRegisterFilter g_registerFilterHelper("builtin.player.videodecoder"); class VideoDecoderFilter::DataCallbackImpl : public Plugin::DataCallbackHelper { diff --git a/engine/pipeline/filters/source/media_source_filter.cpp b/engine/pipeline/filters/source/media_source_filter.cpp index 29cfb252..2dbc5a5a 100644 --- a/engine/pipeline/filters/source/media_source_filter.cpp +++ b/engine/pipeline/filters/source/media_source_filter.cpp @@ -165,6 +165,7 @@ ErrorCode MediaSourceFilter::PullData(const std::string& outPort, uint64_t offse return ErrorCode::PLUGIN_NOT_FOUND; } ErrorCode err; + auto readSize = size; if (isSeekable_) { size_t totalSize = 0; if ((plugin_->GetSize(totalSize) == Status::OK) && (totalSize != 0)) { @@ -172,12 +173,12 @@ ErrorCode MediaSourceFilter::PullData(const std::string& outPort, uint64_t offse MEDIA_LOG_W("offset: %zu is larger than totalSize: %zu", offset, totalSize); return ErrorCode::END_OF_STREAM; } - if ((offset + size) > totalSize) { - size = totalSize - offset; + if ((offset + readSize) > totalSize) { + readSize = totalSize - offset; } auto realSize = data->GetMemory()->GetCapacity(); - if (size > realSize) { - size = realSize; + if (readSize > realSize) { + readSize = realSize; } MEDIA_LOG_D("totalSize_: %zu", totalSize); } @@ -193,7 +194,7 @@ ErrorCode MediaSourceFilter::PullData(const std::string& outPort, uint64_t offse if (data == nullptr) { data = std::make_shared(); } - err = TranslateError(plugin_->Read(data, size)); + err = TranslateError(plugin_->Read(data, readSize)); if (err == ErrorCode::SUCCESS) { position_ += data->GetMemory()->GetSize(); } diff --git a/engine/plugin/common/plugin_buffer.cpp b/engine/plugin/common/plugin_buffer.cpp index dcae1870..db00276e 100644 --- a/engine/plugin/common/plugin_buffer.cpp +++ b/engine/plugin/common/plugin_buffer.cpp @@ -18,12 +18,6 @@ namespace OHOS { namespace Media { namespace Plugin { -template -static constexpr T AlignUp(T num, U alignment) -{ - return (alignment > 0) ? ((num + static_cast(alignment) - 1) & (~(static_cast(alignment) - 1))) : num; -} - Memory::Memory(size_t capacity, std::shared_ptr bufData, size_t align) : capacity(capacity), alignment(align), offset(0), size(0), allocator(nullptr), addr(std::move(bufData)) { diff --git a/engine/plugin/common/plugin_buffer.h b/engine/plugin/common/plugin_buffer.h index eeaa44c6..d298450e 100644 --- a/engine/plugin/common/plugin_buffer.h +++ b/engine/plugin/common/plugin_buffer.h @@ -30,6 +30,18 @@ namespace Plugin { /// End of Stream Buffer Flag #define BUFFER_FLAG_EOS 0x00000001 +// Align value template +template +using MakeUnsigned = typename std::make_unsigned::type; + +template +static constexpr T AlignUp(T num, U alignment) +{ + return (alignment > 0) ? (static_cast((num + static_cast>(alignment) - 1)) & + static_cast((~(static_cast>(alignment) - 1)))) : + num; +} + /** * @brief Memory allocator, which is provided by the plugin implementer. * diff --git a/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h b/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h index 5d31db3e..7eafa3d5 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h +++ b/engine/plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h @@ -33,17 +33,6 @@ extern "C" { namespace OHOS { namespace Media { namespace Plugin { -template -using MakeUnsigned = typename std::make_unsigned::type; - -template -static constexpr T AlignUp(T num, U alignment) -{ - return alignment > 0 ? (static_cast((num + static_cast>(alignment) - 1)) & - static_cast((~(static_cast>(alignment) - 1)))) : - num; -} - std::string AVStrError(int errnum); /** diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp index b2df0b55..8ab93e4e 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp @@ -144,8 +144,12 @@ Status VideoFfmpegDecoderPlugin::Init() } avCodec_ = iter->second; cachedFrame_ = std::shared_ptr(av_frame_alloc(), [](AVFrame* fp) { av_frame_free(&fp); }); - state_ = State::INITIALIZED; videoDecParams_[Tag::REQUIRED_OUT_BUFFER_CNT] = (uint32_t)BUFFER_QUEUE_SIZE; + if (!decodeTask_) { + decodeTask_ = std::make_shared("videoFfmpegDecThread"); + decodeTask_->RegisterHandler([this] { ReceiveBuffer(); }); + } + state_ = State::INITIALIZED; MEDIA_LOG_I("Init success"); return Status::OK; } @@ -156,6 +160,10 @@ Status VideoFfmpegDecoderPlugin::Deinit() avCodec_.reset(); cachedFrame_.reset(); ResetLocked(); + if (decodeTask_) { + decodeTask_->Stop(); + decodeTask_.reset(); + } state_ = State::DESTROYED; return Status::OK; } @@ -370,6 +378,7 @@ Status VideoFfmpegDecoderPlugin::Start() state_ = State::RUNNING; } outBufferQ_.SetActive(true); + decodeTask_->Start(); MEDIA_LOG_I("Start success"); return Status::OK; } @@ -386,6 +395,8 @@ Status VideoFfmpegDecoderPlugin::Stop() state_ = State::INITIALIZED; } outBufferQ_.SetActive(false); + decodeTask_->Stop(); + MEDIA_LOG_I("Stop success"); return ret; } @@ -400,7 +411,7 @@ Status VideoFfmpegDecoderPlugin::Flush() { OSAL::ScopedLock l(lock_); if (avCodecContext_ != nullptr) { - avcodec_flush_buffers(avCodecContext_.get()); + // flush avcodec buffers } return Status::OK; } @@ -408,18 +419,17 @@ Status VideoFfmpegDecoderPlugin::Flush() Status VideoFfmpegDecoderPlugin::QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) { MEDIA_LOG_D("queue input buffer"); - Status status = SendBuffer(inputBuffer); - if (status != Status::OK) { - return status; - } - bool receiveOneFrame = false; - do { - receiveOneFrame = ReceiveBuffer(status); - if (status != Status::OK) { - break; - } - } while (receiveOneFrame); - return status; + if (inputBuffer->IsEmpty() && !(inputBuffer->flag & BUFFER_FLAG_EOS)) { + MEDIA_LOG_E("decoder does not support fd buffer"); + return Status::ERROR_INVALID_DATA; + } + Status ret = Status::OK; + { + OSAL::ScopedLock l(lock_); + ret = SendBufferLocked(inputBuffer); + } + NotifyInputBufferDone(inputBuffer); + return ret; } Status VideoFfmpegDecoderPlugin::SendBufferLocked(const std::shared_ptr& inputBuffer) @@ -463,25 +473,11 @@ Status VideoFfmpegDecoderPlugin::SendBufferLocked(const std::shared_ptr& auto ret = avcodec_send_packet(avCodecContext_.get(), packetPtr); if (ret < 0) { MEDIA_LOG_E("send buffer error %s", AVStrError(ret).c_str()); + return Status::ERROR_NO_MEMORY; } return Status::OK; } -Status VideoFfmpegDecoderPlugin::SendBuffer(const std::shared_ptr& inputBuffer) -{ - if (inputBuffer->IsEmpty() && !(inputBuffer->flag & BUFFER_FLAG_EOS)) { - MEDIA_LOG_E("decoder does not support fd buffer"); - return Status::ERROR_INVALID_DATA; - } - Status ret = Status::OK; - { - OSAL::ScopedLock l(lock_); - ret = SendBufferLocked(inputBuffer); - } - NotifyInputBufferDone(inputBuffer); - return ret; -} - void VideoFfmpegDecoderPlugin::CheckResolutionChange() { if ((width_ > 0) && (height_ > 0) && ((cachedFrame_->width != width_) || (cachedFrame_->height != height_))) { @@ -528,8 +524,7 @@ void VideoFfmpegDecoderPlugin::CalculateFrameSizes(size_t& ySize, size_t& uvSize } } -Status VideoFfmpegDecoderPlugin::FillFrameBuffer(const std::shared_ptr& frameBuffer, bool& receiveOneFrame, - bool& notifyBufferDone) +Status VideoFfmpegDecoderPlugin::FillFrameBuffer(const std::shared_ptr& frameBuffer) { MEDIA_LOG_D("receive one frame: %d, picture type: %d, pixel format: %d, packet size: %d", cachedFrame_->key_frame, static_cast(cachedFrame_->pict_type), static_cast(cachedFrame_->format), @@ -562,8 +557,6 @@ Status VideoFfmpegDecoderPlugin::FillFrameBuffer(const std::shared_ptr& auto frameBufferMem = frameBuffer->GetMemory(); if (frameBufferMem->GetCapacity() < frameSize) { MEDIA_LOG_W("output buffer size is not enough: real[%zu], need[%zu]", frameBufferMem->GetCapacity(), frameSize); - receiveOneFrame = false; - notifyBufferDone = false; return Status::ERROR_NO_MEMORY; } if (cachedFrame_->format == AV_PIX_FMT_YUV420P) { @@ -575,61 +568,50 @@ Status VideoFfmpegDecoderPlugin::FillFrameBuffer(const std::shared_ptr& frameBufferMem->Write(cachedFrame_->data[1], uvSize); } frameBuffer->pts = static_cast(cachedFrame_->pts); - notifyBufferDone = true; - receiveOneFrame = true; return Status::OK; } -void VideoFfmpegDecoderPlugin::ReceiveBufferLocked(Status& status, const std::shared_ptr& frameBuffer, - bool& receiveOneFrame, bool& notifyBufferDone) +Status void VideoFfmpegDecoderPlugin::ReceiveBufferLocked(const std::shared_ptr& frameBuffer) { if (state_ != State::RUNNING) { MEDIA_LOG_W("queue input buffer in wrong state"); - status = Status::ERROR_WRONG_STATE; - receiveOneFrame = false; - notifyBufferDone = false; - return; + return Status::ERROR_WRONG_STATE; } + Status status; auto ret = avcodec_receive_frame(avCodecContext_.get(), cachedFrame_.get()); if (ret >= 0) { - status = FillFrameBuffer(frameBuffer, receiveOneFrame, notifyBufferDone); + status = FillFrameBuffer(frameBuffer); } else if (ret == AVERROR_EOF) { MEDIA_LOG_I("eos received"); frameBuffer->GetMemory()->Reset(); - notifyBufferDone = true; - receiveOneFrame = false; + avcodec_flush_buffers(avCodecContext_.get()); status = Status::END_OF_STREAM; } else { MEDIA_LOG_I("video decoder receive error: %s", AVStrError(ret).c_str()); - notifyBufferDone = false; - receiveOneFrame = false; - status = Status::OK; + status = Status::ERROR_UNKNOWN; } av_frame_unref(cachedFrame_.get()); + return status; } -bool VideoFfmpegDecoderPlugin::ReceiveBuffer(Status& status) +void VideoFfmpegDecoderPlugin::ReceiveBuffer() { - bool notifyBufferDone = false; - bool receiveOneFrame = false; std::shared_ptr frameBuffer = outBufferQ_.Pop(); - if (frameBuffer == nullptr || frameBuffer->IsEmpty()) { + if (frameBuffer == nullptr || frameBuffer->IsEmpty() || + frameBuffer->GetBufferMeta()->GetType() != BufferMetaType::VIDEO) { MEDIA_LOG_W("cannot fetch valid buffer to output"); return false; } - if (frameBuffer->GetBufferMeta()->GetType() != BufferMetaType::VIDEO) { - MEDIA_LOG_W("Cannot handle non-video buffer"); - receiveOneFrame = false; - } else { + Status status; + { OSAL::ScopedLock l(lock_); - ReceiveBufferLocked(status, frameBuffer, receiveOneFrame, notifyBufferDone); + status = ReceiveBufferLocked(frameBuffer); } - if (notifyBufferDone) { + if (status == Status::OK || status == Status::END_OF_STREAM) { NotifyOutputBufferDone(frameBuffer); } else { outBufferQ_.Push(frameBuffer); } - return receiveOneFrame; } void VideoFfmpegDecoderPlugin::NotifyInputBufferDone(const std::shared_ptr& input) diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h index 155bafae..826caa56 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h @@ -20,6 +20,7 @@ #include #include +#include "osal/thread/task.h" #include "foundation/blocking_queue.h" #include "plugin/interface/codec_plugin.h" @@ -92,24 +93,23 @@ private: template void FindInParameterMapThenAssignLocked(Tag tag, T &assign); - Status SendBuffer(const std::shared_ptr &inputBuffer); Status SendBufferLocked(const std::shared_ptr &inputBuffer); void CalculateFrameSizes(size_t &ySize, size_t &uvSize, size_t &frameSize); - Status FillFrameBuffer(const std::shared_ptr &frameBuffer, bool &receiveOneFrame, bool ¬ifyBufferDone); - bool ReceiveBuffer(Status &err); - void ReceiveBufferLocked(Status &status, const std::shared_ptr &ioInfo, - bool &receiveOneFrame, bool ¬ifyBufferDone); + + Status FillFrameBuffer(const std::shared_ptr &frameBuffer); + + Status ReceiveBufferLocked(const std::shared_ptr &frameBuffer); void CheckResolutionChange(); + void ReceiveBuffer(); + #ifdef DUMP_RAW_DATA std::ofstream dumpData_; void DumpVideoRawOutData(); #endif - void CopyYUV2NativeBuffer(const std::shared_ptr &ioInfo, const uint32_t frameSize); - void NotifyInputBufferDone(const std::shared_ptr &input); void NotifyOutputBufferDone(const std::shared_ptr &output); @@ -129,6 +129,7 @@ private: State state_ {State::CREATED}; std::shared_ptr avCodecContext_ {}; OHOS::Media::BlockingQueue> outBufferQ_; + std::shared_ptr decodeTask_; }; } } diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp index 7d179b01..0968f51c 100644 --- a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp @@ -15,11 +15,10 @@ #define LOG_TAG "FileSourcePlugin" +#include "file_source_plugin.h" #include "foundation/log.h" - #include "plugin/common/plugin_types.h" #include "plugin/core/plugin_manager.h" -#include "file_source_plugin.h" #include "plugin/common/plugin_buffer.h" namespace OHOS { diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp index 0087ac5a..64fe8ca5 100644 --- a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp @@ -51,7 +51,7 @@ void* StreamSourceAllocator::Alloc(size_t size) void StreamSourceAllocator::Free(void* ptr) { if (ptr != nullptr) { - delete[](uint8_t*) ptr; + delete[] (uint8_t*) ptr; } } -- Gitee From ea186146ce99530488fb490afdf8f732cba73a09 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Thu, 30 Sep 2021 21:09:22 +0800 Subject: [PATCH 28/34] CMakeLists.txt supported to be included in demo. Signed-off-by: Chen Guodong --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3296d085..a61f8c5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,6 @@ add_definitions(-DOSAL_OHOS) # add -DVIDEO_SUPPORT to enable video play set(CMAKE_VERBOSE_MAKEFILE ON) -add_subdirectory(engine/plugin/plugins/source/file_source) -add_subdirectory(engine) -add_subdirectory(tests) +add_subdirectory(${DEV_TOP_DIR}/histreamer/engine/plugin/plugins/source/file_source file_source.out) +add_subdirectory(${DEV_TOP_DIR}/histreamer/engine engine.out) +add_subdirectory(${DEV_TOP_DIR}/histreamer/tests tests.out) -- Gitee From f56708fe5f34a97fde52f1231663b06404236bd6 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Fri, 1 Oct 2021 07:19:17 +0800 Subject: [PATCH 29/34] fix compile error. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 3b2391bc..9992328f 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -82,6 +82,7 @@ if (WIN32) swresample SDL2 ) +elseif (CMAKE_HOST_UNIX) else () link_libraries( log -- Gitee From 73c5ec7e60b600a878b5633b51afbdc0be0ea3b8 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Fri, 1 Oct 2021 07:38:46 +0800 Subject: [PATCH 30/34] fix some linux pc tests link error, z not found. Signed-off-by: Chen Guodong --- tests/ut/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index 8c8971cb..6dd9a5fd 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -92,7 +92,7 @@ else () ${ffmpeg_lib_path}/libswresample.a ${ffmpeg_lib_path}/liblzma.a m - z + /usr/lib/x86_64-linux-gnu/libz.so.1 ${sdl_lib_path}/libSDL2.a gtest gtest_main -- Gitee From 72a93e8ffb7b9ae64059b1446c312bbbbb17eae7 Mon Sep 17 00:00:00 2001 From: Chen Guodong Date: Sat, 2 Oct 2021 06:33:08 +0800 Subject: [PATCH 31/34] fix link error. Signed-off-by: Chen Guodong --- engine/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 9992328f..e5895d0b 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -82,7 +82,7 @@ if (WIN32) swresample SDL2 ) -elseif (CMAKE_HOST_UNIX) +elseif (LINUX_DEMO) else () link_libraries( log -- Gitee From 57a9be2fd00d2e7e6feaf7564cc23213681cc872 Mon Sep 17 00:00:00 2001 From: ancimoon Date: Fri, 8 Oct 2021 01:07:22 +0000 Subject: [PATCH 32/34] !20 add default state management in plugin core Signed-off-by: Hu Change --- engine/plugin/BUILD.gn | 6 +- engine/plugin/core/audio_sink.cpp | 39 ++++++- engine/plugin/core/base.cpp | 105 +++++++++++++++--- engine/plugin/core/base.h | 17 +-- engine/plugin/core/codec.cpp | 2 +- engine/plugin/core/demuxer.cpp | 4 +- engine/plugin/core/utils.h | 71 ++++++++++++ engine/plugin/core/video_sink.cpp | 35 +++++- engine/plugin/interface/audio_sink_plugin.h | 2 + engine/plugin/interface/codec_plugin.h | 2 + engine/plugin/interface/demuxer_plugin.h | 2 + engine/plugin/interface/plugin_base.h | 16 +++ engine/plugin/interface/source_plugin.h | 2 + engine/plugin/interface/video_sink_plugin.h | 2 + .../audio_ffmpeg_decoder_plugin.cpp | 6 +- .../audio_ffmpeg_decoder_plugin.h | 1 - .../demuxer/ffmpeg_demuxer_plugin.cpp | 6 +- .../demuxer/ffmpeg_demuxer_plugin.h | 1 - .../video_ffmpeg_decoder_plugin.cpp | 6 +- .../video_ffmpeg_decoder_plugin.h | 1 - .../plugins/hdi_adapter/sink/hos_au_sink.cpp | 6 +- .../plugins/hdi_adapter/sink/hos_au_sink.h | 2 - .../sdl/audio_sink/sdl_audio_sink_plugin.cpp | 4 +- .../sdl/audio_sink/sdl_audio_sink_plugin.h | 1 - .../sdl/video_sink/sdl_video_sink_plugin.cpp | 2 +- .../source/file_source/file_source_plugin.cpp | 4 +- .../source/file_source/file_source_plugin.h | 7 +- .../stream_source/stream_source_plugin.cpp | 10 +- .../stream_source/stream_source_plugin.h | 3 +- 29 files changed, 299 insertions(+), 66 deletions(-) create mode 100644 engine/plugin/core/utils.h diff --git a/engine/plugin/BUILD.gn b/engine/plugin/BUILD.gn index e3172d48..d207a5d4 100644 --- a/engine/plugin/BUILD.gn +++ b/engine/plugin/BUILD.gn @@ -52,7 +52,7 @@ if (defined(ohos_lite)) { "core/plugin_manager.cpp", "core/source.cpp", ] - defines = [ "HI3516DV300" ] + defines = [ "HI3516DV300", "OSAL_OHOS" ] if (plugin_dynamic_register) { sources += [ "engine/plugin/core/plugin_loader.cpp", @@ -62,6 +62,10 @@ if (defined(ohos_lite)) { include_dirs = [ ".", "../", + "../foundation", + "../foundation/osal", + "//foundation/multimedia/utils/lite/interfaces/kits", + "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits", ] cflags = [ "-O2", "-fPIC", "-Wall" ] cflags_cc = cflags diff --git a/engine/plugin/core/audio_sink.cpp b/engine/plugin/core/audio_sink.cpp index c3b457e1..5b175d61 100644 --- a/engine/plugin/core/audio_sink.cpp +++ b/engine/plugin/core/audio_sink.cpp @@ -13,25 +13,54 @@ * limitations under the License. */ +#define LOG_TAG "PluginCoreAuSink" + #include "audio_sink.h" +#include "foundation/log.h" +#include "foundation/osal/thread/scoped_lock.h" #include "interface/audio_sink_plugin.h" +#include "utils.h" using namespace OHOS::Media::Plugin; AudioSink::AudioSink(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin) - : Base(pkgVer, apiVer, plugin), audioSink(std::move(plugin)) -{ -} + : Base(pkgVer, apiVer, plugin), audioSink(std::move(plugin)) {} Status AudioSink::Pause() { - return audioSink->Pause(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + if (pluginState_ != State::RUNNING) { + MEDIA_LOG_I("plugin %s pause in status %s, ignore pause", plugin_->GetName().c_str(), + GetStateString(pluginState_.load())); + return Status::OK; + } + auto ret = audioSink->Pause(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::PAUSED; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status AudioSink::Resume() { - return audioSink->Resume(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + if (pluginState_ != State::PAUSED) { + MEDIA_LOG_I("plugin %s resume in status %s, ignore pause", plugin_->GetName().c_str(), + GetStateString(pluginState_.load())); + return Status::OK; + } + auto ret = audioSink->Resume(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::RUNNING; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status AudioSink::Flush() diff --git a/engine/plugin/core/base.cpp b/engine/plugin/core/base.cpp index 4a563b96..4007ec97 100644 --- a/engine/plugin/core/base.cpp +++ b/engine/plugin/core/base.cpp @@ -13,59 +13,138 @@ * limitations under the License. */ +#define LOG_TAG "PluginCoreBase" + #include "base.h" +#include "foundation/log.h" +#include "foundation/osal/thread/scoped_lock.h" #include "plugin/interface/plugin_base.h" +#include "utils.h" using namespace OHOS::Media::Plugin; Base::Base(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr base) - : pkgVersion(pkgVer), apiVersion(apiVer), plugin(std::move(base)) -{ -} + : pkgVersion_(pkgVer), apiVersion_(apiVer), plugin_(std::move(base)) {} Status Base::Init() { - return plugin->Init(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + RETURN_WRONG_STATE_IF_CON_TRUE(pluginState_ == State::DESTROYED, plugin_, pluginState_.load()); + if (pluginState_ != State::CREATED) { + MEDIA_LOG_I("plugin %s has been already inited", plugin_->GetName().c_str()); + return Status::OK; + } + auto ret = plugin_->Init(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::INITIALIZED; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status Base::Deinit() { - return plugin->Deinit(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + if (pluginState_ == State::DESTROYED) { + MEDIA_LOG_I("plugin %s already deinited, no need to destroy any more", plugin_->GetName().c_str()); + return Status::OK; + } + auto ret = plugin_->Deinit(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + pluginState_ = State::DESTROYED; + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status Base::Prepare() { - return plugin->Prepare(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + RETURN_WRONG_STATE_IF_CON_TRUE(pluginState_ != State::PREPARED && pluginState_ != State::INITIALIZED, + plugin_, pluginState_.load()); + if (pluginState_ == State::PREPARED) { + MEDIA_LOG_I("plugin %s already prepared, ignore this prepare", plugin_->GetName().c_str()); + return Status::OK; + } + auto ret = plugin_->Prepare(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::PREPARED; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status Base::Reset() { - return plugin->Reset(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + RETURN_WRONG_STATE_IF_CON_TRUE(pluginState_ == State::RUNNING || pluginState_ == State::PAUSED || + pluginState_ == State::DESTROYED, plugin_, pluginState_.load()); + if (pluginState_ == State::CREATED || pluginState_ == State::INITIALIZED) { + MEDIA_LOG_I("plugin %s no need to reset in state %s", plugin_->GetName().c_str(), + GetStateString(pluginState_.load())); + return Status::OK; + } + auto ret = plugin_->Reset(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + pluginState_ = State::INITIALIZED; + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status Base::Start() { - return plugin->Start(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + RETURN_WRONG_STATE_IF_CON_TRUE(pluginState_ != State::PREPARED && pluginState_ != State::RUNNING, plugin_, + pluginState_.load()); + if (pluginState_ == State::RUNNING) { + MEDIA_LOG_I("plugin %s already in running state, ignore start", plugin_->GetName().c_str()); + return Status::OK; + } + auto ret = plugin_->Start(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::RUNNING; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status Base::Stop() { - return plugin->Stop(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + if (pluginState_ != State::RUNNING && pluginState_ != State::PAUSED) { + MEDIA_LOG_I("plugin %s not running or paused, no need to stop", plugin_->GetName().c_str()); + return Status::OK; + } + auto ret = plugin_->Stop(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::PREPARED; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } bool Base::IsParameterSupported(Tag tag) { - return plugin->IsParameterSupported(tag); + return plugin_->IsParameterSupported(tag); } Status Base::GetParameter(Tag tag, ValueType& value) { - return plugin->GetParameter(tag, value); + return plugin_->GetParameter(tag, value); } Status Base::SetParameter(Tag tag, const ValueType& value) { - return plugin->SetParameter(tag, value); + return plugin_->SetParameter(tag, value); } Status Base::GetState(State &state) @@ -76,5 +155,5 @@ Status Base::GetState(State &state) std::shared_ptr Base::GetAllocator() { - return plugin->GetAllocator(); + return plugin_->GetAllocator(); } diff --git a/engine/plugin/core/base.h b/engine/plugin/core/base.h index 36761221..f718dc77 100644 --- a/engine/plugin/core/base.h +++ b/engine/plugin/core/base.h @@ -13,14 +13,16 @@ * limitations under the License. */ -#ifndef HISTREAMER_PLUGIN_CORE_PLUGIN_H -#define HISTREAMER_PLUGIN_CORE_PLUGIN_H +#ifndef HISTREAMER_PLUGIN_CORE_BASE_H +#define HISTREAMER_PLUGIN_CORE_BASE_H +#include #include #include "common/plugin_types.h" #include "common/plugin_tags.h" #include "common/plugin_buffer.h" +#include "foundation/osal/thread/mutex.h" namespace OHOS { namespace Media { @@ -63,15 +65,16 @@ protected: Base(uint32_t pkgVer, uint32_t apiVer, std::shared_ptr plugin); protected: - const uint32_t pkgVersion; + const uint32_t pkgVersion_; - const uint32_t apiVersion; + const uint32_t apiVersion_; - std::shared_ptr plugin; + std::shared_ptr plugin_; - State pluginState_ {State::CREATED}; + OHOS::Media::OSAL::Mutex stateChangeMutex_ {}; + std::atomic pluginState_ {State::CREATED}; }; } // namespace Plugin } // namespace Media } // namespace OHOS -#endif // HISTREAMER_PLUGIN_CORE_PLUGIN_H +#endif // HISTREAMER_PLUGIN_CORE_BASE_H diff --git a/engine/plugin/core/codec.cpp b/engine/plugin/core/codec.cpp index d2018840..e7382af6 100644 --- a/engine/plugin/core/codec.cpp +++ b/engine/plugin/core/codec.cpp @@ -71,6 +71,6 @@ private: Status Codec::SetDataCallback(const std::weak_ptr& helper) { - dataCallback_ = std::make_shared(pkgVersion, helper); + dataCallback_ = std::make_shared(pkgVersion_, helper); return codec_->SetDataCallback(dataCallback_); } diff --git a/engine/plugin/core/demuxer.cpp b/engine/plugin/core/demuxer.cpp index a34e084a..17c8e474 100644 --- a/engine/plugin/core/demuxer.cpp +++ b/engine/plugin/core/demuxer.cpp @@ -33,14 +33,14 @@ Status Demuxer::SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) Status Demuxer::SetDataSource(const std::shared_ptr& source) { - return demuxer_->SetDataSource(std::make_shared(pkgVersion, source)); + return demuxer_->SetDataSource(std::make_shared(pkgVersion_, source)); } Status Demuxer::GetMediaInfo(MediaInfoHelper& mediaInfo) { MediaInfo info; demuxer_->GetMediaInfo(info); - ConvertToMediaInfoHelper(pkgVersion, info, mediaInfo); + ConvertToMediaInfoHelper(pkgVersion_, info, mediaInfo); return Status::OK; } diff --git a/engine/plugin/core/utils.h b/engine/plugin/core/utils.h new file mode 100644 index 00000000..2faefb3e --- /dev/null +++ b/engine/plugin/core/utils.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * 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 HISTREAMER_PLUGIN_CORE_UTILS_H +#define HISTREAMER_PLUGIN_CORE_UTILS_H + +namespace OHOS { +namespace Media { +namespace Plugin { +#define LOG_WARN_IF_NOT_OK(plugin, status) \ +do { \ + if ((status) != Status::OK) { \ + MEDIA_LOG_W("plugin %s %s failed with status %d", (plugin)->GetName().c_str(), __FUNCTION__, status); \ + } \ +} while (0) + +#define RETURN_WRONG_STATE_IF_CON_TRUE(condition, plugin, state) \ +do { \ + if (condition) { \ + MEDIA_LOG_E("plugin %s cannot %s in state %s", (plugin)->GetName().c_str(), __FUNCTION__, \ + GetStateString(state)); \ + return Status::ERROR_WRONG_STATE; \ + } \ +} while (0) + +#define RETURN_WRONG_STATE_IF_CON_FALSE(condition, plugin, state) \ +do { \ + if (!(condition)) { \ + MEDIA_LOG_E("plugin %s cannot %s in state %s", (plugin)->GetName().c_str(), __FUNCTION__, \ + GetStateString(state)); \ + return Status::ERROR_WRONG_STATE; \ + } \ +} while (0) + +inline const char* GetStateString(OHOS::Media::Plugin::State state) +{ + using namespace OHOS::Media::Plugin; + switch (state) { + case State::CREATED: + return "Created"; + case State::INITIALIZED: + return "Initialized"; + case State::DESTROYED: + return "Destroyed"; + case State::PREPARED: + return "Prepared"; + case State::RUNNING: + return "Running"; + case State::PAUSED: + return "Paused"; + default: + return "Invalid"; + } +} +} +} +} + +#endif //HISTREAMER_PLUGIN_CORE_UTILS_H diff --git a/engine/plugin/core/video_sink.cpp b/engine/plugin/core/video_sink.cpp index a0ecd4c6..598d70a4 100644 --- a/engine/plugin/core/video_sink.cpp +++ b/engine/plugin/core/video_sink.cpp @@ -13,9 +13,14 @@ * limitations under the License. */ +#define LOG_TAG "PluginCoreViSink" + #include "video_sink.h" +#include "foundation/log.h" +#include "foundation/osal/thread/scoped_lock.h" #include "interface/video_sink_plugin.h" +#include "utils.h" using namespace OHOS::Media::Plugin; @@ -26,12 +31,38 @@ VideoSink::VideoSink(uint32_t pkgVer, uint32_t apiVer, std::shared_ptrPause(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + if (pluginState_ != State::RUNNING) { + MEDIA_LOG_I("plugin %s pause in status %s, ignore pause", plugin_->GetName().c_str(), + GetStateString(pluginState_.load())); + return Status::OK; + } + auto ret = videoSink->Pause(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::PAUSED; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status VideoSink::Resume() { - return videoSink->Resume(); + MEDIA_LOG_I("%s Enter.", __FUNCTION__); + OSAL::ScopedLock lock(stateChangeMutex_); + if (pluginState_ != State::PAUSED) { + MEDIA_LOG_I("plugin %s resume in status %s, ignore pause", plugin_->GetName().c_str(), + GetStateString(pluginState_.load())); + return Status::OK; + } + auto ret = videoSink->Resume(); + LOG_WARN_IF_NOT_OK(plugin_, ret); + if (ret == Status::OK) { + pluginState_ = State::RUNNING; + } + MEDIA_LOG_I("%s Exit.", __FUNCTION__); + return ret; } Status VideoSink::Flush() diff --git a/engine/plugin/interface/audio_sink_plugin.h b/engine/plugin/interface/audio_sink_plugin.h index 18ffdd51..fc58a01c 100644 --- a/engine/plugin/interface/audio_sink_plugin.h +++ b/engine/plugin/interface/audio_sink_plugin.h @@ -33,6 +33,8 @@ namespace Plugin { * @version 1.0 */ struct AudioSinkPlugin : public PluginBase { + /// constructor + explicit AudioSinkPlugin(std::string name): PluginBase(std::forward(name)){} /** * @brief Get the mute operation set for the audio. * diff --git a/engine/plugin/interface/codec_plugin.h b/engine/plugin/interface/codec_plugin.h index d648d8c6..617e058b 100644 --- a/engine/plugin/interface/codec_plugin.h +++ b/engine/plugin/interface/codec_plugin.h @@ -61,6 +61,8 @@ struct DataCallback { * @version 1.0 */ struct CodecPlugin : public PluginBase { + /// constructor + explicit CodecPlugin(std::string name): PluginBase(std::forward(name)) {} /** * @brief Queues input data * diff --git a/engine/plugin/interface/demuxer_plugin.h b/engine/plugin/interface/demuxer_plugin.h index 19f5334e..777e1eb4 100644 --- a/engine/plugin/interface/demuxer_plugin.h +++ b/engine/plugin/interface/demuxer_plugin.h @@ -84,6 +84,8 @@ struct DataSource { * @version 1.0 */ struct DemuxerPlugin : public PluginBase { + /// constructor + explicit DemuxerPlugin(std::string name): PluginBase(std::forward(name)) {} /** * @brief Set the data source to demuxer component. * diff --git a/engine/plugin/interface/plugin_base.h b/engine/plugin/interface/plugin_base.h index 1f616f26..41b00cc8 100644 --- a/engine/plugin/interface/plugin_base.h +++ b/engine/plugin/interface/plugin_base.h @@ -64,9 +64,22 @@ struct Callback { * @version 1.0 */ struct PluginBase { + /// Constructor + explicit PluginBase(std::string name): pluginName_(std::move(name)) {} + /// Destructor virtual ~PluginBase() = default; + /** + * @brief Get plugin name + * + * @return plugin name + */ + std::string GetName() const + { + return pluginName_; + } + /** * @brief Plugin initialization, which is used to load external resources or plugin common resources. * @@ -228,6 +241,9 @@ struct PluginBase { * @retval ERROR_WRONG_STATE: Call this function in non wrong state. */ virtual Status SetCallback(const std::shared_ptr &cb) = 0; + +protected: + const std::string pluginName_; }; } // namespace Plugin } // namespace Media diff --git a/engine/plugin/interface/source_plugin.h b/engine/plugin/interface/source_plugin.h index 3eae469d..16de3668 100644 --- a/engine/plugin/interface/source_plugin.h +++ b/engine/plugin/interface/source_plugin.h @@ -34,6 +34,8 @@ namespace Plugin { * @version 1.0 */ struct SourcePlugin : public PluginBase { + /// constructor + explicit SourcePlugin(std::string name): PluginBase(std::forward(name)) {} /** * @brief Set the data source to demuxer component. * diff --git a/engine/plugin/interface/video_sink_plugin.h b/engine/plugin/interface/video_sink_plugin.h index 86552d50..857f5aee 100644 --- a/engine/plugin/interface/video_sink_plugin.h +++ b/engine/plugin/interface/video_sink_plugin.h @@ -33,6 +33,8 @@ namespace Plugin { * @version 1.0 */ struct VideoSinkPlugin : public PluginBase { + /// constructor + explicit VideoSinkPlugin(std::string name): PluginBase(std::forward(name)) {} /** * @brief Pauses video rendering * diff --git a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp index 815f95b3..b092104a 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.cpp @@ -176,16 +176,16 @@ namespace OHOS { namespace Media { namespace Plugin { AudioFfmpegDecoderPlugin::AudioFfmpegDecoderPlugin(std::string name) - : name_(std::move(name)), outBufferQ_("adecPluginQueue", BUFFER_QUEUE_SIZE) + : CodecPlugin(std::move(name)), outBufferQ_("adecPluginQueue", BUFFER_QUEUE_SIZE) { } Status AudioFfmpegDecoderPlugin::Init() { OSAL::ScopedLock l(lock_); - auto ite = codecMap.find(name_); + auto ite = codecMap.find(pluginName_); if (ite == codecMap.end()) { - MEDIA_LOG_W("cannot find codec with name %s", name_.c_str()); + MEDIA_LOG_W("cannot find codec with name %s", pluginName_.c_str()); return Status::ERROR_UNSUPPORTED_FORMAT; } avCodec_ = ite->second; diff --git a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h index c7d04e73..faa1c244 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/audio_decoder/audio_ffmpeg_decoder_plugin.h @@ -101,7 +101,6 @@ private: void NotifyOutputBufferDone(const std::shared_ptr& output); - std::string name_; std::shared_ptr avCodec_ {}; std::map audioParameter_ {}; std::vector paddedBuffer_ {}; diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp index 95ca4287..cb19dd2c 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp @@ -74,7 +74,7 @@ void FFmpegDemuxerPlugin::DemuxerPluginAllocator::Free(void* ptr) } FFmpegDemuxerPlugin::FFmpegDemuxerPlugin(std::string name) - : name_(std::move(name)), + : DemuxerPlugin(std::move(name)), ioContext_(), callback_(nullptr), pluginImpl_(nullptr), @@ -83,7 +83,7 @@ FFmpegDemuxerPlugin::FFmpegDemuxerPlugin(std::string name) mediaInfo_(nullptr), selectedTrackIds_() { - MEDIA_LOG_I("ctor called, plugin name: %s", name_.c_str()); + MEDIA_LOG_I("ctor called, plugin name: %s", pluginName_.c_str()); } FFmpegDemuxerPlugin::~FFmpegDemuxerPlugin() @@ -96,7 +96,7 @@ Status FFmpegDemuxerPlugin::Init() { MEDIA_LOG_I("Init called."); Reset(); - pluginImpl_ = g_pluginInputFormat[name_]; + pluginImpl_ = g_pluginInputFormat[pluginName_]; return pluginImpl_ ? Status::OK : Status::ERROR_UNSUPPORTED_FORMAT; } diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h index 1b71a654..fc87faaf 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h @@ -97,7 +97,6 @@ private: static int64_t AVSeek(void* opaque, int64_t offset, int whence); - std::string name_; IOContext ioContext_; std::shared_ptr callback_ {}; std::shared_ptr pluginImpl_; diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp index 8ab93e4e..cd7093c4 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.cpp @@ -130,16 +130,16 @@ VideoPixelFormat TranslatePixelFormat(int32_t pixelFormat) } // namespace VideoFfmpegDecoderPlugin::VideoFfmpegDecoderPlugin(std::string name) - : name_(std::move(name)), outBufferQ_("vdecPluginQueue", BUFFER_QUEUE_SIZE) + : CodecPlugin(std::move(name)), outBufferQ_("vdecPluginQueue", BUFFER_QUEUE_SIZE) { } Status VideoFfmpegDecoderPlugin::Init() { OSAL::ScopedLock l(lock_); - auto iter = codecMap.find(name_); + auto iter = codecMap.find(pluginName_); if (iter == codecMap.end()) { - MEDIA_LOG_W("cannot find codec with name %s", name_.c_str()); + MEDIA_LOG_W("cannot find codec with name %s", pluginName_.c_str()); return Status::ERROR_UNSUPPORTED_FORMAT; } avCodec_ = iter->second; diff --git a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h index 826caa56..b9a7fc8b 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/video_decoder/video_ffmpeg_decoder_plugin.h @@ -113,7 +113,6 @@ private: void NotifyInputBufferDone(const std::shared_ptr &input); void NotifyOutputBufferDone(const std::shared_ptr &output); - std::string name_; std::shared_ptr avCodec_; std::map videoDecParams_ {}; std::vector paddedBuffer_; diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp index c10f20d3..d7fa44e2 100644 --- a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.cpp @@ -161,7 +161,7 @@ namespace Media { namespace HosLitePlugin { using namespace OHOS::Media::Plugin; -HdiSink::HdiSink(std::string name) : adapterName_(std::move(name)), audioManager_(nullptr) +HdiSink::HdiSink(std::string name) : Plugin::AudioSinkPlugin(std::move(name)), audioManager_(nullptr) { // default is media sampleAttributes_.type = AUDIO_IN_MEDIA; @@ -192,7 +192,7 @@ Status HdiSink::Init() } for (int32_t index = 0; index < adapterSize; index++) { const auto &desc = descriptors[index]; - if (adapterName_ != desc.adapterName) { + if (pluginName_ != desc.adapterName) { continue; } @@ -202,7 +202,7 @@ Status HdiSink::Init() adapterDescriptor_ = descriptors[index]; } if (audioAdapter_ == nullptr) { - MEDIA_LOG_E("cannot find adapter with name %s", adapterName_.c_str()); + MEDIA_LOG_E("cannot find adapter with name %s", pluginName_.c_str()); return Status::ERROR_UNKNOWN; } if (!renderThread_) { diff --git a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h index 7dd5a6b6..006bf7d7 100644 --- a/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h +++ b/engine/plugin/plugins/hdi_adapter/sink/hos_au_sink.h @@ -94,8 +94,6 @@ private: void DoRender(); private: - const std::string adapterName_; - std::atomic pluginState_ {OHOS::Media::Plugin::State::CREATED}; OHOS::Media::OSAL::Mutex renderMutex_ {}; diff --git a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp index 2cefd34e..5444bfa1 100644 --- a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp +++ b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.cpp @@ -96,7 +96,7 @@ namespace OHOS { namespace Media { namespace Plugin { SdlAudioSinkPlugin::SdlAudioSinkPlugin(std::string name) - : aliasName_(std::move(name)), + : AudioSinkPlugin(std::move(name)), transformCache_((MAX_AUDIO_FRAME_SIZE * 3) / 2), // 3, 2 mixCache_((MAX_AUDIO_FRAME_SIZE * 3) / 2) // 3, 2 { @@ -277,12 +277,14 @@ Status SdlAudioSinkPlugin::SetSpeed(float speed) Status SdlAudioSinkPlugin::Pause() { + MEDIA_LOG_I("Paused"); SDL_PauseAudio(1); return Status::OK; } Status SdlAudioSinkPlugin::Resume() { + MEDIA_LOG_I("Resume"); SDL_PauseAudio(0); return Status::OK; } diff --git a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h index adeb5076..885b8788 100644 --- a/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h +++ b/engine/plugin/plugins/sink/sdl/audio_sink/sdl_audio_sink_plugin.h @@ -92,7 +92,6 @@ public: private: void AudioCallback(void* userdata, uint8_t* stream, int len); - std::string aliasName_ {}; std::vector transformCache_ {}; std::vector mixCache_ {}; std::unique_ptr rb {}; diff --git a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp index 73c95ac2..9599f283 100644 --- a/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp +++ b/engine/plugin/plugins/sink/sdl/video_sink/sdl_video_sink_plugin.cpp @@ -96,7 +96,7 @@ static uint32_t TranslatePixelFormat(const VideoPixelFormat pixelFormat) #define ALIGN_UP_16(value) (((value) + 15) & (~15)) SdlVideoSinkPlugin::SdlVideoSinkPlugin(std::string name) - : windowWidth_(DEFAULT_WINDOW_WIDTH), windowHeight_(DEFAULT_WINDOW_HEIGHT) + : VideoSinkPlugin(std::move(name)), windowWidth_(DEFAULT_WINDOW_WIDTH), windowHeight_(DEFAULT_WINDOW_HEIGHT) { } diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp index 0968f51c..5c28670d 100644 --- a/engine/plugin/plugins/source/file_source/file_source_plugin.cpp +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.cpp @@ -57,8 +57,8 @@ void FileSourceAllocator::Free(void* ptr) } } -FileSourcePlugin::FileSourcePlugin(const std::string &name) - : name_(name), state_(State::CREATED), fileSize_(0), isSeekable_(true), position_(0) +FileSourcePlugin::FileSourcePlugin(std::string name) + : SourcePlugin(std::move(name)), state_(State::CREATED), fileSize_(0), isSeekable_(true), position_(0) { MEDIA_LOG_D("IN"); state_ = State::CREATED; diff --git a/engine/plugin/plugins/source/file_source/file_source_plugin.h b/engine/plugin/plugins/source/file_source/file_source_plugin.h index 521e3949..619c1284 100644 --- a/engine/plugin/plugins/source/file_source/file_source_plugin.h +++ b/engine/plugin/plugins/source/file_source/file_source_plugin.h @@ -26,7 +26,7 @@ namespace Plugin { class FileSourceAllocator : public Allocator { public: FileSourceAllocator() = default; - ~FileSourceAllocator() = default; + ~FileSourceAllocator() override= default; void* Alloc(size_t size) override; void Free(void* ptr) override; @@ -34,8 +34,8 @@ public: class FileSourcePlugin : public SourcePlugin { public: - explicit FileSourcePlugin(const std::string &name); - ~FileSourcePlugin(); + explicit FileSourcePlugin(std::string name); + ~FileSourcePlugin() override; Status Init() override; Status Deinit() override; @@ -56,7 +56,6 @@ public: Status SeekTo(uint64_t offset) override; private: - std::string name_; State state_; std::string fileName_ {}; std::ifstream fin_; diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp index 64fe8ca5..4f310ac8 100644 --- a/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.cpp @@ -75,13 +75,9 @@ void StreamSourceCallback::QueueBuffer(size_t index, size_t offset, size_t size, dataSource_->EnqueBuffer(bufferPtr); } -StreamSourcePlugin::StreamSourcePlugin(const std::string& name) - : bufferPool_(0), - name_(name), - state_(State::CREATED), - isSeekable_(false), - waitBuffers_(), - bufferQueue_("SourceBuffQue") +StreamSourcePlugin::StreamSourcePlugin(std::string name) + : SourcePlugin(std::move(name)), bufferPool_(0), state_(State::CREATED), isSeekable_(false), + waitBuffers_(), bufferQueue_("SourceBuffQue") { MEDIA_LOG_D("ctor called"); } diff --git a/engine/plugin/plugins/source/stream_source/stream_source_plugin.h b/engine/plugin/plugins/source/stream_source/stream_source_plugin.h index 5eec3191..c5a5a695 100644 --- a/engine/plugin/plugins/source/stream_source/stream_source_plugin.h +++ b/engine/plugin/plugins/source/stream_source/stream_source_plugin.h @@ -63,7 +63,7 @@ private: class StreamSourcePlugin : public SourcePlugin, std::enable_shared_from_this { public: - explicit StreamSourcePlugin(const std::string& name); + explicit StreamSourcePlugin(std::string name); ~StreamSourcePlugin(); Status Init() override; @@ -92,7 +92,6 @@ protected: AVBufferPool bufferPool_; private: - std::string name_; State state_; bool isSeekable_; OSAL::Mutex mutex_ {}; -- Gitee From 5a3af1c58da3450d509c8f7ed851746d143d9762 Mon Sep 17 00:00:00 2001 From: liyong Date: Fri, 8 Oct 2021 11:30:02 +0800 Subject: [PATCH 33/34] fix code-check problems for foundation. Signed-off-by: liyong --- engine/foundation/meta.cpp | 6 +++ engine/foundation/meta.h | 42 +++++++++---------- engine/foundation/osal/base/common.h | 2 +- engine/foundation/osal/osal_config.h | 2 +- engine/foundation/osal/thread/task.cpp | 28 ++++++------- engine/foundation/osal/thread/task.h | 14 +++---- .../{prepare_state.h => preparing_state.h} | 0 .../demuxer/ffmpeg_demuxer_plugin.h | 3 +- .../demuxer/ffmpeg_track_meta.h | 1 + 9 files changed, 53 insertions(+), 45 deletions(-) rename engine/player/internal/{prepare_state.h => preparing_state.h} (100%) diff --git a/engine/foundation/meta.cpp b/engine/foundation/meta.cpp index 41569743..dee75192 100644 --- a/engine/foundation/meta.cpp +++ b/engine/foundation/meta.cpp @@ -135,6 +135,9 @@ void Meta::Update(const Meta& meta) bool Meta::SetPointer(Plugin::MetaID id, const void* ptr, size_t size) { + if (size == 0) { + return false; + } auto tmp = new (std::nothrow) uint8_t[size]; if (tmp == nullptr) { return false; @@ -152,6 +155,9 @@ bool Meta::GetPointer(Plugin::MetaID id, void** ptr, size_t& size) const std::pair, size_t> item; if (GetData, size_t>>(id, item)) { size = item.second; + if (size == 0) { + return false; + } auto tmp = new (std::nothrow) uint8_t[size]; if (tmp == nullptr) { return false; diff --git a/engine/foundation/meta.h b/engine/foundation/meta.h index f4eb6e07..8edb2340 100644 --- a/engine/foundation/meta.h +++ b/engine/foundation/meta.h @@ -27,15 +27,15 @@ namespace OHOS { namespace Media { class Meta { public: - explicit Meta()= default; + explicit Meta() = default; ~Meta(); bool Empty() const; - bool SetString(Plugin::MetaID, const std::string &); - bool SetInt32(Plugin::MetaID, int32_t); - bool SetUint32(Plugin::MetaID, uint32_t); - bool SetInt64(Plugin::MetaID, int64_t); - bool SetUint64(Plugin::MetaID, uint64_t); - bool SetFloat(Plugin::MetaID, float); + bool SetString(Plugin::MetaID id, const std::string& value); + bool SetInt32(Plugin::MetaID id, int32_t value); + bool SetUint32(Plugin::MetaID id, uint32_t value); + bool SetInt64(Plugin::MetaID id, int64_t value); + bool SetUint64(Plugin::MetaID id, uint64_t value); + bool SetFloat(Plugin::MetaID id, float value); /** * this function will copy from ptr with size. @@ -45,12 +45,12 @@ public: */ bool SetPointer(Plugin::MetaID, const void* ptr, size_t size); - bool GetString(Plugin::MetaID, std::string &) const; - bool GetInt32(Plugin::MetaID, int32_t &) const; - bool GetUint32(Plugin::MetaID, uint32_t &) const; - bool GetInt64(Plugin::MetaID, int64_t &) const; - bool GetUint64(Plugin::MetaID, uint64_t &) const; - bool GetFloat(Plugin::MetaID, float &) const; + bool GetString(Plugin::MetaID id, std::string& value) const; + bool GetInt32(Plugin::MetaID id, int32_t& value) const; + bool GetUint32(Plugin::MetaID id, uint32_t& value) const; + bool GetInt64(Plugin::MetaID id, int64_t& value) const; + bool GetUint64(Plugin::MetaID id, uint64_t& value) const; + bool GetFloat(Plugin::MetaID id, float& value) const; /** * this function will copy from inner storage with size if exists. The user should delete the memory when it's not @@ -59,10 +59,10 @@ public: * @param size size * @return */ - bool GetPointer(Plugin::MetaID, void **ptr, size_t &size) const; + bool GetPointer(Plugin::MetaID, void** ptr, size_t& size) const; - template - bool GetData(Plugin::MetaID id, T &value) const + template + bool GetData(Plugin::MetaID id, T& value) const { auto ite = items_.find(id); if (ite == items_.end() || typeid(T) != ite->second.Type()) { @@ -93,16 +93,16 @@ public: * @param value * @return */ - template - bool SetData(Plugin::MetaID id, const T &value) + template + bool SetData(Plugin::MetaID id, const T& value) { items_[id] = value; return true; } private: - std::map items_ {}; + std::map items_{}; }; -} -} +} // namespace Media +} // namespace OHOS #endif // HISTREAMER_FOUNDATION_META_H diff --git a/engine/foundation/osal/base/common.h b/engine/foundation/osal/base/common.h index b822b7d5..17178da7 100644 --- a/engine/foundation/osal/base/common.h +++ b/engine/foundation/osal/base/common.h @@ -24,7 +24,7 @@ namespace Media { namespace OSAL { inline void Assert(const char* file, int line) { - fprintf(stderr, "assertion failed for: %s, at line: %d", file, line); + (void)fprintf(stderr, "assertion failed for: %s, at line: %d", file, line); abort(); } } // namespace OSAL diff --git a/engine/foundation/osal/osal_config.h b/engine/foundation/osal/osal_config.h index 03e7fdcf..f7f4bffe 100644 --- a/engine/foundation/osal/osal_config.h +++ b/engine/foundation/osal/osal_config.h @@ -18,7 +18,7 @@ #ifdef OSAL_OHOS #include "platform/ohos/ohos_config.h" -#elif OSAL_LINUX +#elif defined OSAL_LINUX #error "OSAL not support linux." #else #error "unknown OSAL platform" diff --git a/engine/foundation/osal/thread/task.cpp b/engine/foundation/osal/thread/task.cpp index bd876be4..d93cfe14 100644 --- a/engine/foundation/osal/thread/task.cpp +++ b/engine/foundation/osal/thread/task.cpp @@ -24,7 +24,7 @@ namespace OHOS { namespace Media { namespace OSAL { Task::Task(std::string name) - : name_(std::move(name)), runningState_(PAUSED), loop_(), pauseDone_(false), workInProgress_(false) + : name_(std::move(name)), runningState_(RunningState::PAUSED), loop_(), pauseDone_(false), workInProgress_(false) { MEDIA_LOG_D("task %s ctor called", name_.c_str()); loop_.SetName(name_); @@ -39,7 +39,7 @@ Task::Task(std::string name, std::function handler) : Task(std::move(nam Task::~Task() { MEDIA_LOG_D("task %s dtor called", name_.c_str()); - runningState_ = STOPPED; + runningState_ = RunningState::STOPPED; cv_.NotifyOne(); } @@ -47,7 +47,7 @@ void Task::Start() { #ifndef START_FAKE_TASK OSAL::ScopedLock lock(stateMutex_); - runningState_ = STARTED; + runningState_ = RunningState::STARTED; if (!loop_ && !loop_.CreateThread([this] { Run(); })) { MEDIA_LOG_E("task %s create failed", name_.c_str()); } else { @@ -60,7 +60,7 @@ void Task::Start() void Task::Stop() { OSAL::ScopedLock lock(stateMutex_); - runningState_ = STOPPED; + runningState_ = RunningState::STOPPED; cv_.NotifyOne(); while (workInProgress_.load()) {} MEDIA_LOG_D("task %s stop called", name_.c_str()); @@ -69,21 +69,21 @@ void Task::Stop() void Task::StopAsync() { OSAL::ScopedLock lock(stateMutex_); - runningState_ = STOPPED; + runningState_ = RunningState::STOPPED; cv_.NotifyOne(); MEDIA_LOG_D("task %s stop called", name_.c_str()); } void Task::Pause() { - if (runningState_.load() != STARTED) { + if (runningState_.load() != RunningState::STARTED) { return; } OSAL::ScopedLock lock(stateMutex_); MEDIA_LOG_D("task %s Pause called", name_.c_str()); - if (runningState_.load() == STARTED) { + if (runningState_.load() == RunningState::STARTED) { pauseDone_ = false; - runningState_ = PAUSED; + runningState_ = RunningState::PAUSED; while (!pauseDone_.load()) {} } MEDIA_LOG_D("task %s Pause done.", name_.c_str()); @@ -91,12 +91,12 @@ void Task::Pause() void Task::PauseAsync() { - if (runningState_.load() != STARTED) { + if (runningState_.load() != RunningState::STARTED) { return; } OSAL::ScopedLock lock(stateMutex_); MEDIA_LOG_D("task %s Pause called", name_.c_str()); - runningState_ = PAUSED; + runningState_ = RunningState::PAUSED; } void Task::RegisterHandler(std::function handler) @@ -113,20 +113,20 @@ void Task::DoTask() void Task::Run() { for (;;) { - if (runningState_.load() == STARTED) { + if (runningState_.load() == RunningState::STARTED) { workInProgress_ = true; handler_(); workInProgress_ = false; continue; } - if (runningState_.load() == STOPPED) { + if (runningState_.load() == RunningState::STOPPED) { MEDIA_LOG_D("task %s stopped, exit task", name_.c_str()); break; } - if (runningState_.load() == PAUSED) { + if (runningState_.load() == RunningState::PAUSED) { OSAL::ScopedLock lock(cvMutex_); pauseDone_ = true; - cv_.Wait(lock, [this] { return runningState_.load() != PAUSED; }); + cv_.Wait(lock, [this] { return runningState_.load() != RunningState::PAUSED; }); } } } diff --git a/engine/foundation/osal/thread/task.h b/engine/foundation/osal/thread/task.h index e13b0bfb..0b8d38f4 100644 --- a/engine/foundation/osal/thread/task.h +++ b/engine/foundation/osal/thread/task.h @@ -51,7 +51,7 @@ public: void RegisterHandler(std::function handler); private: - enum RunningState { + enum class RunningState { STARTED, PAUSED, STOPPED, @@ -60,15 +60,15 @@ private: void Run(); const std::string name_; - std::atomic runningState_ {PAUSED}; + std::atomic runningState_{RunningState::PAUSED}; std::function handler_ = [this] { DoTask(); }; OSAL::Thread loop_; - OSAL::Mutex stateMutex_ {}; - OSAL::Mutex cvMutex_ {}; - OSAL::ConditionVariable cv_ {}; - std::atomic pauseDone_ {}; - std::atomic workInProgress_ {}; + OSAL::Mutex stateMutex_{}; + OSAL::Mutex cvMutex_{}; + OSAL::ConditionVariable cv_{}; + std::atomic pauseDone_{}; + std::atomic workInProgress_{}; }; } // namespace OSAL } // namespace Media diff --git a/engine/player/internal/prepare_state.h b/engine/player/internal/preparing_state.h similarity index 100% rename from engine/player/internal/prepare_state.h rename to engine/player/internal/preparing_state.h diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h index fc87faaf..31a80d2d 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h @@ -29,8 +29,9 @@ extern "C" { #endif #include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" #ifdef __cplusplus -}; +} #endif namespace OHOS { diff --git a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h index 21c21a68..d18f61dd 100644 --- a/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h +++ b/engine/plugin/plugins/ffmpeg_adapter/demuxer/ffmpeg_track_meta.h @@ -24,6 +24,7 @@ extern "C" { #endif #include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" #ifdef __cplusplus }; #endif -- Gitee From fc0e0c648b10747ee8f779ab655b7a284e1d803d Mon Sep 17 00:00:00 2001 From: liyong Date: Fri, 8 Oct 2021 12:11:35 +0800 Subject: [PATCH 34/34] fix code-check problems for player. Signed-off-by: liyong --- engine/player/hiplayer_impl.cpp | 22 +++++----- engine/player/internal/init_state.h | 8 ++-- engine/player/internal/pause_state.h | 12 +++--- engine/player/internal/playing_state.h | 16 +++---- engine/player/internal/preparing_state.h | 17 ++++---- engine/player/internal/ready_state.h | 13 +++--- engine/player/internal/state.cpp | 53 +++++++++++++----------- engine/player/internal/state.h | 44 ++++++++++---------- engine/player/internal/state_machine.cpp | 42 +++++++++---------- engine/player/internal/state_machine.h | 14 +++---- engine/player/play_executor.h | 6 ++- 11 files changed, 127 insertions(+), 120 deletions(-) diff --git a/engine/player/hiplayer_impl.cpp b/engine/player/hiplayer_impl.cpp index 9b74fbfa..fbcedf56 100644 --- a/engine/player/hiplayer_impl.cpp +++ b/engine/player/hiplayer_impl.cpp @@ -105,32 +105,32 @@ ErrorCode HiPlayer::HiPlayerImpl::Prepare() ErrorCode HiPlayer::HiPlayerImpl::Play() { - return fsm_.SendEvent(PLAY); + return fsm_.SendEvent(Intent::PLAY); } ErrorCode HiPlayer::HiPlayerImpl::Pause() { - return fsm_.SendEvent(PAUSE); + return fsm_.SendEvent(Intent::PAUSE); } ErrorCode HiPlayer::HiPlayerImpl::Resume() { - return fsm_.SendEvent(RESUME); + return fsm_.SendEvent(Intent::RESUME); } ErrorCode HiPlayer::HiPlayerImpl::Stop() { - return fsm_.SendEvent(STOP); + return fsm_.SendEvent(Intent::STOP); } ErrorCode HiPlayer::HiPlayerImpl::StopAsync() { - return fsm_.SendEventAsync(STOP); + return fsm_.SendEventAsync(Intent::STOP); } ErrorCode HiPlayer::HiPlayerImpl::SetSource(std::shared_ptr source) { - return fsm_.SendEvent(SET_SOURCE, source); + return fsm_.SendEvent(Intent::SET_SOURCE, source); } ErrorCode HiPlayer::HiPlayerImpl::SetBufferSize(size_t size) @@ -143,14 +143,14 @@ void HiPlayer::HiPlayerImpl::OnEvent(Event event) MEDIA_LOG_D("[HiStreamer] OnEvent (%d)", event.type); switch (event.type) { case EVENT_ERROR: { - fsm_.SendEventAsync(NOTIFY_ERROR, event.param); + fsm_.SendEventAsync(Intent::NOTIFY_ERROR, event.param); break; } case EVENT_READY: - fsm_.SendEventAsync(NOTIFY_READY); + fsm_.SendEventAsync(Intent::NOTIFY_READY); break; case EVENT_COMPLETE: - fsm_.SendEventAsync(NOTIFY_COMPLETE); + fsm_.SendEventAsync(Intent::NOTIFY_COMPLETE); break; default: MEDIA_LOG_E("Unknown event(%d)", event.type); @@ -165,7 +165,7 @@ ErrorCode HiPlayer::HiPlayerImpl::SetSingleLoop(bool loop) ErrorCode HiPlayer::HiPlayerImpl::Seek(size_t time, size_t& position) { - auto rtv = fsm_.SendEvent(SEEK, static_cast(time)); + auto rtv = fsm_.SendEvent(Intent::SEEK, static_cast(time)); if (rtv == SUCCESS) { int64_t pos = 0; rtv = GetCurrentTime(pos); @@ -227,7 +227,7 @@ ErrorCode HiPlayer::HiPlayerImpl::DoOnComplete() if (!singleLoop) { StopAsync(); } else { - fsm_.SendEventAsync(SEEK, static_cast(0)); + fsm_.SendEventAsync(Intent::SEEK, static_cast(0)); } auto ptr = callback_.lock(); if (ptr != nullptr) { diff --git a/engine/player/internal/init_state.h b/engine/player/internal/init_state.h index e5985067..a9868b05 100644 --- a/engine/player/internal/init_state.h +++ b/engine/player/internal/init_state.h @@ -41,22 +41,22 @@ public: std::shared_ptr source; if (param.Type() != typeid(std::shared_ptr) || !(source = Plugin::AnyCast>(param))) { - return {INVALID_SOURCE, ACTION_BUTT}; + return {INVALID_SOURCE, Action::ACTION_BUTT}; } auto ret = executor_.DoSetSource(source); - return {ret, TRANS_TO_PREPARING}; + return {ret, Action::TRANS_TO_PREPARING}; } std::tuple Stop() override { - return {SUCCESS, TRANS_TO_INIT}; + return {SUCCESS, Action::TRANS_TO_INIT}; } std::tuple Enter(Intent) override { OSAL::ScopedLock lock(mutex_); auto ret = executor_.DoStop(); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } private: diff --git a/engine/player/internal/pause_state.h b/engine/player/internal/pause_state.h index f65a6f91..fd4fce98 100644 --- a/engine/player/internal/pause_state.h +++ b/engine/player/internal/pause_state.h @@ -38,36 +38,36 @@ public: { MEDIA_LOG_D("Pause state entered."); auto ret = executor_.DoPause(); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } std::tuple Play() override { MEDIA_LOG_D("Play in pause state."); - return {SUCCESS, TRANS_TO_PLAYING}; + return {SUCCESS, Action::TRANS_TO_PLAYING}; } std::tuple Seek(const Plugin::Any& param) override { MEDIA_LOG_D("Seek in pause state."); if (param.Type() != typeid(int64_t)) { - return {INVALID_PARAM_VALUE, ACTION_BUTT}; + return {INVALID_PARAM_VALUE, Action::ACTION_BUTT}; } auto timeMs = Plugin::AnyCast(param); auto ret = executor_.DoSeek(timeMs); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } std::tuple Resume() override { MEDIA_LOG_D("Resume in pause state."); - return {SUCCESS, TRANS_TO_PLAYING}; + return {SUCCESS, Action::TRANS_TO_PLAYING}; } std::tuple Stop() override { MEDIA_LOG_D("Stop called in pause state."); - return {SUCCESS, TRANS_TO_INIT}; + return {SUCCESS, Action::TRANS_TO_INIT}; } }; } // namespace Media diff --git a/engine/player/internal/playing_state.h b/engine/player/internal/playing_state.h index a9df76fb..464f346e 100644 --- a/engine/player/internal/playing_state.h +++ b/engine/player/internal/playing_state.h @@ -38,44 +38,44 @@ public: { MEDIA_LOG_D("Enter state: %s", name_.c_str()); ErrorCode ret; - if (intent == RESUME) { + if (intent == Intent::RESUME) { ret = executor_.DoResume(); } else { ret = executor_.DoPlay(); } - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } std::tuple Play() override { - return {SUCCESS, ACTION_BUTT}; + return {SUCCESS, Action::ACTION_BUTT}; } std::tuple Seek(const Plugin::Any& param) override { MEDIA_LOG_D("Seek in playing state."); if (param.Type() != typeid(int64_t)) { - return {INVALID_PARAM_VALUE, ACTION_BUTT}; + return {INVALID_PARAM_VALUE, Action::ACTION_BUTT}; } auto timeMs = Plugin::AnyCast(param); auto ret = executor_.DoSeek(timeMs); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } std::tuple Pause() override { - return {SUCCESS, TRANS_TO_PAUSE}; + return {SUCCESS, Action::TRANS_TO_PAUSE}; } std::tuple Stop() override { - return {SUCCESS, TRANS_TO_INIT}; + return {SUCCESS, Action::TRANS_TO_INIT}; } std::tuple OnComplete() override { auto ret = executor_.DoOnComplete(); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } }; } // namespace Media diff --git a/engine/player/internal/preparing_state.h b/engine/player/internal/preparing_state.h index 12c62f58..489b71dd 100644 --- a/engine/player/internal/preparing_state.h +++ b/engine/player/internal/preparing_state.h @@ -34,13 +34,14 @@ public: ~PreparingState() override = default; - std::tuple Enter(Intent) override + std::tuple Enter(Intent intent) override { MEDIA_LOG_D("Enter state: %s", name_.c_str()); - Action nextAction = ACTION_BUTT; + (void)intent; + Action nextAction = Action::ACTION_BUTT; auto rtv = executor_.PrepareFilters(); if (rtv != SUCCESS) { - nextAction = TRANS_TO_INIT; + nextAction = Action::TRANS_TO_INIT; } return {rtv, nextAction}; } @@ -48,28 +49,28 @@ public: std::tuple Play() override { MEDIA_LOG_W("Play received in preparing state."); - return {SUCCESS, ACTION_PENDING}; + return {SUCCESS, Action::ACTION_PENDING}; } std::tuple Seek(const Plugin::Any& param) override { MEDIA_LOG_D("Seek in preparing state."); if (param.Type() != typeid(int64_t)) { - return {INVALID_PARAM_VALUE, ACTION_BUTT}; + return {INVALID_PARAM_VALUE, Action::ACTION_BUTT}; } auto timeMs = Plugin::AnyCast(param); auto ret = executor_.DoSeek(timeMs); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } std::tuple Stop() override { - return {SUCCESS, TRANS_TO_INIT}; + return {SUCCESS, Action::TRANS_TO_INIT}; } std::tuple OnReady() override { - return {SUCCESS, TRANS_TO_READY}; + return {SUCCESS, Action::TRANS_TO_READY}; } }; } // namespace Media diff --git a/engine/player/internal/ready_state.h b/engine/player/internal/ready_state.h index 6603d531..9784ef4e 100644 --- a/engine/player/internal/ready_state.h +++ b/engine/player/internal/ready_state.h @@ -34,33 +34,34 @@ public: ~ReadyState() override = default; - std::tuple Enter(Intent) override + std::tuple Enter(Intent intent) override { + (void)intent; auto rtv = executor_.DoOnReady(); - return {rtv, ACTION_BUTT}; + return {rtv, Action::ACTION_BUTT}; } std::tuple Seek(const Plugin::Any& param) override { MEDIA_LOG_D("Seek in ready state."); if (param.Type() != typeid(int64_t)) { - return {INVALID_PARAM_VALUE, ACTION_BUTT}; + return {INVALID_PARAM_VALUE, Action::ACTION_BUTT}; } auto timeMs = Plugin::AnyCast(param); auto ret = executor_.DoSeek(timeMs); - return {ret, ACTION_BUTT}; + return {ret, Action::ACTION_BUTT}; } std::tuple Play() override { MEDIA_LOG_D("Play in ready state."); - return {SUCCESS, TRANS_TO_PLAYING}; + return {SUCCESS, Action::TRANS_TO_PLAYING}; } std::tuple Stop() override { MEDIA_LOG_D("Stop in ready state."); - return {SUCCESS, TRANS_TO_INIT}; + return {SUCCESS, Action::TRANS_TO_INIT}; } }; } // namespace Media diff --git a/engine/player/internal/state.cpp b/engine/player/internal/state.cpp index c9ae2d1e..8cf42e1a 100644 --- a/engine/player/internal/state.cpp +++ b/engine/player/internal/state.cpp @@ -23,10 +23,11 @@ namespace Media { State::State(PlayExecutor& executor, std::string name) : executor_(executor), name_(std::move(name)) { } -std::tuple State::Enter(Intent) +std::tuple State::Enter(Intent intent) { + (void)intent; MEDIA_LOG_D("Enter state: %s", name_.c_str()); - return {SUCCESS, ACTION_BUTT}; + return {SUCCESS, Action::ACTION_BUTT}; } void State::Exit() { @@ -40,37 +41,39 @@ const std::string& State::GetName() { return name_; } -std::tuple State::SetSource(const Plugin::Any&) +std::tuple State::SetSource(const Plugin::Any& source) { - return {INVALID_OPERATION, ACTION_BUTT}; + (void)source; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::Play() { - return {INVALID_OPERATION, ACTION_BUTT}; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::Stop() { - return {INVALID_OPERATION, ACTION_BUTT}; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::Pause() { - return {INVALID_OPERATION, ACTION_BUTT}; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::Resume() { - return {INVALID_OPERATION, ACTION_BUTT}; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } -std::tuple State::Seek(const Plugin::Any&) +std::tuple State::Seek(const Plugin::Any& param) { - return {INVALID_OPERATION, ACTION_BUTT}; + (void)param; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::SetAttribute() { - return {INVALID_OPERATION, ACTION_BUTT}; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::OnReady() { - return {INVALID_OPERATION, ACTION_BUTT}; + return {INVALID_OPERATION, Action::ACTION_BUTT}; } std::tuple State::OnError(const Plugin::Any& param) { @@ -79,45 +82,45 @@ std::tuple State::OnError(const Plugin::Any& param) errorCode = Plugin::AnyCast(param); } executor_.DoOnError(errorCode); - return {SUCCESS, TRANS_TO_INIT}; + return {SUCCESS, Action::TRANS_TO_INIT}; } std::tuple State::OnComplete() { - return {SUCCESS, ACTION_BUTT}; + return {SUCCESS, Action::ACTION_BUTT}; } std::tuple State::DispatchIntent(Intent intent, const Plugin::Any& param) { ErrorCode rtv = SUCCESS; - Action nextAction = ACTION_BUTT; + Action nextAction = Action::ACTION_BUTT; switch (intent) { - case SET_SOURCE: + case Intent::SET_SOURCE: std::tie(rtv, nextAction) = SetSource(param); break; - case SEEK: + case Intent::SEEK: std::tie(rtv, nextAction) = Seek(param); break; - case PLAY: + case Intent::PLAY: std::tie(rtv, nextAction) = Play(); break; - case PAUSE: + case Intent::PAUSE: std::tie(rtv, nextAction) = Pause(); break; - case RESUME: + case Intent::RESUME: std::tie(rtv, nextAction) = Resume(); break; - case STOP: + case Intent::STOP: std::tie(rtv, nextAction) = Stop(); break; - case SET_ATTRIBUTE: + case Intent::SET_ATTRIBUTE: std::tie(rtv, nextAction) = SetAttribute(); break; - case NOTIFY_READY: + case Intent::NOTIFY_READY: std::tie(rtv, nextAction) = OnReady(); break; - case NOTIFY_COMPLETE: + case Intent::NOTIFY_COMPLETE: std::tie(rtv, nextAction) = OnComplete(); break; - case NOTIFY_ERROR: + case Intent::NOTIFY_ERROR: std::tie(rtv, nextAction) = OnError(param); break; default: diff --git a/engine/player/internal/state.h b/engine/player/internal/state.h index 23d7cb94..ee70b15b 100644 --- a/engine/player/internal/state.h +++ b/engine/player/internal/state.h @@ -28,7 +28,7 @@ namespace OHOS { namespace Media { -enum Intent { +enum class Intent { SET_SOURCE, SEEK, PLAY, @@ -42,7 +42,7 @@ enum Intent { INTENT_BUTT }; -enum Action { +enum class Action { TRANS_TO_INIT, TRANS_TO_PREPARING, TRANS_TO_READY, @@ -65,10 +65,10 @@ public: virtual std::tuple Stop(); virtual std::tuple Pause(); virtual std::tuple Resume(); - virtual std::tuple Seek(const Plugin::Any&); + virtual std::tuple Seek(const Plugin::Any& param); virtual std::tuple SetAttribute(); virtual std::tuple OnReady(); - virtual std::tuple OnError(const Plugin::Any&) final; + virtual std::tuple OnError(const Plugin::Any& param) final; virtual std::tuple OnComplete(); protected: @@ -77,26 +77,26 @@ protected: PlayExecutor& executor_; const std::string name_; const std::map intentDesc_ = { - { SET_SOURCE, "SET_SOURCE" }, - { SEEK, "SEEK" }, - { PLAY, "PLAY" }, - { PAUSE, "PAUSE" }, - { RESUME, "RESUME" }, - { STOP, "STOP" }, - { SET_ATTRIBUTE, "SET_ATTRIBUTE" }, - { NOTIFY_READY, "NOTIFY_READY" }, - { NOTIFY_COMPLETE, "NOTIFY_COMPLETE" }, - { NOTIFY_ERROR, "NOTIFY_ERROR" }, - { INTENT_BUTT, "INTENT_BUTT" } + { Intent::SET_SOURCE, "SET_SOURCE" }, + { Intent::SEEK, "SEEK" }, + { Intent::PLAY, "PLAY" }, + { Intent::PAUSE, "PAUSE" }, + { Intent::RESUME, "RESUME" }, + { Intent::STOP, "STOP" }, + { Intent::SET_ATTRIBUTE, "SET_ATTRIBUTE" }, + { Intent::NOTIFY_READY, "NOTIFY_READY" }, + { Intent::NOTIFY_COMPLETE, "NOTIFY_COMPLETE" }, + { Intent::NOTIFY_ERROR, "NOTIFY_ERROR" }, + { Intent::INTENT_BUTT, "INTENT_BUTT" } }; const std::map actionDesc_ = { - { TRANS_TO_INIT, "TRANS_TO_INIT" }, - { TRANS_TO_PREPARING, "TRANS_TO_PREPARING" }, - { TRANS_TO_READY, "TRANS_TO_READY" }, - { TRANS_TO_PLAYING, "TRANS_TO_PLAYING" }, - { TRANS_TO_PAUSE, "TRANS_TO_PAUSE" }, - { ACTION_PENDING, "ACTION_PENDING" }, - { ACTION_BUTT, "ACTION_BUTT" } + { Action::TRANS_TO_INIT, "TRANS_TO_INIT" }, + { Action::TRANS_TO_PREPARING, "TRANS_TO_PREPARING" }, + { Action::TRANS_TO_READY, "TRANS_TO_READY" }, + { Action::TRANS_TO_PLAYING, "TRANS_TO_PLAYING" }, + { Action::TRANS_TO_PAUSE, "TRANS_TO_PAUSE" }, + { Action::ACTION_PENDING, "ACTION_PENDING" }, + { Action::ACTION_BUTT, "ACTION_BUTT" } }; }; } // namespace Media diff --git a/engine/player/internal/state_machine.cpp b/engine/player/internal/state_machine.cpp index 9607e203..4196c246 100644 --- a/engine/player/internal/state_machine.cpp +++ b/engine/player/internal/state_machine.cpp @@ -81,13 +81,13 @@ Action StateMachine::ProcessIntent(Intent intent, const Plugin::Any& param) OSAL::ScopedLock lock(mutex_); lastIntent = intent; ErrorCode rtv = SUCCESS; - Action nextAction = ACTION_BUTT; + Action nextAction = Action::ACTION_BUTT; std::tie(rtv, nextAction) = curState_->Execute(intent, param); if (rtv == SUCCESS) { rtv = ProcAction(nextAction); } OnIntentExecuted(intent, nextAction, rtv); - return (rtv == SUCCESS) ? nextAction : ACTION_BUTT; + return (rtv == SUCCESS) ? nextAction : Action::ACTION_BUTT; } void StateMachine::DoTask() @@ -99,25 +99,25 @@ void StateMachine::DoTask() } auto action = job(); switch (action) { - case ACTION_PENDING: + case Action::ACTION_PENDING: pendingJobs_.push(job); break; - case TRANS_TO_INIT: - case TRANS_TO_READY: - case TRANS_TO_PREPARING: - case TRANS_TO_PLAYING: - case TRANS_TO_PAUSE: { + case Action::TRANS_TO_INIT: + case Action::TRANS_TO_READY: + case Action::TRANS_TO_PREPARING: + case Action::TRANS_TO_PLAYING: + case Action::TRANS_TO_PAUSE: { if (!pendingJobs_.empty()) { job = pendingJobs_.front(); pendingJobs_.pop(); action = job(); - if (action == ACTION_PENDING) { + if (action == Action::ACTION_PENDING) { pendingJobs_.push(job); } } break; } - case ACTION_BUTT: + case Action::ACTION_BUTT: // fall through default: break; @@ -133,19 +133,19 @@ ErrorCode StateMachine::ProcAction(Action nextAction) { std::shared_ptr nextState = nullptr; switch (nextAction) { - case TRANS_TO_INIT: + case Action::TRANS_TO_INIT: nextState = states_["InitState"]; break; - case TRANS_TO_PREPARING: + case Action::TRANS_TO_PREPARING: nextState = states_["PreparingState"]; break; - case TRANS_TO_READY: + case Action::TRANS_TO_READY: nextState = states_["ReadyState"]; break; - case TRANS_TO_PLAYING: + case Action::TRANS_TO_PLAYING: nextState = states_["PlayingState"]; break; - case TRANS_TO_PAUSE: + case Action::TRANS_TO_PAUSE: nextState = states_["PauseState"]; break; default: @@ -184,16 +184,16 @@ void StateMachine::OnIntentExecuted(Intent intent, Action action, ErrorCode resu { MEDIA_LOG_D("OnIntentExecuted, curState: %s, intent: %d, action: %d, result: %d", curState_->GetName().c_str(), static_cast(intent), static_cast(action), static_cast(result)); - if (action == ACTION_PENDING) { + if (action == Action::ACTION_PENDING) { return; } - if (intent == PLAY) { - if (action == TRANS_TO_PLAYING) { - intentSync_.Notify(PLAY, result); + if (intent == Intent::PLAY) { + if (action == Action::TRANS_TO_PLAYING) { + intentSync_.Notify(Intent::PLAY, result); } } else { - if (intent == NOTIFY_READY && action == TRANS_TO_PLAYING) { - intentSync_.Notify(PLAY, result); + if (intent == Intent::NOTIFY_READY && action == Action::TRANS_TO_PLAYING) { + intentSync_.Notify(Intent::PLAY, result); } else { intentSync_.Notify(intent, result); } diff --git a/engine/player/internal/state_machine.h b/engine/player/internal/state_machine.h index 3821f7cc..86d4e1d1 100644 --- a/engine/player/internal/state_machine.h +++ b/engine/player/internal/state_machine.h @@ -34,7 +34,7 @@ #include "init_state.h" #include "pause_state.h" #include "playing_state.h" -#include "prepare_state.h" +#include "preparing_state.h" #include "ready_state.h" namespace OHOS { @@ -80,14 +80,14 @@ private: void OnIntentExecuted(Intent intent, Action action, ErrorCode result); - mutable OSAL::Mutex mutex_ {}; + mutable OSAL::Mutex mutex_{}; mutable OSAL::Synchronizer intentSync_; - mutable std::shared_ptr curState_ {nullptr}; - mutable Intent lastIntent {INTENT_BUTT}; - std::map> states_ {}; + mutable std::shared_ptr curState_{nullptr}; + mutable Intent lastIntent{Intent::INTENT_BUTT}; + std::map> states_{}; Media::BlockingQueue jobs_; - std::queue pendingJobs_ {}; - StateChangeCallback* callback_ {nullptr}; + std::queue pendingJobs_{}; + StateChangeCallback* callback_{nullptr}; }; } // namespace Media } // namespace OHOS diff --git a/engine/player/play_executor.h b/engine/player/play_executor.h index 1f31d91d..bef1a01a 100644 --- a/engine/player/play_executor.h +++ b/engine/player/play_executor.h @@ -35,8 +35,9 @@ public: return SUCCESS; } - virtual ErrorCode DoSetSource(const std::shared_ptr&) const + virtual ErrorCode DoSetSource(const std::shared_ptr& source) const { + (void)source; return SUCCESS; } @@ -76,8 +77,9 @@ public: return SUCCESS; } - virtual ErrorCode DoOnError(ErrorCode) + virtual ErrorCode DoOnError(ErrorCode errorCode) { + (void)errorCode; return SUCCESS; } }; -- Gitee