diff --git a/engine/plugin/plugins/demuxer/minmp4_demuxer/minimp4_demuxer_plugin.cpp b/engine/plugin/plugins/demuxer/minmp4_demuxer/minimp4_demuxer_plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..92c6f972d6700c25d352eca3f3e470e6db54bd59 --- /dev/null +++ b/engine/plugin/plugins/demuxer/minmp4_demuxer/minimp4_demuxer_plugin.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "minimp4_demuxer_plugin.h" +#include +#include +#include +#include + +#include + +#include "utils/constants.h" +#include "utils/memory_helper.h" +#include "foundation/log.h" +#include "core/plugin_manager.h" +#include "plugin/common/plugin_buffer.h" + + +namespace OHOS { +namespace Media { +namespace Plugin { +std::vector sampleRateVec { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 +}; + +constexpr int8_t ADTS_HEADER_SIZE = 7; +constexpr int8_t MP4_HEADER_OFFSET = 4; +constexpr int8_t RANK_MAX = 100; +constexpr uint32_t DEFAULT_AUDIO_SAMPLE_PER_FRAME = 1024; +namespace { + int ReadCallback(int64_t offset, void *buffer, size_t size, void *token); + int Sniff(const std::string &name, std::shared_ptr dataSource); + Status RegisterPlugins(const std::shared_ptr ®); + void UpdatePluginDefinition(CodecPluginDef& definition); +} + +MiniMP4DemuxerPlugin::MiniMP4DemuxerPlugin(std::string name) + : DemuxerPlugin(std::move(name)), + ioContext_(), + callback_(nullptr), + mediaInfo_(nullptr), + fileSize_(0), + sampleIndex_(0) +{ + memset_s(&miniMP4_, sizeof(MP4D_demux_t), 0, sizeof(MP4D_demux_t)); + MEDIA_LOG_I("MiniMP4DemuxerPlugin, plugin name: %s", pluginName_.c_str()); +} + +MiniMP4DemuxerPlugin ~MiniMP4DemuxerPlugin() +{ + MEDIA_LOG_I("~MiniMP4DemuxerPlugin"); +} + +Status MiniMP4DemuxerPlugin::SetDataSource(const std::shared_ptr& source) +{ + ioContext_.dataSource = source; + source->GetSize(fileSize_); + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::Init() +{ + MEDIA_LOG_I("Init called"); + Reset(); + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::Deinit() +{ + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::Prepare() +{ + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::Reset() +{ + MP4D_close(&miniMP4_); + ioContext_.offset = 0; + ioContext_.eos = false; + mediaInfo_.reset(); + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::Start() +{ + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::Stop() +{ + return Status::OK; +} + +bool MiniMP4DemuxerPlugin::IsParameterSupported(Tag tag) +{ + (void)tag; + return false; +} + +Status MiniMP4DemuxerPlugin::GetParameter(Tag tag, ValueType& value) +{ + (void)tag; + (void)value; + return Status::ERROR_UNIMPLEMENTED; +} + +Status MiniMP4DemuxerPlugin::SetParameter(Tag tag, const ValueType& value) +{ + (void)tag; + (void)value; + return Status::ERROR_UNIMPLEMENTED; +} + + +std::shared_ptr MiniMP4DemuxerPlugin::GetAllocator() +{ + return nullptr; +} + +Status MiniMP4DemuxerPlugin::SetCallback(const std::shared_ptr& cb) +{ + callback_ = cb; + return Status::OK; +} + +size_t MiniMP4DemuxerPlugin::GetTrackCount() +{ + return 1; +} + +Status MiniMP4DemuxerPlugin::SelectTrack(int32_t trackId) +{ + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::UnselectTrack(int32_t trackId) +{ + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::GetSelectedTracks(std::vector& trackIds) +{ + trackIds.clear(); + trackIds.push_back(1); + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::GetMediaInfo(MediaInfo& mediaInfo) +{ + if (fileSize_ == 0 || ioContext_.dataSource == nullptr) { + return Status::ERROR_UNKNOWN; + } + MemoryHelper::make_unique().swap(mediaInfo_); + mediaInfo_->general.clear(); + mediaInfo_->tracks.clear(); + + if (MP4D_open(&miniMP4_, ReadCallback, reinterpret_cast(this), fileSize_) == 0) { + MEDIA_LOG_E("MP4D_open IS ERROR"); + mediaInfo_ = nullptr; + return Status::ERROR_MISMATCHED_TYPE; + } + if (AudioAdapterForDecoder() != Status::OK) { + mediaInfo_ = nullptr; + return Status::ERROR_UNKNOWN; + } + + TagMap tagPair { { Tag::AUDIO_SAMPLE_RATE, + static_cast(miniMP4_.track->SampleDescription.audio.samplerate_hz) }, + {Tag::MEDIA_BITRATE, static_cast(miniMP4_.track->avg_bitrate_bps) }, + {Tag::AUDIO_CHANNELS, static_cast(miniMP4_.track->SampleDescription.audio.channelcount) }, + {Tag::TRACK_ID, static_cast(0) }, + {Tag::MIME, std::string(MEDIA_MIME_AUDIO_AAC) }, + {Tag::AUDIO_MPEG_VERSION, static_cast(4) }, + {Tag::AUDIO_AAC_PROFILE, AudioAacProfile::LC }, + {Tag::AUDIO_AAC_STREAM_FORMAT, AudioAacStreamFormat::MP4ADTS }, + {Tag::AUDIO_SAMPLE_FORMAT, AudioSampleFormat::F32P }, + {Tag::AUDIO_SAMPLE_PER_FRAME, DEFAULT_AUDIO_SAMPLE_PER_FRAME }, + {Tag::AUDIO_CHANNEL_LAYOUT, AudioChannelLayout::STEREO } }; + mediaInfo_->tracks.push_back(tagPair); + + if (!mediaInfo_) { + return Status::ERROR_INVALID_PARAMETER; + } + mediaInfo = *mediaInfo_; + return Status::OK; +} + +void MiniMP4DemuxerPlugin::FillADTSHead(std::shared_ptr &data, uint32_t frameSize) +{ + uint8_t _adts_header[ADTS_HEADER_SIZE] = {0}; + uint32_t channel_config = miniMP4_.track->SampleDescription.audio.channelcount; + uint32_t packet_len = frameSize + 7; + uint32_t samplerate_index = 0; + /* 按格式读取信息帧 */ + uint8_t object_type_indication = miniMP4_.track->object_type_indication; + samplerate_index = ((miniMP4_.track->dsi[0] & 0x7) << 1) + (miniMP4_.track->dsi[1] >> 7); + _adts_header[0] = static_cast(0xFF); + _adts_header[1] = static_cast(0xF1); + _adts_header[2] = static_cast(object_type_indication) + (samplerate_index << 2) + (channel_config >> 2); + _adts_header[3] = static_cast(((channel_config & 0x3) << 6) + (packet_len >> 11)); + _adts_header[4] = static_cast((packet_len & 0x7FF) >> 3); + _adts_header[5] = static_cast(((packet_len & 0x7) << 5) + 0x1F); + _adts_header[6] = static_cast(0xFC); + data->Write(_adts_header, ADTS_HEADER_SIZE, 0); +} + +Status MiniMP4DemuxerPlugin::ReadFrame(Buffer& info, int32_t timeOutMs) +{ + if (!mediaInfo_) { + return Status::ERROR_INVALID_PARAMETER; + } + + if (sampleIndex_ >= miniMP4_.track->sample_count) { + return Status::END_OF_STREAM; + } + uint32_t frameSize = 0; + uint32_t timeStamp = 0; + uint32_t duration = 0; + uint64_t offset = MP4D_frame_offset(&miniMP4_, 0, sampleIndex_, &frameSize, &timeStamp, &duration); + if (offset > fileSize_){ + return Status::ERROR_UNKNOWN; + } + sampleIndex_++; + auto buffer = std::make_shared(); + auto bufData = buffer->AllocMemory(nullptr, frameSize); + if (bufData == nullptr) { + return Status::ERROR_UNKNOWN; + } + auto result = ioContext_.dataSource->ReadAt(offset, buffer, static_cast(frameSize)); + if (result != Status::OK) { + return Status::ERROR_UNKNOWN; + } + auto data = info.AllocMemory(nullptr, frameSize + ADTS_HEADER_SIZE); + auto writePtr = bufData->GetWritableData(frameSize); + if (data != nullptr && writePtr != nullptr) { + FillADTSHead(data, frameSize); + size_t writeSize = data->Write(writePtr, frameSize, ADTS_HEADER_SIZE); + ASSERT_CONDITION(writeSize == frameSize, "copy data failed"); + } + return Status::OK; +} + +Status MiniMP4DemuxerPlugin::SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) +{ + uint32_t frameSize = 0; + uint32_t timeStamp = 0; + uint32_t duration = 0; + uint64_t offsetStart = MP4D_frame_offset(&miniMP4_, 0, 0, &frameSize, &timeStamp, &duration); + uint64_t offsetEnd = + MP4D_frame_offset(&miniMP4_, 0, miniMP4_.track->sample_count - 1, &frameSize, &timeStamp, &duration); + uint64_t targetPos = (timeStampUs * static_cast(miniMP4_.track->avg_bitrate_bps)) / 8 + offsetStart; + if (targetPos >= offsetEnd) { + sampleIndex_ = miniMP4_.track->sample_count; + return Status::OK; + } + sampleIndex_ = 0; + uint64_t tempPos = 0; + while (sampleIndex_ < miniMP4_.track->sample_count) { + tempPos = MP4D_frame_offset(&miniMP4_, 0, sampleIndex_, &frameSize, &timeStamp, &duration); + if (tempPos <= targetPos) { + sampleIndex_++; + } else { + break; + } + } + return Status::OK; +} + +size_t MiniMP4DemuxerPlugin::GetFileSize() +{ + return fileSize_; +} + +std::shared_ptr MiniMP4DemuxerPlugin::GetInputBuffer() +{ + return ioContext_.dataSource; +} + +Status MiniMP4DemuxerPlugin::AudioAdapterForDecoder() +{ + if (miniMP4_.track == nullptr) { + return Status::ERROR_UNKNOWN; + } + /* 适配解码协议 */ + size_t sampleRateIndex = (static_cast(miniMP4_.track->dsi[0] & 0x7) << 1) + + (static_cast(miniMP4_.track->dsi[1]) >> 7); + + if ((sampleRateVec.size() <= sampleRateIndex) || (miniMP4_.track->dsi_bytes >= 20)) { + return Status::ERROR_MISMATCHED_TYPE; + } + miniMP4_.track->SampleDescription.audio.samplerate_hz = sampleRateVec[sampleRateIndex]; + miniMP4_.track->SampleDescription.audio.channelcount = (miniMP4_.track->dsi[1] & 0x7F) >> 3; + return Status::OK; +} +namespace { +int ReadCallback(int64_t offset, void *buffer, size_t size, void *token) +{ + MiniMP4DemuxerPlugin *mp4Demuxer = (MiniMP4DemuxerPlugin *)token; + uint32_t file_size = mp4Demuxer->GetFileSize(); + if (offset >= file_size) { + MEDIA_LOG_E("ReadCallback offset is bigger"); + return -1; + } + std::shared_ptr inputBuffer = mp4Demuxer->GetInputBuffer(); + auto bufferObj = std::make_shared(); + auto bufData = bufferObj->AllocMemory(nullptr, size)->GetReadOnlyData(); + if (inputBuffer->ReadAt(offset, bufferObj, size) != Status::OK) { + return -1; + } + memcpy_s(buffer, size, bufData, size); + return 0; +} + +int Sniff(const std::string &name, std::shared_ptr dataSource) +{ + unsigned char mp4Check[] = {'f', 't', 'y', 'p'}; + auto buffer = std::make_shared(); + auto bufData = buffer->AllocMemory(nullptr, sizeof(mp4Check)); + if (dataSource->ReadAt(MP4_HEADER_OFFSET, buffer, static_cast(sizeof(mp4Check))) != Status::OK) { + return 0; + } + if (memcmp_s(bufData->GetReadOnlyData(), sizeof(mp4Check), &mp4Check, sizeof(mp4Check)) != 0) { + MEDIA_LOG_E("memcmp mp4Check is error"); + return 0; + } + return RANK_MAX; +} + +Status RegisterPlugins(const std::shared_ptr ®) +{ + MEDIA_LOG_D("RegisterPlugins called"); + if (!reg) { + MEDIA_LOG_E("RegisterPlugins fail due to null pointer for reg"); + return Status::ERROR_INVALID_PARAMETER; + } + std::string pluginName = "MiniMP4DemuxerPlugin"; + DemuxerPluginDef regInfo; + regInfo.name = pluginName; + regInfo.description = "adapter for minimp4 demuxer plugin"; + regInfo.rank = RANK_MAX; + regInfo.creator = [](const std::string &name) -> std::shared_ptr { + return std::make_shared(name); + }; + regInfo.sniffer = Sniff; + auto rtv = reg->AddPlugin(regInfo); + if (rtv != Status::OK) { + MEDIA_LOG_E("RegisterPlugin AddPlugin failed with return %d", static_cast(rtv)); + } + return Status::OK; +} +} +PLUGIN_DEFINITION(MiniMP4Demuxer, LicenseType::CC0, RegisterPlugins, [] {}); + +} // namespace Plugin +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/plugin/plugins/demuxer/minmp4_demuxer/minimp4_demuxer_plugin.h b/engine/plugin/plugins/demuxer/minmp4_demuxer/minimp4_demuxer_plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..2ca17831f3ba71bad67acc53bdcf88f31ffac499 --- /dev/null +++ b/engine/plugin/plugins/demuxer/minmp4_demuxer/minimp4_demuxer_plugin.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MINIMP4_DEMUXER_PLUGIN_H +#define MINIMP4_DEMUXER_PLUGIN_H + +#include +#include +#include +#include + +#include "utils/type_define.h" +#include "core/plugin_register.h" +#include "interface/demuxer_plugin.h" +#include "thread/mutex.h" +#include "minimp4.h" + + +namespace OHOS { +namespace Media { +namespace Plugin { + class MiniMP4DemuxerPlugin : public DemuxerPlugin { + public: + explicit MiniMP4DemuxerPlugin(std::string name); + ~MiniMP4DemuxerPlugin() override; + Status Init() override; + Status Deinit() override; + Status Prepare() override; + Status Reset() override; + Status Start() override; + Status Stop() override; + bool IsParameterSupported(Tag tag) override; + Status GetParameter(Tag tag, ValueType& value) override; + Status SetParameter(Tag tag, const ValueType& value) override; + std::shared_ptr GetAllocator() override; + Status SetCallback(const std::shared_ptr& cb) override; + + Status SetDataSource(const std::shared_ptr& source) override; + Status GetMediaInfo(MediaInfo& mediaInfo) override; + Status ReadFrame(Buffer& info, int32_t timeOutMs) override; + Status SeekTo(int32_t trackId, int64_t timeStampUs, SeekMode mode) override; + + size_t GetTrackCount() override; + Status SelectTrack(int32_t trackId) override; + Status UnselectTrack(int32_t trackId) override; + Status GetSelectedTracks(std::vector& trackIds) override; + size_t GetFileSize(); + std::shared_ptr GetInputBuffer(); + Status AudioAdapterForDecoder(); + private: + void FillADTSHead(std::shared_ptr &data, uint32_t frameSize); + struct IOContext { + std::shared_ptr dataSource {nullptr}; + int64_t offset {0}; + bool eos {false}; + }; + IOContext ioContext_; + std::shared_ptr callback_ {nullptr}; + MP4D_demux_t miniMP4_; + size_t fileSize_; + uint32_t sampleIndex_; + std::unique_ptr mediaInfo_; + }; +} // namespace Plugin +} // namespace Media +} // namespace OHOS +#endif + diff --git a/engine/plugin/plugins/fdkaac_adapter/fdkaac_decoder_plugin.cpp b/engine/plugin/plugins/fdkaac_adapter/fdkaac_decoder_plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a7d0ad8b385fba4bd82c83d01b0b2e1f6ffbe0b --- /dev/null +++ b/engine/plugin/plugins/fdkaac_adapter/fdkaac_decoder_plugin.cpp @@ -0,0 +1,377 @@ +#define LOG_TAG "FDKAACDecoderPlugin" + +#include "fdkaac_decoder_plugin.h" +#include +#include +#include +#include "utils/constants.h" +#include "utils/memory_helper.h" +#include "foundation/log.h" + +#include "plugin/common/plugin_audio_tags.h" +#include "plugin/common/plugin_buffer.h" +#include "plugin/interface/codec_plugin.h" +#include "securec.h" + +namespace { + using namespace OHOS::Media::Plugin; + void UpdatePluginDefinition(CodecPluginDef& definition); + constexpr int8_t RANK_MAX = 100; + constexpr uint32_t BUFFER_ITEM_CNT = 6; + + std::shared_ptr FDKAACDecoderCreator(const std::string& name) + { + return std::make_shared(name); + } + + Status RegisterDecoderPlugins(const std::shared_ptr& reg) + { + MEDIA_LOG_D("RegisterPlugins called."); + if (!reg) { + MEDIA_LOG_E("RegisterPlugins failed due to null pointer for reg."); + return Status::ERROR_INVALID_PARAMETER; + } + + CodecPluginDef definition; + definition.name = "FDKAACDecoderPlugin"; + definition.codecType = CodecType::AUDIO_DECODER; + definition.rank = RANK_MAX; + definition.creator = FDKAACDecoderCreator, UpdatePluginDefinition(definition); + if (reg->AddPlugin(definition) != Status::OK) { + MEDIA_LOG_W("register minimp4 aac decoder plugin failed"); + } + return Status::OK; + } + + void UpdatePluginDefinition(CodecPluginDef& definition) + { + Capability cap("audio/unknown"); + cap.SetMime(OHOS::Media::MEDIA_MIME_AUDIO_AAC); + + MEDIA_LOG_W("UpdatePluginDefinition\n"); + DiscreteCapability values {96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350}; + cap.AppendDiscreteKeys(Capability::Key::AUDIO_SAMPLE_RATE, values); + + + DiscreteCapability channelLayoutValues {AudioChannelLayout::MONO, AudioChannelLayout::STEREO}; + cap.AppendDiscreteKeys(Capability::Key::AUDIO_CHANNEL_LAYOUT, channelLayoutValues); + + DiscreteCapability sampleFmtValues {AudioSampleFormat::S16, AudioSampleFormat::F32P, AudioSampleFormat::S16P}; + cap.AppendDiscreteKeys(Capability::Key::AUDIO_SAMPLE_FORMAT, sampleFmtValues); + + DiscreteCapability aacProfileValues {AudioAacProfile::LC}; + cap.AppendDiscreteKeys(Capability::Key::AUDIO_AAC_PROFILE, aacProfileValues); + + DiscreteCapability aacStreamFmtValues {AudioAacStreamFormat::MP4ADTS}; + cap.AppendDiscreteKeys(Capability::Key::AUDIO_AAC_STREAM_FORMAT, aacStreamFmtValues); + + definition.inCaps.push_back(cap); + definition.outCaps.emplace_back(Capability(OHOS::Media::MEDIA_MIME_AUDIO_RAW)); + } + + void UnRegisterAudioDecoderPlugin() + { + return; + } +} + +PLUGIN_DEFINITION(FDKAACDecoderPlugin, LicenseType::LGPL, RegisterDecoderPlugins, [] {}); + +namespace OHOS { +namespace Media { +namespace Plugin { + +constexpr uint32_t AAC_DECODER_SAMPLE_SIZE = sizeof(signed short); +constexpr uint32_t PCM_SIZE = 2048 * AAC_DECODER_SAMPLE_SIZE; +constexpr uint32_t FRAME_MAX_COUNT = 16 * 1024; + +FDKAACDecoderPlugin::FDKAACDecoderPlugin(std::string name) + : CodecPlugin(std::move(name)), + fdkAAC_{nullptr}, + audioParameter_{ { Tag::REQUIRED_OUT_BUFFER_CNT, (uint32_t)BUFFER_ITEM_CNT } }, + inBuffer_ {nullptr}, + outBuffer_ {nullptr} +{ + MEDIA_LOG_I("FDKAACDecoderPlugin, plugin name: %s", pluginName_.c_str()); +} + +FDKAACDecoderPlugin::~FDKAACDecoderPlugin() +{ + MEDIA_LOG_I("~FDKAACDecoderPlugin, plugin name: %s", pluginName_.c_str()); +} + +Status FDKAACDecoderPlugin::Init() +{ + return Status::OK; +} + +Status FDKAACDecoderPlugin::Deinit() +{ + Reset(); + return Status::OK; +} + +Status FDKAACDecoderPlugin::SetParameter(Tag tag, const ValueType& value) +{ + OSAL::ScopedLock lock(parameterMutex_); + audioParameter_.insert(std::make_pair(tag, value)); + return Status::OK; +} + +Status FDKAACDecoderPlugin::GetParameter(Tag tag, ValueType& value) +{ + OSAL::ScopedLock lock(parameterMutex_); + auto item = audioParameter_.find(tag); + if (item != audioParameter_.end()) { + value = item->second; + return Status::OK; + } + return Status::ERROR_INVALID_PARAMETER; +} + +Status FDKAACDecoderPlugin::Prepare() +{ + Status ret; + { + OSAL::ScopedLock lock(avMutex_); + ret = AudioDecoderAACMp4Open(); + } + return ret; +} + +Status FDKAACDecoderPlugin::Reset() +{ + { + OSAL::ScopedLock lock(avMutex_); + OSAL::ScopedLock lock1(parameterMutex_); + AudioDecoderAACMp4Close(); + audioParameter_.clear(); + if(outBuffer_){ + outBuffer_.reset(); + outBuffer_ = nullptr; + } + } + return Status::OK; +} + +Status FDKAACDecoderPlugin::Start() +{ + { + OSAL::ScopedLock lock(avMutex_); + if(fdkAAC_ == nullptr){ + return Status::ERROR_WRONG_STATE; + } + } + return Status::OK; +} + +Status FDKAACDecoderPlugin::Stop() +{ + OSAL::ScopedLock lock(avMutex_); + AudioDecoderAACMp4Close(); + if(outBuffer_){ + outBuffer_.reset(); + outBuffer_ = nullptr; + } + return Status::OK; +} + +bool FDKAACDecoderPlugin::IsParameterSupported(Tag tag) +{ + if(tag == Tag::REQUIRED_OUT_BUFFER_CNT){ + return true; + } + return false; +} + +std::shared_ptr FDKAACDecoderPlugin::GetAllocator() +{ + return nullptr; +} + +Status FDKAACDecoderPlugin::SetCallback(const std::shared_ptr &cb) +{ + return Status::OK; +} + +Status FDKAACDecoderPlugin::SetDataCallback(const std::weak_ptr &dataCallback) +{ + return Status::OK; +} + +Status FDKAACDecoderPlugin::QueueInputBuffer(const std::shared_ptr &inputBuffer, int32_t timeoutMs) +{ + MEDIA_LOG_D("queue input buffer"); + if(inBuffer_ != nullptr){ + return Status::ERROR_TIMED_OUT; + } + (void)timeoutMs; + if(inputBuffer->IsEmpty() && !(inputBuffer->flag & BUFFER_FLAG_EOS)){ + MEDIA_LOG_E("decoder does not support fd buffer"); + return Status::ERROR_INVALID_DATA; + } + inBuffer_ = inputBuffer; + return Status::OK; +} + +Status FDKAACDecoderPlugin::DequeueInputBuffer(std::shared_ptr& inputBuffer, int32_t timeoutMs) +{ + MEDIA_LOG_D("DequeueInputBuffer"); + (void)timeoutMs; + return Status::OK; +} + +Status FDKAACDecoderPlugin::QueueOutputBuffer(const std::shared_ptr& outputBuffers, int32_t timeoutMs) +{ + MEDIA_LOG_I("queue out put"); + (void)timeoutMs; + if(outBuffer_ != nullptr){ + return Status::ERROR_TIMED_OUT; + } + if(!outputBuffers){ + return Status::ERROR_INVALID_PARAMETER; + } + outBuffer_ = outputBuffers; + return Status::OK; +} + +Status FDKAACDecoderPlugin::DequeueOutputBuffer(std::shared_ptr& outputBuffers, int32_t timeoutMs) +{ + MEDIA_LOG_D("DequeueOutputBuffer"); + (void)timeoutMs; + Status status; + status = ReceiveBuffer(); + inBuffer_.reset(); + inBuffer_ = nullptr; + if(status == Status::OK || status == Status::END_OF_STREAM){ + outputBuffers = outBuffer_; + } + outBuffer_.reset(); + outBuffer_ = nullptr; + return status; +} + +Status FDKAACDecoderPlugin::ReceiveBufferLocked(const std::shared_ptr &ioInfo) +{ + Status status; + if(inBuffer_->IsEmpty() && (inBuffer_->flag & BUFFER_FLAG_EOS) != 0){ + ioInfo->GetMemory()->Reset(); + ioInfo->flag = BUFFER_FLAG_EOS; + status = Status::END_OF_STREAM; + }else{ + status = AudioDecoderAACMp4Process(inBuffer_, ioInfo); + } + return status; +} + +Status FDKAACDecoderPlugin::ReceiveBuffer() +{ + if(inBuffer_ == nullptr){ + return Status::ERROR_NOT_ENOUGH_DATA; + } + std::shared_ptr ioInfo = outBuffer_; + if((ioInfo == nullptr) || ioInfo->IsEmpty() || (ioInfo->GetBufferMeta()->GetType() != BufferMetaType::AUDIO)){ + MEDIA_LOG_W("no buffer"); + return Status::ERROR_NO_MEMORY; + } + Status status; + { + OSAL::ScopedLock lock(avMutex_); + status = ReceiveBufferLocked(ioInfo); + } + return status; +} + +Status FDKAACDecoderPlugin::Flush() +{ + OSAL::ScopedLock lock(avMutex_); + outBuffer_.reset(); + outBuffer_ = nullptr; + return Status::OK; +} + +Status FDKAACDecoderPlugin::AudioDecoderAACMp4Open() +{ + fdkAAC_ = aacDecoder_Open(TT_MP4_ADTS,1); + return fdkAAC_ == nullptr ? Status::ERROR_UNKNOWN : Status::OK; +} + +void FDKAACDecoderPlugin::AudioDecoderAACMp4Close() +{ + if(fdkAAC_ != nullptr) { + aacDecoder_Close(fdkAAC_); + } +} + +Status FDKAACDecoderPlugin::AudioDecoderAACMp4Process(std::shared_ptr inBuffer, std::shared_ptr outBuffer) +{ + if(inBuffer == nullptr || outBuffer == nullptr){ + return Status::ERROR_UNKNOWN; + } + auto inData = inBuffer->GetMemory(); + auto outData = outBuffer->GetMemory(); + const uint8_t *FramePtr = inData->GetReadOnlyData(); + size_t inputLength = inData->GetSize(); + uint32_t remain_size = inputLength; + AAC_DECODER_ERROR err; + + err = aacDecoder_Fill(fdkAAC_, const_cast(&FramePtr), &inputLength, &remain_size); + if (AAC_DEC_OK != err || remain_size != 0){ + MEDIA_LOG_E("aacDecoder_Fill error is %d remain =%d",err,remain_size); + return Status::ERROR_UNKNOWN; + } + + signed short *cachedFrame = static_cast(malloc(PCM_SIZE)); + if(cachedFrame == nullptr){ + return Status::ERROR_NO_MEMORY; + } + (void)memset_s(cachedFrame, sizeof(PCM_SIZE), 0, sizeof(PCM_SIZE)); + + err = aacDecoder_DecodeFrame(fdkAAC_, cachedFrame, PCM_SIZE / 2, 0); + if (AAC_DEC_OK != err){ + MEDIA_LOG_E("aacDecoder_DecodeFrame error is %d",err); + free(cachedFrame); + return Status::ERROR_UNKNOWN; + } + + CStreamInfo *streamInfo { nullptr }; + streamInfo = aacDecoder_GetStreamInfo(fdkAAC_); + if (streamInfo == nullptr){ + MEDIA_LOG_E("aacDecoder_GetStreamInfo failed"); + free(cachedFrame); + return Status::ERROR_UNKNOWN; + } + + if (streamInfo->frameSize == 0 || streamInfo->frameSize >= FRAME_MAX_COUNT){ + MEDIA_LOG_E("streamInfo is invalid"); + free(cachedFrame); + return Status::ERROR_INVALID_PARAMETER; + } + + uint32_t packet_length = static_cast(streamInfo->frameSize) * static_cast(streamInfo->numChannels) * AAC_DECODER_SAMPLE_SIZE; + uint8_t *tempPtr = static_cast(malloc(packet_length)); + if(tempPtr == nullptr){ + free(cachedFrame); + return Status::ERROR_UNKNOWN; + } + (void)memset_s(tempPtr, packet_length, 0, packet_length); + memcpy_s(tempPtr, packet_length, cachedFrame, packet_length); + if ((outData->GetCapacity() >= packet_length) && (packet_length <= PCM_SIZE)) { + outData->Write(tempPtr, packet_length); + MEDIA_LOG_I("packet_length = %d", packet_length); + free(cachedFrame); + free(tempPtr); + return Status::OK; + } else { + MEDIA_LOG_E("not enough buffer memory"); + free(cachedFrame); + free(tempPtr); + return Status::ERROR_NO_MEMORY; + } +} + + +} // namespace Plugin +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/engine/plugin/plugins/fdkaac_adapter/fdkaac_decoder_plugin.h b/engine/plugin/plugins/fdkaac_adapter/fdkaac_decoder_plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..b8899ed2e259e4f1b2f73dba51df8129617fa9b2 --- /dev/null +++ b/engine/plugin/plugins/fdkaac_adapter/fdkaac_decoder_plugin.h @@ -0,0 +1,81 @@ +#ifndef HISTREAMER_AUDIO_FDK_AAC_DECODER_PLUGIN_H +#define HISTREAMER_AUDIO_FDK_AAC_DECODER_PLUGIN_H + +#include +#include +#include "plugin/interface/codec_plugin.h" +#include "plugin/common/plugin_types.h" + +#include "foundation/osal/thread/scoped_lock.h" + +#include "aacdecoder_lib.h" + +using FDKAAC_DECODER_T = HANDLE_AACDECODER; + +namespace OHOS { +namespace Media { +namespace Plugin { +class FDKAACDecoderPlugin : public CodecPlugin { +public: + explicit FDKAACDecoderPlugin(std::string name); + + ~FDKAACDecoderPlugin() override; + + Status Init() override; + + Status Deinit() override; + + Status Prepare() override; + + Status Reset() override; + + Status Start() override; + + Status Stop() override; + + bool IsParameterSupported(Tag tag) override; + + Status GetParameter(Tag tag, ValueType& value) override; + + Status SetParameter(Tag tag, const ValueType& value) override; + + std::shared_ptr GetAllocator() override; + + Status SetCallback(const std::shared_ptr& cb) override; + + Status QueueInputBuffer(const std::shared_ptr& inputBuffer, int32_t timeoutMs) override; + + Status DequeueInputBuffer(std::shared_ptr& inputBuffer, int32_t timeoutMs) override; + + Status QueueOutputBuffer(const std::shared_ptr& outputBuffers, int32_t timeoutMs) override; + + Status DequeueOutputBuffer(std::shared_ptr& outputBuffers, int32_t timeoutMs) override; + + Status Flush() override; + + Status SetDataCallback(const std::weak_ptr& dataCallback) override; + +private: + Status ReceiveBuffer(); + + Status ReceiveBufferLocked(const std::shared_ptr &ioInfo); + + Status AudioDecoderAACMp4Open(); + + void AudioDecoderAACMp4Close(); + + Status AudioDecoderAACMp4Process(std::shared_ptr inBuffer, std::shared_ptr outBuffer); + + FDKAAC_DECODER_T fdkAAC_; + std::map audioParameter_ {}; + mutable OSAL::Mutex parameterMutex_ {}; + mutable OSAL::Mutex avMutex_ {}; + std::shared_ptr inBuffer_; + std::shared_ptr outBuffer_; +}; +} // namespace Plugin +} // namespace Media +} // namespace OHOS + +#endif // HISTREAMER_MINIMP4_AAC_DECODER_PLUGIN_H + diff --git a/engine/plugin/plugins/source/http_source/buffer.cpp b/engine/plugin/plugins/source/http_source/buffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6ec855292ffb350af2ff52f95f3a912709553c8 --- /dev/null +++ b/engine/plugin/plugins/source/http_source/buffer.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "buffer.h" +#include "foundation/log.h" +#include +#include + +namespace OHOS { +namespace Media { +namespace Plugin { +constexpr int BUFFER_MIN_SIZE = 150 * 1024; +Buffer::Buffer() noexcept {} +Buffer::~Buffer() {} +unsigned char *Buffer::RingbufferOpen(int &size) +{ + addr_ = MallocWithReduce(size, 80); + if (addr_ == nullptr) { + capability_ = 0; + return nullptr; + } + capability_ = size; + memset_s(addr_, size, 0, size); + return (InitCQueue(cQueue_, size, addr_) == true) ? addr_ : nullptr; +} + +void Buffer::RingbufferClose() +{ + if (addr_ != nullptr) { + free(addr_); + addr_ = nullptr; + capability_ = 0; + } +} + +bool Buffer::RingbufferWrite(unsigned char *src, unsigned int dataSize) +{ + if (addr_ == nullptr) { + return false; + } + { + OSAL::ScopedLock lock(bufferMutex_); + int ret = EnCQueue(cQueue_, src, dataSize); + return (ret == 0); + } + +} + +bool Buffer::RingbufferRead(unsigned char *dst, unsigned int wantReadSize, unsigned int &realReadSize) +{ + if (dst == nullptr) { + return false; + } + { + OSAL::ScopedLock lock(bufferMutex_); + unsigned char *buf1 = nullptr; + unsigned char *buf2 = nullptr; + unsigned int len1 = 0; + unsigned int len2 = 0; + PeekCQueue(cQueue_, wantReadSize, &buf1, len1, &buf2, len2); + if (wantReadSize == (len1 + len2)) { + memcpy(dst, buf1, len1); + memcpy(dst + len1, buf2, len2); + DeCQueue(cQueue_, nullptr, wantReadSize); + realReadSize = wantReadSize; + return true; + } + memset_s(dst, wantReadSize, 0x00, wantReadSize); + realReadSize = 0; + } + return false; +} + +bool Buffer::RingbufferPeek(unsigned char *dst, unsigned int wantReadSize, unsigned int &realReadSize) +{ + if (dst == nullptr) { + return false; + } + { + OSAL::ScopedLock lock(bufferMutex_); + unsigned char *buf1 = nullptr; + unsigned char *buf2 = nullptr; + unsigned int len1 = 0; + unsigned int len2 = 0; + PeekCQueue(cQueue_, wantReadSize, &buf1, len1, &buf2, len2); + if (wantReadSize == (len1 + len2)) { + memcpy(dst, buf1, len1); + memcpy(dst + len1, buf2, len2); + realReadSize = wantReadSize; + return true; + } + memset_s(dst, wantReadSize, 0x00, wantReadSize); + realReadSize = 0; + } + return false; +} + +bool Buffer::RingbufferDrop(unsigned int dataSize) +{ + { + OSAL::ScopedLock lock(bufferMutex_); + unsigned char *buf1 = nullptr; + unsigned char *buf2 = nullptr; + unsigned int len1 = 0; + unsigned int len2 = 0; + PeekCQueue(cQueue_, dataSize, &buf1, len1, &buf2, len2); + if (dataSize == len1 + len2) { + DeCQueue(cQueue_, nullptr, dataSize); + return true; + } + return false; + } +} + +unsigned int Buffer::GetAvailableWriteSize() +{ + { + OSAL::ScopedLock lock(bufferMutex_); + int ret = LengthOfCQueue(cQueue_); + return ret; + } +} + +void Buffer::RingbufferReset() +{ + { + OSAL::ScopedLock lock(bufferMutex_); + ResetCQueue(cQueue_); + if (addr_ != nullptr) { + memset_s(addr_, capability_, 0, capability_); + } + } +} + +unsigned int Buffer::GetAvailableReadSize() +{ + { + OSAL::ScopedLock lock(bufferMutex_); + int ret = AvailableOfCQueue(cQueue_); + return ret; + } +} + +unsigned char *Buffer::MallocWithReduce(int &size, int rate) +{ + if (size <= 0) { + MEDIA_LOG_E("alloc %d failed", size); + return nullptr; + } + int mallocSize = size; + unsigned char *ptr = (unsigned char *)malloc(mallocSize); + if (ptr != nullptr) { + MEDIA_LOG_I("malloc %d success", mallocSize); + return ptr; + } + + while (mallocSize > BUFFER_MIN_SIZE) { + int nextMallocSize = mallocSize * rate / 100; + mallocSize = std::min(nextMallocSize, BUFFER_MIN_SIZE); + ptr = (unsigned char *)malloc(mallocSize); + if (ptr != nullptr) { + MEDIA_LOG_I("malloc %d success", mallocSize); + size = mallocSize; + return ptr; + } + } + return nullptr; +} + +bool Buffer::InitCQueue(CQueue &queue, unsigned int size, unsigned char *buf) +{ + if (buf == nullptr) { + return false; + } + queue.capability = size; + queue.base = buf; + queue.len = 0; + queue.read = 0; + queue.write = 0; + return true; +} + +bool Buffer::IsEmptyCQueue(CQueue &queue) +{ + return (queue.len == 0); +} + +int Buffer::LengthOfCQueue(CQueue &queue) +{ + return queue.len; +} + +int Buffer::AvailableOfCQueue(CQueue &queue) +{ + return (queue.capability - queue.len); +} + +int Buffer::EnCQueue(CQueue &queue, unsigned char *buf, unsigned int len) +{ + if (AvailableOfCQueue(queue) < len) { + return -1; + } + queue.len += len; + int distanceToEnd = queue.capability - queue.write; + if (distanceToEnd > len) { + if (buf != nullptr) { + memcpy((uint8_t *)&queue.base[queue.write], (uint8_t *)buf, len); + } + queue.write += len; + } else { + if (buf != nullptr) { + memcpy((uint8_t *)&queue.base[queue.write], (uint8_t *)buf, distanceToEnd); + memcpy((uint8_t *)&queue.base[0], (((uint8_t *)buf) + distanceToEnd), len - distanceToEnd); + } + queue.write = len - distanceToEnd; + } + return 0; +} + +int Buffer::DeCQueue(CQueue &queue, unsigned char *buf, unsigned int len) +{ + if (LengthOfCQueue(queue) < len) { + return -1; + } + queue.len -= len; + + if (buf != nullptr) { + int distanceToEnd = queue.capability - queue.read; + if (distanceToEnd > len) { + if (buf != nullptr) { + memcpy((uint8_t *)buf, (uint8_t *)&queue.base[queue.read], len); + queue.read += len; + } else { + if (buf != nullptr) { + memcpy((uint8_t *)buf, (uint8_t *)&queue.base[queue.read], distanceToEnd); + memcpy((((uint8_t *)buf) + distanceToEnd), (uint8_t *)&queue.base[0], len - distanceToEnd); + queue.read = len - distanceToEnd; + } + } + } + } else { + queue.read = (queue.capability > 0) ? (queue.read + len) % queue.capability : 0; + } + return 0; +} + +int Buffer::PeekCQueue(CQueue &queue, unsigned int wantLength, unsigned char **buf1, unsigned int &len1, unsigned char **buf2, unsigned int &len2) +{ + if (LengthOfCQueue(queue) < wantLength) { + return -1; + } + *buf1 = &(queue.base[queue.read]); + if ((queue.write > queue.read) || (queue.capability - queue.read >= wantLength)) { + len1 = wantLength; + *buf2 = nullptr; + len2 = 0; + return 0; + } else { + len1 = queue.capability - queue.read; + buf2 = &(queue.base[0]); + len2 = wantLength - len1; + return 0; + } +} + +void Buffer::ResetCQueue(CQueue &queue) +{ + queue.len = 0; + queue.capability = 0; + queue.read = 0; + queue.write = 0; + queue.base = nullptr; +} + +} +} +} \ No newline at end of file diff --git a/engine/plugin/plugins/source/http_source/buffer.h b/engine/plugin/plugins/source/http_source/buffer.h new file mode 100644 index 0000000000000000000000000000000000000000..dc826c3ea04b609a3c4133b487c416ee20df3be4 --- /dev/null +++ b/engine/plugin/plugins/source/http_source/buffer.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HISTREAMER_BUFFER_H +#define HISTREAMER_BUFFER_H +#include "foundation/osal/thread/scoped_lock.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +struct CQueue { + int read; + int write; + int capability; + int len; + unsigned char *base; +}; + +class Buffer { +public: + Buffer() noexcept; + virtual ~Buffer(); + virtual unsigned char *RingbufferOpen(int &size); + virtual void RingbufferClose(); + virtual bool RingbufferWrite(unsigned char *src, unsigned int dataSize); + virtual bool RingbufferRead(unsigned char *dst, unsigned int wantReadSize, unsigned int &realReadSize); + virtual bool RingbufferPeek(unsigned char *dst, unsigned int wantReadSize, unsigned int &realReadSize); + virtual void RingbufferReset(); + virtual unsigned int GetAvailableReadSize(); + bool RingbufferDrop(unsigned int dataSize); + unsigned int GetAvailableWriteSize(); + +private: + unsigned char *MallocWithReduce(int &size, int rate); + bool InitCQueue(CQueue &queue, unsigned int size, unsigned char *buf); + bool IsEmptyCQueue(CQueue &queue); + int LengthOfCQueue(CQueue &queue); + int AvailableOfCQueue(CQueue &queue); + int EnCQueue(CQueue &queue, unsigned char *buf, unsigned int len); + int DeCQueue(CQueue &queue, unsigned char *buf, unsigned int len); + int PeekCQueue(CQueue &queue, unsigned int lenWant, unsigned char **buf1, unsigned int &len1, unsigned char **buf2, unsigned int &len2); + void ResetCQueue(CQueue &queue); + int capability_ {0}; + unsigned char *addr_ {nullptr}; + mutable OSAL::Mutex bufferMutex_ {}; + CQueue cQueue_ {0, 0, 0, 0, nullptr}; +}; +} +} +} + +#endif \ No newline at end of file diff --git a/engine/plugin/plugins/source/http_source/http_manager.cpp b/engine/plugin/plugins/source/http_source/http_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdf0f0799a812db1eba7a13987a2ea5bef3abe7d --- /dev/null +++ b/engine/plugin/plugins/source/http_source/http_manager.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "http_manager.h" +#include +#include +#include "foundation/log.h" +#include "osal/utils/util.h" + +namespace OHOS { +namespace Media { +namespace Plugin { +constexpr int SEGMENT_BUFFER_SIZE = 48 * 1024; +constexpr int ERROR_CODE_COULDNT_RESOLVE_HOST = 6; +constexpr int ERROR_MAX_RETRY_TIMES = 2; + +void ReceiveData(unsigned char *data, int len, void *priv) +{ + if (priv == nullptr || data == nullptr || len < 0) { + return; + } + auto httpManager = reinterpret_cast(priv); + if (httpManager->bufferHandle_ == nullptr) { + return; + } + bool ret = false; + do { + if (httpManager->GetHttpStatus() != HTTP_STATUS_PLAY) { + return; + } + if (httpManager->retryTimes_ > 0 && len >= SEGMENT_BUFFER_SIZE) { + httpManager->retryTimes_ = 0; + } + ret = httpManager->bufferHandle_->RingbufferWrite(data, len); + if (!ret) { + OHOS::Media::OSAL::SleepFor(100); + } + } while (!ret); + return; +} + +void OnFinished(void *priv) +{ + if (priv == nullptr) { + return; + } + auto httpManager = reinterpret_cast(priv); + if (httpManager->bufferHandle_ == nullptr) { + return; + } + if (httpManager->GetHttpStatus() != HTTP_STATUS_PLAY) { + return; + } + httpManager->bufferHandle_->SetControlCommend(BUFFER_CTL_TYPE_NO_MORE_DATA, nullptr, 0, nullptr, 0); + httpManager->status_ = HTTP_STATUS_END; +} + +void OnFailed(int httpError, int localError, void *priv, int supportRetry) +{ + if (priv == nullptr) { + return; + } + MEDIA_LOG_E("http %d LocalError %d", httpError, localError); + auto httpManager = reinterpret_cast(priv); + if (httpManager->GetHttpStatus() == HTTP_STATUS_PAUSE) { + MEDIA_LOG_E("Current status is pause"); + return; + } + if (httpManager->IsNeedRetry(localError, supportRetry)) { + return; + } + if (httpManager->httpAttr_.pluginHandle != nullptr && httpManager->httpAttr_.callbackFunc != nullptr) { + httpManager->httpAttr_.callbackFunc(httpError, localError, httpManager->httpAttr_.pluginHandle, 0); + } +} + +HttpManager::HttpManager() noexcept {} +HttpManager::~HttpManager() {} + +bool HttpManager::IsNeedRetry(int localError, int supportRetry) +{ + if (supportRetry == 0 || localError == -1) { + return false; + } + if (localError == ERROR_CODE_COULDNT_RESOLVE_HOST) { + return false; + } + if (retryTimes_ < ERROR_MAX_RETRY_TIMES && !isRetry_) { + isRetry_ = true; + retryTimes_++; + return true; + } + return false; +} + +UrlType HttpManager::IsHlsSource(std::string &url) +{ + if (url.empty() || url.find("http") == 0) { + return URL_UNKNOWN; + } + if (url.substr(url.length() - 5, url.length()) == ".m3u8") { + return URL_HLS; + } else { + return URL_HTTP; + } +} + +bool HttpManager::HttpOpen(std::string &url, HttpAttr &attr) +{ + if (url.empty()) { + return false; + } + bufferHandle_ = std::make_shared(); + if (IsHlsSource(url) == URL_HTTP) { + sourceHandle_ = std::make_shared(); + } else { + // todo + } + if (bufferHandle_ == nullptr || sourceHandle_ == nullptr) { + return false; + } + httpAttr_ = attr; + httpAttr_.bufferSize = attr.bufferSize > 0 ? attr.bufferSize : DEFAULT_SOURCE_SIZE; + httpAttr_.priority = attr.priority > 0 ? attr.priority : DEFAULT_PRIORITY; + sourceBuffer_ = bufferHandle_->RingbufferOpen(httpAttr_.bufferSize); + sourceHandle_->SourceSetOnData(ReceiveData, this); + sourceHandle_->SourceSetOnEnd(OnFinished, this); + sourceHandle_->SourceSetOnError(OnFailed, this); + sourceHandle_->SourceSetCertFile(httpAttr_.certFile); + sourceHandle_->SourceSetPriority(httpAttr_.priority); + if (!sourceHandle_->SourceOpen(url)) { + MEDIA_LOG_E("SourceOpen failed"); + HttpClose(); + return false; + } + /*todo: monitor handle*/ + status_ = HTTP_STATUS_PLAY; + return true; +} + +void HttpManager::HttpClose() +{ + status_ = HTTP_STATUS_STOP; + httpAttr_.callbackFunc = nullptr; + httpAttr_.pluginHandle = nullptr; + httpAttr_.certFile = ""; + /*todo: monitor handle*/ + if (sourceHandle_ != nullptr) { + sourceHandle_->SourceClose(); + sourceHandle_.reset(); + sourceHandle_ = nullptr; + } + if (sourceBuffer_ != nullptr) { + bufferHandle_.RingbufferClose(); + bufferHandle_.reset(); + bufferHandle_ = nullptr; + sourceBuffer_ = nullptr; + } +} + +bool HttpManager::HttpRead(unsigned char *buff, unsigned int wantReadLength, unsigned int &realReadLength, int &flag) +{ + if (bufferHandle_ == nullptr || sourceHandle_ == nullptr) { + return false; + } + lastReadTime_ = 0; + if (status_ == HTTP_STATUS_SEEK) { + return true; + } else if (status_ == HTTP_STATUS_PAUSE) { + if (IsStreaming()) { + MEDIA_LOG_I("Info: PAUSE=>PLAY streaming"); + bufferHandle_->RingbufferReset(); + sourceHandle_->SourceSeek(0); + } else { + unsigned int position = bufferHandle_->GetWritePosition(); + sourceHandle_->SourceSeek(position); + } + status_ = HTTP_STATUS_PLAY; + } + bool ret = bufferHandle_->RingbufferRead(buff, wantReadLength, realReadLength); + if (status_ == HTTP_STATUS_END && realReadLength == 0) { + flag = 1; + } + return ret; +} + +bool HttpManager::HttpPeek(unsigned char *buff, unsigned int wantReadLength, unsigned int &realReadLength) +{ + if (bufferHandle_ == nullptr) { + return false; + } + return bufferHandle_->RingbufferPeek(buff, wantReadLength, realReadLength); +} + +bool HttpManager::HttpSeek(int offset) +{ + if (bufferHandle_ == nullptr || sourceHandle_ == nullptr) { + return false; + } + if (status_ == HTTP_STATUS_SEEK) { + MEDIA_LOG_W("http seek"); + return true; + } + status_ = HTTP_STATUS_SEEK; + lastReadTime_ = 0; + if (isRetry_ == 0) { + if (bufferHandle_->RingBufferSeek(offset)) { + status_ = HTTP_STATUS_PLAY; + return true; + } + } else { + MEDIA_LOG_W("Retry for error"); + } + if (sourceHandle_->SourceSeek(offset)) { + bufferHandle_->RingbufferReset(); + bufferHandle_->SetWritePosition(offset); + status_ = HTTP_STATUS_PLAY; + return true; + } + return false; +} + +bool HttpManager::HttpPause() +{ + if (sourceHandle_ == nullptr) { + return false; + } + sourceHandle_->SourceStop(); + status_ = HTTP_STATUS_PAUSE; + return true; +} + +bool HttpManager::HttpReset() +{ + if (bufferHandle_ == nullptr || sourceHandle_ == nullptr) { + return false; + } + status_ = HTTP_STATUS_SEEK; + unsigned int position = bufferHandle_->GetWritePosition(); + if (sourceHandle_->SourceSeek(position)) { + status_ = HTTP_STATUS_PLAY; + return true; + } + return false; +} + +unsigned int HttpManager::GetContentLength() const +{ + if (sourceHandle_ == nullptr) { + return 0; + } + unsigned int length = 0; + if (!sourceHandle_->SourceControl(SOURCE_CTL_TYPE_GET_CONTENT_LENGTH, nullptr, 0, &length, sizeof(length))) { + MEDAI_LOG_W("GetContentLength failed"); + } + return length; +} + +HttpStatus HttpManager::GetHttpStatus() const +{ + return status_; +} + +unsigned int HttpManager::GetLastReadTime() const +{ + return lastReadTime_; +} + +void HttpManager::GetHttpBufferRange(unsigned int &read, unsigned int &write) +{ + if (bufferHandle_ == nullptr) { + read = 0; + write = 0; + return; + } + read = bufferHandle_->GetReadPosition(); + write = bufferHandle_->GetWritePosition(); + return; +} + +void HttpManager::SetWaterline(int high, int low) +{ + if (bufferHandle_ == nullptr) { + return; + } + if (bufferHandle_->SetControlCommend(BUFFER_CTL_MODIFY_WATERLINE_H, &high, sizeof(high), nullptr, 0) == -1) { + MEDIA_LOG_E("SetWaterline failed"); + } + return; +} + +bool HttpManager::IsStreaming() +{ + if (sourceHandle_ == nullptr) { + return false; + } + bool ret = false; + sourceHandle_->SourceControl(SOURCE_CTL_TYPE_IS_STREAMING, nullptr, 0, &ret, sizeof(ret)); + return ret; +} + +} +} +} \ No newline at end of file diff --git a/engine/plugin/plugins/source/http_source/http_manager.h b/engine/plugin/plugins/source/http_source/http_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..40b704b3b4b07884665c587a770de7964d589677 --- /dev/null +++ b/engine/plugin/plugins/source/http_source/http_manager.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HISTREAMER_HTTP_MANAGER_H +#define HISTREAMER_HTTP_MANAGER_H + +#include "waterline_buffer.h" +#include "source_http_segment.h" +#include "source.h" +#include +#include +#include + +namespace OHOS { +namespace Media { +namespace Plugin { +using OnError = void(*)(int httpError, int localError, void *param, int supportRetry); +constexpr unsigned int DEFAULT_SOURCE_SIZE = 20 * 1024; +constexpr unsigned int DEFAULT_PRIORITY = 24; + +struct HttpAttr { + std::string certFile; + OnError callbackFunc; + void *pluginHandle; + int bufferSize; + int priority; +}; + +enum HttpStatus { + HTTP_STATUS_IDLE = 0, + HTTP_STATUS_PLAY, + HTTP_STATUS_PAUSE, + HTTP_STATUS_SEEK, + HTTP_STATUS_END, + HTTP_STATUS_STOP +}; + +enum UrlType { + URL_HTTP, + URL_HLS, + URL_WEBSOCKET, + URL_UNKNOWN +}; + +class HttpManager { +public: + HttpManager() noexcept; + virtual ~HttpManager(); + bool HttpOpen(std::string &url, HttpAttr &attr); + void HttpClose(); + bool HttpRead(unsigned char *buff, unsigned int wantReadLength, unsigned int &realReadLength, int &flag); + bool HttpPeek(unsigned char *buff, unsigned int wantReadLength, unsigned int &realReadLength); + bool HttpSeek(int offset); + bool HttpPause(); + bool HttpReset(); + unsigned int GetContentLength() const; + HttpStatus GetHttpStatus() const; + unsigned int GetLastReadTime() const; + void GetHttpBufferRange(unsigned int &read, unsigned int &write); + void SetWaterLine(int high, int low); + bool IsStreaming(); + UrlType IsHlsSource(std::string &url); + +private: + friend void ReceiveData(unsigned char *data, int len, void *priv); + friend void OnFinished(void *priv); + friend void OnFailed(int httpError, int localError, void *priv, int supportRetry); + bool IsNeedRetry(int localError, int supportRetry); + std::shared_ptr bufferHandle_ {nullptr}; + std::shared_ptr sourceHandle_ {nullptr}; + HttpAttr httpAttr_ {nullptr, nullptr, DEFAULT_SOURCE_SIZE, DEFAULT_PRIORITY, false, 0, ""}; + HttpStatus status_ {HTTP_STATUS_IDLE}; + unsigned int lastReadTime_ {0}; + bool isRetry_ {false}; + int retryTimes_ {0}; + unsigned char *sourceBuffer_ {nullptr}; +}; +} +} +} + + +#endif \ No newline at end of file