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 "player_sinkprovider.h"
17 #include "securec.h"
18 #include "display_type.h"
19 #include "param_wrapper.h"
20 #include "gst/video/gstvideometa.h"
21 #include "media_log.h"
22 #include "media_errors.h"
23 #include "media_dfx.h"
24
25 namespace {
26 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "PlayerSinkProvider"};
27 constexpr uint32_t DEFAULT_BUFFER_NUM = 8;
28 }
29
30 namespace OHOS {
31 namespace Media {
PlayerSinkProvider(const sptr<Surface> & surface)32 PlayerSinkProvider::PlayerSinkProvider(const sptr<Surface> &surface)
33 : producerSurface_(surface)
34 {
35 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
36 }
37
~PlayerSinkProvider()38 PlayerSinkProvider::~PlayerSinkProvider()
39 {
40 MEDIA_LOGD("enter dtor, instance: 0x%{public}06" PRIXPTR "", FAKE_POINTER(this));
41
42 producerSurface_ = nullptr;
43 if (audioSink_ != nullptr) {
44 gst_object_unref(audioSink_);
45 audioSink_ = nullptr;
46 }
47 if (videoSink_ != nullptr) {
48 gst_object_unref(videoSink_);
49 videoSink_ = nullptr;
50 }
51 if (subtitleSink_ != nullptr) {
52 gst_object_unref(subtitleSink_);
53 subtitleSink_ = nullptr;
54 }
55 if (audioCaps_ != nullptr) {
56 gst_caps_unref(audioCaps_);
57 audioCaps_ = nullptr;
58 }
59 if (videoCaps_ != nullptr) {
60 gst_caps_unref(videoCaps_);
61 videoCaps_ = nullptr;
62 }
63 }
64
CreateAudioSink()65 PlayBinSinkProvider::SinkPtr PlayerSinkProvider::CreateAudioSink()
66 {
67 constexpr gint rate = 44100;
68 constexpr gint channels = 2;
69
70 if (audioSink_ != nullptr) {
71 gst_object_unref(audioSink_);
72 audioSink_ = nullptr;
73 }
74 if (audioCaps_ == nullptr) {
75 audioCaps_ = gst_caps_new_simple("audio/x-raw",
76 "format", G_TYPE_STRING, "S16LE",
77 "rate", G_TYPE_INT, rate,
78 "channels", G_TYPE_INT, channels, nullptr);
79 CHECK_AND_RETURN_RET_LOG(audioCaps_ != nullptr, nullptr, "gst_caps_new_simple failed..");
80
81 audioSink_ = DoCreateAudioSink(audioCaps_, reinterpret_cast<gpointer>(this));
82 CHECK_AND_RETURN_RET_LOG(audioSink_ != nullptr, nullptr, "CreateAudioSink failed..");
83 }
84
85 return audioSink_;
86 }
87
EnableOptRenderDelay() const88 bool PlayerSinkProvider::EnableOptRenderDelay() const
89 {
90 std::string enable;
91 int32_t res = OHOS::system::GetStringParameter("sys.media.kpi.opt.renderdelay.enable", enable, "");
92 if (res != 0 || enable.empty()) {
93 MEDIA_LOGW("KPI-TRACE: get value fail, default enable");
94 return true;
95 }
96
97 MEDIA_LOGI("KPI-TRACE: sys.media.kpi.opt.renderdelay.enable=%{public}s", enable.c_str());
98 if (enable != "true") {
99 return false;
100 }
101 return true;
102 }
103
DoCreateAudioSink(const GstCaps * caps,const gpointer userData)104 GstElement *PlayerSinkProvider::DoCreateAudioSink(const GstCaps *caps, const gpointer userData)
105 {
106 (void)caps;
107 MEDIA_LOGI("CreateAudioSink in.");
108 CHECK_AND_RETURN_RET_LOG(userData != nullptr, nullptr, "input userData is nullptr..");
109
110 auto sink = GST_ELEMENT_CAST(gst_object_ref_sink(gst_element_factory_make("audioserversink", nullptr)));
111 CHECK_AND_RETURN_RET_LOG(sink != nullptr, nullptr, "gst_element_factory_make failed..");
112
113 g_object_set(G_OBJECT(sink), "app-uid", uid_, nullptr);
114 g_object_set(G_OBJECT(sink), "app-pid", pid_, nullptr);
115 g_object_set(G_OBJECT(sink), "app-token-id", tokenId_, nullptr);
116
117 gboolean enable = static_cast<gboolean>(EnableOptRenderDelay());
118 g_object_set(G_OBJECT(sink), "enable-opt-render-delay", enable, nullptr);
119
120 GstPad *pad = gst_element_get_static_pad(sink, "sink");
121 if (pad == nullptr) {
122 gst_object_unref(sink);
123 MEDIA_LOGE("gst_element_get_static_pad failed..");
124 return nullptr;
125 }
126
127 (void)gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM,
128 PlayerSinkProvider::SinkPadProbeCb, userData, nullptr);
129 gst_object_unref(pad);
130 return sink;
131 }
132
EnableKpiAVSyncLog() const133 bool PlayerSinkProvider::EnableKpiAVSyncLog() const
134 {
135 std::string enable;
136 int32_t res = OHOS::system::GetStringParameter("sys.media.kpi.avsync.log.enable", enable, "");
137 if (res != 0 || enable.empty()) {
138 return false;
139 }
140
141 MEDIA_LOGI("KPI-TRACE: sys.media.kpi.avsync.log.enable=%{public}s", enable.c_str());
142 if (enable != "true") {
143 return false;
144 }
145 return true;
146 }
147
CreateVideoSink()148 PlayBinSinkProvider::SinkPtr PlayerSinkProvider::CreateVideoSink()
149 {
150 CHECK_AND_RETURN_RET_LOG(producerSurface_ != nullptr, nullptr,
151 "producerSurface_ is nullptr, cannot create video sink!");
152 if (videoCaps_ == nullptr) {
153 videoCaps_ = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "RGBA", nullptr);
154 CHECK_AND_RETURN_RET_LOG(videoCaps_ != nullptr, nullptr, "gst_caps_new_simple failed..");
155
156 videoSink_ = DoCreateVideoSink(videoCaps_, reinterpret_cast<gpointer>(this));
157 CHECK_AND_RETURN_RET_LOG(videoSink_ != nullptr, nullptr, "CreateVideoSink failed..");
158 }
159
160 if (audioSink_ != nullptr) {
161 MEDIA_LOGI("KPI-TRACE: set audio sink to video sink");
162 gboolean enable = static_cast<gboolean>(EnableKpiAVSyncLog());
163 g_object_set(G_OBJECT(videoSink_), "audio-sink", audioSink_, "enable-kpi-avsync-log", enable, nullptr);
164 }
165
166 (void)producerSurface_->SetQueueSize(DEFAULT_BUFFER_NUM);
167 queueSize_ = DEFAULT_BUFFER_NUM;
168
169 return videoSink_;
170 }
171
DoCreateVideoSink(const GstCaps * caps,const gpointer userData)172 GstElement *PlayerSinkProvider::DoCreateVideoSink(const GstCaps *caps, const gpointer userData)
173 {
174 MEDIA_LOGI("CreateVideoSink in.");
175 CHECK_AND_RETURN_RET_LOG(caps != nullptr, nullptr, "input caps is nullptr..");
176 CHECK_AND_RETURN_RET_LOG(userData != nullptr, nullptr, "input userData is nullptr..");
177 PlayerSinkProvider *sinkProvider = reinterpret_cast<PlayerSinkProvider *>(userData);
178
179 auto sink = GST_ELEMENT_CAST(gst_object_ref_sink(gst_element_factory_make("videodisplaysink", "sink")));
180 CHECK_AND_RETURN_RET_LOG(sink != nullptr, nullptr, "gst_element_factory_make failed..");
181 gst_base_sink_set_async_enabled(GST_BASE_SINK(sink), FALSE);
182
183 g_object_set(G_OBJECT(sink), "caps", caps, nullptr);
184 g_object_set(G_OBJECT(sink), "surface", static_cast<gpointer>(sinkProvider->GetProducerSurface()), nullptr);
185 g_object_set(G_OBJECT(sink), "video-scale-type", videoScaleType_, nullptr);
186
187 GstMemSinkCallbacks sinkCallbacks = { PlayerSinkProvider::EosCb, PlayerSinkProvider::NewPrerollCb,
188 PlayerSinkProvider::NewSampleCb };
189 gst_mem_sink_set_callback(GST_MEM_SINK(sink), &sinkCallbacks, userData, nullptr);
190
191 return sink;
192 }
193
GetVideoSink()194 PlayBinSinkProvider::SinkPtr PlayerSinkProvider::GetVideoSink()
195 {
196 CHECK_AND_RETURN_RET_LOG(videoSink_ != nullptr, nullptr, "videoSink is nullptr");
197 return GST_ELEMENT_CAST(videoSink_);
198 }
199
FirstRenderFrame(gpointer userData)200 void PlayerSinkProvider::FirstRenderFrame(gpointer userData)
201 {
202 CHECK_AND_RETURN_LOG(userData != nullptr, "input userData is nullptr..");
203 PlayerSinkProvider *sinkProvider = reinterpret_cast<PlayerSinkProvider *>(userData);
204
205 sinkProvider->OnFirstRenderFrame();
206 }
207
OnFirstRenderFrame()208 void PlayerSinkProvider::OnFirstRenderFrame()
209 {
210 std::unique_lock<std::mutex> lock(mutex_);
211 CHECK_AND_RETURN(notifier_ != nullptr);
212 if (GetFirstRenderFrameFlag()) {
213 PlayBinMessage msg { PLAYBIN_MSG_SUBTYPE, PLAYBIN_SUB_MSG_VIDEO_RENDERING_START, 0, {} };
214 notifier_(msg);
215 SetFirstRenderFrameFlag(false);
216 MEDIA_LOGW("KPI-TRACE: FIRST-VIDEO-FRAME rendered");
217 }
218 }
219
CreateSubtitleSink()220 PlayBinSinkProvider::SinkPtr PlayerSinkProvider::CreateSubtitleSink()
221 {
222 if (subtitleSink_ != nullptr) {
223 gst_object_unref(subtitleSink_);
224 subtitleSink_ = nullptr;
225 }
226 subtitleSink_ = DoCreateSubtitleSink(reinterpret_cast<gpointer>(this));
227 CHECK_AND_RETURN_RET_LOG(subtitleSink_ != nullptr, nullptr, "CreateSubtitleSink failed..");
228 g_object_set(G_OBJECT(subtitleSink_), "audio-sink", audioSink_, nullptr);
229 return subtitleSink_;
230 }
231
DoCreateSubtitleSink(const gpointer userData)232 GstElement *PlayerSinkProvider::DoCreateSubtitleSink(const gpointer userData)
233 {
234 MEDIA_LOGI("CreateSubtitleSink in.");
235 CHECK_AND_RETURN_RET_LOG(userData != nullptr, nullptr, "input userData is nullptr..");
236
237 auto sink = GST_ELEMENT_CAST(gst_object_ref_sink(gst_element_factory_make("subtitledisplaysink", nullptr)));
238 CHECK_AND_RETURN_RET_LOG(sink != nullptr, nullptr, "gst_element_factory_make failed..");
239
240 GstSubtitleSinkCallbacks sinkCallbacks = { PlayerSinkProvider::SubtitleUpdated };
241 gst_subtitle_sink_set_callback(GST_SUBTITLE_SINK(sink), &sinkCallbacks, userData, nullptr);
242 MEDIA_LOGI("CreateSubtitleSink out.");
243 return sink;
244 }
245
HandleSubtitleBuffer(GstBuffer * sample,Format & subtitle)246 void PlayerSinkProvider::HandleSubtitleBuffer(GstBuffer *sample, Format &subtitle)
247 {
248 if (sample == nullptr) {
249 (void)subtitle.PutStringValue(PlayerKeys::SUBTITLE_TEXT, "");
250 return;
251 }
252 GstMapInfo mapInfo;
253 CHECK_AND_RETURN(gst_buffer_map(sample, &mapInfo, GST_MAP_READ));
254 uint32_t gstBufferSize = static_cast<uint32_t>(gst_buffer_get_size(sample));
255 char *textFrame = new (std::nothrow) char[gstBufferSize + 1];
256 (void)memcpy_s(textFrame, gstBufferSize + 1, mapInfo.data, gstBufferSize);
257 textFrame[gstBufferSize] = static_cast<char>(0);
258 (void)subtitle.PutStringValue(PlayerKeys::SUBTITLE_TEXT, std::string_view(textFrame));
259 MEDIA_LOGD("text = %{public}s", textFrame);
260 delete[] textFrame;
261 gst_buffer_unmap(sample, &mapInfo);
262 }
263
SubtitleUpdated(GstBuffer * sample,gpointer userData)264 GstFlowReturn PlayerSinkProvider::SubtitleUpdated(GstBuffer *sample, gpointer userData)
265 {
266 MediaTrace trace("PlayerSinkProvider::SubtitleUpdated");
267 CHECK_AND_RETURN_RET(userData != nullptr, GST_FLOW_ERROR);
268 PlayerSinkProvider *sinkProvider = reinterpret_cast<PlayerSinkProvider *>(userData);
269 Format subtitle;
270 sinkProvider->HandleSubtitleBuffer(sample, subtitle);
271 sinkProvider->OnSubtitleUpdated(subtitle);
272 return GST_FLOW_OK;
273 }
274
OnSubtitleUpdated(const Format & subtitle)275 void PlayerSinkProvider::OnSubtitleUpdated(const Format &subtitle)
276 {
277 std::unique_lock<std::mutex> lock(mutex_);
278 MEDIA_LOGD("OnSubtitleUpdated enter");
279 auto temp_notifier = notifier_;
280 lock.unlock();
281 CHECK_AND_RETURN(temp_notifier != nullptr);
282 PlayBinMessage msg = {PLAYBIN_MSG_SUBTYPE, PLAYBIN_SUB_MSG_SUBTITLE_UPDATED, 0, subtitle};
283 temp_notifier(msg);
284 MEDIA_LOGD("OnSubtitleUpdated exit");
285 }
286
EosCb(GstMemSink * memSink,gpointer userData)287 void PlayerSinkProvider::EosCb(GstMemSink *memSink, gpointer userData)
288 {
289 (void)memSink;
290 (void)userData;
291 MEDIA_LOGI("EOS in");
292 }
293
NewPrerollCb(GstMemSink * memSink,GstBuffer * sample,gpointer userData)294 GstFlowReturn PlayerSinkProvider::NewPrerollCb(GstMemSink *memSink, GstBuffer *sample, gpointer userData)
295 {
296 (void)userData;
297 MEDIA_LOGI("NewPrerollCb in");
298 MediaTrace trace("PlayerSinkProvider::NewPrerollCb");
299 CHECK_AND_RETURN_RET(gst_mem_sink_app_preroll_render(memSink, sample) == GST_FLOW_OK, GST_FLOW_ERROR);
300 return GST_FLOW_OK;
301 }
302
NewSampleCb(GstMemSink * memSink,GstBuffer * sample,gpointer userData)303 GstFlowReturn PlayerSinkProvider::NewSampleCb(GstMemSink *memSink, GstBuffer *sample, gpointer userData)
304 {
305 MEDIA_LOGI("NewSampleCb in");
306 MediaTrace trace("PlayerSinkProvider::NewSampleCb");
307 CHECK_AND_RETURN_RET(gst_mem_sink_app_render(memSink, sample) == GST_FLOW_OK, GST_FLOW_ERROR);
308
309 FirstRenderFrame(userData);
310 return GST_FLOW_OK;
311 }
312
SinkPadProbeCb(GstPad * pad,GstPadProbeInfo * info,gpointer userData)313 GstPadProbeReturn PlayerSinkProvider::SinkPadProbeCb(GstPad *pad, GstPadProbeInfo *info, gpointer userData)
314 {
315 (void)pad;
316 (void)userData;
317 GstQuery *query = GST_PAD_PROBE_INFO_QUERY(info);
318 if (GST_QUERY_TYPE(query) == GST_QUERY_ALLOCATION) {
319 GstCaps *caps = nullptr;
320 gboolean needPool;
321 gst_query_parse_allocation(query, &caps, &needPool);
322
323 auto s = gst_caps_get_structure(caps, 0);
324 auto mediaType = gst_structure_get_name(s);
325 gboolean isVideo = g_str_has_prefix(mediaType, "video/");
326 if (isVideo) {
327 gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr);
328 }
329 }
330 return GST_PAD_PROBE_OK;
331 }
332
SetMsgNotifier(PlayBinMsgNotifier notifier)333 void PlayerSinkProvider::SetMsgNotifier(PlayBinMsgNotifier notifier)
334 {
335 std::unique_lock<std::mutex> lock(mutex_);
336 notifier_ = notifier;
337 }
338
SetFirstRenderFrameFlag(bool firstRenderFrame)339 void PlayerSinkProvider::SetFirstRenderFrameFlag(bool firstRenderFrame)
340 {
341 firstRenderFrame_ = firstRenderFrame;
342 }
343
GetFirstRenderFrameFlag() const344 bool PlayerSinkProvider::GetFirstRenderFrameFlag() const
345 {
346 return firstRenderFrame_;
347 }
348
GetProducerSurface() const349 const sptr<Surface> PlayerSinkProvider::GetProducerSurface() const
350 {
351 return producerSurface_;
352 }
353
SetVideoScaleType(const uint32_t videoScaleType)354 void PlayerSinkProvider::SetVideoScaleType(const uint32_t videoScaleType)
355 {
356 if (videoSink_ != nullptr) {
357 g_object_set(videoSink_, "video-scale-type", videoScaleType, nullptr);
358 } else {
359 videoScaleType_ = videoScaleType;
360 }
361 }
362
SetAppInfo(int32_t uid,int32_t pid,uint32_t tokenId)363 void PlayerSinkProvider::SetAppInfo(int32_t uid, int32_t pid, uint32_t tokenId)
364 {
365 uid_ = uid;
366 pid_ = pid;
367 tokenId_ = tokenId;
368 }
369 }
370 }
371