• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifdef RECORDER_SUPPORT
17 
18 #define HST_LOG_TAG "AudioEncoderFilter"
19 
20 #include "audio_encoder_filter.h"
21 #include "factory/filter_factory.h"
22 #include "foundation/cpp_ext/memory_ext.h"
23 #include "osal/utils/util.h"
24 #include "utils/steady_clock.h"
25 
26 #define DEFAULT_OUT_BUFFER_POOL_SIZE 5
27 #define MAX_OUT_DECODED_DATA_SIZE_PER_FRAME 20 * 1024 // 20kB
28 
29 namespace OHOS {
30 namespace Media {
31 namespace Pipeline {
32 static AutoRegisterFilter<AudioEncoderFilter> g_registerFilterHelper("builtin.recorder.audioencoder");
33 
AudioEncoderFilter(const std::string & name)34 AudioEncoderFilter::AudioEncoderFilter(const std::string& name) : CodecFilterBase(name)
35 {
36     filterType_ = FilterType::AUDIO_ENCODER;
37     MEDIA_LOG_D("audio encoder ctor called");
38 }
39 
~AudioEncoderFilter()40 AudioEncoderFilter::~AudioEncoderFilter()
41 {
42     MEDIA_LOG_D("audio encoder dtor called");
43     Release();
44 }
45 
Start()46 ErrorCode AudioEncoderFilter::Start()
47 {
48     MEDIA_LOG_I("audio encoder start called");
49     if (state_ != FilterState::READY && state_ != FilterState::PAUSED) {
50         MEDIA_LOG_W("call encoder start() when state is not ready or working");
51         return ErrorCode::ERROR_INVALID_OPERATION;
52     }
53     rb_->SetActive(true);
54     return FilterBase::Start();
55 }
56 
SetAudioEncoder(int32_t sourceId,std::shared_ptr<Plugin::Meta> encoderMeta)57 ErrorCode AudioEncoderFilter::SetAudioEncoder(int32_t sourceId, std::shared_ptr<Plugin::Meta> encoderMeta)
58 {
59     std::string mime;
60     FALSE_RETURN_V_MSG_E(encoderMeta->GetString(Plugin::MetaID::MIME, mime), ErrorCode::ERROR_INVALID_PARAMETER_VALUE,
61                          "encoder meta must contains mime");
62     mime_ = mime;
63     encoderMeta_ = std::move(encoderMeta);
64     return ErrorCode::SUCCESS;
65 }
66 
Negotiate(const std::string & inPort,const std::shared_ptr<const Plugin::Capability> & upstreamCap,Plugin::Capability & negotiatedCap,const Plugin::TagMap & upstreamParams,Plugin::TagMap & downstreamParams)67 bool AudioEncoderFilter::Negotiate(const std::string& inPort,
68                                    const std::shared_ptr<const Plugin::Capability>& upstreamCap,
69                                    Plugin::Capability& negotiatedCap,
70                                    const Plugin::TagMap& upstreamParams,
71                                    Plugin::TagMap& downstreamParams)
72 {
73     PROFILE_BEGIN("Audio Encoder Negotiate begin");
74     FALSE_RETURN_V_MSG_E(state_ == FilterState::PREPARING, false, "not preparing when negotiate");
75     auto targetOutPort = GetRouteOutPort(inPort);
76     FALSE_RETURN_V_MSG_E(targetOutPort != nullptr, false, "out port not found");
77     std::shared_ptr<Plugin::PluginInfo> selectedPluginInfo = nullptr;
78     bool atLeastOutCapMatched = false;
79     auto candidatePlugins = FindAvailablePlugins(*upstreamCap, Plugin::PluginType::CODEC);
80     for (const auto& candidate : candidatePlugins) {
81         FALSE_LOG_MSG(!candidate.first->outCaps.empty(), "encoder plugin must have out caps");
82         for (const auto& outCap : candidate.first->outCaps) { // each codec plugin should have at least one out cap
83             Plugin::Meta tmpMeta;
84             if (outCap.mime != mime_ || !MergeMetaWithCapability(*encoderMeta_, outCap, tmpMeta)) {
85                 continue;
86             }
87             auto thisOut = std::make_shared<Plugin::Capability>();
88             if (!MergeCapabilityKeys(*upstreamCap, outCap, *thisOut)) {
89                 MEDIA_LOG_I("one cap of plugin " PUBLIC_LOG_S " mismatch upstream cap", candidate.first->name.c_str());
90                 continue;
91             }
92             atLeastOutCapMatched = true;
93             thisOut->mime = outCap.mime;
94             if (targetOutPort->Negotiate(thisOut, capNegWithDownstream_, upstreamParams, downstreamParams)) {
95                 capNegWithUpstream_ = candidate.second;
96                 selectedPluginInfo = candidate.first;
97                 MEDIA_LOG_I("use plugin " PUBLIC_LOG_S, candidate.first->name.c_str());
98                 MEDIA_LOG_I("neg upstream cap " PUBLIC_LOG_S, Capability2String(capNegWithUpstream_).c_str());
99                 MEDIA_LOG_I("neg downstream cap " PUBLIC_LOG_S, Capability2String(capNegWithDownstream_).c_str());
100                 break;
101             }
102         }
103         if (selectedPluginInfo != nullptr) {
104             break;
105         }
106     }
107     FALSE_RETURN_V_MSG_E(atLeastOutCapMatched && selectedPluginInfo != nullptr, false,
108         "can't find available encoder plugin with " PUBLIC_LOG_S, Capability2String(*upstreamCap).c_str());
109     auto res = UpdateAndInitPluginByInfo<Plugin::Codec>(plugin_, pluginInfo_, selectedPluginInfo,
110         [](const std::string& name)-> std::shared_ptr<Plugin::Codec> {
111         return Plugin::PluginManager::Instance().CreateCodecPlugin(name);
112     });
113     negotiatedCap = *upstreamCap;
114     plugin_->SetDataCallback(this);
115     PROFILE_END("audio encoder negotiate end");
116     return res;
117 }
118 
CalculateBufferSize(const std::shared_ptr<const Plugin::Meta> & meta)119 uint32_t AudioEncoderFilter::CalculateBufferSize(const std::shared_ptr<const Plugin::Meta>& meta)
120 {
121     Plugin::ValueType value;
122     if (plugin_->GetParameter(Plugin::Tag::AUDIO_SAMPLE_PER_FRAME, value) != Plugin::Status::OK ||
123         !value.SameTypeWith(typeid(uint32_t))) {
124         MEDIA_LOG_E("Get samplePerFrame from plugin fail");
125         return 0;
126     }
127     auto samplesPerFrame = Plugin::AnyCast<uint32_t>(value);
128     uint32_t channels;
129     if (!meta->GetUint32(Plugin::MetaID::AUDIO_CHANNELS, channels)) {
130         return 0;
131     }
132     Plugin::AudioSampleFormat format;
133     if (!meta->GetData<Plugin::AudioSampleFormat>(Plugin::MetaID::AUDIO_SAMPLE_FORMAT, format)) {
134         return 0;
135     }
136     return GetBytesPerSample(format) * samplesPerFrame * channels;
137 }
138 
Configure(const std::string & inPort,const std::shared_ptr<const Plugin::Meta> & upstreamMeta,Plugin::TagMap & upstreamParams,Plugin::TagMap & downstreamParams)139 bool AudioEncoderFilter::Configure(const std::string &inPort, const std::shared_ptr<const Plugin::Meta> &upstreamMeta,
140                                    Plugin::TagMap &upstreamParams, Plugin::TagMap &downstreamParams)
141 {
142     PROFILE_BEGIN("Audio encoder configure begin");
143     MEDIA_LOG_I("receive upstream meta " PUBLIC_LOG_S, Meta2String(*upstreamMeta).c_str());
144     FALSE_RETURN_V_MSG_E(plugin_ != nullptr && pluginInfo_ != nullptr, false,
145         "can't configure encoder when no plugin available");
146     auto thisMeta = std::make_shared<Plugin::Meta>();
147     // todo how to decide the caps ?
148     FALSE_RETURN_V_MSG_E(MergeMetaWithCapability(*upstreamMeta, pluginInfo_->outCaps[0], *thisMeta), false,
149         "can't configure encoder plugin since meta is not compatible with negotiated caps");
150     auto targetOutPort = GetRouteOutPort(inPort);
151     FALSE_RETURN_V_MSG_E(targetOutPort != nullptr, false, "encoder out port is not found");
152     auto err = ConfigureToStartPluginLocked(thisMeta);
153     if (err != ErrorCode::SUCCESS) {
154         MEDIA_LOG_E("encoder configure error");
155         OnEvent({name_, EventType::EVENT_ERROR, err});
156         return false;
157     }
158     FAIL_LOG(UpdateMetaFromPlugin(*thisMeta));
159     FALSE_RETURN_V_MSG_E(targetOutPort->Configure(thisMeta, upstreamParams, downstreamParams), false,
160         "fail to configure downstream");
161     state_ = FilterState::READY;
162     OnEvent({name_, EventType::EVENT_READY});
163     MEDIA_LOG_I("audio encoder send EVENT_READY");
164     PROFILE_END("Audio encoder configure end");
165     return true;
166 }
167 
ConfigureToStartPluginLocked(const std::shared_ptr<const Plugin::Meta> & meta)168 ErrorCode AudioEncoderFilter::ConfigureToStartPluginLocked(const std::shared_ptr<const Plugin::Meta>& meta)
169 {
170     FAIL_RETURN_MSG(ConfigPluginWithMeta(*plugin_, *meta), "configure encoder plugin error");
171     FAIL_RETURN_MSG(TranslatePluginStatus(plugin_->Prepare()), "encoder prepare failed");
172     FAIL_RETURN_MSG(TranslatePluginStatus(plugin_->Start()), "encoder start failed");
173 
174     uint32_t bufferCnt = 0;
175     if (GetPluginParameterLocked(Tag::REQUIRED_OUT_BUFFER_CNT, bufferCnt) != ErrorCode::SUCCESS) {
176         bufferCnt = DEFAULT_OUT_BUFFER_POOL_SIZE;
177     }
178     // 每次重新创建bufferPool
179     outBufferPool_ = std::make_shared<BufferPool<AVBuffer>>(bufferCnt);
180     frameSize_ = CalculateBufferSize(meta);
181     if (frameSize_ == 0) {
182         frameSize_ = MAX_OUT_DECODED_DATA_SIZE_PER_FRAME;
183     }
184     auto outAllocator = plugin_->GetAllocator();
185     if (outAllocator == nullptr) {
186         MEDIA_LOG_I("plugin doest not support out allocator, using framework allocator");
187         outBufferPool_->Init(frameSize_);
188     } else {
189         MEDIA_LOG_I("using plugin output allocator");
190         for (size_t cnt = 0; cnt < bufferCnt; cnt++) {
191             auto buf = CppExt::make_unique<AVBuffer>();
192             buf->AllocMemory(outAllocator, frameSize_);
193             outBufferPool_->Append(std::move(buf));
194         }
195     }
196     rb_ = CppExt::make_unique<RingBuffer>(frameSize_ * 10); // 最大缓存10帧
197     FALSE_RETURN_V_MSG_E(rb_ != nullptr, ErrorCode::ERROR_NO_MEMORY, "create ring buffer failed");
198     rb_->Init();
199     cahceBuffer_ = std::make_shared<AVBuffer>(Plugin::BufferMetaType::AUDIO);
200     FALSE_RETURN_V_MSG_E(cahceBuffer_->AllocMemory(nullptr, frameSize_) != nullptr, ErrorCode::ERROR_NO_MEMORY,
201                          "alloc cache mem failed");
202     return ErrorCode::SUCCESS;
203 }
204 
PushData(const std::string & inPort,const AVBufferPtr & buffer,int64_t offset)205 ErrorCode AudioEncoderFilter::PushData(const std::string& inPort, const AVBufferPtr& buffer, int64_t offset)
206 {
207     const static int8_t maxRetryCnt = 3; // max retry times of handling one frame
208     if (state_ != FilterState::READY && state_ != FilterState::PAUSED && state_ != FilterState::RUNNING) {
209         MEDIA_LOG_W("pushing data to encoder when state is " PUBLIC_LOG_D32, static_cast<int>(state_.load()));
210         return ErrorCode::ERROR_INVALID_OPERATION;
211     }
212     auto inputMemory = buffer->GetMemory();
213     if (inputMemory && inputMemory->GetSize() > 0) {
214         rb_->WriteBuffer(const_cast<uint8_t *>(inputMemory->GetReadOnlyData()), inputMemory->GetSize());
215     }
216     bool shouldDrainRb = buffer->flag & BUFFER_FLAG_EOS;
217     bool isRbDrained = false;
218     for (auto available = rb_->GetSize(); (available >= frameSize_) || (shouldDrainRb && !isRbDrained);
219          available = rb_->GetSize()) {
220         cahceBuffer_->Reset();
221         auto encodeSize = std::min(available, frameSize_);
222         if (encodeSize > 0) { // ring buffer has buffer available
223             if (rb_->ReadBuffer(cahceBuffer_->GetMemory()->GetWritableAddr(encodeSize), encodeSize) != encodeSize) {
224                 MEDIA_LOG_E("Read data from ring buffer fail");
225                 return ErrorCode::ERROR_UNKNOWN;
226             }
227         } else { // EOS
228             cahceBuffer_->flag |= BUFFER_FLAG_EOS;
229             isRbDrained = true;
230         }
231         ErrorCode handleFrameRes;
232         int8_t retryCnt = 0;
233         do {
234             handleFrameRes = HandleFrame(cahceBuffer_);
235             while (FinishFrame() == ErrorCode::SUCCESS) {
236                 MEDIA_LOG_DD("finish frame");
237             }
238             retryCnt++;
239             if (retryCnt >= maxRetryCnt) { // if retry cnt exceeds we will drop this frame
240                 break;
241             }
242             // if timed out or returns again we should try again
243         } while (handleFrameRes == ErrorCode::ERROR_TIMED_OUT || handleFrameRes == ErrorCode::ERROR_AGAIN);
244     }
245     return ErrorCode::SUCCESS;
246 }
247 
Stop()248 ErrorCode AudioEncoderFilter::Stop()
249 {
250     MEDIA_LOG_I("AudioEncoderFilter stop start.");
251     // 先改变底层状态 然后停掉上层线程 否则会产生死锁
252     if (plugin_ != nullptr) {
253         FAIL_RETURN_MSG(TranslatePluginStatus(plugin_->Flush()), "encoder flush error");
254         FAIL_RETURN_MSG(TranslatePluginStatus(plugin_->Stop()), "encoder stop error");
255     }
256     if (rb_) {
257         rb_->SetActive(false);
258     }
259     MEDIA_LOG_I("AudioEncoderFilter stop end.");
260     return FilterBase::Stop();
261 }
262 
Release()263 ErrorCode AudioEncoderFilter::Release()
264 {
265     if (plugin_) {
266         plugin_->Stop();
267         plugin_->Deinit();
268     }
269     return ErrorCode::SUCCESS;
270 }
271 
HandleFrame(const std::shared_ptr<AVBuffer> & buffer)272 ErrorCode AudioEncoderFilter::HandleFrame(const std::shared_ptr<AVBuffer>& buffer)
273 {
274     MEDIA_LOG_DD("HandleFrame called");
275     auto ret = TranslatePluginStatus(plugin_->QueueInputBuffer(buffer, 0));
276     FALSE_LOG_MSG(ret == ErrorCode::SUCCESS || ret == ErrorCode::ERROR_TIMED_OUT,
277                     "Queue input buffer to plugin fail: " PUBLIC_LOG_D32, ret);
278     return ret;
279 }
280 
FinishFrame()281 ErrorCode AudioEncoderFilter::FinishFrame()
282 {
283     MEDIA_LOG_DD("begin finish frame");
284     auto outBuffer = outBufferPool_->AllocateAppendBufferNonBlocking();
285     FALSE_RETURN_V_MSG_E(outBuffer != nullptr, ErrorCode::ERROR_NO_MEMORY, "Get out buffer from buffer pool fail");
286     outBuffer->Reset();
287     auto status = plugin_->QueueOutputBuffer(outBuffer, 0);
288     if (status != Plugin::Status::OK && status != Plugin::Status::END_OF_STREAM) {
289         if (status != Plugin::Status::ERROR_NOT_ENOUGH_DATA) {
290             MEDIA_LOG_E("Queue output buffer to plugin fail: " PUBLIC_LOG_D32, static_cast<int32_t>((status)));
291         }
292     }
293     MEDIA_LOG_DD("end finish frame");
294     return TranslatePluginStatus(status);
295 }
296 
OnInputBufferDone(const std::shared_ptr<Plugin::Buffer> & input)297 void AudioEncoderFilter::OnInputBufferDone(const std::shared_ptr<Plugin::Buffer>& input)
298 {
299     MEDIA_LOG_DD("AudioEncoderFilter::OnInputBufferDone");
300 }
301 
OnOutputBufferDone(const std::shared_ptr<Plugin::Buffer> & output)302 void AudioEncoderFilter::OnOutputBufferDone(const std::shared_ptr<Plugin::Buffer>& output)
303 {
304     FALSE_RETURN(output != nullptr);
305 
306     // push to port
307     auto oPort = outPorts_[0];
308     if (oPort->GetWorkMode() == WorkMode::PUSH) {
309         oPort->PushData(output, -1);
310     } else {
311         MEDIA_LOG_W("encoder out port works in pull mode");
312     }
313 
314     // 释放buffer 如果没有被缓存使其回到buffer pool 如果被sink缓存 则从buffer pool拿其他的buffer
315     std::const_pointer_cast<Plugin::Buffer>(output).reset();
316 }
317 } // Pipeline
318 } // Media
319 } // OHOS
320 #endif // RECORDER_SUPPORT