• 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 "avmeta_elem_meta_collector.h"
17 #include <string_view>
18 #include <limits>
19 #include "avmetadatahelper.h"
20 #include "avsharedmemorybase.h"
21 #include "av_common.h"
22 #include "gst_meta_parser.h"
23 #include "gst_utils.h"
24 #include "media_errors.h"
25 #include "media_log.h"
26 #include "securec.h"
27 
28 namespace {
29     constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "AVMetaElemCollector"};
30 }
31 
32 namespace OHOS {
33 namespace Media {
34 #define AVMETA_KEY_TO_X_MAP_ITEM(key, innerKey) { key, innerKey }
35 
36 static const std::unordered_map<int32_t, std::string_view> AVMETA_KEY_TO_X_MAP = {
37     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_ALBUM, INNER_META_KEY_ALBUM),
38     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_ALBUM_ARTIST, INNER_META_KEY_ALBUM_ARTIST),
39     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_ARTIST, INNER_META_KEY_ARTIST),
40     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_AUTHOR, INNER_META_KEY_AUTHOR),
41     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_COMPOSER, INNER_META_KEY_COMPOSER),
42     /**
43      * The most of gst plugins don't send the GST_TAG_DURATION, we obtain this
44      * information from duration query.
45      */
46     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_DURATION, ""),
47     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_GENRE, INNER_META_KEY_GENRE),
48     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_HAS_AUDIO, ""),
49     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_HAS_VIDEO, ""),
50     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_MIME_TYPE, INNER_META_KEY_MIME_TYPE),
51     /**
52      * The GST_TAG_TRACK_COUNT does not means the actual track count, we obtain
53      * this information from pads count.
54      */
55     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_NUM_TRACKS, ""),
56     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_SAMPLE_RATE, INNER_META_KEY_SAMPLE_RATE),
57     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_TITLE, INNER_META_KEY_TITLE),
58     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_VIDEO_HEIGHT, INNER_META_KEY_VIDEO_HEIGHT),
59     AVMETA_KEY_TO_X_MAP_ITEM(AV_KEY_VIDEO_WIDTH, INNER_META_KEY_VIDEO_WIDTH),
60 };
61 
PopulateMeta(Metadata & meta)62 void PopulateMeta(Metadata &meta)
63 {
64     for (auto &item : AVMETA_KEY_TO_X_MAP) {
65         if (!meta.HasMeta(item.first)) {
66             meta.SetMeta(item.first, "");
67         }
68     }
69 }
70 
71 struct AVMetaElemMetaCollector::TrackInfo {
72     bool valid = true;
73     Metadata uploadMeta;
74     Format innerMeta;
75 };
76 
Create(AVMetaSourceType type,const MetaResCb & resCb)77 std::unique_ptr<AVMetaElemMetaCollector> AVMetaElemMetaCollector::Create(AVMetaSourceType type, const MetaResCb &resCb)
78 {
79     switch (type) {
80         case AVMetaSourceType::TYPEFIND:
81             return std::make_unique<TypeFindMetaCollector>(type, resCb);
82         case AVMetaSourceType::DEMUXER:
83             return std::make_unique<DemuxerMetaCollector>(type, resCb);
84         case AVMetaSourceType::PARSER:
85             return std::make_unique<ParserMetaCollector>(type, resCb);
86         default:
87             MEDIA_LOGE("unknown type: %{public}hhu", type);
88             break;
89     }
90 
91     return nullptr;
92 }
93 
AVMetaElemMetaCollector(AVMetaSourceType type,const MetaResCb & resCb)94 AVMetaElemMetaCollector::AVMetaElemMetaCollector(AVMetaSourceType type, const MetaResCb &resCb)
95     : type_(type), resCb_(resCb)
96 {
97     MEDIA_LOGD("enter ctor, instance: 0x%{public}06" PRIXPTR ", type: %{public}hhu", FAKE_POINTER(this), type_);
98 }
99 
~AVMetaElemMetaCollector()100 AVMetaElemMetaCollector::~AVMetaElemMetaCollector()
101 {
102     MEDIA_LOGD("enter dtor, instance: 0x%{public}06" PRIXPTR "", FAKE_POINTER(this));
103 }
104 
Stop()105 void AVMetaElemMetaCollector::Stop()
106 {
107     std::unique_lock<std::mutex> lock(mutex_);
108     stopCollecting_ = true;
109 
110     for (auto &[elem, signalId] : signalIds_) {
111         g_signal_handler_disconnect(elem, signalId);
112     }
113     signalIds_.clear();
114 
115     for (auto &[pad, probeId] : padProbes_) {
116         gst_pad_remove_probe(pad, probeId);
117     }
118     padProbes_.clear();
119 
120     lock.unlock();
121     lock.lock();
122 }
123 
IsMetaCollected()124 bool AVMetaElemMetaCollector::IsMetaCollected()
125 {
126     std::unique_lock<std::mutex> lock(mutex_);
127     for (auto &[dummy, trackInfo] : trackInfos_) {
128         if (!trackInfo.valid) {
129             continue;
130         }
131         if (trackInfo.innerMeta.GetFormatMap().empty()) {
132             return false;
133         }
134     }
135 
136     // at least the duration meta and track count or container mime.
137     if (fileUploadMeta_.tbl_.empty()) {
138         return false;
139     }
140 
141     return true;
142 }
143 
FetchArtPicture()144 std::shared_ptr<AVSharedMemory> AVMetaElemMetaCollector::FetchArtPicture()
145 {
146     std::unique_lock<std::mutex> lock(mutex_);
147 
148     auto artPicMem = DoFetchArtPicture(fileInnerMeta_);
149     if (artPicMem != nullptr) {
150         return artPicMem;
151     }
152 
153     for (auto &[dummy, trackInnerMeta] : trackInfos_) {
154         artPicMem = DoFetchArtPicture(trackInnerMeta.innerMeta);
155         if (artPicMem != nullptr) {
156             return artPicMem;
157         }
158     }
159 
160     return artPicMem;
161 }
162 
DoFetchArtPicture(const Format & innerMeta)163 std::shared_ptr<AVSharedMemory> AVMetaElemMetaCollector::DoFetchArtPicture(const Format &innerMeta)
164 {
165     if (!innerMeta.ContainKey(INNER_META_KEY_IMAGE)) {
166         return nullptr;
167     }
168 
169     MEDIA_LOGD("has art picture");
170 
171     uint8_t *addr = nullptr;
172     size_t size = 0;
173     (void)innerMeta.GetBuffer(INNER_META_KEY_IMAGE, &addr, size);
174 
175     static constexpr size_t maxImageSize = 1 * 1024 * 1024;
176     if (addr == nullptr || size == 0 || size > maxImageSize) {
177         MEDIA_LOGW("invalid param, size = %{public}zu", size);
178         return nullptr;
179     }
180 
181     auto artPicMem = AVSharedMemoryBase::CreateFromLocal(
182         static_cast<int32_t>(size), AVSharedMemory::FLAGS_READ_ONLY, "artpic");
183     CHECK_AND_RETURN_RET_LOG(artPicMem != nullptr, nullptr, "create art pic failed");
184 
185     errno_t rc = memcpy_s(artPicMem->GetBase(), static_cast<size_t>(artPicMem->GetSize()), addr, size);
186     CHECK_AND_RETURN_RET_LOG(rc == EOK, nullptr, "memcpy_s failed");
187 
188     return artPicMem;
189 }
190 
AddProbeToPadList(GList & list)191 bool AVMetaElemMetaCollector::AddProbeToPadList(GList &list)
192 {
193     for (GList *padNode = g_list_first(&list); padNode != nullptr; padNode = padNode->next) {
194         if (padNode->data == nullptr) {
195             continue;
196         }
197 
198         GstPad *pad = reinterpret_cast<GstPad *>(padNode->data);
199         if (!AddProbeToPad(*pad)) {
200             return false;
201         }
202     }
203 
204     return true;
205 }
206 
AddProbeToPad(GstPad & pad)207 bool AVMetaElemMetaCollector::AddProbeToPad(GstPad &pad)
208 {
209     std::unique_lock<std::mutex> lock(mutex_);
210     if (stopCollecting_) {
211         MEDIA_LOGI("stop collecting...");
212         return false;
213     }
214 
215     gulong probeId = gst_pad_add_probe(&pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, ProbeCallback, this, nullptr);
216     if (probeId == 0) {
217         MEDIA_LOGE("add probe for %{public}s's pad %{public}s failed", PAD_PARENT_NAME(&pad), PAD_NAME(&pad));
218         return false;
219     }
220 
221     (void)padProbes_.emplace(&pad, probeId);
222     (void)trackInfos_.emplace(&pad, TrackInfo {});
223 
224     // report the track count change when caps arrived.
225     trackcount_ += 1;
226     MEDIA_LOGD("add probe to pad %{public}s of %{public}s", PAD_NAME(&pad), PAD_PARENT_NAME(&pad));
227     return true;
228 }
229 
ConnectSignal(GstElement & elem,std::string_view signal,GCallback callback)230 bool AVMetaElemMetaCollector::ConnectSignal(GstElement &elem, std::string_view signal, GCallback callback)
231 {
232     std::unique_lock<std::mutex> lock(mutex_);
233     if (stopCollecting_) {
234         MEDIA_LOGI("stop collecting...");
235         return false;
236     }
237 
238     gulong signalId = g_signal_connect(&elem, signal.data(), callback, this);
239     if (signalId == 0) {
240         MEDIA_LOGE("connect signal '%{public}s' to %{public}s failed", signal.data(), ELEM_NAME(&elem));
241         return false;
242     }
243 
244     (void)signalIds_.emplace(&elem, signalId);
245     return true;
246 }
247 
ProbeCallback(GstPad * pad,GstPadProbeInfo * info,gpointer usrdata)248 GstPadProbeReturn AVMetaElemMetaCollector::ProbeCallback(GstPad *pad, GstPadProbeInfo *info, gpointer usrdata)
249 {
250     if (pad == nullptr || info ==  nullptr || usrdata == nullptr) {
251         MEDIA_LOGE("param is invalid");
252         return GST_PAD_PROBE_OK;
253     }
254 
255     auto collector = reinterpret_cast<AVMetaElemMetaCollector *>(usrdata);
256     if (static_cast<unsigned int>(info->type) & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
257         GstEvent *event = gst_pad_probe_info_get_event(info);
258         CHECK_AND_RETURN_RET_LOG(event != nullptr, GST_PAD_PROBE_OK, "event is null");
259         collector->OnEventProbe(*pad, *event);
260     }
261 
262     return GST_PAD_PROBE_OK;
263 }
264 
ParseTagList(const GstTagList & tagList,TrackInfo & trackInfo)265 void AVMetaElemMetaCollector::ParseTagList(const GstTagList &tagList, TrackInfo &trackInfo)
266 {
267     if (!trackInfo.valid) {
268         return;
269     }
270 
271     GstTagScope scope = gst_tag_list_get_scope(&tagList);
272     MEDIA_LOGI("catch tag %{public}s event", scope == GST_TAG_SCOPE_GLOBAL ? "global" : "stream");
273 
274     if (scope == GST_TAG_SCOPE_GLOBAL) {
275         if (globalTagCatched_) {
276             return;
277         }
278         globalTagCatched_ = true;
279     }
280 
281     if (scope == GST_TAG_SCOPE_GLOBAL) {
282         GstMetaParser::ParseTagList(tagList, fileInnerMeta_);
283         ConvertToAVMeta(fileInnerMeta_, fileUploadMeta_);
284         ReportMeta(fileUploadMeta_);
285     } else {
286         GstMetaParser::ParseTagList(tagList, trackInfo.innerMeta);
287         ConvertToAVMeta(trackInfo.innerMeta, trackInfo.uploadMeta);
288         ReportMeta(trackInfo.uploadMeta);
289     }
290 }
291 
ParseCaps(const GstCaps & caps,TrackInfo & trackInfo)292 void AVMetaElemMetaCollector::ParseCaps(const GstCaps &caps, TrackInfo &trackInfo)
293 {
294     GstMetaParser::ParseStreamCaps(caps, trackInfo.innerMeta);
295 
296     if (!EnsureTrackValid(trackInfo)) {
297         return;
298     }
299 
300     fileUploadMeta_.SetMeta(AV_KEY_NUM_TRACKS, std::to_string(trackcount_));
301     ReportMeta(fileUploadMeta_);
302 
303     ConvertToAVMeta(trackInfo.innerMeta, trackInfo.uploadMeta);
304     ReportMeta(trackInfo.uploadMeta);
305 }
306 
EnsureTrackValid(TrackInfo & trackInfo)307 bool AVMetaElemMetaCollector::EnsureTrackValid(TrackInfo &trackInfo)
308 {
309     /**
310      * If the track can not supported, it would not be taken account into the
311      * total track counts. The ffmpeg will generate one track for one image of
312      * the metadata, the track's caps is image/png or image/jpeg, etc. For such
313      * tracks, them should be considered as invalid tracks.
314      */
315     int32_t trackType;
316     std::string mimeType;
317     if (!trackInfo.innerMeta.GetIntValue(INNER_META_KEY_TRACK_TYPE, trackType) ||
318         !trackInfo.innerMeta.GetStringValue(INNER_META_KEY_MIME_TYPE, mimeType)) {
319         trackInfo.valid = false;
320         trackcount_ -= 1;
321         fileUploadMeta_.SetMeta(AV_KEY_NUM_TRACKS, std::to_string(trackcount_));
322         ReportMeta(fileUploadMeta_);
323         return false;
324     }
325 
326     if (trackType == MediaType::MEDIA_TYPE_VID) {
327         trackInfo.uploadMeta.SetMeta(AV_KEY_HAS_VIDEO, "yes");
328     } else if (trackType == MediaType::MEDIA_TYPE_AUD) {
329         trackInfo.uploadMeta.SetMeta(AV_KEY_HAS_AUDIO, "yes");
330     }
331 
332     return true;
333 }
334 
OnEventProbe(GstPad & pad,GstEvent & event)335 void AVMetaElemMetaCollector::OnEventProbe(GstPad &pad, GstEvent &event)
336 {
337     std::unique_lock<std::mutex> lock(mutex_);
338     if (stopCollecting_) {
339         MEDIA_LOGI("stop collecting...");
340         return;
341     }
342 
343     auto it = trackInfos_.find(&pad);
344     CHECK_AND_RETURN_LOG(it != trackInfos_.end(), "unrecognized pad %{public}s", PAD_NAME(&pad));
345 
346     if (GST_EVENT_TYPE(&event) == GST_EVENT_TAG) {
347         QueryDuration(pad);
348 
349         GstTagList *tagList = nullptr;
350         gst_event_parse_tag(&event, &tagList);
351         CHECK_AND_RETURN_LOG(tagList != nullptr, "taglist is nullptr");
352         MEDIA_LOGI("catch tags at pad %{public}s", PAD_NAME(&pad));
353         ParseTagList(*tagList, it->second);
354     }
355 
356     if (GST_EVENT_TYPE(&event) == GST_EVENT_CAPS) {
357         GstCaps *caps = nullptr;
358         gst_event_parse_caps(&event, &caps);
359         CHECK_AND_RETURN_LOG(caps != nullptr, "caps is nullptr");
360         MEDIA_LOGI("catch caps at pad %{public}s", PAD_NAME(&pad));
361         ParseCaps(*caps, it->second);
362     }
363 }
364 
QueryDuration(GstPad & pad)365 void AVMetaElemMetaCollector::QueryDuration(GstPad &pad)
366 {
367     GstQuery *query = gst_query_new_duration(GST_FORMAT_TIME);
368     CHECK_AND_RETURN_LOG(query != nullptr, "query is failed");
369 
370     gint64 streamDuration = 0;
371     if (gst_pad_query(&pad, query)) {
372         GstFormat format = GST_FORMAT_TIME;
373         gst_query_parse_duration(query, &format, &streamDuration);
374         if (!GST_CLOCK_TIME_IS_VALID(streamDuration)) {
375             streamDuration = 0;
376         }
377     }
378     gst_query_unref(query);
379 
380     if (duration_ < streamDuration) {
381         duration_ = streamDuration;
382         MEDIA_LOGI("update duration to %{public}" PRIi64 "", duration_);
383 
384         static constexpr int32_t NASEC_PER_HALF_MILLISEC = 500000;
385         static constexpr int32_t NASEC_PER_MILLISEC = 1000000;
386 
387         int64_t milliSecond;
388         if ((std::numeric_limits<int64_t>::max() - NASEC_PER_HALF_MILLISEC) < duration_) {
389             milliSecond = duration_ / NASEC_PER_MILLISEC; // ns -> ms
390         } else {
391             milliSecond = (duration_ + NASEC_PER_HALF_MILLISEC) / NASEC_PER_MILLISEC; // ns -> ms, round up.
392         }
393 
394         fileUploadMeta_.SetMeta(AV_KEY_DURATION, std::to_string(milliSecond));
395         ReportMeta(fileUploadMeta_);
396     }
397 }
398 
ReportMeta(const Metadata & uploadMeta)399 void AVMetaElemMetaCollector::ReportMeta(const Metadata &uploadMeta)
400 {
401     if (resCb_ == nullptr) {
402         return;
403     }
404 
405     mutex_.unlock();
406     resCb_(uploadMeta);
407     mutex_.lock();
408 }
409 
ConvertToAVMeta(const Format & innerMeta,Metadata & avmeta) const410 void AVMetaElemMetaCollector::ConvertToAVMeta(const Format &innerMeta, Metadata &avmeta) const
411 {
412     for (const auto &[avKey, innerKey] : AVMETA_KEY_TO_X_MAP) {
413         if (innerKey.compare("") == 0) {
414             continue;
415         }
416 
417         if (innerKey.compare(INNER_META_KEY_MIME_TYPE) == 0) { // only need the file mime type
418             continue;
419         }
420 
421         if (!innerMeta.ContainKey(innerKey)) {
422             continue;
423         }
424 
425         std::string strVal;
426         int32_t intVal;
427         FormatDataType type = innerMeta.GetValueType(innerKey);
428         switch (type) {
429             case FORMAT_TYPE_STRING:
430                 innerMeta.GetStringValue(innerKey, strVal);
431                 avmeta.SetMeta(avKey, strVal);
432                 break;
433             case FORMAT_TYPE_INT32:
434                 innerMeta.GetIntValue(innerKey, intVal);
435                 avmeta.SetMeta(avKey, std::to_string(intVal));
436                 break;
437             default:
438                 break;
439         }
440     }
441 }
442 
443 /**
444  * Detail Element Meta Collector Implementation Begin.
445  */
446 
HaveTypeCallback(GstElement * elem,guint probability,GstCaps * caps,gpointer userdata)447 void TypeFindMetaCollector::HaveTypeCallback(GstElement *elem, guint probability, GstCaps *caps, gpointer userdata)
448 {
449     if (elem == nullptr || caps == nullptr || userdata == nullptr) {
450         return;
451     }
452 
453     MEDIA_LOGD("typefind %{public}s have type, probalibity = %{public}u", ELEM_NAME(elem), probability);
454 
455     TypeFindMetaCollector *collector = reinterpret_cast<TypeFindMetaCollector *>(userdata);
456     collector->OnHaveType(*elem, *caps);
457 }
458 
OnHaveType(const GstElement & elem,const GstCaps & caps)459 void TypeFindMetaCollector::OnHaveType(const GstElement &elem, const GstCaps &caps)
460 {
461     std::unique_lock<std::mutex> lock(mutex_);
462     if (stopCollecting_) {
463         MEDIA_LOGI("stop collecting...");
464         return;
465     }
466 
467     GstMetaParser::ParseFileMimeType(caps, fileInnerMeta_);
468 
469     std::string mimeType;
470     (void)fileInnerMeta_.GetStringValue(INNER_META_KEY_MIME_TYPE, mimeType);
471     fileUploadMeta_.SetMeta(AV_KEY_MIME_TYPE, mimeType);
472 
473     ReportMeta(fileUploadMeta_);
474 }
475 
AddMetaSource(GstElement & elem)476 void TypeFindMetaCollector::AddMetaSource(GstElement &elem)
477 {
478     (void)ConnectSignal(elem, "have-type", G_CALLBACK(&TypeFindMetaCollector::HaveTypeCallback));
479 }
480 
PadAddedCallback(GstElement * elem,GstPad * pad,gpointer userdata)481 void DemuxerMetaCollector::PadAddedCallback(GstElement *elem, GstPad *pad, gpointer userdata)
482 {
483     if (elem == nullptr || pad == nullptr || userdata == nullptr) {
484         return;
485     }
486 
487     auto collector = reinterpret_cast<DemuxerMetaCollector *>(userdata);
488     collector->OnPadAdded(*elem, *pad);
489 }
490 
OnPadAdded(GstElement & src,GstPad & pad)491 void DemuxerMetaCollector::OnPadAdded(GstElement &src, GstPad &pad)
492 {
493     MEDIA_LOGD("demuxer %{public}s sinkpad %{public}s added", ELEM_NAME(&src), PAD_NAME(&pad));
494     (void)AddProbeToPad(pad);
495 }
496 
AddMetaSource(GstElement & elem)497 void DemuxerMetaCollector::AddMetaSource(GstElement &elem)
498 {
499     if (!AddProbeToPadList(*elem.srcpads)) {
500         return;
501     }
502 
503     (void)ConnectSignal(elem, "pad-added", G_CALLBACK(&DemuxerMetaCollector::PadAddedCallback));
504 }
505 
AddMetaSource(GstElement & elem)506 void ParserMetaCollector::AddMetaSource(GstElement &elem)
507 {
508     (void)AddProbeToPadList(*elem.srcpads);
509 }
510 } // namespace Media
511 } // namespace OHOS