1 /*
2 * Copyright (c) 2024-2024 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 #define HST_LOG_TAG "StreamDemuxer"
17
18 #include "stream_demuxer.h"
19
20 #include <algorithm>
21 #include <map>
22 #include <memory>
23
24 #include "avcodec_common.h"
25 #include "avcodec_trace.h"
26 #include "cpp_ext/type_traits_ext.h"
27 #include "buffer/avallocator.h"
28 #include "common/event.h"
29 #include "common/log.h"
30 #include "meta/media_types.h"
31 #include "meta/meta.h"
32 #include "osal/utils/dump_buffer.h"
33 #include "plugin/plugin_buffer.h"
34 #include "plugin/plugin_info.h"
35 #include "plugin/plugin_time.h"
36 #include "source/source.h"
37
38 namespace {
39 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "StreamDemuxer" };
40 }
41
42 namespace OHOS {
43 namespace Media {
44
45 const int32_t TRY_READ_SLEEP_TIME = 10; //ms
46 const int32_t TRY_READ_TIMES = 10;
47 constexpr uint64_t LIVE_CONTENT_LENGTH = 2147483646;
StreamDemuxer()48 StreamDemuxer::StreamDemuxer() : position_(0)
49 {
50 MEDIA_LOG_I("VodStreamDemuxer called");
51 }
52
~StreamDemuxer()53 StreamDemuxer::~StreamDemuxer()
54 {
55 MEDIA_LOG_I("~VodStreamDemuxer called");
56 ResetAllCache();
57 }
58
ReadFrameData(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Buffer> & bufferPtr)59 Status StreamDemuxer::ReadFrameData(int32_t streamID, uint64_t offset, size_t size,
60 std::shared_ptr<Buffer>& bufferPtr)
61 {
62 if (IsDash()) {
63 MEDIA_LOG_D("GetPeekRange read cache, offset: " PUBLIC_LOG_U64, offset);
64 if (cacheDataMap_.find(streamID) != cacheDataMap_.end() && cacheDataMap_[streamID].CheckCacheExist(offset)) {
65 MEDIA_LOG_D("GetPeekRange read cache, offset: " PUBLIC_LOG_U64, offset);
66 auto memory = cacheDataMap_[streamID].GetData()->GetMemory();
67 if (memory != nullptr && memory->GetSize() > 0) {
68 MEDIA_LOG_D("GetPeekRange read cache, Read data from cache data.");
69 return PullDataWithCache(streamID, offset, size, bufferPtr);
70 }
71 }
72 }
73 return PullData(streamID, offset, size, bufferPtr);
74 }
75
ReadHeaderData(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Buffer> & bufferPtr)76 Status StreamDemuxer::ReadHeaderData(int32_t streamID, uint64_t offset, size_t size,
77 std::shared_ptr<Buffer>& bufferPtr)
78 {
79 if (cacheDataMap_.find(streamID) != cacheDataMap_.end() && cacheDataMap_[streamID].CheckCacheExist(offset)) {
80 MEDIA_LOG_D("GetPeekRange read cache, offset: " PUBLIC_LOG_U64, offset);
81 auto memory = cacheDataMap_[streamID].GetData()->GetMemory();
82 if (memory != nullptr && memory->GetSize() > 0) {
83 MEDIA_LOG_D("GetPeekRange read cache, Read data from cache data.");
84 return PullDataWithCache(streamID, offset, size, bufferPtr);
85 }
86 }
87 return PullDataWithoutCache(streamID, offset, size, bufferPtr);
88 }
89
GetPeekRange(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Buffer> & bufferPtr)90 Status StreamDemuxer::GetPeekRange(int32_t streamID, uint64_t offset, size_t size, std::shared_ptr<Buffer>& bufferPtr)
91 {
92 FALSE_RETURN_V_MSG_E(!isInterruptNeeded_.load(), Status::ERROR_WRONG_STATE,
93 "GetPeekRange interrupt " PUBLIC_LOG_D32 " " PUBLIC_LOG_U64 " " PUBLIC_LOG_ZU, streamID, offset, size);
94 if (bufferPtr == nullptr) {
95 MEDIA_LOG_E("GetPeekRange bufferPtr invalid.");
96 return Status::ERROR_INVALID_PARAMETER;
97 }
98 bufferPtr->streamID = streamID;
99 if (pluginStateMap_[streamID] == DemuxerState::DEMUXER_STATE_PARSE_FRAME) {
100 return ReadFrameData(streamID, offset, size, bufferPtr);
101 }
102 return ReadHeaderData(streamID, offset, size, bufferPtr);
103 }
104
Init(const std::string & uri)105 Status StreamDemuxer::Init(const std::string& uri)
106 {
107 MediaAVCodec::AVCodecTrace trace("StreamDemuxer::Init");
108 MEDIA_LOG_I("StreamDemuxer::Init called");
109 checkRange_ = [](int32_t streamID, uint64_t offset, uint32_t size) {
110 return Status::OK;
111 };
112 peekRange_ = [this](int32_t streamID, uint64_t offset, size_t size, std::shared_ptr<Buffer>& bufferPtr) -> Status {
113 return GetPeekRange(streamID, offset, size, bufferPtr);
114 };
115 getRange_ = peekRange_;
116 uri_ = uri;
117 return Status::OK;
118 }
119
PullDataWithCache(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Buffer> & bufferPtr)120 Status StreamDemuxer::PullDataWithCache(int32_t streamID, uint64_t offset, size_t size,
121 std::shared_ptr<Buffer>& bufferPtr)
122 {
123 FALSE_RETURN_V_MSG_E(bufferPtr->GetMemory() != nullptr, Status::ERROR_UNKNOWN, "bufferPtr invalid");
124 auto memory = cacheDataMap_[streamID].GetData()->GetMemory();
125 FALSE_RETURN_V_MSG_E(memory != nullptr, Status::ERROR_UNKNOWN, "memory invalid");
126 MEDIA_LOG_D("PullDataWithCache, Read data from cache data.");
127 uint64_t offsetInCache = offset - cacheDataMap_[streamID].GetOffset();
128 if (size <= memory->GetSize() - offsetInCache) {
129 bufferPtr->GetMemory()->Write(memory->GetReadOnlyData() + offsetInCache, size, 0);
130 return Status::OK;
131 }
132 bufferPtr->GetMemory()->Write(memory->GetReadOnlyData() + offsetInCache, memory->GetSize() - offsetInCache, 0);
133 uint64_t remainOffset = cacheDataMap_[streamID].GetOffset() + memory->GetSize();
134 uint64_t remainSize = size - (memory->GetSize() - offsetInCache);
135 std::shared_ptr<Buffer> tempBuffer = Buffer::CreateDefaultBuffer(remainSize);
136 if (tempBuffer == nullptr || tempBuffer->GetMemory() == nullptr) {
137 MEDIA_LOG_W("PullDataWithCache, Read data from cache data. only get partial data.");
138 return Status::ERROR_UNKNOWN;
139 }
140 Status ret = PullData(streamID, remainOffset, remainSize, tempBuffer);
141 if (ret == Status::OK) {
142 FALSE_RETURN_V_MSG_E(tempBuffer->GetMemory() != nullptr, Status::ERROR_UNKNOWN, "tempBuffer invalid");
143 bufferPtr->GetMemory()->Write(tempBuffer->GetMemory()->GetReadOnlyData(),
144 tempBuffer->GetMemory()->GetSize(), memory->GetSize() - offsetInCache);
145 if (pluginStateMap_[streamID] == DemuxerState::DEMUXER_STATE_PARSE_FRAME) {
146 MEDIA_LOG_W("PullDataWithCache, not cache begin.");
147 return ret;
148 }
149 std::shared_ptr<Buffer> mergedBuffer = Buffer::CreateDefaultBuffer(
150 tempBuffer->GetMemory()->GetSize() + memory->GetSize());
151 FALSE_RETURN_V_MSG_E(mergedBuffer != nullptr, Status::ERROR_UNKNOWN, "mergedBuffer invalid");
152 FALSE_RETURN_V_MSG_E(mergedBuffer->GetMemory() != nullptr, Status::ERROR_UNKNOWN,
153 "mergedBuffer->GetMemory invalid");
154 mergedBuffer->GetMemory()->Write(memory->GetReadOnlyData(), memory->GetSize(), 0);
155 mergedBuffer->GetMemory()->Write(tempBuffer->GetMemory()->GetReadOnlyData(),
156 tempBuffer->GetMemory()->GetSize(), memory->GetSize());
157 cacheDataMap_[streamID].SetData(mergedBuffer);
158 memory = cacheDataMap_[streamID].GetData()->GetMemory();
159 FALSE_RETURN_V_MSG_E(memory != nullptr, Status::ERROR_UNKNOWN, "memory invalid");
160 MEDIA_LOG_I("PullDataWithCache, offset: " PUBLIC_LOG_U64 ", cache offset: " PUBLIC_LOG_U64
161 ", cache size: " PUBLIC_LOG_ZU, offset, cacheDataMap_[streamID].GetOffset(), memory->GetSize());
162 }
163 return ret;
164 }
165
ProcInnerDash(int32_t streamID,uint64_t offset,std::shared_ptr<Buffer> & bufferPtr)166 Status StreamDemuxer::ProcInnerDash(int32_t streamID, uint64_t offset, std::shared_ptr<Buffer>& bufferPtr)
167 {
168 FALSE_RETURN_V_MSG_E(bufferPtr != nullptr, Status::ERROR_UNKNOWN, "bufferPtr invalid");
169 if (IsDash()) {
170 MEDIA_LOG_D("dash PullDataWithoutCache, cacheDataMap_ exist streamID , merge it.");
171 FALSE_RETURN_V_MSG_E(cacheDataMap_[streamID].GetData() != nullptr, Status::ERROR_UNKNOWN, "getdata invalid");
172 auto cacheMemory = cacheDataMap_[streamID].GetData()->GetMemory();
173 auto bufferMemory = bufferPtr->GetMemory();
174 FALSE_RETURN_V_MSG_E(bufferMemory != nullptr, Status::ERROR_UNKNOWN, "bufferPtr invalid");
175 FALSE_RETURN_V_MSG_E(cacheMemory != nullptr, Status::ERROR_UNKNOWN, "cacheMemory invalid");
176 std::shared_ptr<Buffer> mergedBuffer = Buffer::CreateDefaultBuffer(
177 bufferMemory->GetSize() + cacheMemory->GetSize());
178 FALSE_RETURN_V_MSG_E(mergedBuffer != nullptr, Status::ERROR_UNKNOWN, "mergedBuffer invalid");
179 auto mergeMemory = mergedBuffer->GetMemory();
180 FALSE_RETURN_V_MSG_E(mergeMemory != nullptr, Status::ERROR_UNKNOWN, "mergeMemory invalid");
181 MEDIA_LOG_I("dash PullDataWithoutCache merge before: cache offset: " PUBLIC_LOG_U64
182 ", cache size: " PUBLIC_LOG_ZU, cacheDataMap_[streamID].GetOffset(), cacheMemory->GetSize());
183 mergeMemory->Write(cacheMemory->GetReadOnlyData(), cacheMemory->GetSize(), 0);
184 mergeMemory->Write(bufferMemory->GetReadOnlyData(), bufferMemory->GetSize(), cacheMemory->GetSize());
185 cacheDataMap_[streamID].SetData(mergedBuffer);
186 MEDIA_LOG_I("dash PullDataWithoutCache merge after: " PUBLIC_LOG_U64 ", cache offset: " PUBLIC_LOG_U64,
187 offset, cacheDataMap_[streamID].GetOffset());
188 }
189 return Status::OK;
190 }
191
PullDataWithoutCache(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Buffer> & bufferPtr)192 Status StreamDemuxer::PullDataWithoutCache(int32_t streamID, uint64_t offset, size_t size,
193 std::shared_ptr<Buffer>& bufferPtr)
194 {
195 Status ret = PullData(streamID, offset, size, bufferPtr);
196 if (ret != Status::OK) {
197 MEDIA_LOG_E("PullDataWithoutCache, PullData error " PUBLIC_LOG_D32, static_cast<int32_t>(ret));
198 return ret;
199 }
200 if (cacheDataMap_.find(streamID) != cacheDataMap_.end()) {
201 MEDIA_LOG_D("PullDataWithoutCache, cacheDataMap_ exist streamID , do nothing.");
202 ret = ProcInnerDash(streamID, offset, bufferPtr);
203 if (ret != Status::OK) {
204 MEDIA_LOG_E("ProcInnerDash error " PUBLIC_LOG_D32, static_cast<int32_t>(ret));
205 return ret;
206 }
207 } else {
208 CacheData cacheTmp;
209 cacheDataMap_[streamID] = cacheTmp;
210 }
211 if (cacheDataMap_[streamID].GetData() == nullptr || cacheDataMap_[streamID].GetData()->GetMemory() == nullptr) {
212 MEDIA_LOG_D("PullDataWithoutCache, write cache data.");
213 if (bufferPtr->GetMemory() == nullptr) {
214 MEDIA_LOG_W("PullDataWithoutCache, write cache data error. memory is nullptr!");
215 } else {
216 auto buffer = Buffer::CreateDefaultBuffer(bufferPtr->GetMemory()->GetSize());
217 if (buffer != nullptr && buffer->GetMemory() != nullptr) {
218 buffer->GetMemory()->Write(bufferPtr->GetMemory()->GetReadOnlyData(),
219 bufferPtr->GetMemory()->GetSize(), 0);
220 cacheDataMap_[streamID].Init(buffer, offset);
221 MEDIA_LOG_D("PullDataWithoutCache, write cache data success. offset=" PUBLIC_LOG_U64, offset);
222 } else {
223 MEDIA_LOG_W("PullDataWithoutCache, write cache data failed. memory is nullptr!");
224 }
225 }
226 }
227 return ret;
228 }
229
ReadRetry(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Plugins::Buffer> & data)230 Status StreamDemuxer::ReadRetry(int32_t streamID, uint64_t offset, size_t size,
231 std::shared_ptr<Plugins::Buffer>& data)
232 {
233 FALSE_RETURN_V_MSG_E(data->GetMemory() != nullptr, Status::ERROR_UNKNOWN, "getmemory invalid");
234 Status err = Status::OK;
235 int32_t retryTimes = 0;
236 while (true && !isInterruptNeeded_.load()) {
237 err = source_->Read(streamID, data, offset, size);
238 if (IsDash() && streamID != data->streamID) {
239 break;
240 }
241 if (err != Status::END_OF_STREAM && data->GetMemory()->GetSize() == 0) {
242 OSAL::SleepFor(TRY_READ_SLEEP_TIME);
243 retryTimes++;
244 if (retryTimes > TRY_READ_TIMES) {
245 break;
246 }
247 continue;
248 }
249 break;
250 }
251 FALSE_LOG_MSG(!isInterruptNeeded_.load(), "ReadRetry interrupted");
252 return err;
253 }
254
PullData(int32_t streamID,uint64_t offset,size_t size,std::shared_ptr<Plugins::Buffer> & data)255 Status StreamDemuxer::PullData(int32_t streamID, uint64_t offset, size_t size,
256 std::shared_ptr<Plugins::Buffer>& data)
257 {
258 MEDIA_LOG_DD("IN, offset: " PUBLIC_LOG_U64 ", size: " PUBLIC_LOG_ZU
259 ", position: " PUBLIC_LOG_U64, offset, size, position_);
260 if (!source_) {
261 return Status::ERROR_INVALID_OPERATION;
262 }
263 Status err;
264 auto readSize = size;
265 if (source_->IsSeekToTimeSupported() || source_->GetSeekable() == Plugins::Seekable::UNSEEKABLE) {
266 err = ReadRetry(streamID, offset, readSize, data);
267 FALSE_LOG_MSG(err == Status::OK, "hls, plugin read failed.");
268 return err;
269 }
270
271 uint64_t totalSize = 0;
272 if ((source_->GetSize(totalSize) == Status::OK) && (totalSize != 0)) {
273 if (offset >= totalSize) {
274 MEDIA_LOG_D("Offset: " PUBLIC_LOG_U64 " is larger than totalSize: " PUBLIC_LOG_U64, offset, totalSize);
275 return Status::END_OF_STREAM;
276 }
277 if ((offset + readSize) > totalSize) {
278 readSize = totalSize - offset;
279 }
280 if (data->GetMemory() != nullptr) {
281 auto realSize = data->GetMemory()->GetCapacity();
282 readSize = (readSize > realSize) ? realSize : readSize;
283 }
284 MEDIA_LOG_DD("TotalSize_: " PUBLIC_LOG_U64, totalSize);
285 }
286 if (position_ != offset) {
287 err = source_->SeekTo(offset);
288 FALSE_RETURN_V_MSG_E(err == Status::OK, err, "Seek to " PUBLIC_LOG_U64 " fail", offset);
289 position_ = offset;
290 }
291
292 err = ReadRetry(streamID, offset, readSize, data);
293 if (err == Status::OK) {
294 FALSE_RETURN_V_MSG_E(data->GetMemory() != nullptr, Status::ERROR_UNKNOWN, "data->GetMemory invalid");
295 position_ += data->GetMemory()->GetSize();
296 }
297 return err;
298 }
299
ResetCache(int32_t streamID)300 Status StreamDemuxer::ResetCache(int32_t streamID)
301 {
302 if (cacheDataMap_.find(streamID) != cacheDataMap_.end()) {
303 cacheDataMap_[streamID].Reset();
304 cacheDataMap_.erase(streamID);
305 }
306 return Status::OK;
307 }
308
ResetAllCache()309 Status StreamDemuxer::ResetAllCache()
310 {
311 for (auto& iter : cacheDataMap_) {
312 iter.second.Reset();
313 }
314 cacheDataMap_.clear();
315 return Status::OK;
316 }
317
Start()318 Status StreamDemuxer::Start()
319 {
320 return Status::OK;
321 }
322
323
Stop()324 Status StreamDemuxer::Stop()
325 {
326 return Status::OK;
327 }
328
329
Resume()330 Status StreamDemuxer::Resume()
331 {
332 return Status::OK;
333 }
334
335
Pause()336 Status StreamDemuxer::Pause()
337 {
338 return Status::OK;
339 }
340
Flush()341 Status StreamDemuxer::Flush()
342 {
343 return Status::OK;
344 }
345
HandleReadHeader(int32_t streamID,int64_t offset,std::shared_ptr<Buffer> & buffer,size_t expectedLen)346 Status StreamDemuxer::HandleReadHeader(int32_t streamID, int64_t offset, std::shared_ptr<Buffer>& buffer,
347 size_t expectedLen)
348 {
349 MEDIA_LOG_D("Demuxer parse DEMUXER_STATE_PARSE_HEADER, offset: " PUBLIC_LOG_D64
350 ", expectedLen: " PUBLIC_LOG_ZU, offset, expectedLen);
351 if (expectedLen == 0) {
352 return Status::END_OF_STREAM;
353 }
354 Status ret = getRange_(streamID, static_cast<uint64_t>(offset), expectedLen, buffer);
355 if (ret == Status::OK) {
356 Status result = CheckChangeStreamID(streamID, buffer);
357 FALSE_RETURN_V(result == Status::OK, result);
358 DUMP_BUFFER2FILE(DEMUXER_INPUT_PEEK, buffer);
359 return ret;
360 }
361 if (mediaDataSize_ == LIVE_CONTENT_LENGTH) {
362 return Status::OK;
363 }
364 MEDIA_LOG_W("Demuxer parse DEMUXER_STATE_PARSE_HEADER, getRange_ failed, ret = " PUBLIC_LOG_D32, ret);
365 return ret;
366 }
367
CheckChangeStreamID(int32_t streamID,std::shared_ptr<Buffer> & buffer)368 Status StreamDemuxer::CheckChangeStreamID(int32_t streamID, std::shared_ptr<Buffer>& buffer)
369 {
370 if (IsDash()) {
371 if (buffer != nullptr && buffer->streamID != streamID) {
372 if (GetNewVideoStreamID() == streamID) {
373 SetNewVideoStreamID(buffer->streamID);
374 } else if (GetNewAudioStreamID() == streamID) {
375 SetNewAudioStreamID(buffer->streamID);
376 } else if (GetNewSubtitleStreamID() == streamID) {
377 SetNewSubtitleStreamID(buffer->streamID);
378 } else {}
379 MEDIA_LOG_I("Demuxer parse dash change, oldStreamID = " PUBLIC_LOG_D32
380 ", newStreamID = " PUBLIC_LOG_D32, streamID, buffer->streamID);
381 return Status::END_OF_STREAM;
382 }
383 }
384 return Status::OK;
385 }
386
HandleReadPacket(int32_t streamID,int64_t offset,std::shared_ptr<Buffer> & buffer,size_t expectedLen)387 Status StreamDemuxer::HandleReadPacket(int32_t streamID, int64_t offset, std::shared_ptr<Buffer>& buffer,
388 size_t expectedLen)
389 {
390 MEDIA_LOG_D("Demuxer parse DEMUXER_STATE_PARSE_FRAME");
391 Status ret = getRange_(streamID, static_cast<uint64_t>(offset), expectedLen, buffer);
392 if (ret == Status::OK) {
393 Status result = CheckChangeStreamID(streamID, buffer);
394 FALSE_RETURN_V(result == Status::OK, result);
395 DUMP_BUFFER2LOG("Demuxer GetRange", buffer, offset);
396 DUMP_BUFFER2FILE(DEMUXER_INPUT_GET, buffer);
397 if (buffer != nullptr && buffer->GetMemory() != nullptr &&
398 buffer->GetMemory()->GetSize() == 0) {
399 MEDIA_LOG_I("Demuxer parse DEMUXER_STATE_PARSE_FRAME in pausing(isIgnoreParse),"
400 " Read fail and try again");
401 return Status::ERROR_AGAIN;
402 }
403 return ret;
404 }
405 MEDIA_LOG_W("Demuxer parse DEMUXER_STATE_PARSE_FRAME, getRange_ failed, ret = " PUBLIC_LOG_D32, ret);
406 return ret;
407 }
408
CallbackReadAt(int32_t streamID,int64_t offset,std::shared_ptr<Buffer> & buffer,size_t expectedLen)409 Status StreamDemuxer::CallbackReadAt(int32_t streamID, int64_t offset, std::shared_ptr<Buffer>& buffer,
410 size_t expectedLen)
411 {
412 FALSE_RETURN_V(!isInterruptNeeded_.load(), Status::ERROR_WRONG_STATE);
413 switch (pluginStateMap_[streamID]) {
414 case DemuxerState::DEMUXER_STATE_NULL:
415 return Status::ERROR_WRONG_STATE;
416 case DemuxerState::DEMUXER_STATE_PARSE_HEADER: {
417 auto ret = HandleReadHeader(streamID, offset, buffer, expectedLen);
418 if (ret != Status::OK) {
419 return ret;
420 }
421 break;
422 }
423 case DemuxerState::DEMUXER_STATE_PARSE_FIRST_FRAME:
424 case DemuxerState::DEMUXER_STATE_PARSE_FRAME: {
425 auto ret = HandleReadPacket(streamID, offset, buffer, expectedLen);
426 if (ret == Status::END_OF_STREAM &&
427 pluginStateMap_[streamID] == DemuxerState::DEMUXER_STATE_PARSE_FIRST_FRAME) {
428 SetDemuxerState(streamID, DemuxerState::DEMUXER_STATE_PARSE_FRAME);
429 return ret;
430 }
431 if (ret != Status::OK) {
432 return ret;
433 }
434 if (pluginStateMap_[streamID] == DemuxerState::DEMUXER_STATE_PARSE_FIRST_FRAME) {
435 SetDemuxerState(streamID, DemuxerState::DEMUXER_STATE_PARSE_FRAME);
436 }
437 break;
438 }
439 default:
440 break;
441 }
442 return Status::OK;
443 }
444
445 } // namespace Media
446 } // namespace OHOS
447