1 /*
2 * Copyright (C) 2023-2025 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 "subtitle_sink.h"
17
18 #include "common/log.h"
19 #include "syspara/parameters.h"
20 #include "meta/format.h"
21
22 namespace {
23 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "SubtitleSink" };
24 }
25
26 namespace OHOS {
27 namespace Media {
28 namespace {
29 constexpr bool SUBTITME_LOOP_RUNNING = true;
30 }
31
SubtitleSink()32 SubtitleSink::SubtitleSink()
33 {
34 MEDIA_LOG_I("SubtitleSink ctor");
35 syncerPriority_ = IMediaSynchronizer::SUBTITLE_SINK;
36 }
37
~SubtitleSink()38 SubtitleSink::~SubtitleSink()
39 {
40 MEDIA_LOG_I("SubtitleSink dtor");
41 {
42 std::unique_lock<std::mutex> lock(mutex_);
43 isThreadExit_ = true;
44 }
45 updateCond_.notify_all();
46 if (readThread_ != nullptr && readThread_->joinable()) {
47 readThread_->join();
48 readThread_ = nullptr;
49 }
50
51 if (inputBufferQueueProducer_ != nullptr) {
52 for (auto &buffer : inputBufferVector_) {
53 inputBufferQueueProducer_->DetachBuffer(buffer);
54 }
55 inputBufferVector_.clear();
56 inputBufferQueueProducer_->SetQueueSize(0);
57 }
58 }
59
NotifySeek()60 void SubtitleSink::NotifySeek()
61 {
62 Flush();
63 }
64
GetTargetSubtitleIndex(int64_t currentTime)65 void SubtitleSink::GetTargetSubtitleIndex(int64_t currentTime)
66 {
67 int32_t left = 0;
68 int32_t right = subtitleInfoVec_.size();
69 while (left < right) {
70 int32_t mid = (left + right) / 2;
71 int64_t startTime = subtitleInfoVec_[mid].pts_;
72 int64_t endTime = subtitleInfoVec_[mid].duration_ + startTime;
73 if (startTime > currentTime) {
74 right = mid;
75 continue;
76 } else if (endTime < currentTime) {
77 left = mid + 1;
78 continue;
79 } else {
80 left = mid;
81 break;
82 }
83 }
84 currentInfoIndex_ = left;
85 }
86
Init(std::shared_ptr<Meta> & meta,const std::shared_ptr<Pipeline::EventReceiver> & receiver)87 Status SubtitleSink::Init(std::shared_ptr<Meta> &meta, const std::shared_ptr<Pipeline::EventReceiver> &receiver)
88 {
89 state_ = Pipeline::FilterState::INITIALIZED;
90 if (meta != nullptr) {
91 meta->SetData(Tag::APP_PID, appPid_);
92 meta->SetData(Tag::APP_UID, appUid_);
93 }
94 return Status::OK;
95 }
96
GetBufferQueueProducer()97 sptr<AVBufferQueueProducer> SubtitleSink::GetBufferQueueProducer()
98 {
99 if (state_ != Pipeline::FilterState::READY) {
100 return nullptr;
101 }
102 return inputBufferQueueProducer_;
103 }
104
GetBufferQueueConsumer()105 sptr<AVBufferQueueConsumer> SubtitleSink::GetBufferQueueConsumer()
106 {
107 if (state_ != Pipeline::FilterState::READY) {
108 return nullptr;
109 }
110 return inputBufferQueueConsumer_;
111 }
112
SetParameter(const std::shared_ptr<Meta> & meta)113 Status SubtitleSink::SetParameter(const std::shared_ptr<Meta> &meta)
114 {
115 return Status::OK;
116 }
117
GetParameter(std::shared_ptr<Meta> & meta)118 Status SubtitleSink::GetParameter(std::shared_ptr<Meta> &meta)
119 {
120 return Status::OK;
121 }
122
Prepare()123 Status SubtitleSink::Prepare()
124 {
125 state_ = Pipeline::FilterState::PREPARING;
126 Status ret = PrepareInputBufferQueue();
127 if (ret != Status::OK) {
128 state_ = Pipeline::FilterState::INITIALIZED;
129 return ret;
130 }
131 state_ = Pipeline::FilterState::READY;
132 return ret;
133 }
134
Start()135 Status SubtitleSink::Start()
136 {
137 isEos_ = false;
138 state_ = Pipeline::FilterState::RUNNING;
139 readThread_ = std::make_unique<std::thread>(&SubtitleSink::RenderLoop, this);
140 pthread_setname_np(readThread_->native_handle(), "SubtitleRenderLoop");
141 return Status::OK;
142 }
143
Stop()144 Status SubtitleSink::Stop()
145 {
146 updateCond_.notify_all();
147 state_ = Pipeline::FilterState::INITIALIZED;
148 return Status::OK;
149 }
150
Pause()151 Status SubtitleSink::Pause()
152 {
153 state_ = Pipeline::FilterState::PAUSED;
154 return Status::OK;
155 }
156
Resume()157 Status SubtitleSink::Resume()
158 {
159 {
160 std::unique_lock<std::mutex> lock(mutex_);
161 isEos_ = false;
162 state_ = Pipeline::FilterState::RUNNING;
163 }
164 updateCond_.notify_all();
165 return Status::OK;
166 }
167
Flush()168 Status SubtitleSink::Flush()
169 {
170 {
171 std::unique_lock<std::mutex> lock(mutex_);
172 shouldUpdate_ = true;
173 if (!subtitleInfoVec_.empty()) {
174 subtitleInfoVec_.clear();
175 }
176 }
177 if (inputBufferQueueConsumer_ != nullptr) {
178 uint32_t queueSize = inputBufferQueueConsumer_->GetQueueSize();
179 std::shared_ptr<AVBuffer> filledOutputBuffer;
180 for (uint32_t i = 0; i < queueSize; i++) {
181 Status ret = inputBufferQueueConsumer_->AcquireBuffer(filledOutputBuffer);
182 if (ret != Status::OK || filledOutputBuffer == nullptr || filledOutputBuffer->memory_ == nullptr) {
183 break;
184 }
185 inputBufferQueueConsumer_->ReleaseBuffer(filledOutputBuffer);
186 }
187 }
188 isFlush_.store(true);
189 updateCond_.notify_all();
190 return Status::OK;
191 }
192
Release()193 Status SubtitleSink::Release()
194 {
195 return Status::OK;
196 }
197
SetIsTransitent(bool isTransitent)198 Status SubtitleSink::SetIsTransitent(bool isTransitent)
199 {
200 isTransitent_ = isTransitent;
201 return Status::OK;
202 }
203
PrepareInputBufferQueue()204 Status SubtitleSink::PrepareInputBufferQueue()
205 {
206 if (inputBufferQueue_ != nullptr && inputBufferQueue_->GetQueueSize() > 0) {
207 MEDIA_LOG_I("InputBufferQueue already create");
208 return Status::ERROR_INVALID_OPERATION;
209 }
210 int32_t inputBufferNum = 2;
211 int32_t capacity = 1024;
212 MemoryType memoryType = MemoryType::SHARED_MEMORY;
213 #ifndef MEDIA_OHOS
214 memoryType = MemoryType::VIRTUAL_MEMORY;
215 #endif
216 MEDIA_LOG_I("PrepareInputBufferQueue");
217 if (inputBufferQueue_ == nullptr) {
218 inputBufferQueue_ = AVBufferQueue::Create(inputBufferNum, memoryType, INPUT_BUFFER_QUEUE_NAME);
219 }
220 FALSE_RETURN_V_MSG_E(inputBufferQueue_ != nullptr, Status::ERROR_UNKNOWN, "inputBufferQueue_ is nullptr");
221
222 inputBufferQueueProducer_ = inputBufferQueue_->GetProducer();
223 inputBufferQueueConsumer_ = inputBufferQueue_->GetConsumer();
224
225 for (int i = 0; i < inputBufferNum; i++) {
226 std::shared_ptr<AVAllocator> avAllocator;
227 #ifndef MEDIA_OHOS
228 MEDIA_LOG_D("CreateVirtualAllocator,i=%{public}d capacity=%{public}d", i, capacity);
229 avAllocator = AVAllocatorFactory::CreateVirtualAllocator();
230 #else
231 MEDIA_LOG_D("CreateSharedAllocator,i=%{public}d capacity=%{public}d", i, capacity);
232 avAllocator = AVAllocatorFactory::CreateSharedAllocator(MemoryFlag::MEMORY_READ_WRITE);
233 #endif
234 std::shared_ptr<AVBuffer> inputBuffer = AVBuffer::CreateAVBuffer(avAllocator, capacity);
235 FALSE_RETURN_V_MSG_E(inputBuffer != nullptr, Status::ERROR_UNKNOWN,
236 "inputBuffer is nullptr");
237 FALSE_RETURN_V_MSG_E(inputBufferQueueProducer_ != nullptr, Status::ERROR_UNKNOWN,
238 "inputBufferQueueProducer_ is nullptr");
239 inputBufferQueueProducer_->AttachBuffer(inputBuffer, false);
240 MEDIA_LOG_I("Attach intput buffer. index: %{public}d, bufferId: %{public}" PRIu64,
241 i, inputBuffer->GetUniqueId());
242 inputBufferVector_.push_back(inputBuffer);
243 }
244 return Status::OK;
245 }
246
DrainOutputBuffer(bool flushed)247 void SubtitleSink::DrainOutputBuffer(bool flushed)
248 {
249 Status ret;
250 FALSE_RETURN(inputBufferQueueConsumer_ != nullptr);
251 FALSE_RETURN(!isEos_.load());
252 std::shared_ptr<AVBuffer> filledOutputBuffer;
253 ret = inputBufferQueueConsumer_->AcquireBuffer(filledOutputBuffer);
254 if (ret != Status::OK || filledOutputBuffer == nullptr || filledOutputBuffer->memory_ == nullptr) {
255 return;
256 }
257 if (filledOutputBuffer->flag_ & BUFFER_FLAG_EOS) {
258 isEos_ = true;
259 }
260 std::string subtitleText(reinterpret_cast<const char *>(filledOutputBuffer->memory_->GetAddr()),
261 filledOutputBuffer->memory_->GetSize());
262 SubtitleInfo subtitleInfo{ subtitleText, filledOutputBuffer->pts_, filledOutputBuffer->duration_ };
263 {
264 std::unique_lock<std::mutex> lock(mutex_);
265 subtitleInfoVec_.push_back(subtitleInfo);
266 inputBufferQueueConsumer_->ReleaseBuffer(filledOutputBuffer);
267 }
268 updateCond_.notify_all();
269 }
270
RenderLoop()271 void SubtitleSink::RenderLoop()
272 {
273 while (SUBTITME_LOOP_RUNNING) {
274 std::unique_lock<std::mutex> lock(mutex_);
275 updateCond_.wait(lock, [this] {
276 return isThreadExit_.load() ||
277 (!subtitleInfoVec_.empty() && state_ == Pipeline::FilterState::RUNNING);
278 });
279 if (isFlush_) {
280 MEDIA_LOG_I("SubtitleSink RenderLoop flush");
281 isFlush_.store(false);
282 continue;
283 }
284 FALSE_RETURN(!isThreadExit_.load());
285 // wait timeout, seek or stop
286 SubtitleInfo tempSubtitleInfo = subtitleInfoVec_.front();
287 SubtitleInfo subtitleInfo{ tempSubtitleInfo.text_, tempSubtitleInfo.pts_, tempSubtitleInfo.duration_ };
288 int64_t waitTime = CalcWaitTime(subtitleInfo);
289 updateCond_.wait_for(lock, std::chrono::microseconds(waitTime),
290 [this] { return isThreadExit_.load() || shouldUpdate_; });
291 MEDIA_LOG_I("SubtitleSink NotifyRender buffer. pts = " PUBLIC_LOG_D64 " waitTime: " PUBLIC_LOG_D64,
292 subtitleInfo.pts_, waitTime);
293 if (isFlush_) {
294 MEDIA_LOG_I("SubtitleSink RenderLoop flush");
295 isFlush_.store(false);
296 continue;
297 }
298 FALSE_RETURN(!isThreadExit_.load());
299 auto actionToDo = ActionToDo(subtitleInfo);
300 if (actionToDo == SubtitleBufferState::DROP) {
301 subtitleInfoVec_.pop_front();
302 continue;
303 } else if (actionToDo == SubtitleBufferState::WAIT) {
304 continue;
305 } else {}
306 NotifyRender(subtitleInfo);
307 subtitleInfoVec_.pop_front();
308 }
309 }
310
ResetSyncInfo()311 void SubtitleSink::ResetSyncInfo()
312 {
313 auto syncCenter = syncCenter_.lock();
314 if (syncCenter) {
315 syncCenter->Reset();
316 }
317 lastReportedClockTime_ = HST_TIME_NONE;
318 }
319
CalcWaitTime(SubtitleInfo & subtitleInfo)320 uint64_t SubtitleSink::CalcWaitTime(SubtitleInfo &subtitleInfo)
321 {
322 int64_t curTime;
323 if (shouldUpdate_.load()) {
324 shouldUpdate_ = false;
325 }
326 curTime = GetMediaTime();
327 if (subtitleInfo.pts_ < curTime) {
328 return 0;
329 }
330 return (subtitleInfo.pts_ - curTime) / speed_;
331 }
332
ActionToDo(SubtitleInfo & subtitleInfo)333 uint32_t SubtitleSink::ActionToDo(SubtitleInfo &subtitleInfo)
334 {
335 auto curTime = GetMediaTime();
336 if (shouldUpdate_ || subtitleInfo.pts_ + subtitleInfo.duration_ < curTime) {
337 return SubtitleBufferState::DROP;
338 }
339 if (subtitleInfo.pts_ > curTime || state_ != Pipeline::FilterState::RUNNING) {
340 return SubtitleBufferState::WAIT;
341 }
342 subtitleInfo.duration_ -= curTime - subtitleInfo.pts_;
343 return SubtitleBufferState::SHOW;
344 }
345
DoSyncWrite(const std::shared_ptr<OHOS::Media::AVBuffer> & buffer)346 int64_t SubtitleSink::DoSyncWrite(const std::shared_ptr<OHOS::Media::AVBuffer> &buffer)
347 {
348 (void)buffer;
349 return 0;
350 }
351
NotifyRender(SubtitleInfo & subtitleInfo)352 void SubtitleSink::NotifyRender(SubtitleInfo &subtitleInfo)
353 {
354 Format format;
355 (void)format.PutStringValue(Tag::SUBTITLE_TEXT, subtitleInfo.text_);
356 (void)format.PutIntValue(Tag::SUBTITLE_PTS, Plugins::Us2Ms(subtitleInfo.pts_));
357 (void)format.PutIntValue(Tag::SUBTITLE_DURATION, Plugins::Us2Ms(subtitleInfo.duration_));
358 Event event{ .srcFilter = "SubtitleSink", .type = EventType::EVENT_SUBTITLE_TEXT_UPDATE, .param = format };
359 FALSE_RETURN(playerEventReceiver_ != nullptr);
360 playerEventReceiver_->OnEvent(event);
361 }
362
OnInterrupted(bool isInterruptNeeded)363 void SubtitleSink::OnInterrupted(bool isInterruptNeeded)
364 {
365 MEDIA_LOG_I("onInterrupted %{public}d", isInterruptNeeded);
366 std::unique_lock<std::mutex> lock(mutex_);
367 isInterruptNeeded_ = isInterruptNeeded;
368 isThreadExit_ = true;
369 updateCond_.notify_all();
370 }
371
SetEventReceiver(const std::shared_ptr<Pipeline::EventReceiver> & receiver)372 void SubtitleSink::SetEventReceiver(const std::shared_ptr<Pipeline::EventReceiver> &receiver)
373 {
374 FALSE_RETURN(receiver != nullptr);
375 playerEventReceiver_ = receiver;
376 }
377
SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)378 void SubtitleSink::SetSyncCenter(std::shared_ptr<Pipeline::MediaSyncManager> syncCenter)
379 {
380 syncCenter_ = syncCenter;
381 MediaSynchronousSink::Init();
382 }
383
SetSpeed(float speed)384 Status SubtitleSink::SetSpeed(float speed)
385 {
386 FALSE_RETURN_V_MSG_W(speed > 0, Status::OK, "Invalid speed %{public}f", speed);
387 {
388 std::unique_lock<std::mutex> lock(mutex_);
389 speed_ = speed;
390 shouldUpdate_ = true;
391 }
392 updateCond_.notify_all();
393 return Status::OK;
394 }
395
GetMediaTime()396 int64_t SubtitleSink::GetMediaTime()
397 {
398 auto syncCenter = syncCenter_.lock();
399 if (!syncCenter) {
400 return 0;
401 }
402 return syncCenter->GetMediaTimeNow();
403 }
404 } // namespace MEDIA
405 } // namespace OHOS