1 /*
2 * Copyright (C) 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 #include "avmetadatahelper_engine_gst_impl.h"
17 #include <gst/gst.h>
18 #include "media_errors.h"
19 #include "media_log.h"
20 #include "i_playbin_ctrler.h"
21 #include "avmeta_sinkprovider.h"
22 #include "avmeta_frame_extractor.h"
23 #include "avmeta_meta_collector.h"
24 #include "scope_guard.h"
25 #include "uri_helper.h"
26 #include "time_perf.h"
27 #include "media_dfx.h"
28
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "AVMetaEngineGstImpl"};
31 const std::map<OHOS::Media::PlayBinState, std::string> AVMETADATA_STATE_MAP = {
32 {OHOS::Media::PLAYBIN_STATE_IDLE, "idle"},
33 {OHOS::Media::PLAYBIN_STATE_INITIALIZED, "initialized"},
34 {OHOS::Media::PLAYBIN_STATE_PREPARING, "preparing"},
35 {OHOS::Media::PLAYBIN_STATE_PREPARED, "prepared"},
36 {OHOS::Media::PLAYBIN_STATE_PLAYING, "playing"},
37 {OHOS::Media::PLAYBIN_STATE_PAUSED, "paused"},
38 {OHOS::Media::PLAYBIN_STATE_STOPPED, "stopped"},
39 {OHOS::Media::PLAYBIN_STATE_PLAYBACK_COMPLETE, "playbackcomplete"},
40 };
41 }
42
43 namespace OHOS {
44 namespace Media {
45 static const std::set<PixelFormat> SUPPORTED_PIXELFORMAT = {
46 PixelFormat::RGB_565, PixelFormat::RGB_888, PixelFormat::RGBA_8888
47 };
48
CheckFrameFetchParam(int64_t timeUsOrIndex,int32_t option,const OutputConfiguration & param)49 static bool CheckFrameFetchParam(int64_t timeUsOrIndex, int32_t option, const OutputConfiguration ¶m)
50 {
51 (void)timeUsOrIndex;
52 if ((option != AV_META_QUERY_CLOSEST) && (option != AV_META_QUERY_CLOSEST_SYNC) &&
53 (option != AV_META_QUERY_NEXT_SYNC) && (option != AV_META_QUERY_PREVIOUS_SYNC)) {
54 MEDIA_LOGE("Invalid query option: %{public}d", option);
55 return false;
56 }
57
58 if (SUPPORTED_PIXELFORMAT.count(param.colorFormat) == 0) {
59 MEDIA_LOGE("Unsupported pixelformat: %{public}d", param.colorFormat);
60 return false;
61 }
62
63 static constexpr int32_t maxDstWidth = 7680;
64 static constexpr int32_t minDstWidth = 32;
65 if (param.dstWidth > maxDstWidth || (param.dstWidth < minDstWidth && param.dstWidth != -1)) {
66 MEDIA_LOGE("Invalid dstWidth: %{public}d", param.dstWidth);
67 return false;
68 }
69
70 static constexpr int32_t maxDstHeight = 4320;
71 static constexpr int32_t minDstHeight = 32;
72 if (param.dstHeight > maxDstHeight || (param.dstHeight < minDstHeight && param.dstHeight != -1)) {
73 MEDIA_LOGE("Invalid dstHeight: %{public}d", param.dstHeight);
74 return false;
75 }
76
77 return true;
78 }
79
AVMetadataHelperEngineGstImpl()80 AVMetadataHelperEngineGstImpl::AVMetadataHelperEngineGstImpl()
81 {
82 MEDIA_LOGD("enter ctor, instance: 0x%{public}06" PRIXPTR "", FAKE_POINTER(this));
83 }
84
~AVMetadataHelperEngineGstImpl()85 AVMetadataHelperEngineGstImpl::~AVMetadataHelperEngineGstImpl()
86 {
87 MEDIA_LOGD("enter dtor, instance: 0x%{public}06" PRIXPTR "", FAKE_POINTER(this));
88 Reset();
89 CLEAN_PERF_RECORD(this);
90 }
91
SetSource(const std::string & uri,int32_t usage)92 int32_t AVMetadataHelperEngineGstImpl::SetSource(const std::string &uri, int32_t usage)
93 {
94 if ((usage != AVMetadataUsage::AV_META_USAGE_META_ONLY) &&
95 (usage != AVMetadataUsage::AV_META_USAGE_PIXEL_MAP)) {
96 MEDIA_LOGE("Invalid avmetadatahelper usage: %{public}d", usage);
97 return MSERR_INVALID_VAL;
98 }
99
100 UriHelper uriHelper(uri);
101 if (uriHelper.UriType() != UriHelper::URI_TYPE_FILE && uriHelper.UriType() != UriHelper::URI_TYPE_FD) {
102 MEDIA_LOGE("Unsupported uri type : %{public}s", uri.c_str());
103 return MSERR_UNSUPPORT;
104 }
105
106 MEDIA_LOGI("uri: %{public}s, usage: %{public}d", uri.c_str(), usage);
107
108 if (usage == AVMetadataUsage::AV_META_USAGE_PIXEL_MAP) {
109 ASYNC_PERF_START(this, "FirstFetchFrame");
110 }
111
112 int32_t ret = SetSourceInternel(uri, usage);
113 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Failed to call SetSourceInternel");
114
115 MEDIA_LOGI("set source success");
116 return MSERR_OK;
117 }
118
ResolveMetadata(int32_t key)119 std::string AVMetadataHelperEngineGstImpl::ResolveMetadata(int32_t key)
120 {
121 MEDIA_LOGD("enter");
122 std::string result;
123
124 int32_t ret = ExtractMetadata();
125 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, result, "Failed to call ExtractMetadata");
126
127 if (collectedMeta_.count(key) == 0 || collectedMeta_.at(key).empty()) {
128 MEDIA_LOGE("The specified metadata %{public}d cannot be obtained from the specified stream.", key);
129 return result;
130 }
131
132 MEDIA_LOGD("exit");
133 result = collectedMeta_[key];
134 return result;
135 }
136
ResolveMetadata()137 std::unordered_map<int32_t, std::string> AVMetadataHelperEngineGstImpl::ResolveMetadata()
138 {
139 MEDIA_LOGD("enter");
140
141 int32_t ret = ExtractMetadata();
142 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, {}, "Failed to call ExtractMetadata");
143
144 MEDIA_LOGD("exit");
145 return collectedMeta_;
146 }
147
FetchArtPicture()148 std::shared_ptr<AVSharedMemory> AVMetadataHelperEngineGstImpl::FetchArtPicture()
149 {
150 MEDIA_LOGD("enter");
151
152 int32_t ret = ExtractMetadata();
153 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, nullptr, "Failed to call ExtractMetadata");
154
155 auto result = metaCollector_->FetchArtPicture();
156 MEDIA_LOGD("exit");
157 return result;
158 }
159
FetchFrameAtTime(int64_t timeUs,int32_t option,const OutputConfiguration & param)160 std::shared_ptr<AVSharedMemory> AVMetadataHelperEngineGstImpl::FetchFrameAtTime(
161 int64_t timeUs, int32_t option, const OutputConfiguration ¶m)
162 {
163 MEDIA_LOGD("enter");
164
165 if (usage_ != AVMetadataUsage::AV_META_USAGE_PIXEL_MAP) {
166 MEDIA_LOGE("current instance is unavailable for fetch frame, check usage !");
167 return nullptr;
168 }
169
170 std::vector<std::shared_ptr<AVSharedMemory>> outFrames;
171 int32_t ret = FetchFrameInternel(timeUs, option, 1, param, outFrames);
172 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, nullptr, "fetch frame failed");
173
174 MEDIA_LOGD("exit");
175 return outFrames[0];
176 }
177
OnNotifyAutoPlugSort(GValueArray & factories)178 GValueArray *AVMetadataHelperEngineGstImpl::OnNotifyAutoPlugSort(GValueArray &factories)
179 {
180 GValueArray *result = g_value_array_new(factories.n_values);
181
182 for (uint32_t i = 0; i < factories.n_values; i++) {
183 GstElementFactory *factory =
184 static_cast<GstElementFactory *>(g_value_get_object(g_value_array_get_nth(&factories, i)));
185 if (strstr(gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS),
186 "Codec/Decoder/Video/Hardware")) {
187 MEDIA_LOGD("set remove hardware codec plugins from pipeline");
188 continue;
189 }
190 GValue val = G_VALUE_INIT;
191 g_value_init(&val, G_TYPE_OBJECT);
192 g_value_set_object(&val, factory);
193 result = g_value_array_append(result, &val);
194 g_value_unset(&val);
195 }
196 return result;
197 }
198
SetSourceInternel(const std::string & uri,int32_t usage)199 int32_t AVMetadataHelperEngineGstImpl::SetSourceInternel(const std::string &uri, int32_t usage)
200 {
201 Reset();
202 ON_SCOPE_EXIT(0) { Reset(); };
203
204 uint8_t renderMode = IPlayBinCtrler::PlayBinRenderMode::NATIVE_STREAM;
205 renderMode = renderMode | IPlayBinCtrler::PlayBinRenderMode::DISABLE_TEXT;
206 auto notifier = std::bind(&AVMetadataHelperEngineGstImpl::OnNotifyMessage, this, std::placeholders::_1);
207 sinkProvider_ = std::make_shared<AVMetaSinkProvider>(usage);
208
209 IPlayBinCtrler::PlayBinCreateParam createParam = {
210 static_cast<IPlayBinCtrler::PlayBinRenderMode>(renderMode), notifier, sinkProvider_
211 };
212
213 playBinCtrler_ = IPlayBinCtrler::Create(IPlayBinCtrler::PlayBinKind::PLAYBIN2, createParam);
214 CHECK_AND_RETURN_RET_LOG(playBinCtrler_ != nullptr, MSERR_UNKNOWN, "Failed to call IPlayBinCtrler::Create");
215
216 int32_t ret = playBinCtrler_->SetSource(uri);
217 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Failed to call playBinCtrler_->SetSource");
218
219 metaCollector_ = std::make_unique<AVMetaMetaCollector>();
220 auto listener = std::bind(&AVMetadataHelperEngineGstImpl::OnNotifyElemSetup, this, std::placeholders::_1);
221 playBinCtrler_->SetElemSetupListener(listener);
222
223 listener = std::bind(&AVMetadataHelperEngineGstImpl::OnNotifyElemUnSetup, this, std::placeholders::_1);
224 playBinCtrler_->SetElemUnSetupListener(listener);
225
226 auto autoPlugSortListener =
227 std::bind(&AVMetadataHelperEngineGstImpl::OnNotifyAutoPlugSort, this, std::placeholders::_1);
228 playBinCtrler_->SetAutoPlugSortListener(autoPlugSortListener);
229
230 if (usage == AVMetadataUsage::AV_META_USAGE_PIXEL_MAP) {
231 auto vidSink = sinkProvider_->CreateVideoSink();
232 CHECK_AND_RETURN_RET_LOG(vidSink != nullptr, MSERR_UNKNOWN, "get video sink failed");
233 frameExtractor_ = std::make_unique<AVMetaFrameExtractor>();
234 ret = frameExtractor_->Init(playBinCtrler_, *vidSink);
235 if (ret != MSERR_OK) {
236 MEDIA_LOGE("frameExtractor init failed");
237 }
238 }
239
240 metaCollector_->Start();
241 usage_ = usage;
242
243 ret = PrepareInternel(true);
244 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Failed to call PrepareInternel");
245
246 std::string mimeType = metaCollector_->GetMetadata(AV_KEY_MIME_TYPE);
247 if (mimeType.empty()) {
248 MEDIA_LOGE("can not recognize the media source's mimetype, set source failed");
249 Reset();
250 return MSERR_INVALID_OPERATION;
251 }
252
253 CANCEL_SCOPE_EXIT_GUARD(0);
254 return MSERR_OK;
255 }
256
PrepareInternel(bool async)257 int32_t AVMetadataHelperEngineGstImpl::PrepareInternel(bool async)
258 {
259 CHECK_AND_RETURN_RET_LOG(playBinCtrler_ != nullptr, MSERR_INVALID_OPERATION, "set source firstly");
260
261 {
262 std::unique_lock<std::mutex> lock(mutex_);
263 if (status_ == PLAYBIN_STATE_PREPARED || status_ == PLAYBIN_STATE_PLAYING || status_ == PLAYBIN_STATE_PAUSED) {
264 return MSERR_OK;
265 }
266 }
267 asyncDone_ = false;
268 int32_t ret = playBinCtrler_->PrepareAsync();
269 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "prepare failed");
270
271 if (!async) {
272 metaCollector_->Stop(true);
273 static constexpr int32_t timeout = 5;
274 std::unique_lock<std::mutex> lock(mutex_);
275 cond_.wait_for(lock, std::chrono::seconds(timeout), [this]() {
276 return (status_ == PLAYBIN_STATE_PREPARED && asyncDone_) || errHappened_;
277 });
278 CHECK_AND_RETURN_RET_LOG(!errHappened_, MSERR_UNKNOWN, "prepare failed");
279 }
280
281 return MSERR_OK;
282 }
283
FetchFrameInternel(int64_t timeUsOrIndex,int32_t option,int32_t numFrames,const OutputConfiguration & param,std::vector<std::shared_ptr<AVSharedMemory>> & outFrames)284 int32_t AVMetadataHelperEngineGstImpl::FetchFrameInternel(int64_t timeUsOrIndex, int32_t option, int32_t numFrames,
285 const OutputConfiguration ¶m, std::vector<std::shared_ptr<AVSharedMemory>> &outFrames)
286 {
287 (void)numFrames;
288
289 AUTO_PERF(this, "FetchFrame");
290
291 if (!CheckFrameFetchParam(timeUsOrIndex, option, param)) {
292 MEDIA_LOGE("fetch frame's param invalid");
293 return MSERR_INVALID_OPERATION;
294 }
295
296 CHECK_AND_RETURN_RET_LOG(frameExtractor_ != nullptr, MSERR_INVALID_OPERATION, "frameExtractor is nullptr");
297
298 int32_t ret = ExtractMetadata();
299 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Failed to call ExtractMetadata");
300
301 if (collectedMeta_.find(AV_KEY_HAS_VIDEO) == collectedMeta_.end() ||
302 collectedMeta_[AV_KEY_HAS_VIDEO] != "yes") {
303 MEDIA_LOGE("There is no video track in the current media source !");
304 return MSERR_INVALID_OPERATION;
305 }
306
307 if (!metaCollector_->IsCollecteCompleted()) {
308 MEDIA_LOGE("extract meta failed, exit");
309 return MSERR_UNKNOWN;
310 }
311
312 ret = PrepareInternel(false);
313 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Failed to call PrepareInternel");
314
315 auto frame = frameExtractor_->ExtractFrame(timeUsOrIndex, option, param);
316 if (frame == nullptr) {
317 MEDIA_LOGE("fetch frame failed");
318 return MSERR_UNKNOWN;
319 }
320
321 if (firstFetch_) {
322 ASYNC_PERF_STOP(this, "FirstFetchFrame");
323 firstFetch_ = false;
324 }
325
326 outFrames.push_back(frame);
327 return MSERR_OK;
328 }
329
ExtractMetadata()330 int32_t AVMetadataHelperEngineGstImpl::ExtractMetadata()
331 {
332 CHECK_AND_RETURN_RET_LOG(metaCollector_ != nullptr, MSERR_INVALID_OPERATION, "metaCollector is nullptr");
333
334 if (!hasCollectMeta_) {
335 collectedMeta_ = metaCollector_->GetMetadata();
336 hasCollectMeta_ = true;
337 }
338 return MSERR_OK;
339 }
340
Reset()341 void AVMetadataHelperEngineGstImpl::Reset()
342 {
343 std::unique_lock<std::mutex> lock(mutex_);
344
345 if (metaCollector_ != nullptr) {
346 metaCollector_->Stop();
347 hasCollectMeta_ = false;
348 }
349
350 if (frameExtractor_ != nullptr) {
351 frameExtractor_->Reset();
352 }
353
354 if (playBinCtrler_ != nullptr) {
355 playBinCtrler_->SetElemSetupListener(nullptr);
356 playBinCtrler_->SetAutoPlugSortListener(nullptr);
357
358 auto tmp = playBinCtrler_;
359 playBinCtrler_ = nullptr;
360 // Some msg maybe be reported by the playbinCtrler_ during the playbinCtler_ destroying.
361 // unlock to avoid the deadlock.
362 lock.unlock();
363 tmp->Stop(false);
364 tmp = nullptr;
365 lock.lock();
366 }
367
368 sinkProvider_ = nullptr;
369 metaCollector_ = nullptr;
370 frameExtractor_ = nullptr;
371
372 errHappened_ = false;
373 status_ = PLAYBIN_STATE_IDLE;
374
375 firstFetch_ = true;
376 asyncDone_ = false;
377
378 lock.unlock();
379 lock.lock();
380 }
381
OnNotifyMessage(const PlayBinMessage & msg)382 void AVMetadataHelperEngineGstImpl::OnNotifyMessage(const PlayBinMessage &msg)
383 {
384 switch (msg.type) {
385 case PLAYBIN_MSG_STATE_CHANGE: {
386 std::unique_lock<std::mutex> lock(mutex_);
387 status_ = msg.code;
388 BehaviorEventWrite(
389 GetStatusDescription(static_cast<OHOS::Media::PlayBinState>(status_)), "AVMetadata");
390 cond_.notify_all();
391 if (msg.code == PLAYBIN_STATE_PREPARED) {
392 MEDIA_LOGI("prepare finished");
393 }
394 if (msg.code == PLAYBIN_STATE_STOPPED) {
395 MEDIA_LOGI("stop finished");
396 }
397 break;
398 }
399 case PLAYBIN_MSG_ERROR: {
400 std::unique_lock<std::mutex> lock(mutex_);
401 errHappened_ = true;
402 if (metaCollector_ != nullptr) {
403 metaCollector_->Stop();
404 }
405 if (frameExtractor_ != nullptr) {
406 frameExtractor_->Reset();
407 }
408 cond_.notify_all();
409 MEDIA_LOGE("error happened, cancel inprocessing job");
410 FaultEventWrite("error happened, cancel inprocessing job", "AVMetadata");
411 break;
412 }
413 case PLAYBIN_MSG_SEEKDONE: {
414 std::unique_lock<std::mutex> lock(mutex_);
415 if (frameExtractor_ != nullptr) {
416 frameExtractor_->NotifyPlayBinMsg(msg);
417 }
418 break;
419 }
420 case PLAYBIN_MSG_ASYNC_DONE: {
421 asyncDone_ = true;
422 cond_.notify_all();
423 MEDIA_LOGI("async done");
424 break;
425 }
426 default:
427 break;
428 }
429 }
430
OnNotifyElemSetup(GstElement & elem)431 void AVMetadataHelperEngineGstImpl::OnNotifyElemSetup(GstElement &elem)
432 {
433 const gchar *metadata = gst_element_get_metadata(&elem, GST_ELEMENT_METADATA_KLASS);
434 std::string metaStr(metadata);
435 if (metaStr.find("Codec/Decoder/Video") != std::string::npos) {
436 MEDIA_LOGI("Only need one frame!");
437 g_object_set(const_cast<GstElement *>(&elem), "only-one-frame-required", TRUE, nullptr);
438 }
439
440 std::unique_lock<std::mutex> lock(mutex_);
441 if (metaCollector_ != nullptr) {
442 metaCollector_->AddMetaSource(elem);
443 }
444 }
445
OnNotifyElemUnSetup(GstElement & elem)446 void AVMetadataHelperEngineGstImpl::OnNotifyElemUnSetup(GstElement &elem)
447 {
448 std::unique_lock<std::mutex> lock(mutex_);
449 if (metaCollector_ != nullptr) {
450 metaCollector_->RemoveMetaSource(elem);
451 }
452 }
453
GetStatusDescription(OHOS::Media::PlayBinState status)454 const std::string &AVMetadataHelperEngineGstImpl::GetStatusDescription(OHOS::Media::PlayBinState status)
455 {
456 static const std::string ILLEGAL_STATE = "PLAYER_STATUS_ILLEGAL";
457 if (status < OHOS::Media::PLAYBIN_STATE_IDLE || status > OHOS::Media::PLAYBIN_STATE_PLAYBACK_COMPLETE) {
458 return ILLEGAL_STATE;
459 }
460
461 return AVMETADATA_STATE_MAP.find(status)->second;
462 }
463 } // namespace Media
464 } // namespace OHOS