diff --git a/bundle.json b/bundle.json index 0c85061098ab683ee06b990780947501d5b5a3b3..5c46521290e677193ff32c781eb401a75a2048b8 100644 --- a/bundle.json +++ b/bundle.json @@ -276,12 +276,22 @@ }, { "type": "so", - "name": "//foundation/multimedia/av_codec/services/media_engine/plugins/ffmpeg_adapter:media_plugin_FFmpegDemuxer", + "name": "//foundation/multimedia/av_codec/services/media_engine/plugins/demuxer:media_plugin_FFmpegDemuxer", "header": { "header_files": [ "reference_parser.h" ], - "header_base": "//foundation/multimedia/av_codec/services/media_engine/plugins/ffmpeg_adapter/common" + "header_base": "//foundation/multimedia/av_codec/services/media_engine/plugins/demuxer/common" + } + }, + { + "type": "so", + "name": "//foundation/multimedia/av_codec/services/media_engine/plugins/demuxer:media_plugin_Mp4Demuxer", + "header": { + "header_files": [ + "reference_parser.h" + ], + "header_base": "//foundation/multimedia/av_codec/services/media_engine/plugins/demuxer/common" } }, { diff --git a/interfaces/inner_api/native/BUILD.gn b/interfaces/inner_api/native/BUILD.gn index fb320ff3dffc8fa3c9b27e307192111aa34e9725..a67b10bb701cebac4034483f84bb9822d087d3f5 100644 --- a/interfaces/inner_api/native/BUILD.gn +++ b/interfaces/inner_api/native/BUILD.gn @@ -162,6 +162,7 @@ config("av_codec_client_codec_config") { "$av_codec_root_dir/services/engine/codec/include/audio/", "$av_codec_root_dir/services/engine/common/include/", "$av_codec_root_dir/services/media_engine/modules/media_codec/", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/common/", "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/common/", ] } diff --git a/services/media_engine/modules/demuxer/type_finder.h b/services/media_engine/modules/demuxer/type_finder.h index 2b983b9096235442ce18422a09e546af7fd5c0a5..e0ab176a4637d8f2af4337a183185d285c839505 100644 --- a/services/media_engine/modules/demuxer/type_finder.h +++ b/services/media_engine/modules/demuxer/type_finder.h @@ -51,7 +51,6 @@ public: void SetInterruptState(bool isInterruptNeeded); bool IsDash() override { return false; } - private: std::string SniffMediaType(); diff --git a/services/media_engine/plugins/BUILD.gn b/services/media_engine/plugins/BUILD.gn index 4f4dd700841a66b7c8695bfc3ee6b7fea131400b..ae1822e83d184adf8f8df1595fc2cfc67c639383 100644 --- a/services/media_engine/plugins/BUILD.gn +++ b/services/media_engine/plugins/BUILD.gn @@ -16,7 +16,8 @@ import("//foundation/multimedia/av_codec/config.gni") group("av_codec_media_engine_plugins") { deps = [ - "ffmpeg_adapter:media_plugin_FFmpegDemuxer", + "demuxer:media_plugin_FFmpegDemuxer", + "demuxer:media_plugin_Mp4Demuxer", "ffmpeg_adapter:media_plugin_FFmpegMuxer", "ffmpeg_adapter/audio_decoder:media_plugin_FFmpegAudioDecoders", "ffmpeg_adapter/audio_decoder/g711mu:media_plugin_G711muAudioDecoder", diff --git a/services/media_engine/plugins/demuxer/BUILD.gn b/services/media_engine/plugins/demuxer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b724f75f35b5759b57e6f3adbbf78efaff0b3b5a --- /dev/null +++ b/services/media_engine/plugins/demuxer/BUILD.gn @@ -0,0 +1,165 @@ +# Copyright (C) 2025 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") +import("//foundation/multimedia/av_codec/config.gni") + +config("demuxer_config") { + defines = [ + "HST_ANY_WITH_NO_RTTI", + "MEDIA_OHOS", + ] + + cflags = [ + "-fno-exceptions", + "-Wall", + "-fno-common", + "-fstack-protector-all", + "-Wshadow", + "-FPIC", + "-FS", + "-O2", + "-D_FORTIFY_SOURCE=2", + "-Wformat=2", + "-Wdate-time", + ] + + cflags_cc = [ + "-std=c++17", + "-fno-rtti", + ] + + include_dirs = [ + "$av_codec_root_dir/interfaces", + "$av_codec_root_dir/services/media_engine/plugins/demuxer", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/common", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/ffmpeg_demuxer", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/mp4_demuxer", + ] +} + + +if (av_codec_support_demuxer) { + ohos_shared_library("media_plugin_FFmpegDemuxer") { + branch_protector_ret = "pac_ret" + install_enable = true + + sanitize = av_codec_sanitize + + defines = [] + + defines += [ + "OHOS_EXPAND_MP4_INFO", + "OHOS_AV3A_DEMUXER", + ] + defines += av_codec_defines + + public_configs = [ ":demuxer_config" ] + + if (build_variant != "user") { + defines += [ "BUILD_ENG_VERSION" ] + } + + configs = [ + ":demuxer_config", + "$av_codec_root_dir/services/dfx:av_codec_service_log_dfx_public_config", + ] + + sources = [ + "common/demuxer_log_compressor.cpp", + "common/reference_parser_manager.cpp", + "common/multi_stream_parser_manager.cpp", + "ffmpeg_demuxer/ffmpeg_converter.cpp", + "ffmpeg_demuxer/ffmpeg_demuxer_plugin.cpp", + "ffmpeg_demuxer/ffmpeg_demuxer_thread.cpp", + "ffmpeg_demuxer/ffmpeg_format_helper.cpp", + "ffmpeg_demuxer/ffmpeg_reference_parser.cpp", + "ffmpeg_demuxer/ffmpeg_utils.cpp", + ] + + deps = [ "$av_codec_root_dir/services/dfx:av_codec_service_dfx" ] + + external_deps = [ + "c_utils:utils", + "ffmpeg:libohosffmpeg", + "hicollie:libhicollie", + "hilog:libhilog", + "hisysevent:libhisysevent", + "init:libbegetutil", + "ipc:ipc_single", + "media_foundation:media_foundation", + ] + + relative_install_dir = "media/media_plugins" + subsystem_name = "multimedia" + part_name = "av_codec" + } + + ohos_shared_library("media_plugin_Mp4Demuxer") { + branch_protector_ret = "pac_ret" + install_enable = true + + sanitize = av_codec_sanitize + + defines = [] + + defines += [ + "OHOS_EXPAND_MP4_INFO", + "OHOS_AV3A_DEMUXER", + ] + defines += av_codec_defines + + public_configs = [ ":demuxer_config" ] + + if (build_variant != "user") { + defines += [ "BUILD_ENG_VERSION" ] + } + + configs = [ + ":demuxer_config", + "$av_codec_root_dir/services/dfx:av_codec_service_log_dfx_public_config", + ] + + sources = [ + "common/demuxer_data_reader.cpp", + "common/demuxer_log_compressor.cpp", + "common/reference_parser_manager.cpp", + "common/multi_stream_parser_manager.cpp", + "ffmpeg_demuxer/ffmpeg_converter.cpp", + "ffmpeg_demuxer/ffmpeg_format_helper.cpp", + "ffmpeg_demuxer/ffmpeg_utils.cpp", + "mp4_demuxer/mp4_audio_parser.cpp", + "mp4_demuxer/mp4_box_parser.cpp", + "mp4_demuxer/mp4_demuxer_plugin.cpp", + "mp4_demuxer/mp4_reference_parser.cpp", + "mp4_demuxer/mp4_sample_helper.cpp", + ] + + deps = [ "$av_codec_root_dir/services/dfx:av_codec_service_dfx" ] + + external_deps = [ + "c_utils:utils", + "ffmpeg:libohosffmpeg", + "hicollie:libhicollie", + "hilog:libhilog", + "hisysevent:libhisysevent", + "init:libbegetutil", + "ipc:ipc_single", + "media_foundation:media_foundation", + ] + + relative_install_dir = "media/media_plugins" + subsystem_name = "multimedia" + part_name = "av_codec" + } +} diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue.h b/services/media_engine/plugins/demuxer/common/block_queue.h similarity index 99% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue.h rename to services/media_engine/plugins/demuxer/common/block_queue.h index e568a1b03df19cd44255234cef418e1b19102d3c..b362a94acd268583045df5da9d8fdf93600458e6 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue.h +++ b/services/media_engine/plugins/demuxer/common/block_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.cpp b/services/media_engine/plugins/demuxer/common/block_queue_pool.h similarity index 64% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.cpp rename to services/media_engine/plugins/demuxer/common/block_queue_pool.h index e1b0ecf9eab29a14797b05c2188318b0a885ccec..c50313e53a15fa227fba9576918ced1f82ba1aa5 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.cpp +++ b/services/media_engine/plugins/demuxer/common/block_queue_pool.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 @@ -13,19 +13,166 @@ * limitations under the License. */ -#define HST_LOG_TAG "BlockQueuePool" +#ifndef BLOCK_QUEUE_POOL_H +#define BLOCK_QUEUE_POOL_H +#include +#include +#include +#include "block_queue.h" +#include "common/status.h" -#include "common/log.h" -#include "block_queue_pool.h" - -namespace { -constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "BlockQueuePool" }; +#ifdef __cplusplus +extern "C" { +#endif +#include "libavcodec/avcodec.h" +#ifdef __cplusplus } +#endif namespace OHOS { namespace Media { +namespace Plugins { + +namespace Ffmpeg { +struct SamplePacket { + uint32_t offset = 0; + std::vector pkts {}; + bool isEOS = false; + bool isAnnexb = false; + uint32_t queueIndex = 0; + ~SamplePacket() + { + for (auto pkt : pkts) { + if (pkt) { + av_packet_free(&pkt); + } + } + } +}; +} // namespace Ffmpeg + +namespace MP4 { +struct Sample { + enum SampleFlag : uint32_t { + NONE = 0, + EOS = 1 << 0, + SYNC_FRAME = 1 << 1, + DISCARD = 1 << 4, + }; + int64_t pts; + int64_t dts; + int64_t duration; + uint32_t flag; + int32_t size; + std::unique_ptr data; +}; -BlockQueuePool::~BlockQueuePool() +struct MP4Sample { + int32_t offset = 0; + std::shared_ptr sample = nullptr; + bool isAnnexb = false; + uint32_t queueIndex = 0; +}; +} // namespace MP4 +} // namespace Plugins + +template +struct BlockTraits { + static uint32_t GetDataSize(const std::shared_ptr& block); + static void UpdateMaxPts(const std::shared_ptr& block, int64_t& maxPts); +}; + +// 为SamplePacket特化 +template<> +struct BlockTraits { + static inline constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "BlockQueuePool" }; + + static uint32_t GetDataSize(const std::shared_ptr& block) + { + uint32_t totalSize = 0; + for (auto pkt : block->pkts) { + FALSE_CONTINUE_LOGD(pkt != nullptr, "Pkt is nullptr, will find next"); + FALSE_CONTINUE_LOGD(pkt->size > 0, "Invalid pkt size: " PUBLIC_LOG_D32, pkt->size); + totalSize += static_cast(pkt->size); + } + return totalSize; + } + + static void UpdateMaxPts(const std::shared_ptr& block, int64_t& maxPts) + { + for (auto pkt : block->pkts) { + if (pkt != nullptr && pkt->pts != AV_NOPTS_VALUE && pkt->pts > maxPts) { + maxPts = pkt->pts; + } + } + } +}; + +// 为MP4Sample特化 +template<> +struct BlockTraits { + static uint32_t GetDataSize(const std::shared_ptr& block) + { + return block->sample != nullptr ? static_cast(block->sample->size) : 0; + } + + static void UpdateMaxPts(const std::shared_ptr& block, int64_t& maxPts) + { + if (block->sample != nullptr && block->sample->pts > maxPts) { + maxPts = block->sample->pts; + } + } +}; + +template +class BlockQueuePool { +public: + static inline constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "BlockQueuePool" }; + + explicit BlockQueuePool(std::string name, size_t singleQueSize = SINGLE_QUEUE_SIZE) + : name_(std::move(name)), quePool_(), queMap_(), sizeMap_(), singleQueSize_(singleQueSize) + { + } + ~BlockQueuePool(); + + Status AddTrackQueue(uint32_t trackIndex); + Status RemoveTrackQueue(uint32_t trackIndex); + bool HasCache(uint32_t trackIndex); + size_t GetCacheSize(uint32_t trackIndex); + uint32_t GetCacheDataSize(uint32_t trackIndex); + bool ResetInfo(std::shared_ptr block); + bool SetInfo(std::shared_ptr block); + void FreeQueue(uint32_t queueIndex); + bool Push(uint32_t trackIndex, std::shared_ptr block); + std::shared_ptr Pop(uint32_t trackIndex); + std::shared_ptr Front(uint32_t trackIndex); + std::shared_ptr Back(uint32_t trackIndex); + Status GetLastPTSByTrackId(uint32_t trackIndex, int64_t& maxPts); + +private: + struct InnerQueue { + bool isValid {false}; + uint32_t dataSize {0}; + std::shared_ptr>> blockQue {nullptr}; + int64_t maxPts {INT64_MIN}; + }; + static constexpr size_t SINGLE_QUEUE_SIZE = 100; + std::string name_; + uint32_t queCount_ {0}; + std::map quePool_; + std::map> queMap_; + std::map sizeMap_; + size_t singleQueSize_ {0}; + + uint32_t GetValidQueue(); + bool InnerQueueIsFull(uint32_t queueIndex); + bool HasQueue(uint32_t trackIndex); + void ResetQueue(uint32_t queueIndex); + std::recursive_mutex mutextCacheQ_ {}; +}; + +template +BlockQueuePool::~BlockQueuePool() { MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S, name_.c_str()); for (auto que : quePool_) { @@ -34,7 +181,8 @@ BlockQueuePool::~BlockQueuePool() MEDIA_LOG_D("Out, block queue " PUBLIC_LOG_S, name_.c_str()); } -Status BlockQueuePool::AddTrackQueue(uint32_t trackIndex) +template +Status BlockQueuePool::AddTrackQueue(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -51,7 +199,8 @@ Status BlockQueuePool::AddTrackQueue(uint32_t trackIndex) return Status::OK; } -Status BlockQueuePool::RemoveTrackQueue(uint32_t trackIndex) +template +Status BlockQueuePool::RemoveTrackQueue(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -69,7 +218,8 @@ Status BlockQueuePool::RemoveTrackQueue(uint32_t trackIndex) return Status::OK; } -size_t BlockQueuePool::GetCacheSize(uint32_t trackIndex) +template +size_t BlockQueuePool::GetCacheSize(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -89,7 +239,8 @@ size_t BlockQueuePool::GetCacheSize(uint32_t trackIndex) return size; } -uint32_t BlockQueuePool::GetCacheDataSize(uint32_t trackIndex) +template +uint32_t BlockQueuePool::GetCacheDataSize(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -108,7 +259,8 @@ uint32_t BlockQueuePool::GetCacheDataSize(uint32_t trackIndex) return dataSize; } -bool BlockQueuePool::HasCache(uint32_t trackIndex) +template +bool BlockQueuePool::HasCache(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -127,7 +279,8 @@ bool BlockQueuePool::HasCache(uint32_t trackIndex) return false; } -void BlockQueuePool::ResetQueue(uint32_t queueIndex) +template +void BlockQueuePool::ResetQueue(uint32_t queueIndex) { if (quePool_.count(queueIndex) == 0) { MEDIA_LOG_D("Error queueIndex"); @@ -144,41 +297,37 @@ void BlockQueuePool::ResetQueue(uint32_t queueIndex) return; } -bool BlockQueuePool::ResetInfo(std::shared_ptr block) +template +bool BlockQueuePool::ResetInfo(std::shared_ptr block) { FALSE_RETURN_V_MSG_E(block != nullptr, false, "Block is nullptr"); MEDIA_LOG_D("Reset for block " PUBLIC_LOG_U32, block->queueIndex); uint32_t queIndex = block->queueIndex; FALSE_RETURN_V_MSG_E(quePool_.count(queIndex) > 0, false, "Index is invalid"); - for (auto pkt : block->pkts) { - FALSE_CONTINUE_LOGD(pkt != nullptr, "Pkt is nullptr, will find next"); - FALSE_CONTINUE_LOGD(pkt->size > 0, "Invalid pkt size: " PUBLIC_LOG_D32, pkt->size); - int64_t tempSize = static_cast(quePool_[queIndex].dataSize) - static_cast(pkt->size); - quePool_[queIndex].dataSize = static_cast(std::max(tempSize, 0LL)); - } + uint32_t blockSize = BlockTraits::GetDataSize(block); + quePool_[queIndex].dataSize = quePool_[queIndex].dataSize >= blockSize ? + quePool_[queIndex].dataSize - blockSize : 0; return true; } -bool BlockQueuePool::SetInfo(std::shared_ptr block) +template +bool BlockQueuePool::SetInfo(std::shared_ptr block) { FALSE_RETURN_V_MSG_E(block != nullptr, false, "Block is nullptr"); MEDIA_LOG_D("Set for block " PUBLIC_LOG_U32, block->queueIndex); uint32_t queIndex = block->queueIndex; FALSE_RETURN_V_MSG_E(quePool_.count(queIndex) > 0, false, "Index is invalid"); - for (auto pkt : block->pkts) { - FALSE_CONTINUE_LOGD(pkt != nullptr, "Pkt is nullptr, will find next"); - FALSE_CONTINUE_LOGD(pkt->size > 0, "Invalid pkt size: " PUBLIC_LOG_D32, pkt->size); - uint32_t pktSize = static_cast(pkt->size); - if (quePool_[queIndex].dataSize <= UINT32_MAX - pktSize) { - quePool_[queIndex].dataSize += pktSize; - } else { - quePool_[queIndex].dataSize = UINT32_MAX; - } + uint32_t blockSize = BlockTraits::GetDataSize(block); + if (quePool_[queIndex].dataSize <= UINT32_MAX - blockSize) { + quePool_[queIndex].dataSize += blockSize; + } else { + quePool_[queIndex].dataSize = UINT32_MAX; } return true; } -void BlockQueuePool::FreeQueue(uint32_t queueIndex) +template +void BlockQueuePool::FreeQueue(uint32_t queueIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); if (quePool_.count(queueIndex) == 0) { @@ -188,7 +337,8 @@ void BlockQueuePool::FreeQueue(uint32_t queueIndex) quePool_[queueIndex].blockQue = nullptr; } -bool BlockQueuePool::Push(uint32_t trackIndex, std::shared_ptr block) +template +bool BlockQueuePool::Push(uint32_t trackIndex, std::shared_ptr block) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -215,19 +365,15 @@ bool BlockQueuePool::Push(uint32_t trackIndex, std::shared_ptr blo return false; } sizeMap_[trackIndex] += 1; - for (auto pkt : block->pkts) { - FALSE_CONTINUE_LOGD(pkt != nullptr, "Pkt is nullptr, will find next"); - FALSE_CONTINUE_LOGD(pkt->size > 0, "Invalid pkt size: " PUBLIC_LOG_D32, pkt->size); - quePool_[pushIndex].dataSize += static_cast(pkt->size); - if (pkt->pts != AV_NOPTS_VALUE && pkt->pts > quePool_[pushIndex].maxPts) { - quePool_[pushIndex].maxPts = pkt->pts; - } - } + quePool_[pushIndex].dataSize += BlockTraits::GetDataSize(block); + BlockTraits::UpdateMaxPts(block, quePool_[pushIndex].maxPts); block->queueIndex = pushIndex; + return quePool_[pushIndex].blockQue->Push(block); } -std::shared_ptr BlockQueuePool::Pop(uint32_t trackIndex) +template +std::shared_ptr BlockQueuePool::Pop(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -251,13 +397,9 @@ std::shared_ptr BlockQueuePool::Pop(uint32_t trackIndex) MEDIA_LOG_D("Block is nullptr"); continue; } - for (auto pkt : block->pkts) { - FALSE_CONTINUE_LOGD(pkt != nullptr, "Pkt is nullptr, will find next"); - FALSE_CONTINUE_LOGD(pkt->size > 0, "Invalid pkt size: " PUBLIC_LOG_D32, pkt->size); - uint32_t pktSize = static_cast(pkt->size); - quePool_[queIndex].dataSize = - quePool_[queIndex].dataSize >= pktSize ? quePool_[queIndex].dataSize -= pktSize : 0; - } + uint32_t blockSize = BlockTraits::GetDataSize(block); + quePool_[queIndex].dataSize = quePool_[queIndex].dataSize >= blockSize ? + quePool_[queIndex].dataSize -= blockSize : 0; if (quePool_[queIndex].blockQue->Empty()) { ResetQueue(queIndex); quePool_[queIndex].maxPts = INT64_MIN; @@ -276,7 +418,8 @@ std::shared_ptr BlockQueuePool::Pop(uint32_t trackIndex) return nullptr; } -std::shared_ptr BlockQueuePool::Front(uint32_t trackIndex) +template +std::shared_ptr BlockQueuePool::Front(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -285,7 +428,7 @@ std::shared_ptr BlockQueuePool::Front(uint32_t trackIndex) return nullptr; } auto queVector = queMap_[trackIndex]; - for (int i = 0; i < static_cast(queVector.size()); ++i) { + for (int32_t i = 0; i < static_cast(queVector.size()); ++i) { auto queIndex = queVector[i]; if (quePool_[queIndex].blockQue == nullptr) { MEDIA_LOG_D("Block queue " PUBLIC_LOG_D32 " is nullptr, will find next", queIndex); @@ -300,7 +443,8 @@ std::shared_ptr BlockQueuePool::Front(uint32_t trackIndex) return nullptr; } -std::shared_ptr BlockQueuePool::Back(uint32_t trackIndex) +template +std::shared_ptr BlockQueuePool::Back(uint32_t trackIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); @@ -320,7 +464,8 @@ std::shared_ptr BlockQueuePool::Back(uint32_t trackIndex) return nullptr; } -uint32_t BlockQueuePool::GetValidQueue() +template +uint32_t BlockQueuePool::GetValidQueue() { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S, name_.c_str()); @@ -333,7 +478,7 @@ uint32_t BlockQueuePool::GetValidQueue() quePool_[queCount_] = { false, 0, - std::make_shared>>("source_que_" + std::to_string(queCount_), + std::make_shared>>("source_que_" + std::to_string(queCount_), singleQueSize_) }; MEDIA_LOG_D("Out, block queue " PUBLIC_LOG_S ", valid queue index: " PUBLIC_LOG_U32, @@ -342,7 +487,8 @@ uint32_t BlockQueuePool::GetValidQueue() return (queCount_ - 1); } -bool BlockQueuePool::InnerQueueIsFull(uint32_t queueIndex) +template +bool BlockQueuePool::InnerQueueIsFull(uint32_t queueIndex) { std::unique_lock lockCacheQ(mutextCacheQ_); MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", queue " PUBLIC_LOG_U32, name_.c_str(), queueIndex); @@ -353,13 +499,15 @@ bool BlockQueuePool::InnerQueueIsFull(uint32_t queueIndex) return quePool_[queueIndex].blockQue->Size() >= quePool_[queueIndex].blockQue->Capacity(); } -bool BlockQueuePool::HasQueue(uint32_t trackIndex) +template +bool BlockQueuePool::HasQueue(uint32_t trackIndex) { MEDIA_LOG_D("In, block queue " PUBLIC_LOG_S ", track " PUBLIC_LOG_U32, name_.c_str(), trackIndex); return queMap_.count(trackIndex) > 0; } -Status BlockQueuePool::GetLastPTSByTrackId(uint32_t trackIndex, int64_t& maxPts) +template +Status BlockQueuePool::GetLastPTSByTrackId(uint32_t trackIndex, int64_t& maxPts) { std::unique_lock lockCacheQ(mutextCacheQ_); maxPts = INT64_MIN; @@ -380,5 +528,9 @@ Status BlockQueuePool::GetLastPTSByTrackId(uint32_t trackIndex, int64_t& maxPts) MEDIA_LOG_E("Track " PUBLIC_LOG_U32 " has not cache data", trackIndex); return Status::ERROR_NOT_EXISTED; } + +using Mp4BlockQueuePool = BlockQueuePool; +using FfmpegBlockQueuePool = BlockQueuePool; } // namespace Media -} // namespace OHOS \ No newline at end of file +} // namespace OHOS +#endif // BLOCK_QUEUE_POOL_H \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/common/demuxer_data_reader.cpp b/services/media_engine/plugins/demuxer/common/demuxer_data_reader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f014ac33b78ffe2e31b91523ae890f82f204fe6 --- /dev/null +++ b/services/media_engine/plugins/demuxer/common/demuxer_data_reader.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2025 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 HST_LOG_TAG "DemuxerDataReader" + +#include "common/log.h" +#include "demuxer_data_reader.h" + +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "DemuxerDataReader" }; +} + +namespace OHOS { +namespace Media { +namespace Plugins { +constexpr uint8_t BYTE_LENGTH = 8; + +Status DemuxerDataReader::SetDataReader(const std::shared_ptr& source) +{ + FALSE_RETURN_V_MSG_E(source != nullptr, Status::ERROR_INVALID_PARAMETER, "Source is nullptr"); + + dataSource_ = source; + MEDIA_LOG_D("SetDataReader Finish"); + return Status::OK; +} + +Status DemuxerDataReader::ReadUintData(int64_t offset, uint8_t* buffer, size_t size) +{ + FALSE_RETURN_V_MSG_E(buffer != nullptr, Status::ERROR_INVALID_PARAMETER, "Buffer is nullptr"); + FALSE_RETURN_V_MSG_E(size > 0, Status::ERROR_INVALID_PARAMETER, "Size must be greater than 0"); + + auto bufferInfo = std::make_shared(); + FALSE_RETURN_V_MSG_E(bufferInfo != nullptr, Status::ERROR_INVALID_PARAMETER, "bufferInfo is nullptr"); + auto bufData = bufferInfo->WrapMemory(buffer, size, size); + FALSE_RETURN_V_MSG_E(bufferInfo->GetMemory() != nullptr, Status::ERROR_UNKNOWN, "Alloc memory failed"); + + Status ret = dataSource_->ReadAt(offset, bufferInfo, size); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read data failed"); + FALSE_RETURN_V_MSG_E(bufferInfo->GetMemory()->GetSize() == size, Status::ERROR_UNKNOWN, "No enough data"); + + return Status::OK; +} + +bool DemuxerBitReader::HasBits(size_t numBits) const +{ + size_t totalBitsRead = 0; + size_t totalBitsAvailable = 0; + if (__builtin_mul_overflow(byteOffset_, BYTE_LENGTH, &totalBitsRead) || + __builtin_mul_overflow(totalBytes_, BYTE_LENGTH, &totalBitsAvailable)) { + return false; + } + if (__builtin_add_overflow(totalBitsRead, bitOffset_, &totalBitsRead)) { + return false; + } + + return (totalBitsAvailable - totalBitsRead) >= numBits; +} + +uint16_t DemuxerBitReader::ShowBits(uint8_t numBits) +{ + if (numBits < 0 || numBits > BYTE_LENGTH || !HasBits(numBits)) { + return 0; + } + uint16_t value = 0; + uint8_t bitOffset = bitOffset_; + uint32_t byteOffset = byteOffset_; + while (numBits > 0) { + uint8_t bitsInCurrentByte = BYTE_LENGTH - bitOffset; + uint8_t bitsToRead = std::min(numBits, bitsInCurrentByte); + + if (byteOffset >= totalBytes_) { + return 0; + } + uint8_t currentByte = data_[byteOffset]; + uint8_t maskedBits = (currentByte >> (BYTE_LENGTH - bitOffset - bitsToRead)) & ((1 << bitsToRead) - 1); + value |= (maskedBits << (numBits - bitsToRead)); + bitOffset += bitsToRead; + if (bitOffset == BYTE_LENGTH) { + bitOffset = 0; + byteOffset++; + } + numBits -= bitsToRead; + } + + return value; +} + +uint8_t DemuxerBitReader::ReadBits(uint8_t numBits) +{ + if (numBits < 0 || numBits > BYTE_LENGTH || !HasBits(numBits)) { + return 0; + } + uint8_t value = 0; + while (numBits > 0) { + uint8_t bitsInCurrentByte = BYTE_LENGTH - bitOffset_; + uint8_t bitsToRead = std::min(numBits, bitsInCurrentByte); + + if (byteOffset_ >= totalBytes_) { + return 0; + } + uint8_t currentByte = data_[byteOffset_]; + uint8_t maskedBits = (currentByte >> (BYTE_LENGTH - bitOffset_ - bitsToRead)) & ((1 << bitsToRead) - 1); + value |= (maskedBits << (numBits - bitsToRead)); + bitOffset_ += bitsToRead; + if (bitOffset_ == BYTE_LENGTH) { + bitOffset_ = 0; + byteOffset_++; + } + numBits -= bitsToRead; + } + + return value; +} + +bool DemuxerBitReader::SkipBits(uint8_t numBits) +{ + while (numBits > 0 && HasBits(numBits)) { + uint8_t bitsInCurrentByte = BYTE_LENGTH - bitOffset_; + uint8_t bitsToSkip = std::min(numBits, bitsInCurrentByte); + bitOffset_ += bitsToSkip; + if (bitOffset_ == BYTE_LENGTH) { + bitOffset_ = 0; + byteOffset_++; + } + numBits -= bitsToSkip; + } + if (numBits > 0) { + return false; + } + return true; +} +} // namespace Plugins +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/common/demuxer_data_reader.h b/services/media_engine/plugins/demuxer/common/demuxer_data_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..b765fa92a1774feb0b961d2fe4afff7970bda38c --- /dev/null +++ b/services/media_engine/plugins/demuxer/common/demuxer_data_reader.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 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 DEMUXER_DATA_READER_H +#define DEMUXER_DATA_READER_H + +#include "common/status.h" +#include "plugin/plugin_definition.h" + +namespace OHOS { +namespace Media { +namespace Plugins { +class DemuxerDataReader { +public: + Status SetDataReader(const std::shared_ptr& source); + Status ReadUintData(int64_t offset, uint8_t* buffer, size_t size); +private: + std::shared_ptr dataSource_ {nullptr}; +}; + +class DemuxerBitReader { +public: + DemuxerBitReader(const uint8_t* data, size_t size) + : data_(data), byteOffset_(0), bitOffset_(0), totalBytes_(size) {} + uint16_t ShowBits(uint8_t numBits); + uint8_t ReadBits(uint8_t numBits); + bool HasBits(size_t numBits) const; + bool SkipBits(uint8_t numBits); +private: + const uint8_t* data_; // Pointer to the raw data buffer + size_t byteOffset_; // Current byte offset indicating the position within the buffer + uint8_t bitOffset_; // Current bit offset within the byte (0-7) + size_t totalBytes_; // Total number of bytes +}; +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif //DEMUXER_LOG_COMPRESSOR_H \ No newline at end of file diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/demuxer_log_compressor.cpp b/services/media_engine/plugins/demuxer/common/demuxer_log_compressor.cpp similarity index 94% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/demuxer_log_compressor.cpp rename to services/media_engine/plugins/demuxer/common/demuxer_log_compressor.cpp index 99fc6dd8486a9bd9695503f30881852366c66931..1fb7a7b1aca804fce5dc4533fb99c14c9b5c20f3 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/demuxer_log_compressor.cpp +++ b/services/media_engine/plugins/demuxer/common/demuxer_log_compressor.cpp @@ -31,7 +31,6 @@ constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "D namespace OHOS { namespace Media { namespace Plugins { -namespace Ffmpeg { static std::unordered_map g_formatToIndex = { {Tag::AUDIO_AAC_IS_ADTS, "adts"}, {Tag::AUDIO_BITS_PER_CODED_SAMPLE, "bitsPerCodedSmp"}, @@ -77,6 +76,7 @@ static std::unordered_map g_formatToIndex = { {Tag::MEDIA_START_TIME, "trackStartTime"}, {Tag::MEDIA_TITLE, "title"}, {Tag::MEDIA_TRACK_COUNT, "trackCnt"}, + {Tag::MEDIA_TIME_SCALE, "timeScale"}, {Tag::MEDIA_TYPE, "trackType"}, {Tag::MIME_TYPE, "mime"}, {Tag::TIMED_METADATA_KEY, "metaKey"}, @@ -102,6 +102,24 @@ static std::unordered_map g_formatToIndex = { {Tag::REFERENCE_TRACK_IDS, "refIds"} }; +std::vector g_supportSourceFormat = { + Tag::MEDIA_TITLE, + Tag::MEDIA_ARTIST, + Tag::MEDIA_ALBUM, + Tag::MEDIA_ALBUM_ARTIST, + Tag::MEDIA_DATE, + Tag::MEDIA_COMMENT, + Tag::MEDIA_GENRE, + Tag::MEDIA_COPYRIGHT, + Tag::MEDIA_LANGUAGE, + Tag::MEDIA_DESCRIPTION, + Tag::MEDIA_LYRICS, + Tag::MEDIA_AUTHOR, + Tag::MEDIA_COMPOSER, + Tag::MEDIA_CREATION_TIME, + Tag::MEDIA_AIGC +}; + std::string DemuxerLogCompressor::FormatTagSerialize(Format& format) { std::stringstream dumpStr; @@ -182,7 +200,6 @@ void DemuxerLogCompressor::StringifyMeta(Meta meta, int32_t trackIndex) MEDIA_LOG_I("[track " PUBLIC_LOG_D32 "]: " PUBLIC_LOG_S, trackIndex, FormatTagSerialize(format).c_str()); } } -} // namespace Ffmpeg } // namespace Plugins } // namespace Media } // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/demuxer_log_compressor.h b/services/media_engine/plugins/demuxer/common/demuxer_log_compressor.h similarity index 91% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/demuxer_log_compressor.h rename to services/media_engine/plugins/demuxer/common/demuxer_log_compressor.h index 2b3ff1ec52f3389cfc4b7531d8566ebc646f5376..b3811c7b54847d5fd220b14ec7e359ea5374201a 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/demuxer_log_compressor.h +++ b/services/media_engine/plugins/demuxer/common/demuxer_log_compressor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 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 @@ -21,13 +21,11 @@ namespace OHOS { namespace Media { namespace Plugins { -namespace Ffmpeg { class DemuxerLogCompressor { public: static std::string FormatTagSerialize(Format& format); static void StringifyMeta(Meta meta, int32_t trackIndex); }; -} // namespace Ffmpeg } // namespace Plugins } // namespace Media } // namespace OHOS diff --git a/services/media_engine/plugins/ffmpeg_adapter/common/multi_stream_parser_manager.cpp b/services/media_engine/plugins/demuxer/common/multi_stream_parser_manager.cpp similarity index 88% rename from services/media_engine/plugins/ffmpeg_adapter/common/multi_stream_parser_manager.cpp rename to services/media_engine/plugins/demuxer/common/multi_stream_parser_manager.cpp index c378d17bbd32326e0836b0a0b70bde7d8e0cc218..9a045a9f8aa75c0ff9d5aea9f6021ac43fc4325b 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/common/multi_stream_parser_manager.cpp +++ b/services/media_engine/plugins/demuxer/common/multi_stream_parser_manager.cpp @@ -15,8 +15,10 @@ #include "multi_stream_parser_manager.h" #include #include "common/log.h" +#include "ffmpeg_converter.h" namespace { +const std::string AVC_LIB_PATH = "libav_codec_avc_parser.z.so"; const std::string HEVC_LIB_PATH = "libav_codec_hevc_parser.z.so"; const std::string VVC_LIB_PATH = "libav_codec_vvc_parser.z.so"; constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "MultiStreamParserManager" }; @@ -66,7 +68,9 @@ bool MultiStreamParserManager::Init(VideoStreamType videoStreamType) } std::string streamParserPath; - if (videoStreamType == VideoStreamType::HEVC) { + if (videoStreamType == VideoStreamType::AVC) { + streamParserPath = AVC_LIB_PATH; + } else if (videoStreamType == VideoStreamType::HEVC) { streamParserPath = HEVC_LIB_PATH; } else if (videoStreamType == VideoStreamType::VVC) { streamParserPath = VVC_LIB_PATH; @@ -228,6 +232,21 @@ void MultiStreamParserManager::ParseAnnexbExtraData(uint32_t trackId, const uint FALSE_RETURN_MSG(ParserIsInited(trackId), "Stream parser is invalid"); streamMap_[trackId].parser->ParseAnnexbExtraData(sample, size); } + +void MultiStreamParserManager::ParseMetadataInfo(uint32_t trackIndex, + std::shared_ptr streamParsers, HevcParseFormat& parse) +{ + parse.isHdrVivid = streamParsers->IsHdrVivid(trackIndex); + parse.colorRange = streamParsers->GetColorRange(trackIndex); + parse.colorPrimaries = streamParsers->GetColorPrimaries(trackIndex); + parse.colorTransfer = streamParsers->GetColorTransfer(trackIndex); + parse.colorMatrixCoeff = streamParsers->GetColorMatrixCoeff(trackIndex); + parse.profile = streamParsers->GetProfileIdc(trackIndex); + parse.level = streamParsers->GetLevelIdc(trackIndex); + parse.chromaLocation = streamParsers->GetChromaLocation(trackIndex); + parse.picWidInLumaSamples = streamParsers->GetPicWidInLumaSamples(trackIndex); + parse.picHetInLumaSamples = streamParsers->GetPicHetInLumaSamples(trackIndex); +} } // namespace Plugins } // namespace Media } // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/ffmpeg_adapter/common/multi_stream_parser_manager.h b/services/media_engine/plugins/demuxer/common/multi_stream_parser_manager.h similarity index 95% rename from services/media_engine/plugins/ffmpeg_adapter/common/multi_stream_parser_manager.h rename to services/media_engine/plugins/demuxer/common/multi_stream_parser_manager.h index 14e142a8c92f7eae9ed3f0e48e032c8baf36094b..0031a5136ff987122be0e9a14ac2852d9dba3ada 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/common/multi_stream_parser_manager.h +++ b/services/media_engine/plugins/demuxer/common/multi_stream_parser_manager.h @@ -56,6 +56,8 @@ public: size_t sideDataSize, bool isExtradata); void ParseAnnexbExtraData(uint32_t trackId, const uint8_t *sample, int32_t size); + static void ParseMetadataInfo(uint32_t trackIndex, std::shared_ptr streamParsers, + HevcParseFormat &parse); private: static std::mutex mtx_; diff --git a/services/media_engine/plugins/ffmpeg_adapter/common/reference_parser.h b/services/media_engine/plugins/demuxer/common/reference_parser.h similarity index 98% rename from services/media_engine/plugins/ffmpeg_adapter/common/reference_parser.h rename to services/media_engine/plugins/demuxer/common/reference_parser.h index 3fcd7de0003411b62a954d33d494eb992590de06..7a6033c2a461fe20d5567329dec2151c62570eeb 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/common/reference_parser.h +++ b/services/media_engine/plugins/demuxer/common/reference_parser.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 diff --git a/services/media_engine/plugins/ffmpeg_adapter/common/reference_parser_manager.cpp b/services/media_engine/plugins/demuxer/common/reference_parser_manager.cpp similarity index 99% rename from services/media_engine/plugins/ffmpeg_adapter/common/reference_parser_manager.cpp rename to services/media_engine/plugins/demuxer/common/reference_parser_manager.cpp index 8847030482cf707133fbd1370a813aa4adceac25..7e922ba35a33fb08415eb42133136a993167d91d 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/common/reference_parser_manager.cpp +++ b/services/media_engine/plugins/demuxer/common/reference_parser_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 diff --git a/services/media_engine/plugins/ffmpeg_adapter/common/reference_parser_manager.h b/services/media_engine/plugins/demuxer/common/reference_parser_manager.h similarity index 97% rename from services/media_engine/plugins/ffmpeg_adapter/common/reference_parser_manager.h rename to services/media_engine/plugins/demuxer/common/reference_parser_manager.h index d3c088dcaf440f180a7bd94affacd0b45d10f71e..0929e0b53f63458118e1ce220818cf05111210ec 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/common/reference_parser_manager.h +++ b/services/media_engine/plugins/demuxer/common/reference_parser_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 diff --git a/services/media_engine/plugins/demuxer/common/stream_parser.h b/services/media_engine/plugins/demuxer/common/stream_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..bf2c22aca5996be6a8469c90bef2e240bb280161 --- /dev/null +++ b/services/media_engine/plugins/demuxer/common/stream_parser.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023-2025 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 STREAM_PARSER_H +#define STREAM_PARSER_H + +#include + +namespace OHOS { +namespace Media { +namespace Plugins { +enum VideoStreamType { + AVC = 0, + HEVC = 1, + VVC = 2, +}; + +struct HevcParseFormat { + int32_t isHdrVivid = 0; + int32_t colorRange = 0; + uint8_t colorPrimaries = 0x02; + uint8_t colorTransfer = 0x02; + uint8_t colorMatrixCoeff = 0x02; + uint8_t profile = 0; + uint8_t level = 0; + uint32_t chromaLocation = 0; + uint32_t picWidInLumaSamples = 0; + uint32_t picHetInLumaSamples = 0; +}; + +class StreamParser { +public: + explicit StreamParser() = default; + virtual ~StreamParser() = default; + virtual void ParseExtraData(const uint8_t *sample, int32_t size, + uint8_t **extraDataBuf, int32_t *extraDataSize) = 0; + virtual bool ConvertExtraDataToAnnexb(uint8_t *extraData, int32_t extraDataSize) = 0; + virtual void ConvertPacketToAnnexb(uint8_t **hvccPacket, int32_t &hvccPacketSize, uint8_t *sideData, + size_t sideDataSize, bool isExtradata) = 0; + virtual void ParseAnnexbExtraData(const uint8_t *sample, int32_t size) = 0; + virtual void ResetXPSSendStatus(); + virtual bool IsHdrVivid() = 0; + virtual bool IsSyncFrame(const uint8_t *sample, int32_t size) = 0; + virtual bool GetColorRange() = 0; + virtual uint8_t GetColorPrimaries() = 0; + virtual uint8_t GetColorTransfer() = 0; + virtual uint8_t GetColorMatrixCoeff() = 0; + virtual uint8_t GetProfileIdc() = 0; + virtual uint8_t GetLevelIdc() = 0; + virtual uint32_t GetChromaLocation() = 0; + virtual uint32_t GetPicWidInLumaSamples() = 0; + virtual uint32_t GetPicHetInLumaSamples() = 0; + virtual std::vector GetLogInfo() = 0; + virtual uint32_t GetMaxReorderPic() = 0; +}; +} // Plugins +} // Media +} // OHOS +#endif // STREAM_PARSER_H \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_converter.cpp b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_converter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90ec43a7b515905ff36fedc793d3658d2234be76 --- /dev/null +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_converter.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2025 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 HST_LOG_TAG "FfmpegConverter" + +#include +#include +#include "common/log.h" +#include "ffmpeg_converter.h" +namespace { +constexpr int US_PER_SECOND = 1000000; +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_DEMUXER, "FFmpegConverter"}; +} +namespace OHOS { +namespace Media { +namespace Plugins { +// ffmpeg channel layout to histreamer channel layout +const std::vector> g_toFFMPEGChannelLayout = { + {AudioChannelLayout::MONO, AV_CH_LAYOUT_MONO}, + {AudioChannelLayout::STEREO, AV_CH_LAYOUT_STEREO}, + {AudioChannelLayout::CH_2POINT1, AV_CH_LAYOUT_2POINT1}, + {AudioChannelLayout::CH_2_1, AV_CH_LAYOUT_2_1}, + {AudioChannelLayout::SURROUND, AV_CH_LAYOUT_SURROUND}, + {AudioChannelLayout::CH_3POINT1, AV_CH_LAYOUT_3POINT1}, + {AudioChannelLayout::CH_4POINT0, AV_CH_LAYOUT_4POINT0}, + {AudioChannelLayout::CH_4POINT1, AV_CH_LAYOUT_4POINT1}, + {AudioChannelLayout::CH_2_2, AV_CH_LAYOUT_2_2}, + {AudioChannelLayout::QUAD, AV_CH_LAYOUT_QUAD}, + {AudioChannelLayout::CH_5POINT0, AV_CH_LAYOUT_5POINT0}, + {AudioChannelLayout::CH_5POINT1, AV_CH_LAYOUT_5POINT1}, + {AudioChannelLayout::CH_5POINT0_BACK, AV_CH_LAYOUT_5POINT0_BACK}, + {AudioChannelLayout::CH_5POINT1_BACK, AV_CH_LAYOUT_5POINT1_BACK}, + {AudioChannelLayout::CH_6POINT0, AV_CH_LAYOUT_6POINT0}, + {AudioChannelLayout::CH_6POINT0_FRONT, AV_CH_LAYOUT_6POINT0_FRONT}, + {AudioChannelLayout::HEXAGONAL, AV_CH_LAYOUT_HEXAGONAL}, + {AudioChannelLayout::CH_6POINT1, AV_CH_LAYOUT_6POINT1}, + {AudioChannelLayout::CH_6POINT1_BACK, AV_CH_LAYOUT_6POINT1_BACK}, + {AudioChannelLayout::CH_6POINT1_FRONT, AV_CH_LAYOUT_6POINT1_FRONT}, + {AudioChannelLayout::CH_7POINT0, AV_CH_LAYOUT_7POINT0}, + {AudioChannelLayout::CH_7POINT0_FRONT, AV_CH_LAYOUT_7POINT0_FRONT}, + {AudioChannelLayout::CH_7POINT1, AV_CH_LAYOUT_7POINT1}, + {AudioChannelLayout::CH_7POINT1_WIDE, AV_CH_LAYOUT_7POINT1_WIDE}, + {AudioChannelLayout::CH_7POINT1_WIDE_BACK, AV_CH_LAYOUT_7POINT1_WIDE_BACK}, + {AudioChannelLayout::OCTAGONAL, AV_CH_LAYOUT_OCTAGONAL}, + {AudioChannelLayout::HEXADECAGONAL, AV_CH_LAYOUT_HEXADECAGONAL}, + {AudioChannelLayout::STEREO_DOWNMIX, AV_CH_LAYOUT_STEREO_DOWNMIX}, +}; + +const std::vector> g_audioVividChannelLayoutMap = { + {AudioChannelLayout::MONO, 1}, + {AudioChannelLayout::STEREO, 2}, + {AudioChannelLayout::CH_4POINT0, 4}, + {AudioChannelLayout::CH_5POINT1, 6}, + {AudioChannelLayout::CH_7POINT1, 8}, + {AudioChannelLayout::CH_5POINT1POINT2, 8}, + {AudioChannelLayout::CH_5POINT1POINT4, 10}, + {AudioChannelLayout::CH_7POINT1POINT2, 10}, + {AudioChannelLayout::CH_7POINT1POINT4, 12}, + {AudioChannelLayout::HOA_ORDER1_ACN_SN3D, 4}, + {AudioChannelLayout::HOA_ORDER2_ACN_SN3D, 9}, + {AudioChannelLayout::HOA_ORDER3_ACN_SN3D, 16}, +}; + +const std::vector> g_channelLayoutDefaultMap = { + {2, AudioChannelLayout::STEREO}, // 2: STEREO + {4, AudioChannelLayout::CH_4POINT0}, // 4: CH_4POINT0 + {6, AudioChannelLayout::CH_5POINT1}, // 6: CH_5POINT1 + {8, AudioChannelLayout::CH_5POINT1POINT2}, // 8: CH_5POINT1POINT2 + {9, AudioChannelLayout::HOA_ORDER2_ACN_N3D}, // 9: HOA_ORDER2_ACN_N3D + {10, AudioChannelLayout::CH_7POINT1POINT2}, // 10: CH_7POINT1POINT2 or CH_5POINT1POINT4 ? + {12, AudioChannelLayout::CH_7POINT1POINT4}, // 12: CH_7POINT1POINT4 + {14, AudioChannelLayout::CH_9POINT1POINT4}, // 14: CH_9POINT1POINT4 + {16, AudioChannelLayout::CH_9POINT1POINT6}, // 16: CH_9POINT1POINT6 + {24, AudioChannelLayout::CH_22POINT2}, // 24: CH_22POINT2 +}; + +const std::vector> g_pFfSampleFmtMap = { + {AVSampleFormat::AV_SAMPLE_FMT_U8, AudioSampleFormat::SAMPLE_U8}, + {AVSampleFormat::AV_SAMPLE_FMT_S16, AudioSampleFormat::SAMPLE_S16LE}, + {AVSampleFormat::AV_SAMPLE_FMT_S32, AudioSampleFormat::SAMPLE_S32LE}, + {AVSampleFormat::AV_SAMPLE_FMT_FLT, AudioSampleFormat::SAMPLE_F32LE}, + {AVSampleFormat::AV_SAMPLE_FMT_U8P, AudioSampleFormat::SAMPLE_U8P}, + {AVSampleFormat::AV_SAMPLE_FMT_S16P, AudioSampleFormat::SAMPLE_S16P}, + {AVSampleFormat::AV_SAMPLE_FMT_S32P, AudioSampleFormat::SAMPLE_S32P}, + {AVSampleFormat::AV_SAMPLE_FMT_FLTP, AudioSampleFormat::SAMPLE_F32P}, +}; + +// align with player framework capability. +const std::vector> g_pFfCodeIDToSampleFmtMap = { + {AVCodecID::AV_CODEC_ID_PCM_U8, AudioSampleFormat::SAMPLE_U8}, + {AVCodecID::AV_CODEC_ID_PCM_S16LE, AudioSampleFormat::SAMPLE_S16LE}, + {AVCodecID::AV_CODEC_ID_PCM_S24LE, AudioSampleFormat::SAMPLE_S24LE}, + {AVCodecID::AV_CODEC_ID_PCM_S32LE, AudioSampleFormat::SAMPLE_S32LE}, + {AVCodecID::AV_CODEC_ID_PCM_F32LE, AudioSampleFormat::SAMPLE_F32LE}, + {AVCodecID::AV_CODEC_ID_PCM_S16BE, AudioSampleFormat::SAMPLE_S16BE}, + {AVCodecID::AV_CODEC_ID_PCM_S24BE, AudioSampleFormat::SAMPLE_S24BE}, + {AVCodecID::AV_CODEC_ID_PCM_S32BE, AudioSampleFormat::SAMPLE_S32BE}, + {AVCodecID::AV_CODEC_ID_PCM_F32BE, AudioSampleFormat::SAMPLE_F32BE}, + {AVCodecID::AV_CODEC_ID_PCM_F64BE, AudioSampleFormat::SAMPLE_F64BE}, +}; + +const std::vector> g_ChannelLayoutToString = { + {AudioChannelLayout::UNKNOWN, "UNKNOW"}, + {AudioChannelLayout::MONO, "MONO"}, + {AudioChannelLayout::STEREO, "STEREO"}, + {AudioChannelLayout::CH_2POINT1, "2POINT1"}, + {AudioChannelLayout::CH_2_1, "CH_2_1"}, + {AudioChannelLayout::SURROUND, "SURROUND"}, + {AudioChannelLayout::CH_3POINT1, "3POINT1"}, + {AudioChannelLayout::CH_4POINT0, "4POINT0"}, + {AudioChannelLayout::CH_4POINT1, "4POINT1"}, + {AudioChannelLayout::CH_2_2, "CH_2_2"}, + {AudioChannelLayout::QUAD, "QUAD"}, + {AudioChannelLayout::CH_5POINT0, "5POINT0"}, + {AudioChannelLayout::CH_5POINT1, "5POINT1"}, + {AudioChannelLayout::CH_5POINT0_BACK, "5POINT0_BACK"}, + {AudioChannelLayout::CH_5POINT1_BACK, "5POINT1_BACK"}, + {AudioChannelLayout::CH_6POINT0, "6POINT0"}, + {AudioChannelLayout::CH_6POINT0_FRONT, "6POINT0_FRONT"}, + {AudioChannelLayout::HEXAGONAL, "HEXAGONAL"}, + {AudioChannelLayout::CH_6POINT1, "6POINT1"}, + {AudioChannelLayout::CH_6POINT1_BACK, "6POINT1_BACK"}, + {AudioChannelLayout::CH_6POINT1_FRONT, "6POINT1_FRONT"}, + {AudioChannelLayout::CH_7POINT0, "7POINT0"}, + {AudioChannelLayout::CH_7POINT0_FRONT, "7POINT0_FRONT"}, + {AudioChannelLayout::CH_7POINT1, "7POINT1"}, + {AudioChannelLayout::CH_7POINT1_WIDE, "7POINT1_WIDE"}, + {AudioChannelLayout::CH_7POINT1_WIDE_BACK, "7POINT1_WIDE_BACK"}, + {AudioChannelLayout::CH_3POINT1POINT2, "CH_3POINT1POINT2"}, + {AudioChannelLayout::CH_5POINT1POINT2, "CH_5POINT1POINT2"}, + {AudioChannelLayout::CH_5POINT1POINT4, "CH_5POINT1POINT4"}, + {AudioChannelLayout::CH_7POINT1POINT2, "CH_7POINT1POINT2"}, + {AudioChannelLayout::CH_7POINT1POINT4, "CH_7POINT1POINT4"}, + {AudioChannelLayout::CH_9POINT1POINT4, "CH_9POINT1POINT4"}, + {AudioChannelLayout::CH_9POINT1POINT6, "CH_9POINT1POINT6"}, + {AudioChannelLayout::CH_10POINT2, "CH_10POINT2"}, + {AudioChannelLayout::CH_22POINT2, "CH_22POINT2"}, + {AudioChannelLayout::OCTAGONAL, "OCTAGONAL"}, + {AudioChannelLayout::HEXADECAGONAL, "HEXADECAGONAL"}, + {AudioChannelLayout::STEREO_DOWNMIX, "STEREO_DOWNMIX"}, + {AudioChannelLayout::CH_2POINT0POINT2, "CH_2POINT0POINT2"}, + {AudioChannelLayout::CH_2POINT1POINT2, "CH_2POINT1POINT2"}, + {AudioChannelLayout::CH_3POINT0POINT2, "CH_3POINT0POINT2"}, + {AudioChannelLayout::HOA_ORDER1_ACN_N3D, "HOA_ORDER1_ACN_N3D"}, + {AudioChannelLayout::HOA_ORDER1_ACN_SN3D, "HOA_ORDER1_ACN_SN3D"}, + {AudioChannelLayout::HOA_ORDER1_FUMA, "HOA_ORDER1_FUMA"}, + {AudioChannelLayout::HOA_ORDER2_ACN_N3D, "HOA_ORDER2_ACN_N3D"}, + {AudioChannelLayout::HOA_ORDER2_ACN_SN3D, "HOA_ORDER2_ACN_SN3D"}, + {AudioChannelLayout::HOA_ORDER2_FUMA, "HOA_ORDER2_FUMA"}, + {AudioChannelLayout::HOA_ORDER3_ACN_N3D, "HOA_ORDER3_ACN_N3D"}, + {AudioChannelLayout::HOA_ORDER3_ACN_SN3D, "HOA_ORDER3_ACN_SN3D"}, + {AudioChannelLayout::HOA_ORDER3_FUMA, "HOA_ORDER3_FUMA"}, +}; + +const std::vector> g_pFfColorPrimariesMap = { + {AVColorPrimaries::AVCOL_PRI_BT709, ColorPrimary::BT709}, + {AVColorPrimaries::AVCOL_PRI_UNSPECIFIED, ColorPrimary::UNSPECIFIED}, + {AVColorPrimaries::AVCOL_PRI_BT470M, ColorPrimary::BT470_M}, + {AVColorPrimaries::AVCOL_PRI_BT470BG, ColorPrimary::BT601_625}, + {AVColorPrimaries::AVCOL_PRI_SMPTE170M, ColorPrimary::BT601_525}, + {AVColorPrimaries::AVCOL_PRI_SMPTE240M, ColorPrimary::SMPTE_ST240}, + {AVColorPrimaries::AVCOL_PRI_FILM, ColorPrimary::GENERIC_FILM}, + {AVColorPrimaries::AVCOL_PRI_BT2020, ColorPrimary::BT2020}, + {AVColorPrimaries::AVCOL_PRI_SMPTE428, ColorPrimary::SMPTE_ST428}, + {AVColorPrimaries::AVCOL_PRI_SMPTEST428_1, ColorPrimary::SMPTE_ST428}, + {AVColorPrimaries::AVCOL_PRI_SMPTE431, ColorPrimary::P3DCI}, + {AVColorPrimaries::AVCOL_PRI_SMPTE432, ColorPrimary::P3D65}, +}; + +const std::vector> g_pFfTransferCharacteristicMap = { + {AVColorTransferCharacteristic::AVCOL_TRC_BT709, TransferCharacteristic::BT709}, + {AVColorTransferCharacteristic::AVCOL_TRC_UNSPECIFIED, TransferCharacteristic::UNSPECIFIED}, + {AVColorTransferCharacteristic::AVCOL_TRC_GAMMA22, TransferCharacteristic::GAMMA_2_2}, + {AVColorTransferCharacteristic::AVCOL_TRC_GAMMA28, TransferCharacteristic::GAMMA_2_8}, + {AVColorTransferCharacteristic::AVCOL_TRC_SMPTE170M, TransferCharacteristic::BT601}, + {AVColorTransferCharacteristic::AVCOL_TRC_SMPTE240M, TransferCharacteristic::SMPTE_ST240}, + {AVColorTransferCharacteristic::AVCOL_TRC_LINEAR, TransferCharacteristic::LINEAR}, + {AVColorTransferCharacteristic::AVCOL_TRC_LOG, TransferCharacteristic::LOG}, + {AVColorTransferCharacteristic::AVCOL_TRC_LOG_SQRT, TransferCharacteristic::LOG_SQRT}, + {AVColorTransferCharacteristic::AVCOL_TRC_IEC61966_2_4, TransferCharacteristic::IEC_61966_2_4}, + {AVColorTransferCharacteristic::AVCOL_TRC_BT1361_ECG, TransferCharacteristic::BT1361}, + {AVColorTransferCharacteristic::AVCOL_TRC_IEC61966_2_1, TransferCharacteristic::IEC_61966_2_1}, + {AVColorTransferCharacteristic::AVCOL_TRC_BT2020_10, TransferCharacteristic::BT2020_10BIT}, + {AVColorTransferCharacteristic::AVCOL_TRC_BT2020_12, TransferCharacteristic::BT2020_12BIT}, + {AVColorTransferCharacteristic::AVCOL_TRC_SMPTE2084, TransferCharacteristic::PQ}, + {AVColorTransferCharacteristic::AVCOL_TRC_SMPTEST2084, TransferCharacteristic::PQ}, + {AVColorTransferCharacteristic::AVCOL_TRC_SMPTE428, TransferCharacteristic::SMPTE_ST428}, + {AVColorTransferCharacteristic::AVCOL_TRC_SMPTEST428_1, TransferCharacteristic::SMPTE_ST428}, + {AVColorTransferCharacteristic::AVCOL_TRC_ARIB_STD_B67, TransferCharacteristic::HLG}, +}; + +const std::vector> g_pFfMatrixCoefficientMap = { + {AVColorSpace::AVCOL_SPC_RGB, MatrixCoefficient::IDENTITY}, + {AVColorSpace::AVCOL_SPC_BT709, MatrixCoefficient::BT709}, + {AVColorSpace::AVCOL_SPC_UNSPECIFIED, MatrixCoefficient::UNSPECIFIED}, + {AVColorSpace::AVCOL_SPC_FCC, MatrixCoefficient::FCC}, + {AVColorSpace::AVCOL_SPC_BT470BG, MatrixCoefficient::BT601_625}, + {AVColorSpace::AVCOL_SPC_SMPTE170M, MatrixCoefficient::BT601_525}, + {AVColorSpace::AVCOL_SPC_SMPTE240M, MatrixCoefficient::SMPTE_ST240}, + {AVColorSpace::AVCOL_SPC_YCGCO, MatrixCoefficient::YCGCO}, + {AVColorSpace::AVCOL_SPC_YCOCG, MatrixCoefficient::YCGCO}, + {AVColorSpace::AVCOL_SPC_BT2020_NCL, MatrixCoefficient::BT2020_NCL}, + {AVColorSpace::AVCOL_SPC_BT2020_CL, MatrixCoefficient::BT2020_CL}, + {AVColorSpace::AVCOL_SPC_SMPTE2085, MatrixCoefficient::SMPTE_ST2085}, + {AVColorSpace::AVCOL_SPC_CHROMA_DERIVED_NCL, MatrixCoefficient::CHROMATICITY_NCL}, + {AVColorSpace::AVCOL_SPC_CHROMA_DERIVED_CL, MatrixCoefficient::CHROMATICITY_CL}, + {AVColorSpace::AVCOL_SPC_ICTCP, MatrixCoefficient::ICTCP}, +}; + +const std::vector> g_pFfColorRangeMap = { + {AVColorRange::AVCOL_RANGE_MPEG, 0}, + {AVColorRange::AVCOL_RANGE_JPEG, 1}, +}; + +const std::vector> g_pFfChromaLocationMap = { + {AVChromaLocation::AVCHROMA_LOC_UNSPECIFIED, ChromaLocation::UNSPECIFIED}, + {AVChromaLocation::AVCHROMA_LOC_LEFT, ChromaLocation::LEFT}, + {AVChromaLocation::AVCHROMA_LOC_CENTER, ChromaLocation::CENTER}, + {AVChromaLocation::AVCHROMA_LOC_TOPLEFT, ChromaLocation::TOPLEFT}, + {AVChromaLocation::AVCHROMA_LOC_TOP, ChromaLocation::TOP}, + {AVChromaLocation::AVCHROMA_LOC_BOTTOMLEFT, ChromaLocation::BOTTOMLEFT}, + {AVChromaLocation::AVCHROMA_LOC_BOTTOM, ChromaLocation::BOTTOM}, +}; + +const std::vector> g_pFfHEVCProfileMap = { + {FF_PROFILE_HEVC_MAIN, HEVCProfile::HEVC_PROFILE_MAIN}, + {FF_PROFILE_HEVC_MAIN_10, HEVCProfile::HEVC_PROFILE_MAIN_10}, + {FF_PROFILE_HEVC_MAIN_STILL_PICTURE, HEVCProfile::HEVC_PROFILE_MAIN_STILL}, +}; + +const std::vector> g_pFfHEVCLevelMap = { + {30, HEVCLevel::HEVC_LEVEL_1}, {60, HEVCLevel::HEVC_LEVEL_2}, {63, HEVCLevel::HEVC_LEVEL_21}, + {90, HEVCLevel::HEVC_LEVEL_3}, {93, HEVCLevel::HEVC_LEVEL_31}, {120, HEVCLevel::HEVC_LEVEL_4}, + {123, HEVCLevel::HEVC_LEVEL_41}, {150, HEVCLevel::HEVC_LEVEL_5}, {153, HEVCLevel::HEVC_LEVEL_51}, + {156, HEVCLevel::HEVC_LEVEL_52}, {180, HEVCLevel::HEVC_LEVEL_6}, {183, HEVCLevel::HEVC_LEVEL_61}, + {186, HEVCLevel::HEVC_LEVEL_62}, +}; + +HEVCLevel FFMpegConverter::ConvertFFMpegToOHHEVCLevel(int ffHEVCLevel) +{ + auto ite = std::find_if(g_pFfHEVCLevelMap.begin(), g_pFfHEVCLevelMap.end(), + [&ffHEVCLevel](const auto &item) -> bool { return item.first == ffHEVCLevel; }); + if (ite == g_pFfHEVCLevelMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, ffHEVCLevel); + return HEVCLevel::HEVC_LEVEL_UNKNOW; + } + return ite->second; +} + +HEVCProfile FFMpegConverter::ConvertFFMpegToOHHEVCProfile(int ffHEVCProfile) +{ + auto ite = std::find_if(g_pFfHEVCProfileMap.begin(), g_pFfHEVCProfileMap.end(), + [&ffHEVCProfile](const auto &item) -> bool { return item.first == ffHEVCProfile; }); + if (ite == g_pFfHEVCProfileMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, ffHEVCProfile); + return HEVCProfile::HEVC_PROFILE_UNKNOW; + } + return ite->second; +} + +ColorPrimary FFMpegConverter::ConvertFFMpegToOHColorPrimaries(AVColorPrimaries ffColorPrimaries) +{ + auto ite = std::find_if(g_pFfColorPrimariesMap.begin(), g_pFfColorPrimariesMap.end(), + [&ffColorPrimaries](const auto &item) -> bool { return item.first == ffColorPrimaries; }); + if (ite == g_pFfColorPrimariesMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(ffColorPrimaries)); + return ColorPrimary::UNSPECIFIED; + } + return ite->second; +} + +TransferCharacteristic FFMpegConverter::ConvertFFMpegToOHColorTrans(AVColorTransferCharacteristic ffColorTrans) +{ + auto ite = std::find_if(g_pFfTransferCharacteristicMap.begin(), g_pFfTransferCharacteristicMap.end(), + [&ffColorTrans](const auto &item) -> bool { return item.first == ffColorTrans; }); + if (ite == g_pFfTransferCharacteristicMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(ffColorTrans)); + return TransferCharacteristic::UNSPECIFIED; + } + return ite->second; +} + +MatrixCoefficient FFMpegConverter::ConvertFFMpegToOHColorMatrix(AVColorSpace ffColorSpace) +{ + auto ite = std::find_if(g_pFfMatrixCoefficientMap.begin(), g_pFfMatrixCoefficientMap.end(), + [&ffColorSpace](const auto &item) -> bool { return item.first == ffColorSpace; }); + if (ite == g_pFfMatrixCoefficientMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(ffColorSpace)); + return MatrixCoefficient::UNSPECIFIED; + } + return ite->second; +} + +int FFMpegConverter::ConvertFFMpegToOHColorRange(AVColorRange ffColorRange) +{ + auto ite = std::find_if(g_pFfColorRangeMap.begin(), g_pFfColorRangeMap.end(), + [&ffColorRange](const auto &item) -> bool { return item.first == ffColorRange; }); + if (ite == g_pFfColorRangeMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(ffColorRange)); + return 0; + } + return ite->second; +} + +ChromaLocation FFMpegConverter::ConvertFFMpegToOHChromaLocation(AVChromaLocation ffChromaLocation) +{ + auto ite = std::find_if(g_pFfChromaLocationMap.begin(), g_pFfChromaLocationMap.end(), + [&ffChromaLocation](const auto &item) -> bool { return item.first == ffChromaLocation; }); + if (ite == g_pFfChromaLocationMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(ffChromaLocation)); + return ChromaLocation::UNSPECIFIED; + } + return ite->second; +} + +AudioSampleFormat FFMpegConverter::ConvertFFMpegAVCodecIdToOHAudioFormat(AVCodecID codecId) +{ + auto ite = std::find_if(g_pFfCodeIDToSampleFmtMap.begin(), g_pFfCodeIDToSampleFmtMap.end(), + [&codecId](const auto &item) -> bool { return item.first == codecId; }); + if (ite == g_pFfCodeIDToSampleFmtMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(codecId)); + return AudioSampleFormat::INVALID_WIDTH; + } + return ite->second; +} + +AudioSampleFormat FFMpegConverter::ConvertFFMpegToOHAudioFormat(AVSampleFormat ffSampleFormat) +{ + auto ite = std::find_if(g_pFfSampleFmtMap.begin(), g_pFfSampleFmtMap.end(), + [&ffSampleFormat](const auto &item) -> bool { return item.first == ffSampleFormat; }); + if (ite == g_pFfSampleFmtMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(ffSampleFormat)); + return AudioSampleFormat::INVALID_WIDTH; + } + return ite->second; +} + +AVSampleFormat FFMpegConverter::ConvertOHAudioFormatToFFMpeg(AudioSampleFormat sampleFormat) +{ + auto ite = std::find_if(g_pFfSampleFmtMap.begin(), g_pFfSampleFmtMap.end(), + [&sampleFormat](const auto &item) -> bool { return item.second == sampleFormat; }); + if (ite == g_pFfSampleFmtMap.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(sampleFormat)); + return AVSampleFormat::AV_SAMPLE_FMT_NONE; + } + return ite->first; +} + +AudioChannelLayout FFMpegConverter::ConvertFFToOHAudioChannelLayout(uint64_t ffChannelLayout) +{ + auto ite = std::find_if(g_toFFMPEGChannelLayout.begin(), g_toFFMPEGChannelLayout.end(), + [&ffChannelLayout](const auto &item) -> bool { return item.second == ffChannelLayout; }); + if (ite == g_toFFMPEGChannelLayout.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_U64, ffChannelLayout); + return AudioChannelLayout::MONO; + } + return ite->first; +} + +AudioChannelLayout FFMpegConverter::GetDefaultChannelLayout(int channels) +{ + AudioChannelLayout layout = AudioChannelLayout::MONO; + auto ite = std::find_if(g_channelLayoutDefaultMap.begin(), g_channelLayoutDefaultMap.end(), + [&channels](const auto &item) -> bool { return item.first == channels; }); + if (ite != g_channelLayoutDefaultMap.end()) { + layout = ite->second; + } + MEDIA_LOG_W("Default: " PUBLIC_LOG_S, ConvertOHAudioChannelLayoutToString(layout).data()); + return layout; +} + +AudioChannelLayout FFMpegConverter::ConvertFFToOHAudioChannelLayoutV2(uint64_t ffChannelLayout, int channels) +{ + auto ite = std::find_if(g_toFFMPEGChannelLayout.begin(), g_toFFMPEGChannelLayout.end(), + [&ffChannelLayout](const auto &item) -> bool { return item.second == ffChannelLayout; }); + if (ite == g_toFFMPEGChannelLayout.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_U64, ffChannelLayout); + return GetDefaultChannelLayout(channels); + } + return ite->first; +} + +uint64_t FFMpegConverter::ConvertOHAudioChannelLayoutToFFMpeg(AudioChannelLayout channelLayout) +{ + auto ite = std::find_if(g_toFFMPEGChannelLayout.begin(), g_toFFMPEGChannelLayout.end(), + [&channelLayout](const auto &item) -> bool { return item.first == channelLayout; }); + if (ite == g_toFFMPEGChannelLayout.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(channelLayout)); + return AV_CH_LAYOUT_NATIVE; + } + return ite->second; +} + +std::string_view FFMpegConverter::ConvertOHAudioChannelLayoutToString(AudioChannelLayout layout) +{ + auto ite = std::find_if(g_ChannelLayoutToString.begin(), g_ChannelLayoutToString.end(), + [&layout](const auto &item) -> bool { return item.first == layout; }); + if (ite == g_ChannelLayoutToString.end()) { + MEDIA_LOG_W("Failed: " PUBLIC_LOG_D32, static_cast(layout)); + return g_ChannelLayoutToString[0].second; + } + return ite->second; +} + +int64_t FFMpegConverter::ConvertAudioPtsToUs(int64_t pts, AVRational base) +{ + if (pts == AV_NOPTS_VALUE) { + return -1; + } + AVRational us = {1, US_PER_SECOND}; + return av_rescale_q(pts, base, us); +} + +std::string FFMpegConverter::AVStrError(int errnum) +{ + char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); + return std::string(errbuf); +} + +AudioChannelLayout FFMpegConverter::ConvertAudioVividToOHAudioChannelLayout(uint64_t ffChannelLayout, int channels) +{ + auto ite = std::find_if(g_audioVividChannelLayoutMap.begin(), g_audioVividChannelLayoutMap.end(), + [&ffChannelLayout](const auto &item) -> bool { + return static_cast(item.first) == ffChannelLayout; + }); + if (ite == g_audioVividChannelLayoutMap.end() || ite -> second != channels) { + MEDIA_LOG_W("Convert channel layout failed: " PUBLIC_LOG_U64, ffChannelLayout); + return GetDefaultChannelLayout(channels); + } + return ite->first; +} +} // namespace Plugins +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_converter.h b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_converter.h new file mode 100644 index 0000000000000000000000000000000000000000..d5d27644a9cb6ed772ee4188096c0f0202f9179e --- /dev/null +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_converter.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 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 FFMPEG_CONVERTER_H +#define FFMPEG_CONVERTER_H + +#include +#include +#include "meta/meta_key.h" +#include "meta/media_types.h" +#include "meta/mime_type.h" +#include "meta/video_types.h" +#include "meta/audio_types.h" +#ifdef __cplusplus +extern "C" { +#endif +#include "libavcodec/avcodec.h" +#include "libavutil/pixfmt.h" +#include "libavcodec/bsf.h" +#include "libavutil/channel_layout.h" +#ifdef __cplusplus +} +#endif +namespace OHOS { +namespace Media { +namespace Plugins { +class FFMpegConverter { +public: + static ColorPrimary ConvertFFMpegToOHColorPrimaries(AVColorPrimaries ffColorPrimaries); + static TransferCharacteristic ConvertFFMpegToOHColorTrans(AVColorTransferCharacteristic ffColorTrans); + static MatrixCoefficient ConvertFFMpegToOHColorMatrix(AVColorSpace ffColorSpace); + static int ConvertFFMpegToOHColorRange(AVColorRange ffColorRange); + static ChromaLocation ConvertFFMpegToOHChromaLocation(AVChromaLocation ffChromaLocation); + static HEVCProfile ConvertFFMpegToOHHEVCProfile(int ffHEVCProfile); + static HEVCLevel ConvertFFMpegToOHHEVCLevel(int ffHEVCLevel); + static AudioSampleFormat ConvertFFMpegAVCodecIdToOHAudioFormat(AVCodecID codecId); + static AudioSampleFormat ConvertFFMpegToOHAudioFormat(AVSampleFormat ffSampleFormat); + static AVSampleFormat ConvertOHAudioFormatToFFMpeg(AudioSampleFormat sampleFormat); + static AudioChannelLayout ConvertFFToOHAudioChannelLayout(uint64_t ffChannelLayout); + static AudioChannelLayout GetDefaultChannelLayout(int channels); + static AudioChannelLayout ConvertFFToOHAudioChannelLayoutV2(uint64_t ffChannelLayout, int channels); + static uint64_t ConvertOHAudioChannelLayoutToFFMpeg(AudioChannelLayout channelLayout); + static std::string_view ConvertOHAudioChannelLayoutToString(AudioChannelLayout layout); + static int64_t ConvertAudioPtsToUs(int64_t pts, AVRational base); + static std::string AVStrError(int errnum); + static AudioChannelLayout ConvertAudioVividToOHAudioChannelLayout(uint64_t ffChannelLayout, int channels); +private: + FFMpegConverter() = delete; + ~FFMpegConverter() = delete; +}; +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.cpp similarity index 99% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp rename to services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.cpp index 544b95a0712a3fafe486724a6de41679879a4f84..4a2125fdd5269a16885dedbce6ea19fcc99978a1 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 @@ -1824,17 +1824,7 @@ Status FFmpegDemuxerPlugin::ParseVideoFirstFrames() void FFmpegDemuxerPlugin::ParseHEVCMetadataInfo(const AVStream& avStream, Meta& format) { HevcParseFormat parse; - parse.isHdrVivid = streamParsers_->IsHdrVivid(avStream.index); - parse.colorRange = streamParsers_->GetColorRange(avStream.index); - parse.colorPrimaries = streamParsers_->GetColorPrimaries(avStream.index); - parse.colorTransfer = streamParsers_->GetColorTransfer(avStream.index); - parse.colorMatrixCoeff = streamParsers_->GetColorMatrixCoeff(avStream.index); - parse.profile = streamParsers_->GetProfileIdc(avStream.index); - parse.level = streamParsers_->GetLevelIdc(avStream.index); - parse.chromaLocation = streamParsers_->GetChromaLocation(avStream.index); - parse.picWidInLumaSamples = streamParsers_->GetPicWidInLumaSamples(avStream.index); - parse.picHetInLumaSamples = streamParsers_->GetPicHetInLumaSamples(avStream.index); - + MultiStreamParserManager::ParseMetadataInfo(avStream.index, streamParsers_, parse); FFmpegFormatHelper::ParseHevcInfo(*formatContext_, parse, format); } diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.h similarity index 99% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h rename to services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.h index cd32d1a75eaedd7421196fc5cabb15e39de12167..9485948878e1468b0f14a12924cda531f6a7ff7c 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.h +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 @@ -236,7 +236,7 @@ private: Seekable seekable_; IOContext ioContext_; std::vector selectedTrackIds_; - BlockQueuePool cacheQueue_; + FfmpegBlockQueuePool cacheQueue_; MediaInfo mediaInfo_; FileType fileType_ = FileType::UNKNOW; diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_thread.cpp b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_thread.cpp similarity index 100% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_thread.cpp rename to services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_thread.cpp diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.cpp b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.cpp similarity index 99% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.cpp rename to services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.cpp index bf9c52f098a380f48122081f36a3db436e6cd686..967ce48fc73db827129330beaab649bc79ff97c8 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.cpp +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.cpp @@ -177,24 +177,6 @@ static std::map g_formatToString = { {"aigc", Tag::MEDIA_AIGC} }; -std::vector g_supportSourceFormat = { - Tag::MEDIA_TITLE, - Tag::MEDIA_ARTIST, - Tag::MEDIA_ALBUM, - Tag::MEDIA_ALBUM_ARTIST, - Tag::MEDIA_DATE, - Tag::MEDIA_COMMENT, - Tag::MEDIA_GENRE, - Tag::MEDIA_COPYRIGHT, - Tag::MEDIA_LANGUAGE, - Tag::MEDIA_DESCRIPTION, - Tag::MEDIA_LYRICS, - Tag::MEDIA_AUTHOR, - Tag::MEDIA_COMPOSER, - Tag::MEDIA_CREATION_TIME, - Tag::MEDIA_AIGC -}; - std::vector SplitByChar(const char* str, const char* pattern) { FALSE_RETURN_V_NOLOG(str != nullptr && pattern != nullptr, {}); diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.h b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.h similarity index 90% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.h rename to services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.h index 82271bca0552eee42045682d845618a3f10636b7..4b553b520f257b04e302db639ef9e79393b8988a 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.h +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.h @@ -19,6 +19,7 @@ #include #include "meta/meta.h" #include "ffmpeg_utils.h" +#include "multi_stream_parser_manager.h" #ifdef __cplusplus extern "C" { @@ -48,19 +49,6 @@ struct ParserSdtpInfo { uint8_t *sdtpData = nullptr; }; -struct HevcParseFormat { - int32_t isHdrVivid = 0; - int32_t colorRange = 0; - uint8_t colorPrimaries = 0x02; - uint8_t colorTransfer = 0x02; - uint8_t colorMatrixCoeff = 0x02; - uint8_t profile = 0; - uint8_t level = 0; - uint32_t chromaLocation = 0; - uint32_t picWidInLumaSamples = 0; - uint32_t picHetInLumaSamples = 0; -}; - class FFmpegFormatHelper { public: static void ParseMediaInfo(const AVFormatContext& avFormatContext, Meta& format); @@ -100,7 +88,6 @@ private: static void ParseAv3aInfo(const AVStream& avStream, Meta &format); static void ConvertAv3aSampleFormat(const AVStream& avStream, Meta &format); }; -extern std::vector g_supportSourceFormat; } // namespace Ffmpeg } // namespace Plugins } // namespace Media diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_reference_parser.cpp b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_reference_parser.cpp similarity index 97% rename from services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_reference_parser.cpp rename to services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_reference_parser.cpp index 97d0a69f8f7ecd305eda3b82595c881975c5e892..9b8c787db9276a0db06088b77351102b76be962c 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_reference_parser.cpp +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_reference_parser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * Copyright (C) 2025 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 diff --git a/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.cpp b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6549900bf0f57dd6d24e2d8300b777cc3f7b8f7 --- /dev/null +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2025 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 HST_LOG_TAG "FfmpegUtils" + +#include +#include +#include +#include "common/log.h" +#include "meta/mime_type.h" +#include "ffmpeg_utils.h" + +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "HiStreamer" }; +} + +#define AV_CODEC_TIME_BASE (static_cast(1)) +#define AV_CODEC_NSECOND AV_CODEC_TIME_BASE +#define AV_CODEC_USECOND (static_cast(1000) * AV_CODEC_NSECOND) +#define AV_CODEC_MSECOND (static_cast(1000) * AV_CODEC_USECOND) +#define AV_CODEC_SECOND (static_cast(1000) * AV_CODEC_MSECOND) + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace Ffmpeg { +bool Mime2CodecId(const std::string &mime, AVCodecID &codecId) +{ + /* MIME to AVCodecID */ + static const std::unordered_map table = { + {MimeType::AUDIO_MPEG, AV_CODEC_ID_MP3}, + {MimeType::AUDIO_AAC, AV_CODEC_ID_AAC}, + {MimeType::AUDIO_AMR_NB, AV_CODEC_ID_AMR_NB}, + {MimeType::AUDIO_AMR_WB, AV_CODEC_ID_AMR_WB}, + {MimeType::AUDIO_RAW, AV_CODEC_ID_PCM_U8}, + {MimeType::AUDIO_G711MU, AV_CODEC_ID_PCM_MULAW}, + {MimeType::AUDIO_FLAC, AV_CODEC_ID_FLAC}, + {MimeType::VIDEO_MPEG4, AV_CODEC_ID_MPEG4}, + {MimeType::VIDEO_AVC, AV_CODEC_ID_H264}, + {MimeType::VIDEO_HEVC, AV_CODEC_ID_HEVC}, + {MimeType::IMAGE_JPG, AV_CODEC_ID_MJPEG}, + {MimeType::IMAGE_PNG, AV_CODEC_ID_PNG}, + {MimeType::IMAGE_BMP, AV_CODEC_ID_BMP}, + {MimeType::TIMED_METADATA, AV_CODEC_ID_FFMETADATA}, + }; + auto it = table.find(mime); + if (it != table.end()) { + codecId = it->second; + return true; + } + return false; +} + +bool Raw2CodecId(AudioSampleFormat sampleFormat, AVCodecID &codecId) +{ + static const std::unordered_map table = { + {AudioSampleFormat::SAMPLE_U8, AV_CODEC_ID_PCM_U8}, + {AudioSampleFormat::SAMPLE_S16LE, AV_CODEC_ID_PCM_S16LE}, + {AudioSampleFormat::SAMPLE_S24LE, AV_CODEC_ID_PCM_S24LE}, + {AudioSampleFormat::SAMPLE_S32LE, AV_CODEC_ID_PCM_S32LE}, + {AudioSampleFormat::SAMPLE_F32LE, AV_CODEC_ID_PCM_F32LE}, + }; + auto it = table.find(sampleFormat); + if (it != table.end()) { + codecId = it->second; + return true; + } + return false; +} + +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); +} + +int64_t ConvertTimeFromFFmpeg(int64_t pts, AVRational base) +{ + int64_t out; + if (pts == AV_NOPTS_VALUE) { + out = -1; + } else { + AVRational bq = {1, AV_CODEC_SECOND}; + out = av_rescale_q(pts, base, bq); + } + MEDIA_LOG_D("Base: [" PUBLIC_LOG_D32 "/" PUBLIC_LOG_D32 "], time convert [" + PUBLIC_LOG_D64 "]->[" PUBLIC_LOG_D64 "].", base.num, base.den, pts, out); + return out; +} + +int64_t ConvertTimeToFFmpeg(int64_t timestampNs, AVRational base) +{ + int64_t result; + if (base.num == 0) { + result = AV_NOPTS_VALUE; + } else { + AVRational bq = {1, AV_CODEC_SECOND}; + result = av_rescale_q(timestampNs, bq, base); + } + MEDIA_LOG_D("Base: [" PUBLIC_LOG_D32 "/" PUBLIC_LOG_D32 "], time convert [" + PUBLIC_LOG_D64 "]->[" PUBLIC_LOG_D64 "].", base.num, base.den, timestampNs, result); + return result; +} + +int64_t ConvertTimeToFFmpegByUs(int64_t timestampUs, AVRational base) +{ + int64_t result; + if (base.num == 0) { + result = AV_NOPTS_VALUE; + } else { + AVRational bq = {1, AV_CODEC_MSECOND}; + result = av_rescale_q(timestampUs, bq, base); + } + MEDIA_LOG_D("Base: [" PUBLIC_LOG_D32 "/" PUBLIC_LOG_D32 "], time convert [" + PUBLIC_LOG_D64 "]->[" PUBLIC_LOG_D64 "].", base.num, base.den, timestampUs, result); + return result; +} + +bool StartWith(const char* name, const char* chars) +{ + if (name == nullptr || chars == nullptr) { + return false; + } + return !strncmp(name, chars, strlen(chars)); +} + +uint32_t ConvertFlagsFromFFmpeg(const AVPacket& pkt, bool memoryNotEnough) +{ + uint32_t flags = (uint32_t)(AVBufferFlag::NONE); + if (static_cast(pkt.flags) & static_cast(AV_PKT_FLAG_KEY)) { + flags |= (uint32_t)(AVBufferFlag::SYNC_FRAME); + flags |= (uint32_t)(AVBufferFlag::CODEC_DATA); + } + if (static_cast(pkt.flags) & static_cast(AV_PKT_FLAG_DISCARD)) { + flags |= (uint32_t)(AVBufferFlag::DISCARD); + } + if (memoryNotEnough) { + flags |= (uint32_t)(AVBufferFlag::PARTIAL_FRAME); + } + return flags; +} + +int64_t CalculateTimeByFrameIndex(AVStream* avStream, int keyFrameIdx) +{ + FALSE_RETURN_V_MSG_E(avStream != nullptr, 0, "Track is nullptr."); +#if defined(LIBAVFORMAT_VERSION_INT) && defined(AV_VERSION_INT) +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 78, 0) // 58 and 78 are avformat version range + FALSE_RETURN_V_MSG_E(avformat_index_get_entry(avStream, keyFrameIdx) != nullptr, 0, "Track is nullptr."); + return avformat_index_get_entry(avStream, keyFrameIdx)->timestamp; +#elif LIBAVFORMAT_VERSION_INT == AV_VERSION_INT(58, 76, 100) // 58, 76 and 100 are avformat version range + return avStream->index_entries[keyFrameIdx].timestamp; +#elif LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(58, 64, 100) // 58, 64 and 100 are avformat version range + FALSE_RETURN_V_MSG_E(avStream->internal != nullptr, 0, "Track is nullptr."); + return avStream->internal->index_entries[keyFrameIdx].timestamp; +#else + return avStream->index_entries[keyFrameIdx].timestamp; +#endif +#else + return avStream->index_entries[keyFrameIdx].timestamp; +#endif +} + +void ReplaceDelimiter(const std::string &delimiters, char newDelimiter, std::string &str) +{ + for (char &it : str) { + if (delimiters.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; +} + +std::pair ColorPrimary2AVColorPrimaries(ColorPrimary primary) +{ + static const std::unordered_map table = { + {ColorPrimary::BT709, AVCOL_PRI_BT709}, + {ColorPrimary::UNSPECIFIED, AVCOL_PRI_UNSPECIFIED}, + {ColorPrimary::BT470_M, AVCOL_PRI_BT470M}, + {ColorPrimary::BT601_625, AVCOL_PRI_BT470BG}, + {ColorPrimary::BT601_525, AVCOL_PRI_SMPTE170M}, + {ColorPrimary::SMPTE_ST240, AVCOL_PRI_SMPTE240M}, + {ColorPrimary::GENERIC_FILM, AVCOL_PRI_FILM}, + {ColorPrimary::BT2020, AVCOL_PRI_BT2020}, + {ColorPrimary::SMPTE_ST428, AVCOL_PRI_SMPTE428}, + {ColorPrimary::P3DCI, AVCOL_PRI_SMPTE431}, + {ColorPrimary::P3D65, AVCOL_PRI_SMPTE432}, + }; + auto it = table.find(primary); + if (it != table.end()) { + return { true, it->second }; + } + return { false, AVCOL_PRI_UNSPECIFIED }; +} + +std::pair ColorTransfer2AVColorTransfer(TransferCharacteristic transfer) +{ + static const std::unordered_map table = { + {TransferCharacteristic::BT709, AVCOL_TRC_BT709}, + {TransferCharacteristic::UNSPECIFIED, AVCOL_TRC_UNSPECIFIED}, + {TransferCharacteristic::GAMMA_2_2, AVCOL_TRC_GAMMA22}, + {TransferCharacteristic::GAMMA_2_8, AVCOL_TRC_GAMMA28}, + {TransferCharacteristic::BT601, AVCOL_TRC_SMPTE170M}, + {TransferCharacteristic::SMPTE_ST240, AVCOL_TRC_SMPTE240M}, + {TransferCharacteristic::LINEAR, AVCOL_TRC_LINEAR}, + {TransferCharacteristic::LOG, AVCOL_TRC_LOG}, + {TransferCharacteristic::LOG_SQRT, AVCOL_TRC_LOG_SQRT}, + {TransferCharacteristic::IEC_61966_2_4, AVCOL_TRC_IEC61966_2_4}, + {TransferCharacteristic::BT1361, AVCOL_TRC_BT1361_ECG}, + {TransferCharacteristic::IEC_61966_2_1, AVCOL_TRC_IEC61966_2_1}, + {TransferCharacteristic::BT2020_10BIT, AVCOL_TRC_BT2020_10}, + {TransferCharacteristic::BT2020_12BIT, AVCOL_TRC_BT2020_12}, + {TransferCharacteristic::PQ, AVCOL_TRC_SMPTE2084}, + {TransferCharacteristic::SMPTE_ST428, AVCOL_TRC_SMPTE428}, + {TransferCharacteristic::HLG, AVCOL_TRC_ARIB_STD_B67}, + }; + auto it = table.find(transfer); + if (it != table.end()) { + return { true, it->second }; + } + return { false, AVCOL_TRC_UNSPECIFIED }; +} + +std::pair ColorMatrix2AVColorSpace(MatrixCoefficient matrix) +{ + static const std::unordered_map table = { + {MatrixCoefficient::IDENTITY, AVCOL_SPC_RGB}, + {MatrixCoefficient::BT709, AVCOL_SPC_BT709}, + {MatrixCoefficient::UNSPECIFIED, AVCOL_SPC_UNSPECIFIED}, + {MatrixCoefficient::FCC, AVCOL_SPC_FCC}, + {MatrixCoefficient::BT601_625, AVCOL_SPC_BT470BG}, + {MatrixCoefficient::BT601_525, AVCOL_SPC_SMPTE170M}, + {MatrixCoefficient::SMPTE_ST240, AVCOL_SPC_SMPTE240M}, + {MatrixCoefficient::YCGCO, AVCOL_SPC_YCGCO}, + {MatrixCoefficient::BT2020_NCL, AVCOL_SPC_BT2020_NCL}, + {MatrixCoefficient::BT2020_CL, AVCOL_SPC_BT2020_CL}, + {MatrixCoefficient::SMPTE_ST2085, AVCOL_SPC_SMPTE2085}, + {MatrixCoefficient::CHROMATICITY_NCL, AVCOL_SPC_CHROMA_DERIVED_NCL}, + {MatrixCoefficient::CHROMATICITY_CL, AVCOL_SPC_CHROMA_DERIVED_CL}, + {MatrixCoefficient::ICTCP, AVCOL_SPC_ICTCP}, + }; + auto it = table.find(matrix); + if (it != table.end()) { + return { true, it->second }; + } + return { false, AVCOL_SPC_UNSPECIFIED }; +} + +std::vector GenerateAACCodecConfig(int32_t profile, int32_t sampleRate, int32_t channels) +{ + const std::unordered_map profiles = { + {AAC_PROFILE_LC, 1}, + {AAC_PROFILE_ELD, 38}, + {AAC_PROFILE_ERLC, 1}, + {AAC_PROFILE_HE, 4}, + {AAC_PROFILE_HE_V2, 28}, + {AAC_PROFILE_LD, 22}, + {AAC_PROFILE_MAIN, 0}, + }; + const std::unordered_map sampleRates = { + {96000, 0}, {88200, 1}, {64000, 2}, {48000, 3}, + {44100, 4}, {32000, 5}, {24000, 6}, {22050, 7}, + {16000, 8}, {12000, 9}, {11025, 10}, {8000, 11}, + {7350, 12}, + }; + uint32_t profileVal = FF_PROFILE_AAC_LOW; + auto it1 = profiles.find(static_cast(profile)); + if (it1 != profiles.end()) { + profileVal = it1->second; + } + uint32_t sampleRateIndex = 0x10; + uint32_t baseIndex = 0xF; + auto it2 = sampleRates.find(sampleRate); + if (it2 != sampleRates.end()) { + sampleRateIndex = it2->second; + } + it2 = sampleRates.find(sampleRate / 2); // 2: HE-AAC require divide base sample rate + if (it2 != sampleRates.end()) { + baseIndex = it2->second; + } + std::vector codecConfig; + if (profile == AAC_PROFILE_HE || profile == AAC_PROFILE_HE_V2) { + // HE-AAC v2 only support stereo and only one channel exist + uint32_t realCh = (profile == AAC_PROFILE_HE_V2) ? 1 : static_cast(channels); + codecConfig = {0, 0, 0, 0, 0}; + // 5 bit AOT(0x03:left 3 bits for sample rate) + 4 bit sample rate idx(0x01: 4 - 0x03) + codecConfig[0] = ((profileVal + 1) << 0x03) | ((baseIndex & 0x0F) >> 0x01); + // 0x07: left 7bits for other, 4 bit channel cfg,0x03:left for other + codecConfig[1] = ((baseIndex & 0x01) << 0x07) | ((realCh & 0x0F) << 0x03) | ((sampleRateIndex & 0x0F) >> 1) ; + // 4 bit ext sample rate idx(0x07: left 7 bits for other) + 4 bit aot(2: LC-AAC, 0x02: left for other) + codecConfig[2] = ((sampleRateIndex & 0x01) << 0x07) | (2 << 0x02); + } else { + codecConfig = {0, 0, 0x56, 0xE5, 0}; + codecConfig[0] = ((profileVal + 1) << 0x03) | ((sampleRateIndex & 0x0F) >> 0x01); + codecConfig[1] = ((sampleRateIndex & 0x01) << 0x07) | ((channels & 0x0F) << 0x03); + } + + return codecConfig; +} + +int FindNaluSpliter(int size, const uint8_t* data) +{ + int naluPos = -1; + if (size >= 4 && data[0] == 0x00 && data[1] == 0x00) { // 4: least size + if (data[2] == 0x01) { // 2: next index + naluPos = 3; // 3: the actual start pos of nal unit + } else if (size >= 5 && data[2] == 0x00 && data[3] == 0x01) { // 5: least size, 2, 3: next indecies + naluPos = 4; // 4: the actual start pos of nal unit + } + } + return naluPos; +} + +bool CanDropAvcPkt(const AVPacket& pkt) +{ + const uint8_t *data = pkt.data; + int size = pkt.size; + int naluPos = FindNaluSpliter(size, data); + if (naluPos < 0) { + MEDIA_LOG_D("pkt->data starts with error start code!"); + return false; + } + int nalRefIdc = (data[naluPos] >> 5) & 0x03; // 5: get H.264 nal_ref_idc + int nalUnitType = data[naluPos] & 0x1f; // get H.264 nal_unit_type + bool isCodedSliceData = nalUnitType == 1 || nalUnitType == 2 || // 1: non-IDR, 2: partiton A + nalUnitType == 3 || nalUnitType == 4 || nalUnitType == 5; // 3: partiton B, 4: partiton C, 5: IDR + return nalRefIdc == 0 && isCodedSliceData; +} + +bool CanDropHevcPkt(const AVPacket& pkt) +{ + const uint8_t *data = pkt.data; + int size = pkt.size; + int naluPos = FindNaluSpliter(size, data); + if (naluPos < 0) { + MEDIA_LOG_D("pkt->data starts with error start code!"); + return false; + } + int nalUnitType = (data[naluPos] >> 1) & 0x3f; // get H.265 nal_unit_type + return nalUnitType == 0 || nalUnitType == 2 || nalUnitType == 4 || // 0: TRAIL_N, 2: TSA_N, 4: STSA_N + nalUnitType == 6 || nalUnitType == 8; // 6: RADL_N, 8: RASL_N +} + +void SetDropTag(const AVPacket& pkt, std::shared_ptr sample, AVCodecID codecId) +{ + sample->meta_->Remove(Media::Tag::VIDEO_BUFFER_CAN_DROP); + bool canDrop = false; + if (codecId == AV_CODEC_ID_HEVC) { + canDrop = CanDropHevcPkt(pkt); + } else if (codecId == AV_CODEC_ID_H264) { + canDrop = CanDropAvcPkt(pkt); + } + if (canDrop) { + sample->meta_->SetData(Media::Tag::VIDEO_BUFFER_CAN_DROP, true); + } +} + +int64_t AvTime2Us(int64_t hTime) +{ + return hTime / AV_CODEC_USECOND; +} +} // namespace Ffmpeg +} // namespace Plugins +} // namespace Media +} // namespace OHOS diff --git a/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.h b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..5adf6ec90d11e1d53a25315fdf3fbda86880f65f --- /dev/null +++ b/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 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 FFMPEG_UTILS_H +#define FFMPEG_UTILS_H + +#include +#include "meta/media_types.h" +#include "meta/audio_types.h" +#include "meta/video_types.h" +#include "buffer/avbuffer.h" +#ifdef __cplusplus +extern "C" { +#endif +#include "libavutil/rational.h" +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" +#ifdef __cplusplus +}; +#endif + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace Ffmpeg { +const uint32_t MS_TO_SEC = 1000; +bool Mime2CodecId(const std::string &mime, AVCodecID &codecId); +bool Raw2CodecId(AudioSampleFormat sampleFormat, AVCodecID &codecId); +std::string AVStrError(int errnum); +int64_t ConvertTimeFromFFmpeg(int64_t pts, AVRational base); +int64_t ConvertTimeToFFmpeg(int64_t timestampNs, AVRational base); +int64_t ConvertTimeToFFmpegByUs(int64_t timestampUs, AVRational base); +bool StartWith(const char* name, const char* chars); +uint32_t ConvertFlagsFromFFmpeg(const AVPacket& pkt, bool memoryNotEnough); +int64_t CalculateTimeByFrameIndex(AVStream* avStream, int keyFrameIdx); +void ReplaceDelimiter(const std::string &delimiters, char newDelimiter, std::string &str); + +std::vector SplitString(const char* str, char delimiter); +std::vector SplitString(const std::string& str, char delimiter); + +std::pair ColorPrimary2AVColorPrimaries(ColorPrimary primary); +std::pair ColorTransfer2AVColorTransfer(TransferCharacteristic transfer); +std::pair ColorMatrix2AVColorSpace(MatrixCoefficient matrix); + +std::vector GenerateAACCodecConfig(int32_t profile, int32_t sampleRate, int32_t channels); +void SetDropTag(const AVPacket& pkt, std::shared_ptr sample, AVCodecID codecId); +int64_t AvTime2Us(int64_t hTime); +} // namespace Ffmpeg +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif // FFMPEG_UTILS_H diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_audio_parser.cpp b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_audio_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f498fa7f0ced83ac62b7642ecb8024128503832a --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_audio_parser.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2025 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 MEDIA_PLUGIN +#define HST_LOG_TAG "MP4AudioParser" +#include "common/log.h" +#include "mp4_audio_parser.h" +#include "mp4_utils.h" + +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "MP4AudioParser" }; +} + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +constexpr uint32_t DTS_SYNC_WORD = 0x7FFE8001; +constexpr uint8_t MAX_DTS_AUDIO_MODE = 10; + +const std::vector> g_channelLayoutDefaultMap = { + {2, AudioChannelLayout::STEREO}, // 2: STEREO + {4, AudioChannelLayout::CH_4POINT0}, // 4: CH_4POINT0 + {6, AudioChannelLayout::CH_5POINT1}, // 6: CH_5POINT1 + {8, AudioChannelLayout::CH_5POINT1POINT2}, // 8: CH_5POINT1POINT2 + {9, AudioChannelLayout::HOA_ORDER2_ACN_N3D}, // 9: HOA_ORDER2_ACN_N3D + {10, AudioChannelLayout::CH_7POINT1POINT2}, // 10: CH_7POINT1POINT2 or CH_5POINT1POINT4 ? + {12, AudioChannelLayout::CH_7POINT1POINT4}, // 12: CH_7POINT1POINT4 + {14, AudioChannelLayout::CH_9POINT1POINT4}, // 14: CH_9POINT1POINT4 + {16, AudioChannelLayout::CH_9POINT1POINT6}, // 16: CH_9POINT1POINT6 + {24, AudioChannelLayout::CH_22POINT2}, // 24: CH_22POINT2 +}; + +const std::vector g_supportChannelLayout = { + AudioChannelLayout::UNKNOWN, + AudioChannelLayout::MONO, + AudioChannelLayout::STEREO, + AudioChannelLayout::CH_2POINT1, + AudioChannelLayout::CH_2_1, + AudioChannelLayout::SURROUND, + AudioChannelLayout::CH_3POINT1, + AudioChannelLayout::CH_4POINT0, + AudioChannelLayout::CH_4POINT1, + AudioChannelLayout::CH_2_2, + AudioChannelLayout::QUAD, + AudioChannelLayout::CH_5POINT0, + AudioChannelLayout::CH_5POINT1, + AudioChannelLayout::CH_5POINT0_BACK, + AudioChannelLayout::CH_5POINT1_BACK, + AudioChannelLayout::CH_6POINT0, + AudioChannelLayout::CH_6POINT0_FRONT, + AudioChannelLayout::HEXAGONAL, + AudioChannelLayout::CH_6POINT1, + AudioChannelLayout::CH_6POINT1_BACK, + AudioChannelLayout::CH_6POINT1_FRONT, + AudioChannelLayout::CH_7POINT0, + AudioChannelLayout::CH_7POINT0_FRONT, + AudioChannelLayout::CH_7POINT1, + AudioChannelLayout::CH_7POINT1_WIDE, + AudioChannelLayout::CH_7POINT1_WIDE_BACK, + AudioChannelLayout::CH_3POINT1POINT2, + AudioChannelLayout::CH_5POINT1POINT2, + AudioChannelLayout::CH_5POINT1POINT4, + AudioChannelLayout::CH_7POINT1POINT2, + AudioChannelLayout::CH_7POINT1POINT4, + AudioChannelLayout::CH_9POINT1POINT4, + AudioChannelLayout::CH_9POINT1POINT6, + AudioChannelLayout::CH_10POINT2, + AudioChannelLayout::CH_22POINT2, + AudioChannelLayout::OCTAGONAL, + AudioChannelLayout::HEXADECAGONAL, + AudioChannelLayout::STEREO_DOWNMIX, + AudioChannelLayout::CH_2POINT0POINT2, + AudioChannelLayout::CH_2POINT1POINT2, + AudioChannelLayout::CH_3POINT0POINT2 +}; + +const std::vector g_dtsAudioMode2DtsChannelLayout = { + AudioChannelLayout::MONO, + AudioChannelLayout::STEREO, + AudioChannelLayout::STEREO, + AudioChannelLayout::STEREO, + AudioChannelLayout::STEREO, + AudioChannelLayout::SURROUND, + AudioChannelLayout::CH_2_1, + AudioChannelLayout::CH_4POINT0, + AudioChannelLayout::CH_2_2, + AudioChannelLayout::CH_5POINT0, +}; + +MP4AudioParser::MP4AudioParser() +{ + MEDIA_LOG_D("In"); +} + +MP4AudioParser::~MP4AudioParser() +{ + MEDIA_LOG_D("In"); +} + +AudioChannelLayout MP4AudioParser::FindValidChannelLayout(uint64_t layoutMask) +{ + auto it = std::find(g_supportChannelLayout.begin(), g_supportChannelLayout.end(), + static_cast(layoutMask)); + if (it != g_supportChannelLayout.end()) { + return *it; + } + return AudioChannelLayout::UNKNOWN; +} + +AudioChannelLayout MP4AudioParser::GetDefaultChannelLayout(int32_t channels) +{ + AudioChannelLayout layout = AudioChannelLayout::MONO; + auto ite = std::find_if(g_channelLayoutDefaultMap.begin(), g_channelLayoutDefaultMap.end(), + [&channels](const auto &item) -> bool { return item.first == channels; }); + if (ite != g_channelLayoutDefaultMap.end()) { + layout = ite->second; + } + return layout; +} + +Status MP4AudioParser::ParseAudioFrame(uint8_t* data, uint32_t size, std::string mime, uint32_t trackIndex, + MediaInfo& mediaInfo) +{ + MEDIA_LOG_D("In"); + audioData_ = data; + dataSize_ = size; + index_ = trackIndex; + auto ret = Status::OK; + if (mime == MimeType::AUDIO_MPEG) { + ret = ParseMpegAudio(mediaInfo); + } else if (mime == "audio/dts") { + ret = ParseDtsAudio(mediaInfo); + } + + return ret; +} + +Status MP4AudioParser::ParseMpegAudio(MediaInfo& mediaInfo) +{ + MEDIA_LOG_D("In"); + const int32_t layerTwoSamples = 1152; + const int32_t layerThreeLsfSamples = 576; + if (dataSize_ < 0x02) { + return Status::ERROR_INVALID_DATA; + } + uint32_t layer = 4 - ((audioData_[1] & 0x06) >> 1); + bool lsf = ((audioData_[2] >> 4) & 0x03) == 2; + int32_t samplesPerFrame = 0; + AudioSampleFormat sampleFormat = AudioSampleFormat::INVALID_WIDTH; + switch (layer) { + case 0x01: + return Status::ERROR_INVALID_DATA; + case 0x02: + samplesPerFrame = layerTwoSamples; + sampleFormat = AudioSampleFormat::SAMPLE_S16P; + break; + default: + samplesPerFrame = lsf ? layerThreeLsfSamples : layerTwoSamples; + sampleFormat = AudioSampleFormat::SAMPLE_F32P; + break; + } + mediaInfo.tracks[index_].SetData(Tag::AUDIO_SAMPLE_PER_FRAME, samplesPerFrame); + mediaInfo.tracks[index_].SetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); + return Status::OK; +} + +Status MP4AudioParser::ParseDtsAudio(MediaInfo& mediaInfo) +{ + MEDIA_LOG_D("In"); + uint32_t currentOffset = 0; + if (dataSize_ < 0x04 || GetU32Value(&audioData_[currentOffset]) != DTS_SYNC_WORD) { + return Status::ERROR_INVALID_DATA; + } + currentOffset += 0x04; + uint8_t mode = ((audioData_[currentOffset + 3] & 0xF) << 4) + + (audioData_[currentOffset + 4] >> 6); + if (mode >= MAX_DTS_AUDIO_MODE) { + return Status::ERROR_INVALID_DATA; + } + AudioChannelLayout layout = g_dtsAudioMode2DtsChannelLayout[mode]; + mediaInfo.tracks[index_].SetData(Tag::AUDIO_CHANNEL_LAYOUT, layout); + return Status::OK; +} +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_audio_parser.h b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_audio_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..cba914c1c39c17f6cbb9c9c097fb457d3dcdd327 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_audio_parser.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 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 MP4_AUDIO_PARSER_H +#define MP4_AUDIO_PARSER_H +#include "plugin/plugin_definition.h" +#include "plugin/plugin_info.h" + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +class MP4AudioParser { +public: + explicit MP4AudioParser(); + ~MP4AudioParser(); + AudioChannelLayout FindValidChannelLayout(uint64_t layoutMask); + AudioChannelLayout GetDefaultChannelLayout(int32_t channels); + Status ParseAudioFrame(uint8_t* data, uint32_t size, std::string mime, uint32_t trackIndex, MediaInfo& mediaInfo); + +private: + uint8_t* audioData_ = nullptr; + uint32_t dataSize_ = 0; + uint32_t index_ = 0; + Status ParseMpegAudio(MediaInfo& mediaInfo); + Status ParseDtsAudio(MediaInfo& mediaInfo); +}; +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif // MP4_AUDIO_PARSER_H \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_box_parser.cpp b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_box_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e18c8596fa096ae1c81f422ac521e2d74bbaba4e --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_box_parser.cpp @@ -0,0 +1,4276 @@ +/* + * Copyright (C) 2025 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 MEDIA_PLUGIN +#define HST_LOG_TAG "MP4BoxParser" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "avcodec_trace.h" +#include "securec.h" +#include "mp4_box_parser.h" +#include "mp4_utils.h" +#include "plugin/plugin_buffer.h" +#include "plugin/plugin_definition.h" +#include "meta/video_types.h" +#include "syspara/parameters.h" + +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "MP4BoxParser" }; +} + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +constexpr uint32_t ALAC_SPECIFIC_SIZE = 36; +constexpr int32_t BYTE_TO_BITS = 8; +constexpr int32_t CREATION_TIME_OFFSET = 4; +constexpr int32_t CONVERT_SCALE = 1 << 16; +constexpr uint16_t DISPLAY_MATRIX_ROWS = 3; +constexpr uint16_t DISPLAY_MATRIX_COLS = 3; +constexpr uint32_t DISPLAY_MATRIX_SIZE = 36; +constexpr int64_t DYNAMIC_SIZE_V0 = 24; +constexpr int64_t DYNAMIC_SIZE_V1 = 36; +constexpr int64_t DYNAMIC_MATRIX_OFFSET_V0 = 36; +constexpr int64_t DYNAMIC_MATRIX_OFFSET_V1 = 48; +constexpr int64_t DEFAULT_HEAD_SIZE = 8; +constexpr int64_t ESDS_READ_SIZE = 13; +constexpr int64_t HDR_VIVID_TAG_LENGTH = 31; +constexpr int64_t HDR_VIVID_TAG_OFFSET = 43; +constexpr int32_t MATRIX_SIZE = 4; +constexpr int32_t MAX_DEPTH = 10; +constexpr uint64_t MAX_TITLE_SIZE = 24; +constexpr uint64_t MAX_EXTRA_DATA_SIZE = 1024 * 1024; +constexpr uint64_t MAX_DATA_SIZE = 256; +constexpr uint64_t MAX_METADATA_SIZE = 16 * 1024; +constexpr uint64_t MIN_COLOR_SIZE = 4; +constexpr uint64_t MIN_COLOR_PARAM_SIZE = 6; +constexpr uint64_t MIN_MVHD_SIZE = 32; +constexpr uint64_t MIN_TKHD_SIZE = 84; +constexpr uint64_t MIN_HDLR_DATA_SIZE = 24; +constexpr uint64_t MIN_BOX_SIZE = 16; +constexpr uint64_t MIN_TFHD_SIZE = 8; +constexpr uint64_t MIN_VIDEO_SAMPLE_ENTRY_SIZE = 78; +constexpr uint64_t MIN_AUDIO_SAMPLE_ENTRY_SIZE = 28; +constexpr uint64_t MIN_ACLR_SIZE = 13; +constexpr uint64_t MIN_PCMC_SIZE = 6; +constexpr uint64_t MIN_SIDX_SIZE = 12; +constexpr uint64_t MIN_TIMED_METADATA_SAMPLE_ENTRY_SIZE = 16; +constexpr uint64_t MIN_TRADITIONAL_METADATA_SIZE = 4; +constexpr uint64_t MIN_LOCI_SIZE = 20; +constexpr int32_t MP4_ROOT_DEPTH = 0; // Root level atom, ftyp, moov, mdat etc. +constexpr int32_t MP4_DEPTH_ONE = 1; // moov's children atom, mvhd, trak, mvex etc. +constexpr int32_t MP4_DEPTH_TWO = 2; // trak's direct children atom, tkhd, mdia, edts etc. +constexpr int32_t MP4_DEPTH_THREE = 3; // mdia's direct children atom, mdhd, hdlr, minf etc. +constexpr int32_t MP4_DEPTH_FOUR = 4; // minf's direct children atom, vmhd, smhd, stbl etc. +constexpr uint16_t G711MULAW_SAMPLE_SIZE = 8; +constexpr uint8_t ID3V1_GENRE_MAX = 191; +constexpr uint64_t QUICKTIME_EPOCH_OFFSET = 2082844800; +constexpr size_t OFFSET_V0 = 12; +constexpr size_t OFFSET_V1 = 20; +constexpr size_t OPUS_MIN_SIZE = 11; +constexpr size_t MAX_SIZE = 1 << 30; +constexpr size_t DURATION_OFFSET_V0 = 16; +constexpr size_t DURATION_OFFSET_V1 = 24; +constexpr size_t DIRECT_META_PATH_SIZE = 2; // moov/meta 直接结构的路径大小 +constexpr uint8_t AOT_SBR = 5; +constexpr uint8_t AOT_PS = 29; +constexpr uint8_t AAC_MAIN = 1; +constexpr uint8_t AAC_LC = 2; +constexpr uint8_t AAC_SSR = 3; +constexpr uint8_t AAC_LTP = 4; +constexpr uint8_t AAC_ER_LC = 17; +constexpr uint8_t AAC_ER_LD = 23; +constexpr uint8_t AAC_ER_ELD = 39; +// ESDS 描述符标签常量 +constexpr uint8_t MP4_ES_DESCR_TAG = 0x03; +constexpr uint8_t MP4_DEC_CONFIG_DESCR_TAG = 0x04; +constexpr uint8_t MP4_DEC_SPECIFIC_DESCR_TAG = 0x05; + +constexpr std::array MATRIX_2X2_INDICES = {0, 1, 3, 4}; // [0][0], [0][1], [1][0], [1][1] +constexpr std::array MATRIX_DIAGONAL_INDICES = {0, 4, 8}; // [0][0], [1][1], [2][2] +constexpr std::array MATRIX_OFF_DIAGONAL_INDICES = {1, 2, 3, 5, 6, 7}; // 非对角线元素 + +struct PathAdder { + PathAdder(std::vector *path, uint32_t atomType): path_(path) + { + path_->emplace_back(atomType); + } + ~PathAdder() + { + path_->pop_back(); + } +private: + std::vector* path_; + + PathAdder(const PathAdder&); + PathAdder& operator=(const PathAdder &); +}; + +static std::string GetMimeType(int32_t atomtype) +{ + static std::map typeToMime = { + { FourccType("mp4v"), MimeType::VIDEO_MPEG4 }, + { FourccType("s263"), MimeType::VIDEO_H263 }, + { FourccType("H263"), MimeType::VIDEO_H263 }, + { FourccType("h263"), MimeType::VIDEO_H263 }, + { FourccType("avc1"), MimeType::VIDEO_AVC }, + { FourccType("acv3"), MimeType::VIDEO_AVC }, + { FourccType("hvc1"), MimeType::VIDEO_HEVC }, + { FourccType("hev1"), MimeType::VIDEO_HEVC }, + { FourccType("vvc1"), MimeType::VIDEO_VVC }, + { FourccType("vvi1"), MimeType::VIDEO_VVC }, + { FourccType("fLaC"), MimeType::AUDIO_FLAC }, + { FourccType(".mp2"), MimeType::AUDIO_MPEG }, + { FourccType(".mp3"), MimeType::AUDIO_MPEG }, + { 0x6D730055, MimeType::AUDIO_MPEG }, + { FourccType("mp4a"), MimeType::AUDIO_AAC }, + { FourccType("av3a"), MimeType::AUDIO_AVS3DA }, + { FourccType("alaw"), MimeType::AUDIO_G711A }, + { FourccType("ulaw"), MimeType::AUDIO_G711MU }, + { FourccType("fl32"), MimeType::AUDIO_RAW }, + { FourccType("fl64"), MimeType::AUDIO_RAW }, + { FourccType("in24"), MimeType::AUDIO_RAW }, + { FourccType("in32"), MimeType::AUDIO_RAW }, + { FourccType("raw "), MimeType::AUDIO_RAW }, + { FourccType("NONE"), MimeType::AUDIO_RAW }, + { FourccType("ipcm"), MimeType::AUDIO_RAW }, + { FourccType("lpcm"), MimeType::AUDIO_RAW }, + { FourccType("twos"), MimeType::AUDIO_RAW }, + { FourccType("sowt"), MimeType::AUDIO_RAW }, + { FourccType("msVo"), MimeType::AUDIO_VORBIS }, + { FourccType("Opus"), MimeType::AUDIO_OPUS }, + { FourccType("samr"), MimeType::AUDIO_AMR_NB }, + { FourccType("sawb"), MimeType::AUDIO_AMR_WB }, + { FourccType("alac"), "audio/alac"}, + { FourccType("dtsc"), "audio/dts" }, + { FourccType("dtsh"), "audio/dts" }, + { FourccType("dtsl"), "audio/dts" }, + { FourccType("dtse"), "audio/dts" }, + { FourccType("wvtt"), MimeType::TEXT_WEBVTT }, + { FourccType("mebx"), MimeType::TIMED_METADATA }, + }; + + auto it = typeToMime.find(atomtype); + if (it != typeToMime.end()) { + return it->second; + } + return MimeType::INVALID_TYPE; +} + +static const std::map matrixTypes = { + /** + * display matrix + * | a b u | + * (a, b, u, c, d, v, x, y, w) -> | c d v | + * | x y w | + * [a b c d] can confirm the orientation type + */ + {"0 -1 1 0", VideoOrientationType::ROTATE_90}, + {"-1 0 0 -1", VideoOrientationType::ROTATE_180}, + {"0 1 -1 0", VideoOrientationType::ROTATE_270}, + {"-1 0 0 1", VideoOrientationType::FLIP_H}, + {"1 0 0 -1", VideoOrientationType::FLIP_V}, + {"0 1 1 0", VideoOrientationType::FLIP_H_ROT90}, + {"0 -1 -1 0", VideoOrientationType::FLIP_V_ROT90}, +}; + +static const std::map FLAC_CHANNEL_LAYOUT_TABLE = { + {1, AudioChannelLayout::MONO}, + {2, AudioChannelLayout::STEREO}, + {3, AudioChannelLayout::SURROUND}, + {4, AudioChannelLayout::QUAD}, + {5, AudioChannelLayout::CH_5POINT0}, + {6, AudioChannelLayout::CH_5POINT1}, + {7, AudioChannelLayout::CH_6POINT1}, + {8, AudioChannelLayout::CH_7POINT1} +}; + +static const std::map ALAC_CHANNEL_LAYOUT_TABLE = { + {1, AudioChannelLayout::MONO}, + {2, AudioChannelLayout::STEREO}, + {3, AudioChannelLayout::SURROUND}, + {4, AudioChannelLayout::CH_4POINT0}, + {5, AudioChannelLayout::CH_5POINT0_BACK}, + {6, AudioChannelLayout::CH_5POINT1_BACK}, + {7, AudioChannelLayout::CH_6POINT1_BACK}, + {8, AudioChannelLayout::CH_7POINT1_WIDE_BACK}, + {9, AudioChannelLayout::UNKNOWN} +}; + +static const std::map PCM_CHANNEL_LAYOUT_TABLE = { + {(100 << 16) | 1, AudioChannelLayout::MONO}, + {(101 << 16) | 2, AudioChannelLayout::STEREO}, + {(113 << 16) | 3, AudioChannelLayout::SURROUND}, + {(108 << 16) | 4, AudioChannelLayout::QUAD}, + {(117 << 16) | 5, AudioChannelLayout::CH_5POINT0}, + {(121 << 16) | 6, AudioChannelLayout::CH_5POINT1}, + {(125 << 16) | 7, AudioChannelLayout::CH_6POINT1}, + {(126 << 16) | 8, AudioChannelLayout::CH_7POINT1_WIDE}, + {(128 << 16) | 8, AudioChannelLayout::CH_7POINT1}, +}; + +static const AudioChannelLayout AAC_CHANNEL_LAYOUT_TABLE[14] = { + AudioChannelLayout::UNKNOWN, + AudioChannelLayout::MONO, + AudioChannelLayout::STEREO, + AudioChannelLayout::SURROUND, + AudioChannelLayout::CH_4POINT0, + AudioChannelLayout::CH_5POINT0_BACK, + AudioChannelLayout::CH_5POINT1_BACK, + AudioChannelLayout::CH_7POINT1, + AudioChannelLayout::UNKNOWN, + AudioChannelLayout::UNKNOWN, + AudioChannelLayout::UNKNOWN, + AudioChannelLayout::CH_6POINT1_BACK, + AudioChannelLayout::CH_7POINT1, + AudioChannelLayout::CH_22POINT2 +}; + +static const uint32_t AAC_SAMPLE_RATE_TABLE[] = { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000, 7350 +}; + +const std::vector MP4AtomParser::standardRates_ = { + {23.976, "23.976"}, {24.0, "24"}, {25.0, "25"}, {29.97, "29.97"}, + {30.0, "30"}, {48.0, "48"}, {50.0, "50"}, {59.94, "59.94"}, + {60.0, "60"}, {72.0, "72"}, {90.0, "90"}, {100.0, "100"}, + {120.0, "120"}, {144.0, "144"}, {240.0, "240"}, {12.0, "12"}, + {15.0, "15"}, {20.0, "20"}, {75.0, "75"}, +}; + +const std::vector MP4AtomParser::ranges_ = { + {23.5, 24.5, 24.0, "24fps"}, + {24.5, 25.5, 25.0, "25fps"}, + {27.0, 32.0, 30.0, "30fps"}, + {29.5, 30.5, 30.0, "30fps"}, + {47.0, 51.0, 50.0, "50fps"}, + {59.0, 61.0, 60.0, "60fps"}, + {119.0, 121.0, 120.0, "120fps"} +}; + +enum class FieldOrder { + FIELD_ORDER_UNKNOWN, + FIELD_ORDER_PROGRESSIVE, + FIELD_ORDER_TT, // top coded first, top displayed first + FIELD_ORDER_BB, // bottom coded first, bottom displayed first + FIELD_ORDER_TB, // top coded first, bottom displayed first + FIELD_ORDER_BT, // bottom coded first, top displayed first +}; + +enum AACDataBlockType : uint8_t { + TYPE_SCE, + TYPE_CPE, + TYPE_CCE, + TYPE_LFE, + TYPE_DSE, + TYPE_PCE, + TYPE_FIL, + TYPE_END +}; + +MP4AtomParser::MP4AtomParser() + : firstTrack_(nullptr), + lastTrack_(nullptr), + isIsom_(false), + hasMdatBox_(false), + hasSidxBox_(false), + isDistributedSidx_(false), + hasMoofBox_(false), + moovFound_(false), + seekable_(Seekable::SEEKABLE), + userFormat_(std::make_shared()) +{ + MEDIA_LOG_D("In"); + InitParseTable(); + MEDIA_LOG_D("Out"); +} + +MP4AtomParser::~MP4AtomParser() +{ + MEDIA_LOG_D("In"); + firstTrack_.reset(); + lastTrack_.reset(); + dataSource_.reset(); + dataReader_.reset(); + MP4ParseTable_.clear(); + MEDIA_LOG_D("Out"); +} + +inline bool MP4AtomParser::IsValidTrackIndex(const MediaInfo& mediaInfo, uint32_t index) +{ + return index < mediaInfo.tracks.size(); +} + +inline int32_t ConvFp(int32_t x) +{ + return static_cast(x / (CONVERT_SCALE)); +} + +inline double ConvDp(int32_t x) +{ + return static_cast(x) / (CONVERT_SCALE); +} + +std::array Extract2x2Transform(const int32_t* displayMatrix) +{ + std::array result; + for (size_t i = 0; i < MATRIX_2X2_INDICES.size(); ++i) { + result[i] = ConvFp(displayMatrix[MATRIX_2X2_INDICES[i]]); + } + return result; +} + +bool IsIdentityMatrix(const int32_t* matrix) +{ + // 检查对角线元素 + if (matrix[MATRIX_DIAGONAL_INDICES[0]] != CONVERT_SCALE || // [0][0] + matrix[MATRIX_DIAGONAL_INDICES[1]] != CONVERT_SCALE || // [1][1] + matrix[MATRIX_DIAGONAL_INDICES[2]] != (1 << 0x1E)) { // [2][2] + return false; + } + + // 检查非对角线元素是否都为0 + for (size_t idx : MATRIX_OFF_DIAGONAL_INDICES) { + if (matrix[idx] != 0) { + return false; + } + } + return true; +} + +bool IsDisplayMatrixIdentity(const int32_t matrix[3][3]) +{ + return IsIdentityMatrix(reinterpret_cast(matrix)); +} + +void MultiplyDisplayMatrix(const int32_t a[3][3], const int32_t b[3][3], int32_t result[3][3]) +{ + FALSE_RETURN_MSG(a != nullptr && b != nullptr && result != nullptr, "Invalid params"); + // MP4 display matrix的位移量,用于定点数运算 + // [0][0], [1][1] 元素使用16位小数 + // [2][2] 元素使用30位小数 + const int32_t shift[3] = { 16, 16, 30 }; + const int32_t elementCount = 9; // 3x3矩阵有9个元素 + // 初始化结果矩阵 + std::fill(&result[0][0], &result[0][0] + elementCount, 0); + // 执行矩阵乘法:result = a * b + for (uint16_t i = 0; i < DISPLAY_MATRIX_ROWS; ++i) { + for (uint16_t j = 0; j < DISPLAY_MATRIX_COLS; ++j) { + for (uint16_t k = 0; k < DISPLAY_MATRIX_COLS; ++k) { + // 使用64位避免中间计算溢出,然后右移恢复定点数精度 + result[i][j] += static_cast( + (static_cast(a[i][k]) * b[k][j]) >> shift[k]); + } + } + } +} + +void PrintMatrixToLog(const int32_t* matrix, const std::string& matrixName) +{ + MEDIA_LOG_D(PUBLIC_LOG_S ": [" PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 " " + PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 " " PUBLIC_LOG_D32 "]", + matrixName.c_str(), matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], + matrix[5], matrix[6], matrix[7], matrix[8]); +} + +static int64_t ReadChannelSet(uint32_t tag) +{ + const uint32_t chanTopBackRightTag = 18; + const uint32_t chanWideLeftTag = 35; + const uint32_t chanWideRightTag = 36; + const uint32_t chanLowFrequency2 = 37; + const uint32_t chanStereoLeft = 38; + const uint32_t chanStereoRight = 39; + + if (tag > 0 && tag <= chanTopBackRightTag) { + return 1U << (tag - 1); + } + + switch (tag) { + case chanWideLeftTag: + return static_cast(AudioChannelSet::WIDE_LEFT); + case chanWideRightTag: + return static_cast(AudioChannelSet::WIDE_RIGHT); + case chanLowFrequency2: + return static_cast(AudioChannelSet::LOW_FREQUENCY_2); + case chanStereoLeft: + return static_cast(AudioChannelSet::STEREO_LEFT); + case chanStereoRight: + return static_cast(AudioChannelSet::STEREO_RIGHT); + default: + return 0; + } +} + +int64_t MP4AtomParser::GetChannelLayoutByBitmap(uint32_t tag, uint32_t bitmap) +{ + if (tag == 0) { + return 0; + } else if (tag == (1 << 0x10)) { + return bitmap < 0x040000 ? bitmap : 0; + } + + constexpr uint32_t maxChannels = 9; + uint32_t channels = tag & 0xFFFF; + if (channels > maxChannels) { + return 0; + } + std::map layoutMap = {}; + std::string mime = lastTrack_->sampleHelper->mimeType_.c_str(); + if (mime == MimeType::AUDIO_RAW) { + layoutMap = PCM_CHANNEL_LAYOUT_TABLE; + } + + auto it = layoutMap.find(tag); + if (it != layoutMap.end()) { + return static_cast(it->second); + } else { + return 0; + } +} + +Status MP4AtomParser::ParseDisplayMatrix(const uint8_t* data, int32_t matrix[3][3]) +{ + FALSE_RETURN_V_MSG_E(data != nullptr && matrix != nullptr, Status::ERROR_INVALID_PARAMETER, "Invalid params"); + // MP4 display matrix按行存储:[a b u c d v x y w] + // 矩阵格式:[a b u] + // [c d v] + // [x y w] + for (uint16_t i = 0; i < DISPLAY_MATRIX_ROWS; ++i) { + for (uint16_t j = 0; j < DISPLAY_MATRIX_COLS; ++j) { + matrix[i][j] = static_cast(GetU32Value(&data[(i * DISPLAY_MATRIX_ROWS + j) * sizeof(int32_t)])); + } + } + PrintMatrixToLog(&matrix[0][0], "displayMatrix"); + return Status::OK; +} + +std::string ConvertArrayToString(const int32_t* array, size_t size) +{ + std::string result; + for (size_t i = 0; i < size; ++i) { + if (i > 0) { + result += ' '; + } + result += std::to_string(array[i]); + } + return result; +} + +VideoOrientationType GetMatrixType(const std::string& value) +{ + auto it = matrixTypes.find(value); + return (it != matrixTypes.end()) ? it->second : VideoOrientationType::ROTATE_NONE; +} + +void MP4AtomParser::ParseOrientationFromMatrix(std::shared_ptr track) +{ + VideoOrientationType orientationType = VideoOrientationType::ROTATE_NONE; + if (track && track->hasDisplayMatrix && track->displayMatrix) { + PrintMatrixToLog(track->displayMatrix.get(), "displayMatrix"); + // 转换矩阵:提取前2x2部分并转换为整数 + auto convertedMatrix = Extract2x2Transform(track->displayMatrix.get()); + // 转换为字符串并查找方向类型 + std::string matrixStr = ConvertArrayToString(convertedMatrix.data(), convertedMatrix.size()); + orientationType = GetMatrixType(matrixStr); + } else { + MEDIA_LOG_W("Parse orientation info from display matrix failed, set orientation as default 0"); + } + + if (!IsValidTrackIndex(mediaInfo_, track->trackIndex)) { + MEDIA_LOG_E("Invalid track index: " PUBLIC_LOG_U32, track->trackIndex); + return; + } + mediaInfo_.tracks[track->trackIndex].SetData(Tag::VIDEO_ORIENTATION_TYPE, orientationType); +} + +double CalculateDisplayRotation(const int32_t matrix[9]) +{ + FALSE_RETURN_V_MSG_E(matrix != nullptr, 0.0, "Matrix is null"); + constexpr double pi = 3.14159265358979323846; + constexpr double halfCircle = 180.0; + double scale[2]; + scale[0] = std::hypot(ConvDp(matrix[0]), ConvDp(matrix[0x03])); + scale[1] = std::hypot(ConvDp(matrix[1]), ConvDp(matrix[0x04])); + + if (scale[0] == 0.0 || scale[1] == 0.0) { + MEDIA_LOG_W("Invalid matrix: zero scale factor"); + return std::numeric_limits::quiet_NaN(); + } + + double rotationRad = std::atan2(ConvDp(matrix[1]) / scale[1], ConvDp(matrix[0]) / scale[0]); + double rotation = -rotationRad * halfCircle / pi; + return rotation; +} + +void MP4AtomParser::ParseRotationTypeFromMatrix(std::shared_ptr track) +{ + VideoRotation rotationType = VIDEO_ROTATION_0; + constexpr int32_t VIDEO_ROTATION_360 = 360; + if (track && track->hasDisplayMatrix && track->displayMatrix) { + float rotation = -round(CalculateDisplayRotation(track->displayMatrix.get())); + if (isnan(rotation)) { + rotationType = VIDEO_ROTATION_0; + } else if (rotation < 0) { + rotation += VIDEO_ROTATION_360; + } + switch (static_cast(rotation)) { + case VIDEO_ROTATION_90: + rotationType = VIDEO_ROTATION_90; + break; + case VIDEO_ROTATION_180: + rotationType = VIDEO_ROTATION_180; + break; + case VIDEO_ROTATION_270: + rotationType = VIDEO_ROTATION_270; + break; + default: + rotationType = VIDEO_ROTATION_0; + break; + } + } else { + MEDIA_LOG_W("Parse rotate info from display matrix failed, set rotation as default 0"); + rotationType = VIDEO_ROTATION_0; + } + mediaInfo_.tracks[track->trackIndex].SetData(Tag::VIDEO_ROTATION, rotationType); +} + +Status MP4AtomParser::ParseContainerAtom(const MP4Atom& containerAtom, int32_t depth, ParseContext* ctx) +{ + int64_t endOffset = 0; + if (__builtin_add_overflow(ctx->dataOffset, containerAtom.dataSize, &endOffset)) { + MEDIA_LOG_E("Container atom size overflow"); + return Status::ERROR_INVALID_DATA; + } + // 设置解析起始位置为容器的数据开始位置 + ctx->offset = ctx->dataOffset; + while (ctx->offset < endOffset) { + Status ret = MP4ParseAtom(depth + 1, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Failed to parse atom at offset " PUBLIC_LOG_D64, ctx->offset); + } + FALSE_RETURN_V_MSG_E(ctx->offset == endOffset, Status::ERROR_INVALID_DATA, + "Finished parsing container atom at offset " PUBLIC_LOG_D64 ", expected end at " PUBLIC_LOG_D64, + ctx->offset, endOffset); + + return Status::OK; +} + +Status MP4AtomParser::ParseFtyp(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= DEFAULT_HEAD_SIZE && currentAtom.dataSize <= MAX_DATA_SIZE, + Status::ERROR_INVALID_DATA, "Invalid atom size for ftyp atom"); + FALSE_RETURN_V_MSG_E(depth == MP4_ROOT_DEPTH, Status::ERROR_INVALID_PARAMETER, "Invalid ftyp depth"); + + auto ftypInfo = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, ftypInfo.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read ftyp atom header failed"); + uint32_t majorBrand = GetU32Value(&ftypInfo[0]); + + uint32_t compatibleBrandsCount = (currentAtom.dataSize - 8) / 4; + FALSE_RETURN_V_MSG_E(compatibleBrandsCount > 0, Status::ERROR_INVALID_DATA, "Invalid compatible brands number"); + // 一次性读取所有compatible brands数据 + if (compatibleBrandsCount > 0) { + for (uint32_t i = 0; i < compatibleBrandsCount; ++i) { + // 解析和存储所有brands, 跳过 minor version,从第8字节开始 + uint32_t compatibleBrand = GetU32Value(&ftypInfo[8 + 4 * i]); + std::string brandStr = FourccToString(compatibleBrand); + ctx->compatibleBrands.insert(compatibleBrand); + } + } + + if (majorBrand == FourccType("qt ") || majorBrand == FourccType("QT ")) { + mediaInfo_.general.SetData(Tag::MEDIA_FILE_TYPE, FileType::MOV); + } else if (majorBrand == FourccType("m4v ") || majorBrand == FourccType("m4a ") || + majorBrand == FourccType("M4V ") || majorBrand == FourccType("M4A ")) { + mediaInfo_.general.SetData(Tag::MEDIA_FILE_TYPE, FileType::M4A); + } else { + isIsom_ = true; // 若为MP4格式,设置isom标志 + mediaInfo_.general.SetData(Tag::MEDIA_FILE_TYPE, FileType::MP4); + } + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseMoov(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_ROOT_DEPTH, Status::ERROR_INVALID_PARAMETER, "Invalid moov depth"); + FALSE_RETURN_V_MSG_E(!moovFound_, Status::ERROR_INVALID_DATA, "Duplicate moov atom"); + + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse moov atom failed"); + + moovFound_ = true; + return ret; +} + +Status MP4AtomParser::ParseMdat(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(currentAtom.size > 0, Status::ERROR_INVALID_DATA, "mdat atom size is zero, wrong MP4 file"); + hasMdatBox_ = true; + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseWide(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + if (currentAtom.dataSize < DEFAULT_HEAD_SIZE) { + ctx->offset += currentAtom.size; + return Status::OK; // continue + } + + uint8_t headBuffer[8]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, headBuffer, sizeof(headBuffer)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read embedded size failed"); + + uint32_t embeddedSize = GetU32Value(headBuffer); + // 如果不是0 sized mdat atom,跳过剩余数据 + if (embeddedSize != 0) { + ctx->offset += currentAtom.size; + return Status::OK; + } + + uint32_t embeddedType = GetU32Value(&headBuffer[4]); + // 如果不是mdat类型,跳过剩余数据 + if (embeddedType != FourccType("mdat")) { + ctx->offset += currentAtom.size; + return Status::OK; + } + + MP4Atom mdatAtom; + mdatAtom.type = embeddedType; + mdatAtom.size = currentAtom.dataSize - sizeof(headBuffer); + mdatAtom.dataSize = mdatAtom.size; + // 更新ctx指向mdat数据的开始位置 + int64_t originalOffset = ctx->offset; + int64_t originalDataOffset = ctx->dataOffset; + ctx->dataOffset = originalDataOffset + sizeof(headBuffer); + + ret = ParseMdat(mdatAtom, depth, ctx); + ctx->dataOffset = originalDataOffset; + ctx->offset = originalOffset + currentAtom.size; + return ret; +} + +std::string FormatTimestamp(uint64_t creationTime) +{ + time_t seconds = creationTime; + struct tm* timeinfo = gmtime(&seconds); + if (timeinfo == nullptr) { + return ""; + } + char buffer[64]; + if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S UTC", timeinfo) == 0) { + MEDIA_LOG_W("Failed to format timestamp"); + return ""; + } + return std::string(buffer); +} + +Status MP4AtomParser::ParseMvhd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_ONE, Status::ERROR_INVALID_DATA, "Invalid mvhd depth"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_MVHD_SIZE && currentAtom.dataSize <= MAX_DATA_SIZE, + Status::ERROR_INVALID_DATA, "Invalid atom size"); + ctx->offset += currentAtom.size; + + uint8_t headerInfo[currentAtom.dataSize]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, headerInfo, sizeof(headerInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read mvhd header info failed"); + uint8_t version = headerInfo[0]; + uint64_t creationTime; + uint64_t duration; + uint32_t timeScale; + int32_t matrixOffset = version == 1 ? DYNAMIC_MATRIX_OFFSET_V1 : DYNAMIC_MATRIX_OFFSET_V0; + + if (version == 1) { + creationTime = GetU64Value(&headerInfo[CREATION_TIME_OFFSET]); + timeScale = GetU32Value(&headerInfo[OFFSET_V1]); + duration = GetU64Value(&headerInfo[DURATION_OFFSET_V1]); + } else { + creationTime = static_cast(GetU32Value(&headerInfo[CREATION_TIME_OFFSET])); + if (creationTime > 0 && creationTime < QUICKTIME_EPOCH_OFFSET) { + creationTime += QUICKTIME_EPOCH_OFFSET; // Convert to Unix epoch time + } + timeScale = GetU32Value(&headerInfo[OFFSET_V0]); + duration = static_cast(GetU32Value(&headerInfo[DURATION_OFFSET_V0])); + } + if (timeScale <= 0) { + timeScale = 1; // set to default value + } + std::string creationTimeStr; + if (creationTime) { + creationTime -= QUICKTIME_EPOCH_OFFSET; + creationTimeStr = FormatTimestamp(creationTime); + } + mediaInfo_.general.SetData(Tag::MEDIA_CREATION_TIME, creationTimeStr); + mediaInfo_.general.SetData(Tag::MEDIA_TIME_SCALE, static_cast(timeScale)); + + int64_t durationUs = 0; + if (ConvertTimeScale(duration, S_TO_US, timeScale, &durationUs) != Status::OK) { + MEDIA_LOG_E("Mul overflow"); + } + mediaInfo_.general.SetData(Tag::MEDIA_DURATION, durationUs); + // 解析movie level display matrix + if (currentAtom.dataSize >= static_cast(matrixOffset + DISPLAY_MATRIX_SIZE)) { + ret = ParseDisplayMatrix(&headerInfo[matrixOffset], ctx->movieDisplayMatrix); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Failed to parse movie display matrix"); + ctx->hasMovieDisplayMatrix = !IsDisplayMatrixIdentity(ctx->movieDisplayMatrix); // 单位阵不是有效的旋转矩阵 + if (ctx->hasMovieDisplayMatrix) { + MEDIA_LOG_D("Movie has non-identity display matrix"); + } + } + return ret; +} + +int64_t MP4AtomParser::GetTrackDuration(std::shared_ptr track) +{ + if (!track || !track->sampleHelper || !IsValidTrackIndex(mediaInfo_, track->trackIndex)) { + MEDIA_LOG_E("Invalid track for duration calculation"); + return 0; + } + + int64_t duration = 0; + if (track->sampleHelper && !track->sampleHelper->sampleIndexEntry_.empty() && !hasMoofBox_) { + duration = track->sampleHelper->totalDuration_; + MEDIA_LOG_D("Using sample helper duration for track " PUBLIC_LOG_U32, track->trackIndex); + } + + // 从trun中累加 (适用于FMP4文件) + if (hasMoofBox_ && track->sampleHelper) { + duration = track->sampleHelper->totalTrunDuration_; + MEDIA_LOG_D("Using fragment duration for track " PUBLIC_LOG_U32 ": " PUBLIC_LOG_D64 + " (fragments: " PUBLIC_LOG_ZU ")", track->trackIndex, duration, fragmentEntry_.size()); + } + + if (duration > 0 && duration < INT64_MAX) { + return duration; + } else { + MEDIA_LOG_W("Cannot get valid duration for track " PUBLIC_LOG_U32, track->trackIndex); + return -1; // 返回-1表示无法获取有效的持续时间 + } +} + +void MP4AtomParser::CalculateTrackBitrates(std::shared_ptr track) +{ + FALSE_RETURN_MSG(track != nullptr || track->sampleHelper != nullptr, "Invalid track or sample helper"); + FALSE_RETURN_MSG(IsValidTrackIndex(mediaInfo_, track->trackIndex), "Invalid track index"); + // get track time scale and duration + int32_t timeScale = 0; + if (!mediaInfo_.tracks[track->trackIndex].GetData(Tag::MEDIA_TIME_SCALE, timeScale) || timeScale <= 0) { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has invalid time scale", track->trackIndex); + } + + int64_t duration = GetTrackDuration(track); + const auto& samples = track->sampleHelper->sampleIndexEntry_; + // calculate total sample size + int64_t totalSize = 0; + for (const auto& sample : samples) { + totalSize += sample.size; + } + // bitrate = (总字节数 * 8) / 时长(秒) + int64_t bitrate = 0; + int64_t temp1 = 0; + int64_t temp2 = 0; + if (duration > 0) { + if (!hasMoofBox_) { + if (__builtin_mul_overflow(totalSize, BYTE_TO_BITS, &temp1) || + __builtin_mul_overflow(temp1, timeScale, &temp2)) { + MEDIA_LOG_E("Mul overflow: [" PUBLIC_LOG_D64 "," PUBLIC_LOG_D32 "," PUBLIC_LOG_D32 "]", + totalSize, BYTE_TO_BITS, timeScale); + } + bitrate = temp2 / duration; + } else { + if (__builtin_mul_overflow(BYTE_TO_BITS, timeScale, &temp1)) { + MEDIA_LOG_E("Mul overflow: [" PUBLIC_LOG_D32 "," PUBLIC_LOG_D32 "]", + BYTE_TO_BITS, timeScale); + } + double exactBitrate = static_cast(totalSize) * temp1 / duration; + bitrate = static_cast(std::round(exactBitrate)); // 四舍五入 + } + } + mediaInfo_.tracks[track->trackIndex].SetData(Tag::MEDIA_BITRATE, bitrate); +} + +void MP4AtomParser::CalculateVideoFrameRate(std::shared_ptr track) +{ + FALSE_RETURN_MSG(track != nullptr && track->sampleHelper != nullptr, "Invalid track or sample helper"); + FALSE_RETURN_MSG(IsValidTrackIndex(mediaInfo_, track->trackIndex), "Invalid track index"); + // get track time scale and duration + int32_t timeScale = 0; + if (!mediaInfo_.tracks[track->trackIndex].GetData(Tag::MEDIA_TIME_SCALE, timeScale) || timeScale <= 0) { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has invalid time scale", track->trackIndex); + } + + int64_t duration = GetTrackDuration(track); + int64_t totalSamples = track->sampleHelper->sampleIndexEntry_.size(); + if (totalSamples <= 0) { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has invalid sample count", track->trackIndex); + return; + } + // 计算原始帧率 + if (duration > 0) { + double rawFrameRate = static_cast(timeScale) * totalSamples / duration; + double finalFrameRate = GetStandardFrameRate(rawFrameRate); + mediaInfo_.tracks[track->trackIndex].SetData(Tag::VIDEO_FRAME_RATE, finalFrameRate); + MEDIA_LOG_D("Track:" PUBLIC_LOG_U32 ", totalSamples=" PUBLIC_LOG_D64 ", duration=" PUBLIC_LOG_D64 + ", timeScale=" PUBLIC_LOG_D32 ", frameRate=" PUBLIC_LOG_F " fps", track->trackIndex, totalSamples, + duration, timeScale, finalFrameRate); + } +} +void MP4AtomParser::SetCodecConfig(std::shared_ptr track) +{ + FALSE_RETURN_MSG(track != nullptr, "track is null"); + if (track->codecParms.extradataSize > 0 && track->codecParms.data != nullptr) { + std::vector extra(track->codecParms.extradataSize); + extra.assign(track->codecParms.data.get(), track->codecParms.data.get() + track->codecParms.extradataSize); + mediaInfo_.tracks[track->trackIndex].SetData(Tag::MEDIA_CODEC_CONFIG, extra); + } else { + MEDIA_LOG_E("Set codec config failed"); + } +} + +Status MP4AtomParser::ParseTrak(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_ONE, Status::ERROR_INVALID_DATA, "Invalid trak depth"); + auto track = std::make_shared(); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate memory for track"); + + size_t originalTrackCount = mediaInfo_.tracks.size(); + Meta newTrackMeta; + mediaInfo_.tracks.emplace_back(std::move(newTrackMeta)); + + if (mediaInfo_.tracks.size() != originalTrackCount + 1) { + return Status::ERROR_NO_MEMORY; + } + + if (lastTrack_) { + lastTrack_->next = track; + } else { + firstTrack_ = track; + } + lastTrack_ = track; + lastTrack_->trackIndex = trackCount_; + ++trackCount_; + lastTrack_->sampleHelper = std::make_shared(); + FALSE_RETURN_V_MSG_E(lastTrack_->sampleHelper != nullptr, Status::ERROR_NO_MEMORY, + "Failed create sample helper"); + lastTrack_->sampleHelper->seekable_ = seekable_; + lastTrack_->sampleHelper->dataReader_ = dataReader_; + // 使用通用容器解析函数 + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Failed to parse trak atom"); + + if (lastTrack_->codecParms.trackType == VIDEO_TYPE) { + ParseOrientationFromMatrix(lastTrack_); + ParseRotationTypeFromMatrix(lastTrack_); + } + + if (lastTrack_->sampleHelper) { + if (lastTrack_->sampleHelper->BuildSampleIndexEntries() != Status::OK) { + MEDIA_LOG_E("BuildSampleIndexEntry failed"); + } + } + SetCodecConfig(lastTrack_); + return Status::OK; +} + +void MP4AtomParser::CalculateSampleAspectRatio(std::shared_ptr track, uint32_t width, uint32_t height) +{ + FALSE_RETURN_MSG(track != nullptr, "CalculateSampleAspectRatio: track is null"); + FALSE_RETURN_MSG(track->hasDisplayMatrix && track->displayMatrix != nullptr, "Track has no valid display matrix"); + FALSE_RETURN_MSG(IsValidTrackIndex(mediaInfo_, track->trackIndex), "Invalid track index"); + + constexpr int32_t transformComponents = 2; // 变换组件数量:X和Y + constexpr int32_t matrixColOffset = 3; // 矩阵列偏移量 + std::array dispTransform; + + for (int32_t i = 0; i < transformComponents; ++i) { + double x = static_cast(track->displayMatrix[0 + i]) / CONVERT_SCALE; + double y = static_cast(track->displayMatrix[matrixColOffset + i]) / CONVERT_SCALE; + dispTransform[i] = std::hypot(x, y); + } + constexpr double minTransform = 1.0; + constexpr double maxTransform = 1 << 24; + constexpr double sarThreshold = 0.01; + + if (dispTransform[0] > minTransform && dispTransform[1] > minTransform && + dispTransform[0] < maxTransform && dispTransform[1] < maxTransform && + fabs((dispTransform[0] / dispTransform[1]) - minTransform) > sarThreshold) { + double sar = dispTransform[0] / dispTransform[1]; + mediaInfo_.tracks[track->trackIndex].SetData(Tag::VIDEO_SAR, sar); + } else { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " SAR calculation skip", track->trackIndex); + } +} + +void MP4AtomParser::SetTrackDisplayMatrix(std::shared_ptr track, const int32_t resultMatrix[3][3], + uint32_t width, uint32_t height) +{ + if (!IsDisplayMatrixIdentity(resultMatrix)) { + constexpr uint16_t matrixSize = 9; + track->displayMatrix = std::make_unique(matrixSize); + FALSE_RETURN_MSG(track->displayMatrix != nullptr, "Failed to allocate memory for display matrix"); + for (uint16_t i = 0; i < DISPLAY_MATRIX_ROWS; ++i) { + for (uint16_t j = 0; j < DISPLAY_MATRIX_COLS; ++j) { + track->displayMatrix[i * DISPLAY_MATRIX_COLS + j] = resultMatrix[i][j]; + } + } + track->hasDisplayMatrix = true; + if (width && height) { + CalculateSampleAspectRatio(track, width, height); + } + } else { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has identity display matrix, skipping", track->trackIndex); + track->hasDisplayMatrix = false; + } +} + +Status MP4AtomParser::ParseTkhd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_TKHD_SIZE, Status::ERROR_INVALID_DATA, + "Invalid tkhd atom size"); + ctx->offset += currentAtom.size; + /* + * tkhd的所有版本中最大的值 + * creation(8) + modification(8) + trackId(4) + reserved(4) + duration(8) + reserved(8) + + * layer(2) + alternate_group(2) + volume(2) + reserved(2) + matrix(36) + width(4) + height(4) + */ + constexpr uint32_t infoSize = 96; + auto headerInfo = std::make_unique(infoSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, headerInfo.get(), infoSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read tkhd header info failed"); + uint8_t version = headerInfo[0]; + + int64_t dynamicSize = 0; + if (version == 1) { + // skip some unneeded parameters + lastTrack_->trackId = static_cast(GetU32Value(&headerInfo[OFFSET_V1])); + dynamicSize = DYNAMIC_SIZE_V1; + } else if (version == 0) { + lastTrack_->trackId = static_cast(GetU32Value(&headerInfo[OFFSET_V0])); + dynamicSize = DYNAMIC_SIZE_V0; + } else { + MEDIA_LOG_E("Invalid tkhd version"); + return Status::ERROR_INVALID_DATA; + } + + uint32_t width = GetU32Value(&headerInfo[dynamicSize + 52]) >> 16; + uint32_t height = GetU32Value(&headerInfo[dynamicSize + 56]) >> 16; + // 解析track level display matrix + int64_t matrixOffset = dynamicSize + 16; // track matrix在width/height之前 + if (currentAtom.dataSize >= static_cast(matrixOffset + DISPLAY_MATRIX_SIZE)) { + int32_t trackDisplayMatrix[DISPLAY_MATRIX_ROWS][DISPLAY_MATRIX_COLS]; + ret = ParseDisplayMatrix(&headerInfo[matrixOffset], trackDisplayMatrix); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Failed to parse track display matrix"); + // 计算最终的display matrix (track * movie) + int32_t resultMatrix[DISPLAY_MATRIX_ROWS][DISPLAY_MATRIX_COLS]; + if (ctx->hasMovieDisplayMatrix) { + MultiplyDisplayMatrix(trackDisplayMatrix, ctx->movieDisplayMatrix, resultMatrix); + } else { + if (memcpy_s(&resultMatrix[0][0], sizeof(int32_t) * DISPLAY_MATRIX_ROWS * DISPLAY_MATRIX_COLS, + &trackDisplayMatrix[0][0], sizeof(int32_t) * DISPLAY_MATRIX_ROWS * DISPLAY_MATRIX_COLS) != EOK) { + MEDIA_LOG_E("Failed to copy track display matrix"); + return Status::ERROR_INVALID_DATA; + } + } + SetTrackDisplayMatrix(lastTrack_, resultMatrix, width, height); + } else { + MEDIA_LOG_E("tkhd atom too small for display matrix, size: " PUBLIC_LOG_U64, currentAtom.dataSize); + lastTrack_->hasDisplayMatrix = false; + } + return ret; +} + +Status MP4AtomParser::ParseEdts(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse edts container failed"); + return ret; +} + +Status ParseElstEntry(const uint8_t* elstInfo, uint32_t index, uint8_t version, + int64_t& segmentDuration, int64_t& mediaTime) +{ + constexpr uint32_t elstHeaderSize = 8; + constexpr uint32_t entrySizeV0 = 12; + constexpr uint32_t entrySizeV1 = 20; + uint32_t entrySize = (version == 1) ? entrySizeV1 : entrySizeV0; + uint32_t entryOffset = elstHeaderSize + index * entrySize; + if (version == 1) { + segmentDuration = static_cast(GetU64Value(&elstInfo[entryOffset])); + mediaTime = static_cast(GetU64Value(&elstInfo[entryOffset + sizeof(uint64_t)])); + } else { + int32_t tempDuration = static_cast(GetU32Value(&elstInfo[entryOffset])); + int32_t tempMediaTime = static_cast(GetU32Value(&elstInfo[entryOffset + sizeof(uint32_t)])); + segmentDuration = tempDuration; + mediaTime = tempMediaTime; + } + MEDIA_LOG_D("Parsed elst entry " PUBLIC_LOG_U32 ": segmentDuration=" PUBLIC_LOG_U64 ", mediaTime=" PUBLIC_LOG_D64, + index, segmentDuration, mediaTime); + FALSE_RETURN_V_MSG_E(mediaTime >= 0 || mediaTime == -1, Status::ERROR_INVALID_DATA, + "Invalid edit list media time: " PUBLIC_LOG_D64, mediaTime); + + return Status::OK; +} + +Status MP4AtomParser::ParseElst(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + ctx->offset += currentAtom.size; + constexpr uint32_t maxEntryNum = 2; // elst 支持最多2个条目 + + auto elstInfo = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, elstInfo.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read elst info failed"); + uint8_t version = elstInfo[0]; + uint32_t entryCount = GetU32Value(&elstInfo[4]); + FALSE_RETURN_V_MSG_E(entryCount > 0 && entryCount <= maxEntryNum, Status::ERROR_INVALID_DATA, + "Invalid entry count: " PUBLIC_LOG_U32, entryCount); + + uint32_t elstStartIndex = 0; + for (uint32_t i = 0; i < entryCount; ++i) { + int64_t segmentDuration = 0; + int64_t mediaTime = 0; + + ret = ParseElstEntry(elstInfo.get(), i, version, segmentDuration, mediaTime); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse elst entry failed"); + + if (entryCount == maxEntryNum) { + if (i == 0 && mediaTime == -1) { + lastTrack_->elstInitEmptyEdit = segmentDuration; + elstStartIndex = 1; + } else if (i == elstStartIndex && mediaTime >= 0) { + lastTrack_->elstShiftStartTime = mediaTime; + } else { + MEDIA_LOG_E("Invalid elst entry"); + return Status::ERROR_INVALID_DATA; + } + } else if (entryCount == 1) { + if (segmentDuration >= 0 && mediaTime != -1) { + lastTrack_->elstInitEmptyEdit = 0; + lastTrack_->elstShiftStartTime = mediaTime; + } else { + MEDIA_LOG_E("Invalid elst entry"); + return Status::ERROR_INVALID_DATA; + } + } + } + + int32_t fileTimeScale = 0; + int64_t startTime = 0; + if (mediaInfo_.general.GetData(Tag::MEDIA_TIME_SCALE, fileTimeScale) && fileTimeScale > 0) { + FALSE_RETURN_V_MSG_E( + ConvertTimeScale(lastTrack_->elstInitEmptyEdit, S_TO_US, fileTimeScale, &startTime) == Status::OK, + Status::ERROR_INVALID_PARAMETER, "Mul overflow"); + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MEDIA_START_TIME, startTime); + return ret; +} + +Status MP4AtomParser::ParseTref(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_TWO, Status::ERROR_INVALID_DATA, "Invalid tref depth"); + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse tref container failed"); + return ret; +} + +Status MP4AtomParser::ParseCdsc(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + uint32_t idCount = currentAtom.dataSize / 4; + FALSE_RETURN_V_MSG_E(idCount > 0, Status::ERROR_INVALID_DATA, "Invalid id count in cdsc atom"); + + auto idData = std::make_unique(idCount * sizeof(uint32_t)); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, idData.get(), idCount * sizeof(uint32_t)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read cdsc atom id data failed"); + std::vector referencedTrackIds; + std::string referencedIdsStr; + for (uint32_t i = 0; i < idCount; ++i) { + uint32_t trackId = GetU32Value(&idData[i * sizeof(uint32_t)]); + if (trackId > 0) { + referencedTrackIds.emplace_back(trackId - 1); + if (!referencedIdsStr.empty()) { + referencedIdsStr += ","; + } + referencedIdsStr += std::to_string(trackId - 1); + } else { + MEDIA_LOG_W("Invalid track ID: " PUBLIC_LOG_U32, trackId); + } + } + + if (!referencedTrackIds.empty()) { + int32_t referencedTrackId = referencedTrackIds[0]; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::TIMED_METADATA_SRC_TRACK, referencedTrackId); + MEDIA_LOG_D("Referenced track IDs: " PUBLIC_LOG_S, referencedIdsStr.c_str()); + } + + ctx->offset += currentAtom.size; + return ret; +} + +Status MP4AtomParser::ParseMdia(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_TWO, Status::ERROR_INVALID_DATA, "Invalid mdia depth"); + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse mdia container failed"); + return ret; +} + +Status MP4AtomParser::ParseMdhd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "current track is null"); + constexpr uint64_t minMdhdSize = 24; // 最小mdhd atom大小 + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= minMdhdSize, Status::ERROR_INVALID_DATA, "Invalid mdhd atom size"); + ctx->offset += currentAtom.size; + + auto headerInfo = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, headerInfo.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read mdhd info failed"); + + uint8_t version = headerInfo[0]; + FALSE_RETURN_V_MSG_E(version == 0 || version == 1, Status::ERROR_INVALID_DATA, "Invalid mdhd version"); + // skip some unused attributes + uint32_t timeScale = 0; + if (version == 1) { + timeScale = GetU32Value(&headerInfo[OFFSET_V1]); + } else if (version == 0) { + timeScale = GetU32Value(&headerInfo[OFFSET_V0]); + } + + if (timeScale <= 0) { + MEDIA_LOG_E("Invalid timescale"); + timeScale = 1; // set default timescale to 1 + } + if (!IsValidTrackIndex(mediaInfo_, lastTrack_->trackIndex)) { + MEDIA_LOG_E("Track index out of bounds: " PUBLIC_LOG_U32, lastTrack_->trackIndex); + return Status::ERROR_INVALID_DATA; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MEDIA_TIME_SCALE, static_cast(timeScale)); + //duration 4 bytes or 8 bytes + uint16_t languageValue; + constexpr int32_t languageOffsetVersionOne = 32; + constexpr int32_t languageOffsetVersionZero = 20; + if (version == 1) { + languageValue = GetU16Value(&headerInfo[languageOffsetVersionOne]); + } else { + languageValue = GetU16Value(&headerInfo[languageOffsetVersionZero]); + } + + std::string langString = ConvertLanguageCode(languageValue); + MEDIA_LOG_D("Language code: " PUBLIC_LOG_S, langString.c_str()); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MEDIA_LANGUAGE, langString); + return ret; +} + +bool MP4AtomParser::UnderMetaPath(const std::vector& path, int32_t depth) +{ + FALSE_RETURN_V_MSG_E(path.size() >= DIRECT_META_PATH_SIZE, false, "Invalid depth for meta path check"); + constexpr size_t pathTwo = 2; + constexpr size_t pathThree = 3; + constexpr size_t metaPathSize = 3; // moov/udta/meta 结构的路径大小 + constexpr size_t hdlrPathSize = 4; // 包含hdlr的最大路径大小 + + // 情况1: moov/meta 直接结构 + if (path.size() >= DIRECT_META_PATH_SIZE && path[0] == FourccType("moov") && path[1] == FourccType("meta")) { + return (depth == MP4_DEPTH_TWO || (depth == MP4_DEPTH_THREE && + path.size() >= metaPathSize && path[pathTwo] == FourccType("hdlr"))); + } + + // 情况2: moov/udta/meta 结构 + if (path.size() >= metaPathSize && path[0] == FourccType("moov") && + path[1] == FourccType("udta") && path[pathTwo] == FourccType("meta")) { + return (depth == MP4_DEPTH_THREE || (depth == MP4_DEPTH_FOUR && + path.size() >= hdlrPathSize && path[pathThree] == FourccType("hdlr"))); + } + + return false; +} + +Status MP4AtomParser::ParseHdlr(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_HDLR_DATA_SIZE, Status::ERROR_INVALID_DATA, "Invalid size"); + // Skip version and flags (4 bytes), then read componentType and handlerType + ctx->offset += currentAtom.size; + uint8_t typeInfo[12]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, typeInfo, sizeof(typeInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read hdlr type info failed"); + uint32_t componentType = GetU32Value(&typeInfo[4]); + uint32_t handlerType = GetU32Value(&typeInfo[8]); + // If this is a metadata handler outside a valid track, mark it and return + if (UnderMetaPath(ctx->path, depth)) { + ctx->foundHdlrMdta = handlerType == FourccType("mdta") ? true : false; + return Status::OK; + } + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "not in a valid track"); + switch (handlerType) { + case FourccType("vide"): + lastTrack_->codecParms.trackType = VIDEO_TYPE; + break; + case FourccType("soun"): + lastTrack_->codecParms.trackType = AUDIO_TYPE; + break; + case FourccType("subp"): + case FourccType("clcp"): + case FourccType("text"): + lastTrack_->codecParms.trackType = SUBTITLE_TYPE; + break; + case FourccType("meta"): + ret = ProcessMetaHandler(currentAtom, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Process meta handler failed"); + break; + default: + if (componentType == FourccType("mhlr") && handlerType == FourccType("MPEG")) { + MEDIA_LOG_E("Unsupported MPEG handler type, it's MPEGPS in MOV "); + return Status::ERROR_UNSUPPORTED_FORMAT; + } else if (ctx->path.size() > DIRECT_META_PATH_SIZE && + ctx->path[ctx->path.size() - DIRECT_META_PATH_SIZE] != FourccType("mdia")) { + return Status::OK; + } + lastTrack_->codecParms.trackType = INVALID_TYPE; + break; + } + lastTrack_->sampleHelper->trackType_ = lastTrack_->codecParms.trackType; + MediaType mediaType = static_cast(lastTrack_->codecParms.trackType); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MEDIA_TYPE, mediaType); + return ret; +} + +Status MP4AtomParser::ProcessMetaHandler(MP4Atom currentAtom, ParseContext* ctx) +{ + char titleBuffer[MAX_TITLE_SIZE + 1] = {'\0'}; + uint64_t titleSize = std::min(static_cast(MAX_TITLE_SIZE), currentAtom.dataSize - MAX_TITLE_SIZE); + if (titleSize <= 0) { + return Status::OK; + } + + Status ret = dataReader_->ReadUintData(ctx->dataOffset + MAX_TITLE_SIZE, + reinterpret_cast(titleBuffer), titleSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read meta handler title failed"); + + int32_t offset = (!isIsom_ && static_cast(titleBuffer[0]) == titleSize - 1) ? 1 : 0; + constexpr uint32_t timedMetaDataLength = 14; + + if (titleSize >= static_cast(offset + timedMetaDataLength) && + strncmp(titleBuffer + offset, "timed_metadata", timedMetaDataLength) == 0) { + lastTrack_->codecParms.trackType = TIMEDMETA_TYPE; + lastTrack_->sampleHelper->trackType_ = TIMEDMETA_TYPE; + } + return Status::OK; +} + +Status MP4AtomParser::ParseMinf(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_THREE, Status::ERROR_INVALID_DATA, "Invalid minf depth"); + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse minf container failed"); + return ret; +} + +Status MP4AtomParser::ParseStbl(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_FOUR, Status::ERROR_INVALID_DATA, "Invalid stbl depth"); + + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse stbl container failed"); + return ret; +} + +Status MP4AtomParser::ParseStsd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= DEFAULT_HEAD_SIZE, Status::ERROR_INVALID_DATA, "Invalid atom size"); + + uint8_t stsdData[8]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, stsdData, sizeof(stsdData)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read stsd entry count failed"); + uint32_t entryCount = GetU32Value(&stsdData[4]); + if (entryCount <= 0 || entryCount > 0x0400) { + MEDIA_LOG_E("Invalid STSD entries"); + return Status::ERROR_INVALID_DATA; + } + int64_t endOffset = ctx->dataOffset + currentAtom.dataSize; + ctx->offset = ctx->dataOffset + sizeof(stsdData); // 跳过stsd头部信息 + // 读取sample description entries + for (uint32_t i = 0; i < entryCount; ++i) { + int64_t atomOffset = ctx->offset; + uint8_t sampleEntryHeader[8]; + ret = dataReader_->ReadUintData(atomOffset, sampleEntryHeader, sizeof(sampleEntryHeader)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read sample entry header failed"); + MP4Atom atom{ + .size = GetU32Value(&sampleEntryHeader[0]), + .type = static_cast(GetU32Value(&sampleEntryHeader[4])), + .dataSize = GetU32Value(&sampleEntryHeader[0]) - sizeof(sampleEntryHeader) + }; + ctx->dataOffset = atomOffset + sizeof(sampleEntryHeader); + MEDIA_LOG_D("Sample Entry type: " PUBLIC_LOG_S, FourccToString(atom.type).c_str()); + if (lastTrack_->codecParms.trackType == VIDEO_TYPE) { + ret = ParseVideoSampleEntry(atom, depth + 1, ctx); + } else if (lastTrack_->codecParms.trackType == AUDIO_TYPE) { + ret = ParseAudioSampleEntry(atom, depth + 1, ctx); + } else if (lastTrack_->codecParms.trackType == SUBTITLE_TYPE) { + ret = ParseSubtitleSampleEntry(atom, depth + 1, ctx); + } else if (lastTrack_->codecParms.trackType == TIMEDMETA_TYPE) { + ret = ParseMebx(atom, depth + 1, ctx); + } + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse stsd container failed"); + } + FALSE_RETURN_V_MSG_E(ctx->offset == endOffset, Status::ERROR_INVALID_DATA, "Invalid stsd size"); + if (lastTrack_->codecParms.trackType == AUDIO_TYPE) { + ret = SetAudioAttribute(); + } + return ret; +} + +// 该解析函数专门用于处理视频SampleEntry atom,如avc1 hvc1 vvc1 enca +Status MP4AtomParser::ParseVideoSampleEntry(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_INVALID_DATA, "Track is nullptr"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_VIDEO_SAMPLE_ENTRY_SIZE, Status::ERROR_INVALID_DATA, + "Invalid video codec atom size"); + /* Video SampleEntry的数据大小至少为78字节 + * pre_defined(2) + reserved(2) + pre_defined(12) + width(2) + height(2) + + * horizresolution(4) + vertresolution(4) + reserved(4) + frame_count(2) + + * compressorname(32) + depth(2) + pre_defined(2) + */ + uint8_t sampleEntryInfo[78]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, sampleEntryInfo, sizeof(sampleEntryInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read Video Sample Entry info failed"); + + uint16_t width = GetU16Value(&sampleEntryInfo[24]); + uint16_t height = GetU16Value(&sampleEntryInfo[26]); + MEDIA_LOG_D("width: " PUBLIC_LOG_U16 ", height: " PUBLIC_LOG_U16, width, height); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_WIDTH, static_cast(width)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_HEIGHT, static_cast(height)); + + if (currentAtom.type != FourccType("encv")) { + std::string mimeType = GetMimeType(currentAtom.type); + lastTrack_->sampleHelper->mimeType_ = mimeType; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MIME_TYPE, mimeType); + } + + if (HDR_VIVID_TAG_OFFSET + HDR_VIVID_TAG_LENGTH <= sizeof(sampleEntryInfo)) { + char hdrTag[HDR_VIVID_TAG_LENGTH]; + for (int64_t i = 0; i < HDR_VIVID_TAG_LENGTH; ++i) { + hdrTag[i] = sampleEntryInfo[HDR_VIVID_TAG_OFFSET + i]; + } + if (!strncmp(hdrTag, "CUVA HDR Video", strlen("CUVA HDR Video"))) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_IS_HDR_VIVID, true); + } + } else { + MEDIA_LOG_E("Invalid HDR tag offset or length"); + } + + int64_t endOffset = 0; + if (__builtin_add_overflow(ctx->offset, currentAtom.size, &endOffset)) { + MEDIA_LOG_E("Atom size overflow"); + return Status::ERROR_INVALID_DATA; + } + ctx->offset = ctx->dataOffset + sizeof(sampleEntryInfo); + while (ctx->offset < endOffset) { + ret = MP4ParseAtom(depth + 1, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse Video Sample Entry failed"); + } + + FALSE_RETURN_V_MSG_E(ctx->offset == endOffset, Status::ERROR_INVALID_DATA, "Invalid Video Sample Entry size"); + return ret; +} + +Status MP4AtomParser::ParseSubtitleSampleEntry(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + if (currentAtom.type == FourccType("wvtt")) { + std::string mimeType = GetMimeType(currentAtom.type); + lastTrack_->sampleHelper->mimeType_ = mimeType; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MIME_TYPE, mimeType); + } + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseMebx(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_TIMED_METADATA_SAMPLE_ENTRY_SIZE, + Status::ERROR_INVALID_DATA, "Invalid mebx atom size"); + constexpr uint64_t skipSize = 8; + // 设置MIME类型 + std::string mimeType = GetMimeType(currentAtom.type); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MIME_TYPE, mimeType); + + // 读取mebx数据 + auto mebxData = std::make_unique(currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(mebxData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate memory for mebx data"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, mebxData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read mebx data failed"); + + uint64_t readOffset = 0; + // 跳过前8字节的填充数据 + readOffset += skipSize; + // 读取size_keys + uint32_t sizeKeys = GetU32Value(&mebxData[readOffset]); + // 跳过'keys'标识 + readOffset += skipSize; + uint64_t readSize = skipSize; // read_counts = 8 (已读取size_keys和keys fourcc) + std::string timedMetadataKey; + + uint64_t skipOffset = 16; + uint64_t skipKeydOffset = 12; + while (readSize < sizeKeys && readOffset + skipOffset <= currentAtom.dataSize) { + // 读取atom_size + uint32_t atomSize = GetU32Value(&mebxData[readOffset]); + // 跳过local key id + readOffset += skipSize; + // 读取keyd_size + uint32_t keydSize = GetU32Value(&mebxData[readOffset]); + // 跳过'keyd' 'mdta' + readOffset += skipKeydOffset; + + FALSE_RETURN_V_MSG_E(keydSize > skipKeydOffset, Status::ERROR_INVALID_DATA, "Invalid keyd size"); + uint32_t keyValueSize = keydSize - skipKeydOffset; + + if (readOffset + keyValueSize <= currentAtom.dataSize && keyValueSize > 0) { + std::string keyValue(reinterpret_cast(&mebxData[readOffset]), keyValueSize); + if (!keyValue.empty()) { + timedMetadataKey = keyValue; + } + readOffset += keyValueSize; + } else { + MEDIA_LOG_E("Invalid key value size: " PUBLIC_LOG_U32, keyValueSize); + break; + } + readSize += atomSize; + } + if (!timedMetadataKey.empty()) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::TIMED_METADATA_KEY, timedMetadataKey); + } + + ctx->offset += currentAtom.size; + MEDIA_LOG_D("Mebx parsing completed. Key: " PUBLIC_LOG_S, timedMetadataKey.c_str()); + return Status::OK; +} + +// 该解析函数专门用于处理CodecConfig atom,如avcC hvcC vvcC +Status MP4AtomParser::ParseCodecConfig(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize <= MAX_EXTRA_DATA_SIZE, Status::ERROR_INVALID_DATA, + "CodecConfig size too large"); + ctx->offset += currentAtom.size; + // 检查是否已经有 codec config 数据 + if (lastTrack_->codecParms.data != nullptr) { + MEDIA_LOG_W("CodecConfig already exists, replacing with new data. Old size: " PUBLIC_LOG_D64 ", New size: " + PUBLIC_LOG_D64, lastTrack_->codecParms.extradataSize, currentAtom.dataSize); + lastTrack_->codecParms.data.reset(); + lastTrack_->codecParms.extradataSize = 0; + } + + auto configData = std::make_unique(currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(configData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate CodecConfig extradata"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, configData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, Status::ERROR_NOT_ENOUGH_DATA, "Read CodecConfig data failed"); + + lastTrack_->codecParms.data = std::move(configData); + lastTrack_->codecParms.extradataSize = currentAtom.dataSize; + MEDIA_LOG_D("CodecConfig extradata size: " PUBLIC_LOG_D64, lastTrack_->codecParms.extradataSize); + return ret; +} + +// 该解析函数专门用于处理音频SampleEntry atom,如mp4a enca +Status MP4AtomParser::ParseAudioSampleEntry(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + + int64_t endOffset = 0; + if (__builtin_add_overflow(ctx->offset, currentAtom.size, &endOffset)) { + MEDIA_LOG_E("Atom size overflow"); + return Status::ERROR_INVALID_DATA; + } + Status ret = Status::OK; + // 音频配置信息至少需要28字节的数据 + if (currentAtom.dataSize >= MIN_AUDIO_SAMPLE_ENTRY_SIZE) { + ret = ParseAudioAttributes(currentAtom, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse audio attributes failed"); + } else { + MEDIA_LOG_W("Audio sample entry too small (" PUBLIC_LOG_U64 " bytes), skipping parsing", currentAtom.dataSize); + ctx->offset = endOffset; + } + + // 递归解析子atom + while (ctx->offset < endOffset) { + ret = MP4ParseAtom(depth + 1, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse Audio Sample Entry child failed"); + } + FALSE_RETURN_V_MSG_E(ctx->offset == endOffset, Status::ERROR_INVALID_DATA, "Invalid Audio Sample Entry size"); + + return ret; +} + +AudioSampleFormat GetLpcmSampleFormat(uint16_t sampleSize, uint32_t flags) +{ + constexpr uint16_t maxSampleSize = 64; + constexpr uint16_t extraSampleSizeAdd = 7; + uint32_t floatLE = flags & 0x01; + uint32_t bigEndian = flags & 0x02; + int32_t extraflag = flags & 0x04 ? -1 : 0; + if (sampleSize <= 0 || sampleSize > maxSampleSize) { + return AudioSampleFormat::INVALID_WIDTH; + } + if (floatLE) { + switch (sampleSize) { + case 0x20: + return bigEndian ? AudioSampleFormat::SAMPLE_F32BE : AudioSampleFormat::SAMPLE_F32LE; + case 0x40: + return bigEndian ? AudioSampleFormat::SAMPLE_F64BE : AudioSampleFormat::INVALID_WIDTH; + default: + return AudioSampleFormat::INVALID_WIDTH; + } + } else { + sampleSize += extraSampleSizeAdd; + sampleSize >>= 0x03; + if (extraflag & (1 << (sampleSize - 1))) { + switch (sampleSize) { + case 0x01: + return AudioSampleFormat::SAMPLE_S8; + case 0x02: + return bigEndian ? AudioSampleFormat::SAMPLE_S16BE : AudioSampleFormat::SAMPLE_S16LE; + case 0x03: + return bigEndian ? AudioSampleFormat::SAMPLE_S24BE : AudioSampleFormat::SAMPLE_S24LE; + case 0x04: + return bigEndian ? AudioSampleFormat::SAMPLE_S32BE : AudioSampleFormat::SAMPLE_S32LE; + case 0x08: + return bigEndian ? AudioSampleFormat::SAMPLE_F64BE : AudioSampleFormat::INVALID_WIDTH; + default: + return AudioSampleFormat::INVALID_WIDTH; + } + } else { + switch (sampleSize) { + case 0x01: + return AudioSampleFormat::SAMPLE_U8; + default: + return AudioSampleFormat::INVALID_WIDTH; + } + } + } +} + +Status MP4AtomParser::ParseAudioAttributes(MP4Atom currentAtom, ParseContext* ctx) +{ + /* Audio SampleEntry的数据大小 reserved(8) + version (2) + revision_level(2) + * + vendor(4) + channelcount(2) + samplesize(2) + pre_defined(2) + reserved2(2) + samplerate(4) + */ + uint8_t sampleEntryInfo[28]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, sampleEntryInfo, sizeof(sampleEntryInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read Audio Sample Entry info failed"); + // 读取当前sample entry的信息 + uint16_t version = GetU16Value(&sampleEntryInfo[0x08]); + uint16_t channelCount = GetU16Value(&sampleEntryInfo[0x10]); + uint16_t sampleSize = GetU16Value(&sampleEntryInfo[0x12]); // bits_per_coded_sample + //skip unneed distribute + uint32_t sampleRate = GetU32Value(&sampleEntryInfo[0x18]) / CONVERT_SCALE; + ctx->offset = ctx->dataOffset + sizeof(sampleEntryInfo); + // 处理不同版本的扩展数据 + constexpr int32_t versionOneSikpSize = 16; + constexpr int32_t versionTwoSikpSize = 36; + constexpr uint16_t sampleSizeOffset = 20; + constexpr uint16_t versionTwo = 2; + uint32_t samplePerFrame = 0; + if (!isIsom_ || (ctx->compatibleBrands.count(FourccType("qt "))) > 0 || version > 0) { + size_t entryInfoSize = (version == 1) ? versionOneSikpSize : versionTwoSikpSize; + auto entryInfo = std::make_unique(entryInfoSize); + ret = dataReader_->ReadUintData(ctx->offset, entryInfo.get(), entryInfoSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read entry info failed"); + if (version == 1) { + samplePerFrame = GetU32Value(&entryInfo[0]); + ctx->offset += versionOneSikpSize; + } else if (version == versionTwo) { + //skip struct 4bytes + sampleRate = static_cast(GetDoubleValue(&entryInfo[0x04])); + channelCount = static_cast(GetU32Value(&entryInfo[0x0C])); + // skip 4byets + sampleSize = static_cast(GetU32Value(&entryInfo[sampleSizeOffset])); + uint32_t flags = GetU32Value(&entryInfo[0x18]); + samplePerFrame = GetU32Value(&entryInfo[0x20]); + if (currentAtom.type == FourccType("lpcm")) { + AudioSampleFormat sampleFormat = GetLpcmSampleFormat(sampleSize, flags); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); + } + ctx->offset += versionTwoSikpSize; + } + } + SetAudioMimeTypeAndSampleSize(currentAtom, sampleSize); + + int32_t finalSampleSize = static_cast(sampleSize); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_COUNT, static_cast(channelCount)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_RATE, static_cast(sampleRate)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_BITS_PER_CODED_SAMPLE, finalSampleSize); + if (samplePerFrame) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_PER_FRAME, + static_cast(samplePerFrame)); + } + + if (isIsom_ && currentAtom.type == FourccType("alac")) { + ret = ParseAlacEntry(*ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse ALAC entry failed"); + } + + return ret; +} + +void MP4AtomParser::SetAmrAudioAttribute(std::string mime) +{ + if (mime == MimeType::AUDIO_AMR_NB) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_F32P); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::MONO); + } else { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_F32LE); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::MONO); + } +} + +void MP4AtomParser::SetMpegAudioAttribute() +{ + int32_t channelCount = 0; + AudioChannelLayout layout = AudioChannelLayout::UNKNOWN; + if (mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_CHANNEL_COUNT, channelCount)) { + layout = channelCount == 1 ? AudioChannelLayout::MONO : AudioChannelLayout::STEREO; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, layout); + } +} + +Status MP4AtomParser::SetVorbisAudioAttribute() +{ + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_F32P); + uint8_t* vorbisData = lastTrack_->codecParms.data.get(); + uint32_t currentOffset = 4; + if (vorbisData[currentOffset] != 0x76 || vorbisData[currentOffset + 0x01] != 0x6F || + vorbisData[currentOffset + 0x02] != 0x72 || vorbisData[currentOffset + 0x03] != 0x62 || + vorbisData[currentOffset + 0x04] != 0x69 || vorbisData[currentOffset + 0x05] != 0x73) { + return Status::ERROR_INVALID_DATA; + } + currentOffset += 0x0A; + int32_t channels = vorbisData[currentOffset]; + AudioChannelLayout channelLayout = FLAC_CHANNEL_LAYOUT_TABLE.find(channels) -> second; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, channelLayout); + return Status::OK; +} + +Status MP4AtomParser::SetAudioAttribute() +{ + int32_t sampleRate = 0; + int32_t timescale = 0; + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_SAMPLE_RATE, sampleRate); + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::MEDIA_TIME_SCALE, timescale); + if (sampleRate == 0 && timescale > 0) { + sampleRate = timescale; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_RATE, sampleRate); + } + + std::string mime = lastTrack_->sampleHelper->mimeType_; + if (mime == MimeType::AUDIO_AAC) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_AAC_IS_ADTS, 1); + } + Status ret = Status::OK; + int32_t bitsPerRawSample = 0; + if (!mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_BITS_PER_RAW_SAMPLE, bitsPerRawSample)) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_BITS_PER_RAW_SAMPLE, bitsPerRawSample); + } + + if (mime == MimeType::AUDIO_AMR_NB || mime == MimeType::AUDIO_AMR_WB) { + SetAmrAudioAttribute(mime); + } else if (mime == MimeType::AUDIO_MPEG) { + SetMpegAudioAttribute(); + } else if (mime == MimeType::AUDIO_VORBIS) { + ret = SetVorbisAudioAttribute(); + } else if (mime == "audio/dts" || mime == MimeType::AUDIO_AAC) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_F32P); + } + return ret; +} + +void MP4AtomParser::SetAudioMimeTypeAndSampleSize(const MP4Atom& currentAtom, uint16_t& sampleSize) +{ + if (currentAtom.type != FourccType("enca")) { + std::string mimeType = GetMimeType(currentAtom.type); + lastTrack_->sampleHelper->mimeType_ = mimeType; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MIME_TYPE, mimeType); + if (mimeType == MimeType::AUDIO_G711MU || mimeType == MimeType::AUDIO_G711A) { + sampleSize = G711MULAW_SAMPLE_SIZE; // G.711 mu-law uses 8 bits per sample + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, + AudioSampleFormat::SAMPLE_S16LE); + } else if (mimeType == MimeType::AUDIO_RAW) { + sampleSize = GetRawPCMBitsPerSample(currentAtom, sampleSize); + SetRawPCMSampleFormat(currentAtom, sampleSize); + } + } +} + +int32_t MP4AtomParser::GetRawPCMBitsPerSample(const MP4Atom& currentAtom, uint16_t& sampleSize) +{ + constexpr uint16_t bitPCM8 = 8; + constexpr uint16_t bitPCM16 = 16; + constexpr uint16_t bitPCM24 = 24; + constexpr uint16_t bitPCM32 = 32; + constexpr uint16_t bitPCM64 = 64; + + switch (currentAtom.type) { + case FourccType("raw "): + case FourccType("NONE"): + return bitPCM8; + case FourccType("twos"): + case FourccType("lpcm"): + case FourccType("ipcm"): + return bitPCM16; + case FourccType("in24"): + return bitPCM24; + case FourccType("in32"): + case FourccType("fl32"): + return bitPCM32; + case FourccType("fl64"): + return bitPCM64; + case FourccType("sowt"): + if (sampleSize == 0x08) { + return bitPCM8; + } else if (sampleSize == 0x10) { + return bitPCM16; + } + return 0; + default: + return 0; + } +} + +void MP4AtomParser::SetRawPCMSampleFormat(const MP4Atom& currentAtom, uint16_t& sampleSize) +{ + AudioSampleFormat sampleFormat = AudioSampleFormat::INVALID_WIDTH; + switch (currentAtom.type) { + case FourccType("raw "): + case FourccType("NONE"): + sampleFormat = AudioSampleFormat::SAMPLE_U8; + break; + case FourccType("twos"): + case FourccType("lpcm"): + case FourccType("ipcm"): + sampleFormat = AudioSampleFormat::SAMPLE_S16BE; + break; + case FourccType("in24"): + sampleFormat = AudioSampleFormat::SAMPLE_S24BE; + break; + case FourccType("in32"): + sampleFormat = AudioSampleFormat::SAMPLE_S32BE; + break; + case FourccType("fl32"): + sampleFormat = AudioSampleFormat::SAMPLE_F32BE; + break; + case FourccType("fl64"): + sampleFormat = AudioSampleFormat::SAMPLE_F64BE; + break; + case FourccType("sowt"): + if (sampleSize == 0x08) { + sampleFormat = AudioSampleFormat::SAMPLE_U8; + } else if (sampleSize == 0x10) { + sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + } + break; + default: + sampleFormat = AudioSampleFormat::INVALID_WIDTH; + break; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); +} + +Status MP4AtomParser::ParseAlacEntry(ParseContext& ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + constexpr uint32_t alacHeaderSize = 12; // ALAC header size + constexpr uint32_t typeOffset = 4; // ALAC type offset in the header + constexpr uint32_t flagOffset = 8; // ALAC flag offset in the header + int64_t dataOffset = ctx.offset; + auto alacData = std::make_unique(alacHeaderSize); + Status ret = dataReader_->ReadUintData(dataOffset, alacData.get(), alacHeaderSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read ALAC entry data failed"); + + // 解析ALAC头部信息 + uint32_t size = GetU32Value(&alacData[0]); + uint32_t type = GetU32Value(&alacData[typeOffset]); + uint32_t flag = GetU32Value(&alacData[flagOffset]); + if (size != ALAC_SPECIFIC_SIZE || type != FourccType("alac") || flag != 0) { + MEDIA_LOG_E("Invalid ALAC Entry"); + return Status::ERROR_INVALID_DATA; + } + dataOffset += alacHeaderSize; + uint8_t buffer[size - alacHeaderSize]; + ret = dataReader_->ReadUintData(dataOffset, buffer, sizeof(buffer)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read buffer data failed"); + dataOffset += sizeof(buffer); + // 解析ALAC的 sample size 音频通道数和采样率 + uint8_t sampleSize = buffer[5]; + uint8_t channelCount = buffer[9]; + uint32_t sampleRate = GetU32Value(&buffer[20]); + if (channelCount > ALAC_CHANNEL_LAYOUT_TABLE.size()) { + MEDIA_LOG_E("Invalid ALAC channel count"); + return Status::ERROR_INVALID_DATA; + } + AudioChannelLayout channelLayout = ALAC_CHANNEL_LAYOUT_TABLE.find(channelCount)->second; + switch (sampleSize) { + case 0x10: + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_S16P); + break; + case 0x14: + case 0x18: + case 0x20: + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_S32P); + break; + default: + MEDIA_LOG_E("Invalid sample size"); + return Status::ERROR_INVALID_DATA; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, channelLayout); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_BITS_PER_RAW_SAMPLE, static_cast(sampleSize)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_COUNT, static_cast(channelCount)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_RATE, static_cast(sampleRate)); + ctx.offset = dataOffset; + return Status::OK; +} + +bool ReadDescriptor(const uint8_t* data, uint64_t dataSize, uint64_t& offset, uint8_t& tag, uint32_t& size) +{ + FALSE_RETURN_V_MSG_E(offset < dataSize, false, "Read descriptor failed"); + tag = data[offset++]; + // 读取可变长度大小字段 + size = 0; + uint8_t count = 0; + uint8_t temp; + const uint8_t maxCount = 4; // 最大可变长度大小字段为4字节 + do { + if (offset >= dataSize || count >= maxCount) { + return false; + } + temp = data[offset++]; + size = (size << 0x07) | (temp & 0x7F); + count++; + } while (temp & 0x80); + + return true; +} + +Status MP4AtomParser::ParseEsds(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + ctx->offset += currentAtom.size; + + auto esdsData = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, esdsData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read esds data failed"); + // 跳过 version + flags + uint64_t offset = 4; + + // 跳过 ES Descriptor + uint8_t tag; + uint32_t size; + FALSE_RETURN_V_MSG_E(ReadDescriptor(esdsData.get(), currentAtom.dataSize, offset, tag, size), + Status::ERROR_INVALID_DATA, "Failed to read ES descriptor"); + + const uint64_t skipOffsetMax = 3; // ES_ID + flags + const uint64_t skipOffsetMin = 2; + offset += (tag == MP4_ES_DESCR_TAG) ? skipOffsetMax : skipOffsetMin; + + // 读取 Decoder Config Descriptor + FALSE_RETURN_V_MSG_E(ReadDescriptor(esdsData.get(), currentAtom.dataSize, offset, tag, size), + Status::ERROR_INVALID_DATA, "Failed to read decoder config descriptor"); + FALSE_RETURN_V_MSG_E(tag == MP4_DEC_CONFIG_DESCR_TAG, Status::ERROR_INVALID_DATA, + "Expected decoder config descriptor, got tag: " PUBLIC_LOG_U8, tag); + + // 解析 Decoder Config Descriptor + FALSE_RETURN_V_MSG_E(ParseDecoderConfigDescriptor(esdsData.get(), currentAtom.dataSize, offset), + Status::ERROR_INVALID_DATA, "Failed to parse decoder config descriptor"); + + return Status::OK; +} + +std::string GetMimeTypeFromObjectType(uint8_t objectTypeId) +{ + static const std::map objectTypeToMimeMap = { + // 视频编解码器 + {0x20, MimeType::VIDEO_MPEG4}, // MPEG-4 Visual + {0x21, MimeType::VIDEO_AVC}, // H.264/AVC + {0x23, MimeType::VIDEO_HEVC}, // H.265/HEVC + {0x33, MimeType::VIDEO_VVC}, // H.266/VVC (OHOS扩展) + // MPEG-2 Video系列 + {0x60, MimeType::VIDEO_MPEG2}, // MPEG-2 Simple Profile + {0x61, MimeType::VIDEO_MPEG2}, // MPEG-2 Main Profile + {0x62, MimeType::VIDEO_MPEG2}, // MPEG-2 SNR Profile + {0x63, MimeType::VIDEO_MPEG2}, // MPEG-2 Spatial Profile + {0x64, MimeType::VIDEO_MPEG2}, // MPEG-2 High Profile + {0x65, MimeType::VIDEO_MPEG2}, // MPEG-2 422 Profile + // 音频编解码器 + {0x40, MimeType::AUDIO_AAC}, // MPEG-4 AAC + {0x66, MimeType::AUDIO_AAC}, // MPEG-2 AAC Main + {0x67, MimeType::AUDIO_AAC}, // MPEG-2 AAC Low Complexity + {0x68, MimeType::AUDIO_AAC}, // MPEG-2 AAC SSR + {0x69, MimeType::AUDIO_MPEG}, // MPEG-2 Layer 3 (MP3) / MPEG-1 Layer 2 (MP2) + {0x6B, MimeType::AUDIO_MPEG}, // MPEG-1 Layer 3 (MP3) + {0xC1, MimeType::AUDIO_FLAC}, // FLAC + {0xDD, MimeType::AUDIO_VORBIS}, // Vorbis + {0xAD, MimeType::AUDIO_OPUS}, // Opus + {0xA9, "audio/dts"} // DTS + }; + auto it = objectTypeToMimeMap.find(objectTypeId); + return (it != objectTypeToMimeMap.end()) ? it->second : ""; +} + +bool MP4AtomParser::ParseDecoderConfigDescriptor(const uint8_t* data, uint64_t dataSize, uint64_t& offset) +{ + FALSE_RETURN_V_MSG_E(offset + ESDS_READ_SIZE <= dataSize, false, "Parse decoderConfig descriptor failed"); + + uint8_t objectTypeId = data[offset++]; // Object Type ID + // 跳过 streamType(1字节) buffer size (3字节) 和 max bitrate (4字节) + offset += sizeof(uint64_t); + + std::string mimetype = GetMimeTypeFromObjectType(objectTypeId); + if (!mimetype.empty()) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MIME_TYPE, mimetype); + lastTrack_->sampleHelper->mimeType_ = mimetype; + } + uint32_t avgBitrate = GetU32Value(&data[offset]); + offset += sizeof(uint32_t); + + if (lastTrack_ && avgBitrate > 0) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MEDIA_BITRATE, static_cast(avgBitrate)); + } + // 读取解码器特定信息描述符 + uint8_t tag; + uint32_t size; + FALSE_RETURN_V_MSG_E(ReadDescriptor(data, dataSize, offset, tag, size), false, "No decoder specific info found"); + + if (tag == MP4_DEC_SPECIFIC_DESCR_TAG) { + // 特殊情况:MPEG-1/2 Audio 不需要 decoder specific info + const uint32_t maxSize = 1 << 30; + if (objectTypeId == 0x69 || objectTypeId == 0x6B) { + MEDIA_LOG_D("MPEG-1/2 Audio detected, no decoder specific info needed"); + return true; + } + + FALSE_RETURN_V_MSG_E(size > 0 && size <= maxSize, false, "Wrong descriptor size"); + FALSE_RETURN_V_MSG_E(offset + size <= dataSize, false, "Decoder specific info extends beyond atom boundary"); + + // 提取 decoder specific info + const uint8_t* specificInfo = &data[offset]; + if (specificInfo && size > 0) { + if (lastTrack_->codecParms.data) { + lastTrack_->codecParms.data.reset(); + } + lastTrack_->codecParms.data = std::make_unique(size); + FALSE_RETURN_V_MSG_E(lastTrack_->codecParms.data, false, "Failed to allocate codec config"); + + auto ret = memcpy_s(lastTrack_->codecParms.data.get(), size, specificInfo, size); + FALSE_RETURN_V_MSG_E(ret == EOK, false, "Failed to copy codec config data"); + lastTrack_->codecParms.extradataSize = size; + } + if (mimetype == MimeType::AUDIO_AAC) { + return ParseAACSpecificConfig(specificInfo, size); + } + } + + return true; +} + +void GetAACConfigExtInfo(const std::shared_ptr bitReader, uint8_t& audioObjectType) +{ + bitReader->SkipBits(0x04); + audioObjectType = bitReader->ReadBits(0x05); + if (audioObjectType == 0x1F) { + audioObjectType = 0x20 + bitReader->ReadBits(0x06); + } +} + +bool DecodeChannelMap(uint8_t layoutMap[][3], uint8_t channelType, const std::shared_ptr bitReader, + uint8_t num) +{ + uint8_t element; + while (num--) { + switch (channelType) { + case 0x01: + case 0x02: + case 0x03: { + element = bitReader->ReadBits(1); + break; + } + case 0x04: { + element = TYPE_LFE; + break; + } + case 0x05: { + bitReader->SkipBits(1); + element = TYPE_CCE; + break; + } + default: + return false; + } + layoutMap[0][0] = element; + layoutMap[0][1] = bitReader->ReadBits(0x04); + layoutMap[0][0x02] = channelType; + ++layoutMap; + } + return true; +} + +uint8_t CountChannels(uint8_t (*layoutMap)[3], uint8_t tags) +{ + uint8_t sum = 0; + for (uint8_t i = 0; i < tags; ++i) { + uint8_t element = layoutMap[i][0]; + uint8_t pos = layoutMap[i][0x02]; + sum += (1 + (element == TYPE_CPE)) * (pos != 0 && pos != 0x05); + } + return sum; +} + +bool ParseGeneralAACConfig(const std::shared_ptr bitReader, uint8_t audioObjectType, + uint32_t& channels) +{ + uint8_t layoutMap[64][3]; + FALSE_RETURN_V(bitReader->HasBits(0x1A), false); + uint32_t flag = bitReader->ReadBits(1); + if (flag) { + bitReader->SkipBits(0x19); + } else { + bitReader->SkipBits(0x0B); + } + FALSE_RETURN_V(bitReader->HasBits(0x23), false); + uint8_t numFront = bitReader->ReadBits(0x04); + uint8_t numSide = bitReader->ReadBits(0x04); + uint8_t numBack = bitReader->ReadBits(0x04); + uint8_t numLfe = bitReader->ReadBits(0x02); + uint8_t numAssociateData = bitReader->ReadBits(0x03); + uint8_t numCc = bitReader->ReadBits(0x04); + if (bitReader->ReadBits(1)) { + bitReader->ReadBits(0x04); + } + if (bitReader->ReadBits(1)) { + bitReader->ReadBits(0x04); + } + if (bitReader->ReadBits(1)) { + bitReader->ReadBits(0x03); + } + if (!(bitReader->HasBits(0x05 * (numFront + numSide + numBack + numCc) + + 0x04 * (numLfe + numAssociateData + numCc)))) { + return false; + } + uint8_t tags = 0; + FALSE_RETURN_V(DecodeChannelMap(layoutMap, 0x01, bitReader, numFront), false); + tags += numFront; + FALSE_RETURN_V(DecodeChannelMap(layoutMap + tags, 0x02, bitReader, numSide), false); + tags += numSide; + FALSE_RETURN_V(DecodeChannelMap(layoutMap + tags, 0x03, bitReader, numBack), false); + tags += numBack; + FALSE_RETURN_V(DecodeChannelMap(layoutMap + tags, 0x04, bitReader, numLfe), false); + tags += numLfe; + bitReader->SkipBits(0x04 * numAssociateData); + FALSE_RETURN_V(DecodeChannelMap(layoutMap + tags, 0x05, bitReader, numCc), false); + tags += numCc; + + channels = CountChannels(layoutMap, tags); + return true; +} + +bool CheckAACType(uint8_t audioObjectType) +{ + switch (audioObjectType) { + case AAC_MAIN: + case AAC_LC: + case AAC_SSR: + case AAC_LTP: + case AAC_ER_LC: + case AAC_ER_LD: + case AAC_ER_ELD: + return true; + default: + return false; + } +} + +bool GetAACLayoutConfig(uint8_t audioObjectType, const std::shared_ptr bitReader, uint32_t size, + uint32_t& channels, AudioChannelLayout &layout) +{ + switch (audioObjectType) { + case AAC_MAIN: + case AAC_LC: + case AAC_SSR: + case AAC_LTP: + case AAC_ER_LC: + case AAC_ER_LD: { + FALSE_RETURN_V(ParseGeneralAACConfig(bitReader, audioObjectType, channels), false); + break; + } + case AAC_ER_ELD: { + return true; + } + default: + return false; + } + return true; +} + +bool MP4AtomParser::ParseAACConfig(const uint8_t* data, uint32_t size, uint32_t& sampleRate, uint32_t& channels, + AudioChannelLayout &layout) +{ + auto bitReader = std::make_shared(data, size); + FALSE_RETURN_V_MSG_E(bitReader != nullptr, false, "Failed create bitReader"); + FALSE_RETURN_V(bitReader->HasBits(MIN_BOX_SIZE), false); + uint8_t audioObjectType = bitReader->ReadBits(0x05); + if (audioObjectType == 0x1F) { + FALSE_RETURN_V(bitReader->HasBits(0x11), false); + audioObjectType = 0x20 + bitReader->ReadBits(0x06); + } + uint8_t samplingFreqIndex = bitReader->ReadBits(0x04); + if (samplingFreqIndex == 0x0F) { + FALSE_RETURN_V(bitReader->HasBits(0x1F), false); + sampleRate = bitReader->ReadBits(0x08) << 0x10 | bitReader->ReadBits(0x08) << 0x08 | bitReader->ReadBits(0x08); + } else { + if (samplingFreqIndex < sizeof(AAC_SAMPLE_RATE_TABLE) / sizeof(AAC_SAMPLE_RATE_TABLE[0])) { + sampleRate = AAC_SAMPLE_RATE_TABLE[samplingFreqIndex]; + } + } + uint8_t channelConfig = bitReader->ReadBits(0x04); + uint32_t frameLengthFlag = 0; + static const uint8_t aacAudioChannels[14] = { + 0, 1, 2, 3, 4, 5, 6, 8, 0, 0, 0, 7, 8, 24 + }; + if (channelConfig < sizeof(aacAudioChannels) / sizeof(aacAudioChannels[0])) { + channels = aacAudioChannels[channelConfig]; + layout = AAC_CHANNEL_LAYOUT_TABLE[channelConfig]; + } else { + MEDIA_LOG_E("Invalid AAC channel configuration: " PUBLIC_LOG_U8, channelConfig); + return false; + } + if (audioObjectType == AOT_SBR || (audioObjectType == AOT_PS && + (!(bitReader->ShowBits(0x03) & 0x03) && !(bitReader->ShowBits(0x09) & 0x0F)))) { + FALSE_RETURN_V(bitReader->HasBits(0x09), false); + GetAACConfigExtInfo(bitReader, audioObjectType); + } + frameLengthFlag = bitReader->ReadBits(1); + if (channelConfig == 0) { + FALSE_RETURN_V_MSG_E(GetAACLayoutConfig(audioObjectType, bitReader, size, channels, layout), false, + "Get AAC layout config failed"); + } + FALSE_RETURN_V_MSG_E(CheckAACType(audioObjectType), false, "Unsupported AAC type"); + int32_t samplePerFrame = frameLengthFlag ? 960 : 1024; + if (audioObjectType == AAC_ER_LD || (audioObjectType == AAC_ER_ELD)) { + samplePerFrame >>= 1; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_PER_FRAME, samplePerFrame); + MEDIA_LOG_D("AAC Config: objType=" PUBLIC_LOG_U8 ", samplingIdx=" PUBLIC_LOG_U8 ", channels=" + PUBLIC_LOG_U8, audioObjectType, samplingFreqIndex, channelConfig); + return true; +} + +bool MP4AtomParser::ParseAACSpecificConfig(const uint8_t* data, uint32_t size) +{ + constexpr uint32_t minConfigSize = 2; + if (!data || size < minConfigSize) { + MEDIA_LOG_E("Invalid AAC config data"); + return false; + } + uint32_t sampleRate = 44100; + uint32_t channels = 2; + AudioChannelLayout layout = AudioChannelLayout::UNKNOWN; + if (ParseAACConfig(data, size, sampleRate, channels, layout)) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_COUNT, static_cast(channels)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_RATE, static_cast(sampleRate)); + if (layout == AudioChannelLayout::UNKNOWN) { + int32_t channelCount = 0; + auto audioParser = std::make_shared(); + FALSE_RETURN_V_MSG_E(audioParser != nullptr, false, "Failed create audioParser"); + FALSE_RETURN_V_MSG_E( + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_CHANNEL_COUNT, channelCount), + false, "Channel count missed"); + layout = audioParser->GetDefaultChannelLayout(channelCount); + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, layout); + return true; + } + + return false; +} + +Status MP4AtomParser::ParseWave(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse moov atom failed"); + return ret; +} + +Status MP4AtomParser::ParseBtrt(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + ctx->offset += currentAtom.size; + + uint8_t btrtInfo[8]; + // Skip version and flags (4 bytes) + Status ret = dataReader_->ReadUintData(ctx->dataOffset + 4, btrtInfo, sizeof(btrtInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read btrt info failed"); + uint32_t maxBitrate = GetU32Value(&btrtInfo[0]); + uint32_t avgBitrate = GetU32Value(&btrtInfo[4]); + MEDIA_LOG_D("Btrt: maxBitrate=" PUBLIC_LOG_U32 ", avgBitrate=" PUBLIC_LOG_U32, maxBitrate, avgBitrate); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::MEDIA_BITRATE, static_cast(avgBitrate)); + return ret; +} + +Status MP4AtomParser::ParsePasp(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= DEFAULT_HEAD_SIZE, Status::ERROR_INVALID_DATA, "Invalid pasp size"); + ctx->offset += currentAtom.size; + + uint8_t paspInfo[8]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, paspInfo, sizeof(paspInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read pasp info failed"); + uint32_t hSpacing = GetU32Value(&paspInfo[0]); + uint32_t vSpacing = GetU32Value(&paspInfo[4]); + // Check if hSpacing and vSpacing are valid + if (vSpacing != 0) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_SAR, static_cast(hSpacing / vSpacing)); + MEDIA_LOG_D("Check pixel aspect ratio: " PUBLIC_LOG_F, static_cast(hSpacing / vSpacing)); + } else { + MEDIA_LOG_E("Invalid hSpacing or vSpacing"); + } + return ret; +} + +bool MP4AtomParser::ParseFLACExtradata(const uint8_t* data) +{ + const int32_t channelsOffset = 12; + const int32_t bitsPerRawSampleOffset = 13; + int32_t channels = ((data[channelsOffset] >> 1) & 0x07) + 1; + int32_t bitsPerRawSample = (((data[channelsOffset] & 0x01) << 4) | + ((data[bitsPerRawSampleOffset] >> 4) & 0x0F)) + 1; + const int32_t bitsPerRawSampleMin = 4; + if (bitsPerRawSample < bitsPerRawSampleMin) { + MEDIA_LOG_E("Invalid bitsPerRawSample: " PUBLIC_LOG_D32, bitsPerRawSample); + return false; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_BITS_PER_RAW_SAMPLE, bitsPerRawSample); + if (static_cast(channels) <= FLAC_CHANNEL_LAYOUT_TABLE.size()) { + AudioChannelLayout channelLayout = FLAC_CHANNEL_LAYOUT_TABLE.find(channels) -> second; + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, channelLayout); + } else { + MEDIA_LOG_E("Invalid channels: " PUBLIC_LOG_D32, channels); + return false; + } + const int32_t audio16BitSampleThreshold = 16; + bool need32BitSampleFormat = bitsPerRawSample > audio16BitSampleThreshold; + if (need32BitSampleFormat) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_S32LE); + } else { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_S16LE); + } + return true; +} + +Status MP4AtomParser::ParseDfla(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= 42 && currentAtom.dataSize <= (1UL << 30), Status::ERROR_INVALID_DATA, + "dfla atom size out of range: " PUBLIC_LOG_U64, currentAtom.dataSize); + + ctx->offset += currentAtom.size; + + auto dflaData = std::make_unique(currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(dflaData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate dfla buffer"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, dflaData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read dfla data failed"); + + uint8_t version = dflaData[0]; + FALSE_RETURN_V_MSG_E(version == 0, Status::ERROR_INVALID_DATA, "Unsupported FLAC version"); + + uint32_t offset = 4; // 跳过版本(1字节) 和 flags(3字节) + uint8_t* blockHeader = &dflaData[offset]; // 解析 FLAC 元数据块头部 + uint8_t blockType = blockHeader[0] & 0x7F; + uint32_t blockSize = (static_cast(blockHeader[1]) << 0x10) | + (static_cast(blockHeader[2]) << 0x08) | + static_cast(blockHeader[3]); + + constexpr uint32_t streamInfoSize = 34; + FALSE_RETURN_V_MSG_E(blockType == 0, Status::ERROR_INVALID_DATA, + "StreamInfo must be first FLACMetadataBlock"); + FALSE_RETURN_V_MSG_E(blockSize == streamInfoSize, Status::ERROR_INVALID_DATA, + "Invalid streamInfo size: " PUBLIC_LOG_U32 ", expected: " PUBLIC_LOG_U32, blockSize, streamInfoSize); + + offset += sizeof(streamInfoSize); // 跳过元数据块头部 + FALSE_RETURN_V_MSG_E(offset + blockSize <= currentAtom.dataSize, Status::ERROR_INVALID_DATA, + "dfla atom truncated at streamInfo data"); + + // 提取 StreamInfo 数据作为 extradata + auto extradataSize = blockSize; + auto extradata = std::make_unique(extradataSize); + FALSE_RETURN_V_MSG_E(extradata != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate FLAC extradata"); + if (memcpy_s(extradata.get(), extradataSize, &dflaData[offset], extradataSize) != EOK) { + MEDIA_LOG_E("Failed to copy extradata"); + return Status::ERROR_INVALID_DATA; + } + FALSE_RETURN_V_MSG_E(ParseFLACExtradata(extradata.get()), Status::ERROR_INVALID_DATA, + "Failed to parse FLAC extradata"); + lastTrack_->codecParms.data = std::move(extradata); + lastTrack_->codecParms.extradataSize = extradataSize; + return Status::OK; +} + +bool MP4AtomParser::ParseOPUSExtradata(const uint8_t* data, uint64_t dataSize) +{ + constexpr uint64_t minExtradataSize = 19; + FALSE_RETURN_V_MSG_E(dataSize >= minExtradataSize, false, "Invalid extradata size"); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::SAMPLE_F32P); + int32_t channels = data[9]; + int32_t mapType = data[18]; + AudioChannelLayout channelLayout = AudioChannelLayout::UNKNOWN; + constexpr int32_t maxChannels = 2; + if (mapType == 0) { + if (channels > maxChannels) { + MEDIA_LOG_E("Invalid channels: " PUBLIC_LOG_D32, channels); + return false; + } + channelLayout = (channels == 1) ? AudioChannelLayout::MONO : + AudioChannelLayout::STEREO; + } else if (mapType == 1) { + channelLayout = FLAC_CHANNEL_LAYOUT_TABLE.find(channels)->second; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, channelLayout); + return true; +} + +Status MP4AtomParser::ParseDops(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + // dops box至少11字节,最大1GB + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= OPUS_MIN_SIZE && currentAtom.dataSize <= MAX_SIZE, + Status::ERROR_INVALID_DATA, "Invalid dops atom size"); + ctx->offset += currentAtom.size; + auto dopsData = std::make_unique(currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(dopsData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate dops buffer"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, dopsData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read dops data failed"); + // 检查 dops version + uint8_t dopsVersion = dopsData[0]; + FALSE_RETURN_V_MSG_E(dopsVersion == 0, Status::ERROR_INVALID_DATA, "Unsupported OpusSpecificBox version"); + // 构造 OpusHead extradata + constexpr uint64_t headLen = 8; + constexpr uint64_t headVer = 1; + constexpr std::string_view opusHeadName = "OpusHead"; + uint64_t opusHeadSize = currentAtom.dataSize + headLen; + auto opusHead = std::make_unique(opusHeadSize); + FALSE_RETURN_V_MSG_E(opusHead != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate OpusHead buffer"); + if (memcpy_s(opusHead.get(), opusHeadName.size(), opusHeadName.data(), opusHeadName.size()) != EOK) { + MEDIA_LOG_E("Failed to copy opus head name"); + return Status::ERROR_INVALID_DATA; + } + + if (currentAtom.dataSize > 1) { + size_t copySize = currentAtom.dataSize - headVer; + size_t destSize = opusHeadSize - headLen - headVer; + if (memcpy_s(opusHead.get() + headLen + headVer, destSize, dopsData.get() + headVer, copySize) != EOK) { + MEDIA_LOG_E("Failed to copy extradata"); + return Status::ERROR_INVALID_DATA; + } + } + FALSE_RETURN_V_MSG_E(ParseOPUSExtradata(opusHead.get(), opusHeadSize), + Status::ERROR_INVALID_DATA, "Parse opus extradata failed"); + lastTrack_->codecParms.data = std::move(opusHead); + lastTrack_->codecParms.extradataSize = opusHeadSize; + return Status::OK; +} + +AudioSampleFormat ConvertToLEFormat(AudioSampleFormat sampleFormat) +{ + switch (sampleFormat) { + case AudioSampleFormat::SAMPLE_S16BE: + return AudioSampleFormat::SAMPLE_S16LE; + case AudioSampleFormat::SAMPLE_S24BE: + return AudioSampleFormat::SAMPLE_S24LE; + case AudioSampleFormat::SAMPLE_S32BE: + return AudioSampleFormat::SAMPLE_S32LE; + case AudioSampleFormat::SAMPLE_F32BE: + return AudioSampleFormat::SAMPLE_F32LE; + default: + return sampleFormat; // No conversion needed + } +} + +Status MP4AtomParser::ParsePcmc(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_PCMC_SIZE, Status::ERROR_INVALID_DATA, "Invalid pcmc atom size"); + ctx->offset += currentAtom.size; + + auto pcmcData = std::make_unique(currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(pcmcData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate pcmc buffer"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, pcmcData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read pcmc data failed"); + + constexpr size_t flagOffset = 4; + uint8_t formatFlag = pcmcData[flagOffset]; + + AudioSampleFormat sampleFormat = AudioSampleFormat::INVALID_WIDTH; + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); + if (formatFlag == 1) { + sampleFormat = ConvertToLEFormat(sampleFormat); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); + } + return Status::OK; +} + +Status MP4AtomParser::ParseEnda(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + ctx->offset += currentAtom.size; + auto endaData = std::make_unique(currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(endaData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate enda buffer"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, endaData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read enda data failed"); + uint16_t littleEndian = GetU16Value(&endaData[0]) & 0xFF; + + AudioSampleFormat sampleFormat = AudioSampleFormat::INVALID_WIDTH; + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); + if (littleEndian == 1) { + sampleFormat = ConvertToLEFormat(sampleFormat); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_SAMPLE_FORMAT, sampleFormat); + } + return Status::OK; +} + +// 解析ICC Profile +Status MP4AtomParser::ParseIccProfile(int64_t offset, uint64_t dataSize) +{ + FALSE_RETURN_V_MSG_E(dataSize <= MAX_DATA_SIZE, Status::ERROR_INVALID_DATA, + "Invalid ICC profile size"); + + std::vector iccProfile(dataSize); + Status ret = dataReader_->ReadUintData(offset, iccProfile.data(), dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read ICC profile data failed"); + return ret; +} + +Status MP4AtomParser::ParseColorParams(int64_t offset, uint64_t dataSize, const std::string& colorType, + ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(dataSize >= MIN_COLOR_PARAM_SIZE, Status::ERROR_INVALID_DATA, "Invalid color parameters size"); + + uint8_t colorParams[8]; // 读取颜色参数 最多8字节:6字节基本参数 + 可能的range字节 + uint32_t readSize = std::min(static_cast(sizeof(colorParams)), dataSize); + Status ret = dataReader_->ReadUintData(offset, colorParams, readSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read color parameters failed"); + + uint16_t colorPrimaries = GetU16Value(&colorParams[0]); + uint16_t colorTrc = GetU16Value(&colorParams[2]); + uint16_t colorMatrix = GetU16Value(&colorParams[4]); + uint8_t colorRange = 0; + + constexpr uint8_t colorRangeOffset = 6; + if (colorType == "nclx" && dataSize >= 0x07) { // nclx类型有额外的color range信息 + colorRange = (colorParams[colorRangeOffset] >> 0x07) & 1; // 取最高位 + if (colorRange == 0) { + colorRange = ColorRange::JPEG; + } else { + colorRange = ColorRange::MPEG; + } + } + std::string mimeType; + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::MIME_TYPE, mimeType); + if (mimeType == MimeType::VIDEO_HEVC) { + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_COLOR_RANGE, static_cast(colorRange)); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_COLOR_PRIMARIES, colorPrimaries); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_COLOR_TRC, colorTrc); + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_COLOR_MATRIX_COEFF, colorMatrix); + } + return Status::OK; +} + +Status MP4AtomParser::ParseColr(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "No current track"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_COLOR_SIZE, Status::ERROR_INVALID_DATA, "colr atom too small"); + ctx->offset += currentAtom.size; + + uint8_t colorParamType[4]; + int32_t typeSize = sizeof(colorParamType); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, colorParamType, sizeof(colorParamType)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read color parameter type failed"); + std::string colorType(reinterpret_cast(colorParamType), sizeof(colorParamType)); + + if (colorType != "nclx" && colorType != "nclc" && colorType != "prof") { + MEDIA_LOG_W("Unsupported color parameter type: " PUBLIC_LOG_S, colorType.c_str()); + return Status::OK; + } + + uint64_t dataSize = 0; + if (currentAtom.dataSize > static_cast(typeSize)) { + dataSize = currentAtom.dataSize - typeSize; + } else { + return Status::ERROR_INVALID_DATA; + } + + if (colorType == "prof") { + // ICC Profile类型:剩余数据全部是profile + ret = ParseIccProfile(ctx->dataOffset + typeSize, dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse ICC profile failed"); + } else { + ret = ParseColorParams(ctx->dataOffset + typeSize, dataSize, colorType, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse color parameters failed"); + } + return ret; +} + +Status MP4AtomParser::ParseAclr(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_ACLR_SIZE, Status::ERROR_INVALID_DATA, + "aclr atom size too small"); + ctx->offset += currentAtom.size; + auto aclrData = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, aclrData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read aclr data failed"); + uint8_t colorRange = aclrData[12]; // 第13字节是colorRange + switch (colorRange) { + case 0x01: + colorRange = ColorRange::MPEG; // MPEG range (16-235) + break; + case 0x02: + colorRange = ColorRange::JPEG; // JPEG/PC range (0-255) + break; + default: + break; + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::VIDEO_COLOR_RANGE, static_cast(colorRange)); + return Status::OK; +} + +Status MP4AtomParser::ParseGlbl(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize <= MAX_SIZE, Status::ERROR_INVALID_DATA, "glbl atom size too large"); + constexpr uint64_t minCheckSize = 10; + // 检查是否嵌套fiel atom + if (currentAtom.dataSize >= minCheckSize) { + uint8_t header[8]; + constexpr uint32_t typeOffset = 4; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read glbl header failed"); + uint32_t size = GetU32Value(&header[0]); + uint32_t type = GetU32Value(&header[typeOffset]); + if (type == FourccType("fiel") && size == currentAtom.dataSize) { + MP4Atom fielAtom = currentAtom; + fielAtom.type = type; + fielAtom.size = size; + fielAtom.dataSize = size - sizeof(header); + ctx->offset = ctx->dataOffset; + return ParseFiel(fielAtom, depth, ctx); + } + } + // 避免重复extradata + if (lastTrack_->codecParms.extradataSize > 1 && lastTrack_->codecParms.data) { + MEDIA_LOG_W("ignoring multiple glbl"); + ctx->offset += currentAtom.size; + return Status::OK; + } + // 读取glbl内容到extradata + auto extradata = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, extradata.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read glbl extradata failed"); + lastTrack_->codecParms.data = std::move(extradata); + lastTrack_->codecParms.extradataSize = currentAtom.dataSize; + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseStts(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr && lastTrack_->sampleHelper != nullptr, Status::ERROR_INVALID_DATA, + "Track or sampleHelper is nullptr"); + ctx->offset += currentAtom.size; + if (currentAtom.size > MIN_BOX_SIZE) { + Status ret = lastTrack_->sampleHelper->SetTimeToSampleParams(ctx->dataOffset, currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set time to sample params failed"); + } else { + MEDIA_LOG_W("Stts atom size is too small, skipping time to sample parsing"); + } + return Status::OK; +} + +Status MP4AtomParser::ParseStss(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr && lastTrack_->sampleHelper != nullptr, Status::ERROR_INVALID_DATA, + "Track or sampleHelper is nullptr"); + + ctx->offset += currentAtom.size; + Status ret = lastTrack_->sampleHelper->SetSyncSampleParams(ctx->dataOffset, currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set sync sample params failed"); + return ret; +} + +Status MP4AtomParser::ParseCtts(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr && lastTrack_->sampleHelper != nullptr, Status::ERROR_INVALID_DATA, + "Track or sampleHelper is nullptr"); + + ctx->offset += currentAtom.size; + Status ret = lastTrack_->sampleHelper->SetCompositionTimeToSampleParams(ctx->dataOffset, currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set composition time to sample params failed"); + return ret; +} + +Status MP4AtomParser::ParseStsc(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr && lastTrack_->sampleHelper != nullptr, Status::ERROR_INVALID_DATA, + "Track or sampleHelper is nullptr"); + ctx->offset += currentAtom.size; + if (currentAtom.size > MIN_BOX_SIZE) { + Status ret = lastTrack_->sampleHelper->SetSampleToChunkParams(ctx->dataOffset, currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set sample to chunk params failed"); + } else { + MEDIA_LOG_W("Stsc atom size is too small, skipping sample to chunk parsing"); + } + return Status::OK; +} + +Status MP4AtomParser::ParseStsz(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr && lastTrack_->sampleHelper != nullptr, Status::ERROR_INVALID_DATA, + "Track or sampleHelper is nullptr"); + ctx->offset += currentAtom.size; + constexpr int64_t minStszSize = 20; // stsz atom最小大小 + if (currentAtom.size >= minStszSize) { + Status ret = lastTrack_->sampleHelper->SetSampleSizeParams(currentAtom.type, ctx->dataOffset, + currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set sample size params failed"); + } else { + MEDIA_LOG_W("Stsz atom size is too small, skipping sample size parsing"); + } + return Status::OK; +} + +Status MP4AtomParser::ParseStco(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr && lastTrack_->sampleHelper != nullptr, Status::ERROR_INVALID_DATA, + "Track or sampleHelper is nullptr"); + ctx->offset += currentAtom.size; + if (currentAtom.size > MIN_BOX_SIZE) { + Status ret = lastTrack_->sampleHelper->SetChunkOffsetParams(currentAtom.type, ctx->dataOffset, + currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set chunk offset params failed"); + } else { + MEDIA_LOG_W("Stco atom size is too small, skipping chunk offset parsing"); + } + return Status::OK; +} + +Status MP4AtomParser::ParseFiel(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + ctx->offset += currentAtom.size; + + FieldOrder decodedFieldOrder = FieldOrder::FIELD_ORDER_UNKNOWN; + uint8_t fieldOrder[2]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, fieldOrder, sizeof(fieldOrder)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read field order failed"); + uint16_t fieldOrderValue = GetU16Value(fieldOrder); + if ((fieldOrderValue & 0xFF00) == 0X0100) { + decodedFieldOrder = FieldOrder::FIELD_ORDER_PROGRESSIVE; + } else if ((fieldOrderValue & 0xFF00) == 0X0200) { + switch (fieldOrderValue & 0xFF) { + case 0x01: + decodedFieldOrder = FieldOrder::FIELD_ORDER_TT; + break; + case 0x06: + decodedFieldOrder = FieldOrder::FIELD_ORDER_BB; + break; + case 0x09: + decodedFieldOrder = FieldOrder::FIELD_ORDER_TB; + break; + case 0x0E: + decodedFieldOrder = FieldOrder::FIELD_ORDER_BT; + break; + default: + MEDIA_LOG_E("Invalid field order: " PUBLIC_LOG_U16, fieldOrderValue); + return Status::ERROR_INVALID_DATA; + } + } + if (decodedFieldOrder == FieldOrder::FIELD_ORDER_UNKNOWN && fieldOrderValue != 0) { + MEDIA_LOG_E("Invalid field order: " PUBLIC_LOG_U16, fieldOrderValue); + return Status::ERROR_INVALID_DATA; + } + return ret; +} + +Status MP4AtomParser::ParseMvex(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_ONE, Status::ERROR_INVALID_PARAMETER, "Invalid mvex depth"); + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse mvex atom failed"); + return ret; +} + +Status MP4AtomParser::ParseTrex(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == MP4_DEPTH_TWO, Status::ERROR_INVALID_PARAMETER, "Invalid trex depth"); + ctx->offset += currentAtom.size; + static uint32_t trexIndex = 0; + // 根据索引找到对应的轨道 + auto track = firstTrack_; + for (uint32_t i = 0; i < trexIndex && track != nullptr; ++i) { + track = track->next; + } + if (track && track->sampleHelper) { + Status ret = track->sampleHelper->SetTrackExtendsParams(ctx->dataOffset, currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set track extends params failed"); + } else { + MEDIA_LOG_W("Track index " PUBLIC_LOG_U32 " not found or has no sampleHelper", trexIndex); + } + ++trexIndex; + return Status::OK; +} + +Status MP4AtomParser::ParseSidx(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(depth == 0, Status::ERROR_INVALID_DATA, "Invalid sidx depth"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_SIDX_SIZE, Status::ERROR_INVALID_DATA, "Invalid sidx atom size"); + + if (isDistributedSidx_) { + ctx->offset += currentAtom.size; + MEDIA_LOG_W("Distributed sidx, skip parse"); + return Status::OK; + } + + auto headerInfo = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, headerInfo.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read sidx header info failed"); + uint8_t version = headerInfo[0]; + uint32_t referenceId = GetU32Value(&headerInfo[4]); + auto currentTrack = firstTrack_; + while (currentTrack && static_cast(currentTrack->trackId) != referenceId) { + currentTrack = currentTrack->next; + } + uint32_t timeScale = GetU32Value(&headerInfo[8]); + FALSE_RETURN_V_MSG_E(timeScale > 0, Status::ERROR_INVALID_DATA, "Invalid time scale: " PUBLIC_LOG_U32, timeScale); + int64_t currentOffset = 12; // Skip version, flags, referenceId, and timeScale + + uint64_t firstOffset = 0; + constexpr uint64_t firstOffsetV1 = 20; + constexpr uint64_t firstOffsetV0 = 16; + if (version == 0) { + sidxCtx_.dts = GetU32Value(&headerInfo[currentOffset]); + firstOffset = GetU32Value(&headerInfo[firstOffsetV0]); + currentOffset += sizeof(uint64_t); + } else if (version == 1) { + sidxCtx_.dts = GetU64Value(&headerInfo[currentOffset]); + firstOffset = GetU64Value(&headerInfo[firstOffsetV1]); + currentOffset += sizeof(sidxCtx_.dts) + sizeof(firstOffset); + } + int64_t skipSize = 2; + sidxCtx_.firstMoofOffset = ctx->offset + currentAtom.size + firstOffset; + sidxCtx_.entryCount = GetU16Value(&headerInfo[currentOffset + skipSize]); + FALSE_RETURN_V_MSG_E(sidxCtx_.entryCount > 0, Status::ERROR_INVALID_DATA, "Invalid sidx entry count"); + currentOffset += sizeof(uint32_t); + constexpr uint32_t defaultEntrySize = 12; + if (currentAtom.dataSize < sidxCtx_.entryCount * defaultEntrySize) { + MEDIA_LOG_E("Invalid sidx data size: " PUBLIC_LOG_U64 ", expected at least " PUBLIC_LOG_U32, + currentAtom.dataSize, sidxCtx_.entryCount * defaultEntrySize); + return Status::ERROR_INVALID_DATA; + } + + ret = ParseSidxEntries(headerInfo.get(), currentOffset, sidxCtx_, currentTrack); + MEDIA_LOG_D("current track index" PUBLIC_LOG_U32 " sidx duration: " PUBLIC_LOG_U64, + currentTrack->trackIndex, currentTrack->sidxDuration); + ctx->offset += currentAtom.size; + hasSidxBox_ = true; + return ret; +} + +Status MP4AtomParser::ParseSidxEntries(const uint8_t* headerInfo, int64_t entryOffset, + SidxParseContext& sidxCtx, std::shared_ptr currentTrack) +{ + constexpr uint32_t defaultEntrySize = 12; + for (uint32_t i = 0; i < sidxCtx.entryCount; ++i) { + uint32_t size = GetU32Value(&headerInfo[entryOffset + defaultEntrySize * i]); + uint32_t duration = GetU32Value(&headerInfo[entryOffset + 4 + defaultEntrySize * i]); + if (size & 0x80000000) { + MEDIA_LOG_W("Sub sidx not supported"); + } + MEDIA_LOG_D("Entry " PUBLIC_LOG_U32 ": offset=" PUBLIC_LOG_D64 ", size=" + PUBLIC_LOG_U32 ", duration=" PUBLIC_LOG_U32, i, sidxCtx.firstMoofOffset, size, duration); + int64_t firstMoofOffset = sidxCtx.firstMoofOffset; + bool found = std::any_of(fragmentEntry_.begin(), fragmentEntry_.end(), + [firstMoofOffset](const FragmentEntry& entry) { + return entry.moofOffset == firstMoofOffset; + }); + if (!found) { + fragmentEntry_.emplace_back(FragmentEntry { + .firstDts = static_cast(sidxCtx.dts), + .moofOffset = sidxCtx.firstMoofOffset, + .duration = static_cast(duration), + .hasRead = false + }); + } + if (currentTrack) { + currentTrack->sidxDuration += duration; + } + sidxCtx.firstMoofOffset += size; + sidxCtx.dts += duration; + } + return Status::OK; +} + +Status MP4AtomParser::FindNextMoof(int64_t& headerOffset, uint32_t& atomSize) +{ + const uint32_t maxAtomSize = 4096; + Status ret = Status::OK; + int32_t atomType = 0; + uint32_t typeOffset = 4; + do { + uint8_t atomHeader[8]; + ret = dataReader_->ReadUintData(headerOffset, atomHeader, sizeof(atomHeader)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Find next moof header info failed"); + atomSize = GetU32Value(&atomHeader[0]); + atomType = static_cast(GetU32Value(&atomHeader[typeOffset])); + if (atomType != FourccType("moof")) { + MEDIA_LOG_D("not moof box, go find next"); + if (atomSize > sizeof(atomHeader) && atomSize < maxAtomSize) { + std::vector atomData(atomSize - sizeof(atomHeader)); + ret = dataReader_->ReadUintData(headerOffset + sizeof(atomHeader), atomData.data(), atomData.size()); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read atom data failed"); + } else if (atomSize >= maxAtomSize) { + return Status::ERROR_INVALID_DATA; + } + headerOffset += atomSize; + } + } while (atomType != FourccType("moof")); + return Status::OK; +} + +Status MP4AtomParser::UpdateFragmentEntryAndOffset(bool inEntry, uint32_t fragIndex, FragmentEntry& frag, + int64_t moofOffset, int64_t& currentOffset) +{ + if (inEntry) { + fragmentEntry_[fragIndex].hasRead = true; + } else { + while (fragIndex < fragmentEntry_.size() && moofOffset > fragmentEntry_[fragIndex].moofOffset) { + ++fragIndex; + } + frag.hasRead = true; + fragmentEntry_.insert(fragmentEntry_.begin() + fragIndex, frag); + } + + Status ret = Status::OK; + uint32_t typeOffset = 4; + if (seekable_ == Seekable::UNSEEKABLE) { + uint8_t atomHeader[8]; + uint32_t size = 0; + int32_t type = 0; + do { + ret = dataReader_->ReadUintData(currentOffset, atomHeader, sizeof(atomHeader)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read next atom header failed"); + size = GetU32Value(&atomHeader[0]); + type = static_cast(GetU32Value(&atomHeader[typeOffset])); + currentOffset += size; + } while (type != FourccType("mdat")); + fragmentEntry_[fragIndex].nextAtomOffset = currentOffset; + } + + if (!isDistributedSidx_ && seekable_ == Seekable::SEEKABLE) { + uint8_t atomHeader[8]; + ret = dataReader_->ReadUintData(currentOffset, atomHeader, sizeof(atomHeader)); + if (ret == Status::END_OF_STREAM) { + return Status::OK; + } + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read next atom header failed"); + uint32_t size = GetU32Value(&atomHeader[0]); + currentOffset += size; + ret = dataReader_->ReadUintData(currentOffset, atomHeader, sizeof(atomHeader)); + if (ret == Status::END_OF_STREAM) { + return Status::OK; + } + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read next atom header failed"); + // 检查下一个atom是否是sidx + if (GetU32Value(&atomHeader[0x04]) == FourccType("sidx")) { + isDistributedSidx_ = true; + MEDIA_LOG_D("File has distributed sidx, ignore them all"); + } + } + return Status::OK; +} + +Status MP4AtomParser::ParseMoof(const std::shared_ptr& track, int64_t offset) +{ + uint32_t atomSize = 0; + int64_t headerOffset = offset; + Status ret = FindNextMoof(headerOffset, atomSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Find next moof atom failed"); + hasMoofBox_ = true; + moofOffset_ = headerOffset; + + FragmentEntry frag; + frag.moofOffset = moofOffset_; + uint32_t fragIndex = 0; + bool inEntry = false; + if (!fragmentEntry_.empty()) { + // 如果已经有moof信息,直接返回 + auto it = std::find_if(fragmentEntry_.begin(), fragmentEntry_.end(), + [this](const FragmentEntry& entry) { return entry.moofOffset == moofOffset_; }); + if (it != fragmentEntry_.end()) { + inEntry = true; + fragIndex = std::distance(fragmentEntry_.begin(), it); + if (it->hasRead) { + MEDIA_LOG_D("Moof already parsed at offset: " PUBLIC_LOG_D64, moofOffset_); + return Status::OK; + } + } + } + if (firstTrack_ == nullptr) { + firstTrack_ = track; + } + uint32_t typeOffset = 4; + int64_t moofEndOffset = moofOffset_ + atomSize; + int64_t currentOffset = moofOffset_ + sizeof(uint64_t); // Skip size and type + auto currentTrack = firstTrack_; + while (currentOffset < moofEndOffset) { + MP4Atom atom; + uint8_t atomHeader[8]; + ret = dataReader_->ReadUintData(currentOffset, atomHeader, sizeof(atomHeader)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read moof atom header failed"); + atom.size = GetU32Value(&atomHeader[0]); + atom.type = static_cast(GetU32Value(&atomHeader[typeOffset])); + if (currentOffset + static_cast(atom.size) > moofEndOffset) { + MEDIA_LOG_E("Invalid moof atom size: " PUBLIC_LOG_U64 ", exceeds moof end offset: " PUBLIC_LOG_D64, + atom.size, moofEndOffset); + return Status::ERROR_INVALID_DATA; + } + atom.dataSize = atom.size - sizeof(atomHeader); + int64_t atomDataOffset = currentOffset + sizeof(atomHeader); // Skip size and type + std::string atomTypeStr = FourccToString(atom.type); + MEDIA_LOG_D("Processing box: " PUBLIC_LOG_S ", size: " PUBLIC_LOG_U64 " at offset " PUBLIC_LOG_D64, + atomTypeStr.c_str(), atom.dataSize, atomDataOffset); + switch (atom.type) { + case FourccType("mfhd"): { + uint8_t mfhdData[atom.dataSize]; + ret = dataReader_->ReadUintData(atomDataOffset, mfhdData, sizeof(mfhdData)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read data failed"); + currentOffset += atom.size; + break; + } + case FourccType("traf"): { + currentOffset += sizeof(atomHeader); + break; + } + case FourccType("tfhd"): { + currentOffset += atom.size; + ret = ProcessTfhdAtom(atom, atomDataOffset, currentTrack); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Process tfhd atom failed"); + break; + } + case FourccType("tfdt"): { + currentOffset += atom.size; + ret = currentTrack->sampleHelper->SetTrackFragmentDecodeTimeParams(atomDataOffset, atom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set track fragment decode time params failed"); + break; + } + case FourccType("trun"): { + currentOffset += atom.size; + int64_t sidxDts = inEntry ? fragmentEntry_[fragIndex].firstDts : -1; + int64_t moofDts = 0; + int64_t moofDuration = 0; + ret = currentTrack->sampleHelper->SetTrackFragmentRunParams(atomDataOffset, atom.dataSize, sidxDts, + moofDts, moofDuration); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set track run params failed"); + if (frag.firstDts == -1) { + frag.firstDts = moofDts; + } + if (frag.duration == -1) { + frag.duration = moofDuration; + } + break; + } + default: { + MEDIA_LOG_W("Unknown atom type: " PUBLIC_LOG_S, atomTypeStr.c_str()); + ret = Status::ERROR_INVALID_DATA; + break; + } + } + } + ret = UpdateFragmentEntryAndOffset(inEntry, fragIndex, frag, moofOffset_, currentOffset); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Update fragment entry and offset failed"); + return Status::OK; +} + +Status MP4AtomParser::ProcessTfhdAtom(const MP4Atom& atom, int64_t atomDataOffset, std::shared_ptr& currentTrack) +{ + MEDIA_LOG_D("extract tfhd"); + if (atomDataOffset < 0 || atom.dataSize < MIN_TFHD_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t tfhdHeader[8]; + Status ret = dataReader_->ReadUintData(atomDataOffset, tfhdHeader, sizeof(tfhdHeader)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + + uint32_t tfhdFlag = GetU32Value(&tfhdHeader[0]); + uint32_t trackId = GetU32Value(&tfhdHeader[4]); + + while (currentTrack && static_cast(currentTrack->trackId) != trackId) { + currentTrack = currentTrack->next; + } + if (!currentTrack || !currentTrack->sampleHelper) { + MEDIA_LOG_W("tfhd: TrackId " PUBLIC_LOG_U32 " not found or has no sampleHelper", trackId); + return Status::OK; + } + + int64_t tfhdDataOffset = atomDataOffset + sizeof(tfhdHeader); + int64_t dataSize = static_cast(atom.dataSize - sizeof(tfhdHeader)); + ret = currentTrack->sampleHelper->SetTrackFragmentHeaderParams(tfhdDataOffset, dataSize, tfhdFlag, moofOffset_); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set track fragment header params failed"); + + return Status::OK; +} + +Status MP4AtomParser::ParseUdta(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse udta container failed"); + return Status::OK; +} + +void MP4AtomParser::ParseLocationString(const std::string& locationStr) +{ + std::regex pattern(R"([+-]\d+\.\d+)"); + std::sregex_iterator numbers(locationStr.cbegin(), locationStr.cend(), pattern); + std::sregex_iterator end; + + const uint32_t minLocationNum = 2; // 至少需要两个数字:纬度和经度 + if (static_cast(std::distance(numbers, end)) >= minLocationNum) { + float latitude = std::stof(numbers->str()); + float longitude = std::stof((++numbers)->str()); + + mediaInfo_.general.SetData(Tag::MEDIA_LATITUDE, latitude); + mediaInfo_.general.SetData(Tag::MEDIA_LONGITUDE, longitude); + } else { + MEDIA_LOG_W("Invalid location format: " PUBLIC_LOG_S, locationStr.c_str()); + } +} + +std::string GetCoverMimeType(uint32_t dataType) +{ + switch (dataType) { + case 0x0D: + return MimeType::IMAGE_JPG; + case 0x0E: + return MimeType::IMAGE_PNG; + case 0x1B: + return MimeType::IMAGE_BMP; + default: + MEDIA_LOG_W("Unknown cover type: " PUBLIC_LOG_U32, dataType); + return ""; + } +} + +Status MP4AtomParser::ParseCover(MP4Atom currentAtom, uint32_t dataType, uint64_t stringSize, ParseContext* ctx) +{ + std::string mimeType = GetCoverMimeType(dataType); + if (mimeType.empty()) { + return Status::OK; // 不支持的封面类型,直接返回 + } + + auto coverData = std::make_unique(stringSize); + FALSE_RETURN_V_MSG_E(coverData != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate cover data buffer"); + Status ret = dataReader_->ReadUintData(ctx->dataOffset + 16, coverData.get(), stringSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read cover data failed"); + + auto coverTrack = std::make_shared(); + FALSE_RETURN_V_MSG_E(coverTrack != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate cover track"); + + coverTrack->trackIndex = trackCount_; + coverTrack->codecParms.trackType = ATTACHMENT_TYPE; + coverTrack->codecParms.extradataSize = stringSize; + + Meta coverMeta; + coverMeta.SetData(Tag::MIME_TYPE, mimeType); + + int32_t videoWidth = 0; + int32_t videoHeight = 0; + auto currentTrack = firstTrack_; + while (currentTrack) { + if (currentTrack->codecParms.trackType == VIDEO_TYPE && + IsValidTrackIndex(mediaInfo_, currentTrack->trackIndex)) { + if (mediaInfo_.tracks[currentTrack->trackIndex].GetData(Tag::VIDEO_WIDTH, videoWidth) && + mediaInfo_.tracks[currentTrack->trackIndex].GetData(Tag::VIDEO_HEIGHT, videoHeight)) { + break; + } + } + currentTrack = currentTrack->next; + } + std::vector cover(stringSize); + cover.assign(coverData.get(), coverData.get() + stringSize); + coverMeta.SetData(Tag::MEDIA_COVER, std::move(cover)); + mediaInfo_.tracks.emplace_back(std::move(coverMeta)); + + if (lastTrack_) { + lastTrack_->next = coverTrack; + } else { + firstTrack_ = coverTrack; + } + lastTrack_ = coverTrack; + return ret; +} + +Status MP4AtomParser::ParseUserDataString(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + std::string key = GetMetadataKey(currentAtom.type, ctx); + if (key.empty() && currentAtom.type != FourccType("covr")) { + std::string atomTypeStr = FourccToString(currentAtom.type); + MEDIA_LOG_W("Unknown metadata type: " PUBLIC_LOG_S, atomTypeStr.c_str()); + return Status::OK; + } + + std::string value; + Status ret = Status::OK; + if (ctx->founditunesMetadata && currentAtom.dataSize > DEFAULT_HEAD_SIZE) { + ret = ParseItunesMetadata(currentAtom, key, value, ctx); + } else { + ret = ParseTraditionalMetadata(currentAtom, value, ctx); + } + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse metadata string failed"); + + if (!value.empty()) { + SetMetadataValue(key, value); + } + return ret; +} + +std::string MP4AtomParser::GetMetadataKey(uint32_t atomType, ParseContext* ctx) +{ + // 如果是动态key(iTunes metadata with keys box) + if (ctx && ctx->foundHdlrMdta && !ctx->metaKeys.empty()) { + // atomType在iTunes格式中实际是大端序的索引,需要字节序转换 + uint32_t index = atomType; + if (index > 0 && index < ctx->metaKeysCount) { + std::string key = ctx->metaKeys[index]; + if (!key.empty()) { + MEDIA_LOG_D("Found dynamic key[" PUBLIC_LOG_U32 "]: " PUBLIC_LOG_S, index, key.c_str()); + return key; + } + } else if (atomType != (FourccType("covr"))) { + std::string atomTypeStr = FourccToString(atomType); + MEDIA_LOG_W("Dynamic key index out of range: " PUBLIC_LOG_S, atomTypeStr.c_str()); + } + } + + static std::map keyMap = { + { FourccType("\251nam"), "title" }, + { FourccType("\251ART"), "artist" }, // "\251ART" and "\251aut" refer the same value + { FourccType("\251aut"), "artist" }, + { FourccType("\251alb"), "album" }, + { FourccType("aART"), "album_artist" }, + { FourccType("\251day"), "date" }, + { FourccType("\251cmt"), "comment" }, + { FourccType("\251inf"), "comment" }, + { FourccType("\251gen"), "genre" }, + { FourccType("\251cpy"), "copyright" }, + { FourccType("cprt"), "copyright" }, + { FourccType("\251com"), "composer" }, + { FourccType("\251wrt"), "composer" }, + { FourccType("\251lyr"), "lyrics" }, + { FourccType("\251xyz"), "location" }, + { FourccType("desc"), "description" }, + }; + + auto it = keyMap.find(atomType); + return (it != keyMap.end()) ? it->second : ""; +} + +Status MP4AtomParser::ParseItunesMetadata(MP4Atom currentAtom, std::string& key, std::string& value, + ParseContext* ctx) +{ + uint8_t dataHeader[8]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, dataHeader, sizeof(dataHeader)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read iTunes metadata header failed"); + + uint32_t dataSize = GetU32Value(&dataHeader[0]); + uint32_t dataTag = GetU32Value(&dataHeader[4]); + constexpr uint32_t headerSize = 16; // size + type + if (dataTag == FourccType("data") && dataSize <= currentAtom.dataSize && dataSize >= headerSize) { + uint8_t typeInfo[8]; + ret = dataReader_->ReadUintData(ctx->dataOffset + sizeof(typeInfo), typeInfo, sizeof(typeInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read data type info failed"); + + uint32_t dataType = GetU32Value(&typeInfo[0]); + uint64_t stringSize = dataSize - headerSize; + if (currentAtom.type == FourccType("covr") || (!key.empty() && key == "com.apple.quicktime.artwork")) { + return ParseCover(currentAtom, dataType, stringSize, ctx); + } + ret = ReadMetadataString(ctx->dataOffset + headerSize, stringSize, dataType, value); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read iTunes metadata string failed"); + } else { + MEDIA_LOG_W("Invalid iTunes metadata format: dataTag=" PUBLIC_LOG_U32, dataTag); + } + + return Status::OK; +} + +Status MP4AtomParser::ParseTraditionalMetadata(MP4Atom currentAtom, std::string& value, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_TRADITIONAL_METADATA_SIZE, Status::ERROR_INVALID_DATA, + "Invalid traditional metadata size"); + uint8_t lengthData[2]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, lengthData, sizeof(lengthData)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read string length failed"); + + uint16_t stringLength = GetU16Value(lengthData); + constexpr uint16_t defaultSkipSize = 4; + if (stringLength > currentAtom.dataSize - defaultSkipSize) { + // 作为原始数据处理 + ret = ReadMetadataString(ctx->dataOffset, currentAtom.dataSize, 0, value); + } else { + // 跳过语言代码 + ret = ReadMetadataString(ctx->dataOffset + defaultSkipSize, stringLength, 0, value); + } + + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read traditional metadata string failed"); + return Status::OK; +} + +std::string ParseSignedInteger(uint8_t* data, uint64_t size) +{ + FALSE_RETURN_V_MSG_E(data != nullptr, "", "ParseSignedInteger: data is null"); + int32_t value = 0; + switch (size) { + case 0x01: + value = static_cast(data[0]); + break; + case 0x02: + value = static_cast(GetU16Value(data)); + break; + case 0x03: + value = static_cast((GetU32Value(data) & 0xFFFFFF00) >> 0x08); + if (value & 0x800000) { + value |= 0xFF000000; + } + break; + case 0x04: + value = static_cast(GetU32Value(data)); + break; + default: + value = 0; + break; + } + return std::to_string(value); +} + +std::string ParseUnsignedInteger(uint8_t* data, uint64_t size) +{ + FALSE_RETURN_V_MSG_E(data != nullptr, "", "ParseSignedInteger: data is null"); + uint32_t value = 0; + constexpr uint32_t BYTE_SHIFT_8 = 8; + switch (size) { + case 0x01: + value = data[0]; + break; + case 0x02: + value = GetU16Value(data); + break; + case 0x03: + value = (GetU32Value(data) >> BYTE_SHIFT_8) & 0xFFFFFF; + break; + case 0x04: + value = GetU32Value(data); + break; + default: + value = 0; + break; + } + return std::to_string(value); +} + +std::string DecodeMacString(const uint8_t* data, size_t size) +{ + FALSE_RETURN_V_MSG_E(data != nullptr || size > 0, "", "DecodeMacString: data is null"); + + std::string result; + result.reserve(size); + constexpr size_t asciiStart = 32; // ASCII 可打印字符的起始值 + constexpr size_t asciiEnd = 126; // ASCII 可打印字符的结束值 + + for (size_t i = 0; i < size; ++i) { + if (data[i] == 0) { + break; // 遇到空字符结束 + } + if (data[i] >= asciiStart && data[i] <= asciiEnd) { + // ASCII 可打印字符 + result += static_cast(data[i]); + } else { + // 非 ASCII 字符,可以选择跳过或转换 + result += '?'; + } + } + return result; +} + +void MP4AtomParser::SetUserDataByValueType(const std::string& key, const std::string& value) +{ + if (value.empty()) { + userFormat_->SetData(key, value); + return; + } + + size_t start = (value[0] == '-' || value[0] == '+') ? 1 : 0; + std::string_view numPart(value.data() + start, value.length() - start); + bool isInt = !numPart.empty() && std::all_of(numPart.begin(), numPart.end(), [](char c) { + return std::isdigit(static_cast(c)); + }); + + constexpr size_t maxValueLength = 10; + if (isInt && value.length() <= maxValueLength) { + int32_t intVal = std::atoi(value.c_str()); + userFormat_->SetData(key, intVal); + return; + } + + if (auto dotPos = value.find('.'); dotPos != std::string::npos) { + // 检查只有一个小数点,且其他都是数字 + bool isFloat = std::count(value.begin(), value.end(), '.') == 1 && + std::all_of(value.begin() + start, value.end(), [](char c) { + return std::isdigit(static_cast(c)) || c == '.'; + }); + if (isFloat) { + float floatVal = std::stof(value); + userFormat_->SetData(key, floatVal); + return; + } + } + userFormat_->SetData(key, value); +} + +void MP4AtomParser::SetMetadataValue(const std::string& key, const std::string& value) +{ + static const std::map keyToTagMap = { + {"title", Tag::MEDIA_TITLE}, + {"artist", Tag::MEDIA_ARTIST}, + {"album", Tag::MEDIA_ALBUM}, + {"album_artist", Tag::MEDIA_ALBUM_ARTIST}, + {"date", Tag::MEDIA_DATE}, + {"comment", Tag::MEDIA_COMMENT}, + {"genre", Tag::MEDIA_GENRE}, + {"copyright", Tag::MEDIA_COPYRIGHT}, + {"language", Tag::MEDIA_LANGUAGE}, + {"description", Tag::MEDIA_DESCRIPTION}, + {"lyrics", Tag::MEDIA_LYRICS}, + {"author", Tag::MEDIA_AUTHOR}, + {"composer", Tag::MEDIA_COMPOSER}, + {"creation_time", Tag::MEDIA_CREATION_TIME} + }; + + auto it = keyToTagMap.find(key); + if (it != keyToTagMap.end()) { + mediaInfo_.general.SetData(it->second, value); + } else if (key == "location") { + ParseLocationString(value); + } else { + if (userFormat_) { + SetUserDataByValueType(key, value); + } else { + MEDIA_LOG_E("userFormat_ is null, cannot set custom metadata: " PUBLIC_LOG_S, key.c_str()); + } + MEDIA_LOG_D("Custom metadata: " PUBLIC_LOG_S " = " PUBLIC_LOG_S, key.c_str(), value.c_str()); + } +} + +Status MP4AtomParser::ReadMetadataString(int64_t offset, uint64_t size, uint32_t dataType, std::string& value) +{ + if (size == 0 || size > MAX_METADATA_SIZE) { + return Status::OK; + } + + auto buffer = std::make_unique(size + 1); + FALSE_RETURN_V_MSG_E(buffer != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate metadata buffer"); + Status ret = dataReader_->ReadUintData(offset, buffer.get(), size); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read metadata string data failed"); + + buffer[size] = '\0'; + constexpr uint64_t minFloatSize = 4; + switch (dataType) { + case 0x15: // BE signed integer + value = ParseSignedInteger(buffer.get(), size); + break; + case 0x16: // BE unsigned integer + case 0x43: // BE unsigned integer (Apple specific) + value = ParseUnsignedInteger(buffer.get(), size); + break; + case 0x17: // BE float32 + if (size >= minFloatSize) { + uint32_t intValue = GetU32Value(buffer.get()); + float floatValue = *reinterpret_cast(&intValue); + value = std::to_string(floatValue); + } + break; + case 0x03: // MAC encoded + value = DecodeMacString(buffer.get(), size); + break; + default: // UTF-8 string + value = std::string(reinterpret_cast(buffer.get()), size); + break; + } + + return Status::OK; +} + +Status MP4AtomParser::ParseMeta(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + int64_t endOffset = 0; + if (__builtin_add_overflow(ctx->offset, currentAtom.size, &endOffset)) { + MEDIA_LOG_E("Container atom size overflow"); + return Status::ERROR_INVALID_DATA; + } + if (currentAtom.dataSize < sizeof(uint32_t) || depth == MP4_ROOT_DEPTH) { + // 数据太小,或者在root级别,直接跳过 + ctx->offset = endOffset; + return Status::OK; + } + // 检查是否直接以hdlr开始(检查前8字节是否为合理的hdlr header) + uint8_t testBytes[8]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, testBytes, sizeof(testBytes)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read meta test bytes failed"); + + uint32_t firstSize = GetU32Value(&testBytes[0]); + uint32_t firstType = GetU32Value(&testBytes[4]); + if (firstType == FourccType("hdlr") && firstSize > sizeof(testBytes) && firstSize <= currentAtom.dataSize) { + // 直接以hdlr开始,没有version/flags + ctx->offset = ctx->dataOffset; + MEDIA_LOG_D("Meta starts with hdlr, no version/flags"); + } else { + // 有version/flags,跳过4字节 + ctx->offset = ctx->dataOffset + sizeof(uint32_t); + MEDIA_LOG_D("Meta has version/flags, skipping 4 bytes"); + } + // 解析子atoms + while (ctx->offset < endOffset) { + ret = MP4ParseAtom(depth + 1, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Failed to parse child at offset " PUBLIC_LOG_D64, ctx->offset); + } + ctx->offset = endOffset; + return Status::OK; +} + +Status MP4AtomParser::ParseKeys(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= DEFAULT_HEAD_SIZE, Status::ERROR_INVALID_DATA, "Invalid atom size"); + ctx->offset += currentAtom.size; + // skip version and flags (4 bytes) + auto keysInfo = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, keysInfo.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read keys atom info failed"); + uint32_t keysCount = GetU32Value(&keysInfo[4]); + MEDIA_LOG_D("Parsing keys atom with count: " PUBLIC_LOG_U32, keysCount); + if (keysCount > UINT_MAX / sizeof(std::string) - 1) { + MEDIA_LOG_E("The 'keys' atom with the invalid key count: " PUBLIC_LOG_U32, keysCount); + return Status::ERROR_INVALID_DATA; + } + // 预分配空间,索引从1开始,0不使用 + ctx->metaKeys.resize(keysCount + 1); + ctx->metaKeysCount = keysCount + 1; + // 解析keys + int64_t keysOffset = 8; + for (uint32_t i = 1; i <= keysCount; ++i) { + uint32_t keySize = GetU32Value(&keysInfo[keysOffset]); + uint32_t keyType = GetU32Value(&keysInfo[keysOffset + sizeof(uint32_t)]); + if (keySize < sizeof(uint64_t)) { + MEDIA_LOG_E("Invalid key size: " PUBLIC_LOG_U32, keySize); + return Status::ERROR_INVALID_DATA; + } + keysOffset += sizeof(uint64_t); + uint32_t strLen = keySize - sizeof(uint64_t); + if (keyType != FourccType("mdta")) { + keysOffset += strLen; + continue; + } + if (strLen > 0) { + std::string keyStr(reinterpret_cast(&keysInfo[keysOffset]), strLen); + // 确保字符串以null结尾 + keyStr = keyStr.substr(0, keyStr.find('\0')); + ctx->metaKeys[i] = keyStr; + } else { + MEDIA_LOG_E("Invalid key string length: " PUBLIC_LOG_U32, strLen); + return Status::ERROR_INVALID_DATA; + } + keysOffset += strLen; + } + return Status::OK; +} + +Status MP4AtomParser::ParseIlst(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->founditunesMetadata = true; + Status ret = ParseContainerAtom(currentAtom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse ilst container failed"); + + ctx->founditunesMetadata = false; + return ret; +} + +Status MP4AtomParser::ParseLoci(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_LOCI_SIZE, Status::ERROR_INVALID_DATA, "loci atom too small"); + ctx->offset += currentAtom.size; + + auto lociInfo = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, lociInfo.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read loci atom info failed"); + + uint16_t languageCode = GetU16Value(&lociInfo[4]); + std::string language = ConvertLanguageCode(languageCode); + MEDIA_LOG_D("Parsing loci atom with language: " PUBLIC_LOG_S, language.c_str()); + + uint64_t offset = 6; // 跳过version+flags(4) + language(2) + uint64_t remainingLen = currentAtom.dataSize - offset; + + // 跳过地点名称字符串(以null结尾) + uint64_t placeStartOffset = offset; + while (offset < currentAtom.dataSize && lociInfo[offset] != 0) { + ++offset; + } + + FALSE_RETURN_V_MSG_E(offset < currentAtom.dataSize, Status::ERROR_INVALID_DATA, + "loci place name not null-terminated"); + // 提取地点名称 + std::string place; + if (offset > placeStartOffset) { + place = std::string(reinterpret_cast(&lociInfo[placeStartOffset]), + offset - placeStartOffset); + } + ++offset; // 跳过null terminator + remainingLen = currentAtom.dataSize - offset; + // 检查剩余数据是否足够(role(1) + coordinates(12) = 13字节) + if (remainingLen < 13) { + MEDIA_LOG_E("loci too short (" PUBLIC_LOG_U64 " bytes left, need at least 13)", remainingLen); + return Status::ERROR_INVALID_DATA; + } + ++offset; // 跳过role (1字节) + + int32_t longitudeRaw = static_cast(GetU32Value(&lociInfo[offset])); + int32_t latitudeRaw = static_cast(GetU32Value(&lociInfo[offset + 4])); + float longitude = longitudeRaw / static_cast(CONVERT_SCALE); + float latitude = latitudeRaw / static_cast(CONVERT_SCALE); + + constexpr int32_t precision = 4; + constexpr int32_t latitudeWidth = 8; + constexpr int32_t longitudeWidth = 9; + std::ostringstream locationStream; + locationStream << std::fixed << + std::setprecision(precision) << + std::showpos << + std::setfill('0') << + std::setw(latitudeWidth) << latitude << + std::setw(longitudeWidth) << longitude; + + std::string locationStr = locationStream.str(); + ParseLocationString(locationStr); + return ret; +} + +static const char* GetID3v1GenreString(uint8_t genreNumber) +{ + static constexpr std::array id3v1_genre_table = {{ + "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", + "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", + "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", + "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", + "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", + "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", + "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", + "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", + "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", + "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", + "Fast Fusion", "Bebop", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", + "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", + "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", + "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", + "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", + "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore Techno", + "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", + "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", + "Anime", "Jpop", "Synthpop", "Abstract", "Art Rock", // "Art Rock" is a genre conforming to ID3v1. + "Baroque", "Bhangra", "Big Beat", "Breakbeat", "Chillout", "Downtempo", "Dub", "EBM", "Eclectic", "Electro", + "Electroclash", "Emo", "Experimental", "Garage", "Global", "IDM", "Illbient", "Industro-Goth", "Jam Band", + "Krautrock", "Leftfield", "Lounge", "Math Rock", "New Romantic", "Nu-Breakz", "Post-Punk", "Post-Rock", + "Psytrance", "Shoegaze", "Space Rock", "Trop Rock", "World Music", "Neoclassical", "Audiobook", + "Audio Theatre", "Neue Deutsche Welle", "Podcast", "Indie Rock", "G-Funk", "Dubstep", "Garage Rock", "Psybient" + }}; + return (genreNumber < id3v1_genre_table.size()) ? id3v1_genre_table[genreNumber] : nullptr; +} + +Status MP4AtomParser::ParseGenre(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + // gnre atom应该至少有2字节数据 + if (currentAtom.dataSize >= 2) { + uint8_t genreData[2]; + Status ret = dataReader_->ReadUintData(ctx->dataOffset, genreData, 2); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read gnre data failed"); + + // 跳过第一个字节 + uint8_t genreNumber = genreData[1]; + if (genreNumber >= 1 && genreNumber <= ID3V1_GENRE_MAX + 1) { + const char* genreString = GetID3v1GenreString(genreNumber - 1); + if (genreString != nullptr) { + mediaInfo_.general.SetData(Tag::MEDIA_GENRE, std::string(genreString)); + } else { + MEDIA_LOG_W("Failed to get genre string for number: " PUBLIC_LOG_U8, genreNumber); + } + } else { + MEDIA_LOG_W("Genre number out of range: " PUBLIC_LOG_U8 " (valid: 1-192)", genreNumber); + } + } else { + MEDIA_LOG_W("gnre atom too small: " PUBLIC_LOG_U64 " bytes", currentAtom.dataSize); + } + return Status::OK; +} + +Status MP4AtomParser::ParseLabl(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseFrma(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + auto info = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, info.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read frma atom info failed"); + uint32_t originalFormat = GetU32Value(info.get()); + std::string originalFormatStr = FourccToString(originalFormat); + MEDIA_LOG_D("Parsing frma atom with original format: " PUBLIC_LOG_S, originalFormatStr.c_str()); + if (!isIsom_ && originalFormat == FourccType("alac")) { + ret = ParseAlacEntry(*ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse alac entry failed"); + } + return Status::OK; +} + +Status MP4AtomParser::ParseSchi(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseSchm(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseKind(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::ParseChan(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(lastTrack_ != nullptr, Status::ERROR_NULL_POINTER, "Current track is null"); + FALSE_RETURN_V_MSG_E(currentAtom.dataSize >= MIN_BOX_SIZE, Status::ERROR_INVALID_DATA, "Chan corrupted"); + const uint32_t chanEntrySize = 20; + ctx->offset += currentAtom.size; + auto chanData = std::make_unique(currentAtom.dataSize); + Status ret = dataReader_->ReadUintData(ctx->dataOffset, chanData.get(), currentAtom.dataSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read chan data failed"); + + const uint32_t entryOffset = 12; + uint32_t layoutTag = GetU32Value(&chanData[4]); + uint32_t bitmap = GetU32Value(&chanData[8]); + uint32_t entryNum = GetU32Value(&chanData[12]); + uint32_t currentOffset = entryOffset; + int64_t channelSets = 0; + int64_t channelLayoutMask = 0; + FALSE_RETURN_V(currentAtom.dataSize >= entryOffset + entryNum * chanEntrySize, Status::OK); + + for (uint32_t i = 0; i < entryNum; ++i) { + uint32_t channelLabel = GetU32Value(&chanData[currentOffset]); + currentOffset += chanEntrySize; + if (layoutTag == 0) { + int64_t channelSet = ReadChannelSet(channelLabel); + if (channelSet == 0) { + channelSets = 0; + break; + } + channelSets |= channelSet; + } + } + channelLayoutMask = layoutTag == 0 ? (channelSets ? channelSets : 0) : GetChannelLayoutByBitmap(layoutTag, bitmap); + if (channelLayoutMask) { + auto audioParser = std::make_shared(); + FALSE_RETURN_V_MSG_E(audioParser != nullptr, Status::ERROR_NO_MEMORY, "Failed create audioParser"); + AudioChannelLayout layout = audioParser->FindValidChannelLayout(channelLayoutMask); + if (layout == AudioChannelLayout::UNKNOWN) { + int32_t channelCount = 0; + FALSE_RETURN_V_MSG_E( + mediaInfo_.tracks[lastTrack_->trackIndex].GetData(Tag::AUDIO_CHANNEL_COUNT, channelCount), + Status::ERROR_INVALID_DATA, "Channel count missed"); + layout = audioParser->GetDefaultChannelLayout(channelCount); + } + mediaInfo_.tracks[lastTrack_->trackIndex].SetData(Tag::AUDIO_CHANNEL_LAYOUT, layout); + } + return Status::OK; +} + +Status MP4AtomParser::ParseChap(MP4Atom currentAtom, int32_t depth, ParseContext* ctx) +{ + ctx->offset += currentAtom.size; + return Status::OK; +} + +Status MP4AtomParser::MP4ParseHeader(const std::shared_ptr& source, Seekable seekable) +{ + FALSE_RETURN_V_MSG_E(source != nullptr, Status::ERROR_INVALID_PARAMETER, "DataSource is nullptr"); + dataSource_ = source; + seekable_ = seekable; + dataReader_ = std::make_shared(); + FALSE_RETURN_V_MSG_E(dataReader_ != nullptr, Status::ERROR_NO_MEMORY, "Failed create dataReader"); + dataReader_->SetDataReader(source); + Status ret = Status::OK; + uint64_t dataSourceSize = 0; + Status sizeRet = dataSource_->GetSize(dataSourceSize); + /* + * Unseekable时,解析完moov和找到mdat就退出解析 + * Seekable时,存在sidx box时只解析到第一个moof为止;若存在分散的sidx则解析完整个文件 + * Seekable时,没有sidx的fmp4解析所有moof,普通MP4解析到文件末尾 + */ + while ((seekable == Seekable::UNSEEKABLE && !(moovFound_ && hasMdatBox_)) || + (seekable == Seekable::SEEKABLE && (!(hasMoofBox_ && hasSidxBox_) || isDistributedSidx_)) || + (seekable == Seekable::SEEKABLE && hasMoofBox_ && !hasSidxBox_)) { + if (sizeRet == Status::OK && ctx_.offset >= static_cast(dataSourceSize)) { + MEDIA_LOG_D("Reached end of data source at offset: " PUBLIC_LOG_D64, ctx_.offset); + break; + } + int64_t originalOffset = ctx_.offset; + int32_t depth = 0; + ret = MP4ParseAtom(depth, &ctx_); + if (ret != Status::OK) { + MEDIA_LOG_E("Parse error occurred: " PUBLIC_LOG_D32, static_cast(ret)); + break; + } else if (ctx_.offset <= originalOffset) { + MEDIA_LOG_E("Parse header failed"); + return Status::ERROR_INVALID_PARAMETER; + } + } + FALSE_RETURN_V_MSG_E(moovFound_, Status::ERROR_INVALID_DATA, "MP4 parse failed"); + CalculateMP4Attributes(); + return ret; +} + +void MP4AtomParser::InitParseTable() +{ + // 填充解析函数表 + MP4ParseTable_[FourccType("ftyp")] = &MP4AtomParser::ParseFtyp; + MP4ParseTable_[FourccType("moov")] = &MP4AtomParser::ParseMoov; + MP4ParseTable_[FourccType("wide")] = &MP4AtomParser::ParseWide; + MP4ParseTable_[FourccType("mdat")] = &MP4AtomParser::ParseMdat; + MP4ParseTable_[FourccType("trak")] = &MP4AtomParser::ParseTrak; + MP4ParseTable_[FourccType("mvhd")] = &MP4AtomParser::ParseMvhd; + MP4ParseTable_[FourccType("tkhd")] = &MP4AtomParser::ParseTkhd; + MP4ParseTable_[FourccType("edts")] = &MP4AtomParser::ParseEdts; + MP4ParseTable_[FourccType("elst")] = &MP4AtomParser::ParseElst; + MP4ParseTable_[FourccType("tref")] = &MP4AtomParser::ParseTref; + MP4ParseTable_[FourccType("cdsc")] = &MP4AtomParser::ParseCdsc; + MP4ParseTable_[FourccType("mdia")] = &MP4AtomParser::ParseMdia; + MP4ParseTable_[FourccType("mdhd")] = &MP4AtomParser::ParseMdhd; + MP4ParseTable_[FourccType("hdlr")] = &MP4AtomParser::ParseHdlr; + MP4ParseTable_[FourccType("minf")] = &MP4AtomParser::ParseMinf; + MP4ParseTable_[FourccType("stbl")] = &MP4AtomParser::ParseStbl; + MP4ParseTable_[FourccType("stsd")] = &MP4AtomParser::ParseStsd; + MP4ParseTable_[FourccType("btrt")] = &MP4AtomParser::ParseBtrt; + MP4ParseTable_[FourccType("pasp")] = &MP4AtomParser::ParsePasp; + MP4ParseTable_[FourccType("esds")] = &MP4AtomParser::ParseEsds; + MP4ParseTable_[FourccType("avcC")] = &MP4AtomParser::ParseCodecConfig; + MP4ParseTable_[FourccType("hvcC")] = &MP4AtomParser::ParseCodecConfig; + MP4ParseTable_[FourccType("vvcC")] = &MP4AtomParser::ParseCodecConfig; + MP4ParseTable_[FourccType("d263")] = &MP4AtomParser::ParseCodecConfig; + MP4ParseTable_[FourccType("dfLa")] = &MP4AtomParser::ParseDfla; + MP4ParseTable_[FourccType("dOps")] = &MP4AtomParser::ParseDops; + MP4ParseTable_[FourccType("pcmC")] = &MP4AtomParser::ParsePcmc; + MP4ParseTable_[FourccType("enda")] = &MP4AtomParser::ParseEnda; + MP4ParseTable_[FourccType("colr")] = &MP4AtomParser::ParseColr; + MP4ParseTable_[FourccType("aclr")] = &MP4AtomParser::ParseAclr; + MP4ParseTable_[FourccType("glbl")] = &MP4AtomParser::ParseGlbl; + MP4ParseTable_[FourccType("stts")] = &MP4AtomParser::ParseStts; + MP4ParseTable_[FourccType("stss")] = &MP4AtomParser::ParseStss; + MP4ParseTable_[FourccType("ctts")] = &MP4AtomParser::ParseCtts; + MP4ParseTable_[FourccType("stsc")] = &MP4AtomParser::ParseStsc; + MP4ParseTable_[FourccType("stsz")] = &MP4AtomParser::ParseStsz; + MP4ParseTable_[FourccType("stz2")] = &MP4AtomParser::ParseStsz; + MP4ParseTable_[FourccType("stco")] = &MP4AtomParser::ParseStco; + MP4ParseTable_[FourccType("co64")] = &MP4AtomParser::ParseStco; + MP4ParseTable_[FourccType("fiel")] = &MP4AtomParser::ParseFiel; + MP4ParseTable_[FourccType("mvex")] = &MP4AtomParser::ParseMvex; + MP4ParseTable_[FourccType("trex")] = &MP4AtomParser::ParseTrex; + MP4ParseTable_[FourccType("sidx")] = &MP4AtomParser::ParseSidx; + MP4ParseTable_[FourccType("udta")] = &MP4AtomParser::ParseUdta; + MP4ParseTable_[FourccType("meta")] = &MP4AtomParser::ParseMeta; + MP4ParseTable_[FourccType("ilst")] = &MP4AtomParser::ParseIlst; + MP4ParseTable_[FourccType("loci")] = &MP4AtomParser::ParseLoci; + MP4ParseTable_[FourccType("labl")] = &MP4AtomParser::ParseLabl; + MP4ParseTable_[FourccType("wave")] = &MP4AtomParser::ParseWave; + MP4ParseTable_[FourccType("frma")] = &MP4AtomParser::ParseFrma; + MP4ParseTable_[FourccType("schi")] = &MP4AtomParser::ParseSchi; + MP4ParseTable_[FourccType("schm")] = &MP4AtomParser::ParseSchm; + MP4ParseTable_[FourccType("kind")] = &MP4AtomParser::ParseKind; + MP4ParseTable_[FourccType("chan")] = &MP4AtomParser::ParseChan; + MP4ParseTable_[FourccType("chap")] = &MP4AtomParser::ParseChap; +} + +Status MP4AtomParser::ParseAtomHeader(MP4Atom& atom, std::string& typeStr, int32_t depth, + bool &isZeroSizeAtom, ParseContext* ctx) +{ + uint8_t atomInfo[8]; + const uint32_t typeOffset = 4; // type在第5-8字节 + Status ret = dataReader_->ReadUintData(ctx->offset, atomInfo, sizeof(atomInfo)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read atom header info failed"); + + atom.size = GetU32Value(&atomInfo[0]); + atom.type = static_cast(GetU32Value(&atomInfo[typeOffset])); + typeStr = FourccToString(atom.type); + ctx->dataOffset = ctx->offset + sizeof(atomInfo); + + const uint64_t minExtendSize = 16; + if (atom.size == 1) { + uint8_t sizeData[8]; + ret = dataReader_->ReadUintData(ctx->dataOffset, sizeData, sizeof(sizeData)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read atom extended size failed"); + atom.size = GetU64Value(sizeData); + ctx->dataOffset += sizeof(sizeData); + if (atom.size < minExtendSize) { + MEDIA_LOG_E("Invalid extended atom size: " PUBLIC_LOG_U64, atom.size); + return Status::ERROR_INVALID_DATA; + } + } else if (atom.size == 0) { + if (depth == 0) { // 根级atoms + uint64_t dataSourceSize = 0; + ret = dataSource_->GetSize(dataSourceSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, Status::ERROR_INVALID_PARAMETER, "Failed to get data source size"); + atom.size = dataSourceSize - ctx->offset; + } else { + ctx->offset += sizeof(uint32_t); // 跳过size字段 + isZeroSizeAtom = true; + return Status::OK; + } + } + FALSE_RETURN_V_MSG_E(atom.size >= sizeof(atomInfo), Status::ERROR_INVALID_DATA, "Invalid atom size"); + return Status::OK; +} + +Status MP4AtomParser::MP4ParseAtom(int32_t depth, ParseContext* ctx) +{ + FALSE_RETURN_V_MSG_E(dataReader_ != nullptr && ctx != nullptr, Status::ERROR_INVALID_PARAMETER, + "Invalid params"); + FALSE_RETURN_V_MSG_E(ctx->offset >= 0 && depth <= MAX_DEPTH, Status::ERROR_INVALID_PARAMETER, + "Invalid offset or depth"); + + MP4Atom atom; + std::string atomTypeStr; + bool isZeroSizeAtom = false; + Status ret = ParseAtomHeader(atom, atomTypeStr, depth, isZeroSizeAtom, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read and parse atom header failed"); + FALSE_RETURN_V_MSG_E(!isZeroSizeAtom, Status::OK, "Zero size non-root atom, skip it"); + + int64_t atomEndOffset = -1LL; + if (__builtin_add_overflow(ctx->offset, atom.size, &atomEndOffset)) { + MEDIA_LOG_E("Atom size overflow at offset: " PUBLIC_LOG_D64, ctx->offset); + return Status::ERROR_INVALID_DATA; + } + PathAdder pathAdder(&ctx->path, atom.type); + atom.dataSize = atom.size - (ctx->dataOffset - ctx->offset); + FALSE_RETURN_V_MSG_E(atom.dataSize >= 0, Status::ERROR_INVALID_DATA, "Invalid atom data size"); + if (atom.type == FourccType("moof") && (isDistributedSidx_ || !hasSidxBox_ || !hasMoofBox_)) { + ret = ParseMoof(firstTrack_, ctx->offset); + } + // 使用函数指针查找和调用解析函数 + ParseFunction parseFunc = FindAtomParser(atom, ctx); + if (parseFunc) { + ret = (this->*parseFunc)(atom, depth, ctx); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse atom failed for type: " PUBLIC_LOG_S, atomTypeStr.c_str()); + } else { + // 无法识别的atom类型,跳过 + ctx->offset += atom.size; + } + return ret; +} + +auto MP4AtomParser::FindAtomParser(const MP4Atom& atom, ParseContext* ctx) -> ParseFunction +{ + // 首先查找静态解析函数表 + auto it = MP4ParseTable_.find(atom.type); + if (it != MP4ParseTable_.end()) { + return it->second; + } + // 查找动态解析函数 + if (ctx->path.size() >= DIRECT_META_PATH_SIZE) { + // 获取父级atom类型 - path中倒数第二个元素 + uint32_t parentType = ctx->path[ctx->path.size() - DIRECT_META_PATH_SIZE]; + if (parentType == FourccType("udta") || parentType == FourccType("ilst")) { + if (atom.type == FourccType("gnre")) { + return &MP4AtomParser::ParseGenre; + } else { + return &MP4AtomParser::ParseUserDataString; + } + } else if (parentType == FourccType("meta") && atom.type == FourccType("keys") && + ctx->foundHdlrMdta && ctx->metaKeysCount == 0) { + return &MP4AtomParser::ParseKeys; + } + } + return nullptr; +} + +void MP4AtomParser::CalculateMP4Attributes() +{ + MEDIA_LOG_D("In"); + for (auto track = firstTrack_; track; track = track->next) { + if (track->codecParms.trackType == VIDEO_TYPE) { + CalculateVideoFrameRate(track); + mediaInfo_.tracks[track->trackIndex].Set(0); + } + if (track->codecParms.trackType != ATTACHMENT_TYPE) { + CalculateTrackBitrates(track); + } + SetTrackType(track); + } + + SetFileDuration(); + SetStartTime(); + FileType fileType; + if (!mediaInfo_.general.GetData(Tag::MEDIA_FILE_TYPE, fileType)) { + MEDIA_LOG_D("MEDIA_FILE_TYPE not found, set default value"); + mediaInfo_.general.SetData(Tag::MEDIA_FILE_TYPE, FileType::MP4); // 设置MEDIA_FILE_TYPE默认值 + } +} + +void MP4AtomParser::SetTrackType(std::shared_ptr track) +{ + TagType tagType; + switch (track->codecParms.trackType) { + case VIDEO_TYPE: + tagType = Tag::MEDIA_HAS_VIDEO; + break; + case AUDIO_TYPE: + tagType = Tag::MEDIA_HAS_AUDIO; + break; + case SUBTITLE_TYPE: + tagType = Tag::MEDIA_HAS_SUBTITLE; + break; + case TIMEDMETA_TYPE: + tagType = Tag::MEDIA_HAS_TIMEDMETA; + break; + default: + MEDIA_LOG_W("Unknown track type for track " PUBLIC_LOG_U32, track->trackIndex); + return; // 不处理未知类型 + } + mediaInfo_.general.SetData(tagType, true); +} + +void MP4AtomParser::SetFileDuration() +{ + int64_t fileDuration = 0; + if (mediaInfo_.general.GetData(Tag::MEDIA_DURATION, fileDuration) && fileDuration > 0) { + MEDIA_LOG_D("File duration already set: " PUBLIC_LOG_D64 " us", fileDuration); + return; // 已有duration,直接跳过 + } + + int64_t maxDuration = 0; + uint32_t maxDurationTrackIndex = 0; + maxDuration = FindMaxTrackDuration(maxDuration, maxDurationTrackIndex); + if (maxDuration <= 0) { + MEDIA_LOG_W("No valid track durations found, setting default duration to 0"); + maxDuration = 0; // 如果没有有效的track duration,设置为0 + } else { + MEDIA_LOG_D("Max track duration: " PUBLIC_LOG_D64 " us (from track " PUBLIC_LOG_U32 ")", + maxDuration, maxDurationTrackIndex); + } + + // 设置文件的最终duration + mediaInfo_.general.SetData(Tag::MEDIA_DURATION, maxDuration); + MEDIA_LOG_D("Final file duration: " PUBLIC_LOG_D64 " us (from track " PUBLIC_LOG_U32 ")", + maxDuration, maxDurationTrackIndex); +} + +int64_t MP4AtomParser::FindMaxTrackDuration(int64_t& maxDuration, uint32_t& maxDurationTrackIndex) +{ + FileType fileType = FileType::MP4; + mediaInfo_.general.GetData(Tag::MEDIA_FILE_TYPE, fileType); + bool isRawFile = (fileType == FileType::M4A); // M4V 和 M4A文件通常标记为M4A类型 + + for (auto track = firstTrack_; track; track = track->next) { + if (track->codecParms.trackType == ATTACHMENT_TYPE) { + continue; + } + double trackDurationRaw = CalculateDuration(track, isRawFile); + if (trackDurationRaw <= 0) { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has no valid sample data", track->trackIndex); + continue; + } + + int64_t trackDuration = static_cast(std::round(trackDurationRaw)); + if (trackDuration > maxDuration) { + maxDuration = trackDuration; + maxDurationTrackIndex = track->trackIndex; + } + } + + return (maxDuration > 0) ? maxDuration : 0; +} + +double MP4AtomParser::CalculateDuration(std::shared_ptr track, bool isRawFile) +{ + FALSE_RETURN_V_MSG_E(track != nullptr, 0.0, "Track is null"); + FALSE_RETURN_V_MSG_E(track->sampleHelper != nullptr, 0.0, "SampleHelper is null"); + FALSE_RETURN_V_MSG_E(!track->sampleHelper->sampleIndexEntry_.empty(), 0.0, "Sample data is empty"); + + int32_t timeScale = 0; + if (!mediaInfo_.tracks[track->trackIndex].GetData(Tag::MEDIA_TIME_SCALE, timeScale) || timeScale <= 0) { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has invalid time scale", track->trackIndex); + return 0.0; + } + + int64_t firstPts = track->sampleHelper->sampleIndexEntry_.front().pts; + int64_t baseDuration = 0; + + if (!hasMoofBox_) { + // 普通MP4文件,使用sampleHelper的duration, mdhd中记录的不可靠 + baseDuration = track->sampleHelper->totalDuration_; + } else if (hasSidxBox_ && track->sidxDuration > 0) { + // FMP4文件有sidx box,使用sidx中的duration + baseDuration = track->sidxDuration; + } else { + // FMP4文件没有sidx box,使用trun duration + baseDuration = track->sampleHelper->totalTrunDuration_; + } + + // 根据文件类型计算最终duration + int64_t totalDuration = baseDuration + track->elstInitEmptyEdit; + if (!isRawFile) { + totalDuration += firstPts; + } + int64_t durationUs = 0; + if (__builtin_mul_overflow(totalDuration, S_TO_US, &durationUs)) { + MEDIA_LOG_E("Mul overflow: [" PUBLIC_LOG_D64 "," PUBLIC_LOG_D64 "]", totalDuration, S_TO_US); + } + return static_cast(durationUs) / timeScale; +} + +void MP4AtomParser::SetStartTime() +{ + int32_t minStartTime = INT32_MAX; + bool hasValidStartTime = false; + for (auto track = firstTrack_; track; track = track->next) { + if (track->codecParms.trackType == ATTACHMENT_TYPE) { + continue; + } + int64_t trackStartTime = 0; + if (track->sampleHelper && track->sampleHelper->sampleIndexEntry_.size() > 0 && + track->elstInitEmptyEdit == 0) { + int64_t firstPts = track->sampleHelper->sampleIndexEntry_.front().pts; + int32_t timeScale = 0; + if (mediaInfo_.tracks[track->trackIndex].GetData(Tag::MEDIA_TIME_SCALE, timeScale) && timeScale > 0) { + FALSE_RETURN_MSG( + ConvertTimeScale(firstPts, S_TO_US, timeScale, &trackStartTime) == Status::OK, "Mul overflow"); + } + mediaInfo_.tracks[track->trackIndex].SetData(Tag::MEDIA_START_TIME, trackStartTime); + } + if (trackStartTime < minStartTime) { + minStartTime = trackStartTime; + hasValidStartTime = true; + } + } + int64_t containerStartTime = hasValidStartTime ? minStartTime : 0; + mediaInfo_.general.SetData(Tag::MEDIA_CONTAINER_START_TIME, containerStartTime); +} + +double MP4AtomParser::MapToStandardRate(double rawRate, double maxErrorPercent) +{ + double bestRate = rawRate; + double minError = std::numeric_limits::max(); + + for (const auto& rate : standardRates_) { + double error = std::abs(rawRate - rate.value) / rate.value * 100.0; + if (error < minError && error <= maxErrorPercent) { + minError = error; + bestRate = rate.value; + } + } + + return bestRate; +} + +double MP4AtomParser::GetStandardFrameRate(double rawRate) +{ + constexpr double precisionMultiplier = 1000.0; // 保留3位小数的乘数 + constexpr double precisePercent = 100.0; + constexpr double tolerancePercent = 5.0; // 误差容忍度百分比 + + // 首先尝试范围映射 + for (const auto& range : ranges_) { + if (rawRate >= range.min && rawRate <= range.max) { + return range.target; + } + } + // 如果范围映射失败,使用精确匹配(误差容忍度5%) + double mappedRate = MapToStandardRate(rawRate, tolerancePercent); + if (rawRate != 0) { + if (std::abs(mappedRate - rawRate) / rawRate * precisePercent <= tolerancePercent) { + return mappedRate; + } + } + // 都失败则返回原始值(四舍五入到小数点后3位) + return std::round(rawRate * precisionMultiplier) / precisionMultiplier; +} + +MediaInfo MP4AtomParser::GetMediaInfo() const +{ + return mediaInfo_; +} + +std::shared_ptr MP4AtomParser::GetFirstTrack() const +{ + return firstTrack_; +} + +std::shared_ptr MP4AtomParser::GetUserFormat() const +{ + return userFormat_; +} + +void MP4AtomParser::GetFragmentEntry(std::vector& entry) +{ + fragmentEntry_.swap(entry); +} + +void MP4AtomParser::SetDataReader(const std::shared_ptr dataReader) +{ + dataReader_ = dataReader; +} + +void MP4AtomParser::SetSeekable(const Seekable seekable) +{ + seekable_ = seekable; +} + +bool MP4AtomParser::GetHasSidx() +{ + return hasSidxBox_; +} + +bool MP4AtomParser::GetHasMoof() +{ + return hasMoofBox_; +} + +constexpr std::array mdhd_language_map = {{ + "eng", /* 0 English */ "fra", /* 1 French */ "ger", /* 2 German */ + "ita", /* 3 Italian */ "dut", /* 4 Dutch */ "sve", /* 5 Swedish */ + "spa", /* 6 Spanish */ "dan", /* 7 Danish */ "por", /* 8 Portuguese */ + "nor", /* 9 Norwegian */ "heb", /* 10 Hebrew */ "jpn", /* 11 Japanese */ + "ara", /* 12 Arabic */ "fin", /* 13 Finnish */ "gre", /* 14 Greek */ + "ice", /* 15 Icelandic */ "mlt", /* 16 Maltese */ "tur", /* 17 Turkish */ + "hr ", /* 18 Croatian */ "chi", /* 19 Traditional Chinese */ "urd", /* 20 Urdu */ + "hin", /* 21 Hindi */ "tha", /* 22 Thai */ "kor", /* 23 Korean */ + "lit", /* 24 Lithuanian */ "pol", /* 25 Polish */ "hun", /* 26 Hungarian */ + "est", /* 27 Estonian */ "lav", /* 28 Latvian */ "smi", /* 29 Sami */ + "fo ", /* 30 Faroese */ "per", /* 31 Farsi */ "rus", /* 32 Russian */ + "chi", /* 33 Simplified Chinese */ "", /* 34 Flemish */ "iri", /* 35 Irish */ + "alb", /* 36 Albanian */ "ron", /* 37 Romanian */ "ces", /* 38 Czech */ + "slk", /* 39 Slovak */ "slv", /* 40 Slovenian */ "yid", /* 41 Yiddish */ + "sr ", /* 42 Serbian */ "mac", /* 43 Macedonian */ "bul", /* 44 Bulgarian */ + "ukr", /* 45 Ukrainian */ "bel", /* 46 Belarusian */ "uzb", /* 47 Uzbek */ + "kaz", /* 48 Kazakh */ "aze", /* 49 Azerbaijani */ "aze", /* 50 AzerbaijanAr */ + "arm", /* 51 Armenian */ "geo", /* 52 Georgian */ "mol", /* 53 Moldavian */ + "kir", /* 54 Kirghiz */ "tgk", /* 55 Tajiki */ "tuk", /* 56 Turkmen */ + "mon", /* 57 Mongolian */ "", /* 58 MongolianCyr */ "pus", /* 59 Pashto */ + "kur", /* 60 Kurdish */ "kas", /* 61 Kashmiri */ "snd", /* 62 Sindhi */ + "tib", /* 63 Tibetan */ "nep", /* 64 Nepali */ "san", /* 65 Sanskrit */ + "mar", /* 66 Marathi */ "ben", /* 67 Bengali */ "asm", /* 68 Assamese */ + "guj", /* 69 Gujarati */ "pa ", /* 70 Punjabi */ "ori", /* 71 Oriya */ + "mal", /* 72 Malayalam */ "kan", /* 73 Kannada */ "tam", /* 74 Tamil */ + "tel", /* 75 Telugu */ "sin", /* 76 Sinhala */ "bur", /* 77 Burmese */ + "khm", /* 78 Khmer */ "lao", /* 79 Lao */ "vie", /* 80 Vietnamese */ + "ind", /* 81 Indonesian */ "tgl", /* 82 Tagalog */ "may", /* 83 MalayRoman */ + "may", /* 84 MalayArabic */ "amh", /* 85 Amharic */ "tir", /* 86 Galla */ + "orm", /* 87 Oromo */ "som", /* 88 Somali */ "swa", /* 89 Swahili */ + "kin", /* 90 Kinyarwanda */ "run", /* 91 Rundi */ "nya", /* 92 Nyanja */ + "mlg", /* 93 Malagasy */ "epo", /* 94 Esperanto */ "", /* 95 */ + "", /* 96 */ "", /* 97 */ "", /* 98 */ + "", /* 99 */ "", /* 100 */ "", /* 101 */ + "", /* 102 */ "", /* 103 */ "", /* 104 */ + "", /* 105 */ "", /* 106 */ "", /* 107 */ + "", /* 108 */ "", /* 109 */ "", /* 110 */ + "", /* 111 */ "", /* 112 */ "", /* 113 */ + "", /* 114 */ "", /* 115 */ "", /* 116 */ + "", /* 117 */ "", /* 118 */ "", /* 119 */ + "", /* 120 */ "", /* 121 */ "", /* 122 */ + "", /* 123 */ "", /* 124 */ "", /* 125 */ + "", /* 126 */ "", /* 127 */ "wel", /* 128 Welsh */ + "baq", /* 129 Basque */ "cat", /* 130 Catalan */ "lat", /* 131 Latin */ + "que", /* 132 Quechua */ "grn", /* 133 Guarani */ "aym", /* 134 Aymara */ + "tat", /* 135 Tatar */ "uig", /* 136 Uighur */ "dzo", /* 137 Dzongkha */ + "jav", /* 138 JavaneseRom */ +}}; + +std::string MP4AtomParser::ConvertLanguageCode(uint16_t langcode) +{ + constexpr int32_t stringLength = 4; + if (langcode >= 0x0400 && langcode != 0x7FFF) { + constexpr int16_t langLength = 3; + std::array language{}; + uint16_t code = langcode; + // 从低位到高位提取3个5位字段 + for (int32_t i = 2; i >= 0; i--) { + language[i] = static_cast(0x60 + (code & 0x1F)); + code >>= 0x05; + } + return std::string(language.data(), langLength); + } + if (langcode < mdhd_language_map.size()) { + std::string_view lang_view = mdhd_language_map[langcode]; + if (!lang_view.empty()) { + std::string result(lang_view); + result.erase(std::find_if(result.rbegin(), result.rend(), + [](uint8_t ch) { return !std::isspace(ch); }).base(), result.end()); + return result; + } + } + return "und"; +} +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_box_parser.h b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_box_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..28852112ee4341012a81e6f48129707ad46e7504 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_box_parser.h @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2025 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 MP4_BOX_PARSER_H +#define MP4_BOX_PARSER_H + +#include +#include +#include +#include +#include "mp4_audio_parser.h" +#include "mp4_sample_helper.h" +#include "plugin/plugin_definition.h" +#include "plugin/plugin_info.h" + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { + +class MP4DemuxerPlugin; + +class MP4AtomParser { +public: + explicit MP4AtomParser(); + ~MP4AtomParser(); + struct CodecParams { + std::unique_ptr data = nullptr; + int64_t extradataSize = 0; + TrackType trackType = INVALID_TYPE; + + CodecParams() = default; + }; + + struct Track { + std::shared_ptr next = nullptr; + uint32_t trackIndex = 0; + int32_t trackId = 0; + std::shared_ptr sampleHelper = nullptr; + std::unique_ptr displayMatrix = nullptr; // 3x3 display matrix (9个int32_t), 按行存储 + bool hasDisplayMatrix = false; // 是否有有效的display matrix + uint32_t currentSampleIndex = 0; + int64_t elstInitEmptyEdit = 0; + int64_t elstShiftStartTime = 0; + int64_t sidxDuration = 0; // sidx box的dts + CodecParams codecParms{}; + + Track() = default; + }; + + struct FragmentEntry { + int64_t firstDts = -1; + int64_t moofOffset; + int64_t nextAtomOffset = -1; + int64_t duration = -1; + bool hasRead = false; + }; + + MediaInfo GetMediaInfo() const; + std::shared_ptr GetFirstTrack() const; + std::shared_ptr GetUserFormat() const; + void GetFragmentEntry(std::vector& entry); + static double MapToStandardRate(double rawRate, double maxErrorPercent); + double GetStandardFrameRate(double rawRate); + void SetDataReader(const std::shared_ptr dataReader); + void SetSeekable(const Seekable seekable); + bool GetHasSidx(); + bool GetHasMoof(); + Status MP4ParseHeader(const std::shared_ptr& source, Seekable seekable); + Status ParseMoof(const std::shared_ptr& track, int64_t offset); +private: + std::shared_ptr firstTrack_; + std::shared_ptr lastTrack_; + + enum ColorRange : uint32_t { + UNSPECIFIED = 0, + MPEG = 1, + JPEG = 2, + NB = 3, + }; + + struct MP4Atom { + int32_t type = 0; + uint64_t size = 0; + uint64_t dataSize = 0; + }; + + struct ParseContext { + int64_t offset = 0; // 当前解析位置 + int64_t dataOffset = 0; // 数据偏移位置 + std::set compatibleBrands; + std::vector path; + std::vector metaKeys; + int32_t movieDisplayMatrix[3][3] = {{0}}; // movie level 3x3显示变换矩阵 + uint32_t metaKeysCount = 0; + bool hasMovieDisplayMatrix = false; // 是否有有效的movie显示矩阵 + bool foundHdlrMdta = false; + bool founditunesMetadata = false; + }; + ParseContext ctx_; + + struct SidxParseContext { + int64_t firstMoofOffset = 0; + uint64_t dts = 0; + uint32_t entryCount = 0; + }; + SidxParseContext sidxCtx_; + + struct FrameRate { + double value; + std::string name; + }; + + struct FrameRateRange { + double min; + double max; + double target; + const char* name; + }; + static const std::vector standardRates_; + static const std::vector ranges_; + + uint32_t trackCount_ = 0; + int64_t moofOffset_ = 0; + using ParseFunction = Status (MP4AtomParser::*)(MP4Atom atom, int32_t depth, ParseContext* ctx); + ParseFunction FindAtomParser(const MP4Atom& atom, ParseContext* ctx); + std::unordered_map MP4ParseTable_; + std::vector fragmentEntry_; + std::shared_ptr dataSource_ {nullptr}; + std::shared_ptr dataReader_ {nullptr}; + bool isIsom_; + bool hasMdatBox_; + bool hasSidxBox_; + bool isDistributedSidx_; + bool hasMoofBox_; + bool moovFound_; + MediaInfo mediaInfo_; + Seekable seekable_; + std::shared_ptr userFormat_ = nullptr; + void InitParseTable(); + void SetCodecConfig(std::shared_ptr track); + bool IsValidTrackIndex(const MediaInfo& mediaInfo, uint32_t index); + bool UnderMetaPath(const std::vector& path, int32_t depth); + + // Display matrix 相关辅助函数 + void CalculateSampleAspectRatio(std::shared_ptr track, uint32_t width, uint32_t height); + void SetTrackDisplayMatrix(std::shared_ptr track, const int32_t resultMatrix[3][3], + uint32_t width, uint32_t height); + Status ParseDisplayMatrix(const uint8_t* data, int32_t matrix[3][3]); + // 视频方向解析相关辅助函数 + void ParseOrientationFromMatrix(std::shared_ptr track); + void ParseRotationTypeFromMatrix(std::shared_ptr track); + // 计算流信息相关辅助函数 + void CalculateVideoFrameRate(std::shared_ptr track); + void CalculateTrackBitrates(std::shared_ptr track); + void CalculateMP4Attributes(); + void SetTrackType(std::shared_ptr track); + void SetFileDuration(); + void SetStartTime(); + int64_t GetTrackDuration(std::shared_ptr track); + int64_t FindMaxTrackDuration(int64_t& maxDuration, uint32_t& maxDurationTrackIndex); + double CalculateDuration(std::shared_ptr track, bool isRawFile); + // 元数据解析辅助函数 + std::string ConvertLanguageCode(uint16_t langcode); + std::string GetMetadataKey(uint32_t atomType, ParseContext* parseCtx); + void SetMetadataValue(const std::string& key, const std::string& value); + void ParseLocationString(const std::string& locationStr); + void SetUserDataByValueType(const std::string& key, const std::string& value); + // 音频属性相关辅助函数 + bool ParseDecoderConfigDescriptor(const uint8_t* data, uint64_t dataSize, uint64_t& offset); + bool ParseAACConfig(const uint8_t* data, uint32_t size, uint32_t& sampleRate, uint32_t& channels, + AudioChannelLayout &layout); + bool ParseAACSpecificConfig(const uint8_t* data, uint32_t size); + bool ParseFLACExtradata(const uint8_t* data); + bool ParseOPUSExtradata(const uint8_t* data, uint64_t dataSize); + void SetAmrAudioAttribute(std::string mime); + void SetMpegAudioAttribute(); + void SetAudioMimeTypeAndSampleSize(const MP4Atom& currentAtom, uint16_t& sampleSize); + void SetRawPCMSampleFormat(const MP4Atom& currentAtom, uint16_t& sampleSize); + int32_t GetRawPCMBitsPerSample(const MP4Atom& currentAtom, uint16_t& sampleSize); + int64_t GetChannelLayoutByBitmap(uint32_t tag, uint32_t bitmap); + Status SetVorbisAudioAttribute(); + Status SetAudioAttribute(); + + Status ParseAtomHeader(MP4Atom& atom, std::string& typeStr, int32_t depth, bool &isZeroSizeAtom, + ParseContext* ctx); + Status ProcessMetaHandler(MP4Atom currentAtom, ParseContext* parseCtx); + Status ProcessTfhdAtom(const MP4Atom& atom, int64_t atomDataOffset, std::shared_ptr& currentTrack); + // 解析函数 + Status ParseContainerAtom(const MP4Atom& containerAtom, int32_t depth, ParseContext* ctx); + Status ParseAudioAttributes(MP4Atom currentAtom, ParseContext* parseCtx); + Status ParseCover(MP4Atom currentAtom, uint32_t dataType, uint64_t stringSize, ParseContext* parseCtx); + Status ParseItunesMetadata(MP4Atom currentAtom, std::string& key, std::string& value, ParseContext* parseCtx); + Status ParseTraditionalMetadata(MP4Atom currentAtom, std::string& value, ParseContext* parseCtx); + Status ReadMetadataString(int64_t offset, uint64_t size, uint32_t dataType, std::string& value); + Status ParseAlacEntry(ParseContext& ctx); + Status ParseIccProfile(int64_t offset, uint64_t dataSize); + Status ParseColorParams(int64_t offset, uint64_t dataSize, const std::string& colorType, ParseContext* parseCtx); + Status ParseSidxEntries(const uint8_t* headerInfo, int64_t entryOffset, + SidxParseContext& sidxCtx, std::shared_ptr currentTrack); + Status FindNextMoof(int64_t& headerOffset, uint32_t& atomSize); + Status UpdateFragmentEntryAndOffset(bool inEntry, uint32_t fragIndex, FragmentEntry& frag, + int64_t moofOffset, int64_t& currentOffset); + Status MP4ParseAtom(int32_t depth, ParseContext* ctx); + Status ParseFtyp(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMoov(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseWide(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMdat(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMvhd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseTrak(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseTkhd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseEdts(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseElst(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseTref(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseCdsc(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMdia(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMdhd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseHdlr(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMinf(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStbl(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStsd(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseVideoSampleEntry(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseCodecConfig(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseAudioSampleEntry(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseSubtitleSampleEntry(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMebx(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseBtrt(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParsePasp(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseEsds(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseDfla(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseDops(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParsePcmc(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseEnda(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseColr(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseAclr(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseGlbl(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStts(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStss(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStsc(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStsz(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseStco(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseCtts(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseFiel(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMvex(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseTrex(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseSidx(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseUserDataString(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseUdta(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseMeta(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseKeys(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseIlst(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseLoci(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseGenre(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseLabl(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseWave(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseFrma(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseSchi(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseSchm(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseKind(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseChan(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); + Status ParseChap(MP4Atom currentAtom, int32_t depth, ParseContext* ctx); +}; +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif // MP4_BOX_PARSER_H \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_demuxer_plugin.cpp b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_demuxer_plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f886852685fa7c22d3057f33b642d71349364524 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_demuxer_plugin.cpp @@ -0,0 +1,1489 @@ +/* + * Copyright (C) 2025 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 MEDIA_PLUGIN +#define HST_LOG_TAG "MP4DemuxerPlugin" +#include +#include +#include +#include +#include +#include +#include +#include +#include "avcodec_trace.h" +#include "securec.h" +#include "ffmpeg_format_helper.h" +#include "mp4_sample_helper.h" +#include "mp4_demuxer_plugin.h" +#include "mp4_utils.h" +#include "plugin/plugin_buffer.h" +#include "plugin/plugin_definition.h" +#include "meta/video_types.h" +#include "demuxer_log_compressor.h" +#include "syspara/parameters.h" + +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "MP4DemuxerPlugin" }; +} + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +constexpr uint32_t DEFAULT_CACHE_LIMIT = 50 * 1024 * 1024; +constexpr uint32_t PROBE_SIZE = 5000000; +constexpr int32_t FIRST_LEVEL_RANK = 100; +constexpr int32_t SECOND_LEVEL_RANK = 95; +constexpr int32_t THIRD_LEVEL_RANK = 50; +constexpr int32_t RANK_MIN = 5; +constexpr int32_t RANK_MAX = 100 + 1; // 适配自研优先 + +static const std::map g_streamParserMap = { + { "video/avc", VideoStreamType::AVC }, + { "video/hevc", VideoStreamType::HEVC }, + { "video/vvc", VideoStreamType::VVC }, +}; + +bool HaveValidParser(const std::string mime) +{ + return g_streamParserMap.count(mime) != 0; +} + +namespace { +std::mutex g_mtx; + +int Sniff(const std::string& pluginName, std::shared_ptr dataSource); + +Status RegisterMp4Plugin(const std::shared_ptr& reg); + +bool IsSupportedTrack(const std::shared_ptr& track) +{ + if (track->codecParms.trackType == ATTACHMENT_TYPE || track->sampleHelper->sampleIndexEntry_.size() <= 0) { + MEDIA_LOG_E("Unsupport image track or empty SampleIndexEntry"); + return false; + } + return true; +} +} // namespace + +MP4DemuxerPlugin::MP4DemuxerPlugin(std::string name) + : DemuxerPlugin(std::move(name)), + firstTrack_(nullptr), + lastTrack_(nullptr), + isIsom_(false), + hasSidxBox_(false), + hasMoofBox_(false), + seekable_(Seekable::SEEKABLE), + selectedTrackIds_(), + cacheQueue_("cacheQueue") +{ + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_D("In"); +} + +MP4DemuxerPlugin::~MP4DemuxerPlugin() +{ + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_D("In"); + + streamParsers_ = nullptr; + selectedTrackIds_.clear(); + trackMtx_.clear(); + fragmentEntry_.clear(); + + MEDIA_LOG_D("Out"); +} + +void MP4DemuxerPlugin::ResetParam() +{ + for (const auto& selectedTrackId : selectedTrackIds_) { + cacheQueue_.RemoveTrackQueue(selectedTrackId); + } + // Remove cacheQueue for all video and audio tracks + for (const auto& avTrackId: avTrackIds_) { + cacheQueue_.RemoveTrackQueue(avTrackId); + } + firstTrack_.reset(); + lastTrack_.reset(); + streamParsers_.reset(); + + hasMoofBox_ = false; + hasSidxBox_ = false; + + selectedTrackIds_.clear(); + trackMtx_.clear(); + mediaInfo_ = MediaInfo(); +} + +Status MP4DemuxerPlugin::Reset() +{ + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_D("In"); + ResetParam(); + return Status::OK; +} + +Status MP4DemuxerPlugin::Start() +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::Stop() +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::Flush() +{ + Status ret = Status::OK; + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_I("In"); + for (const auto& selectedTrackId : selectedTrackIds_) { + ret = cacheQueue_.RemoveTrackQueue(selectedTrackId); + ret = cacheQueue_.AddTrackQueue(selectedTrackId); + } + return ret; +} + +Status MP4DemuxerPlugin::SetDataSource(const std::shared_ptr& source) +{ + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_I("In"); + FALSE_RETURN_V_MSG_E(source != nullptr, Status::ERROR_INVALID_PARAMETER, "DataSource is nullptr"); + + seekable_ = source->IsDash() ? Seekable::UNSEEKABLE : source->GetSeekable(); + dataSource_ = source; + auto parser = std::make_shared(); + FALSE_RETURN_V_MSG_E(parser != nullptr, Status::ERROR_NO_MEMORY, "parser allocation failed"); + Status ret = parser->MP4ParseHeader(source, seekable_); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "MP4ParseHeader failed"); + + mediaInfo_ = parser->GetMediaInfo(); + firstTrack_ = parser->GetFirstTrack(); + userformat_ = parser->GetUserFormat(); + fragmentEntry_.clear(); + parser->GetFragmentEntry(fragmentEntry_); + hasSidxBox_ = parser->GetHasSidx(); + hasMoofBox_ = parser->GetHasMoof(); + + InitParser(); + + GetMediaInfo(); + + if (HasCodecParameters() != Status::OK) { + ResetParam(); + MEDIA_LOG_E("SetDataSource failed cause not enough data"); + return Status::ERROR_NOT_ENOUGH_DATA; + } + + cachelimitSize_ = DEFAULT_CACHE_LIMIT; + MEDIA_LOG_I("Out"); + return Status::OK; +} + +bool MP4DemuxerPlugin::FirstFrameValid(uint32_t trackIndex) +{ + return firstFrameMap_.count(trackIndex) > 0 && firstFrameMap_[trackIndex] != nullptr; +} + +Status MP4DemuxerPlugin::ConvertElstTimescaleToMs() +{ + auto track = firstTrack_; + if (track == nullptr) { + MEDIA_LOG_W("This file has no track"); + return Status::OK; + } + int32_t fileTimeScale; + if (!mediaInfo_.general.GetData(Tag::MEDIA_TIME_SCALE, fileTimeScale)) { + MEDIA_LOG_E("Get mvhd timescale failed"); + return Status::ERROR_INVALID_DATA; + } + int32_t trackTimeScale; + for (uint32_t trackIndex = 0; track != nullptr; ++trackIndex) { + if (track->codecParms.trackType == ATTACHMENT_TYPE) { + MEDIA_LOG_D("Skip attachment track " PUBLIC_LOG_U32, trackIndex); + track = track->next; + continue; + } + if (mediaInfo_.tracks[trackIndex].GetData(Tag::MEDIA_TIME_SCALE, trackTimeScale)) { + int64_t tempEmptyEdit = 0; + FALSE_RETURN_V_MSG_E( + ConvertTimeScale(track->elstInitEmptyEdit, 1000LL, fileTimeScale, &tempEmptyEdit) == Status::OK, + Status::ERROR_INVALID_PARAMETER, "mul overflow"); + track->elstInitEmptyEdit = tempEmptyEdit; + } else { + MEDIA_LOG_E("Get mdhd timescale failed"); + return Status::ERROR_INVALID_PARAMETER; + } + track = track->next; + } + return Status::OK; +} + +Status MP4DemuxerPlugin::HasCodecParameters() +{ + int32_t param; + int32_t trackCount; + auto track = firstTrack_; + Status ret = ConvertElstTimescaleToMs(); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "ConvertElstTimescaleToMs is failed"); + FALSE_RETURN_V_MSG_E(mediaInfo_.general.GetData(Tag::MEDIA_TRACK_COUNT, trackCount), Status::ERROR_INVALID_DATA, + "GetData is failed"); + FALSE_RETURN_V_MSG_E(trackCount == static_cast(mediaInfo_.tracks.size()), Status::ERROR_INVALID_DATA, + "mediaInfo is error"); + + for (int32_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) { + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "track is nullptr"); + Meta &format = mediaInfo_.tracks[trackIndex]; + std::string mime; + FALSE_RETURN_V_MSG_E(format.GetData(Tag::MIME_TYPE, mime), Status::ERROR_INVALID_DATA, "Get mime type failed"); + bool flag = !HaveValidParser(mime) || (HaveValidParser(mime) && streamParsers_ != nullptr); + switch (track->codecParms.trackType) { + case AUDIO_TYPE: + FALSE_RETURN_V_MSG_E(format.GetData(Tag::AUDIO_CHANNEL_COUNT, param), Status::ERROR_INVALID_DATA, + "unspecified channel_count"); + FALSE_RETURN_V_MSG_E(format.GetData(Tag::AUDIO_SAMPLE_RATE, param), Status::ERROR_INVALID_DATA, + "unspecified sample_rate"); + break; + case VIDEO_TYPE: + FALSE_RETURN_V_MSG_E(flag && format.GetData(Tag::VIDEO_WIDTH, param), Status::ERROR_INVALID_DATA, + "unspecified width"); + FALSE_RETURN_V_MSG_E(flag && format.GetData(Tag::VIDEO_HEIGHT, param), Status::ERROR_INVALID_DATA, + "unspecified height"); + break; + default: + break; + } + track = track->next; + } + return Status::OK; +} + +void MP4DemuxerPlugin::InitParser() +{ + auto track = firstTrack_; + FALSE_RETURN_MSG(track != nullptr, "track is nullptr"); + streamParsers_ = std::make_shared(); + for (uint32_t trackIndex = 0; track != nullptr; ++trackIndex) { + std::string mime; + mediaInfo_.tracks[trackIndex].GetData(Tag::MIME_TYPE, mime); + if (HaveValidParser(mime) && streamParsers_ != nullptr) { + Status ret = streamParsers_->Create(trackIndex, g_streamParserMap.at(mime)); + if (ret != Status::OK) { + MEDIA_LOG_W("Init failed"); + } else { + MEDIA_LOG_D("Track " PUBLIC_LOG_D32 " will be converted", trackIndex); + } + break; + } + track = track->next; + } +} + +void MP4DemuxerPlugin::GetAVTrackIds() +{ + avTrackIds_.clear(); + auto track = firstTrack_; + while (track) { + if ((track->codecParms.trackType == AUDIO_TYPE || track->codecParms.trackType == VIDEO_TYPE) + && track->sampleHelper->sampleIndexEntry_.size() > 0) { + avTrackIds_.emplace_back(track->trackIndex); + if (seekable_ == Seekable::UNSEEKABLE) { + trackMtx_[track->trackIndex] = std::make_shared(); + cacheQueue_.AddTrackQueue(track->trackIndex); + } + } + track = track->next; + } +} + +Status MP4DemuxerPlugin::ParseAVFirstFrames() +{ + int32_t trackCount; + FALSE_RETURN_V_MSG_E(mediaInfo_.general.GetData(Tag::MEDIA_TRACK_COUNT, trackCount), Status::ERROR_INVALID_DATA, + "Get track count failed"); + GetAVTrackIds(); + for (const auto& avTrackId : avTrackIds_) { + auto currentTrack = FindTrackById(avTrackId); + FALSE_RETURN_V_MSG_E(currentTrack != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + FALSE_RETURN_V_MSG_E(!isInterruptNeeded_.load(), Status::ERROR_WRONG_STATE, "ParseAVFirstFrames interrupt"); + auto mp4Sample = std::make_shared(); + FALSE_RETURN_V_MSG_E(mp4Sample != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate mp4Sample"); + + // 使用GetSampleBySeekableStatus获取首帧,传入空AVBuffer + Status ret = GetSampleBySeekableStatus(seekable_, avTrackId, mp4Sample, nullptr); + FALSE_CONTINUE_LOGD(ret == Status::OK && mp4Sample->sample != nullptr && mp4Sample->sample->data != nullptr, + "Failed to get first frame for track:" PUBLIC_LOG_U32, avTrackId); + + // Process video track + if (currentTrack->codecParms.trackType == TrackType::VIDEO_TYPE && streamParsers_ != nullptr) { + if (streamParsers_->ParserIsCreated(avTrackId) && !streamParsers_->ParserIsInited(avTrackId)) { + bool convertRet = streamParsers_->ConvertExtraDataToAnnexb(avTrackId, + currentTrack->codecParms.data.get(), currentTrack->codecParms.extradataSize); + FALSE_CONTINUE_LOGD(convertRet, "ConvertExtraDataToAnnexb failed in track:" PUBLIC_LOG_U32, avTrackId); + } + firstFrameMap_[avTrackId] = mp4Sample->sample; + } + + // Process audio track + if (currentTrack->codecParms.trackType == TrackType::AUDIO_TYPE) { + auto audioParser = std::make_shared(); + FALSE_RETURN_V_MSG_E(audioParser != nullptr, Status::ERROR_NO_MEMORY, "Failed create audioParser"); + ret = audioParser->ParseAudioFrame(mp4Sample->sample->data.get(), mp4Sample->sample->size, + currentTrack->sampleHelper->mimeType_, avTrackId, mediaInfo_); + FALSE_CONTINUE_LOGD(ret == Status::OK, "Parse audio failed in track:" PUBLIC_LOG_U32, avTrackId); + } + + // Unseekable cannot reset SampleIndex + if (seekable_ == Seekable::SEEKABLE) { + currentTrack->currentSampleIndex = 0; + } + } + return Status::OK; +} + +Status MP4DemuxerPlugin::GetMediaInfo(MediaInfo& mediaInfo) +{ + std::lock_guard lock(sharedMutex_); + FALSE_RETURN_V_MSG_E(!mediaInfo_.tracks.empty() || !mediaInfo_.general.Empty(), + Status::ERROR_INVALID_DATA, "MediaInfo has no information"); + mediaInfo = mediaInfo_; + return Status::OK; +} + +int32_t MP4DemuxerPlugin::CountTracks() +{ + int32_t count = 0; + auto track = firstTrack_; + while (track) { + ++count; + track = track->next; + } + return count; +} + +Status MP4DemuxerPlugin::GetMediaInfo() +{ + MEDIA_LOG_D("In"); + MediaAVCodec::AVCodecTrace trace("MP4DemuxerPlugin::GetMediaInfo"); + int32_t trackCount = CountTracks(); + mediaInfo_.general.SetData(Tag::MEDIA_TRACK_COUNT, trackCount); + Status ret = ParseAVFirstFrames(); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Parse first frames failed"); + DemuxerLogCompressor::StringifyMeta(mediaInfo_.general, -1); + auto track = firstTrack_; + for (uint32_t trackIndex = 0; track != nullptr; ++trackIndex, track = track->next) { + Meta meta = mediaInfo_.tracks[trackIndex]; + std::string mime; + mediaInfo_.tracks[trackIndex].GetData(Tag::MIME_TYPE, mime); + if (mime == "video/hevc") { + if (streamParsers_ != nullptr && streamParsers_->ParserIsInited(trackIndex) && + FirstFrameValid(trackIndex)) { + auto firstFrame = firstFrameMap_[trackIndex]; + uint8_t* dataPtr = firstFrame->data.get(); + streamParsers_->ConvertPacketToAnnexb(trackIndex, &dataPtr, firstFrame->size, nullptr, 0, false); + streamParsers_->ParseAnnexbExtraData(trackIndex, dataPtr, firstFrame->size); + // Parser only sends xps info when first call ConvertPacketToAnnexb + // readSample will call ConvertPacketToAnnexb again, so rest here + streamParsers_->ResetXPSSendStatus(trackIndex); + ParseHEVCMetadataInfo(trackIndex, meta); + } else { + MEDIA_LOG_W("Parse hevc info failed"); + } + } + if (HaveValidParser(mime)) { + ConvertCsdToAnnexb(*track, meta, trackIndex); + } + mediaInfo_.tracks[trackIndex] = meta; + DemuxerLogCompressor::StringifyMeta(meta, trackIndex); + } + return Status::OK; +} + +Status MP4DemuxerPlugin::GetUserMeta(std::shared_ptr userformat) +{ + MediaAVCodec::AVCodecTrace trace("MP4DemuxerPlugin::GetUserMeta"); + std::lock_guard lock(sharedMutex_); + FALSE_RETURN_V_MSG_E(userformat != nullptr, Status::ERROR_NULL_POINTER, "userformat is nullptr"); + *userformat = *userformat_; + return Status::OK; +} + +Status MP4DemuxerPlugin::GetDrmInfo(std::multimap>& drmInfo) +{ + MEDIA_LOG_D("In"); + std::lock_guard lock(sharedMutex_); + // pass + return Status::OK; +} + +void MP4DemuxerPlugin::ConvertCsdToAnnexb(const MP4AtomParser::Track& track, Meta &format, uint32_t trackIndex) +{ + uint8_t *extradata = track.codecParms.data.get(); + int32_t extradataSize = track.codecParms.extradataSize; + std::string mime; + mediaInfo_.tracks[trackIndex].GetData(Tag::MIME_TYPE, mime); + if (HaveValidParser(mime) && streamParsers_ != nullptr && + streamParsers_->ParserIsInited(trackIndex)) { + streamParsers_->ConvertPacketToAnnexb(trackIndex, &extradata, extradataSize, nullptr, 0, true); + } + if (extradata != nullptr && extradataSize > 0) { + std::vector extra(extradataSize); + extra.assign(extradata, extradata + extradataSize); + format.Set(extra); + } +} + +void MP4DemuxerPlugin::ParseHEVCMetadataInfo(uint32_t trackIndex, Meta& format) +{ + HevcParseFormat parse; + MultiStreamParserManager::ParseMetadataInfo(trackIndex, streamParsers_, parse); + AVFormatContext dummyContext = {}; + Ffmpeg::FFmpegFormatHelper::ParseHevcInfo(dummyContext, parse, format); +} + +bool MP4DemuxerPlugin::TrackIsSelected(const uint32_t trackId) +{ + return std::find(selectedTrackIds_.begin(), selectedTrackIds_.end(), trackId) != selectedTrackIds_.end(); +} + +std::shared_ptr MP4DemuxerPlugin::FindTrackById(int32_t trackId) +{ + auto track = firstTrack_; + while (track && static_cast(track->trackIndex) != trackId) { + track = track->next; + } + return track; +} + +Status MP4DemuxerPlugin::SelectTrack(uint32_t trackId) +{ + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_I("Select " PUBLIC_LOG_D32, trackId); + int32_t trackCount; + FALSE_RETURN_V_MSG_E(mediaInfo_.general.GetData(Tag::MEDIA_TRACK_COUNT, trackCount), + Status::ERROR_INVALID_DATA, "GetData is failed"); + if (trackId >= static_cast(trackCount)) { + MEDIA_LOG_E("Track is Invalid, just have " PUBLIC_LOG_D32 "tracks in file", trackCount); + return Status::ERROR_INVALID_PARAMETER; + } + + auto currentTrack = FindTrackById(trackId); + FALSE_RETURN_V_MSG_E(currentTrack != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + if (!IsSupportedTrack(currentTrack)) { + MEDIA_LOG_E("Track type is unsupport"); + return Status::ERROR_INVALID_PARAMETER; + } + + if (!TrackIsSelected(trackId)) { + selectedTrackIds_.emplace_back(trackId); + trackMtx_[trackId] = std::make_shared(); + return cacheQueue_.AddTrackQueue(trackId); + } else { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " has been selected", trackId); + } + return Status::OK; +} + +Status MP4DemuxerPlugin::UnselectTrack(uint32_t trackId) +{ + std::lock_guard lock(sharedMutex_); + MEDIA_LOG_I("Unselect " PUBLIC_LOG_D32, trackId); + int32_t trackCount; + FALSE_RETURN_V_MSG_E(mediaInfo_.general.GetData(Tag::MEDIA_TRACK_COUNT, trackCount), + Status::ERROR_INVALID_DATA, "GetData is failed"); + if (trackId >= static_cast(trackCount)) { + MEDIA_LOG_E("Track is Invalid, just have " PUBLIC_LOG_D32 "tracks in file", trackCount); + return Status::ERROR_INVALID_PARAMETER; + } + + auto index = std::find(selectedTrackIds_.begin(), selectedTrackIds_.end(), trackId); + if (index != selectedTrackIds_.end()) { + selectedTrackIds_.erase(index); + trackMtx_.erase(trackId); + return cacheQueue_.RemoveTrackQueue(trackId); + } else { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " is not in selected list", trackId); + } + return Status::OK; +} + +Status MP4DemuxerPlugin::DynamicBaseTrackSelect(int64_t seekTime, int64_t &maxPts, int32_t &baseTrackIndex) +{ + int64_t minPts = INT64_MAX; + int64_t convertedMinPts = INT64_MAX; + int32_t baseTrackTimeBase = 0; + bool baseTrackIsVideo = false; + for (const auto& currentTrackId : selectedTrackIds_) { + auto track = FindTrackById(currentTrackId); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + FALSE_RETURN_V_MSG_E(track->sampleHelper != nullptr, Status::ERROR_NULL_POINTER, "sampleHelper is nullptr"); + int32_t trackTimeBase; + if (!mediaInfo_.tracks[track->trackIndex].GetData(Tag::MEDIA_TIME_SCALE, trackTimeBase)) { + return Status::ERROR_INVALID_PARAMETER; + } + int64_t timeDelay = track->elstInitEmptyEdit - minElstInitEmptyEdit_; + int64_t timeDelayToPts; + FALSE_RETURN_V_MSG_E(ConvertTimeScale(timeDelay, static_cast(trackTimeBase), 1000LL, + &timeDelayToPts) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + int64_t seekTimeToPts; + FALSE_RETURN_V_MSG_E(ConvertTimeScale(seekTime - timeDelay, static_cast(trackTimeBase), + 1000LL, &seekTimeToPts) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + seekTimeToPts += track->elstShiftStartTime; + + int64_t firstPts = track->sampleHelper->sampleIndexEntry_[0].pts; // 获取首帧pts + int64_t lastPts = track->sampleHelper->lastPTS_; + if (seekTimeToPts >= firstPts && seekTimeToPts < lastPts) { + int64_t adjustedFirstPts = firstPts + timeDelayToPts; + if (minPts != INT64_MAX) { + FALSE_RETURN_V_MSG_E(ConvertTimeScale(minPts, static_cast(trackTimeBase), + static_cast(baseTrackTimeBase), &convertedMinPts) == Status::OK, + Status::ERROR_INVALID_PARAMETER, "mul overflow"); + } + + bool isVideoTrack = (track->codecParms.trackType == VIDEO_TYPE); + int64_t trackPtsWithDelay = firstPts + timeDelayToPts; + // 1. Current track is video, base track is not, or its PTS is below the minimum of selected tracks. + // 2. Current track is not video, base track also not, and its PTS is below the minimum of selected tracks. + bool shouldUpdate = isVideoTrack ? (!baseTrackIsVideo || trackPtsWithDelay < convertedMinPts) : + (!baseTrackIsVideo && trackPtsWithDelay < convertedMinPts); + + if (shouldUpdate) { + baseTrackIndex = currentTrackId; + minPts = adjustedFirstPts; + baseTrackTimeBase = trackTimeBase; + baseTrackIsVideo = isVideoTrack; + } + } + int64_t tempMaxPts = 0; + FALSE_RETURN_V_MSG_E(ConvertTimeScale(lastPts + timeDelayToPts, 1000LL, static_cast(trackTimeBase), + &tempMaxPts) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + maxPts = std::max(maxPts, tempMaxPts); + } + MEDIA_LOG_D("Selected track " PUBLIC_LOG_D32 " with smallest PTS:" PUBLIC_LOG_D64, + baseTrackIndex, minPts); + return Status::OK; +} + +// find base track & calculate absolute delay time +Status MP4DemuxerPlugin::FindBaseTrackAndCalcDelay(int64_t seekTime, SeekMode mode, int32_t &baseTrackIndex) +{ + for (const auto& currentTrackId : selectedTrackIds_) { + auto track = FindTrackById(currentTrackId); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + // calculate the absolute delay time of selected tracks + minElstInitEmptyEdit_ = (track->elstInitEmptyEdit > 0) ? + std::min(minElstInitEmptyEdit_, track->elstInitEmptyEdit) : 0; + } + + int64_t maxPts = 0; // ms + FALSE_RETURN_V_MSG_E(DynamicBaseTrackSelect(seekTime, maxPts, baseTrackIndex) == Status::OK, + Status::ERROR_INVALID_PARAMETER, "Find base track failed"); + relativeEndPts_ = maxPts; + if (seekTime > static_cast(relativeEndPts_) && mode == SeekMode::SEEK_NEXT_SYNC) { + return Status::ERROR_INVALID_PARAMETER; + } + MEDIA_LOG_D("Find base track ID: " PUBLIC_LOG_D32 ", minElstInitEmptyEdit_=" PUBLIC_LOG_D64, + baseTrackIndex, minElstInitEmptyEdit_); + return Status::OK; +} + +Status MP4DemuxerPlugin::FindFragmentSyncSample(uint32_t fragmentIndex, std::shared_ptr track) +{ + int64_t moofOffset = fragmentEntry_[fragmentIndex].moofOffset; + for (const auto& selectedTrackId : selectedTrackIds_) { + uint32_t currentTrackId = selectedTrackId; + auto currentTrack = FindTrackById(currentTrackId); + FALSE_RETURN_V_MSG_E(currentTrack != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + auto& entry = currentTrack->sampleHelper->sampleIndexEntry_; + auto it = std::upper_bound(entry.begin(), entry.end(), moofOffset, + [](int64_t b, const MP4SampleHelper::SampleIndexEntry& a) { return b < a.pos; }); + + uint32_t sampleIndex = std::distance(entry.begin(), it); + if (sampleIndex < entry.size() - 1) { + currentTrack->currentSampleIndex = sampleIndex; + } else { + return Status::ERROR_UNKNOWN; + } + } + return Status::OK; +} + +Status MP4DemuxerPlugin::FindFragmentAtTime(int64_t fragmentSeekTime, SeekMode flag, + std::shared_ptr track, bool &findFragmentSync) +{ + if (fragmentSeekTime < fragmentEntry_.front().firstDts) { + return Status::OK; + } + if (fragmentSeekTime > fragmentEntry_.back().firstDts + fragmentEntry_.back().duration) { // seek超限 + return Status::ERROR_INVALID_PARAMETER; + } + + auto& entry = fragmentEntry_; + auto it = std::lower_bound(entry.begin(), entry.end(), fragmentSeekTime, + [](const MP4AtomParser::FragmentEntry& a, int64_t b) { return a.firstDts < b; }); + uint32_t fragmentIndex = std::distance(entry.begin(), it); + if (fragmentSeekTime != fragmentEntry_[fragmentIndex].firstDts) { + fragmentIndex = fragmentIndex ? fragmentIndex - 1 : 0; + } + currentFragment_ = fragmentIndex; + MEDIA_LOG_D("Find fragment " PUBLIC_LOG_U32, fragmentIndex); + if (!entry[fragmentIndex].hasRead) { + int64_t moofOffset = entry[fragmentIndex].moofOffset; + FALSE_RETURN_V_MSG_E(ParseMoof(moofOffset) == Status::OK, Status::ERROR_INVALID_PARAMETER, "ParseMoof failed"); + } + bool hasVideoTrack = false; + mediaInfo_.general.GetData(Tag::MEDIA_HAS_VIDEO, hasVideoTrack); + if (hasVideoTrack && FindFragmentSyncSample(fragmentIndex, track) == Status::OK) { + findFragmentSync = true; + } + return Status::OK; +} + +Status MP4DemuxerPlugin::FragmentSeek(int64_t seekTime, SeekMode mode, int32_t trackIndex, bool &findFragmentSync) +{ + Status ret = Status::OK; + auto track = FindTrackById(trackIndex); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + if (!fragmentEntry_.empty()) { + ret = FindFragmentAtTime(seekTime, mode, track, findFragmentSync); + } + return ret; +} + +Status MP4DemuxerPlugin::DoSeekInternal(int32_t trackIndex, int64_t seekTime, SeekMode mode, int64_t& realSeekTime) +{ + auto track = FindTrackById(trackIndex); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + auto& entry = track->sampleHelper->sampleIndexEntry_; + int64_t relativeSeekTime = seekTime - (track->elstInitEmptyEdit - minElstInitEmptyEdit_); + int32_t baseTrackTimeScale; + if (!mediaInfo_.tracks[trackIndex].GetData(Tag::MEDIA_TIME_SCALE, baseTrackTimeScale)) { + return Status::ERROR_INVALID_PARAMETER; + } + int64_t startTime = 0; + FALSE_RETURN_V_MSG_E(ConvertTimeScale(track->elstShiftStartTime, 1000LL, baseTrackTimeScale, + &startTime) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + realSeekTime = relativeSeekTime + startTime; // ms + MEDIA_LOG_I("Time [" PUBLIC_LOG_D64 "/" PUBLIC_LOG_D64 "] mode [" PUBLIC_LOG_D32 "]", seekTime, + realSeekTime, static_cast(mode)); + + int64_t trackSeekTime = 0; + FALSE_RETURN_V_MSG_E(ConvertTimeScale(relativeSeekTime, static_cast(baseTrackTimeScale), 1000LL, + &trackSeekTime) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + trackSeekTime += track->elstShiftStartTime; + + bool findFragmentSync = false; + Status ret = FragmentSeek(trackSeekTime, mode, trackIndex, findFragmentSync); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "FindFragmentAtTime failed"); + if (findFragmentSync) { + return ret; + } + + bool isVideo = track->codecParms.trackType == TrackType::VIDEO_TYPE; + uint32_t sampleIndex = 0; + bool isBaseTrack = true; + ret = track->sampleHelper->FindSyncSampleAtTime(trackSeekTime, isBaseTrack, mode, sampleIndex); + // If seektime > last I-frame and within the track range, and the SeekMode is SEEK_NEXT_SYNC, seek to the EOS frame + sampleIndex = (ret != Status::OK && mode == SeekMode::SEEK_NEXT_SYNC) ? entry.size() : sampleIndex; + if (isVideo && seekTime - minElstInitEmptyEdit_ < static_cast(relativeEndPts_) && + sampleIndex == entry.size()) { + isBaseTrack = false; + } + MEDIA_LOG_I("Find sync sample " PUBLIC_LOG_D32 " in track " PUBLIC_LOG_D32, sampleIndex, track->trackIndex); + + int64_t syncSeekTime = seekTime; + if (isVideo && !isBaseTrack) { + // Use the pts of the last frame as the sync time + FALSE_RETURN_V_MSG_E(ConvertTimeScale(track->sampleHelper->lastPTS_ - track->elstShiftStartTime, 1000LL, + baseTrackTimeScale, &syncSeekTime) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + syncSeekTime += track->elstInitEmptyEdit; + } else if (isVideo) { + FALSE_RETURN_V_MSG_E(ConvertTimeScale(entry[sampleIndex].pts - track->elstShiftStartTime, 1000LL, + baseTrackTimeScale, &syncSeekTime) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + syncSeekTime += track->elstInitEmptyEdit; + } + ret = MultiTrackSync(trackIndex, !isBaseTrack, syncSeekTime, mode); // 多轨同步 + track->currentSampleIndex = ret == Status::OK ? sampleIndex : track->currentSampleIndex; + return ret; +} + +Status MP4DemuxerPlugin::MultiTrackSync(int32_t trackIndex, bool isBaseTrack, int64_t syncSeekTime, SeekMode mode) +{ + Status ret = Status::OK; + for (const auto& selectedTrackId : selectedTrackIds_) { + int32_t currentTrackId = static_cast(selectedTrackId); + if (currentTrackId == trackIndex) { + continue; + } + auto otherTrack = FindTrackById(currentTrackId); + FALSE_RETURN_V_MSG_E(otherTrack != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + + int64_t adjustSeekTime = 0; + uint32_t otherSampleIndex = 0; + adjustSeekTime = syncSeekTime - otherTrack->elstInitEmptyEdit; + int32_t otherTrackTimeScale; + if (!mediaInfo_.tracks[currentTrackId].GetData(Tag::MEDIA_TIME_SCALE, otherTrackTimeScale)) { + return Status::ERROR_INVALID_PARAMETER; + } + + int64_t otherTrackSeekTime = 0; + FALSE_RETURN_V_MSG_E(ConvertTimeScale(adjustSeekTime, static_cast(otherTrackTimeScale), 1000LL, + &otherTrackSeekTime) == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + otherTrackSeekTime += otherTrack->elstShiftStartTime; + ret = otherTrack->sampleHelper->FindSyncSampleAtTime(otherTrackSeekTime, isBaseTrack, mode, otherSampleIndex); + if (ret == Status::OK) { + otherTrack->currentSampleIndex = otherSampleIndex; + MEDIA_LOG_D("Sync track " PUBLIC_LOG_D32 " to sample " PUBLIC_LOG_D32 " at time " PUBLIC_LOG_D64, + currentTrackId, otherSampleIndex, adjustSeekTime); + } else { + MEDIA_LOG_W("Failed to sync track " PUBLIC_LOG_D32 " to time " PUBLIC_LOG_D64, + currentTrackId, adjustSeekTime); + } + } + return ret; +} + +Status MP4DemuxerPlugin::SeekTo(int32_t trackId, int64_t seekTime, SeekMode mode, int64_t &realSeekTime) +{ + std::lock_guard lock(sharedMutex_); + MediaAVCodec::AVCodecTrace trace("SeekTo"); + FALSE_RETURN_V_MSG_E(!selectedTrackIds_.empty(), Status::ERROR_INVALID_OPERATION, "No track has been selected"); + FALSE_RETURN_V_MSG_E(seekTime >= 0 && seekTime <= INT64_MAX / MS_TO_NS, Status::ERROR_INVALID_PARAMETER, + "Seek time " PUBLIC_LOG_D64 " is not supported", seekTime); + + trackId = selectedTrackIds_[0]; + Status ret = FindBaseTrackAndCalcDelay(seekTime, mode, trackId); // find base track + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Find basetrack failed"); + return DoSeekInternal(trackId, seekTime, mode, realSeekTime); +} + +Status MP4DemuxerPlugin::ParseMoof(int64_t offset) +{ + auto parser = std::make_shared(); + FALSE_RETURN_V_MSG_E(parser != nullptr, Status::ERROR_NO_MEMORY, "parser allocation failed"); + auto dataReader = std::make_shared(); + FALSE_RETURN_V_MSG_E(dataReader != nullptr, Status::ERROR_NO_MEMORY, "dataReader allocation failed"); + dataReader->SetDataReader(dataSource_); + parser->SetDataReader(dataReader); + parser->SetSeekable(seekable_); + parser->GetFragmentEntry(fragmentEntry_); + Status ret = parser->ParseMoof(firstTrack_, offset); + parser->GetFragmentEntry(fragmentEntry_); + + return ret; +} + +Status MP4DemuxerPlugin::FindValidSample(std::shared_ptr &track, + std::shared_ptr &sample) +{ + std::vector trackIds = selectedTrackIds_.empty() ? avTrackIds_ : selectedTrackIds_; + for (const auto& trackId : trackIds) { + auto currentTrack = FindTrackById(trackId); + FALSE_RETURN_V_MSG_E(currentTrack != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + + uint32_t index = currentTrack->currentSampleIndex; + uint32_t numSamples = currentTrack->sampleHelper->sampleIndexEntry_.size(); + if (index >= numSamples) { + continue; + } + auto& entry = currentTrack->sampleHelper->sampleIndexEntry_; + if (!sample) { + track = currentTrack; + sample = std::make_shared(entry[index]); + } else if (entry[index].pos < sample->pos) { + while (entry[index].flag == Sample::DISCARD && index < numSamples) { + ++index; + } + currentTrack->currentSampleIndex = index; + if (index < numSamples && entry[index].pos < sample->pos && + entry[index].flag != Sample::DISCARD) { // If find valid Sample, update + track = currentTrack; + sample = std::make_shared(entry[index]); + } + } + } + return Status::OK; +} + +Status MP4DemuxerPlugin::GetUnseekableSampleInfo(uint32_t &trackId, int64_t &pos, int32_t &size) +{ + FALSE_RETURN_V_MSG_E(!isInterruptNeeded_.load(), Status::ERROR_WRONG_STATE, "ReadSample interrupt"); + bool hasFoundSample = false; + bool tryNextMoof = false; + while (!hasFoundSample) { + std::shared_ptr track = nullptr; + std::shared_ptr sample = nullptr; + FALSE_RETURN_V(FindValidSample(track, sample) == Status::OK, Status::ERROR_NULL_POINTER); + if (!sample && !fragmentEntry_.empty() && !tryNextMoof) { + auto ret = ParseMoof(fragmentEntry_[currentFragment_].nextAtomOffset); + FALSE_RETURN_V_MSG_E(ret != Status::ERROR_AGAIN, Status::ERROR_AGAIN, "Parse next moof failed, try again"); + FALSE_RETURN_V_MSG_E(ret == Status::OK, Status::END_OF_STREAM, "Parse next moof failed"); + tryNextMoof = true; + ++currentFragment_; + FALSE_RETURN_V(currentFragment_ < fragmentEntry_.size(), Status::END_OF_STREAM); + continue; + } else if (!sample) { + return Status::END_OF_STREAM; + } + + pos = sample->pos; + size = sample->size; + trackId = track->trackIndex; + MEDIA_LOG_D("Sample: pos=" PUBLIC_LOG_D64 ", size=" PUBLIC_LOG_D32 ", trackId=" PUBLIC_LOG_U32, + pos, size, trackId); + hasFoundSample = true; + } + return Status::OK; +} + +Status MP4DemuxerPlugin::GetSeekableSampleInfo(uint32_t trackId, int64_t &pos, int32_t &size) +{ + FALSE_RETURN_V_MSG_E(!isInterruptNeeded_.load(), Status::ERROR_WRONG_STATE, "ReadSample interrupt"); + auto track = FindTrackById(trackId); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + bool hasFoundSample = false; + bool checkedFragment = false; + + while (!hasFoundSample) { + uint32_t numSamples = track->sampleHelper->sampleIndexEntry_.size(); + uint32_t index = track->currentSampleIndex; + MP4SampleHelper::SampleIndexEntry sample; + if (index < numSamples) { + sample = track->sampleHelper->sampleIndexEntry_[index]; + } + if (hasSidxBox_ && hasMoofBox_ && !checkedFragment) { + int64_t nextMoofOffset = (currentFragment_ < fragmentEntry_.size() - 1) ? + fragmentEntry_[currentFragment_ + 1].moofOffset : -1; + checkedFragment = true; + if (index >= numSamples || (sample.pos > nextMoofOffset && nextMoofOffset != -1)) { + Status ret = ParseMoof(nextMoofOffset); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "ParseMoof failed"); + track = FindTrackById(trackId); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + sample = track->sampleHelper->sampleIndexEntry_[index]; + ++currentFragment_; + } else if (index >= numSamples && nextMoofOffset == -1) { + return Status::END_OF_STREAM; + } + } else { + if (index < numSamples && !(sample.flag & Sample::DISCARD)) { + pos = sample.pos; + size = sample.size; + MEDIA_LOG_D("next sample pos=" PUBLIC_LOG_D64 ", size=" PUBLIC_LOG_D32, pos, size); + hasFoundSample = true; + } else if (index < numSamples && (sample.flag & Sample::DISCARD)) { + ++track->currentSampleIndex; + continue; + } else { + return Status::END_OF_STREAM; + } + } + } + + return Status::OK; +} + +Status MP4DemuxerPlugin::ReadSampleData(std::shared_ptr sample, uint64_t pos, int32_t size, uint32_t trackId) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V(!isInterruptNeeded_.load(), Status::ERROR_WRONG_STATE); + FALSE_RETURN_V_MSG_E(size > 0, Status::ERROR_INVALID_PARAMETER, "Invalid size"); + + auto track = FindTrackById(trackId); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + const uint32_t index = track->currentSampleIndex; + auto entry = track->sampleHelper->sampleIndexEntry_.data(); + auto buffer = std::make_shared(); + FALSE_RETURN_V_MSG_E(buffer != nullptr, Status::ERROR_NO_MEMORY, "Buffer allocation failed"); + sample->data = std::make_unique(size); + FALSE_RETURN_V_MSG_E(sample->data != nullptr, Status::ERROR_NO_MEMORY, "Memory allocation failed"); + buffer->WrapMemory(sample->data.get(), size, size); + FALSE_RETURN_V_MSG_E(buffer->GetMemory() != nullptr, Status::ERROR_UNKNOWN, "Memory is nullptr"); + + Status ret = dataSource_->ReadAt(pos, buffer, static_cast(size)); + int32_t dataSize = static_cast(buffer->GetMemory()->GetSize()); + MEDIA_LOG_D("Want:" PUBLIC_LOG_D32 ", Get:" PUBLIC_LOG_D32 ", offset:" PUBLIC_LOG_D64, size, dataSize, pos); + + if (ret == Status::OK) { + auto currentSample = entry[index]; + int64_t samplePts = 0; + int64_t sampleDts = 0; + int32_t trackTimeScale; + if (!mediaInfo_.tracks[trackId].GetData(Tag::MEDIA_TIME_SCALE, trackTimeScale)) { + return Status::ERROR_INVALID_PARAMETER; + } + FALSE_RETURN_V_MSG_E( + ConvertTimeScale(currentSample.pts, static_cast(S_TO_US), trackTimeScale, &samplePts) + == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + sample->pts = samplePts; // us + FALSE_RETURN_V_MSG_E( + ConvertTimeScale(currentSample.dts, static_cast(S_TO_US), trackTimeScale, &sampleDts) + == Status::OK, Status::ERROR_INVALID_PARAMETER, "mul overflow"); + sample->dts = sampleDts; // us + sample->flag = currentSample.flag; + sample->size = currentSample.size; + uint32_t duration = 0; + ret = track->sampleHelper->GetSampleDuration(index, duration); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "GetSampleDuration failed"); + sample->duration = duration; + // When the sample is copied in the seekable case, add it up again + if (seekable_ == Seekable::UNSEEKABLE) { + ++track->currentSampleIndex; + } + return Status::OK; + } else { + sample->data.reset(); + return ret; + } +} + +Status MP4DemuxerPlugin::ReadSampleData(std::shared_ptr& mp4Sample, uint64_t pos, int32_t size, + uint32_t trackId) +{ + auto it = cacheSamples_.find(trackId); + if (it != cacheSamples_.end()) { + mp4Sample = it->second; + return Status::OK; + } + auto sample = std::make_shared(); + FALSE_RETURN_V_MSG_E(sample != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate sample"); + mp4Sample->sample = sample; + + return ReadSampleData(sample, pos, size, trackId); +} + +Status MP4DemuxerPlugin::AddSampleToCacheQueue(std::shared_ptr sample, const uint32_t trackId) +{ + FALSE_RETURN_V_MSG_E(sample != nullptr, Status::ERROR_NULL_POINTER, "sample is nullptr"); + + Status ret = Status::OK; + std::shared_ptr cacheSample = std::make_shared(); + FALSE_RETURN_V_MSG_E(cacheSample != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate MP4Sample"); + if (cacheSample != nullptr) { + cacheSample->sample = sample; + cacheSample->offset = 0; + cacheQueue_.Push(trackId, cacheSample); + ret = CheckCacheDataLimit(trackId); + } + return ret; +} + +Status MP4DemuxerPlugin::CheckCacheDataLimit(uint32_t trackId) +{ + if (!outOfLimit_) { + auto cacheDataSize = cacheQueue_.GetCacheDataSize(trackId); + if (cacheDataSize > cachelimitSize_) { + MEDIA_LOG_W("Track " PUBLIC_LOG_U32 " cache out of limit: " PUBLIC_LOG_U32 "/" PUBLIC_LOG_U32 ", by user " + PUBLIC_LOG_D32, trackId, cacheDataSize, cachelimitSize_, static_cast(setLimitByUser)); + outOfLimit_ = true; + } + } + return Status::OK; +} + +Status MP4DemuxerPlugin::ConvertSampleToAnnexb(std::shared_ptr dstMp4Sample, const uint32_t trackIndex) +{ + Status ret = Status::OK; + if (dstMp4Sample->isAnnexb) { + MEDIA_LOG_D("Has converted"); + return ret; + } + std::string mime; + mediaInfo_.tracks[trackIndex].GetData(Tag::MIME_TYPE, mime); + if (HaveValidParser(mime) && streamParsers_ != nullptr && + streamParsers_->ParserIsInited(trackIndex)) { + auto srcSample = dstMp4Sample->sample; + uint8_t* dataPtr = srcSample->data.get(); + streamParsers_->ConvertPacketToAnnexb(trackIndex, &dataPtr, srcSample->size, nullptr, 0, false); + + if (dataPtr != srcSample->data.get()) { + std::unique_ptr newData = std::make_unique(srcSample->size); + if (memcpy_s(newData.get(), srcSample->size, dataPtr, srcSample->size) != EOK) { + MEDIA_LOG_E("Copy annexb data failed"); + return Status::ERROR_UNKNOWN; + } + srcSample->data = std::move(newData); + } + } + dstMp4Sample->isAnnexb = true; + return ret; +} + +Status MP4DemuxerPlugin::SetEosSample(std::shared_ptr outBuffer) +{ + outBuffer->pts_ = 0; + outBuffer->flag_ = static_cast(AVBufferFlag::EOS); + MEDIA_LOG_D("Read to end"); + return Status::END_OF_STREAM; +} + +Status MP4DemuxerPlugin::GetSampleBySeekableStatus(const Seekable seekable, uint32_t trackId, + std::shared_ptr &mp4Sample, std::shared_ptr outBuffer) +{ + Status ret = Status::OK; + int64_t pos = 0; + int32_t size = 0; + uint32_t trackIndex = trackId; + if (seekable_ == Seekable::SEEKABLE) { + ret = GetSeekableSampleInfo(trackIndex, pos, size); + if (ret == Status::END_OF_STREAM) { + return SetEosSample(outBuffer); + } else if (ret != Status::OK) { + return ret; + } else { + std::unique_lock sLock(syncMutex_); + ret = ReadSampleData(mp4Sample, pos, size, trackIndex); + sLock.unlock(); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "ReadSampleData failed"); + } + } else if (seekable_ == Seekable::UNSEEKABLE) { + while (!cacheQueue_.HasCache(trackId)) { + ret = GetUnseekableSampleInfo(trackIndex, pos, size); + if (ret == Status::END_OF_STREAM) { + return SetEosSample(outBuffer); + } else if (ret != Status::OK) { + return ret; + } else { + std::unique_lock sLock(syncMutex_); + auto sample = std::make_shared(); + FALSE_RETURN_V_MSG_E(sample != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate sample"); + ret = ReadSampleData(sample, pos, size, trackIndex); + sLock.unlock(); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "ReadSampleData failed"); + ret = AddSampleToCacheQueue(sample, trackIndex); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Add cache failed"); + } + } + std::lock_guard lockTrack(*trackMtx_[trackId].get()); + mp4Sample = cacheQueue_.Front(trackId); + FALSE_RETURN_V_MSG_E(mp4Sample != nullptr, Status::ERROR_NULL_POINTER, "Cache sample is nullptr"); + } + return Status::OK; +} + +uint32_t MP4DemuxerPlugin::GetFlags(const std::shared_ptr sample, bool memoryNotEnough) +{ + uint32_t flags = static_cast(AVBufferFlag::NONE); + if (sample->flag & Sample::SampleFlag::SYNC_FRAME) { + flags |= static_cast(AVBufferFlag::SYNC_FRAME); + flags |= static_cast(AVBufferFlag::CODEC_DATA); + } + if (sample->flag & Sample::SampleFlag::DISCARD) { + flags |= static_cast(AVBufferFlag::DISCARD); + } + if (memoryNotEnough) { + flags |= static_cast(AVBufferFlag::PARTIAL_FRAME); + } + return flags; +} + +Status MP4DemuxerPlugin::WriteBuffer(uint32_t trackIndex, std::shared_ptr outBuffer, + std::shared_ptr mp4Sample) +{ + FALSE_RETURN_V_MSG_E(outBuffer != nullptr, Status::ERROR_NULL_POINTER, "Buffer is nullptr"); + + // copy parameters + outBuffer->pts_ = mp4Sample->sample->pts; + outBuffer->dts_ = mp4Sample->sample->dts; + outBuffer->meta_->SetData(Tag::BUFFER_DECODING_TIMESTAMP, mp4Sample->sample->dts); + outBuffer->duration_ = mp4Sample->sample->duration; + outBuffer->meta_->SetData(Tag::BUFFER_DURATION, mp4Sample->sample->duration); + MEDIA_LOG_D("CurrentBuffer: [" PUBLIC_LOG_D64 "/" PUBLIC_LOG_D64 "/" PUBLIC_LOG_D64 "], track: " + PUBLIC_LOG_U32, outBuffer->pts_, outBuffer->dts_, outBuffer->duration_, trackIndex); + + CheckResetXPSSendStatus(trackIndex, mp4Sample->sample); + if (cacheQueue_.ResetInfo(mp4Sample) == false) { + MEDIA_LOG_D("Reset info failed"); + } + Status ret = ConvertSampleToAnnexb(mp4Sample, trackIndex); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Convert annexb failed"); + if (cacheQueue_.SetInfo(mp4Sample) == false) { + MEDIA_LOG_D("Set info failed"); + } + + int32_t remainSize = mp4Sample->sample->size - mp4Sample->offset; + int32_t copySize = remainSize < outBuffer->memory_->GetCapacity() ? remainSize : outBuffer->memory_->GetCapacity(); + MEDIA_LOG_D("Convert size [" PUBLIC_LOG_D32 "/" PUBLIC_LOG_D32 "/" PUBLIC_LOG_D32 "/" PUBLIC_LOG_D32 "]", + mp4Sample->sample->size, remainSize, copySize, mp4Sample->offset); + outBuffer->flag_ = GetFlags(mp4Sample->sample, copySize != remainSize); + + // copy data + const uint8_t *writeData = mp4Sample->sample->data.get() + mp4Sample->offset; + if (writeData != nullptr && copySize > 0) { + FALSE_RETURN_V_MSG_E(outBuffer->memory_ != nullptr, Status::ERROR_NULL_POINTER, "Memory is nullptr"); + int32_t writeResult = outBuffer->memory_->Write(writeData, copySize, 0); + FALSE_RETURN_V_MSG_E(writeResult >= 0, Status::ERROR_INVALID_OPERATION, "Memory write failed"); + } + + if (copySize < remainSize) { + FALSE_RETURN_V_MSG_E(mp4Sample->offset <= INT32_MAX - copySize, Status::ERROR_INVALID_DATA, + "Invalid offset [" PUBLIC_LOG_D32 "] copySize [" PUBLIC_LOG_D32 "]", mp4Sample->offset, copySize); + mp4Sample->offset += copySize; + cacheSamples_[trackIndex] = mp4Sample; + MEDIA_LOG_D("Buffer is not enough, next buffer to copy remain data"); + return Status::ERROR_NOT_ENOUGH_DATA; + } + return Status::OK; +} + +void MP4DemuxerPlugin::CheckResetXPSSendStatus(uint32_t trackIndex, std::shared_ptr sample) +{ + if (sample == nullptr || streamParsers_ == nullptr) { + return; + } + auto track = FindTrackById(trackIndex); + if (track == nullptr || track->codecParms.trackType != VIDEO_TYPE) { + return; + } + if (FirstFrameValid(trackIndex) && sample->dts == firstFrameMap_[trackIndex]->dts) { + if (streamParsers_ != nullptr) { + streamParsers_->ResetXPSSendStatus(trackIndex); + } + } +} + +Status MP4DemuxerPlugin::ReadSample(uint32_t trackId, std::shared_ptr outBuffer) +{ + std::shared_lock lock(sharedMutex_); + MediaAVCodec::AVCodecTrace trace("ReadSample"); + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(!selectedTrackIds_.empty(), Status::ERROR_INVALID_OPERATION, "No track has been selected"); + FALSE_RETURN_V_MSG_E(TrackIsSelected(trackId), Status::ERROR_INVALID_PARAMETER, "Track has not been selected"); + FALSE_RETURN_V_MSG_E(outBuffer != nullptr && outBuffer->memory_ != nullptr, Status::ERROR_INVALID_PARAMETER, + "AVBuffer or memory is nullptr"); + + auto mp4Sample = std::make_shared(); + FALSE_RETURN_V_MSG_E(mp4Sample != nullptr, Status::ERROR_NO_MEMORY, "Failed to allocate mp4Sample"); + Status ret = GetSampleBySeekableStatus(seekable_, trackId, mp4Sample, outBuffer); + if (ret != Status::OK) { + return ret; + } + + ret = WriteBuffer(trackId, outBuffer, mp4Sample); + if (ret == Status::ERROR_NOT_ENOUGH_DATA) { + return Status::OK; + } else if (ret == Status::OK) { + MEDIA_LOG_D("Sample has been copied"); + if (seekable_ == Seekable::UNSEEKABLE) { + cacheQueue_.Pop(trackId); + } else { + auto track = FindTrackById(trackId); + cacheSamples_.erase(trackId); + ++track->currentSampleIndex; + } + } + return ret; +} + +Status MP4DemuxerPlugin::GetNextSampleSize(uint32_t trackId, int32_t &sampleSize) +{ + std::shared_lock lock(sharedMutex_); + MediaAVCodec::AVCodecTrace trace("GetNextSampleSize"); + MEDIA_LOG_D("In, track " PUBLIC_LOG_D32, trackId); + FALSE_RETURN_V_MSG_E(!selectedTrackIds_.empty(), Status::ERROR_INVALID_OPERATION, "No track has been selected"); + FALSE_RETURN_V_MSG_E(TrackIsSelected(trackId), Status::ERROR_INVALID_PARAMETER, "Track has not been selected"); + + auto track = FindTrackById(trackId); + FALSE_RETURN_V_MSG_E(track != nullptr, Status::ERROR_NULL_POINTER, "Track is nullptr"); + uint32_t currentSampleIndex = track->currentSampleIndex; + sampleSize = track->sampleHelper->sampleIndexEntry_[currentSampleIndex].size; + return Status::OK; +} + +void MP4DemuxerPlugin::SetCacheLimit(uint32_t limitSize) +{ + setLimitByUser = true; + cachelimitSize_ = limitSize; +} + +Status MP4DemuxerPlugin::GetCurrentCacheSize(uint32_t trackId, uint32_t& size) +{ + MEDIA_LOG_D("TrackId " PUBLIC_LOG_U32, trackId); + FALSE_RETURN_V_MSG_E(!selectedTrackIds_.empty(), Status::ERROR_INVALID_OPERATION, "No track has been selected"); + FALSE_RETURN_V_MSG_E(TrackIsSelected(trackId), Status::ERROR_INVALID_PARAMETER, "Track has not been selected"); + uint32_t dataSize = cacheQueue_.GetCacheDataSize(trackId); + FALSE_RETURN_V_MSG_E(dataSize < UINT32_MAX, Status::ERROR_WRONG_STATE, "CacheSize is invalid"); + size = dataSize; + return Status::OK; +} + +bool MP4DemuxerPlugin::GetProbeSize(int32_t &offset, int32_t &size) +{ + offset = 0; + size = PROBE_SIZE; // 5000000 init probe size + return true; +} + +void MP4DemuxerPlugin::SetInterruptState(bool isInterruptNeeded) +{ + MEDIA_LOG_I("SetInterruptState %{public}d", isInterruptNeeded); + isInterruptNeeded_ = isInterruptNeeded; +} + +Status MP4DemuxerPlugin::SetDataSourceWithProbSize(const std::shared_ptr& source, + const int32_t probSize) +{ + return Status::OK; +} + + +namespace { +static bool IsJPEGBrand(uint32_t fourcc) +{ + static const uint32_t jpegBrands[] = { + FourccType("jp2 "), + FourccType("jpx "), + FourccType("jxl "), + }; + for (size_t i = 0; i < sizeof(jpegBrands) / sizeof(jpegBrands[0]); ++i) { + if (jpegBrands[i] == fourcc) { + return true; + } + } + return false; +} + +static bool IsBrand(uint32_t fourcc) +{ + static const uint32_t compatibleBrands[] = { + FourccType("isom"), + FourccType("iso2"), + FourccType("avc1"), + FourccType("hvc1"), + FourccType("hev1"), + FourccType("vvc1"), + FourccType("vvi1"), + FourccType("av01"), + FourccType("vp09"), + FourccType("3gp4"), + FourccType("mp41"), + FourccType("mp42"), + FourccType("dash"), + FourccType("nvr1"), + + FourccType("qt "), + FourccType("MSNV"), + FourccType("wmf "), + FourccType("3g2a"), + FourccType("3g2b"), + FourccType("mif1"), + FourccType("heic"), + FourccType("msf1"), + FourccType("hevc"), + FourccType("avif"), + FourccType("avis"), + FourccType("iso4"), + }; + for (size_t i = 0; i < sizeof(compatibleBrands) / sizeof(compatibleBrands[0]); ++i) { + if (compatibleBrands[i] == fourcc) { + return true; + } + } + return false; +} + +void IsMPEGPS(int64_t offset, int64_t kMaxOffset, const std::shared_ptr& dataReader, + int32_t& confidence) +{ + const int32_t readSize = 16; + const int32_t minDataStep = 2; + const uint8_t secondTagOffset = 2; + const uint8_t thirdTagOffset = 3; + while (offset < kMaxOffset - readSize) { + uint8_t tag[16]; + uint32_t tagType[4]; + FALSE_RETURN_MSG(dataReader != nullptr && + dataReader->ReadUintData(offset, tag, sizeof(tag)) == Status::OK, "Read probe data failed"); + for (uint32_t i = 0; i < sizeof(tagType) / sizeof(tagType[0]); ++i) { + tagType[i] = GetU32Value(&tag[sizeof(uint32_t) * i]); + } + if (tagType[0] == FourccType("hdlr") && tagType[secondTagOffset] == FourccType("mhlr") && + tagType[thirdTagOffset] == FourccType("MPEG")) { + MEDIA_LOG_D("MPEG-PS"); + confidence = RANK_MIN; + break; + } + else { + offset += minDataStep; + } + } +} + +int32_t GetFtypConfidence(std::shared_ptr dataReader_, + int64_t chunkDataOffset, int64_t chunkDataSize, int32_t confidence) +{ + const int32_t readLeng = 8; + const int32_t readLengHalf = 4; + FALSE_RETURN_V_MSG_E(chunkDataSize >= readLeng, 0, "ftyp data size error"); + uint32_t numBrands = (chunkDataSize - readLeng) / readLengHalf; + uint32_t numBrandsIteration = 0; + uint32_t offSetMore = 2; + FALSE_RETURN_V_MSG_E(!(__builtin_add_overflow(numBrands, offSetMore, &numBrandsIteration)), 0, + "numBrands has add overflow"); + for (size_t i = 0; i < numBrandsIteration; ++i) { + if (i == 1) { + continue; + } + uint32_t brand = 0; + uint8_t brandData[4]; + FALSE_RETURN_V_MSG_E(dataReader_->ReadUintData( + chunkDataOffset + readLengHalf * i, brandData, sizeof(brandData)) == Status::OK, + 0, "Read brand failed"); + brand = GetU32Value(brandData); + if (i == 0 && !IsJPEGBrand(brand)) { + confidence = FIRST_LEVEL_RANK; + return confidence; + } + if (i == 0) { + confidence = std::max(confidence, RANK_MIN); + } else if (IsBrand(brand)) { + confidence = FIRST_LEVEL_RANK; + return confidence; + } + } + return confidence; +} + +int32_t GetConfidence(std::shared_ptr dataReader_) +{ + bool moovFound = false; + static const int64_t kMaxOffset = 2048; + int64_t moovAtomEndOffset = -1; + int64_t moovOffset = -1; + int64_t offset = 0; + const int32_t readLeng = 8; + const int32_t minAtomSize = 16; + int32_t confidence = 0; + + while (!moovFound && offset < kMaxOffset) { + uint8_t atomInfo[8]; + FALSE_RETURN_V_MSG_E(dataReader_->ReadUintData(offset, atomInfo, sizeof(atomInfo)) == Status::OK, 0, + "Read atomInfo failed"); + uint64_t atomSize = GetU32Value(&atomInfo[0]); + uint32_t atomType = GetU32Value(&atomInfo[4]); + int64_t chunkDataOffset = offset + readLeng; + if (atomSize == 1) { + uint8_t sizeData[8]; + FALSE_RETURN_V_MSG_E(dataReader_->ReadUintData(offset + readLeng, sizeData, sizeof(sizeData)) + == Status::OK, 0, "Read atom size(version 1) failed"); + atomSize = GetU64Value(sizeData); + chunkDataOffset += readLeng; + FALSE_RETURN_V_MSG_E(atomSize >= minAtomSize && atomSize <= INT64_MAX, 0, "atom size error"); + } else if (atomSize < readLeng) { + MEDIA_LOG_E("atomSize error"); + return 0; + } + int64_t chunkDataSize = atomSize - (chunkDataOffset - offset); + FALSE_RETURN_V_MSG_E(chunkDataSize >= 0, 0, "atom data size error"); + switch (atomType) { + case FourccType("ftyp"): { + int32_t originalConfidence = confidence; + confidence = GetFtypConfidence(dataReader_, chunkDataOffset, chunkDataSize, confidence); + if (confidence == FIRST_LEVEL_RANK && confidence != originalConfidence) { + return confidence; + } + break; + } + case FourccType("mdat"): + case FourccType("pnot"): + case FourccType("udta"): { + confidence = FIRST_LEVEL_RANK; + return confidence; + } + case FourccType("moov"): { + if (__builtin_add_overflow(offset, atomSize, &moovAtomEndOffset)) { + MEDIA_LOG_E("chunk size + offset would overflow"); + return 0; + } + moovOffset = offset + sizeof(atomType); + confidence = FIRST_LEVEL_RANK; + moovFound = true; + break; + } + // case FourccType("ediw"): + // case FourccType("junk"): + // case FourccType("pict"): + case FourccType("wide"): + case FourccType("free"): { + confidence = std::max(confidence, SECOND_LEVEL_RANK); + break; + } + // case 0x82827f7d: + // case FourccType("prfl"): + case FourccType("skip"): + case FourccType("uuid"): { + confidence = std::max(confidence, THIRD_LEVEL_RANK); + break; + } + default: + break; + } + FALSE_RETURN_V_MSG_E(!(__builtin_add_overflow(offset, atomSize, &offset)), 0, + "chunk size + offset would overflow"); + } + if (confidence > THIRD_LEVEL_RANK && moovOffset != -1) { + offset = moovOffset; + IsMPEGPS(offset, kMaxOffset, dataReader_, confidence); + } + return confidence; +} + +int Sniff(const std::string& pluginName, std::shared_ptr source) +{ + FALSE_RETURN_V_MSG_E(!pluginName.empty(), 0, "Plugin name is empty"); + FALSE_RETURN_V_MSG_E(source != nullptr, 0, "source is nullptr"); + auto dataReader_ = std::make_shared(); + FALSE_RETURN_V_MSG_E(dataReader_ != nullptr, 0, "dataReader allocation failed"); + dataReader_->SetDataReader(source); + int32_t confidence = GetConfidence(dataReader_); + MEDIA_LOG_I("Sniff score = " PUBLIC_LOG_D32, confidence); + return confidence == 0 ? 0 : confidence + 1; // 适配自研优先 +} + +Status RegisterMp4Plugin(const std::shared_ptr& reg) +{ + MEDIA_LOG_I("In"); + FALSE_RETURN_V_MSG_E(reg != nullptr, Status::ERROR_INVALID_PARAMETER, "Register is nullptr"); + std::lock_guard lock(g_mtx); + + std::string pluginName = "avdemux_mp4"; + + DemuxerPluginDef regInfo; + regInfo.name = pluginName; + regInfo.description = "mp4 demuxer plugin"; + regInfo.rank = RANK_MAX; + regInfo.AddExtensions({"mp4", "mov"}); + auto func = [](const std::string& name) -> std::shared_ptr { + return std::make_shared(name); + }; + regInfo.SetCreator(func); + regInfo.SetSniffer(Sniff); + auto ret = reg->AddPlugin(regInfo); + if (ret != Status::OK) { + MEDIA_LOG_E("Add plugin failed, err = " PUBLIC_LOG_D32, static_cast(ret)); + } else { + MEDIA_LOG_D("Add plugin " PUBLIC_LOG_S, pluginName.c_str()); + } + + return Status::OK; +} +} // namespace +PLUGIN_DEFINITION(Mp4Demuxer, LicenseType::LGPL, RegisterMp4Plugin, [] {}); +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_demuxer_plugin.h b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_demuxer_plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..678a71a775a47009bc41b8b6a84c70f4cecd39b9 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_demuxer_plugin.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2025 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 MP4_DEMUXER_PLUGIN_H +#define MP4_DEMUXER_PLUGIN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "buffer/avbuffer.h" +#include "plugin/demuxer_plugin.h" +#include "mp4_box_parser.h" +#include "block_queue_pool.h" +#include "multi_stream_parser_manager.h" +#include "reference_parser_manager.h" +#include "common/log.h" +#include "meta/format.h" +#include "meta/meta.h" +#include "demuxer_data_reader.h" + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { + +class MP4SampleHelper; + +class MP4DemuxerPlugin : public DemuxerPlugin { +public: + explicit MP4DemuxerPlugin(std::string name); + ~MP4DemuxerPlugin() override; + Status Reset() override; + Status Start() override; + Status Stop() override; + Status Flush() override; + Status SetDataSource(const std::shared_ptr& source) override; + Status GetMediaInfo(MediaInfo& mediaInfo) override; + Status GetUserMeta(std::shared_ptr meta) override; + Status SelectTrack(uint32_t trackId) override; + Status UnselectTrack(uint32_t trackId) override; + Status SeekTo(int32_t trackId, int64_t seekTime, SeekMode mode, int64_t& realSeekTime) override; + Status ReadSample(uint32_t trackId, std::shared_ptr sample) override; + Status ReadSample(uint32_t trackId, std::shared_ptr sample, uint32_t timeout) override + { return Status::ERROR_UNKNOWN; } + Status GetNextSampleSize(uint32_t trackId, int32_t& size) override; + Status GetNextSampleSize(uint32_t trackId, int32_t& size, uint32_t timeout) override + { return Status::ERROR_UNKNOWN; } + Status Pause() override { return Status::ERROR_UNKNOWN; } + Status GetLastPTSByTrackId(uint32_t trackId, int64_t &lastPTS) override { return Status::ERROR_UNKNOWN; } + Status GetDrmInfo(std::multimap>& drmInfo) override; + void ResetEosStatus() override {} + bool IsRefParserSupported() override; + Status ParserRefUpdatePos(int64_t timeStampMs, bool isForward = true) override; + Status ParserRefInfo() override; + Status GetFrameLayerInfo(std::shared_ptr videoSample, FrameLayerInfo &frameLayerInfo) override; + Status GetFrameLayerInfo(uint32_t frameId, FrameLayerInfo &frameLayerInfo) override; + Status GetGopLayerInfo(uint32_t gopId, GopLayerInfo &gopLayerInfo) override; + Status GetIFramePos(std::vector &IFramePos) override; + Status Dts2FrameId(int64_t dts, uint32_t &frameId) override; + Status SeekMs2FrameId(int64_t seekMs, uint32_t &frameId) override; + Status FrameId2SeekMs(uint32_t frameId, int64_t &seekMs) override; + Status GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) override; + Status GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) override; + void SetCacheLimit(uint32_t limitSize) override; + Status GetCurrentCacheSize(uint32_t trackId, uint32_t& size) override; + bool GetProbeSize(int32_t &offset, int32_t &size) override; + void SetInterruptState(bool isInterruptNeeded) override; + Status SetDataSourceWithProbSize(const std::shared_ptr& source, + const int32_t probSize) override; + Status BoostReadThreadPriority() override { return Status::ERROR_UNKNOWN; } +private: + std::shared_ptr firstTrack_; + std::shared_ptr lastTrack_; + std::vector fragmentEntry_; + + std::mutex mutex_ {}; + std::mutex syncMutex_; + std::shared_mutex sharedMutex_; + std::unordered_map> trackMtx_; + bool isIsom_; + bool hasSidxBox_; + bool hasMoofBox_; + int64_t minElstInitEmptyEdit_ = INT64_MAX; + uint64_t relativeEndPts_ = 0; + uint32_t currentFragment_ = 0; + Seekable seekable_; + std::vector selectedTrackIds_; + Mp4BlockQueuePool cacheQueue_; + MediaInfo mediaInfo_; + std::shared_ptr userformat_ = nullptr; + uint32_t cachelimitSize_ = 0; + bool outOfLimit_ = false; + bool setLimitByUser = false; + std::atomic isInterruptNeeded_{false}; + + std::shared_ptr dataSource_ {nullptr}; + std::shared_ptr dataReader_ {nullptr}; + + std::shared_ptr streamParsers_ {nullptr}; + bool streamParserInited_ {false}; + std::vector avTrackIds_ {}; + std::map> firstFrameMap_ {}; + std::unordered_map> cacheSamples_; + int32_t CountTracks(); + void GetAVTrackIds(); + Status ParseAVFirstFrames(); + bool FirstFrameValid(uint32_t trackIndex); + + Status ParseMoof(int64_t offset); + Status HasCodecParameters(); + Status ConvertElstTimescaleToMs(); + Status GetMediaInfo(); + void ResetParam(); + void InitParser(); + bool TrackIsSelected(const uint32_t trackId); + std::shared_ptr FindTrackById(int32_t trackId); + + Status DynamicBaseTrackSelect(int64_t seekTime, int64_t &maxPts, int32_t &baseTrackIndex); + Status FindBaseTrackAndCalcDelay(int64_t seekTime, SeekMode mode, int32_t &trackId); + Status FindFragmentSyncSample(uint32_t fragmentIndex, std::shared_ptr track); + Status FindFragmentAtTime(int64_t reqTime, SeekMode flag, std::shared_ptr track, + bool &findFragmentSync); + Status FragmentSeek(int64_t seekTime, SeekMode mode, int32_t trackIndex, bool &findFragmentSync); + Status DoSeekInternal(int32_t trackIndex, int64_t seekTime, SeekMode mode, int64_t& realSeekTime); + Status MultiTrackSync(int32_t trackIndex, bool isBaseTrack, int64_t syncSeekTime, SeekMode mode); + + Status FindValidSample(std::shared_ptr &track, + std::shared_ptr &sample); + Status GetUnseekableSampleInfo(uint32_t &trackId, int64_t &pos, int32_t &size); + Status GetSeekableSampleInfo(uint32_t trackId, int64_t &pos, int32_t &size); + Status ReadSampleData(std::shared_ptr sample, uint64_t pos, int32_t size, uint32_t trackId); + Status ReadSampleData(std::shared_ptr& mp4Sample, uint64_t pos, int32_t size, uint32_t trackId); + Status AddSampleToCacheQueue(std::shared_ptr sample, const uint32_t trackId); + Status ConvertSampleToAnnexb(std::shared_ptr dstMp4Sample, const uint32_t trackId); + Status SetEosSample(std::shared_ptr sample); + Status GetSampleBySeekableStatus(const Seekable seekable, uint32_t trackId, std::shared_ptr &mp4Sample, + std::shared_ptr outBuffer); + uint32_t GetFlags(const std::shared_ptr sample, bool memoryNotEnough); + Status WriteBuffer(uint32_t trackIndex, std::shared_ptr outBuffer, std::shared_ptr mp4Sample); + void CheckResetXPSSendStatus(uint32_t trackIndex, std::shared_ptr sample); + + Status CheckCacheDataLimit(uint32_t trackId); + void ParseHEVCMetadataInfo(uint32_t trackIndex, Meta &format); + void ConvertCsdToAnnexb(const MP4AtomParser::Track& track, Meta& format, uint32_t trackIndex); +}; +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif // MP4_DEMUXER_PLUGIN_H \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_reference_parser.cpp b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_reference_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bfee9985ea86a08f7e55b11a9f764e3d6e284a66 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_reference_parser.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2025 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 MEDIA_PLUGIN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "avcodec_trace.h" +#include "securec.h" +#include "mp4_demuxer_plugin.h" +#include "mp4_sample_helper.h" +#include "mp4_utils.h" +#include "buffer/avbuffer.h" +#include "plugin/plugin_buffer.h" +#include "plugin/plugin_definition.h" +#include "meta/video_types.h" +#include "avcodec_sysevent.h" +#include "syspara/parameters.h" + +// namespace { +// constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "MP4ReferenceParser" }; +// } + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { + +bool MP4DemuxerPlugin::IsRefParserSupported() +{ + return true; +} + +Status MP4DemuxerPlugin::ParserRefUpdatePos(int64_t timeStampMs, bool isForward) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::ParserRefInfo() +{ + return Status::ERROR_AGAIN; +} + +Status MP4DemuxerPlugin::GetFrameLayerInfo(std::shared_ptr videoSample, FrameLayerInfo &frameLayerInfo) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::GetFrameLayerInfo(uint32_t frameId, FrameLayerInfo &frameLayerInfo) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::GetGopLayerInfo(uint32_t gopId, GopLayerInfo &gopLayerInfo) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::GetIFramePos(std::vector &IFramePos) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::Dts2FrameId(int64_t dts, uint32_t &frameId) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::SeekMs2FrameId(int64_t seekMs, uint32_t &frameId) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::FrameId2SeekMs(uint32_t frameId, int64_t &seekMs) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) +{ + return Status::OK; +} + +Status MP4DemuxerPlugin::GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) +{ + return Status::OK; +} + +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_sample_helper.cpp b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_sample_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..605815305e53b845a2e038be379a812675b0b628 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_sample_helper.cpp @@ -0,0 +1,998 @@ +/* + * Copyright (c) 2025 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 HST_LOG_TAG "MP4SampleHelper" +#include +#include +#include +#include +#include "meta/media_types.h" +#include "meta/mime_type.h" +#include "meta/video_types.h" +#include "meta/audio_types.h" +#include "common/log.h" +#include "mp4_sample_helper.h" + +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "MP4SampleHelper" }; +} + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +constexpr uint32_t DEFAULT_FIELD_SIZE = 32; +constexpr int64_t MAX_TRUN_SAMPLES = 1000000; //需要确定讨论 +constexpr int64_t NEXT_FRAME = 1; +constexpr int64_t PREVIOUS_FRAME = 0; +constexpr int64_t VTTE_SIZE = 8; +constexpr int64_t MAX_SAMPLES_PER_FRAME = 1024; +constexpr size_t MIN_DATA_SIZE = 8; +constexpr int64_t MIN_DATA_OFFSET = 0; +constexpr uint32_t STSZ_HEAD_LENGTH = 12; // stsz头数据长度 +constexpr uint32_t HALF_BYTE_BITS = 4; +constexpr uint32_t BYTE_BITS = 8; +constexpr uint32_t DUL_BYTE_BITS = 16; + +int64_t GetClosestSync(int64_t prevTime, int64_t nextTime, int64_t reqTime) +{ + int64_t prevDiff = std::abs(reqTime - prevTime); + int64_t nextDiff = std::abs(nextTime - reqTime); + + return (prevDiff <= nextDiff) ? PREVIOUS_FRAME : NEXT_FRAME; +} + +MP4SampleHelper::MP4SampleHelper() + : dataReader_(nullptr), + sampleIndexEntry_(), + cttsEntry_(), + seekable_(Seekable::UNSEEKABLE), + trackType_(INVALID_TYPE), + mimeType_(""), + firstMoofSampleIndex_(-1), + sttsEntry_(), + stscEntry_(), + stssEntry_(), + sampleSizes_(), + chunkOffsets_(), + trex_(), + tfhd_() +{ +} + +MP4SampleHelper::~MP4SampleHelper() +{ +} + +Status MP4SampleHelper::FindWithSyncSamples(int64_t inputPTS, bool isBaseTrack, SeekMode flag, uint32_t &syncIndex) +{ + MEDIA_LOG_D("In"); + auto& entry = stssEntry_; + auto it = std::lower_bound(entry.begin(), entry.end(), inputPTS, + [](const StssEntry& entry, int64_t pts) { return entry.pts < pts; }); + syncIndex = std::distance(entry.begin(), it); + if (isBaseTrack && flag == SeekMode::SEEK_PREVIOUS_SYNC && syncIndex == entry.size()) { + syncIndex--; + } + if (syncIndex == entry.size()) { + return Status::ERROR_UNKNOWN; + } else if (inputPTS == entry[syncIndex].pts || + (inputPTS > entry[syncIndex].pts && flag == SeekMode::SEEK_PREVIOUS_SYNC) || + (inputPTS < entry[syncIndex].pts && flag == SeekMode::SEEK_NEXT_SYNC)) { + return Status::OK; + } + return Status::ERROR_UNKNOWN; +} + +Status MP4SampleHelper::CheckSyncSample(int64_t inputPTS, bool isBaseTrack, SeekMode flag, int64_t ¤tIndex) +{ + MEDIA_LOG_D("In"); + const auto& entry = sampleIndexEntry_; + uint32_t numSamples = entry.size(); + + auto findPrevSync = [&entry, inputPTS](int64_t idx) -> int64_t { + while (idx >= 0 && (entry[idx].flag != Sample::SYNC_FRAME || entry[idx].pts > inputPTS)) { + --idx; + } + return idx; + }; + auto findNextSync = [&entry, numSamples, inputPTS](int64_t idx) -> int64_t { + while (idx < static_cast(numSamples) && (entry[idx].flag != Sample::SYNC_FRAME || + entry[idx].pts < inputPTS)) { + ++idx; + } + return idx; + }; + + if (flag == SeekMode::SEEK_PREVIOUS_SYNC) { + currentIndex = findPrevSync(currentIndex - 1); + if (currentIndex < 0) { + currentIndex = stssEntry_.empty() ? 0 : stssEntry_[0].index; + if (inputPTS < entry[currentIndex].pts && isBaseTrack) { + return Status::ERROR_INVALID_PARAMETER; + } + } + return Status::OK; + } + + if (flag == SeekMode::SEEK_NEXT_SYNC) { + currentIndex = findNextSync(currentIndex); + if (currentIndex == static_cast(numSamples) && isBaseTrack) { + return Status::ERROR_INVALID_PARAMETER; + } + return Status::OK; + } + + // SEEK_CLOSEST_SYNC + int64_t prevSync = findPrevSync(currentIndex); + int64_t nextSync = findNextSync(currentIndex); + if (prevSync >= 0 && nextSync < static_cast(numSamples)) { + currentIndex = GetClosestSync(entry[prevSync].pts, entry[nextSync].pts, inputPTS) ? nextSync : prevSync; + } else if (prevSync < 0) { + currentIndex = nextSync; + } else { + currentIndex = prevSync; + } + return Status::OK; +} + +Status MP4SampleHelper::FindSyncSampleAtTime(int64_t inputPTS, bool isBaseTrack, SeekMode flag, uint32_t &sampleIndex) +{ + MEDIA_LOG_D("In"); + if (inputPTS < 0) { + sampleIndex = stssEntry_.empty() ? 0 : stssEntry_[0].index; + return Status::OK; + } + + int64_t currentIndex = 0; + const auto& entry = sampleIndexEntry_; + Status ret = Status::OK; + if (!stssEntry_.empty()) { + uint32_t syncIndex = 0; + ret = FindWithSyncSamples(inputPTS, isBaseTrack, flag, syncIndex); + currentIndex = syncIndex < stssEntry_.size() ? stssEntry_[syncIndex].index : stssEntry_.back().index; + if (ret == Status::OK) { + sampleIndex = currentIndex; + return ret; + } + } else { + auto it = std::lower_bound(entry.begin(), entry.end(), inputPTS, + [](const SampleIndexEntry& entry, int64_t pts) { return entry.pts < pts; }); + currentIndex = std::distance(entry.begin(), it); + } + + if (entry[currentIndex].flag == Sample::SYNC_FRAME && inputPTS == entry[currentIndex].pts) { + sampleIndex = static_cast(currentIndex); + return Status::OK; + } else if (!isBaseTrack && (inputPTS > lastPTS_ || (!stssEntry_.empty() && + inputPTS > stssEntry_.back().pts && flag == SeekMode::SEEK_NEXT_SYNC))) { + sampleIndex = sampleIndexEntry_.size(); + return Status::OK; + } else if (mimeType_ == MimeType::TEXT_WEBVTT) { + if (currentIndex > 0 && inputPTS < entry[currentIndex].pts) { + sampleIndex = --currentIndex; + } + if (inputPTS > totalDuration_) { + sampleIndex = ++currentIndex; + } + return Status::OK; + } + + ret = CheckSyncSample(inputPTS, isBaseTrack, flag, currentIndex); + if (ret == Status::OK) { + sampleIndex = currentIndex; + return Status::OK; + } else { + return ret; + } +} + +Status MP4SampleHelper::SetTimeToSampleParams(int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(sttsEntry_.empty(), Status::ERROR_INVALID_OPERATION, "sttsEntry exist"); + FALSE_RETURN_V_MSG_E(dataOffset >= 0 && dataSize >= MIN_DATA_SIZE, Status::ERROR_INVALID_PARAMETER, "Invalid data"); + + int64_t currentOffset = dataOffset; + uint8_t header[8]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + if (GetU32Value(header)) { + return Status::ERROR_INVALID_DATA; + } + + uint32_t numSttsEntries = GetU32Value(&header[4]); + currentOffset += sizeof(uint64_t); + + if ((dataSize - sizeof(header)) / sizeof(SttsEntry) < numSttsEntries) { + return Status::ERROR_UNKNOWN; + } + + uint32_t size = numSttsEntries * sizeof(SttsEntry); + auto data = std::make_unique(size); + ret = dataReader_->ReadUintData(currentOffset, data.get(), size); + if (ret != Status::OK) { + return ret; + } + currentOffset += static_cast(size); + sttsEntry_.reserve(numSttsEntries); + + for (uint32_t i = 0; i < numSttsEntries; ++i) { + sttsEntry_.emplace_back(SttsEntry{ + .count = GetU32Value(data.get() + i * sizeof(SttsEntry)), + .delta = GetU32Value(data.get() + 4 + i * sizeof(SttsEntry)) + }); + } + + return Status::OK; +} + +Status MP4SampleHelper::SetCompositionTimeToSampleParams(int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(cttsEntry_.empty(), Status::ERROR_INVALID_OPERATION, "cttsEntry exist"); + + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[8]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + if (GetU32Value(header)) { + return Status::ERROR_INVALID_DATA; + } + + uint32_t numCttsEntries = GetU32Value(&header[4]); + currentOffset += sizeof(header); + + if ((dataSize - sizeof(header)) / sizeof(CttsEntry) < numCttsEntries) { + return Status::ERROR_UNKNOWN; + } + + uint32_t size = numCttsEntries * sizeof(CttsEntry); + auto data = std::make_unique(size); + ret = dataReader_->ReadUintData(currentOffset, data.get(), size); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read data failed"); + currentOffset += static_cast(size); + cttsEntry_.reserve(numCttsEntries); + + for (uint32_t i = 0; i < numCttsEntries; ++i) { + cttsEntry_.emplace_back(CttsEntry { + .count = GetU32Value(data.get() + i * sizeof(CttsEntry)), + .delta = static_cast(GetU32Value(data.get() + 4 + i * sizeof(CttsEntry))) + }); + } + + return Status::OK; +} + +Status MP4SampleHelper::SetSyncSampleParams(int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(stssEntry_.empty(), Status::ERROR_INVALID_OPERATION, "stssEntry exist"); + + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[8]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + if (GetU32Value(header)) { + return Status::ERROR_INVALID_DATA; + } + + uint32_t numSyncs = GetU32Value(&header[4]); + currentOffset += sizeof(header); + + if ((dataSize - sizeof(header)) / sizeof(uint32_t) < numSyncs) { + return Status::ERROR_UNKNOWN; + } + + uint32_t size = numSyncs * sizeof(uint32_t); + auto data = std::make_unique(size); + ret = dataReader_->ReadUintData(currentOffset, data.get(), size); + if (ret != Status::OK) { + return ret; + } + currentOffset += static_cast(size); + stssEntry_.reserve(numSyncs); + + for (uint32_t i = 0; i < numSyncs; ++i) { + uint32_t sampleIndex = GetU32Value(data.get() + i * sizeof(uint32_t)); + FALSE_RETURN_V_MSG_E(sampleIndex > 0, Status::ERROR_INVALID_DATA, "Invalid sample index"); + stssEntry_.emplace_back(StssEntry{ + .pts = 0, + .index = sampleIndex - 1 + }); + } + + return Status::OK; +} + +Status MP4SampleHelper::SetFieldSize(uint32_t atomType, int64_t ¤tOffset, uint32_t &fieldSize) +{ + if (atomType == FourccType("stsz")) { + fieldSize = DEFAULT_FIELD_SIZE; + uint8_t sizeBuffer[4]; + Status ret = dataReader_->ReadUintData(currentOffset, sizeBuffer, sizeof(sizeBuffer)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read sizeBuffer failed"); + uint32_t defaultSampleSize = GetU32Value(sizeBuffer); + currentOffset += sizeof(uint32_t); + if (defaultSampleSize) { + sampleSizes_.emplace_back(defaultSampleSize); + hasDefaultStszSize_ = true; + } + } else { + uint8_t sizeBuffer[4]; + Status ret = dataReader_->ReadUintData(currentOffset, sizeBuffer, sizeof(sizeBuffer)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read sizeBuffer failed"); + fieldSize = GetU32Value(sizeBuffer); + currentOffset += sizeof(uint32_t); + + if (fieldSize != HALF_BYTE_BITS && fieldSize != BYTE_BITS && fieldSize != DUL_BYTE_BITS) { + return Status::ERROR_INVALID_DATA; + } + } + return Status::OK; +} + +Status MP4SampleHelper::ProcessSampleSizeData(uint32_t atomType, int64_t currentOffset, uint32_t size, + uint32_t fieldSize, uint32_t numSampleSizes) +{ + auto data = std::make_unique(size); + Status ret = dataReader_->ReadUintData(currentOffset, data.get(), size); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read sampleSize failed"); + currentOffset += size; + sampleSizes_.reserve(numSampleSizes); + + if (atomType == FourccType("stsz")) { + for (uint32_t i = 0; i < numSampleSizes; ++i) { + uint32_t sampleSize = GetU32Value(data.get() + i * sizeof(uint32_t)); + sampleSizes_.emplace_back(static_cast(sampleSize)); + } + } else { + uint32_t i = 0; + while (i < numSampleSizes) { + uint32_t sampleSize = 0; + if (fieldSize == HALF_BYTE_BITS) { + ProcessHalfByteSampleSizes(data.get(), i, numSampleSizes); + } else if (fieldSize == BYTE_BITS) { + sampleSize = data[i]; + sampleSizes_.emplace_back(sampleSize); + } else { + sampleSize = GetU16Value(data.get() + i * sizeof(uint16_t)); + sampleSizes_.emplace_back(sampleSize); + } + ++i; + } + } + return ret; +} + +void MP4SampleHelper::ProcessHalfByteSampleSizes(const uint8_t* data, uint32_t& entryIndex, uint32_t numSampleSizes) +{ + uint32_t index = entryIndex / 2; + uint8_t sampleSize = data[index]; + sampleSizes_.emplace_back(sampleSize >> 0x4); + if (entryIndex < numSampleSizes) { + sampleSizes_.emplace_back(sampleSize - (sampleSize >> 0x4)); + ++entryIndex; + } +} + +Status MP4SampleHelper::SetSampleSizeParams(uint32_t atomType, int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(sampleSizes_.empty(), Status::ERROR_INVALID_OPERATION, "sampleSizes exist"); + if (atomType != FourccType("stsz") && atomType != FourccType("stz2")) { + return Status::ERROR_INVALID_PARAMETER; + } + + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[4]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + if (GetU32Value(header)) { + return Status::ERROR_INVALID_DATA; + } + currentOffset += sizeof(uint32_t); + + uint32_t fieldSize; + ret = SetFieldSize(atomType, currentOffset, fieldSize); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Set fieldSize failed"); + if (hasDefaultStszSize_) { + return Status::OK; + } + + uint8_t entryBuffer[4]; + ret = dataReader_->ReadUintData(currentOffset, entryBuffer, sizeof(entryBuffer)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read entryBuffer failed"); + uint32_t numSampleSizes = GetU32Value(entryBuffer); + currentOffset += sizeof(uint32_t); + if (!numSampleSizes) { + return Status::OK; + } + + if ((atomType == FourccType("stsz") && (dataSize - STSZ_HEAD_LENGTH) / sizeof(uint32_t) < numSampleSizes) || + (atomType == FourccType("stz2") && + (dataSize - STSZ_HEAD_LENGTH) < (numSampleSizes * fieldSize + sizeof(uint32_t)) / BYTE_BITS)) { + return Status::ERROR_UNKNOWN; + } + + uint32_t size = atomType == FourccType("stsz") ? numSampleSizes * sizeof(uint32_t) : + (numSampleSizes * fieldSize + sizeof(uint32_t)) / 8; + ret = ProcessSampleSizeData(atomType, currentOffset, size, fieldSize, numSampleSizes); + return ret; +} + +Status MP4SampleHelper::SetChunkOffsetParams(uint32_t atomType, int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(chunkOffsets_.empty(), Status::ERROR_INVALID_OPERATION, "chunkOffsets exist"); + if (atomType != FourccType("stco") && atomType != FourccType("co64")) { + return Status::ERROR_INVALID_PARAMETER; + } + + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[4]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + if (GetU32Value(header)) { + return Status::ERROR_INVALID_DATA; + } + currentOffset += sizeof(uint32_t); + + uint8_t entryBuffer[4]; + ret = dataReader_->ReadUintData(currentOffset, entryBuffer, sizeof(entryBuffer)); + if (ret != Status::OK) { + return ret; + } + uint32_t numChunkOffsets = GetU32Value(entryBuffer); + currentOffset += sizeof(uint32_t); + if ((atomType == FourccType("co64") && (dataSize - sizeof(uint64_t)) / sizeof(uint64_t) < numChunkOffsets) || + (atomType == FourccType("stco") && (dataSize - sizeof(uint64_t)) / sizeof(uint32_t) < numChunkOffsets)) { + return Status::ERROR_UNKNOWN; + } + + uint32_t size = atomType == FourccType("stco") ? numChunkOffsets * sizeof(uint32_t) : + numChunkOffsets * sizeof(uint64_t); + auto data = std::make_unique(size); + ret = dataReader_->ReadUintData(currentOffset, data.get(), size); + if (ret != Status::OK) { + return ret; + } + currentOffset += static_cast(size); + chunkOffsets_.reserve(numChunkOffsets); + + if (atomType == FourccType("stco")) { + for (uint32_t i = 0; i < numChunkOffsets; ++i) { + uint32_t offset = GetU32Value(data.get() + i * sizeof(uint32_t)); + chunkOffsets_.emplace_back(static_cast(offset)); + } + } else { + for (uint32_t i = 0; i < numChunkOffsets; ++i) { + uint64_t offset = GetU64Value(data.get() + i * sizeof(uint64_t)); + chunkOffsets_.emplace_back(static_cast(offset)); + } + } + + return Status::OK; +} + +Status MP4SampleHelper::SetSampleToChunkParams(int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(stscEntry_.empty(), Status::ERROR_INVALID_OPERATION, "stscEntry exist"); + + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[8]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + if (GetU32Value(header)) { + return Status::ERROR_INVALID_DATA; + } + + uint32_t numStscEntries = GetU32Value(&header[4]); + currentOffset += sizeof(header); + + if ((dataSize - sizeof(header)) / sizeof(StscEntry) < numStscEntries) { + return Status::ERROR_UNKNOWN; + } + + uint32_t size = numStscEntries * sizeof(StscEntry); + auto data = std::make_unique(size); + ret = dataReader_->ReadUintData(currentOffset, data.get(), size); + if (ret != Status::OK) { + return ret; + } + currentOffset += static_cast(size); + stscEntry_.reserve(numStscEntries); + + for (uint32_t i = 0; i < numStscEntries; ++i) { + uint32_t tempFirst = GetU32Value(data.get() + i * sizeof(StscEntry)); + if (tempFirst <= 0) { + return Status::ERROR_INVALID_DATA; + } + + stscEntry_.emplace_back(StscEntry{ + .first = tempFirst - 1, + .count = GetU32Value(data.get() + sizeof(uint32_t) + i * sizeof(StscEntry)), + .id = GetU32Value(data.get() + sizeof(uint64_t) + i * sizeof(StscEntry)) + }); + } + + return Status::OK; +} + +Status MP4SampleHelper::SetTrackExtendsParams(int64_t dataOffset, size_t dataSize) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(trex_.trackId == 0, Status::ERROR_INVALID_OPERATION, "trex exist"); + + int64_t currentOffset = dataOffset; + const size_t totalTrexSize = 24; // trex size is 24 bytes + if (currentOffset < MIN_DATA_OFFSET || dataSize < totalTrexSize) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t data[totalTrexSize]; + auto ret = dataReader_->ReadUintData(currentOffset, data, sizeof(data)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + currentOffset += sizeof(data); + + const uint32_t trackIdOffset = 4; // trackId字段偏移 + const uint32_t sampleDescIndexOffset = 8; // defaultSampleDescriptionIndex字段偏移 + const uint32_t sampleDurationOffset = 12; // defaultSampleDuration字段偏移 + const uint32_t sampleSizeOffset = 16; // defaultSampleSize字段偏移 + const uint32_t sampleFlagsOffset = 20; // defaultSampleFlags字段偏移 + + trex_.trackId = GetU32Value(data + trackIdOffset); + trex_.defaultSampleDescriptionIndex = GetU32Value(data + sampleDescIndexOffset); + trex_.defaultSampleDuration = GetU32Value(data + sampleDurationOffset); + trex_.defaultSampleSize = GetU32Value(data + sampleSizeOffset); + trex_.defaultSampleFlags = GetU32Value(data + sampleFlagsOffset); + return Status::OK; +} + +Status MP4SampleHelper::SetTrackFragmentHeaderParams(int64_t dataOffset, int64_t dataSize, uint32_t flag, + int64_t moofOffset) +{ + uint32_t tfhdFlag = flag; + uint32_t size = dataSize; + auto data = std::make_unique(size); + Status ret = dataReader_->ReadUintData(dataOffset, data.get(), size); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read data failed"); + + uint32_t tfhdDataOffset = 0; + if ((tfhdFlag & TrackFragmentHeaderInfo::TFHD_BASE_DATA_OFFSET) && seekable_ == Seekable::SEEKABLE) { + tfhd_.baseDataOffset = GetU64Value(data.get()); + tfhdDataOffset += sizeof(uint64_t); + } else { + tfhd_.baseDataOffset = moofOffset; + } + + if (tfhdFlag & TrackFragmentHeaderInfo::TFHD_STSD_ID) { + tfhd_.sampleDescriptionIndex = GetU32Value(data.get() + tfhdDataOffset); + tfhdDataOffset += sizeof(uint32_t); + } else { + tfhd_.sampleDescriptionIndex = trex_.defaultSampleDescriptionIndex; + } + + if (tfhdFlag & TrackFragmentHeaderInfo::TFHD_DEFAULT_DURATION) { + tfhd_.defaultSampleDuration = GetU32Value(data.get() + tfhdDataOffset); + tfhdDataOffset += sizeof(uint32_t); + } else { + tfhd_.defaultSampleDuration = trex_.defaultSampleDuration; + } + + if (tfhdFlag & TrackFragmentHeaderInfo::TFHD_DEFAULT_SIZE) { + tfhd_.defaultSampleSize = GetU32Value(data.get() + tfhdDataOffset); + tfhdDataOffset += sizeof(uint32_t); + } else { + tfhd_.defaultSampleSize = trex_.defaultSampleSize; + } + + if (tfhdFlag & TrackFragmentHeaderInfo::TFHD_DEFAULT_FLAGS) { + tfhd_.flags = GetU32Value(data.get() + tfhdDataOffset); + tfhdDataOffset += sizeof(uint32_t); + } else { + tfhd_.flags = trex_.defaultSampleFlags; + } + hasTfhd_ = true; + return Status::OK; +} + +Status MP4SampleHelper::SetTrackFragmentDecodeTimeParams(int64_t dataOffset, int64_t dataSize) +{ + MEDIA_LOG_D("In"); + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[4]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + uint32_t version = header[0]; + currentOffset += sizeof(uint32_t); + + if ((version && dataSize - sizeof(header) < sizeof(uint64_t)) || + (!version && dataSize - sizeof(header) < sizeof(uint32_t))) { + return Status::ERROR_INVALID_DATA; + } + + if (version) { + uint8_t data[8]; + ret = dataReader_->ReadUintData(currentOffset, data, sizeof(data)); + tfdtDts_ = static_cast(GetU64Value(data)); + } else { + uint8_t data[4]; + ret = dataReader_->ReadUintData(currentOffset, data, sizeof(data)); + tfdtDts_ = GetU32Value(data); + } + return ret; +} + +Status MP4SampleHelper::SetTrackFragmentRunParams(int64_t dataOffset, int64_t dataSize, int64_t sidxDts, + int64_t &moofDts, int64_t &duration) +{ + MEDIA_LOG_D("In"); + FALSE_RETURN_V_MSG_E(hasTfhd_, Status::ERROR_INVALID_DATA, "tfhd does not exist"); + + int64_t currentOffset = dataOffset; + if (currentOffset < MIN_DATA_OFFSET || dataSize < MIN_DATA_SIZE) { + return Status::ERROR_INVALID_PARAMETER; + } + + uint8_t header[8]; + auto ret = dataReader_->ReadUintData(currentOffset, header, sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read header failed"); + FALSE_RETURN_V_MSG_E(header[0] == 0, Status::ERROR_INVALID_DATA, "trun invalid version"); + uint16_t trunFlag = (header[1] << 0x10) + (header[2] << 0x08) + header[3]; + if ((trunFlag & TRUN_FIRST_SAMPLE_FLAGS) && (trunFlag & TRUN_SAMPLE_FLAGS)) { + return Status::ERROR_INVALID_DATA; + } + + uint32_t numSamples = GetU32Value(&header[4]); + currentOffset += sizeof(uint64_t); + + auto data = std::make_unique(dataSize - sizeof(header)); + ret = dataReader_->ReadUintData(currentOffset, data.get(), dataSize - sizeof(header)); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Read data failed"); + + uint32_t bufferOffset = 0; + uint32_t sampleByteLength = 0; + uint32_t sampleDataOffset = 0; + if (trunFlag & TRUN_DATA_OFFSET) { + sampleDataOffset = GetU32Value(data.get() + bufferOffset); + bufferOffset += sizeof(uint32_t); + } + uint32_t firstSampleFlag = 0; + if (trunFlag & TRUN_FIRST_SAMPLE_FLAGS) { + firstSampleFlag = GetU32Value(data.get() + bufferOffset); + bufferOffset += sizeof(uint32_t); + } + uint32_t sampleDuration = 0; + if (trunFlag & TRUN_SAMPLE_DURATION) { + sampleByteLength += sizeof(uint32_t); + } else if (tfhd_.defaultSampleDuration) { + sampleDuration = tfhd_.defaultSampleDuration; + } else { + return Status::ERROR_INVALID_DATA; + } + uint32_t sampleSize = 0; + if (trunFlag & TRUN_SAMPLE_SIZE) { + sampleByteLength += sizeof(uint32_t); + } else if (tfhd_.defaultSampleSize) { + sampleSize = tfhd_.defaultSampleSize; + } else { + return Status::ERROR_INVALID_DATA; + } + uint32_t sampleFlag = 0; + if (trunFlag & TRUN_SAMPLE_FLAGS) { + sampleByteLength += sizeof(uint32_t); + } else if (tfhd_.flags) { + sampleFlag = tfhd_.flags; + } else { + return Status::ERROR_INVALID_DATA; + } + uint32_t sampleCtsDelta = 0; + if (trunFlag & TRUN_SAMPLE_CTS) { + sampleByteLength += sizeof(uint32_t); + } + + if (sampleByteLength) { + if ((dataSize - sizeof(header) - bufferOffset) / sampleByteLength < numSamples) { + return Status::ERROR_INVALID_DATA; + } + } else if (numSamples > MAX_TRUN_SAMPLES) { + return Status::ERROR_INVALID_DATA; + } + + int64_t offset = tfhd_.baseDataOffset + sampleDataOffset; + int64_t currentDts = (tfdtDts_ != -1) ? tfdtDts_ : (sidxDts != -1) ? sidxDts : baseFragmentDts_; + moofDts = currentDts; + + if (firstMoofSampleIndex_ == -1) { + firstMoofSampleIndex_ = sampleIndexEntry_.size(); + firstMoofSttsIndex_ = sttsEntry_.size(); + numMoovSamples_ = sampleIndexEntry_.size(); + } + + for (uint32_t i = 0; i < numSamples; ++i) { + if (trunFlag & TRUN_SAMPLE_DURATION) { + sampleDuration = GetU32Value(data.get() + bufferOffset); + bufferOffset += sizeof(uint32_t); + } + if (trunFlag & TRUN_SAMPLE_SIZE) { + sampleSize = GetU32Value(data.get() + bufferOffset); + bufferOffset += sizeof(uint32_t); + } + if (i == 0 && (trunFlag & TRUN_FIRST_SAMPLE_FLAGS)) { + sampleFlag = firstSampleFlag; + } else if (trunFlag & TRUN_SAMPLE_FLAGS) { + sampleFlag = GetU32Value(data.get() + bufferOffset); + bufferOffset += sizeof(uint32_t); + } else { + sampleFlag = tfhd_.flags; + } + if (trunFlag & TRUN_SAMPLE_CTS) { + sampleCtsDelta = GetU32Value(data.get() + bufferOffset); + bufferOffset += sizeof(uint32_t); + } + + SampleIndexEntry sample; + sample.pos = offset; + sample.size = sampleSize; + sample.pts = currentDts + sampleCtsDelta; + sample.dts = currentDts; + if (trackType_ == AUDIO_TYPE) { + sample.flag |= Sample::SampleFlag::SYNC_FRAME; + } else { + sample.flag |= (sampleFlag & (0x00010000 | 0x01000000)) ? + Sample::SampleFlag::NONE : Sample::SampleFlag::SYNC_FRAME; + } + + if (sampleIndexEntry_.empty()) { + lastPTS_ = sample.pts; + sampleIndexEntry_.emplace_back(std::move(sample)); + sttsEntry_.emplace_back(SttsEntry { + .count = 1, + .delta = sampleDuration + }); + } else { + uint32_t sampleIndex = 0; + auto it = std::upper_bound(sampleIndexEntry_.begin(), sampleIndexEntry_.end(), sample, + [](const SampleIndexEntry& a, const SampleIndexEntry& b) { return a.pos < b.pos; }); + sampleIndex = std::distance(sampleIndexEntry_.begin(), it); + lastPTS_ = std::max(lastPTS_, sample.pts); + sampleIndexEntry_.insert(sampleIndexEntry_.begin() + sampleIndex, std::move(sample)); + + uint32_t sttsIndex = sampleIndex - numMoovSamples_ + firstMoofSttsIndex_ - 1; + sttsEntry_.insert(sttsEntry_.begin() + sttsIndex, SttsEntry { + .count = 1, + .delta = sampleDuration + }); + } + duration += sampleDuration; + currentDts += sampleDuration; + offset += sampleSize; + FALSE_RETURN_V_MSG_E(duration >= 0 && currentDts >= 0 && offset >= 0, Status::ERROR_INVALID_DATA, + "Sample DTS or Offset is negative"); + } + totalTrunDuration_ = currentDts; + baseFragmentDts_ = currentDts; + return Status::OK; +} + +Status MP4SampleHelper::ExpandCttsEntry(uint32_t sttsNum) +{ + uint32_t cttsNum = 0; + for (const auto& entry : cttsEntry_) { + uint32_t temp; + if (__builtin_add_overflow(cttsNum, entry.count, &temp)) { + MEDIA_LOG_E("CTTS count overflow: current=" PUBLIC_LOG_U32 ", add=" + PUBLIC_LOG_U32, cttsNum, entry.count); + return Status::ERROR_INVALID_DATA; + } + cttsNum = temp; + } + FALSE_RETURN_V_MSG_E(sttsNum == cttsNum, Status::ERROR_INVALID_DATA, "ctts did not match stts"); + std::vector tempCttsEntry; + tempCttsEntry.reserve(cttsNum); + for (const auto& entry : cttsEntry_) { + for (uint32_t j = 0; j < entry.count; ++j) { + tempCttsEntry.emplace_back(CttsEntry { + .count = 1, + .delta = entry.delta + }); + } + } + cttsEntry_ = std::move(tempCttsEntry); + return Status::OK; +} + +Status MP4SampleHelper::PreBuildProcessAndCheck() +{ + FALSE_RETURN_V_MSG_E(!sttsEntry_.empty(), Status::ERROR_INVALID_DATA, "stts corrupted"); + FALSE_RETURN_V_MSG_E(!sampleSizes_.empty(), Status::ERROR_INVALID_DATA, "stsz corrupted"); + FALSE_RETURN_V_MSG_E(!chunkOffsets_.empty(), Status::ERROR_INVALID_DATA, "stco corrupted"); + FALSE_RETURN_V_MSG_E(!stscEntry_.empty(), Status::ERROR_INVALID_DATA, "stsc corrupted"); + + uint32_t sttsNum = 0; + for (const auto& entry : sttsEntry_) { + sttsNum += entry.count; + } + + if (!cttsEntry_.empty()) { + // cttsEntry展开 + Status ret = ExpandCttsEntry(sttsNum); + FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "ExpandCttsEntry failed"); + } + + if (!hasDefaultStszSize_) { + FALSE_RETURN_V_MSG_E(sttsNum == sampleSizes_.size(), Status::ERROR_INVALID_DATA, "stsz did not match stts"); + } + + return Status::OK; +} + +bool MP4SampleHelper::GetSampleSize(uint32_t sampleIndex, uint32_t &samplesPerFrame, + SampleIndexEntry &entry, int64_t &remainSamples) +{ + if (mimeType_ == MimeType::AUDIO_G711MU || mimeType_ == MimeType::AUDIO_G711A || mimeType_ == MimeType::AUDIO_RAW) { + int32_t sampleSize = sampleSizes_[0]; + if (sampleSize > INT32_MAX / MAX_SAMPLES_PER_FRAME) { + return false; + } + samplesPerFrame = std::min(MAX_SAMPLES_PER_FRAME, remainSamples); + remainSamples -= samplesPerFrame; + entry.size = samplesPerFrame * sampleSize; + } else { + entry.size = hasDefaultStszSize_ ? sampleSizes_[0] : sampleSizes_[sampleIndex]; + } + return true; +} + +void MP4SampleHelper::GetSampleFlag(uint32_t sampleIndex, SampleIndexEntry &entry, uint32_t &syncIndex) +{ + if (trackType_ == VIDEO_TYPE || trackType_ == AUDIO_TYPE || trackType_ == TIMEDMETA_TYPE) { + if (!stssEntry_.empty()) { + if ((syncIndex < stssEntry_.size()) && (sampleIndex == stssEntry_[syncIndex].index)) { + entry.flag |= Sample::SampleFlag::SYNC_FRAME; + stssEntry_[syncIndex].pts = entry.pts; + ++syncIndex; + } else { + entry.flag |= Sample::SampleFlag::NONE; + } + } else { + entry.flag |= Sample::SampleFlag::SYNC_FRAME; + } + } else if (trackType_ == SUBTITLE_TYPE) { + if (mimeType_ == MimeType::TEXT_WEBVTT) { + if (entry.size == VTTE_SIZE) { + entry.flag |= Sample::SampleFlag::DISCARD; + } else { + entry.flag |= Sample::SampleFlag::SYNC_FRAME; + } + } + } +} + +Status MP4SampleHelper::BuildSampleIndexEntries() +{ + MEDIA_LOG_D("Start"); + FALSE_RETURN_V_MSG_E(PreBuildProcessAndCheck() == Status::OK, Status::ERROR_INVALID_DATA, + "PreBuildProcessAndCheck failed"); + int64_t currentOffset = chunkOffsets_[0]; + uint32_t chunkIndex = 0; + uint32_t stscIndex = 0; + uint32_t sampleIndex = 0; + uint32_t syncIndex = 0; + int64_t currentDts = 0; + uint32_t sampleInChunk = 0; + uint32_t samplesPerFrame = 1; + int64_t remainSamples = static_cast(stscEntry_[stscIndex].count); + + for (const auto& sttsEntry : sttsEntry_) { // 遍历sttsEntry + uint32_t sampleInSttsEntry = 0; + while (sampleInSttsEntry < sttsEntry.count) { // 根据sttsEntry.count进行循环 + sampleIndexEntry_.emplace_back(SampleIndexEntry { + .dts = currentDts, + .pts = cttsEntry_.empty() ? currentDts : (currentDts + cttsEntry_[sampleIndex].delta), + .pos = currentOffset, + .flag = Sample::NONE, + .size = 0 + }); + FALSE_RETURN_V_MSG_E(GetSampleSize(sampleIndex, samplesPerFrame, sampleIndexEntry_.back(), remainSamples), + Status::ERROR_INVALID_DATA, "Sample size is too large"); + GetSampleFlag(sampleIndex, sampleIndexEntry_.back(), syncIndex); + + lastPTS_ = std::max(lastPTS_, sampleIndexEntry_.back().pts); + currentDts += sttsEntry.delta * samplesPerFrame; + currentOffset += sampleIndexEntry_.back().size; + FALSE_RETURN_V_MSG_E(currentDts >= 0 && currentOffset >= 0, Status::ERROR_INVALID_DATA, + "Sample DTS or Offset is negative"); + ++sampleIndex; + sampleInSttsEntry += samplesPerFrame; + sampleInChunk += samplesPerFrame; + if (sampleInChunk >= stscEntry_[stscIndex].count) { + ++chunkIndex; + sampleInChunk = 0; + stscIndex = (chunkIndex == stscEntry_[stscIndex + 1].first && stscIndex < stscEntry_.size() - 1) ? + stscIndex + 1 : stscIndex; + remainSamples = stscEntry_[stscIndex].count; + currentOffset = chunkOffsets_[chunkIndex]; + } + } + } + + totalDuration_ = currentDts; + return Status::OK; +} + +Status MP4SampleHelper::GetSampleDuration(uint32_t index, uint32_t &duration) +{ + if (sampleIndexEntry_.empty() || sttsEntry_.empty()) { + return Status::ERROR_INVALID_OPERATION; + } + + if (firstMoofSampleIndex_ > 0) { + if (index < firstMoofSampleIndex_) { + uint32_t sttsIndex = 0; + while (index > 0 && sttsIndex < sttsEntry_.size()) { + index -= sttsEntry_[sttsIndex].count; + ++sttsIndex; + } + duration = sttsEntry_[sttsIndex].delta; + } else { + uint32_t sttsIndex = index - numMoovSamples_ + firstMoofSttsIndex_ - 1; + duration = sttsEntry_[sttsIndex].delta; + } + return Status::OK; + } + + int64_t temp = index; + uint32_t sttsIndex = 0; + while (temp > 0 && sttsIndex < sttsEntry_.size()) { + temp -= sttsEntry_[sttsIndex].count; + ++sttsIndex; + } + if (temp > 0) { + return Status::ERROR_INVALID_PARAMETER; + } + duration = sttsEntry_[sttsIndex].delta; + return Status::OK; +} +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_sample_helper.h b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_sample_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..786f8b4febb8652d822f0b7fa025da7f5f91ba67 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_sample_helper.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2025 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 MP4_SAMPLE_HELPER_H +#define MP4_SAMPLE_HELPER_H + +#include +#include +#include "common/status.h" +#include "block_queue_pool.h" +#include "demuxer_data_reader.h" +#include "mp4_utils.h" + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +enum TrunFlag : uint32_t { + TRUN_DATA_OFFSET = 0x01, + TRUN_FIRST_SAMPLE_FLAGS = 0x04, + TRUN_SAMPLE_DURATION = 0x100, + TRUN_SAMPLE_SIZE = 0x200, + TRUN_SAMPLE_FLAGS = 0x400, + TRUN_SAMPLE_CTS = 0x800 +}; + +class MP4SampleHelper { +public: + MP4SampleHelper(); + ~MP4SampleHelper(); + + Status FindSyncSampleAtTime(int64_t reqTime, bool isBaseTrack, SeekMode flag, uint32_t &sampleIndex); + Status SetTimeToSampleParams(int64_t dataOffset, size_t dataSize); + Status SetCompositionTimeToSampleParams(int64_t dataOffset, size_t dataSize); + Status SetSyncSampleParams(int64_t dataOffset, size_t dataSize); + Status SetSampleSizeParams(uint32_t type, int64_t dataOffset, size_t dataSize); + Status SetChunkOffsetParams(uint32_t type, int64_t dataOffset, size_t dataSize); + Status SetSampleToChunkParams(int64_t dataOffset, size_t dataSize); + Status SetTrackExtendsParams(int64_t dataOffset, size_t dataSize); + Status SetTrackFragmentHeaderParams(int64_t dataOffset, int64_t dataSize, uint32_t flag, int64_t moofOffset); + Status SetTrackFragmentDecodeTimeParams(int64_t dataOffset, int64_t dataSize); + Status SetTrackFragmentRunParams(int64_t dataOffset, int64_t dataSize, int64_t sidxDts, int64_t &moofDts, + int64_t &duration); + Status BuildSampleIndexEntries(); + Status GetSampleDuration(uint32_t index, uint32_t &delta); + + struct SampleIndexEntry { + int64_t dts; + int64_t pts; + int64_t pos; + uint32_t flag; + int32_t size; + }; + struct CttsEntry { + uint32_t count; + int32_t delta; + }; + + std::shared_ptr dataReader_; + std::vector sampleIndexEntry_; + std::vector cttsEntry_; + Seekable seekable_; + TrackType trackType_; + std::string mimeType_; + int64_t firstMoofSampleIndex_; + int64_t totalDuration_ = 0; + int64_t totalTrunDuration_ = 0; + int64_t lastPTS_ = 0; + +private: + struct TrackExtendsInfo { + uint32_t trackId; + uint32_t defaultSampleDescriptionIndex; + uint32_t defaultSampleDuration; + uint32_t defaultSampleSize; + uint32_t defaultSampleFlags; + }; + struct SttsEntry { + uint32_t count; + uint32_t delta; + }; + struct StscEntry { + uint32_t first; + uint32_t count; + uint32_t id; + }; + struct StssEntry { + uint32_t index; + int64_t pts; + }; + struct TrackFragmentHeaderInfo { + enum Flags { + TFHD_BASE_DATA_OFFSET = 0x01, + TFHD_STSD_ID = 0x02, + TFHD_DEFAULT_DURATION = 0x08, + TFHD_DEFAULT_SIZE = 0x10, + TFHD_DEFAULT_FLAGS = 0x20, + TFHD_DURATION_IS_EMPTY = 0x010000, + TFHD_DEFAULT_BASE_IS_MOOF = 0x020000 + }; + uint32_t flags; + uint64_t baseDataOffset; + uint32_t sampleDescriptionIndex; + uint32_t defaultSampleDuration; + uint32_t defaultSampleSize; + }; + + std::vector sttsEntry_; + std::vector stscEntry_; + std::vector stssEntry_; + std::vector sampleSizes_; + std::vector chunkOffsets_; + TrackExtendsInfo trex_; + TrackFragmentHeaderInfo tfhd_; + int64_t tfdtDts_ = -1; + int64_t baseFragmentDts_ = -1; + int64_t firstMoofSttsIndex_ = 0; + uint32_t numMoovSamples_ = 0; + bool hasTfhd_ = false; + bool hasDefaultStszSize_ = false; + + void ProcessHalfByteSampleSizes(const uint8_t* data, uint32_t& entryIndex, uint32_t numSampleSizes); + Status ExpandCttsEntry(uint32_t sttsNum); + Status FindWithSyncSamples(int64_t inputPTS, bool isBaseTrack, SeekMode flag, uint32_t& syncIndex); + Status CheckSyncSample(int64_t inputPTS, bool isBaseTrack, SeekMode flag, int64_t ¤tIndex); + Status SetFieldSize(uint32_t atomType, int64_t ¤tOffset, uint32_t& fieldSize); + Status ProcessSampleSizeData(uint32_t atomType, int64_t currentOffset, uint32_t size, + uint32_t fieldSize, uint32_t numSampleSizes); + Status PreBuildProcessAndCheck(); + bool GetSampleSize(uint32_t sampleIndex, uint32_t& samplesPerFrame, SampleIndexEntry& entry, + int64_t& remainSamples); + void GetSampleFlag(uint32_t sampleIndex, SampleIndexEntry& entry, uint32_t& syncIndex); +}; +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif // MP4_SAMPLE_HELPER_H \ No newline at end of file diff --git a/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_utils.h b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..e311ed5eae64d0faca60acc88ac39c4566d6c851 --- /dev/null +++ b/services/media_engine/plugins/demuxer/mp4_demuxer/mp4_utils.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 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 MP4_UTILS_H +#define MP4_UTILS_H + +#include "common/log.h" +#include "plugin/plugin_info.h" + +namespace OHOS { +namespace Media { +namespace Plugins { +namespace MP4 { +constexpr int32_t MS_TO_NS = 1000000; +constexpr uint64_t S_TO_US = 1000000; + +const int32_t FOURCC_TYPE_LENGTH = 5; +const int32_t THIRD_CHAR = 2; +const int32_t FOURTH_CHAR = 3; + +enum TrackType : int32_t { + AUDIO_TYPE = 0, + VIDEO_TYPE = 1, + SUBTITLE_TYPE = 2, + ATTACHMENT_TYPE = 3, + TIMEDMETA_TYPE = 5, + INVALID_TYPE = -1, +}; + +inline std::string FourccToString(int32_t fourcc) +{ + return std::string { + static_cast((fourcc >> 0x18) & 0xFF), + static_cast((fourcc >> 0x10) & 0xFF), + static_cast((fourcc >> 0x08) & 0xFF), + static_cast(fourcc & 0xFF) + }; +} + +inline uint16_t GetU16Value(const uint8_t *data) +{ + if (!data) { + return 0; + } + return static_cast((static_cast(data[0]) << 0x08) | data[1]); +} + +inline uint32_t GetU32Value(const uint8_t *data) +{ + if (!data) { + return 0; + } + return (static_cast(data[0]) << 0x18) | + (static_cast(data[1]) << 0x10) | + (static_cast(data[THIRD_CHAR]) << 0x08) | + static_cast(data[FOURTH_CHAR]); +} + +inline uint64_t GetU64Value(const uint8_t *data) +{ + if (!data) { + return 0; + } + return (static_cast(GetU32Value(data)) << 0x20) | GetU32Value(data + 0x04); +} + +inline double GetDoubleValue(const uint8_t* data) +{ + uint64_t intVal = GetU64Value(data); + return *reinterpret_cast(&intVal); +} + +inline Status ConvertTimeScale(int64_t multiplicand, int64_t multiplier, int64_t timeScale, int64_t* result) +{ + int64_t temp; + if (__builtin_mul_overflow(multiplicand, multiplier, &temp) || timeScale == 0) { + return Status::ERROR_INVALID_DATA; + } + *result = temp / timeScale; + + return Status::OK; +} + +template +constexpr int32_t FourccType(const char (&str)[N]) +{ + if (N != FOURCC_TYPE_LENGTH) { + return -1; + } + return (static_cast(str[0]) << 0x18) | + (static_cast(str[1]) << 0x10) | + (static_cast(str[THIRD_CHAR]) << 0x08) | + static_cast(str[FOURTH_CHAR]); +} +} // namespace MP4 +} // namespace Plugins +} // namespace Media +} // namespace OHOS +#endif // MP4_UTILS_H \ No newline at end of file diff --git a/services/media_engine/plugins/ffmpeg_adapter/BUILD.gn b/services/media_engine/plugins/ffmpeg_adapter/BUILD.gn index a88f4e8fc5ccb6d24cff17096e20c0ba6f85aa5f..a8d70c63373e4a930c0ba9b2e03b37f89291fab4 100644 --- a/services/media_engine/plugins/ffmpeg_adapter/BUILD.gn +++ b/services/media_engine/plugins/ffmpeg_adapter/BUILD.gn @@ -77,59 +77,3 @@ ohos_shared_library("media_plugin_FFmpegMuxer") { subsystem_name = "multimedia" part_name = "av_codec" } - -ohos_shared_library("media_plugin_FFmpegDemuxer") { - branch_protector_ret = "pac_ret" - install_enable = true - - sanitize = av_codec_sanitize - - defines = [] - - defines += [ - "OHOS_EXPAND_MP4_INFO", - "OHOS_AV3A_DEMUXER", - ] - defines += av_codec_defines - - public_configs = [ ":ffmpeg_adapter_config" ] - - if (build_variant != "user") { - defines += [ "BUILD_ENG_VERSION" ] - } - - configs = [ - ":ffmpeg_adapter_config", - "$av_codec_root_dir/services/dfx:av_codec_service_log_dfx_public_config", - ] - - sources = [ - "common/ffmpeg_converter.cpp", - "common/ffmpeg_utils.cpp", - "common/reference_parser_manager.cpp", - "common/multi_stream_parser_manager.cpp", - "demuxer/block_queue_pool.cpp", - "demuxer/demuxer_log_compressor.cpp", - "demuxer/ffmpeg_demuxer_plugin.cpp", - "demuxer/ffmpeg_demuxer_thread.cpp", - "demuxer/ffmpeg_format_helper.cpp", - "demuxer/ffmpeg_reference_parser.cpp", - ] - - deps = [ "$av_codec_root_dir/services/dfx:av_codec_service_dfx" ] - - external_deps = [ - "c_utils:utils", - "ffmpeg:libohosffmpeg", - "hicollie:libhicollie", - "hilog:libhilog", - "hisysevent:libhisysevent", - "init:libbegetutil", - "ipc:ipc_single", - "media_foundation:media_foundation", - ] - - relative_install_dir = "media/media_plugins" - subsystem_name = "multimedia" - part_name = "av_codec" -} diff --git a/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.h b/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.h deleted file mode 100644 index c3fb2a6734dfd958666efa99f14e409638f1f780..0000000000000000000000000000000000000000 --- a/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2023 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 BLOCK_QUEUE_POOL_H -#define BLOCK_QUEUE_POOL_H -#include -#include -#include -#include "block_queue.h" -#include "common/status.h" - -#ifdef __cplusplus -extern "C" { -#endif -#include "libavcodec/avcodec.h" -#ifdef __cplusplus -} -#endif - -namespace OHOS { -namespace Media { - -struct SamplePacket { - uint32_t offset = 0; - std::vector pkts {}; - bool isEOS = false; - bool isAnnexb = false; - uint32_t queueIndex = 0; - ~SamplePacket() - { - for (auto pkt : pkts) { - if (pkt) { - av_packet_free(&pkt); - } - } - } -}; - -class BlockQueuePool { -public: - explicit BlockQueuePool(std::string name, size_t singleQueSize = SINGLE_QUEUE_SIZE) - : name_(std::move(name)), quePool_(), queMap_(), sizeMap_(), singleQueSize_(singleQueSize) - { - } - ~BlockQueuePool(); - - Status AddTrackQueue(uint32_t trackIndex); - Status RemoveTrackQueue(uint32_t trackIndex); - bool HasCache(uint32_t trackIndex); - size_t GetCacheSize(uint32_t trackIndex); - uint32_t GetCacheDataSize(uint32_t trackIndex); - bool ResetInfo(std::shared_ptr block); - bool SetInfo(std::shared_ptr block); - void FreeQueue(uint32_t queueIndex); - bool Push(uint32_t trackIndex, std::shared_ptr block); - std::shared_ptr Pop(uint32_t trackIndex); - std::shared_ptr Front(uint32_t trackIndex); - std::shared_ptr Back(uint32_t trackIndex); - Status GetLastPTSByTrackId(uint32_t trackIndex, int64_t& maxPts); - -private: - struct InnerQueue { - bool isValid {false}; - uint32_t dataSize {0}; - std::shared_ptr>> blockQue {nullptr}; - int64_t maxPts {INT64_MIN}; - }; - static constexpr size_t SINGLE_QUEUE_SIZE = 100; - std::string name_; - uint32_t queCount_ {0}; - std::map quePool_; - std::map> queMap_; - std::map sizeMap_; - size_t singleQueSize_ {0}; - - uint32_t GetValidQueue(); - bool InnerQueueIsFull(uint32_t queueIndex); - bool HasQueue(uint32_t trackIndex); - void ResetQueue(uint32_t queueIndex); - std::recursive_mutex mutextCacheQ_ {}; -}; -} // namespace Media -} // namespace OHOS -#endif // BLOCK_QUEUE_POOL_H \ No newline at end of file diff --git a/test/BUILD.gn b/test/BUILD.gn index 64c61969dfe4e1a07fb6a441896cd6e33bc8106f..227c777f42b2a593de44d1dcc2aba4bb5b0278f1 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -67,6 +67,8 @@ group("av_codec_unit_test") { "unittest/avmuxer_test:avmuxer_inner_unit_test", "unittest/avsource_test:avsource_capi_unit_test", "unittest/avsource_test:avsource_inner_unit_test", + "unittest/mp4_source_test:mp4_source_capi_unit_test", + "unittest/mp4_source_test:mp4_source_inner_unit_test", "unittest/calc_max_amplitude_unittest:calc_max_amplitude_unittest", "unittest/codec_capability_adapter_unittest:codec_capability_adapter_unittest", "unittest/dash_media_downloader_unittest:dash_media_downloader_unittest", @@ -99,6 +101,10 @@ group("av_codec_unit_test") { "unittest/demuxer_test:demuxer_inner_buffer_unit_test", "unittest/demuxer_test:demuxer_inner_unit_test", "unittest/demuxer_unit_test:demuxer_unit_test", + "unittest/mp4_demuxer_test:mp4_demuxer_capi_buffer_unit_test", + "unittest/mp4_demuxer_test:mp4_demuxer_capi_unit_test", + "unittest/mp4_demuxer_test:mp4_demuxer_inner_buffer_unit_test", + "unittest/mp4_demuxer_test:mp4_demuxer_inner_unit_test", "unittest/filter_test:filter_unit_test", "unittest/hls_test:hls_media_downloader_unit_test", "unittest/hls_test:hls_playlist_downloader_unit_test", diff --git a/test/fuzztest/demuxerpluginaac_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginaac_fuzzer/BUILD.gn index 1e676b480d732ee152cc65c81197121383abbb34..7584021b8933666581a564cebddd4afeb7f1a87f 100644 --- a/test/fuzztest/demuxerpluginaac_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginaac_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginAacFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginamr_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginamr_fuzzer/BUILD.gn index 032e5a95d91e8a6d013949026f4bbc10caf60cf1..bcc921b1044ae085a92582366f7758bd96ddab85 100644 --- a/test/fuzztest/demuxerpluginamr_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginamr_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginAmrFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginape_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginape_fuzzer/BUILD.gn index eb18c00cd4f9f40c18135afc094e31cfad3c9ca5..e93fe50eef505eb2c1965b52d0ede29743c550ee 100644 --- a/test/fuzztest/demuxerpluginape_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginape_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginApeFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginavi_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginavi_fuzzer/BUILD.gn index 668bad6a23280deee29d12301ce0a84cfe679175..0395324a6b50f73b9076e7d21f18775d42f3a11d 100644 --- a/test/fuzztest/demuxerpluginavi_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginavi_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginAviFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginflac_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginflac_fuzzer/BUILD.gn index d0c3a4b8b301019ee016862e6738c297f9672a1a..97a86ebcec27b038488107a5a775645c6f79d01a 100644 --- a/test/fuzztest/demuxerpluginflac_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginflac_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginFlacFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginflv_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginflv_fuzzer/BUILD.gn index 36c95650b05c1faaaf8b2b7cd172eea2be8aedce..e63bdeee9b544919e98323eac6adba0bef602a72 100644 --- a/test/fuzztest/demuxerpluginflv_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginflv_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginFlvFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginflv_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginflvtest_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginflvtest_fuzzer/BUILD.gn index 300854435be5c7e90a294962a27bdac50ad9e174..f51e01c742c8f2d9dab63664b75a7df85147e1f4 100644 --- a/test/fuzztest/demuxerpluginflvtest_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginflvtest_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginFlvtestFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginfmp4_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginfmp4_fuzzer/BUILD.gn index c728b59c647db547b8de8c9a569ac6d5b4da4c63..d2d9472148977022be17e1289f40be4a7e7d19d0 100644 --- a/test/fuzztest/demuxerpluginfmp4_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginfmp4_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginFmp4FuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginm4a_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginm4a_fuzzer/BUILD.gn index c79bf49f0a8610e22c384dd6fc697432c05fa615..23194d2df7cb8c9c4151615cda2492d8c9042ed5 100644 --- a/test/fuzztest/demuxerpluginm4a_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginm4a_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginM4aFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginmkv_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginmkv_fuzzer/BUILD.gn index 57d8d9c2bef11b616aef70fc4f5f7df4b84ebd85..a10ca47b7042323910145274256d8671b087ce0d 100644 --- a/test/fuzztest/demuxerpluginmkv_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginmkv_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginMkvFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginmov_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginmov_fuzzer/BUILD.gn index b0ee8e45174cf25d7f8313a054e1e0d136319dc4..85926cdc0e877c956bc558e9bd839dac9189c8bb 100644 --- a/test/fuzztest/demuxerpluginmov_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginmov_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginMovFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginmp3_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginmp3_fuzzer/BUILD.gn index 7369fed0a0a18a97c63f24e04347ffdc8945717d..08a4e5343487f5530faeee00759060438583bc51 100644 --- a/test/fuzztest/demuxerpluginmp3_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginmp3_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginMp3FuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginmp4_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginmp4_fuzzer/BUILD.gn index 229ed4b5cf50c2773cf4799fa32b0602d13464a9..2e78c03642d1f063aa4669b74b4a19b36de8d2a0 100644 --- a/test/fuzztest/demuxerpluginmp4_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginmp4_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginMp4FuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginmpegts_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginmpegts_fuzzer/BUILD.gn index 6a023454665735f6c403b06aaf027187ded9d16b..6292265f6a3fd298995d2bb30d017347d918c7c9 100644 --- a/test/fuzztest/demuxerpluginmpegts_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginmpegts_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginMpegtsFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginmpg_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginmpg_fuzzer/BUILD.gn index 778803aed74fbbd48927ecdb034aea2d2d212c95..141b988f9e7c2a4d4ec58261d5d243a8b5f1ec94 100644 --- a/test/fuzztest/demuxerpluginmpg_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginmpg_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginMpgFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginogg_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginogg_fuzzer/BUILD.gn index 7da336eee0a4d7434964b0da89d1ce9b34e5672c..0c3eaaa1c0dc917214865edcdcb2091ce7b9da47 100644 --- a/test/fuzztest/demuxerpluginogg_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginogg_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginOggFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginsrt_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginsrt_fuzzer/BUILD.gn index e3b9f407c5fa7e56f2117fd3acdb8257381f2a44..ddc0f90867d8c21b2d8d80a6ea8c75e2e13c885e 100644 --- a/test/fuzztest/demuxerpluginsrt_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginsrt_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginSrtFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginvtt_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginvtt_fuzzer/BUILD.gn index 16a2e98b8e9bcc70957bbde25ca1bd30a4358090..a36d2446428a181809875ce2d1e28473a8c067a3 100644 --- a/test/fuzztest/demuxerpluginvtt_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginvtt_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginVttFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/fuzztest/demuxerpluginwav_fuzzer/BUILD.gn b/test/fuzztest/demuxerpluginwav_fuzzer/BUILD.gn index f394b9eaeb193cc1033ec2cf06c67012c39ef8b7..4f588d3515e0667d09904d84daf359d5ae8ef9c9 100644 --- a/test/fuzztest/demuxerpluginwav_fuzzer/BUILD.gn +++ b/test/fuzztest/demuxerpluginwav_fuzzer/BUILD.gn @@ -25,7 +25,7 @@ ohos_fuzztest("DemuxerPluginWavFuzzTest") { include_dirs = [ "$MEDIA_ROOT_DIR/interfaces/kits/c", "$MEDIA_ROOT_DIR/test/fuzztest/demuxerpluginmp4_fuzzer", - "$MEDIA_ROOT_DIR/services/media_engine/plugins/ffmpeg_adapter/demuxer", + "$MEDIA_ROOT_DIR/services/media_engine/plugins/demuxer/ffmpeg_demuxer", ] cflags = [ "-g", diff --git a/test/unittest/avsource_test/avsource_unit_test.cpp b/test/unittest/avsource_test/avsource_unit_test.cpp index eea04c3096da4708fe02d87403ea9ed74b41c6d6..7fc27fb78aff1a442d738151fcae45a2d381c1ea 100644 --- a/test/unittest/avsource_test/avsource_unit_test.cpp +++ b/test/unittest/avsource_test/avsource_unit_test.cpp @@ -2987,14 +2987,15 @@ HWTEST_F(AVSourceUnitTest, AVSource_GetFormat_1808, TestSize.Level1) */ HWTEST_F(AVSourceUnitTest, AVSource_GetFormat_Auxl_0003, TestSize.Level1) { - if (access(HEVC_LIB_PATH.c_str(), F_OK) == 0) { - InitResource(g_mp4AuxlPath, LOCAL); - ASSERT_TRUE(initStatus_); - CheckAuxlAvc(); - ASSERT_TRUE(checkPass_); - CheckAuxlAac(); - ASSERT_TRUE(checkPass_); + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; } + InitResource(g_mp4AuxlPath, LOCAL); + ASSERT_TRUE(initStatus_); + CheckAuxlAvc(); + ASSERT_TRUE(checkPass_); + CheckAuxlAac(); + ASSERT_TRUE(checkPass_); } /** @@ -3004,14 +3005,15 @@ HWTEST_F(AVSourceUnitTest, AVSource_GetFormat_Auxl_0003, TestSize.Level1) */ HWTEST_F(AVSourceUnitTest, AVSource_GetFormat_Auxl_0004, TestSize.Level1) { - if (access(HEVC_LIB_PATH.c_str(), F_OK) == 0) { - InitResource(g_mp4AuxlUri, URI); - ASSERT_TRUE(initStatus_); - CheckAuxlAvc(); - ASSERT_TRUE(checkPass_); - CheckAuxlAac(); - ASSERT_TRUE(checkPass_); + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; } + InitResource(g_mp4AuxlUri, URI); + ASSERT_TRUE(initStatus_); + CheckAuxlAvc(); + ASSERT_TRUE(checkPass_); + CheckAuxlAac(); + ASSERT_TRUE(checkPass_); } /** diff --git a/test/unittest/demuxer_plugin_test/BUILD.gn b/test/unittest/demuxer_plugin_test/BUILD.gn index 59ecfd0304b472ecf8282422136f91bbb32f6bc8..95fe32f199117d546e44a4f116474498f96bb667 100644 --- a/test/unittest/demuxer_plugin_test/BUILD.gn +++ b/test/unittest/demuxer_plugin_test/BUILD.gn @@ -21,12 +21,10 @@ demuxer_plugin_test_sources = [ "$av_codec_root_dir/services/media_engine/modules/demuxer/stream_demuxer.cpp", "$av_codec_root_dir/services/media_engine/modules/demuxer/type_finder.cpp", "$av_codec_root_dir/services/media_engine/modules/source/source.cpp", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_plugin.cpp", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_demuxer_thread.cpp", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/demuxer/block_queue_pool.cpp", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/demuxer/ffmpeg_format_helper.cpp", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/common/ffmpeg_convert.cpp", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/common/ffmpeg_utils.cpp", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_plugin.cpp", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_demuxer_thread.cpp", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_format_helper.cpp", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/ffmpeg_demuxer/ffmpeg_utils.cpp", "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", ] config("demuxer_plugin_test_unittest_cfg") { @@ -66,8 +64,8 @@ config("demuxer_plugin_test_unittest_cfg") { "$av_codec_root_dir/services/media_engine/modules", "$av_codec_root_dir/services/media_engine/modules/demuxer", "$av_codec_root_dir/test/unittest/common", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/demuxer", - "$av_codec_root_dir/services/media_engine/plugins/ffmpeg_adapter/common", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/ffmpeg_demuxer", + "$av_codec_root_dir/services/media_engine/plugins/demuxer/common", "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", "$av_codec_root_dir/test/nativedemo/include", ] diff --git a/test/unittest/mp4_demuxer_test/BUILD.gn b/test/unittest/mp4_demuxer_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b2a167b08a40356301b0d346e4064542f5ca3b9c --- /dev/null +++ b/test/unittest/mp4_demuxer_test/BUILD.gn @@ -0,0 +1,303 @@ +# Copyright (c) 2025 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/test.gni") +import("//foundation/multimedia/av_codec/config.gni") + +module_output_path = "av_codec/av_codec/unittest" + +mp4_demuxer_unittest_cflags = [ + "-std=c++17", + "-fno-rtti", + "-fno-exceptions", + "-Wall", + "-fno-common", + "-fstack-protector-strong", + "-Wshadow", + "-FPIC", + "-FS", + "-O2", + "-D_FORTIFY_SOURCE=2", + "-fvisibility=hidden", + "-Wformat=2", + "-Wdate-time", + "-Werror", + "-Wextra", + "-Wimplicit-fallthrough", + "-Wsign-compare", + "-Wunused-parameter", + "-Dprivate=public", + "-Dprotected=public", +] + +################################################################################################################# +ohos_unittest("mp4_demuxer_capi_unit_test") { + sanitize = av_codec_test_sanitize + module_out_path = module_output_path + testonly = true + include_dirs = [ + "./", + "./capi", + "$av_codec_root_dir/interfaces/inner_api/native", + "$av_codec_root_dir/interfaces/kits/c", + "$av_codec_root_dir/frameworks/native/capi/common", + "$av_codec_root_dir/test/unittest/format_test", + "$av_codec_root_dir/test/unittest/format_test/capi", + "$av_codec_root_dir/test/unittest/mp4_source_test", + "$av_codec_root_dir/test/unittest/mp4_source_test/capi", + "$av_codec_root_dir/test/unittest/common/common_mock", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory/capi", + "$av_codec_root_dir/test/nativedemo/include", + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", + "$av_codec_root_dir/services/utils/include", + "$av_codec_root_dir/services/media_engine/modules/demuxer", + ] + + cflags = mp4_demuxer_unittest_cflags + + if (av_codec_support_demuxer) { + sources = [ + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/capi/mp4_source_capi_mock_factory.cpp", + "./capi/mp4_demuxer_capi_mock.cpp", + "./capi/mp4_demuxer_capi_mock_factory.cpp", + "./mp4_demuxer_stream_unit_test.cpp", + "./mp4_demuxer_unit_test.cpp", + "./mp4_demuxer_uri_unit_test.cpp", + ] + } + + if (target_cpu == "arm64" || is_emulator) { + av_codec_path = "\"/system/lib64\"" + } else { + av_codec_path = "\"/system/lib\"" + } + defines = [ + "AV_CODEC_PATH=${av_codec_path}", + "MP4_DEMUXER_CAPI_UNIT_TEST", + ] + defines += av_codec_defines + public_deps = + [ "$av_codec_root_dir/test/unittest/common:av_codec_capi_unit_test" ] + deps = [ + "$av_codec_root_dir/interfaces/kits/c:capi_packages", + "$av_codec_root_dir/services/engine/base:av_codec_codec_base", + "$av_codec_root_dir/services/media_engine/modules:av_codec_media_engine_modules", + "$av_codec_root_dir/services/utils:av_codec_service_utils", + ] + external_deps = [ + "av_codec:av_codec_client", + "c_utils:utils", + "googletest:gmock_main", + "graphic_surface:surface", + "graphic_surface:sync_fence", + "hilog:libhilog", + "hitrace:libhitracechain", + "init:libbegetutil", + "ipc:ipc_single", + "media_foundation:media_foundation", + "safwk:system_ability_fwk", + ] + if (av_codec_support_drm) { + external_deps += [ + "drm_framework:drm_framework", + "drm_framework:native_drm", + ] + } + + resource_config_file = + "$av_codec_root_dir/test/unittest/resources/ohos_test.xml" +} + +#################################################################################################################muxer +ohos_unittest("mp4_demuxer_inner_unit_test") { + sanitize = av_codec_test_sanitize + module_out_path = module_output_path + include_dirs = [ + "./", + "./inner", + "$av_codec_root_dir/interfaces/inner_api/inner", + "$av_codec_root_dir/interfaces/inner_api/native", + "$av_codec_root_dir/interfaces/kits/c", + "$av_codec_root_dir/test/unittest/format_test", + "$av_codec_root_dir/test/unittest/format_test/inner", + "$av_codec_root_dir/test/unittest/mp4_source_test", + "$av_codec_root_dir/test/unittest/mp4_source_test/inner", + "$av_codec_root_dir/test/unittest/common/common_mock", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory/inner", + "$av_codec_root_dir/test/nativedemo/include", + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", + "$av_codec_root_dir/services/utils/include", + ] + + cflags = mp4_demuxer_unittest_cflags + + if (av_codec_support_demuxer) { + sources = [ + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/inner/mp4_source_inner_mock_factory.cpp", + "./mp4_demuxer_stream_unit_test.cpp", + "./mp4_demuxer_unit_test.cpp", + "./mp4_demuxer_uri_unit_test.cpp", + "./inner/mp4_demuxer_inner_mock.cpp", + "./inner/mp4_demuxer_inner_mock_factory.cpp", + ] + } + + if (target_cpu == "arm64" || is_emulator) { + av_codec_path = "\"/system/lib64\"" + } else { + av_codec_path = "\"/system/lib\"" + } + defines = [ "AV_CODEC_PATH=${av_codec_path}" ] + public_deps = + [ "$av_codec_root_dir/test/unittest/common:av_codec_inner_unit_test" ] + deps = [ "$av_codec_root_dir/services/utils:av_codec_service_utils" ] + external_deps = [ + "av_codec:av_codec_client", + "c_utils:utils", + "graphic_surface:surface", + "hilog:libhilog", + "ipc:ipc_single", + ] + + resource_config_file = + "$av_codec_root_dir/test/unittest/resources/ohos_test.xml" +} + +################################################################################################################# +ohos_unittest("mp4_demuxer_capi_buffer_unit_test") { + sanitize = av_codec_test_sanitize + module_out_path = module_output_path + include_dirs = [ + "./", + "./capi", + "$av_codec_root_dir/interfaces/inner_api/native", + "$av_codec_root_dir/interfaces/kits/c", + "$av_codec_root_dir/frameworks/native/capi/common", + "$av_codec_root_dir/test/unittest/format_test", + "$av_codec_root_dir/test/unittest/format_test/capi", + "$av_codec_root_dir/test/unittest/mp4_source_test", + "$av_codec_root_dir/test/unittest/mp4_source_test/capi", + "$av_codec_root_dir/test/unittest/common/common_mock", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory/capi", + "$av_codec_root_dir/test/nativedemo/include", + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", + "$av_codec_root_dir/services/utils/include", + ] + + cflags = mp4_demuxer_unittest_cflags + + if (av_codec_support_demuxer) { + sources = [ + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/capi/mp4_source_capi_mock_factory.cpp", + "./capi/mp4_demuxer_capi_buffer_mock.cpp", + "./capi/mp4_demuxer_capi_mock_factory.cpp", + "./mp4_demuxer_stream_unit_test.cpp", + "./mp4_demuxer_unit_test.cpp", + "./mp4_demuxer_uri_unit_test.cpp", + ] + } + + if (target_cpu == "arm64" || is_emulator) { + av_codec_path = "\"/system/lib64\"" + } else { + av_codec_path = "\"/system/lib\"" + } + defines = [ + "AV_CODEC_PATH=${av_codec_path}", + "MP4_DEMUXER_CAPI_UNIT_TEST", + "MP4_DEMUXER_CAPI_BUFFER_UNIT_TEST", + ] + public_deps = + [ "$av_codec_root_dir/test/unittest/common:av_codec_capi_unit_test" ] + deps = [ + "$av_codec_root_dir/interfaces/kits/c:capi_packages", + "$av_codec_root_dir/services/utils:av_codec_service_utils", + ] + external_deps = [ + "av_codec:av_codec_client", + "c_utils:utils", + "graphic_surface:surface", + "hilog:libhilog", + "ipc:ipc_single", + ] + + resource_config_file = + "$av_codec_root_dir/test/unittest/resources/ohos_test.xml" +} + +# #################################################################################################################muxer +ohos_unittest("mp4_demuxer_inner_buffer_unit_test") { + sanitize = av_codec_test_sanitize + module_out_path = module_output_path + include_dirs = [ + "./", + "./inner", + "$av_codec_root_dir/interfaces/inner_api/inner", + "$av_codec_root_dir/interfaces/inner_api/native", + "$av_codec_root_dir/interfaces/kits/c", + "$av_codec_root_dir/test/unittest/format_test", + "$av_codec_root_dir/test/unittest/format_test/inner", + "$av_codec_root_dir/test/unittest/mp4_source_test", + "$av_codec_root_dir/test/unittest/mp4_source_test/inner", + "$av_codec_root_dir/test/unittest/common/common_mock", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory", + "$av_codec_root_dir/test/unittest/common/common_mock/avmemory/inner", + "$av_codec_root_dir/test/nativedemo/include", + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", + "$av_codec_root_dir/services/utils/include", + ] + + cflags = mp4_demuxer_unittest_cflags + + if (av_codec_support_demuxer) { + sources = [ + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.cpp", + "$av_codec_root_dir/test/unittest/mp4_source_test/inner/mp4_source_inner_mock_factory.cpp", + "./mp4_demuxer_stream_unit_test.cpp", + "./mp4_demuxer_unit_test.cpp", + "./mp4_demuxer_uri_unit_test.cpp", + "./inner/mp4_demuxer_inner_buffer_mock.cpp", + "./inner/mp4_demuxer_inner_mock_factory.cpp", + ] + } + + if (target_cpu == "arm64" || is_emulator) { + av_codec_path = "\"/system/lib64\"" + } else { + av_codec_path = "\"/system/lib\"" + } + defines = [ "AV_CODEC_PATH=${av_codec_path}" ] + public_deps = + [ "$av_codec_root_dir/test/unittest/common:av_codec_inner_unit_test" ] + deps = [ "$av_codec_root_dir/services/utils:av_codec_service_utils" ] + external_deps = [ + "av_codec:av_codec_client", + "c_utils:utils", + "graphic_surface:surface", + "hilog:libhilog", + "ipc:ipc_single", + ] + + resource_config_file = + "$av_codec_root_dir/test/unittest/resources/ohos_test.xml" +} diff --git a/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_buffer_mock.cpp b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_buffer_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..969185354baaa3b64ede89c247ecdb0601c80f84 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_buffer_mock.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2025 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 "native_averrors.h" +#include "avmemory_capi_mock.h" +#include "mp4_demuxer_capi_mock.h" +#include "native_avbuffer.h" + +namespace OHOS { +namespace MediaAVCodec { +int32_t Mp4DemuxerCapiMock::Destroy() +{ + if (demuxer_ != nullptr) { + int32_t ret = OH_AVDemuxer_Destroy(demuxer_); + demuxer_ = nullptr; + return ret; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::SelectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return OH_AVDemuxer_SelectTrackByID(demuxer_, trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::UnselectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return OH_AVDemuxer_UnselectTrackByID(demuxer_, trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo) +{ + auto mem = std::static_pointer_cast(sample); + if (mem == nullptr) { + printf("AVMemoryCapiMock is nullptr\n"); + return AV_ERR_UNKNOWN; + } + int32_t cap = OH_AVMemory_GetSize(mem->GetAVMemory()); + OH_AVBuffer *avBuffer = OH_AVBuffer_Create(cap); + if (avBuffer == nullptr) { + printf("OH_AVBuffer is nullptr\n"); + return AV_ERR_UNKNOWN; + } + if (demuxer_ != nullptr) { + int32_t ret = OH_AVDemuxer_ReadSampleBuffer(demuxer_, trackIndex, avBuffer); + if (ret != AV_ERR_OK) { + return ret; + } + OH_AVCodecBufferAttr bufferAttr; + ret = OH_AVBuffer_GetBufferAttr(avBuffer, &bufferAttr); + if (ret != AV_ERR_OK) { + return ret; + } + bufferInfo->presentationTimeUs = bufferAttr.pts; + bufferInfo->size = bufferAttr.size; + bufferInfo->offset = bufferAttr.offset; + flag = bufferAttr.flags; + if (checkBufferInfo) { + OH_AVFormat *format = OH_AVBuffer_GetParameter(avBuffer); + if (format == nullptr) { + printf("OH_AVBuffer format is nullptr\n"); + } else { + int64_t duration; + int64_t dts; + OH_AVFormat_GetLongValue(format, OH_MD_KEY_BUFFER_DURATION, &duration); + OH_AVFormat_GetLongValue(format, OH_MD_KEY_DECODING_TIMESTAMP, &dts); + printf("[track %d] duration %" PRId64 " dts %" PRId64 "\n", trackIndex, duration, dts); + } + OH_AVFormat_Destroy(format); + } + OH_AVBuffer_Destroy(avBuffer); + return ret; + } + OH_AVBuffer_Destroy(avBuffer); + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::SeekToTime(int64_t mSeconds, Media::SeekMode mode) +{ + if (demuxer_ != nullptr) { + OH_AVSeekMode seekMode = static_cast(mode); + return OH_AVDemuxer_SeekToTime(demuxer_, mSeconds, seekMode); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::SetMediaKeySystemInfoCallback(bool isNull) +{ + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::SetDemuxerMediaKeySystemInfoCallback(bool isNull) +{ + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::GetMediaKeySystemInfo() +{ + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) +{ + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) +{ + return AV_ERR_OK; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock.cpp b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52beec15b42d5b95397ba5e898783160e94a68d5 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2025 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 "native_averrors.h" +#include "avmemory_capi_mock.h" +#include "mp4_demuxer_capi_mock.h" +#ifdef SUPPORT_DRM +#include "native_drm_common.h" +#endif +namespace OHOS { +namespace MediaAVCodec { + +#ifdef SUPPORT_DRM +static void OnMediaKeySystemInfoUpdated(DRM_MediaKeySystemInfo *drmInfo) +{ + printf("OnMediaKeySystemInfoUpdated \n"); + if (drmInfo == nullptr || drmInfo->psshCount > MAX_PSSH_INFO_COUNT) { + return; + } + printf("OnMediaKeySystemInfoUpdated info count: %d \n", drmInfo->psshCount); + for (uint32_t i = 0; i < drmInfo->psshCount; i++) { + const uint32_t uuidLen = DRM_UUID_LEN; + printf("OnMediaKeySystemInfoUpdated print uuid: \n"); + for (uint32_t index = 0; index < uuidLen; index++) { + printf("%x ", drmInfo->psshInfo[i].uuid[index]); + } + printf(" \n"); + printf("OnMediaKeySystemInfoUpdated print pssh length %d \n", drmInfo->psshInfo[i].dataLen); + if (drmInfo->psshInfo[i].dataLen > MAX_PSSH_DATA_LEN) { + return; + } + unsigned char *pssh = static_cast(drmInfo->psshInfo[i].data); + for (uint32_t k = 0; k < drmInfo->psshInfo[i].dataLen; k++) { + printf("%x ", pssh[k]); + } + printf(" \n"); + } +} +#endif + +#ifdef SUPPORT_DRM +static void OnMediaKeySystemInfoUpdatedWithObj(OH_AVDemuxer *demuxer, DRM_MediaKeySystemInfo *drmInfo) +{ + printf("OnMediaKeySystemInfoUpdatedWithObj \n"); + if (drmInfo == nullptr || drmInfo->psshCount > MAX_PSSH_INFO_COUNT) { + return; + } + printf("OnMediaKeySystemInfoUpdatedWithObj info count: %d \n", drmInfo->psshCount); + for (uint32_t i = 0; i < drmInfo->psshCount; i++) { + const uint32_t uuidLen = DRM_UUID_LEN; + printf("OnMediaKeySystemInfoUpdatedWithObj print uuid: \n"); + for (uint32_t index = 0; index < uuidLen; index++) { + printf("%x ", drmInfo->psshInfo[i].uuid[index]); + } + printf(" \n"); + printf("OnMediaKeySystemInfoUpdatedWithObj print pssh length %d \n", drmInfo->psshInfo[i].dataLen); + if (drmInfo->psshInfo[i].dataLen > MAX_PSSH_DATA_LEN) { + return; + } + unsigned char *pssh = static_cast(drmInfo->psshInfo[i].data); + for (uint32_t k = 0; k < drmInfo->psshInfo[i].dataLen; k++) { + printf("%x ", pssh[k]); + } + printf(" \n"); + } +} +#endif + + +int32_t Mp4DemuxerCapiMock::Destroy() +{ + if (demuxer_ != nullptr) { + int32_t ret = OH_AVDemuxer_Destroy(demuxer_); + demuxer_ = nullptr; + return ret; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::SelectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return OH_AVDemuxer_SelectTrackByID(demuxer_, trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::UnselectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return OH_AVDemuxer_UnselectTrackByID(demuxer_, trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo) +{ + (void)checkBufferInfo; + auto mem = std::static_pointer_cast(sample); + OH_AVMemory *avMemory = (mem != nullptr) ? mem->GetAVMemory() : nullptr; + if (demuxer_ != nullptr) { + OH_AVCodecBufferAttr bufferAttr; + int32_t ret = OH_AVDemuxer_ReadSample(demuxer_, trackIndex, avMemory, &bufferAttr); + bufferInfo->presentationTimeUs = bufferAttr.pts; + bufferInfo->size = bufferAttr.size; + bufferInfo->offset = bufferAttr.offset; + flag = bufferAttr.flags; + return ret; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::SeekToTime(int64_t mSeconds, Media::SeekMode mode) +{ + if (demuxer_ != nullptr) { + OH_AVSeekMode seekMode = static_cast(mode); + return OH_AVDemuxer_SeekToTime(demuxer_, mSeconds, seekMode); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerCapiMock::SetMediaKeySystemInfoCallback(bool isNull) +{ + if (demuxer_ != nullptr) { + if (!isNull) { +#ifdef SUPPORT_DRM + return OH_AVDemuxer_SetMediaKeySystemInfoCallback(demuxer_, &OnMediaKeySystemInfoUpdated); + } else { + return OH_AVDemuxer_SetMediaKeySystemInfoCallback(demuxer_, nullptr); +#endif + } + } + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::SetDemuxerMediaKeySystemInfoCallback(bool isNull) +{ + if (demuxer_ != nullptr) { + if (!isNull) { +#ifdef SUPPORT_DRM + return OH_AVDemuxer_SetDemuxerMediaKeySystemInfoCallback(demuxer_, &OnMediaKeySystemInfoUpdatedWithObj); + } else { + return OH_AVDemuxer_SetDemuxerMediaKeySystemInfoCallback(demuxer_, nullptr); +#endif + } + } + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::GetMediaKeySystemInfo() +{ + if (demuxer_ != nullptr) { +#ifdef SUPPORT_DRM + DRM_MediaKeySystemInfo mediaKeySystemInfo; + return OH_AVDemuxer_GetMediaKeySystemInfo(demuxer_, &mediaKeySystemInfo); +#endif + } + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) +{ + return AV_ERR_OK; +} + +int32_t Mp4DemuxerCapiMock::GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) +{ + return AV_ERR_OK; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock.h b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..b28749dba5562028f1ecf3b37b90129b63b85fa0 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 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 MP4_DEMUXER_CAPI_MOCK_H +#define MP4_DEMUXER_CAPI_MOCK_H + +#include "mp4_demuxer_mock.h" +#include "common_mock.h" +#include "avcodec_common.h" +#include "native_avdemuxer.h" +#include "av_common.h" +#include "native_avmagic.h" + + +namespace OHOS { +namespace MediaAVCodec { +class Mp4DemuxerCapiMock : public Mp4DemuxerMock { +public: + explicit Mp4DemuxerCapiMock(OH_AVDemuxer *demuxer) : demuxer_(demuxer) {} + ~Mp4DemuxerCapiMock() = default; + + int32_t Destroy() override; + int32_t SelectTrackByID(uint32_t trackIndex) override; + int32_t UnselectTrackByID(uint32_t trackIndex) override; + int32_t ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo) override; + int32_t SeekToTime(int64_t mSeconds, Media::SeekMode mode) override; + int32_t SetMediaKeySystemInfoCallback(bool isNull) override; + int32_t SetDemuxerMediaKeySystemInfoCallback(bool isNull) override; + int32_t GetMediaKeySystemInfo() override; + int32_t GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) override; + int32_t GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) override; +private: + OH_AVDemuxer *demuxer_ = nullptr; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_DEMUXER_CAPI_MOCK_H \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock_factory.cpp b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff3aef29d5aafd002f300727166fc29460ce6541 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/capi/mp4_demuxer_capi_mock_factory.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 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 "mp4_demuxer_capi_mock.h" +#include "mp4_source_capi_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +std::shared_ptr Mp4DemuxerMockFactory::CreateDemuxer(std::shared_ptr source) +{ + auto capiSource = std::static_pointer_cast(source); + OH_AVSource *avSource = (capiSource != nullptr) ? capiSource->GetAVSource() : nullptr; + OH_AVDemuxer *demuxer = OH_AVDemuxer_CreateWithSource(avSource); + if (demuxer != nullptr) { + return std::make_shared(demuxer); + } + return nullptr; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_buffer_mock.cpp b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_buffer_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f719496ed77b3a4870f88aa2e178d84fa00b02e --- /dev/null +++ b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_buffer_mock.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2025 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 "avmemory_inner_mock.h" +#include "native_averrors.h" +#include "mp4_demuxer_inner_mock.h" +#include "buffer/avbuffer.h" + +namespace OHOS { +namespace MediaAVCodec { +using namespace Media; +int32_t Mp4DemuxerInnerMock::Destroy() +{ + if (demuxer_ != nullptr) { + demuxer_ = nullptr; + return AV_ERR_OK; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::SelectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return demuxer_->SelectTrackByID(trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::UnselectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return demuxer_->UnselectTrackByID(trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo) +{ + if (bufferInfo == nullptr) { + printf("AVCodecBufferInfo is nullptr"); + return AV_ERR_UNKNOWN; + } + auto mem = std::static_pointer_cast(sample); + std::shared_ptr sharedMem = (mem != nullptr) ? mem->GetAVMemory() : nullptr; + if (sharedMem == nullptr) { + printf("AVSharedMemory is nullptr"); + return AV_ERR_UNKNOWN; + } + std::shared_ptr buffer = AVBuffer::CreateAVBuffer( + sharedMem->GetBase(), sharedMem->GetSize(), sharedMem->GetSize()); + if (buffer == nullptr || buffer->memory_ == nullptr) { + printf("AVBuffer is nullptr"); + return AV_ERR_UNKNOWN; + } + if (demuxer_ != nullptr) { + int32_t ret = demuxer_->ReadSampleBuffer(trackIndex, buffer); + bufferInfo->presentationTimeUs = buffer->pts_; + bufferInfo->size = buffer->memory_->GetSize(); + bufferInfo->offset = 0; + flag = buffer->flag_; + if (checkBufferInfo) { + std::shared_ptr bufferMeta = buffer->meta_; + if (bufferMeta == nullptr) { + printf("AVBuffer meta is nullptr"); + } else { + int64_t duration; + int64_t dts; + bufferMeta->GetData(Tag::BUFFER_DURATION, duration); + bufferMeta->GetData(Tag::BUFFER_DECODING_TIMESTAMP, dts); + printf("[track %d] duration %" PRId64 " dts %" PRId64 "\n", trackIndex, duration, dts); + } + } + return ret; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::SeekToTime(int64_t mSeconds, SeekMode mode) +{ + if (demuxer_ != nullptr) { + SeekMode seekMode = static_cast(mode); + return demuxer_->SeekToTime(mSeconds, seekMode); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) +{ + if (demuxer_ != nullptr) { + return demuxer_->GetIndexByRelativePresentationTimeUs(trackIndex, relativePresentationTimeUs, index); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) +{ + if (demuxer_ != nullptr) { + return demuxer_->GetRelativePresentationTimeUsByIndex(trackIndex, index, relativePresentationTimeUs); + } + return AV_ERR_UNKNOWN; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock.cpp b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a26deac0869d76e7957528b2dedcb3ddf0b71448 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2025 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 "avmemory_inner_mock.h" +#include "native_averrors.h" +#include "mp4_demuxer_inner_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +int32_t Mp4DemuxerInnerMock::Destroy() +{ + if (demuxer_ != nullptr) { + demuxer_ = nullptr; + return AV_ERR_OK; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::SelectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return demuxer_->SelectTrackByID(trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::UnselectTrackByID(uint32_t trackIndex) +{ + if (demuxer_ != nullptr) { + return demuxer_->UnselectTrackByID(trackIndex); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo) +{ + (void)checkBufferInfo; + auto mem = std::static_pointer_cast(sample); + std::shared_ptr sharedMem = (mem != nullptr) ? mem->GetAVMemory() : nullptr; + if (demuxer_ != nullptr) { + int32_t ret = demuxer_->ReadSample(trackIndex, sharedMem, *bufferInfo, flag); + return ret; + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::SeekToTime(int64_t mSeconds, SeekMode mode) +{ + if (demuxer_ != nullptr) { + SeekMode seekMode = static_cast(mode); + return demuxer_->SeekToTime(mSeconds, seekMode); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) +{ + if (demuxer_ != nullptr) { + return demuxer_->GetIndexByRelativePresentationTimeUs(trackIndex, relativePresentationTimeUs, index); + } + return AV_ERR_UNKNOWN; +} + +int32_t Mp4DemuxerInnerMock::GetRelativePresentationTimeUsByIndex(uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) +{ + if (demuxer_ != nullptr) { + return demuxer_->GetRelativePresentationTimeUsByIndex(trackIndex, index, relativePresentationTimeUs); + } + return AV_ERR_UNKNOWN; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock.h b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..9b3b01c90449b5a3e3e60f93c0b92fa19a9e211d --- /dev/null +++ b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2025 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 MP4_DEMUXER_INNER_MOCK_H +#define MP4_DEMUXER_INNER_MOCK_H + +#include "mp4_demuxer_mock.h" +#include "common_mock.h" +#include "avformat_inner_mock.h" +#include "avdemuxer.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4DemuxerInnerMock : public Mp4DemuxerMock { +public: + explicit Mp4DemuxerInnerMock(std::shared_ptr demuxer) : demuxer_(demuxer) {} + ~Mp4DemuxerInnerMock() = default; + int32_t Destroy() override; + int32_t SelectTrackByID(uint32_t trackIndex) override; + int32_t UnselectTrackByID(uint32_t trackIndex) override; + int32_t ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo) override; + int32_t SeekToTime(int64_t mSeconds, SeekMode mode) override; + + int32_t GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) override; + int32_t GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) override; + +private: + std::shared_ptr demuxer_ = nullptr; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_DEMUXER_INNER_MOCK_H \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock_factory.cpp b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30b3a377c2fa7f525fed117f241b90bc5be1ff83 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/inner/mp4_demuxer_inner_mock_factory.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 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 "mp4_demuxer_inner_mock.h" +#include "mp4_source_inner_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +std::shared_ptr Mp4DemuxerMockFactory::CreateDemuxer(std::shared_ptr source) +{ + auto innerSource = std::static_pointer_cast(source); + std::shared_ptr avSource = (innerSource != nullptr) ? innerSource->GetAVSource() : nullptr; + std::shared_ptr demuxer = AVDemuxerFactory::CreateWithSource(avSource); + if (demuxer != nullptr) { + return std::make_shared(demuxer); + } + return nullptr; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/mp4_demuxer_mock.h b/test/unittest/mp4_demuxer_test/mp4_demuxer_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..96932bed21a31d15e986d38c3393e5f9c8a946c4 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/mp4_demuxer_mock.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 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 MP4_DEMUXER_MOCK_H +#define MP4_DEMUXER_MOCK_H + +#include +#include +#include +#include "nocopyable.h" +#include "avformat_mock.h" +#include "mp4_source_mock.h" +#include "common_mock.h" +#include "av_common.h" +#include "avcodec_common.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4DemuxerMock : public NoCopyable { +public: + virtual ~Mp4DemuxerMock() = default; + + virtual int32_t Destroy() = 0; + virtual int32_t SelectTrackByID(uint32_t trackIndex) = 0; + virtual int32_t UnselectTrackByID(uint32_t trackIndex) = 0; + virtual int32_t ReadSample(uint32_t trackIndex, std::shared_ptr sample, + AVCodecBufferInfo *bufferInfo, uint32_t &flag, bool checkBufferInfo = false) = 0; + virtual int32_t SeekToTime(int64_t mSeconds, Media::SeekMode mode) = 0; + virtual int32_t SetMediaKeySystemInfoCallback(bool isNull) + { + return AV_ERR_OK; + } + virtual int32_t SetDemuxerMediaKeySystemInfoCallback(bool isNull) + { + return AV_ERR_OK; + } + virtual int32_t GetMediaKeySystemInfo() + { + return AV_ERR_OK; + } + virtual int32_t GetIndexByRelativePresentationTimeUs(const uint32_t trackIndex, + const uint64_t relativePresentationTimeUs, uint32_t &index) = 0; + virtual int32_t GetRelativePresentationTimeUsByIndex(const uint32_t trackIndex, + const uint32_t index, uint64_t &relativePresentationTimeUs) = 0; +}; + +class __attribute__((visibility("default"))) Mp4DemuxerMockFactory { +public: + static std::shared_ptr CreateDemuxer(std::shared_ptr source); +private: + Mp4DemuxerMockFactory() = delete; + ~Mp4DemuxerMockFactory() = delete; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_DEMUXER_MOCK_H \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/mp4_demuxer_stream_unit_test.cpp b/test/unittest/mp4_demuxer_test/mp4_demuxer_stream_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1329f217e59b7698ab64fe6272ffbc298316557 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/mp4_demuxer_stream_unit_test.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2025 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 +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "avcodec_errors.h" +#include "media_description.h" +#include "file_server_demo.h" +#include "mp4_demuxer_unit_test.h" + +#define LOCAL true +#define URI false + +using namespace OHOS; +using namespace OHOS::Media; +using namespace OHOS::MediaAVCodec; +using namespace testing::ext; +using namespace std; + +namespace { +unique_ptr server = nullptr; +static const string TEST_FILE_PATH = "/data/test/media/"; +static const string TEST_URI_PATH = "http://127.0.0.1:46666/"; +static const string TEST_URI_PATH2 = "http://192.168.3.11:8080/share/"; + +const std::string AVC_LIB_PATH = std::string(AV_CODEC_PATH) + "/libav_codec_avc_parser.z.so"; +const std::string HEVC_LIB_PATH = std::string(AV_CODEC_PATH) + "/libav_codec_hevc_parser.z.so"; +const int64_t SOURCE_OFFSET = 0; +list seekModes = {SeekMode::SEEK_NEXT_SYNC, SeekMode::SEEK_PREVIOUS_SYNC, + SeekMode::SEEK_CLOSEST_SYNC}; + +string g_mp4Path = TEST_FILE_PATH + string("test_264_B_Gop25_4sec_cover.mp4"); +string g_mp4Uri = TEST_URI_PATH + string("test_264_B_Gop25_4sec_cover.mp4"); +string g_m4aPath = TEST_FILE_PATH + string("audio/h264_fmp4.m4a"); +string g_m4aUri = TEST_URI_PATH + string("audio/h264_fmp4.m4a"); +} // namespace + +void Mp4DemuxerUnitTest::InitResource(const std::string &path, bool local) +{ + printf("---- %s ------\n", path.c_str()); + if (local) { + fd_ = OpenFile(path); + int64_t size = GetFileSize(path); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size); + EXPECT_NE(source_, nullptr); + } else { + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(path.data())); + EXPECT_NE(source_, nullptr); + } + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRACK_COUNT, nbStreams_)); + demuxer_ = Mp4DemuxerMockFactory::CreateDemuxer(source_); + EXPECT_NE(demuxer_, nullptr); + initStatus_ = true; +} + +void Mp4DemuxerUnitTest::ReadSample(const std::string &path, bool local, bool checkBufferInfo) +{ + InitResource(path, local); + EXPECT_TRUE(initStatus_); + EXPECT_TRUE(SetInitValue()); + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->SelectTrackByID(idx), AV_ERR_OK); + } + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_, checkBufferInfo), AV_ERR_OK); + CountFrames(idx); + } + } +} + +namespace { +/** + * @tc.name: mp4Demuxer_CreateDemuxer_2001 + * @tc.desc: create demuxer. Normal mp4, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_2001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Path, LOCAL); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_3001 + * @tc.desc: create demuxer. Normal mp4, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_3001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Uri, URI); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_UnselectTrackByID_2001 + * @tc.desc: select and remove track by ID + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_UnselectTrackByID_2001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Path, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(2), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(3), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(-1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(3), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(-1), AV_ERR_OK); +} + +/** + * @tc.name: mp4Demuxer_UnselectTrackByID_3001 + * @tc.desc: select and remove track by ID + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_UnselectTrackByID_3001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Uri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(2), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(3), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(-1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(3), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(-1), AV_ERR_OK); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_2001 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_2001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Path, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 103); + EXPECT_EQ(frames_[1], 174); + EXPECT_EQ(keyFrames_[0], 5); + EXPECT_EQ(keyFrames_[1], 174); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_3001 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_3001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Uri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 103); + EXPECT_EQ(frames_[1], 174); + EXPECT_EQ(keyFrames_[0], 5); + EXPECT_EQ(keyFrames_[1], 174); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_2001 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_2001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Path, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(3994, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {1000, 2000, 2600, 3000, 3400}; + vector videoVals = { 78, 78, 78, 53, 53, 53, 28, 53, 28, 28, 28, 28, 3, 28, 28}; + vector audioVals = {129, 130, 130, 86, 87, 87, 43, 87, 44, 43, 44, 44, 0, 44, 44}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_3001 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_3001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_mp4Uri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(3994, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {1000, 2000, 2600, 3000, 3400}; + vector videoVals = { 78, 78, 78, 53, 53, 53, 28, 53, 28, 28, 28, 28, 3, 28, 28}; + vector audioVals = {129, 130, 130, 86, 87, 87, 43, 87, 44, 43, 44, 44, 0, 44, 44}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_2002 + * @tc.desc: seek to the specified time, fd + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_2002, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_m4aPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(10041, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(11000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 4500, 7000, 2000, 6000}; + vector audioVals = {433, 433, 433, 239, 240, 240, 131, 132, 132, 347, 348, 347, 175, 176, 175}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + EXPECT_EQ(frames_[0], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_3002 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_3002, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + InitResource(g_m4aUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(10041, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(11000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 4500, 7000, 2000, 6000}; + vector audioVals = {433, 433, 433, 239, 240, 240, 131, 132, 132, 347, 348, 347, 175, 176, 175}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + EXPECT_EQ(frames_[0], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} +} // namespace \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/mp4_demuxer_unit_test.cpp b/test/unittest/mp4_demuxer_test/mp4_demuxer_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50f686d7897a3fe0d7d6bfc3af095fb6f304d267 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/mp4_demuxer_unit_test.cpp @@ -0,0 +1,1481 @@ +/* + * Copyright (C) 2025 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 +#include +#include +#include +#include "gtest/gtest.h" +#include "avcodec_errors.h" +#include "media_description.h" +#include "file_server_demo.h" +#include "mp4_demuxer_unit_test.h" + +#define LOCAL true + +using namespace OHOS; +using namespace OHOS::Media; +using namespace OHOS::MediaAVCodec; +using namespace testing::ext; +using namespace std; + +namespace { +unique_ptr server = nullptr; +static const string TEST_FILE_PATH = "/data/test/media/"; +static const string TEST_URI_PATH = "http://127.0.0.1:46666/"; +const int64_t SOURCE_OFFSET = 0; +const int32_t WIDTH = 3840; +const int32_t HEIGHT = 2160; +list seekModes = {SeekMode::SEEK_NEXT_SYNC, SeekMode::SEEK_PREVIOUS_SYNC, + SeekMode::SEEK_CLOSEST_SYNC}; + +string g_mp4SniffFreePath = TEST_FILE_PATH + string("only_free_in_first_2kb.mp4"); +string g_mp4SniffMoovPath = TEST_FILE_PATH + string("only_moov_in_first_2kb.mp4"); +string g_mp4SniffSkipPath = TEST_FILE_PATH + string("only_skip_in_first_2kb.mp4"); +string g_mp4SniffMdatPath = TEST_FILE_PATH + string("only_mdat_in_first_2kb.mp4"); +string g_mp4SniffJp2Path = TEST_FILE_PATH + string("ftyp_jp2_in_first_2kb.mp4"); +string g_mp4MPEGPSPath = TEST_FILE_PATH + string("mpeg_ps_in_mov.mp4"); +string g_mp4AbnormalBrandPath = TEST_FILE_PATH + string("abnormal_brand.mp4"); +string g_mp4MoovOverSizePath = TEST_FILE_PATH + string("moov_oversize.mp4"); +string g_mp4DelayPath = TEST_FILE_PATH + string("delay_v1s.mp4"); +string g_fmp4MeetingPath = TEST_FILE_PATH + string("meeting_fmp4.mp4"); +string g_mp4WvttPath = TEST_FILE_PATH + string("wvtt.mp4"); +string g_mp4G711muPath = TEST_FILE_PATH + string("mpeg4_g711mu.mov"); +string g_fmp4SidxPath = TEST_FILE_PATH + string("sidx.mp4"); +string g_fmp4DoubleTopSidxPath = TEST_FILE_PATH + string("double_sidx_at_top.mp4"); +string g_fmp4MultiSidxPath = TEST_FILE_PATH + string("multi_sidx.mp4"); +string g_fmp4MoreSidxPath = TEST_FILE_PATH + string("more_sidx.mp4"); +string g_mp4ShortAudioPath = TEST_FILE_PATH + string("short_audio.mp4"); +string g_mp4ShortVideoPath = TEST_FILE_PATH + string("short_video.mp4"); +string g_mp4H263AmrwbPath = TEST_FILE_PATH + string("h263_amr_wb.3gp"); +string g_mp4Mpeg4OpusPath = TEST_FILE_PATH + string("mpeg4_opus.mp4"); +string g_mp4Mpeg4AmrnbPath = TEST_FILE_PATH + string("mpeg4_amr_nb.3gp"); +string g_mp4Mpeg2AlacPath = TEST_FILE_PATH + string("mpeg2_alac.mp4"); +string g_mp4Mpeg2DtsPath = TEST_FILE_PATH + string("mpeg2_dts.mp4"); +} // namespace + +void Mp4DemuxerUnitTest::SetUpTestCase(void) +{ + server = make_unique(); + server->StartServer(); + cout << "start" << endl; +} + +void Mp4DemuxerUnitTest::TearDownTestCase(void) +{ + server->StopServer(); +} + +void Mp4DemuxerUnitTest::SetUp(void) +{ + bufferSize_ = WIDTH * HEIGHT; +} + +void Mp4DemuxerUnitTest::TearDown(void) +{ + if (source_ != nullptr) { + source_->Destroy(); + source_ = nullptr; + } + if (demuxer_ != nullptr) { + demuxer_->Destroy(); + demuxer_ = nullptr; + } + if (format_ != nullptr) { + format_->Destroy(); + format_ = nullptr; + } + if (sharedMem_ != nullptr) { + sharedMem_->Destory(); + sharedMem_ = nullptr; + } + if (fd_ > 0) { + close(fd_); + fd_ = -1; + } + bufferSize_ = 0; + nbStreams_ = 0; + numbers_ = 0; + ret_ = AV_ERR_OK; + info_.presentationTimeUs = 0; + info_.offset = 0; + info_.size = 0; + initStatus_ = false; + selectedTrackIds_.clear(); +} + +int64_t Mp4DemuxerUnitTest::GetFileSize(const string &fileName) +{ + int64_t fileSize = 0; + if (!fileName.empty()) { + struct stat fileStatus {}; + if (stat(fileName.c_str(), &fileStatus) == 0) { + fileSize = static_cast(fileStatus.st_size); + } + } + return fileSize; +} + +int32_t Mp4DemuxerUnitTest::OpenFile(const string &fileName) +{ + int32_t fd = open(fileName.c_str(), O_RDONLY); + return fd; +} + +bool Mp4DemuxerUnitTest::isEOS(map& countFlag) +{ + for (auto iter = countFlag.begin(); iter != countFlag.end(); ++iter) { + if (!iter->second) { + return false; + } + } + return true; +} + +bool Mp4DemuxerUnitTest::SetInitValue() +{ + for (int i = 0; i < nbStreams_; i++) { + string codecMime = ""; + int32_t trackType = -1; + if (format_ != nullptr) { + format_->Destroy(); + format_ = nullptr; + } + format_ = source_->GetTrackFormat(i); + if (format_ == nullptr) { + return false; + } + format_->GetStringValue(MediaDescriptionKey::MD_KEY_CODEC_MIME, codecMime); + format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRACK_TYPE, trackType); + if (codecMime.find("image/") != std::string::npos) { + continue; + } + if (trackType == MediaType::MEDIA_TYPE_VID || trackType == MediaType::MEDIA_TYPE_AUD || + trackType == MediaType::MEDIA_TYPE_SUBTITLE || trackType == MediaType::MEDIA_TYPE_TIMED_METADATA) { + selectedTrackIds_.push_back(static_cast(i)); + frames_[i] = 0; + keyFrames_[i] = 0; + eosFlag_[i] = false; + } + } + return true; +} + +void Mp4DemuxerUnitTest::RemoveValue() +{ + if (!frames_.empty()) { + frames_.clear(); + } + if (!keyFrames_.empty()) { + keyFrames_.clear(); + } + if (!eosFlag_.empty()) { + eosFlag_.clear(); + } +} + +void Mp4DemuxerUnitTest::SetEosValue() +{ + for (int i = 0; i < nbStreams_; i++) { + eosFlag_[i] = true; + } +} + +void Mp4DemuxerUnitTest::CountFrames(uint32_t index) +{ + if (flag_ & AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_EOS) { + eosFlag_[index] = true; + return; + } + + if (flag_ & AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_SYNC_FRAME) { + keyFrames_[index]++; + frames_[index]++; + } else if ((flag_ & AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_NONE) == AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_NONE) { + frames_[index]++; + } else { + SetEosValue(); + printf("flag is unknown, read sample break"); + } +} + +bool Mp4DemuxerUnitTest::CheckKeyFrameIndex(std::vector keyFrameIndexList, + const uint32_t frameIndex, const bool isKeyFrame) +{ + bool contaionIndex = (std::count(keyFrameIndexList.begin(), keyFrameIndexList.end(), frameIndex) > 0); + return isKeyFrame ? contaionIndex : !contaionIndex; +} + +void Mp4DemuxerUnitTest::ReadData() +{ + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + demuxer_->ReadSample(idx, sharedMem_, &info_, flag_); + if (ret_ != AV_ERR_OK) { + break; + } + CountFrames(idx); + } + if (ret_ != AV_ERR_OK) { + break; + } + } +} + +void Mp4DemuxerUnitTest::ReadData(int readNum, int64_t &seekTime) +{ + int num = 0; + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + ret_ = demuxer_->ReadSample(idx, sharedMem_, &info_, flag_); + if (ret_ != AV_ERR_OK) { + break; + } + CountFrames(idx); + } + if (ret_ != AV_ERR_OK) { + break; + } + if (num == readNum) { + seekTime = info_.presentationTimeUs; + break; + } + } +} + +namespace { +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0001 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource + * contain 'ftyp' box and the brand is jp2. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0001, TestSize.Level1) +{ + InitResource(g_mp4SniffJp2Path, LOCAL); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0002 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'mdat' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0002, TestSize.Level1) +{ + InitResource(g_mp4SniffMdatPath, LOCAL); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0003 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'moov' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0003, TestSize.Level1) +{ + InitResource(g_mp4SniffMoovPath, LOCAL); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0004 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'free' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0004, TestSize.Level1) +{ + InitResource(g_mp4SniffFreePath, LOCAL); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0005 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'skip' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0005, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4SniffSkipPath); + int64_t size = GetFileSize(g_mp4SniffSkipPath); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size); + EXPECT_EQ(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0006 + * @tc.desc: create demuxer. MPEG-PS in mp4. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0006, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEGPSPath); + int64_t size = GetFileSize(g_mp4MPEGPSPath); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size); + EXPECT_EQ(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0007 + * @tc.desc: create demuxer. Abnormal brand in 'ftyp' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0007, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4AbnormalBrandPath); + int64_t size = GetFileSize(g_mp4AbnormalBrandPath); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size); + EXPECT_NE(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_0008 + * @tc.desc: Demuxer creation succeeds, but initialization fails due to excessively large moov box size + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_0008, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MoovOverSizePath); + int64_t size = GetFileSize(g_mp4MoovOverSizePath); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size); + EXPECT_EQ(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_UnselectTrackByID_0001 + * @tc.desc: select and remove track by ID + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_UnselectTrackByID_0001, TestSize.Level1) +{ + InitResource(g_mp4DelayPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(-1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(-1), AV_ERR_OK); +} + +/** + * @tc.name: mp4Demuxer_UnselectTrackByID_0002 + * @tc.desc: select and remove track by ID + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_UnselectTrackByID_0002, TestSize.Level1) +{ + InitResource(g_fmp4MeetingPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(-1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(-1), AV_ERR_OK); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0001 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0001, TestSize.Level1) +{ + InitResource(g_mp4DelayPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 602); + EXPECT_EQ(frames_[1], 434); + EXPECT_EQ(keyFrames_[0], 51); + EXPECT_EQ(keyFrames_[1], 434); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0002 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0002, TestSize.Level1) +{ + InitResource(g_fmp4MeetingPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 178); + EXPECT_EQ(frames_[1], 548); + EXPECT_EQ(keyFrames_[0], 12); + EXPECT_EQ(keyFrames_[1], 548); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0003 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0003, TestSize.Level1) +{ + InitResource(g_mp4WvttPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(2), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[2], keyFrames_[2]); + EXPECT_EQ(frames_[0], 604); + EXPECT_EQ(frames_[1], 434); + EXPECT_EQ(frames_[2], 8); + EXPECT_EQ(keyFrames_[0], 61); + EXPECT_EQ(keyFrames_[1], 434); + EXPECT_EQ(keyFrames_[2], 8); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0004 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0004, TestSize.Level1) +{ + InitResource(g_mp4G711muPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 45); + EXPECT_EQ(frames_[1], 45); + EXPECT_EQ(keyFrames_[0], 5); + EXPECT_EQ(keyFrames_[1], 45); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0005 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0005, TestSize.Level1) +{ + InitResource(g_fmp4SidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + uint8_t index = 0; + uint8_t listIndex = 0; + vector sampleIndexList = {26, 42, 53, 136, 247}; + vector videoPts = {450000, 716666, 966666, 2300000, 4116666}; + vector audioPts = {697573, 1115532, 1402879, 3571043, 6470634}; + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + if (index == sampleIndexList[listIndex] && idx == 0) { + EXPECT_EQ(info_.presentationTimeUs, videoPts[listIndex]); + } else if (index == sampleIndexList[listIndex] && idx == 1) { + EXPECT_EQ(info_.presentationTimeUs, audioPts[listIndex]); + } + CountFrames(idx); + } + if (index == sampleIndexList[listIndex]) { + listIndex++; + } + index++; + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 604); + EXPECT_EQ(frames_[1], 387); + EXPECT_EQ(keyFrames_[0], 21); + EXPECT_EQ(keyFrames_[1], 387); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0006 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0006, TestSize.Level1) +{ + InitResource(g_fmp4DoubleTopSidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 604); + EXPECT_EQ(frames_[1], 387); + EXPECT_EQ(keyFrames_[0], 21); + EXPECT_EQ(keyFrames_[1], 387); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0007 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0007, TestSize.Level1) +{ + InitResource(g_fmp4MultiSidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + EXPECT_EQ(frames_[0], 324); + EXPECT_EQ(keyFrames_[0], 33); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0008 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0008, TestSize.Level1) +{ + InitResource(g_fmp4MoreSidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 420); + EXPECT_EQ(frames_[1], 268); + EXPECT_EQ(keyFrames_[0], 14); + EXPECT_EQ(keyFrames_[1], 268); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0009 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0009, TestSize.Level1) +{ + InitResource(g_mp4ShortAudioPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 120); + EXPECT_EQ(frames_[1], 85); + EXPECT_EQ(keyFrames_[0], 12); + EXPECT_EQ(keyFrames_[1], 85); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0010 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0010, TestSize.Level1) +{ + InitResource(g_mp4ShortVideoPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 150); + EXPECT_EQ(frames_[1], 470); + EXPECT_EQ(keyFrames_[0], 15); + EXPECT_EQ(keyFrames_[1], 470); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0011 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0011, TestSize.Level1) +{ + InitResource(g_mp4H263AmrwbPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 99); + EXPECT_EQ(frames_[1], 165); + EXPECT_EQ(keyFrames_[0], 9); + EXPECT_EQ(keyFrames_[1], 165); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0012 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0012, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4OpusPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 165); + EXPECT_EQ(keyFrames_[0], 8); + EXPECT_EQ(keyFrames_[1], 165); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0013 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0013, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4AmrnbPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 165); + EXPECT_EQ(keyFrames_[0], 8); + EXPECT_EQ(keyFrames_[1], 165); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0014 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0014, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2AlacPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 39); + EXPECT_EQ(keyFrames_[0], 10); + EXPECT_EQ(keyFrames_[1], 39); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0015 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0015, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2DtsPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 308); + EXPECT_EQ(keyFrames_[0], 10); + EXPECT_EQ(keyFrames_[1], 308); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_0016 + * @tc.desc: copy current sample to buffer, samll bufferSize, check total read size + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_0016, TestSize.Level1) +{ + bufferSize_ = 1000; + InitResource(g_mp4G711muPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + vector sizes = {0, 0}; + vector expectSizes = {582040, 12000}; + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + demuxer_->ReadSample(idx, sharedMem_, &info_, flag_); + sizes[idx] += info_.size; + CountFrames(idx); + } + } + printf("sizes[0]=%d | expectSizes[0]=%d\n", sizes[0], expectSizes[0]); + printf("sizes[1]=%d | expectSizes[1]=%d\n", sizes[1], expectSizes[1]); + EXPECT_EQ(sizes[0], expectSizes[0]); + EXPECT_EQ(sizes[1], expectSizes[1]); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0001 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0001, TestSize.Level1) +{ + InitResource(g_mp4DelayPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(12000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {1000, 2000, 2550, 3400, 9050}; + vector videoVals = {602, 602, 602, 542, 542, 542, 506, 518, 506, 458, 458, 458, 110, 122, 122}; + vector audioVals = {390, 391, 390, 347, 348, 347, 321, 331, 321, 287, 288, 287, 37, 46, 46}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0002 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0002, TestSize.Level1) +{ + InitResource(g_fmp4MeetingPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {178, 178, 178, 163, 178, 163, 148, 148, 148, 133, 133, 133, 43, 43, 43}; + vector audioVals = {548, 548, 548, 505, 548, 505, 461, 461, 461, 417, 417, 417, 156, 156, 156}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0003 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0003, TestSize.Level1) +{ + InitResource(g_mp4WvttPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(2), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2500, 3400, 9050}; + vector videoVals = {574, 574, 574, 554, 564, 554, 454, 454, 454, 394, 404, 404, 54, 64, 64}; + vector audioVals = {411, 412, 412, 397, 405, 398, 325, 326, 326, 282, 290, 290, 38, 46, 46}; + vector subtitleVals = { 8, 8, 8, 8, 8, 8, 5, 5, 5, 4, 4, 4, 0, 0, 0}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + printf("time = %" PRId64 " | frames_[2]=%d | kFrames[2]=%d\n", *toPts, frames_[2], keyFrames_[2]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + EXPECT_EQ(frames_[2], subtitleVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0004 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0004, TestSize.Level1) +{ + InitResource(g_mp4G711muPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(2000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 1000, 1200, 1333}; + vector videoVals = {25, 35, 35, 15, 25, 25, 15, 15, 15, 5, 15, 5, 5, 15, 5}; + vector audioVals = {25, 36, 36, 15, 26, 25, 15, 16, 15, 5, 16, 5, 5, 16, 5}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0005 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0005, TestSize.Level1) +{ + InitResource(g_fmp4SidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {604, 604, 604, 574, 574, 574, 454, 454, 454, 424, 424, 424, 34, 34, 34}; + vector audioVals = {387, 387, 387, 368, 368, 368, 292, 292, 292, 272, 272, 272, 24, 24, 24}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0006 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0006, TestSize.Level1) +{ + InitResource(g_fmp4DoubleTopSidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {604, 604, 604, 574, 574, 574, 454, 454, 454, 424, 424, 424, 34, 34, 34}; + vector audioVals = {387, 387, 387, 368, 368, 368, 292, 292, 292, 272, 272, 272, 24, 24, 24}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0007 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0007, TestSize.Level1) +{ + InitResource(g_fmp4MultiSidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {324, 324, 324, 324, 324, 324, 264, 264, 264, 234, 234, 234, 54, 54, 54}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0008 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0008, TestSize.Level1) +{ + InitResource(g_fmp4MoreSidxPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + //边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(8000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + //普通时刻 + list toPtsList = {0, 800, 2500, 3400, 6500}; + vector videoVals = {420, 420, 420, 390, 390, 390, 270, 270, 270, 240, 240, 240, 30, 30, 30}; + vector audioVals = {268, 268, 268, 249, 249, 249, 173, 173, 173, 153, 153, 153, 19, 19, 19}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0009 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0009, TestSize.Level1) +{ + InitResource(g_mp4ShortAudioPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(12000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 1500, 2550, 3000, 3600}; + vector videoVals = {100, 110, 110, 70, 80, 80, 40, 50, 40, 30, 30, 30, 10, 20, 10}; + vector audioVals = { 85, 85, 85, 58, 73, 72, 15, 30, 15, 0, 0, 0, 0, 0, 0}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0010 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0010, TestSize.Level1) +{ + InitResource(g_mp4ShortVideoPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(12000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 2066, 2550, 7033, 8500}; + vector videoVals = {150, 150, 150, 150, 150, 150, 130, 140, 140, 0, 10, 10, 0, 0, 0}; + vector audioVals = {445, 446, 446, 372, 373, 372, 340, 357, 357, 139, 154, 153, 70, 71, 71}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0011 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0011, TestSize.Level1) +{ + InitResource(g_mp4H263AmrwbPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2100, 2633, 3166}; + vector videoVals = { 75, 87, 87, 75, 75, 75, 27, 39, 39, 15, 27, 15, 3, 15, 3}; + vector audioVals = {124, 145, 145, 124, 125, 125, 44, 65, 65, 24, 45, 25, 4, 25, 5}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0012 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0012, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4OpusPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 1500, 2100, 2633}; + vector videoVals = { 83, 95, 83, 71, 83, 71, 47, 59, 59, 35, 47, 35, 11, 23, 23}; + vector audioVals = {138, 165, 138, 118, 139, 118, 78, 99, 98, 58, 79, 58, 18, 39, 38}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0013 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0013, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4AmrnbPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2100, 2633, 3166}; + vector videoVals = { 83, 95, 83, 71, 83, 71, 35, 47, 35, 11, 23, 23, 0, 11, 11}; + vector audioVals = {138, 165, 138, 118, 139, 118, 58, 79, 58, 18, 39, 38, 1, 19, 18}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0014 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0014, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2AlacPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 1500, 2100, 2633}; + vector videoVals = { 75, 85, 85, 75, 75, 75, 45, 55, 55, 35, 45, 35, 15, 25, 25}; + vector audioVals = { 29, 34, 34, 29, 30, 30, 17, 22, 22, 14, 18, 14, 6, 11, 10}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_0015 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_0015, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2DtsPath, LOCAL); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2100, 2633}; + vector videoVals = { 75, 85, 85, 75, 75, 75, 35, 45, 35, 15, 25, 25}; + vector audioVals = {232, 265, 264, 232, 233, 233, 108, 140, 108, 45, 77, 77}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} +} // namespace \ No newline at end of file diff --git a/test/unittest/mp4_demuxer_test/mp4_demuxer_unit_test.h b/test/unittest/mp4_demuxer_test/mp4_demuxer_unit_test.h new file mode 100644 index 0000000000000000000000000000000000000000..5653145b0f05a56a198fef0126a55bdd2368c358 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/mp4_demuxer_unit_test.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 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 MP4_DEMUXER_UNIT_TEST_H +#define MP4_DEMUXER_UNIT_TEST_H + +#include "gtest/gtest.h" +#include "mp4_demuxer_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4DemuxerUnitTest : public testing::Test { +public: + // SetUpTestCase: Called before all test cases + static void SetUpTestCase(void); + // TearDownTestCase: Called after all test case + static void TearDownTestCase(void); + // SetUp: Called before each test cases + void SetUp(void); + // TearDown: Called after each test cases + void TearDown(void); + int64_t GetFileSize(const std::string &fileName); + int32_t OpenFile(const std::string &fileName); + bool isEOS(std::map& countFlag); + bool SetInitValue(); + void ReadData(); + void ReadData(int readNum, int64_t &seekTime); + void RemoveValue(); + void SetEosValue(); + void CountFrames(uint32_t index); + bool CheckKeyFrameIndex(std::vector keyFrameIndexList, const uint32_t frameIndex, const bool isKeyFrame); + + void InitResource(const std::string &path, bool local); + void ReadSample(const std::string &path, bool local, bool checkBufferInfo = false); + +protected: + std::shared_ptr source_ = nullptr; + std::shared_ptr demuxer_ = nullptr; + std::shared_ptr format_ = nullptr; + std::shared_ptr sharedMem_ = nullptr; + int32_t fd_ = -1; + AVCodecBufferInfo info_; + uint32_t flag_; + std::vector selectedTrackIds_; + int32_t bufferSize_ = 0; + int32_t nbStreams_ = 0; + int32_t numbers_ = 0; + int32_t ret_ = AV_ERR_OK; + bool initStatus_ = false; + std::map frames_; + std::map keyFrames_; + std::map eosFlag_; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_DEMUXER_UNIT_TEST_H diff --git a/test/unittest/mp4_demuxer_test/mp4_demuxer_uri_unit_test.cpp b/test/unittest/mp4_demuxer_test/mp4_demuxer_uri_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a8c0784c786dd0e5b9b66922ebfa10c407f4175 --- /dev/null +++ b/test/unittest/mp4_demuxer_test/mp4_demuxer_uri_unit_test.cpp @@ -0,0 +1,1280 @@ +/* + * Copyright (C) 2025 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 +#include +#include +#include +#include "gtest/gtest.h" +#include "avcodec_errors.h" +#include "media_description.h" +#include "file_server_demo.h" +#include "mp4_demuxer_unit_test.h" + +#define URI false + +using namespace OHOS; +using namespace OHOS::Media; +using namespace OHOS::MediaAVCodec; +using namespace testing::ext; +using namespace std; + +namespace { +unique_ptr server = nullptr; +static const string TEST_FILE_PATH = "/data/test/media/"; +static const string TEST_URI_PATH = "http://127.0.0.1:46666/"; +static const string TEST_URI_PATH2 = "http://192.168.3.11:8080/share/"; + +list seekModes = {SeekMode::SEEK_NEXT_SYNC, SeekMode::SEEK_PREVIOUS_SYNC, + SeekMode::SEEK_CLOSEST_SYNC}; + +string g_mp4SniffFreeUri = TEST_URI_PATH + string("only_free_in_first_2kb.mp4"); +string g_mp4SniffMoovUri = TEST_URI_PATH + string("only_moov_in_first_2kb.mp4"); +string g_mp4SniffSkipUri = TEST_URI_PATH + string("only_skip_in_first_2kb.mp4"); +string g_mp4SniffMdatUri = TEST_URI_PATH + string("only_mdat_in_first_2kb.mp4"); +string g_mp4SniffJp2Uri = TEST_URI_PATH + string("ftyp_jp2_in_first_2kb.mp4"); +string g_mp4MPEGPSUri = TEST_URI_PATH + string("mpeg_ps_in_mov.mp4"); +string g_mp4AbnormalBrandUri = TEST_URI_PATH + string("abnormal_brand.mp4"); +string g_mp4MoovOverSizeUri = TEST_URI_PATH + string("moov_oversize.mp4"); +string g_mp4DelayUri = TEST_URI_PATH + string("delay_v1s.mp4"); +string g_fmp4MeetingUri = TEST_URI_PATH + string("meeting_fmp4.mp4"); +string g_mp4WvttUri = TEST_URI_PATH + string("wvtt.mp4"); +string g_mp4G711muUri = TEST_URI_PATH + string("mpeg4_g711mu.mov"); +string g_fmp4SidxUri = TEST_URI_PATH + string("sidx.mp4"); +string g_fmp4DoubleTopSidxUri = TEST_URI_PATH + string("double_sidx_at_top.mp4"); +string g_fmp4MultiSidxUri = TEST_URI_PATH + string("multi_sidx.mp4"); +string g_fmp4MoreSidxUri = TEST_URI_PATH + string("more_sidx.mp4"); +string g_mp4ShortAudioUri = TEST_URI_PATH + string("short_audio.mp4"); +string g_mp4ShortVideoUri = TEST_URI_PATH + string("short_video.mp4"); +string g_mp4H263AmrwbUri = TEST_URI_PATH + string("h263_amr_wb.3gp"); +string g_mp4Mpeg4OpusUri = TEST_URI_PATH + string("mpeg4_opus.mp4"); +string g_mp4Mpeg4AmrnbUri = TEST_URI_PATH + string("mpeg4_amr_nb.3gp"); +string g_mp4Mpeg2AlacUri = TEST_URI_PATH + string("mpeg2_alac.mp4"); +string g_mp4Mpeg2DtsUri = TEST_URI_PATH + string("mpeg2_dts.mp4"); + + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1001 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource + * contain 'ftyp' box and the brand is jp2. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1001, TestSize.Level1) +{ + InitResource(g_mp4SniffJp2Uri, URI); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1002 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'mdat' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1002, TestSize.Level1) +{ + InitResource(g_mp4SniffMdatUri, URI); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1003 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'moov' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1003, TestSize.Level1) +{ + InitResource(g_mp4SniffMoovUri, URI); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1004 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'free' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1004, TestSize.Level1) +{ + InitResource(g_mp4SniffFreeUri, URI); + EXPECT_TRUE(initStatus_); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1005 + * @tc.desc: create demuxer. The first 2048 bytes of this MP4 resource contain only a single 'skip' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1005, TestSize.Level1) +{ + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4SniffSkipUri.data())); + EXPECT_EQ(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1006 + * @tc.desc: create demuxer. MPEG-PS in mp4. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1006, TestSize.Level1) +{ + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEGPSUri.data())); + EXPECT_EQ(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1007 + * @tc.desc: create demuxer. Abnormal brand in 'ftyp' box. + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1007, TestSize.Level1) +{ + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AbnormalBrandUri.data())); + EXPECT_NE(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_CreateDemuxer_1008 + * @tc.desc: Demuxer creation succeeds, but initialization fails due to excessively large moov box size + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_CreateDemuxer_1008, TestSize.Level1) +{ + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MoovOverSizeUri.data())); + EXPECT_EQ(source_, nullptr); +} + +/** + * @tc.name: mp4Demuxer_UnselectTrackByID_1001 + * @tc.desc: select and remove track by ID + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_UnselectTrackByID_1001, TestSize.Level1) +{ + InitResource(g_mp4DelayUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(-1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(-1), AV_ERR_OK); +} + +/** + * @tc.name: mp4Demuxer_UnselectTrackByID_1002 + * @tc.desc: select and remove track by ID + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_UnselectTrackByID_1002, TestSize.Level1) +{ + InitResource(g_fmp4MeetingUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_NE(demuxer_->SelectTrackByID(-1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(2), AV_ERR_OK); + EXPECT_EQ(demuxer_->UnselectTrackByID(-1), AV_ERR_OK); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1001 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1001, TestSize.Level1) +{ + InitResource(g_mp4DelayUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 602); + EXPECT_EQ(frames_[1], 434); + EXPECT_EQ(keyFrames_[0], 51); + EXPECT_EQ(keyFrames_[1], 434); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1002 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1002, TestSize.Level1) +{ + InitResource(g_fmp4MeetingUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 178); + EXPECT_EQ(frames_[1], 548); + EXPECT_EQ(keyFrames_[0], 12); + EXPECT_EQ(keyFrames_[1], 548); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1003 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1003, TestSize.Level1) +{ + InitResource(g_mp4WvttUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(2), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[2], keyFrames_[2]); + EXPECT_EQ(frames_[0], 604); + EXPECT_EQ(frames_[1], 434); + EXPECT_EQ(frames_[2], 8); + EXPECT_EQ(keyFrames_[0], 61); + EXPECT_EQ(keyFrames_[1], 434); + EXPECT_EQ(keyFrames_[2], 8); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1004 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1004, TestSize.Level1) +{ + InitResource(g_mp4G711muUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 45); + EXPECT_EQ(frames_[1], 45); + EXPECT_EQ(keyFrames_[0], 5); + EXPECT_EQ(keyFrames_[1], 45); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1005 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1005, TestSize.Level1) +{ + InitResource(g_fmp4SidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + uint8_t index = 0; + uint8_t listIndex = 0; + vector sampleIndexList = {26, 42, 53, 136, 247}; + vector videoPts = {450000, 716666, 966666, 2300000, 4116666}; + vector audioPts = {697573, 1115532, 1402879, 3571043, 6470634}; + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + if (index == sampleIndexList[listIndex] && idx == 0) { + EXPECT_EQ(info_.presentationTimeUs, videoPts[listIndex]); + } else if (index == sampleIndexList[listIndex] && idx == 1) { + EXPECT_EQ(info_.presentationTimeUs, audioPts[listIndex]); + } + CountFrames(idx); + } + if (index == sampleIndexList[listIndex]) { + listIndex++; + } + index++; + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 604); + EXPECT_EQ(frames_[1], 387); + EXPECT_EQ(keyFrames_[0], 21); + EXPECT_EQ(keyFrames_[1], 387); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1006 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1006, TestSize.Level1) +{ + InitResource(g_fmp4DoubleTopSidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 604); + EXPECT_EQ(frames_[1], 387); + EXPECT_EQ(keyFrames_[0], 21); + EXPECT_EQ(keyFrames_[1], 387); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1007 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1007, TestSize.Level1) +{ + InitResource(g_fmp4MultiSidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + EXPECT_EQ(frames_[0], 324); + EXPECT_EQ(keyFrames_[0], 33); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1008 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1008, TestSize.Level1) +{ + InitResource(g_fmp4MoreSidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 420); + EXPECT_EQ(frames_[1], 268); + EXPECT_EQ(keyFrames_[0], 14); + EXPECT_EQ(keyFrames_[1], 268); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1009 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1009, TestSize.Level1) +{ + InitResource(g_mp4ShortAudioUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 120); + EXPECT_EQ(frames_[1], 85); + EXPECT_EQ(keyFrames_[0], 12); + EXPECT_EQ(keyFrames_[1], 85); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1010 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1010, TestSize.Level1) +{ + InitResource(g_mp4ShortVideoUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 150); + EXPECT_EQ(frames_[1], 470); + EXPECT_EQ(keyFrames_[0], 15); + EXPECT_EQ(keyFrames_[1], 470); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1011 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1011, TestSize.Level1) +{ + InitResource(g_mp4H263AmrwbUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 99); + EXPECT_EQ(frames_[1], 165); + EXPECT_EQ(keyFrames_[0], 9); + EXPECT_EQ(keyFrames_[1], 165); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1012 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1012, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4OpusUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 165); + EXPECT_EQ(keyFrames_[0], 8); + EXPECT_EQ(keyFrames_[1], 165); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1013 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1013, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4AmrnbUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 165); + EXPECT_EQ(keyFrames_[0], 8); + EXPECT_EQ(keyFrames_[1], 165); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1014 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1014, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2AlacUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 39); + EXPECT_EQ(keyFrames_[0], 10); + EXPECT_EQ(keyFrames_[1], 39); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1015 + * @tc.desc: copy current sample to buffer, check read in PTS order + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1015, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2DtsUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + EXPECT_EQ(demuxer_->ReadSample(idx, sharedMem_, &info_, flag_), AV_ERR_OK); + CountFrames(idx); + } + } + printf("frames_[0]=%d | kFrames[0]=%d\n", frames_[0], keyFrames_[0]); + printf("frames_[1]=%d | kFrames[1]=%d\n", frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], 95); + EXPECT_EQ(frames_[1], 308); + EXPECT_EQ(keyFrames_[0], 10); + EXPECT_EQ(keyFrames_[1], 308); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_ReadSample_1016 + * @tc.desc: copy current sample to buffer, samll bufferSize, check total read size + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_ReadSample_1016, TestSize.Level1) +{ + bufferSize_ = 1000; + InitResource(g_mp4G711muUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + EXPECT_TRUE(SetInitValue()); + vector sizes = {0, 0}; + vector expectSizes = {582040, 12000}; + while (!isEOS(eosFlag_)) { + for (auto idx : selectedTrackIds_) { + demuxer_->ReadSample(idx, sharedMem_, &info_, flag_); + sizes[idx] += info_.size; + CountFrames(idx); + } + } + printf("sizes[0]=%d | expectSizes[0]=%d\n", sizes[0], expectSizes[0]); + printf("sizes[1]=%d | expectSizes[1]=%d\n", sizes[1], expectSizes[1]); + EXPECT_EQ(sizes[0], expectSizes[0]); + EXPECT_EQ(sizes[1], expectSizes[1]); + RemoveValue(); +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1001 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1001, TestSize.Level1) +{ + InitResource(g_mp4DelayUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(12000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {1000, 2000, 2550, 3400, 9050}; + vector videoVals = {602, 602, 602, 542, 542, 542, 506, 518, 506, 458, 458, 458, 110, 122, 122}; + vector audioVals = {390, 391, 390, 347, 348, 347, 321, 331, 321, 287, 288, 287, 37, 46, 46}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1002 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1002, TestSize.Level1) +{ + InitResource(g_fmp4MeetingUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {178, 178, 178, 163, 178, 163, 148, 148, 148, 133, 133, 133, 43, 43, 43}; + vector audioVals = {548, 548, 548, 505, 548, 505, 461, 461, 461, 417, 417, 417, 156, 156, 156}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1003 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1003, TestSize.Level1) +{ + InitResource(g_mp4WvttUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(2), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2500, 3400, 9050}; + vector videoVals = {574, 574, 574, 554, 564, 554, 454, 454, 454, 394, 404, 404, 54, 64, 64}; + vector audioVals = {411, 412, 412, 397, 405, 398, 325, 326, 326, 282, 290, 290, 38, 46, 46}; + vector subtitleVals = { 8, 8, 8, 8, 8, 8, 5, 5, 5, 4, 4, 4, 0, 0, 0}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + printf("time = %" PRId64 " | frames_[2]=%d | kFrames[2]=%d\n", *toPts, frames_[2], keyFrames_[2]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + EXPECT_EQ(frames_[2], subtitleVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1004 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1004, TestSize.Level1) +{ + InitResource(g_mp4G711muUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(2000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 1000, 1200, 1333}; + vector videoVals = {25, 35, 35, 15, 25, 25, 15, 15, 15, 5, 15, 5, 5, 15, 5}; + vector audioVals = {25, 36, 36, 15, 26, 25, 15, 16, 15, 5, 16, 5, 5, 16, 5}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1005 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1005, TestSize.Level1) +{ + InitResource(g_fmp4SidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {604, 604, 604, 574, 574, 574, 454, 454, 454, 424, 424, 424, 34, 34, 34}; + vector audioVals = {387, 387, 387, 368, 368, 368, 292, 292, 292, 272, 272, 272, 24, 24, 24}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1006 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1006, TestSize.Level1) +{ + InitResource(g_fmp4DoubleTopSidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {604, 604, 604, 574, 574, 574, 454, 454, 454, 424, 424, 424, 34, 34, 34}; + vector audioVals = {387, 387, 387, 368, 368, 368, 292, 292, 292, 272, 272, 272, 24, 24, 24}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1007 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1007, TestSize.Level1) +{ + InitResource(g_fmp4MultiSidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(15000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 9500}; + vector videoVals = {324, 324, 324, 324, 324, 324, 264, 264, 264, 234, 234, 234, 54, 54, 54}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1008 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1008, TestSize.Level1) +{ + InitResource(g_fmp4MoreSidxUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(8000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {0, 800, 2500, 3400, 6500}; + vector videoVals = {420, 420, 420, 390, 390, 390, 270, 270, 270, 240, 240, 240, 30, 30, 30}; + vector audioVals = {268, 268, 268, 249, 249, 249, 173, 173, 173, 153, 153, 153, 19, 19, 19}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1009 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1009, TestSize.Level1) +{ + InitResource(g_mp4ShortAudioUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(12000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 1500, 2550, 3000, 3600}; + vector videoVals = {100, 110, 110, 70, 80, 80, 40, 50, 40, 30, 30, 30, 10, 20, 10}; + vector audioVals = { 85, 85, 85, 58, 73, 72, 15, 30, 15, 0, 0, 0, 0, 0, 0}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1010 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1010, TestSize.Level1) +{ + InitResource(g_mp4ShortVideoUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(12000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 2066, 2550, 7033, 8500}; + vector videoVals = {150, 150, 150, 150, 150, 150, 130, 140, 140, 0, 10, 10, 0, 0, 0}; + vector audioVals = {445, 446, 446, 372, 373, 372, 340, 357, 357, 139, 154, 153, 70, 71, 71}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1011 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1011, TestSize.Level1) +{ + InitResource(g_mp4H263AmrwbUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2100, 2633, 3166}; + vector videoVals = { 75, 87, 87, 75, 75, 75, 27, 39, 39, 15, 27, 15, 3, 15, 3}; + vector audioVals = {124, 145, 145, 124, 125, 125, 44, 65, 65, 24, 45, 25, 4, 25, 5}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1012 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1012, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4OpusUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 1500, 2100, 2633}; + vector videoVals = { 83, 95, 83, 71, 83, 71, 47, 59, 59, 35, 47, 35, 11, 23, 23}; + vector audioVals = {138, 165, 138, 118, 139, 118, 78, 99, 98, 58, 79, 58, 18, 39, 38}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1013 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1013, TestSize.Level1) +{ + InitResource(g_mp4Mpeg4AmrnbUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2100, 2633, 3166}; + vector videoVals = { 83, 95, 83, 71, 83, 71, 35, 47, 35, 11, 23, 23, 0, 11, 11}; + vector audioVals = {138, 165, 138, 118, 139, 118, 58, 79, 58, 18, 39, 38, 1, 19, 18}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1014 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1014, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2AlacUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 1500, 2100, 2633}; + vector videoVals = { 75, 85, 85, 75, 75, 75, 45, 55, 55, 35, 45, 35, 15, 25, 25}; + vector audioVals = { 29, 34, 34, 29, 30, 30, 17, 22, 22, 14, 18, 14, 6, 11, 10}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} + +/** + * @tc.name: mp4Demuxer_SeekToTime_1015 + * @tc.desc: seek to the specified time + * @tc.type: FUNC + */ +HWTEST_F(Mp4DemuxerUnitTest, mp4Demuxer_SeekToTime_1015, TestSize.Level1) +{ + InitResource(g_mp4Mpeg2DtsUri, URI); + EXPECT_TRUE(initStatus_); + EXPECT_EQ(demuxer_->SelectTrackByID(0), AV_ERR_OK); + EXPECT_EQ(demuxer_->SelectTrackByID(1), AV_ERR_OK); + // 边界情况 + EXPECT_EQ(demuxer_->SeekToTime(0, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_EQ(demuxer_->SeekToTime(800, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(-10, SeekMode::SEEK_PREVIOUS_SYNC), AV_ERR_OK); + EXPECT_NE(demuxer_->SeekToTime(5000, SeekMode::SEEK_NEXT_SYNC), AV_ERR_OK); + // 普通时刻 + list toPtsList = {500, 800, 2100, 2633}; + vector videoVals = { 75, 85, 85, 75, 75, 75, 35, 45, 35, 15, 25, 25}; + vector audioVals = {232, 265, 264, 232, 233, 233, 108, 140, 108, 45, 77, 77}; + sharedMem_ = AVMemoryMockFactory::CreateAVMemoryMock(bufferSize_); + EXPECT_NE(sharedMem_, nullptr); + for (auto toPts = toPtsList.begin(); toPts != toPtsList.end(); toPts++) { + for (auto mode = seekModes.begin(); mode != seekModes.end(); mode++) { + ret_ = demuxer_->SeekToTime(*toPts, *mode); + if (ret_ != AV_ERR_OK) { + printf("seek failed, time = %" PRId64 " | ret = %d\n", *toPts, ret_); + continue; + } + ReadData(); + printf("time = %" PRId64 " | frames_[0]=%d | kFrames[0]=%d\n", *toPts, frames_[0], keyFrames_[0]); + printf("time = %" PRId64 " | frames_[1]=%d | kFrames[1]=%d\n", *toPts, frames_[1], keyFrames_[1]); + EXPECT_EQ(frames_[0], videoVals[numbers_]); + EXPECT_EQ(frames_[1], audioVals[numbers_]); + numbers_ += 1; + RemoveValue(); + selectedTrackIds_.clear(); + } + } +} +} // namespace \ No newline at end of file diff --git a/test/unittest/mp4_source_test/BUILD.gn b/test/unittest/mp4_source_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..88aedc701aa6c09d6e8f709b04d222023e1c860e --- /dev/null +++ b/test/unittest/mp4_source_test/BUILD.gn @@ -0,0 +1,145 @@ +# Copyright (c) 2025 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/test.gni") +import("//foundation/multimedia/av_codec/config.gni") + +module_output_path = "av_codec/av_codec/unittest" + +mp4_source_unittest_cflags = [ + "-std=c++17", + "-fno-rtti", + "-fno-exceptions", + "-Wall", + "-fno-common", + "-fstack-protector-strong", + "-Wshadow", + "-FPIC", + "-FS", + "-O2", + "-D_FORTIFY_SOURCE=2", + "-fvisibility=hidden", + "-Wformat=2", + "-Wdate-time", + "-Werror", + "-Wextra", + "-Wimplicit-fallthrough", + "-Wsign-compare", + "-Wunused-parameter", +] + +################################################################################################################muxer +ohos_unittest("mp4_source_capi_unit_test") { + sanitize = av_codec_test_sanitize + module_out_path = module_output_path + include_dirs = [ + "./", + "./capi", + "$av_codec_root_dir/interfaces/inner_api/native", + "$av_codec_root_dir/interfaces/kits/c", + "$av_codec_root_dir/test/unittest/format_test", + "$av_codec_root_dir/test/unittest/format_test/capi", + "$av_codec_root_dir/test/nativedemo/include", + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", + ] + + cflags = mp4_source_unittest_cflags + + if (av_codec_support_source) { + sources = [ + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", + "./mp4_source_stream_unit_test.cpp", + "./mp4_source_unit_test.cpp", + "./mp4_source_uri_unit_test.cpp", + "./capi/mp4_source_capi_mock.cpp", + "./capi/mp4_source_capi_mock_factory.cpp", + ] + } + + if (target_cpu == "arm64" || is_emulator) { + av_codec_path = "\"/system/lib64\"" + } else { + av_codec_path = "\"/system/lib\"" + } + defines = [ "AV_CODEC_PATH=${av_codec_path}" ] + defines += av_codec_defines + deps = [ "$av_codec_root_dir/interfaces/kits/c:capi_packages" ] + public_deps = + [ "$av_codec_root_dir/test/unittest/common:av_codec_capi_unit_test" ] + external_deps = [ + "av_codec:av_codec_client", + "c_utils:utils", + "graphic_2d:libgraphic_utils", + "graphic_surface:surface", + "hilog:libhilog", + "media_foundation:media_foundation", + "media_foundation:native_media_core", + ] + + resource_config_file = + "$av_codec_root_dir/test/unittest/resources/ohos_test.xml" +} + +#################################################################################################################muxer +ohos_unittest("mp4_source_inner_unit_test") { + sanitize = av_codec_test_sanitize + module_out_path = module_output_path + include_dirs = [ + "./", + "./inner", + "$av_codec_root_dir/interfaces/inner_api/native", + "$av_codec_root_dir/interfaces/kits/c", + "$av_codec_root_dir/test/unittest/format_test", + "$av_codec_root_dir/test/unittest/format_test/inner", + "$av_codec_root_dir/test/nativedemo/include", + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo", + ] + + if (target_cpu == "arm64" || is_emulator) { + av_codec_path = "\"/system/lib64\"" + } else { + av_codec_path = "\"/system/lib\"" + } + defines = [ + "AV_CODEC_PATH=${av_codec_path}", + "MP4_SOURCE_INNER_UNIT_TEST", + ] + + cflags = mp4_source_unittest_cflags + + if (av_codec_support_source) { + sources = [ + "$av_codec_root_dir/test/nativedemo/avdemuxer/server_demo/file_server_demo.cpp", + "./mp4_source_stream_unit_test.cpp", + "./mp4_source_unit_test.cpp", + "./mp4_source_uri_unit_test.cpp", + "./inner/mp4_source_inner_mock.cpp", + "./inner/mp4_source_inner_mock_factory.cpp", + ] + } + deps = [ "$av_codec_root_dir/interfaces/kits/c:capi_packages" ] + public_deps = + [ "$av_codec_root_dir/test/unittest/common:av_codec_inner_unit_test" ] + external_deps = [ + "av_codec:av_codec_client", + "c_utils:utils", + "graphic_2d:libgraphic_utils", + "graphic_surface:surface", + "hilog:libhilog", + "ipc:ipc_single", + "media_foundation:media_foundation", + ] + + resource_config_file = + "$av_codec_root_dir/test/unittest/resources/ohos_test.xml" +} diff --git a/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.cpp b/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1414113f8a9cd7716a78c9c137b6821700f4ab5 --- /dev/null +++ b/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2025 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 "avformat_capi_mock.h" +#include "mp4_source_capi_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +int32_t Mp4SourceCapiMock::Destroy() +{ + if (source_ != nullptr) { + int32_t ret = OH_AVSource_Destroy(source_); + source_ = nullptr; + return ret; + } + return AV_ERR_UNKNOWN; +} + +std::shared_ptr Mp4SourceCapiMock::GetSourceFormat() +{ + if (source_ != nullptr) { + OH_AVFormat *format = OH_AVSource_GetSourceFormat(source_); + if (format != nullptr) { + return std::make_shared(format); + } + return nullptr; + } + return nullptr; +} + +std::shared_ptr Mp4SourceCapiMock::GetTrackFormat(uint32_t trackIndex) +{ + if (source_ != nullptr) { + OH_AVFormat *format = OH_AVSource_GetTrackFormat(source_, trackIndex); + if (format != nullptr) { + return std::make_shared(format); + } + return nullptr; + } + return nullptr; +} + +std::shared_ptr Mp4SourceCapiMock::GetUserData() +{ + if (source_ != nullptr) { + OH_AVFormat *format = OH_AVSource_GetCustomMetadataFormat(source_); + if (format != nullptr) { + return std::make_shared(format); + } + return nullptr; + } + return nullptr; +} + +OH_AVSource* Mp4SourceCapiMock::GetAVSource() +{ + return source_; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.h b/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..d74528aec4f99abf8a6e2b13be68346b01b3e17d --- /dev/null +++ b/test/unittest/mp4_source_test/capi/mp4_source_capi_mock.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 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 MP4_SOURCE_CAPI_MOCK_H +#define MP4_SOURCE_CAPI_MOCK_H + +#include "mp4_source_mock.h" +#include "avcodec_common.h" +#include "native_avsource.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4SourceCapiMock : public Mp4SourceMock { +public: + explicit Mp4SourceCapiMock(OH_AVSource *source) : source_(source) {} + ~Mp4SourceCapiMock() = default; + int32_t Destroy() override; + std::shared_ptr GetSourceFormat() override; + std::shared_ptr GetTrackFormat(uint32_t trackIndex) override; + std::shared_ptr GetUserData() override; + OH_AVSource* GetAVSource(); +private: + OH_AVSource* source_ = nullptr; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_SOURCE_CAPI_MOCK_H \ No newline at end of file diff --git a/test/unittest/mp4_source_test/capi/mp4_source_capi_mock_factory.cpp b/test/unittest/mp4_source_test/capi/mp4_source_capi_mock_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dabbe121b83ca88f09f974fe552f23122abf34f6 --- /dev/null +++ b/test/unittest/mp4_source_test/capi/mp4_source_capi_mock_factory.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2025 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 "mp4_source_capi_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +std::shared_ptr Mp4SourceMockFactory::CreateSourceWithURI(char *uri) +{ + OH_AVSource *source = OH_AVSource_CreateWithURI(uri); + if (source != nullptr) { + return std::make_shared(source); + } + return nullptr; +} + +std::shared_ptr Mp4SourceMockFactory::CreateSourceWithFD(int32_t fd, int64_t offset, int64_t size) +{ + OH_AVSource *source = OH_AVSource_CreateWithFD(fd, offset, size); + if (source != nullptr) { + return std::make_shared(source); + } + return nullptr; +} + +std::shared_ptr Mp4SourceMockFactory::CreateWithDataSource( + const std::shared_ptr &dataSource) +{ + return nullptr; +} + +std::shared_ptr Mp4SourceMockFactory::CreateWithDataSource(OH_AVDataSource *dataSource) +{ + OH_AVSource *source = OH_AVSource_CreateWithDataSource(dataSource); + if (source != nullptr) { + return std::make_shared(source); + } + return nullptr; +} +} +} \ No newline at end of file diff --git a/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.cpp b/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f86accf075769cdee389479ecb3fcefb723cd8f --- /dev/null +++ b/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2025 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 "mp4_source_inner_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +int32_t Mp4SourceInnerMock::Destroy() +{ + if (source_ != nullptr) { + source_ = nullptr; + return AV_ERR_OK; + } + return AV_ERR_UNKNOWN; +} + +std::shared_ptr Mp4SourceInnerMock::GetSourceFormat() +{ + if (source_ != nullptr) { + Format format; + source_->GetSourceFormat(format); + return std::make_shared(format); + } + return nullptr; +} + +std::shared_ptr Mp4SourceInnerMock::GetTrackFormat(uint32_t trackIndex) +{ + if (source_ != nullptr) { + Format format; + source_->GetTrackFormat(format, trackIndex); + return std::make_shared(format); + } + return nullptr; +} + +std::shared_ptr Mp4SourceInnerMock::GetUserData() +{ + if (source_ != nullptr) { + Format format; + source_->GetUserMeta(format); + return std::make_shared(format); + } + return nullptr; +} + +std::shared_ptr Mp4SourceInnerMock::GetAVSource() +{ + return source_; +} +} // namespace MediaAVCodec +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.h b/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..f36694c27b5e6779cfc2a4e2fad0ffa5af4fbdb3 --- /dev/null +++ b/test/unittest/mp4_source_test/inner/mp4_source_inner_mock.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2025 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 AVSOURCE_INNER_MOCK_H +#define AVSOURCE_INNER_MOCK_H + +#include "mp4_source_mock.h" +#include "avformat_inner_mock.h" +#include "avcodec_common.h" +#include "avsource.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4SourceInnerMock : public Mp4SourceMock { +public: + explicit Mp4SourceInnerMock(std::shared_ptr source) : source_(source) {} + ~Mp4SourceInnerMock() = default; + + int32_t Destroy() override; + std::shared_ptr GetSourceFormat() override; + std::shared_ptr GetTrackFormat(uint32_t trackIndex) override; + std::shared_ptr GetUserData() override; + std::shared_ptr GetAVSource(); +private: + std::shared_ptr source_ = nullptr; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // AVSOURCE_INNER_MOCK_H \ No newline at end of file diff --git a/test/unittest/mp4_source_test/inner/mp4_source_inner_mock_factory.cpp b/test/unittest/mp4_source_test/inner/mp4_source_inner_mock_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..056a5240587f364ecf2e51e770ffce5ecf176fd2 --- /dev/null +++ b/test/unittest/mp4_source_test/inner/mp4_source_inner_mock_factory.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2025 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 "mp4_source_inner_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +std::shared_ptr Mp4SourceMockFactory::CreateSourceWithURI(char *uri) +{ + std::shared_ptr source = AVSourceFactory::CreateWithURI(uri); + if (source != nullptr) { + return std::make_shared(source); + } + return nullptr; +} + +std::shared_ptr Mp4SourceMockFactory::CreateSourceWithFD(int32_t fd, int64_t offset, int64_t size) +{ + std::shared_ptr source = AVSourceFactory::CreateWithFD(fd, offset, size); + if (source != nullptr) { + return std::make_shared(source); + } + return nullptr; +} + +std::shared_ptr Mp4SourceMockFactory::CreateWithDataSource( + const std::shared_ptr &dataSource) +{ + std::shared_ptr source = AVSourceFactory::CreateWithDataSource(dataSource); + if (source != nullptr) { + return std::make_shared(source); + } + return nullptr; +} + +std::shared_ptr Mp4SourceMockFactory::CreateWithDataSource(OH_AVDataSource *dataSource) +{ + return nullptr; +} +} +} \ No newline at end of file diff --git a/test/unittest/mp4_source_test/mp4_source_mock.h b/test/unittest/mp4_source_test/mp4_source_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..71745baee88151bc42a61513e53a82415c2375b4 --- /dev/null +++ b/test/unittest/mp4_source_test/mp4_source_mock.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2025 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 MP4_SOURCE_MOCK_H +#define MP4_SOURCE_MOCK_H + +#include +#include +#include "nocopyable.h" +#include "avformat_mock.h" +#include "av_common.h" +#include "avcodec_common.h" +#include "avsource.h" +#include "native_avsource.h" +#include "common/native_mfmagic.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4SourceMock : public NoCopyable { +public: + virtual ~Mp4SourceMock() = default; + virtual int32_t Destroy() =0; + virtual std::shared_ptr GetSourceFormat() = 0; + virtual std::shared_ptr GetTrackFormat(uint32_t trackIndex) = 0; + virtual std::shared_ptr GetUserData() = 0; +}; + +class __attribute__((visibility("default"))) Mp4SourceMockFactory { +public: + static std::shared_ptr CreateSourceWithURI(char *uri); + static std::shared_ptr CreateSourceWithFD(int32_t fd, int64_t offset, int64_t size); + static std::shared_ptr CreateWithDataSource( + const std::shared_ptr &dataSource); + static std::shared_ptr CreateWithDataSource(OH_AVDataSource *dataSource); +private: + Mp4SourceMockFactory() = delete; + ~Mp4SourceMockFactory() = delete; +}; + +class NativeAVDataSource : public OHOS::Media::IMediaDataSource { +public: + explicit NativeAVDataSource(OH_AVDataSource *dataSource) + : dataSource_(dataSource) + { + } + virtual ~NativeAVDataSource() = default; + + int32_t ReadAt(const std::shared_ptr &mem, uint32_t length, int64_t pos = -1) + { + std::shared_ptr buffer = AVBuffer::CreateAVBuffer( + mem->GetBase(), mem->GetSize(), mem->GetSize() + ); + OH_AVBuffer* avBuffer = new OH_AVBuffer(buffer); + return dataSource_->readAt(avBuffer, length, pos); + } + + int32_t GetSize(int64_t &size) + { + size = dataSource_->size; + return 0; + } + + int32_t ReadAt(int64_t pos, uint32_t length, const std::shared_ptr &mem) + { + return ReadAt(mem, length, pos); + } + + int32_t ReadAt(uint32_t length, const std::shared_ptr &mem) + { + return ReadAt(mem, length); + } + +private: + OH_AVDataSource* dataSource_; +}; + +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_SOURCE_MOCK_H \ No newline at end of file diff --git a/test/unittest/mp4_source_test/mp4_source_stream_unit_test.cpp b/test/unittest/mp4_source_test/mp4_source_stream_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1649817633ad46097e96a004e2d4926a1dad6576 --- /dev/null +++ b/test/unittest/mp4_source_test/mp4_source_stream_unit_test.cpp @@ -0,0 +1,2477 @@ +/* + * Copyright (C) 2025 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 +#include +#include "gtest/gtest.h" +#include "avcodec_errors.h" +#include "avcodec_audio_common.h" +#include "avcodec_info.h" +#include "avcodec_audio_channel_layout.h" +#include "avcodec_mime_type.h" +#include "media_description.h" +#include "file_server_demo.h" +#include "mp4_source_unit_test.h" + +#define LOCAL true +#define URI false + +using namespace OHOS; +using namespace OHOS::MediaAVCodec; +using namespace testing::ext; +using namespace std; + +namespace { +unique_ptr server = nullptr; +static const string TEST_FILE_PATH = "/data/test/media/"; +static const string TEST_URI_PATH = "http://127.0.0.1:46666/"; +const std::string AVC_LIB_PATH = std::string(AV_CODEC_PATH) + "/libav_codec_avc_parser.z.so"; +const std::string HEVC_LIB_PATH = std::string(AV_CODEC_PATH) + "/libav_codec_hevc_parser.z.so"; +const int64_t SOURCE_OFFSET = 0; +string g_mp4Path = TEST_FILE_PATH + string("test_h264.mp4"); +string g_mp4Uri = TEST_URI_PATH + string("test_h264.mp4"); +string g_mp4Path2 = TEST_FILE_PATH + string("test_264_B_Gop25_4sec_cover.mp4"); +string g_mp4Uri2 = TEST_URI_PATH + string("test_264_B_Gop25_4sec_cover.mp4"); +string g_mp4InfoPath = TEST_FILE_PATH + string("camera_info_parser.mp4"); +string g_mp4InfoUri = TEST_URI_PATH + string("camera_info_parser.mp4"); +string g_m4vPath = TEST_FILE_PATH + string("h264_fmp4.m4v"); +string g_m4vUri = TEST_URI_PATH + string("h264_fmp4.m4v"); +string g_m4aPath = TEST_FILE_PATH + string("audio/h264_fmp4.m4a"); +string g_m4aUri = TEST_URI_PATH + string("audio/h264_fmp4.m4a"); +string g_fmp4Path1 = TEST_FILE_PATH + string("h264_fmp4.mp4"); +string g_fmp4Uri1 = TEST_URI_PATH + string("h264_fmp4.mp4"); +string g_fmp4Path2 = TEST_FILE_PATH + string("h265_fmp4.mp4"); +string g_fmp4Uri2 = TEST_URI_PATH + string("h265_fmp4.mp4"); +string g_mp4HdrPath = TEST_FILE_PATH + string("aac_hdrvivid.mp4"); +string g_mp4HdrUri = TEST_URI_PATH + string("aac_hdrvivid.mp4"); +string g_mp4AvcFlacPath = TEST_FILE_PATH + string("avc_flac.mp4"); +string g_mp4AvcFlacUri = TEST_URI_PATH + string("avc_flac.mp4"); +string g_mp4AvcAacPath = TEST_FILE_PATH + string("avc_aac.mp4"); +string g_mp4AvcAacUri = TEST_URI_PATH + string("avc_aac.mp4"); +string g_mp4AvcVorbisPath = TEST_FILE_PATH + string("h264_vorbis.mov"); +string g_mp4AvcVorbisUri = TEST_URI_PATH + string("h264_vorbis.mov"); +string g_mp4AvcVorbisPath2 = TEST_FILE_PATH + string("h264_vorbis_quad.mov"); +string g_mp4AvcVorbisUri2 = TEST_URI_PATH + string("h264_vorbis_quad.mov"); +string g_mp4AvcAlacPath = TEST_FILE_PATH + string("h264_alac_ch5.1.mov"); +string g_mp4AvcAlacUri = TEST_URI_PATH + string("h264_alac_ch5.1.mov"); +string g_mp4AvcAacPath2 = TEST_FILE_PATH + string("h264_aac_ch5.1.mp4"); +string g_mp4AvcAacUri2 = TEST_URI_PATH + string("h264_aac_ch5.1.mp4"); +string g_mp4HevcAacPath = TEST_FILE_PATH + string("hevc_aac.mov"); +string g_mp4HevcAacUri = TEST_URI_PATH + string("hevc_aac.mov"); +string g_mp4HevcAacPath2 = TEST_FILE_PATH + string("hevc_aac.mp4"); +string g_mp4HevcAacUri2 = TEST_URI_PATH + string("hevc_aac.mp4"); +string g_mp4HevcOpusPath = TEST_FILE_PATH + string("h265_opus_ch7.1.mp4"); +string g_mp4HevcOpusUri = TEST_URI_PATH + string("h265_opus_ch7.1.mp4"); +string g_mp4HevcPcmPath = TEST_FILE_PATH + string("h265_pcm_s16le.mov"); +string g_mp4HevcPcmUri = TEST_URI_PATH + string("h265_pcm_s16le.mov"); + +std::map> infoMap = { + {"hdrVivid", { + {"profile", static_cast(OH_HEVCProfile::HEVC_PROFILE_MAIN_10)}, + {"level", static_cast(HEVCLevel::HEVC_LEVEL_4)}, + {"colorRange", 0}, {"colorMatrix", static_cast(OH_MatrixCoefficient::MATRIX_COEFFICIENT_BT2020_NCL)}, + {"colorTrans", static_cast(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_HLG)}, + {"colorPrim", static_cast(OH_ColorPrimary::COLOR_PRIMARY_BT2020)}, + {"chromaLoc", static_cast(ChromaLocation::CHROMA_LOC_LEFT)}, + }}, + {"mp4Hevc", { + {"profile", static_cast(OH_HEVCProfile::HEVC_PROFILE_MAIN)}, + {"level", static_cast(HEVCLevel::HEVC_LEVEL_31)}, + {"colorRange", 0}, {"colorMatrix", static_cast(OH_MatrixCoefficient::MATRIX_COEFFICIENT_BT709)}, + {"colorTrans", static_cast(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_BT709)}, + {"colorPrim", static_cast(OH_ColorPrimary::COLOR_PRIMARY_BT709)}, + {"chromaLoc", static_cast(ChromaLocation::CHROMA_LOC_LEFT)}, + }}, + {"Hevcfmp4", { + {"profile", static_cast(OH_HEVCProfile::HEVC_PROFILE_MAIN)}, + {"level", static_cast(HEVCLevel::HEVC_LEVEL_31)}, + {"colorRange", 0}, {"colorMatrix", static_cast(OH_MatrixCoefficient::MATRIX_COEFFICIENT_UNSPECIFIED)}, + {"colorTrans", static_cast(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_UNSPECIFIED)}, + {"colorPrim", static_cast(OH_ColorPrimary::COLOR_PRIMARY_UNSPECIFIED)}, + {"chromaLoc", static_cast(ChromaLocation::CHROMA_LOC_LEFT)}, + }}, + {"Hevcmov", { + {"profile", static_cast(OH_HEVCProfile::HEVC_PROFILE_MAIN)}, + {"level", static_cast(HEVCLevel::HEVC_LEVEL_31)}, + {"colorRange", 0}, {"colorMatrix", static_cast(OH_MatrixCoefficient::MATRIX_COEFFICIENT_UNSPECIFIED)}, + {"colorTrans", static_cast(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_UNSPECIFIED)}, + {"colorPrim", static_cast(OH_ColorPrimary::COLOR_PRIMARY_UNSPECIFIED)}, + {"chromaLoc", static_cast(ChromaLocation::CHROMA_LOC_LEFT)}, + }}, +}; +} // namespace + +void Mp4SourceUnitTest::InitResource(const std::string &path, bool local) +{ + printf("---- %s ------\n", path.c_str()); + if (local) { + fd_ = OpenFile(path); + int64_t size = GetFileSize(path); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size); + EXPECT_NE(source_, nullptr); + } else { + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(path.data())); + EXPECT_NE(source_, nullptr); + } + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRACK_COUNT, streamsCount_)); + for (int i = 0; i < streamsCount_; i++) { + format_ = source_->GetTrackFormat(i); + EXPECT_NE(format_, nullptr); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRACK_TYPE, formatVal_.trackType)); + if (formatVal_.trackType == MediaType::MEDIA_TYPE_VID) { + vTrackIdx_ = i; + } else if (formatVal_.trackType == MediaType::MEDIA_TYPE_AUD) { + aTrackIdx_ = i; + } + } + initStatus_ = true; +} + +void Mp4SourceUnitTest::CheckAvcInfo(const std::string resName) +{ + for (int i = 0; i < streamsCount_; i++) { + format_ = source_->GetTrackFormat(i); + string codecMime; + format_->GetStringValue(MediaDescriptionKey::MD_KEY_CODEC_MIME, codecMime); + if (codecMime == AVCodecMimeType::MEDIA_MIMETYPE_VIDEO_AVC) { + printf("[trackFormat %d]: %s\n", i, format_->DumpInfo()); + // pass + } + } +} + +void Mp4SourceUnitTest::CheckHevcInfo(const std::string resName) +{ + for (int i = 0; i < streamsCount_; i++) { + format_ = source_->GetTrackFormat(i); + string codecMime; + format_->GetStringValue(MediaDescriptionKey::MD_KEY_CODEC_MIME, codecMime); + if (codecMime == AVCodecMimeType::MEDIA_MIMETYPE_VIDEO_HEVC) { + printf("[trackFormat %d]: %s\n", i, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_PROFILE, formatVal_.profile)); + EXPECT_EQ(formatVal_.profile, infoMap[resName]["profile"]); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_COLOR_PRIMARIES, formatVal_.colorPri)); + EXPECT_EQ(formatVal_.colorPri, infoMap[resName]["colorPrim"]); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRANSFER_CHARACTERISTICS, + formatVal_.colorTrans)); + EXPECT_EQ(formatVal_.colorTrans, infoMap[resName]["colorTrans"]); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_MATRIX_COEFFICIENTS, formatVal_.colorMatrix)); + EXPECT_EQ(formatVal_.colorMatrix, infoMap[resName]["colorMatrix"]); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_RANGE_FLAG, formatVal_.colorRange)); + EXPECT_EQ(formatVal_.colorRange, infoMap[resName]["colorRange"]); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + printf("-------input inner--------\n"); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_CHROMA_LOCATION, formatVal_.chromaLoc)); + EXPECT_EQ(formatVal_.chromaLoc, infoMap[resName]["chromaLoc"]); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_LEVEL, formatVal_.level)); + EXPECT_EQ(formatVal_.level, infoMap[resName]["level"]); +#endif + if (resName == "hdrVivid" || resName == "doubleVivid") { + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_VIDEO_IS_HDR_VIVID, + formatVal_.isHdrVivid)); + printf("isHdrVivid = %d\n", formatVal_.isHdrVivid); + EXPECT_EQ(formatVal_.isHdrVivid, 1); + } else { + EXPECT_FALSE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_VIDEO_IS_HDR_VIVID, + formatVal_.isHdrVivid)); + } + } + } +} + +namespace { +/** + * @tc.name: mp4Source_GetFormat_2001 + * @tc.desc: get format when the file is mp4, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4Path); + size_ = GetFileSize(g_mp4Path); + printf("---- %s ----\n", g_mp4Path.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_TITLE, formatVal_.title)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ARTIST, formatVal_.artist)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_COPYRIGHT, formatVal_.copyright)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.title, "Sintel Trailer"); + EXPECT_EQ(formatVal_.artist, "Durian Open Movie Team"); + EXPECT_EQ(formatVal_.copyright, "(c) copyright Blender Foundation | durian.blender.org"); + EXPECT_EQ(formatVal_.duration, 52209000); + EXPECT_EQ(formatVal_.trackCount, 2); + EXPECT_EQ(formatVal_.timeScale, 1000); +} + +/** + * @tc.name: mp4Source_GetFormat_3001 + * @tc.desc: get format when the file is mp4, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3001, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4Uri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4Uri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_TITLE, formatVal_.title)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ARTIST, formatVal_.artist)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_COPYRIGHT, formatVal_.copyright)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.title, "Sintel Trailer"); + EXPECT_EQ(formatVal_.artist, "Durian Open Movie Team"); + EXPECT_EQ(formatVal_.copyright, "(c) copyright Blender Foundation | durian.blender.org"); + EXPECT_EQ(formatVal_.duration, 52209000); + EXPECT_EQ(formatVal_.trackCount, 2); + EXPECT_EQ(formatVal_.timeScale, 1000); +} + +/** + * @tc.name: mp4Source_GetFormat_2002 + * @tc.desc: get format when the file is mp4, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2002, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4Path2); + size_ = GetFileSize(g_mp4Path2); + printf("---- %s ----\n", g_mp4Path2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 4120000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.bitRate, 7782407); + EXPECT_EQ(formatVal_.frameRate, 25.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 128563); +} + +/** + * @tc.name: mp4Source_GetFormat_3002 + * @tc.desc: get format when the file is mp4, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3002, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4Uri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4Uri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 4120000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 12800); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.bitRate, 7782407); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 128563); +} + +/** + * @tc.name: mp4Source_GetFormat_2003 + * @tc.desc: get format when the file is mp4, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2003, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4InfoPath); + size_ = GetFileSize(g_mp4InfoPath); + printf("---- %s ------\n", g_mp4InfoPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[source Format]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetFloatValue(Media::Tag::MEDIA_LONGITUDE, formatVal_.longitude)); + EXPECT_TRUE(format_->GetFloatValue(Media::Tag::MEDIA_LATITUDE, formatVal_.latitude)); + EXPECT_EQ(formatVal_.longitude, float(22.670000)); // longitude test + EXPECT_EQ(formatVal_.latitude, float(114.059998)); // latitude test +} + +/** + * @tc.name: mp4Source_GetFormat_3003 + * @tc.desc: get format when the file is mp4, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3003, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4InfoUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4InfoUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[source Format]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetFloatValue(Media::Tag::MEDIA_LONGITUDE, formatVal_.longitude)); + EXPECT_TRUE(format_->GetFloatValue(Media::Tag::MEDIA_LATITUDE, formatVal_.latitude)); + EXPECT_EQ(formatVal_.longitude, float(22.670000)); // longitude test + EXPECT_EQ(formatVal_.latitude, float(114.059998)); // latitude test +} + +/** + * @tc.name: mp4Source_GetFormat_2004 + * @tc.desc: get format when the file is m4v, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2004, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_m4vPath); + size_ = GetFileSize(g_m4vPath); + printf("---- %s ----\n", g_m4vPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_EQ(formatVal_.duration, 10033333); + EXPECT_EQ(formatVal_.trackCount, 1); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + EXPECT_EQ(formatVal_.bitRate, 1267567); +} + +/** + * @tc.name: mp4Source_GetFormat_3004 + * @tc.desc: get format when the file is m4v, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3004, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_m4vUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_m4vUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_EQ(formatVal_.duration, 10033333); + EXPECT_EQ(formatVal_.trackCount, 1); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + EXPECT_EQ(formatVal_.bitRate, 1267567); +} + +/** + * @tc.name: mp4Source_GetFormat_2005 + * @tc.desc: get format when the file is m4a, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2005, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_m4aPath); + size_ = GetFileSize(g_m4aPath); + printf("---- %s ----\n", g_m4aPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_FILE_TYPE, formatVal_.fileType)); + EXPECT_EQ(formatVal_.duration, 10064354); + EXPECT_EQ(formatVal_.trackCount, 1); + EXPECT_EQ(formatVal_.fileType, 206); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 127829); +} + +/** + * @tc.name: mp4Source_GetFormat_3005 + * @tc.desc: get format when the file is m4a, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3005, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_m4aUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_m4aUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_FILE_TYPE, formatVal_.fileType)); + EXPECT_EQ(formatVal_.duration, 10064354); + EXPECT_EQ(formatVal_.trackCount, 1); + EXPECT_EQ(formatVal_.fileType, 206); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 127829); +} + +/** + * @tc.name: mp4Source_GetFormat_2006 + * @tc.desc: get format when the file is fmp4, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2006, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_fmp4Path1); + size_ = GetFileSize(g_fmp4Path1); + printf("---- %s ----\n", g_fmp4Path1.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10066667); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 1267567); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 127829); +} + +/** + * @tc.name: mp4Source_GetFormat_3006 + * @tc.desc: get format when the file is fmp4, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3006, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_fmp4Uri1.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_fmp4Uri1.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10066667); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 1267567); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 127829); +} + +/** + * @tc.name: mp4Source_GetFormat_2007 + * @tc.desc: get format when the file is fmp4, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2007, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_fmp4Path2); + size_ = GetFileSize(g_fmp4Path2); + printf("---- %s ----\n", g_fmp4Path2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 2133375); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 127407); + CheckHevcInfo("Hevcfmp4"); +} + +/** + * @tc.name: mp4Source_GetFormat_3007 + * @tc.desc: get format when the file is fmp4, uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3007, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_fmp4Uri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_fmp4Uri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 2133375); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_LANGUAGE, formatVal_.language)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.language, "chi"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 127407); + CheckHevcInfo("Hevcfmp4"); +} + +/** + * @tc.name: mp4Source_GetFormat_2008 + * @tc.desc: get format when the file is HDRvivid, local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2008, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HdrPath); + size_ = GetFileSize(g_mp4HdrPath); + printf("---- %s ----\n", g_mp4HdrPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.duration, 7334000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 90000); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 2336); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.bitRate, 15100929); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 192987); + CheckHevcInfo("hdrVivid"); +} + +/** + * @tc.name: mp4Source_GetFormat_3008 + * @tc.desc: get format when the file is HDRvivid + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3008, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4HdrUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HdrUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 10000); + EXPECT_EQ(formatVal_.duration, 7334000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 90000); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 2336); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.bitRate, 15100929); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 192987); + CheckHevcInfo("hdrVivid"); +} + +/** + * @tc.name: mp4Source_GetFormat_2009 + * @tc.desc: get format when the file is MP4 with FLAC audio track + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2009, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcFlacPath); + size_ = GetFileSize(g_mp4AvcFlacPath); + printf("---- %s ----\n", g_mp4AvcFlacPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.duration, 52209000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 12288); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 854); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 539721); + EXPECT_EQ(formatVal_.frameRate, 24.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/flac"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 1266065); +} + +/** + * @tc.name: mp4Source_GetFormat_3009 + * @tc.desc: get format when the file is MP4 with FLAC audio track + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3009, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4AvcFlacUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcFlacUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 52209000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 12288); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 854); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 539721); + EXPECT_EQ(formatVal_.frameRate, 24.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/flac"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 1266065); +} + +/** + * @tc.name: mp4Source_GetFormat_2010 + * @tc.desc: get format when the file is MP4 with AAC audio track + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2010, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcAacPath); + size_ = GetFileSize(g_mp4AvcAacPath); + printf("---- %s ----\n", g_mp4AvcAacPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.timeScale, 44100); + EXPECT_EQ(formatVal_.duration, 6135011); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.bitRate, 2152665); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 130517); +} + +/** + * @tc.name: mp4Source_GetFormat_3010 + * @tc.desc: get format when the file is MP4 with AAC audio track + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3010, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4AvcAacUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcAacUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 44100); + EXPECT_EQ(formatVal_.duration, 6135011); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 600); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.bitRate, 2152665); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 130517); +} + +/** + * @tc.name: mp4Source_GetFormat_2011 + * @tc.desc: get format when the file is MP4(h264 + vorbis) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2011, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcVorbisPath); + size_ = GetFileSize(g_mp4AvcVorbisPath); + printf("---- %s ----\n", g_mp4AvcVorbisPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10067000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 1177099); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/vorbis"); + EXPECT_EQ(formatVal_.bitRate, 80435); +} + +/** + * @tc.name: mp4Source_GetFormat_2012 + * @tc.desc: get format when the file is MP4(h264 + vorbis) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2012, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcVorbisPath); + size_ = GetFileSize(g_mp4AvcVorbisPath); + printf("---- %s ----\n", g_mp4AvcVorbisPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10067000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.startTime, 33008); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_3011 + * @tc.desc: get format when the file is MP4(h264 + vorbis) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3011, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4AvcVorbisUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcVorbisUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10067000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 1177099); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/vorbis"); + EXPECT_EQ(formatVal_.bitRate, 80435); +} + +/** + * @tc.name: mp4Source_GetFormat_3012 + * @tc.desc: get format when the file is MP4(h264 + vorbis) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3012, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4AvcVorbisUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcVorbisUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10067000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.startTime, 33008); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_2013 + * @tc.desc: get format when the file is MOV(h265 + AAC) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2013, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HevcAacPath); + size_ = GetFileSize(g_mp4HevcAacPath); + printf("---- %s ----\n", g_mp4HevcAacPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 52209000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 12288); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 24.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); + CheckHevcInfo("Hevcmov"); +} + +/** + * @tc.name: mp4Source_GetFormat_3013 + * @tc.desc: get format when the file is MOV(h265 + AAC) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3013, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4HevcAacUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HevcAacUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 52209000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 12288); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 24.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); + CheckHevcInfo("Hevcmov"); +} + +/** + * @tc.name: mp4Source_GetFormat_2014 + * @tc.desc: get format when the file is MP4(h265 + AAC) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2014, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HevcAacPath2); + size_ = GetFileSize(g_mp4HevcAacPath2); + printf("---- %s ----\n", g_mp4HevcAacPath2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 52209000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 12288); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 24.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); + CheckHevcInfo("mp4Hevc"); +} + +/** + * @tc.name: mp4Source_GetFormat_3014 + * @tc.desc: get format when the file is MP4(h265 + AAC) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3014, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4HevcAacUri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HevcAacUri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 52209000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 12288); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 24.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); + CheckHevcInfo("mp4Hevc"); +} + +/** + * @tc.name: mp4Source_GetFormat_2015 + * @tc.desc: get format when the file is MP4(h264 + alac), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2015, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcAlacPath); + size_ = GetFileSize(g_mp4AvcAlacPath); + printf("---- %s ----\n", g_mp4AvcAlacPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 876853); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.bitRate, 336610); +} + +/** + * @tc.name: mp4Source_GetFormat_3015 + * @tc.desc: get format when the file is MP4(h264 + alac), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3015, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4AvcAlacUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcAlacUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 876853); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.bitRate, 336610); +} + +/** + * @tc.name: mp4Source_GetFormat_2016 + * @tc.desc: get format when the file is MP4(h264 + alac), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2016, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcAlacPath); + size_ = GetFileSize(g_mp4AvcAlacPath); + printf("---- %s ----\n", g_mp4AvcAlacPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 6); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16P); + EXPECT_EQ(formatVal_.channelLayout, 63); +} + +/** + * @tc.name: mp4Source_GetFormat_3016 + * @tc.desc: get format when the file is MP4(h264 + alac), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3016, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4AvcAlacUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcAlacUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 6); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16P); + EXPECT_EQ(formatVal_.channelLayout, 63); +} + +/** + * @tc.name: mp4Source_GetFormat_2017 + * @tc.desc: get format when the file is MP4(h264 + vorbis), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2017, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcVorbisPath2); + size_ = GetFileSize(g_mp4AvcVorbisPath2); + printf("---- %s ----\n", g_mp4AvcVorbisPath2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 876853); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/vorbis"); + EXPECT_EQ(formatVal_.bitRate, 104171); +} + +/** + * @tc.name: mp4Source_GetFormat_3017 + * @tc.desc: get format when the file is MP4(h264 + vorbis), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3017, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4AvcVorbisUri2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcVorbisUri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 876853); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/vorbis"); + EXPECT_EQ(formatVal_.bitRate, 104171); +} + +/** + * @tc.name: mp4Source_GetFormat_2018 + * @tc.desc: get format when the file is MP4(h264 + vorbis), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2018, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcVorbisPath2); + size_ = GetFileSize(g_mp4AvcVorbisPath2); + printf("---- %s ----\n", g_mp4AvcVorbisPath2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 4); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 51); +} + +/** + * @tc.name: mp4Source_GetFormat_3018 + * @tc.desc: get format when the file is MP4(h264 + vorbis), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3018, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4AvcVorbisUri2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcVorbisUri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 4); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 51); +} + +/** + * @tc.name: mp4Source_GetFormat_2019 + * @tc.desc: get format when the file is MP4(hevc + opus), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2019, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HevcOpusPath); + size_ = GetFileSize(g_mp4HevcOpusPath); + printf("---- %s ----\n", g_mp4HevcOpusPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.bitRate, 454562); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/opus"); + EXPECT_EQ(formatVal_.bitRate, 100079); +} + +/** + * @tc.name: mp4Source_GetFormat_3019 + * @tc.desc: get format when the file is MP4(hevc + opus), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3019, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4HevcOpusUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HevcOpusUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.bitRate, 454562); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/opus"); + EXPECT_EQ(formatVal_.bitRate, 100079); +} + +/** + * @tc.name: mp4Source_GetFormat_2020 + * @tc.desc: get format when the file is MP4(hevc + opus), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2020, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HevcOpusPath); + size_ = GetFileSize(g_mp4HevcOpusPath); + printf("---- %s ----\n", g_mp4HevcOpusPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 1599); +} + +/** + * @tc.name: mp4Source_GetFormat_3020 + * @tc.desc: get format when the file is MP4(hevc + opus), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3020, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4HevcOpusUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HevcOpusUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 1599); +} + +/** + * @tc.name: mp4Source_GetFormat_2021 + * @tc.desc: get format when the file is MP4(hevc + pcm), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2021, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HevcPcmPath); + size_ = GetFileSize(g_mp4HevcPcmPath); + printf("---- %s ----\n", g_mp4HevcPcmPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.bitRate, 2133375); + EXPECT_EQ(formatVal_.startTime, 33008); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/raw"); + EXPECT_EQ(formatVal_.bitRate, 1411200); +} + +/** + * @tc.name: mp4Source_GetFormat_3021 + * @tc.desc: get format when the file is MP4(hevc + pcm), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3021, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4HevcPcmUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HevcPcmUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.bitRate, 2133375); + EXPECT_EQ(formatVal_.startTime, 33008); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/raw"); + EXPECT_EQ(formatVal_.bitRate, 1411200); +} + +/** + * @tc.name: mp4Source_GetFormat_2022 + * @tc.desc: get format when the file is MP4(hevc + pcm), local + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2022, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4HevcPcmPath); + size_ = GetFileSize(g_mp4HevcPcmPath); + printf("---- %s ----\n", g_mp4HevcPcmPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16LE); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_3022 + * @tc.desc: get format when the file is MP4(hevc + pcm), uri + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3022, TestSize.Level1) +{ + if (access(HEVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ----\n", g_mp4HevcPcmUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4HevcPcmUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16LE); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_2023 + * @tc.desc: get format when the file is MP4(h264 + aac 5.1) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2023, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcAacPath2); + size_ = GetFileSize(g_mp4AvcAacPath2); + printf("---- %s ----\n", g_mp4AvcAacPath2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 876853); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 230723); +} + +/** + * @tc.name: mp4Source_GetFormat_3023 + * @tc.desc: get format when the file is MP4(h264 + aac 5.1) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3023, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4AvcAacUri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcAacUri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.bitRate, 876853); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 230723); +} + +/** + * @tc.name: mp4Source_GetFormat_2024 + * @tc.desc: get format when the file is MP4(h264 + acc 5.1) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_2024, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + fd_ = OpenFile(g_mp4AvcAacPath2); + size_ = GetFileSize(g_mp4AvcAacPath2); + printf("---- %s ----\n", g_mp4AvcAacPath2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 6); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 63); +} + +/** + * @tc.name: mp4Source_GetFormat_3024 + * @tc.desc: get format when the file is MP4(h264 + aac 5.1) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_3024, TestSize.Level1) +{ + if (access(AVC_LIB_PATH.c_str(), F_OK) != 0) { + return; + } + printf("---- %s ------\n", g_mp4AvcAacUri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AvcAacUri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 6); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 63); +} +} // namespace \ No newline at end of file diff --git a/test/unittest/mp4_source_test/mp4_source_unit_test.cpp b/test/unittest/mp4_source_test/mp4_source_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..efbadaf054432f821297aac869224bf02f3184c0 --- /dev/null +++ b/test/unittest/mp4_source_test/mp4_source_unit_test.cpp @@ -0,0 +1,1736 @@ +/* + * Copyright (C) 2025 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 +#include +#include "meta/meta_key.h" +#include "meta/meta.h" +#include "gtest/gtest.h" +#include "avcodec_errors.h" +#include "avcodec_audio_common.h" +#include "avcodec_info.h" +#include "media_description.h" +#include "file_server_demo.h" +#include "mp4_source_unit_test.h" +#include "media_data_source.h" +#include "native_avsource.h" +#include "native_avcodec_base.h" + +#define LOCAL true +#define URI false + +using namespace OHOS; +using namespace OHOS::MediaAVCodec; +using namespace testing::ext; +using namespace std; + +namespace { +unique_ptr server = nullptr; +static const string TEST_FILE_PATH = "/data/test/media/"; +static const string TEST_URI_PATH = "http://127.0.0.1:46666/"; +static const string TEST_TIMED_METADATA = "com.openharmony.timed_metadata.test"; +const int64_t SOURCE_OFFSET = 0; +string g_mp4CameraPath = TEST_FILE_PATH + string("camera.mp4"); +string g_multiTrackPath = TEST_FILE_PATH + string("multi_trk.mov"); +string g_fmp4SidxPath = TEST_FILE_PATH + string("sidx.mp4"); +string g_mp4FlipPath = TEST_FILE_PATH + string("FLIP_V_90.mp4"); +string g_mp4TimedMetadataPath = TEST_FILE_PATH + string("timed_metadata_track.mp4"); +string g_mp4AudiovividPath = TEST_FILE_PATH + string("audiovivid_hdrvivid.mp4"); +string g_mp4G711muPath = TEST_FILE_PATH + string("mpeg4_g711mu.mov"); +string g_mp4MPEG4Path = TEST_FILE_PATH + string("mpeg4_aac.mp4"); +string g_mp4MPEG4Path2 = TEST_FILE_PATH + string("mpeg4_flac.mp4"); +string g_mp4MPEG4Path3 = TEST_FILE_PATH + string("mpeg4_mp3.mp4"); +string g_mp4MPEG4Path4 = TEST_FILE_PATH + string("mpeg4_opus.mp4"); +string g_mp4MPEG4Path5 = TEST_FILE_PATH + string("mpeg4_amr_nb.3gp"); +string g_mp4MPEG4Path6 = TEST_FILE_PATH + string("mpeg4_flac_ch7.1.mp4"); +string g_mp4MPEG4Path7 = TEST_FILE_PATH + string("mpeg4_pcm_s32be_ch7.1.mov"); +string g_mp4MPEG4Path8 = TEST_FILE_PATH + string("mpeg4_aac_ch7.1wide.mp4"); +string g_mp4MPEG4Path9 = TEST_FILE_PATH + string("mpeg4_aac_ch4.0_eld.mp4"); +string g_mp4H263Path = TEST_FILE_PATH + string("h263_amr_wb.3gp"); +string g_mp4MPEG2Path = TEST_FILE_PATH + string("mpeg2_aac_test.mp4"); +string g_mp4MPEG2Path2 = TEST_FILE_PATH + string("mpeg2_alac.mp4"); +string g_mp4MPEG2Path3 = TEST_FILE_PATH + string("mpeg2_dts.mp4"); +} // namespace + +void Mp4SourceUnitTest::SetUpTestCase(void) +{ + server = make_unique(); + server->StartServer(); +} + +void Mp4SourceUnitTest::TearDownTestCase(void) +{ + server->StopServer(); +} + +void Mp4SourceUnitTest::SetUp(void) {} + +void Mp4SourceUnitTest::TearDown(void) +{ + if (source_ != nullptr) { + source_->Destroy(); + source_ = nullptr; + } + if (fd_ > 0) { + close(fd_); + fd_ = -1; + } + if (format_ != nullptr) { + format_->Destroy(); + format_ = nullptr; + } + trackIndex_ = 0; + size_ = 0; + addr_ = nullptr; + buffSize_ = 0; + initStatus_ = false; + ResetFormatValue(); +} + +int64_t Mp4SourceUnitTest::GetFileSize(const string &fileName) +{ + int64_t fileSize = 0; + if (!fileName.empty()) { + struct stat fileStatus {}; + if (stat(fileName.c_str(), &fileStatus) == 0) { + fileSize = static_cast(fileStatus.st_size); + } + } + return fileSize; +} + +int32_t Mp4SourceUnitTest::OpenFile(const string &fileName) +{ + int32_t fd = open(fileName.c_str(), O_RDONLY); + return fd; +} + +void Mp4SourceUnitTest::ResetFileFormatValue() +{ + formatVal_.title = ""; + formatVal_.artist = ""; + formatVal_.album = ""; + formatVal_.albumArtist = ""; + formatVal_.date = ""; + formatVal_.comment = ""; + formatVal_.genre = ""; + formatVal_.copyright = ""; + formatVal_.description = ""; + formatVal_.language = ""; + formatVal_.lyrics = ""; + formatVal_.duration = 0; + formatVal_.trackCount = 0; + formatVal_.author = ""; + formatVal_.composer = ""; + formatVal_.hasVideo = -1; + formatVal_.hasAudio = -1; + formatVal_.fileType = 0; +} + +void Mp4SourceUnitTest::ResetFormatValue() +{ + ResetFileFormatValue(); + formatVal_.timeScale = 0; + formatVal_.codecMime = ""; + formatVal_.trackType = 0; + formatVal_.width = 0; + formatVal_.height = 0; + formatVal_.aacIsAdts = -1; + formatVal_.sampleRate = 0; + formatVal_.channelCount = 0; + formatVal_.bitRate = 0; + formatVal_.startTime = 0; + formatVal_.audioSampleFormat = 0; + formatVal_.frameRate = 0; + formatVal_.videoSar = 0; + formatVal_.longitude = 0; + formatVal_.latitude = 0; + formatVal_.rotationAngle = 0; + formatVal_.orientationType = 0; + formatVal_.channelLayout = 0; + formatVal_.hdrType = 0; + formatVal_.codecProfile = 0; + formatVal_.codecLevel = 0; + formatVal_.colorPrimaries = 0; + formatVal_.transferCharacteristics = 0; + formatVal_.rangeFlag = 0; + formatVal_.matrixCoefficients = 0; + formatVal_.chromaLocation = 0; + formatVal_.profile = 0; + formatVal_.level = 0; + formatVal_.colorPri = 0; + formatVal_.colorTrans = 0; + formatVal_.colorMatrix = 0; + formatVal_.colorRange = 0; + formatVal_.chromaLoc = 0; + formatVal_.isHdrVivid = 0; +} + +/**********************************source FD**************************************/ +namespace { +/** + * @tc.name: mp4Source_GetFormat_0001 + * @tc.desc: get user data + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0001, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4CameraPath); + size_ = GetFileSize(g_mp4CameraPath); + printf("---- %s ------\n", g_mp4CameraPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[source Format]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3367000); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_VIDEO, formatVal_.hasVideo)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_AUDIO, formatVal_.hasAudio)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_TIMEDMETA, formatVal_.hasTimedMeta)); + EXPECT_EQ(formatVal_.hasVideo, 1); + EXPECT_EQ(formatVal_.hasAudio, 1); + EXPECT_EQ(formatVal_.hasTimedMeta, 1); +#endif + format_ = source_->GetUserData(); + EXPECT_NE(format_, nullptr); + printf("[User Meta]: %s\n", format_->DumpInfo()); + string isWaterMark = "0"; + EXPECT_TRUE(format_->GetStringValue("com.openharmony.isWaterMark", isWaterMark)); + EXPECT_EQ(isWaterMark, "false"); // test user string data watermark + int flag = 0; + EXPECT_TRUE(format_->GetIntValue("com.openharmony.deferredVideoEnhanceFlag", flag)); + EXPECT_EQ(flag, 0); // test user int flag + + string timeStamp = "0"; + EXPECT_TRUE(format_->GetStringValue("com.openharmony.recorder.timestamp", timeStamp)); + EXPECT_EQ(timeStamp, "11236330712,11239663416"); // test user string data timestamp +} + +/** + * @tc.name: mp4Source_GetFormat_0002 + * @tc.desc: get format when the file is mov + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0002, TestSize.Level1) +{ + fd_ = OpenFile(g_multiTrackPath); + size_ = GetFileSize(g_multiTrackPath); + printf("---- %s ----\n", g_multiTrackPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.duration, 2000000); + EXPECT_EQ(formatVal_.timeScale, 1000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + EXPECT_EQ(formatVal_.bitRate, 304504); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.width, 352); + EXPECT_EQ(formatVal_.height, 288); + EXPECT_EQ(formatVal_.bitRate, 544636); +} + +/** + * @tc.name: mp4Source_GetFormat_0003 + * @tc.desc: get format when the file is mov + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0003, TestSize.Level1) +{ + fd_ = OpenFile(g_multiTrackPath); + size_ = GetFileSize(g_multiTrackPath); + printf("---- %s ----\n", g_multiTrackPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.duration, 2000000); + EXPECT_EQ(formatVal_.trackCount, 4); + EXPECT_EQ(formatVal_.timeScale, 1000); + trackIndex_ = 2; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 125574); + trackIndex_ = 3; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mpeg"); + EXPECT_EQ(formatVal_.sampleRate, 32000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.bitRate, 48412); +} + +/** + * @tc.name: mp4Source_GetFormat_0004 + * @tc.desc: get format when the file is fmp4 and has sidx box + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0004, TestSize.Level1) +{ + fd_ = OpenFile(g_fmp4SidxPath); + size_ = GetFileSize(g_fmp4SidxPath); + printf("---- %s ----\n", g_fmp4SidxPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.startTime, 33333); + EXPECT_EQ(formatVal_.bitRate, 1076608); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mpeg"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 123423); +} + +/** + * @tc.name: mp4Source_GetFormat_0005 + * @tc.desc: get displaymatrix + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0005, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4FlipPath); + size_ = GetFileSize(g_mp4FlipPath); + printf("---- %s ----\n", g_mp4FlipPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::VIDEO_ORIENTATION_TYPE, formatVal_.orientationType)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_ROTATION, formatVal_.rotationAngle)); + EXPECT_EQ(formatVal_.orientationType, 7); + EXPECT_EQ(formatVal_.rotationAngle, 270); +} + +/** + * @tc.name: mp4Source_GetFormat_0006 + * @tc.desc: get format when the file is timed metadata file + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0006, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4TimedMetadataPath); + size_ = GetFileSize(g_mp4TimedMetadataPath); + printf("---- %s ----\n", g_mp4TimedMetadataPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_EQ(formatVal_.trackCount, 2); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_FILE_TYPE, formatVal_.fileType)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_VIDEO, formatVal_.hasVideo)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_AUDIO, formatVal_.hasAudio)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_TIMEDMETA, formatVal_.hasTimedMeta)); + EXPECT_EQ(formatVal_.fileType, 101); + EXPECT_EQ(formatVal_.hasVideo, 1); + EXPECT_EQ(formatVal_.hasAudio, 0); + EXPECT_EQ(formatVal_.hasTimedMeta, 1); +#endif + trackIndex_ = 1; + format_ = source_->GetTrackFormat(trackIndex_); + ASSERT_NE(format_, nullptr); + printf("[trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetStringValue(MediaDescriptionKey::MD_KEY_TIMED_METADATA_KEY, formatVal_.timedMetadataKey)); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TIMED_METADATA_SRC_TRACK_ID, formatVal_.srcTrackID)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_TIMED_METADATA); + EXPECT_EQ(formatVal_.codecMime, "meta/timed-metadata"); + EXPECT_EQ(formatVal_.timedMetadataKey, TEST_TIMED_METADATA.c_str()); + EXPECT_EQ(formatVal_.srcTrackID, 0); +} + +/** + * @tc.name: mp4Source_GetFormat_0007 + * @tc.desc: get format when the file is audiovivid + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0007, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4AudiovividPath); + size_ = GetFileSize(g_mp4AudiovividPath); + printf("---- %s ----\n", g_mp4AudiovividPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.duration, 2699349); + EXPECT_EQ(formatVal_.trackCount, 2); + EXPECT_EQ(formatVal_.timeScale, 1000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 1280); + EXPECT_EQ(formatVal_.height, 720); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + EXPECT_EQ(formatVal_.bitRate, 24913240); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/av3a"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 64083); +} + +/** + * @tc.name: mp4Source_GetFormat_0008 + * @tc.desc: get format when the file is MP4(mpeg4 + g711mu) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0008, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4G711muPath); + size_ = GetFileSize(g_mp4G711muPath); + printf("---- %s ----\n", g_mp4G711muPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 1500000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 3104213); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/g711mu"); + EXPECT_EQ(formatVal_.bitRate, 64000); +} + +/** + * @tc.name: mp4Source_GetFormat_0009 + * @tc.desc: get format when the file is MP4(mpeg4 + g711mu) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0009, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4G711muPath); + size_ = GetFileSize(g_mp4G711muPath); + printf("---- %s ----\n", g_mp4G711muPath.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 1500000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 8000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16LE); + EXPECT_EQ(formatVal_.channelLayout, 4); +} + +/** + * @tc.name: mp4Source_GetFormat_0010 + * @tc.desc: get format when the file is MP4(mpeg4 + aac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0010, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path); + size_ = GetFileSize(g_mp4MPEG4Path); + printf("---- %s ----\n", g_mp4MPEG4Path.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 256189); +} + +/** + * @tc.name: mp4Source_GetFormat_0011 + * @tc.desc: get format when the file is MP4(mpeg4 + aac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0011, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path); + size_ = GetFileSize(g_mp4MPEG4Path); + printf("---- %s ----\n", g_mp4MPEG4Path.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_0012 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0012, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path2); + size_ = GetFileSize(g_mp4MPEG4Path2); + printf("---- %s ----\n", g_mp4MPEG4Path2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/flac"); + EXPECT_EQ(formatVal_.bitRate, 1147385); +} + +/** + * @tc.name: mp4Source_GetFormat_0013 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0013, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path2); + size_ = GetFileSize(g_mp4MPEG4Path2); + printf("---- %s ----\n", g_mp4MPEG4Path2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.bitRate, 1243454); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S32LE); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_0014 + * @tc.desc: get format when the file is MP4(mpeg4 + mp3) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0014, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path3); + size_ = GetFileSize(g_mp4MPEG4Path3); + printf("---- %s ----\n", g_mp4MPEG4Path3.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mpeg"); + EXPECT_EQ(formatVal_.bitRate, 128142); +} + +/** + * @tc.name: mp4Source_GetFormat_0015 + * @tc.desc: get format when the file is MP4(mpeg4 + mp3) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0015, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path3); + size_ = GetFileSize(g_mp4MPEG4Path3); + printf("---- %s ----\n", g_mp4MPEG4Path3.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_0016 + * @tc.desc: get format when the file is MP4(mpeg2 + aac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0016, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG2Path); + size_ = GetFileSize(g_mp4MPEG2Path); + printf("---- %s ----\n", g_mp4MPEG2Path.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_TITLE, formatVal_.title)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ARTIST, formatVal_.artist)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ALBUM, formatVal_.album)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ALBUM_ARTIST, formatVal_.albumArtist)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_DATE, formatVal_.date)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_COMMENT, formatVal_.comment)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_GENRE, formatVal_.genre)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_COPYRIGHT, formatVal_.copyright)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_LYRICS, formatVal_.lyrics)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_DESCRIPTION, formatVal_.description)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_EQ(formatVal_.title, "title"); + EXPECT_EQ(formatVal_.artist, "artist"); + EXPECT_EQ(formatVal_.album, "album"); + EXPECT_EQ(formatVal_.albumArtist, "album artist"); + EXPECT_EQ(formatVal_.date, "2023"); + EXPECT_EQ(formatVal_.comment, "comment"); + EXPECT_EQ(formatVal_.genre, "genre"); + EXPECT_EQ(formatVal_.copyright, "Copyright"); + EXPECT_EQ(formatVal_.lyrics, "lyrics"); + EXPECT_EQ(formatVal_.description, "description"); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 4120000); + EXPECT_EQ(formatVal_.trackCount, 2); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + EXPECT_TRUE(format_->GetStringValue(AVSourceFormat::SOURCE_COMPOSER, formatVal_.composer)); + EXPECT_TRUE(format_->GetStringValue(AVSourceFormat::SOURCE_AUTHOR, formatVal_.author)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_VIDEO, formatVal_.hasVideo)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_AUDIO, formatVal_.hasAudio)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_FILE_TYPE, formatVal_.fileType)); + EXPECT_EQ(formatVal_.author, "author"); + EXPECT_EQ(formatVal_.composer, "composer"); + EXPECT_EQ(formatVal_.hasVideo, 1); + EXPECT_EQ(formatVal_.hasAudio, 1); + EXPECT_EQ(formatVal_.fileType, 101); +#endif +} + +/** + * @tc.name: mp4Source_GetFormat_0017 + * @tc.desc: get format when the file is MP4(mpeg4 + opus) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0017, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path4); + size_ = GetFileSize(g_mp4MPEG4Path4); + printf("---- %s ----\n", g_mp4MPEG4Path4.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/opus"); + EXPECT_EQ(formatVal_.bitRate, 96757); +} + +/** + * @tc.name: mp4Source_GetFormat_0018 + * @tc.desc: get format when the file is MP4(mpeg4 + opus) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0018, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path4); + size_ = GetFileSize(g_mp4MPEG4Path4); + printf("---- %s ----\n", g_mp4MPEG4Path4.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_0019 + * @tc.desc: get format when the file is MP4(mpeg4 + amr_nb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0019, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path5); + size_ = GetFileSize(g_mp4MPEG4Path5); + printf("---- %s ----\n", g_mp4MPEG4Path5.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/3gpp"); + EXPECT_EQ(formatVal_.bitRate, 12832); +} + +/** + * @tc.name: mp4Source_GetFormat_0020 + * @tc.desc: get format when the file is MP4(mpeg4 + amr_nb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0020, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path5); + size_ = GetFileSize(g_mp4MPEG4Path5); + printf("---- %s ----\n", g_mp4MPEG4Path5.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 8000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 4); +} + +/** + * @tc.name: mp4Source_GetFormat_0021 + * @tc.desc: get format when the file is MP4(h263 + amr_wb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0021, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4H263Path); + size_ = GetFileSize(g_mp4H263Path); + printf("---- %s ----\n", g_mp4H263Path.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/h263"); + EXPECT_EQ(formatVal_.bitRate, 508906); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/amr-wb"); + EXPECT_EQ(formatVal_.bitRate, 24509); +} + +/** + * @tc.name: mp4Source_GetFormat_0022 + * @tc.desc: get format when the file is MP4(h263 + amr_wb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0022, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4H263Path); + size_ = GetFileSize(g_mp4H263Path); + printf("---- %s ----\n", g_mp4H263Path.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 352); + EXPECT_EQ(formatVal_.height, 288); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 16000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32LE); + EXPECT_EQ(formatVal_.channelLayout, 4); +} + +/** + * @tc.name: mp4Source_GetFormat_0023 + * @tc.desc: get format when the file is MP4(mpeg2 + alac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0023, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG2Path2); + size_ = GetFileSize(g_mp4MPEG2Path2); + printf("---- %s ----\n", g_mp4MPEG2Path2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mpeg2"); + EXPECT_EQ(formatVal_.bitRate, 1344761); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.bitRate, 1222454); +} + +/** + * @tc.name: mp4Source_GetFormat_0024 + * @tc.desc: get format when the file is MP4(mpeg2 + alac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0024, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG2Path2); + size_ = GetFileSize(g_mp4MPEG2Path2); + printf("---- %s ----\n", g_mp4MPEG2Path2.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_0025 + * @tc.desc: get format when the file is MP4(mpeg2 + dts) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0025, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG2Path3); + size_ = GetFileSize(g_mp4MPEG2Path3); + printf("---- %s ----\n", g_mp4MPEG2Path3.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mpeg2"); + EXPECT_EQ(formatVal_.bitRate, 1344761); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.bitRate, 1413017); +} + +/** + * @tc.name: mp4Source_GetFormat_0026 + * @tc.desc: get format when the file is MP4(mpeg2 + dts) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0026, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG2Path3); + size_ = GetFileSize(g_mp4MPEG2Path3); + printf("---- %s ----\n", g_mp4MPEG2Path3.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_0027 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0027, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path6); + size_ = GetFileSize(g_mp4MPEG4Path6); + printf("---- %s ----\n", g_mp4MPEG4Path6.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/flac"); + EXPECT_EQ(formatVal_.bitRate, 393832); +} + +/** + * @tc.name: mp4Source_GetFormat_0028 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0028, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path6); + size_ = GetFileSize(g_mp4MPEG4Path6); + printf("---- %s ----\n", g_mp4MPEG4Path6.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 981576); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16LE); + EXPECT_EQ(formatVal_.channelLayout, 1599); +} + +/** + * @tc.name: mp4Source_GetFormat_0029 + * @tc.desc: get format when the file is MP4(mpeg4 + pcm) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0029, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path7); + size_ = GetFileSize(g_mp4MPEG4Path7); + printf("---- %s ----\n", g_mp4MPEG4Path7.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 981576); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/raw"); + EXPECT_EQ(formatVal_.bitRate, 11289600); +} + +/** + * @tc.name: mp4Source_GetFormat_0030 + * @tc.desc: get format when the file is MP4(mpeg4 + pcm) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0030, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path7); + size_ = GetFileSize(g_mp4MPEG4Path7); + printf("---- %s ----\n", g_mp4MPEG4Path7.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, 26); + EXPECT_EQ(formatVal_.channelLayout, 1599); +} + +/** + * @tc.name: mp4Source_GetFormat_0031 + * @tc.desc: get format when the file is MP4(mpeg4 + aac-pce 7.1 wide) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0031, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path8); + size_ = GetFileSize(g_mp4MPEG4Path8); + printf("---- %s ----\n", g_mp4MPEG4Path8.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 981576); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 268264); +} + +/** + * @tc.name: mp4Source_GetFormat_0032 + * @tc.desc: get format when the file is MP4(mpeg4 + aac-pce 7.1 wide) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0032, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path8); + size_ = GetFileSize(g_mp4MPEG4Path8); + printf("---- %s ----\n", g_mp4MPEG4Path8.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 206158431759); +} + +/** + * @tc.name: mp4Source_GetFormat_0033 + * @tc.desc: get format when the file is MP4(mpeg4 + aac_eld 4.0) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0033, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path9); + size_ = GetFileSize(g_mp4MPEG4Path9); + printf("---- %s ----\n", g_mp4MPEG4Path9.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 981325); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 320851); +} + +/** + * @tc.name: mp4Source_GetFormat_0034 + * @tc.desc: get format when the file is MP4(mpeg4 + aac_eld 4.0) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_0034, TestSize.Level1) +{ + fd_ = OpenFile(g_mp4MPEG4Path9); + size_ = GetFileSize(g_mp4MPEG4Path9); + printf("---- %s ----\n", g_mp4MPEG4Path9.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithFD(fd_, SOURCE_OFFSET, size_); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 4); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 263); +} +} // namespace \ No newline at end of file diff --git a/test/unittest/mp4_source_test/mp4_source_unit_test.h b/test/unittest/mp4_source_test/mp4_source_unit_test.h new file mode 100644 index 0000000000000000000000000000000000000000..778315499cbeb24176b19538fc1448eb8d753494 --- /dev/null +++ b/test/unittest/mp4_source_test/mp4_source_unit_test.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2025 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 MP4_SOURCE_UNIT_TEST_H +#define MP4_SOURCE_UNIT_TEST_H + +#include "gtest/gtest.h" +#include "mp4_source_mock.h" + +namespace OHOS { +namespace MediaAVCodec { +class Mp4SourceUnitTest : public testing::Test { +public: + // SetUpTestCase: Called before all test cases + static void SetUpTestCase(void); + // TearDownTestCase: Called after all test case + static void TearDownTestCase(void); + // SetUp: Called before each test cases + void SetUp(void); + // TearDown: Called after each test cases + void TearDown(void); + int64_t GetFileSize(const std::string &fileName); + int32_t OpenFile(const std::string &fileName); + void ResetFormatValue(); + void ResetFileFormatValue(); + + void InitResource(const std::string &path, bool local); + void CheckHevcInfo(const std::string resName); + void CheckAvcInfo(const std::string resName); + +protected: + std::shared_ptr source_ = nullptr; + std::shared_ptr format_ = nullptr; + int32_t fd_ = -1; + int64_t size_ = 0; + uint32_t trackIndex_ = 0; + int32_t streamsCount_ = 0; + int32_t vTrackIdx_ = 0; + int32_t aTrackIdx_ = 0; + uint8_t *addr_ = nullptr; + size_t buffSize_ = 0; + bool initStatus_ = false; + + struct FormatValue { + // source format + std::string title = ""; + std::string artist = ""; + std::string album = ""; + std::string albumArtist = ""; + std::string date = ""; + std::string comment = ""; + std::string genre = ""; + std::string copyright = ""; + std::string description = ""; + std::string language = ""; + std::string lyrics = ""; + int64_t duration = 0; + int32_t trackCount = 0; + int32_t timeScale = 0; + std::string author = ""; + std::string composer = ""; + int32_t hasVideo = -1; + int32_t hasAudio = -1; + int32_t hasTimedMeta = -1; + int32_t hasSubtitle = -1; + int32_t fileType = 0; + // track format + std::string codecMime = ""; + int32_t trackType = 0; + int32_t width = 0; + int32_t height = 0; + int32_t aacIsAdts = -1; + int32_t sampleRate = 0; + int32_t channelCount = 0; + int64_t bitRate = 0; + int64_t startTime = 0; + int32_t audioSampleFormat = 0; + double frameRate = 0; + double videoSar = 0; + float longitude = 0; + float latitude = 0; + int32_t rotationAngle = 0; + int32_t orientationType = 0; + int64_t channelLayout = 0; + int32_t hdrType = 0; + int32_t codecProfile = 0; + int32_t codecLevel = 0; + int32_t colorPrimaries = 0; + int32_t transferCharacteristics = 0; + int32_t rangeFlag = 0; + int32_t matrixCoefficients = 0; + int32_t chromaLocation = 0; + std::string timedMetadataKey = ""; + int32_t srcTrackID = -1; + // hevc format + int32_t profile = 0; + int32_t level = 0; + int32_t colorPri = 0; + int32_t colorTrans = 0; + int32_t colorMatrix = 0; + int32_t colorRange = 0; + int32_t chromaLoc = 0; + int32_t isHdrVivid = 0; + // ape format + int32_t audioMaxInputSize = 0; + + int32_t samplePerFrame = 0; + }; + FormatValue formatVal_; +}; +} // namespace MediaAVCodec +} // namespace OHOS +#endif // MP4_SOURCE_UNIT_TEST_H diff --git a/test/unittest/mp4_source_test/mp4_source_uri_unit_test.cpp b/test/unittest/mp4_source_test/mp4_source_uri_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93911630828c241a4a1c8194dbf2b57d49f8f08c --- /dev/null +++ b/test/unittest/mp4_source_test/mp4_source_uri_unit_test.cpp @@ -0,0 +1,1552 @@ +/* + * Copyright (C) 2025 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 +#include "gtest/gtest.h" +#include "avcodec_errors.h" +#include "avcodec_audio_common.h" +#include "avcodec_info.h" +#include "media_description.h" +#include "file_server_demo.h" +#include "mp4_source_unit_test.h" + +using namespace OHOS; +using namespace OHOS::MediaAVCodec; +using namespace testing::ext; +using namespace std; + +namespace { +unique_ptr server = nullptr; +static const string TEST_URI_PATH = "http://127.0.0.1:46666/"; +static const string TEST_TIMED_METADATA = "com.openharmony.timed_metadata.test"; +string g_mp4CameraUri = TEST_URI_PATH + string("camera.mp4"); +string g_multiTrackUri = TEST_URI_PATH + string("multi_trk.mov"); +string g_fmp4SidxUri = TEST_URI_PATH + string("sidx.mp4"); +string g_mp4FlipUri = TEST_URI_PATH + string("FLIP_V_90.mp4"); +string g_mp4TimedMetadataUri = TEST_URI_PATH + string("timed_metadata_track.mp4"); +string g_mp4AudiovividUri = TEST_URI_PATH + string("audiovivid_hdrvivid.mp4"); +string g_mp4G711muUri = TEST_URI_PATH + string("mpeg4_g711mu.mov"); +string g_mp4MPEG4Uri = TEST_URI_PATH + string("mpeg4_aac.mp4"); +string g_mp4MPEG4Uri2 = TEST_URI_PATH + string("mpeg4_flac.mp4"); +string g_mp4MPEG4Uri3 = TEST_URI_PATH + string("mpeg4_mp3.mp4"); +string g_mp4MPEG4Uri4 = TEST_URI_PATH + string("mpeg4_opus.mp4"); +string g_mp4MPEG4Uri5 = TEST_URI_PATH + string("mpeg4_amr_nb.3gp"); +string g_mp4MPEG4Uri6 = TEST_URI_PATH + string("mpeg4_flac_ch7.1.mp4"); +string g_mp4MPEG4Uri7 = TEST_URI_PATH + string("mpeg4_pcm_s32be_ch7.1.mov"); +string g_mp4MPEG4Uri8 = TEST_URI_PATH + string("mpeg4_aac_ch7.1wide.mp4"); +string g_mp4MPEG4Uri9 = TEST_URI_PATH + string("mpeg4_aac_ch4.0_eld.mp4"); +string g_mp4H263Uri = TEST_URI_PATH + string("h263_amr_wb.3gp"); +string g_mp4MPEG2Uri = TEST_URI_PATH + string("mpeg2_aac_test.mp4"); +string g_mp4MPEG2Uri2 = TEST_URI_PATH + string("mpeg2_alac.mp4"); +string g_mp4MPEG2Uri3 = TEST_URI_PATH + string("mpeg2_dts.mp4"); + +/**********************************source URI**************************************/ +/** + * @tc.name: mp4Source_GetFormat_1001 + * @tc.desc: get user data + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1001, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4CameraUri.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4CameraUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[source Format]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3367000); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_VIDEO, formatVal_.hasVideo)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_AUDIO, formatVal_.hasAudio)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_TIMEDMETA, formatVal_.hasTimedMeta)); + EXPECT_EQ(formatVal_.hasVideo, 1); + EXPECT_EQ(formatVal_.hasAudio, 1); + EXPECT_EQ(formatVal_.hasTimedMeta, 1); +#endif + format_ = source_->GetUserData(); + EXPECT_NE(format_, nullptr); + printf("[User Meta]: %s\n", format_->DumpInfo()); + string isWaterMark = "0"; + EXPECT_TRUE(format_->GetStringValue("com.openharmony.isWaterMark", isWaterMark)); + EXPECT_EQ(isWaterMark, "false"); // test user string data watermark + int flag = 0; + EXPECT_TRUE(format_->GetIntValue("com.openharmony.deferredVideoEnhanceFlag", flag)); + EXPECT_EQ(flag, 0); // test user int flag + + string timeStamp = "0"; + EXPECT_TRUE(format_->GetStringValue("com.openharmony.recorder.timestamp", timeStamp)); + EXPECT_EQ(timeStamp, "11236330712,11239663416"); // test user string data timestamp +} + +/** + * @tc.name: mp4Source_GetFormat_1002 + * @tc.desc: get format when the file is mov + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1002, TestSize.Level1) +{ + printf("---- %s ------\n", g_multiTrackUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_multiTrackUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.duration, 2000000); + EXPECT_EQ(formatVal_.timeScale, 1000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + EXPECT_EQ(formatVal_.bitRate, 304504); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.width, 352); + EXPECT_EQ(formatVal_.height, 288); + EXPECT_EQ(formatVal_.bitRate, 544636); +} + +/** + * @tc.name: mp4Source_GetFormat_1003 + * @tc.desc: get format when the file is mov + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1003, TestSize.Level1) +{ + printf("---- %s ------\n", g_multiTrackUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_multiTrackUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_FILE_TYPE, formatVal_.fileType)); + EXPECT_EQ(formatVal_.duration, 2000000); + EXPECT_EQ(formatVal_.trackCount, 4); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.fileType, 107); + trackIndex_ = 2; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 125574); + trackIndex_ = 3; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mpeg"); + EXPECT_EQ(formatVal_.sampleRate, 32000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.bitRate, 48412); +} + +/** + * @tc.name: mp4Source_GetFormat_1004 + * @tc.desc: get format when the file is fmp4 and has sidx box + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1004, TestSize.Level1) +{ + printf("---- %s ------\n", g_fmp4SidxUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_fmp4SidxUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.startTime, 0); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 10100000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/avc"); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.startTime, 33333); + EXPECT_EQ(formatVal_.bitRate, 1076608); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mpeg"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 123423); +} + +/** + * @tc.name: mp4Source_GetFormat_1005 + * @tc.desc: get displaymatrix + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1005, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4FlipUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4FlipUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::VIDEO_ORIENTATION_TYPE, formatVal_.orientationType)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_ROTATION, formatVal_.rotationAngle)); + EXPECT_EQ(formatVal_.orientationType, 7); + EXPECT_EQ(formatVal_.rotationAngle, 270); +} + +/** + * @tc.name: mp4Source_GetFormat_1006 + * @tc.desc: get format when the file is timed metadata file + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1006, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4TimedMetadataUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4TimedMetadataUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_EQ(formatVal_.trackCount, 2); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_FILE_TYPE, formatVal_.fileType)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_VIDEO, formatVal_.hasVideo)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_AUDIO, formatVal_.hasAudio)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_TIMEDMETA, formatVal_.hasTimedMeta)); + EXPECT_EQ(formatVal_.fileType, 101); + EXPECT_EQ(formatVal_.hasVideo, 1); + EXPECT_EQ(formatVal_.hasAudio, 0); + EXPECT_EQ(formatVal_.hasTimedMeta, 1); +#endif + trackIndex_ = 1; + format_ = source_->GetTrackFormat(trackIndex_); + ASSERT_NE(format_, nullptr); + printf("[trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetStringValue(MediaDescriptionKey::MD_KEY_TIMED_METADATA_KEY, formatVal_.timedMetadataKey)); + EXPECT_TRUE(format_->GetIntValue(MediaDescriptionKey::MD_KEY_TIMED_METADATA_SRC_TRACK_ID, formatVal_.srcTrackID)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_TIMED_METADATA); + EXPECT_EQ(formatVal_.codecMime, "meta/timed-metadata"); + EXPECT_EQ(formatVal_.timedMetadataKey, TEST_TIMED_METADATA.c_str()); + EXPECT_EQ(formatVal_.srcTrackID, 0); +} + +/** + * @tc.name: mp4Source_GetFormat_1007 + * @tc.desc: get format when the file is audiovivid + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1007, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4AudiovividUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4AudiovividUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_FILE_TYPE, formatVal_.fileType)); + EXPECT_EQ(formatVal_.duration, 2699349); + EXPECT_EQ(formatVal_.trackCount, 2); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.fileType, 101); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/hevc"); + EXPECT_EQ(formatVal_.width, 1280); + EXPECT_EQ(formatVal_.height, 720); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + EXPECT_EQ(formatVal_.bitRate, 24913240); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/av3a"); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.bitRate, 64083); +} + +/** + * @tc.name: mp4Source_GetFormat_1008 + * @tc.desc: get format when the file is MP4(mpeg4 + g711mu) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1008, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4G711muUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4G711muUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 1500000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 3104213); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/g711mu"); + EXPECT_EQ(formatVal_.bitRate, 64000); +} + +/** + * @tc.name: mp4Source_GetFormat_1009 + * @tc.desc: get format when the file is MP4(mpeg4 + g711mu) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1009, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4G711muUri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4G711muUri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 1500000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 1920); + EXPECT_EQ(formatVal_.height, 1080); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 8000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16LE); + EXPECT_EQ(formatVal_.channelLayout, 4); +} + +/** + * @tc.name: mp4Source_GetFormat_1010 + * @tc.desc: get format when the file is MP4(mpeg4 + aac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1010, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 256189); +} + +/** + * @tc.name: mp4Source_GetFormat_1011 + * @tc.desc: get format when the file is MP4(mpeg4 + aac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1011, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_1012 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1012, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/flac"); + EXPECT_EQ(formatVal_.bitRate, 1147385); +} + +/** + * @tc.name: mp4Source_GetFormat_1013 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1013, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.bitRate, 1243454); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S32LE); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_1014 + * @tc.desc: get format when the file is MP4(mpeg4 + mp3) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1014, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri3.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri3.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mpeg"); + EXPECT_EQ(formatVal_.bitRate, 128142); +} + +/** + * @tc.name: mp4Source_GetFormat_1015 + * @tc.desc: get format when the file is MP4(mpeg4 + mp3) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1015, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri3.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri3.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_1016 + * @tc.desc: get format when the file is MP4(mpeg4 + aac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1016, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG2Uri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG2Uri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_TITLE, formatVal_.title)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ARTIST, formatVal_.artist)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ALBUM, formatVal_.album)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_ALBUM_ARTIST, formatVal_.albumArtist)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_DATE, formatVal_.date)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_COMMENT, formatVal_.comment)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_GENRE, formatVal_.genre)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_COPYRIGHT, formatVal_.copyright)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_LYRICS, formatVal_.lyrics)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_DESCRIPTION, formatVal_.description)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_COUNT, formatVal_.trackCount)); + EXPECT_EQ(formatVal_.title, "title"); + EXPECT_EQ(formatVal_.artist, "artist"); + EXPECT_EQ(formatVal_.album, "album"); + EXPECT_EQ(formatVal_.albumArtist, "album artist"); + EXPECT_EQ(formatVal_.date, "2023"); + EXPECT_EQ(formatVal_.comment, "comment"); + EXPECT_EQ(formatVal_.genre, "genre"); + EXPECT_EQ(formatVal_.copyright, "Copyright"); + EXPECT_EQ(formatVal_.lyrics, "lyrics"); + EXPECT_EQ(formatVal_.description, "description"); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 4120000); + EXPECT_EQ(formatVal_.trackCount, 2); +#ifdef MP4_SOURCE_INNER_UNIT_TEST + EXPECT_TRUE(format_->GetStringValue(AVSourceFormat::SOURCE_COMPOSER, formatVal_.composer)); + EXPECT_TRUE(format_->GetStringValue(AVSourceFormat::SOURCE_AUTHOR, formatVal_.author)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_VIDEO, formatVal_.hasVideo)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_HAS_AUDIO, formatVal_.hasAudio)); + EXPECT_TRUE(format_->GetIntValue(AVSourceFormat::SOURCE_FILE_TYPE, formatVal_.fileType)); + EXPECT_EQ(formatVal_.author, "author"); + EXPECT_EQ(formatVal_.composer, "composer"); + EXPECT_EQ(formatVal_.hasVideo, 1); + EXPECT_EQ(formatVal_.hasAudio, 1); + EXPECT_EQ(formatVal_.fileType, 101); +#endif +} + +/** + * @tc.name: mp4Source_GetFormat_1017 + * @tc.desc: get format when the file is MP4(mpeg4 + opus) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1017, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri4.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri4.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/opus"); + EXPECT_EQ(formatVal_.bitRate, 96757); +} + +/** + * @tc.name: mp4Source_GetFormat_1018 + * @tc.desc: get format when the file is MP4(mpeg4 + opus) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1018, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri4.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri4.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + + +/** + * @tc.name: mp4Source_GetFormat_1019 + * @tc.desc: get format when the file is MP4(mpeg4 + amr_nb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1019, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri5.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri5.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 1243454); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/3gpp"); + EXPECT_EQ(formatVal_.bitRate, 12832); +} + +/** + * @tc.name: mp4Source_GetFormat_1020 + * @tc.desc: get format when the file is MP4(mpeg4 + amr_nb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1020, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri5.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri5.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 8000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 4); +} + +/** + * @tc.name: mp4Source_GetFormat_1021 + * @tc.desc: get format when the file is MP4(h263 + amr_wb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1021, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4H263Uri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4H263Uri.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/h263"); + EXPECT_EQ(formatVal_.bitRate, 508906); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/amr-wb"); + EXPECT_EQ(formatVal_.bitRate, 24509); +} + +/** + * @tc.name: mp4Source_GetFormat_1022 + * @tc.desc: get format when the file is MP4(h263 + amr_wb) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1022, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4H263Uri.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4H263Uri.data())); + EXPECT_NE(source_, nullptr); + trackIndex_ = 1; + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 352); + EXPECT_EQ(formatVal_.height, 288); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 16000); + EXPECT_EQ(formatVal_.channelCount, 1); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32LE); + EXPECT_EQ(formatVal_.channelLayout, 4); +} + +/** + * @tc.name: mp4Source_GetFormat_1023 + * @tc.desc: get format when the file is MP4(mpeg2 + alac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1023, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG2Uri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG2Uri2.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mpeg2"); + EXPECT_EQ(formatVal_.bitRate, 1344761); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.bitRate, 1222454); +} + +/** + * @tc.name: mp4Source_GetFormat_1024 + * @tc.desc: get format when the file is MP4(mpeg2 + alac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1024, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG2Uri2.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG2Uri2.data())); + EXPECT_NE(source_, nullptr); + trackIndex_ = 1; + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_1025 + * @tc.desc: get format when the file is MP4(mpeg2 + dts) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1025, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG2Uri3.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG2Uri3.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mpeg2"); + EXPECT_EQ(formatVal_.bitRate, 1344761); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.bitRate, 1413017); +} + +/** + * @tc.name: mp4Source_GetFormat_1026 + * @tc.desc: get format when the file is MP4(mpeg2 + dts) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1026, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG2Uri3.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG2Uri3.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3300000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 640); + EXPECT_EQ(formatVal_.height, 352); + EXPECT_EQ(formatVal_.frameRate, 30.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 48000); + EXPECT_EQ(formatVal_.channelCount, 2); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 3); +} + +/** + * @tc.name: mp4Source_GetFormat_1027 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1027, TestSize.Level1) +{ + printf("---- %s ----\n", g_mp4MPEG4Uri6.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri6.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/flac"); + EXPECT_EQ(formatVal_.bitRate, 393832); +} + +/** + * @tc.name: mp4Source_GetFormat_1028 + * @tc.desc: get format when the file is MP4(mpeg4 + flac) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1028, TestSize.Level1) +{ + printf("---- %s ----\n", g_mp4MPEG4Uri6.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri6.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.bitRate, 981576); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_S16LE); + EXPECT_EQ(formatVal_.channelLayout, 1599); +} + +/** + * @tc.name: mp4Source_GetFormat_1029 + * @tc.desc: get format when the file is MP4(mpeg4 + pcm) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1029, TestSize.Level1) +{ + printf("---- %s ----\n", g_mp4MPEG4Uri7.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri7.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 981576); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/raw"); + EXPECT_EQ(formatVal_.bitRate, 11289600); +} + +/** + * @tc.name: mp4Source_GetFormat_1030 + * @tc.desc: get format when the file is MP4(mpeg4 + pcm) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1030, TestSize.Level1) +{ + printf("---- %s ----\n", g_mp4MPEG4Uri7.c_str()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri7.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, 26); + EXPECT_EQ(formatVal_.channelLayout, 1599); +} + +/** + * @tc.name: mp4Source_GetFormat_1031 + * @tc.desc: get format when the file is MP4(mpeg4 + aac-pce 7.1 wide) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1031, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri8.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri8.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 981576); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 268264); +} + +/** + * @tc.name: mp4Source_GetFormat_1032 + * @tc.desc: get format when the file is MP4(mpeg4 + aac-pce 7.1 wide) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1032, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri8.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri8.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 8); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 206158431759); +} + +/** + * @tc.name: mp4Source_GetFormat_1033 + * @tc.desc: get format when the file is MP4(mpeg4 + aac_eld 4.0) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1033, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri9.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri9.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_VID); + EXPECT_EQ(formatVal_.codecMime, "video/mp4v-es"); + EXPECT_EQ(formatVal_.bitRate, 981325); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_TRACK_TYPE, formatVal_.trackType)); + EXPECT_TRUE(format_->GetStringValue(OH_MD_KEY_CODEC_MIME, formatVal_.codecMime)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_BITRATE, formatVal_.bitRate)); + EXPECT_EQ(formatVal_.trackType, MediaType::MEDIA_TYPE_AUD); + EXPECT_EQ(formatVal_.codecMime, "audio/mp4a-latm"); + EXPECT_EQ(formatVal_.bitRate, 320851); +} + +/** + * @tc.name: mp4Source_GetFormat_1034 + * @tc.desc: get format when the file is MP4(mpeg4 + aac_eld 4.0) + * @tc.type: FUNC + */ +HWTEST_F(Mp4SourceUnitTest, mp4Source_GetFormat_1034, TestSize.Level1) +{ + printf("---- %s ------\n", g_mp4MPEG4Uri9.data()); + source_ = Mp4SourceMockFactory::CreateSourceWithURI(const_cast(g_mp4MPEG4Uri9.data())); + EXPECT_NE(source_, nullptr); + format_ = source_->GetSourceFormat(); + EXPECT_NE(format_, nullptr); + printf("[ sourceFormat ]: %s\n", format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_DURATION, formatVal_.duration)); + EXPECT_EQ(formatVal_.timeScale, 1000); + EXPECT_EQ(formatVal_.duration, 3000000); + trackIndex_ = 0; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_TRACK_START_TIME, formatVal_.startTime)); + EXPECT_TRUE(format_->GetIntValue(Media::Tag::MEDIA_TIME_SCALE, formatVal_.timeScale)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_WIDTH, formatVal_.width)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_HEIGHT, formatVal_.height)); + EXPECT_TRUE(format_->GetDoubleValue(OH_MD_KEY_FRAME_RATE, formatVal_.frameRate)); + EXPECT_EQ(formatVal_.timeScale, 15360); + EXPECT_EQ(formatVal_.width, 720); + EXPECT_EQ(formatVal_.height, 480); + EXPECT_EQ(formatVal_.frameRate, 60.000000); + trackIndex_ = 1; + format_->Destroy(); + format_ = source_->GetTrackFormat(trackIndex_); + EXPECT_NE(format_, nullptr); + printf("[ trackFormat %d]: %s\n", trackIndex_, format_->DumpInfo()); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_SAMPLE_RATE, formatVal_.sampleRate)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUD_CHANNEL_COUNT, formatVal_.channelCount)); + EXPECT_TRUE(format_->GetIntValue(OH_MD_KEY_AUDIO_SAMPLE_FORMAT, formatVal_.audioSampleFormat)); + EXPECT_TRUE(format_->GetLongValue(OH_MD_KEY_CHANNEL_LAYOUT, formatVal_.channelLayout)); + EXPECT_EQ(formatVal_.sampleRate, 44100); + EXPECT_EQ(formatVal_.channelCount, 4); + EXPECT_EQ(formatVal_.audioSampleFormat, AudioSampleFormat::SAMPLE_F32P); + EXPECT_EQ(formatVal_.channelLayout, 263); +} +} // namespace \ No newline at end of file diff --git a/test/unittest/resources/demuxer_res/aac_hdrvivid.mp4 b/test/unittest/resources/demuxer_res/aac_hdrvivid.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..fee7c48e23b274fa1a92e4d13bff0a9c44ae2696 Binary files /dev/null and b/test/unittest/resources/demuxer_res/aac_hdrvivid.mp4 differ diff --git a/test/unittest/resources/demuxer_res/abnormal_brand.mp4 b/test/unittest/resources/demuxer_res/abnormal_brand.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..23072a731faf5c1b5327da7319309b047217520a Binary files /dev/null and b/test/unittest/resources/demuxer_res/abnormal_brand.mp4 differ diff --git a/test/unittest/resources/demuxer_res/audiovivid_hdrvivid.mp4 b/test/unittest/resources/demuxer_res/audiovivid_hdrvivid.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..52d3217342ade5abcb2bcad11ec2f959134dd896 Binary files /dev/null and b/test/unittest/resources/demuxer_res/audiovivid_hdrvivid.mp4 differ diff --git a/test/unittest/resources/demuxer_res/avc_aac.mp4 b/test/unittest/resources/demuxer_res/avc_aac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..51abad3a248e865cea507a98636360aceb156cad Binary files /dev/null and b/test/unittest/resources/demuxer_res/avc_aac.mp4 differ diff --git a/test/unittest/resources/demuxer_res/avc_flac.mp4 b/test/unittest/resources/demuxer_res/avc_flac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..c2395f04cc88729d965badf7cf44a5a210ac2136 Binary files /dev/null and b/test/unittest/resources/demuxer_res/avc_flac.mp4 differ diff --git a/test/unittest/resources/demuxer_res/camera.mp4 b/test/unittest/resources/demuxer_res/camera.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..de53366de2d1ab4cc88b462828706382fb29109a Binary files /dev/null and b/test/unittest/resources/demuxer_res/camera.mp4 differ diff --git a/test/unittest/resources/demuxer_res/delay_v1s.mp4 b/test/unittest/resources/demuxer_res/delay_v1s.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..72102356fce84a01f2e16e30b80bfd98d6555c36 Binary files /dev/null and b/test/unittest/resources/demuxer_res/delay_v1s.mp4 differ diff --git a/test/unittest/resources/demuxer_res/double_sidx_at_top.mp4 b/test/unittest/resources/demuxer_res/double_sidx_at_top.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..fc6844c25fa9b000d02e23e597cf0385e4645f00 Binary files /dev/null and b/test/unittest/resources/demuxer_res/double_sidx_at_top.mp4 differ diff --git a/test/unittest/resources/demuxer_res/ftyp_jp2_in_first_2kb.mp4 b/test/unittest/resources/demuxer_res/ftyp_jp2_in_first_2kb.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..6dca639b4d8158293c3785e9fbd720ccecb9991b Binary files /dev/null and b/test/unittest/resources/demuxer_res/ftyp_jp2_in_first_2kb.mp4 differ diff --git a/test/unittest/resources/demuxer_res/h263_amr_wb.3gp b/test/unittest/resources/demuxer_res/h263_amr_wb.3gp new file mode 100644 index 0000000000000000000000000000000000000000..744ad4d903d2b9d64cc7793448f20ed4ce015504 Binary files /dev/null and b/test/unittest/resources/demuxer_res/h263_amr_wb.3gp differ diff --git a/test/unittest/resources/demuxer_res/h264_aac_ch5.1.mp4 b/test/unittest/resources/demuxer_res/h264_aac_ch5.1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..80808f10eb278a8fd3c396da0c8a016a5d729289 Binary files /dev/null and b/test/unittest/resources/demuxer_res/h264_aac_ch5.1.mp4 differ diff --git a/test/unittest/resources/demuxer_res/h264_alac_ch5.1.mov b/test/unittest/resources/demuxer_res/h264_alac_ch5.1.mov new file mode 100644 index 0000000000000000000000000000000000000000..fa29ba3a7cc6a250f7acfd1bed000f35cdcc44af Binary files /dev/null and b/test/unittest/resources/demuxer_res/h264_alac_ch5.1.mov differ diff --git a/test/unittest/resources/demuxer_res/h264_vorbis_quad.mov b/test/unittest/resources/demuxer_res/h264_vorbis_quad.mov new file mode 100644 index 0000000000000000000000000000000000000000..523f397cc634239de79ee125b8e309e21dba8a0a Binary files /dev/null and b/test/unittest/resources/demuxer_res/h264_vorbis_quad.mov differ diff --git a/test/unittest/resources/demuxer_res/h265_opus_ch7.1.mp4 b/test/unittest/resources/demuxer_res/h265_opus_ch7.1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..65767155a2626cfe55ee58765964fb8bbc435a6f Binary files /dev/null and b/test/unittest/resources/demuxer_res/h265_opus_ch7.1.mp4 differ diff --git a/test/unittest/resources/demuxer_res/h265_pcm_s16le.mov b/test/unittest/resources/demuxer_res/h265_pcm_s16le.mov new file mode 100644 index 0000000000000000000000000000000000000000..9b041429ababa7552273365b34547007e8a4c4e6 Binary files /dev/null and b/test/unittest/resources/demuxer_res/h265_pcm_s16le.mov differ diff --git a/test/unittest/resources/demuxer_res/hevc_aac.mov b/test/unittest/resources/demuxer_res/hevc_aac.mov new file mode 100644 index 0000000000000000000000000000000000000000..335eeee1561f140419e71b7a48e19bd94ede9cdd Binary files /dev/null and b/test/unittest/resources/demuxer_res/hevc_aac.mov differ diff --git a/test/unittest/resources/demuxer_res/hevc_aac.mp4 b/test/unittest/resources/demuxer_res/hevc_aac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..067dbbabef1f54a530c9c7691980626200e14f5a Binary files /dev/null and b/test/unittest/resources/demuxer_res/hevc_aac.mp4 differ diff --git a/test/unittest/resources/demuxer_res/meeting_fmp4.mp4 b/test/unittest/resources/demuxer_res/meeting_fmp4.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..1ccff9ef29c9e259a6356108f334d8c099f96d44 Binary files /dev/null and b/test/unittest/resources/demuxer_res/meeting_fmp4.mp4 differ diff --git a/test/unittest/resources/demuxer_res/moov_oversize.mp4 b/test/unittest/resources/demuxer_res/moov_oversize.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..2219372ca633c630b0e230872162c486650bc393 Binary files /dev/null and b/test/unittest/resources/demuxer_res/moov_oversize.mp4 differ diff --git a/test/unittest/resources/demuxer_res/more_sidx.mp4 b/test/unittest/resources/demuxer_res/more_sidx.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..67a0cc3c0b50f826ee2ef38a3a22730b0e829850 Binary files /dev/null and b/test/unittest/resources/demuxer_res/more_sidx.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg2_aac_test.mp4 b/test/unittest/resources/demuxer_res/mpeg2_aac_test.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..20aa0f022756e905ff275f74b04e3d6a8c96ad10 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg2_aac_test.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg2_alac.mp4 b/test/unittest/resources/demuxer_res/mpeg2_alac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..b85302a64da605d8ec1c4393baf25ada311be632 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg2_alac.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg2_dts.mp4 b/test/unittest/resources/demuxer_res/mpeg2_dts.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..a774ff6c62c9593f7c598bdb5cb5eee06f5dc850 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg2_dts.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_aac.mp4 b/test/unittest/resources/demuxer_res/mpeg4_aac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..dd5f9e736a19f71cdb220bba4697e0bbd51e0e69 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_aac.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_aac_ch4.0_eld.mp4 b/test/unittest/resources/demuxer_res/mpeg4_aac_ch4.0_eld.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..fcbb8ae93770daf2b16592c92db59d43d6f8c2a9 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_aac_ch4.0_eld.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_aac_ch7.1wide.mp4 b/test/unittest/resources/demuxer_res/mpeg4_aac_ch7.1wide.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..85b169a4dad92cd496af380452b80529177e88c0 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_aac_ch7.1wide.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_amr_nb.3gp b/test/unittest/resources/demuxer_res/mpeg4_amr_nb.3gp new file mode 100644 index 0000000000000000000000000000000000000000..4c7b2cad8ffd93a43a98b0cd3991a4cb66014b18 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_amr_nb.3gp differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_flac.mp4 b/test/unittest/resources/demuxer_res/mpeg4_flac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..15a5092d012dc794435e4d34e722a3d97989b2cb Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_flac.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_flac_ch7.1.mp4 b/test/unittest/resources/demuxer_res/mpeg4_flac_ch7.1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..b16a87eef7435a2bc078f9c6a1bcb898724957ae Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_flac_ch7.1.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_g711mu.mov b/test/unittest/resources/demuxer_res/mpeg4_g711mu.mov new file mode 100644 index 0000000000000000000000000000000000000000..339284921938c644fdeb6b0177a7c2b0a945e2c7 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_g711mu.mov differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_mp3.mp4 b/test/unittest/resources/demuxer_res/mpeg4_mp3.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..1688239ed140bb01102dbff31dffcc9a313d351c Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_mp3.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_opus.mp4 b/test/unittest/resources/demuxer_res/mpeg4_opus.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..e68a876f6a01c6e9786e4b75d70fe05e587b2e90 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_opus.mp4 differ diff --git a/test/unittest/resources/demuxer_res/mpeg4_pcm_s32be_ch7.1.mov b/test/unittest/resources/demuxer_res/mpeg4_pcm_s32be_ch7.1.mov new file mode 100644 index 0000000000000000000000000000000000000000..f2a2babc00106afe26fd0ee7d7204be07a871cf6 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg4_pcm_s32be_ch7.1.mov differ diff --git a/test/unittest/resources/demuxer_res/mpeg_ps_in_mov.mp4 b/test/unittest/resources/demuxer_res/mpeg_ps_in_mov.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..403d26940e6440ce5a4a1f6d2b25ffc4fb7babf4 Binary files /dev/null and b/test/unittest/resources/demuxer_res/mpeg_ps_in_mov.mp4 differ diff --git a/test/unittest/resources/demuxer_res/multi_sidx.mp4 b/test/unittest/resources/demuxer_res/multi_sidx.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..aaa67a9586de21ee2a3fb50051fcd2dc62cf58f5 Binary files /dev/null and b/test/unittest/resources/demuxer_res/multi_sidx.mp4 differ diff --git a/test/unittest/resources/demuxer_res/multi_trk.mov b/test/unittest/resources/demuxer_res/multi_trk.mov new file mode 100644 index 0000000000000000000000000000000000000000..b37d48b1a639e84e0889af4672431fcf3765d6ec Binary files /dev/null and b/test/unittest/resources/demuxer_res/multi_trk.mov differ diff --git a/test/unittest/resources/demuxer_res/only_free_in_first_2kb.mp4 b/test/unittest/resources/demuxer_res/only_free_in_first_2kb.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..2f8690087670c004cdc165d8689e85f7825a0d78 Binary files /dev/null and b/test/unittest/resources/demuxer_res/only_free_in_first_2kb.mp4 differ diff --git a/test/unittest/resources/demuxer_res/only_mdat_in_first_2kb.mp4 b/test/unittest/resources/demuxer_res/only_mdat_in_first_2kb.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..a68789cb638d115bacaae0e2bb980b2834914ce6 Binary files /dev/null and b/test/unittest/resources/demuxer_res/only_mdat_in_first_2kb.mp4 differ diff --git a/test/unittest/resources/demuxer_res/only_moov_in_first_2kb.mp4 b/test/unittest/resources/demuxer_res/only_moov_in_first_2kb.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..6ed5384deea806259bec4ac69c4e54923b259089 Binary files /dev/null and b/test/unittest/resources/demuxer_res/only_moov_in_first_2kb.mp4 differ diff --git a/test/unittest/resources/demuxer_res/only_skip_in_first_2kb.mp4 b/test/unittest/resources/demuxer_res/only_skip_in_first_2kb.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..da535c8ffd87e3affed5d1fed93e383ad2cf2625 Binary files /dev/null and b/test/unittest/resources/demuxer_res/only_skip_in_first_2kb.mp4 differ diff --git a/test/unittest/resources/demuxer_res/short_audio.mp4 b/test/unittest/resources/demuxer_res/short_audio.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..8c90f341a18779693f0158734e6f4743211a1b37 Binary files /dev/null and b/test/unittest/resources/demuxer_res/short_audio.mp4 differ diff --git a/test/unittest/resources/demuxer_res/short_video.mp4 b/test/unittest/resources/demuxer_res/short_video.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..5d228018a0fbd1c784f5e99470a16679b5543b0e Binary files /dev/null and b/test/unittest/resources/demuxer_res/short_video.mp4 differ diff --git a/test/unittest/resources/demuxer_res/sidx.mp4 b/test/unittest/resources/demuxer_res/sidx.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..00b63899c77c9fda4e94031abac7724ccfe2df06 Binary files /dev/null and b/test/unittest/resources/demuxer_res/sidx.mp4 differ diff --git a/test/unittest/resources/demuxer_res/test_h264.mp4 b/test/unittest/resources/demuxer_res/test_h264.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..037a1290405da7a1da800979e30ff673b980ff05 Binary files /dev/null and b/test/unittest/resources/demuxer_res/test_h264.mp4 differ diff --git a/test/unittest/resources/demuxer_res/wvtt.mp4 b/test/unittest/resources/demuxer_res/wvtt.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..cf39493fd82aecb618aa10a28f1cc43c4d7416e8 Binary files /dev/null and b/test/unittest/resources/demuxer_res/wvtt.mp4 differ diff --git a/test/unittest/resources/ohos_test.xml b/test/unittest/resources/ohos_test.xml index ce7c1c317a95f936ee96c9df3aed1c562914c1f0..1ee0c80e2127cab3585b3a4847c26d6242b7066a 100644 --- a/test/unittest/resources/ohos_test.xml +++ b/test/unittest/resources/ohos_test.xml @@ -652,6 +652,20 @@