• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &param)
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 &param)
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 &param, 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