1 /*
2 * Copyright (c) 2021-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 #define HST_LOG_TAG "FFMpeg_Muxer"
17
18 #include "ffmpeg_muxer_plugin.h"
19
20 #include <functional>
21 #include <set>
22
23 #include "foundation/log.h"
24 #include "plugin/interface/plugin_definition.h"
25 #include "plugin/plugins/ffmpeg_adapter/utils/ffmpeg_utils.h"
26 #include "plugin/plugins/ffmpeg_adapter/utils/ffmpeg_codec_map.h"
27
28 namespace {
29 using namespace OHOS::Media;
30 using namespace Plugin::Ffmpeg;
31
32 std::map<std::string, std::shared_ptr<AVOutputFormat>> g_pluginOutputFmt;
33
34 std::set<std::string> g_supportedMuxer = {"mp4"};
35
IsMuxerSupported(const char * name)36 bool IsMuxerSupported(const char* name)
37 {
38 return g_supportedMuxer.count(name) == 1;
39 }
40
UpdatePluginInCapability(AVCodecID codecId,CapabilitySet & capSet)41 bool UpdatePluginInCapability(AVCodecID codecId, CapabilitySet& capSet)
42 {
43 if (codecId != AV_CODEC_ID_NONE) {
44 Capability cap;
45 if (!FFCodecMap::CodecId2Cap(codecId, true, cap)) {
46 return false;
47 } else {
48 capSet.emplace_back(cap);
49 }
50 }
51 return true;
52 }
53
UpdatePluginCapability(const AVOutputFormat * oFmt,Plugin::MuxerPluginDef & pluginDef)54 bool UpdatePluginCapability(const AVOutputFormat* oFmt, Plugin::MuxerPluginDef& pluginDef)
55 {
56 if (!FFCodecMap::FormatName2Cap(oFmt->name, pluginDef.outCaps)) {
57 MEDIA_LOG_D("%" PUBLIC_LOG "s is not supported now", oFmt->name);
58 return false;
59 }
60 UpdatePluginInCapability(oFmt->audio_codec, pluginDef.inCaps);
61 UpdatePluginInCapability(oFmt->video_codec, pluginDef.inCaps);
62 UpdatePluginInCapability(oFmt->subtitle_codec, pluginDef.inCaps);
63 return true;
64 }
65
RegisterMuxerPlugins(const std::shared_ptr<Plugin::Register> & reg)66 Plugin::Status RegisterMuxerPlugins(const std::shared_ptr<Plugin::Register>& reg)
67 {
68 MEDIA_LOG_D("register muxer plugins.");
69 if (!reg) {
70 MEDIA_LOG_E("RegisterPlugins failed due to null pointer for reg.");
71 return Plugin::Status::ERROR_INVALID_PARAMETER;
72 }
73 const AVOutputFormat* outputFormat = nullptr;
74 void* ite = nullptr;
75 while ((outputFormat = av_muxer_iterate(&ite))) {
76 MEDIA_LOG_D("check ffmpeg muxer %" PUBLIC_LOG "s", outputFormat->name);
77 if (!IsMuxerSupported(outputFormat->name)) {
78 continue;
79 }
80 if (outputFormat->long_name != nullptr) {
81 if (!strncmp(outputFormat->long_name, "raw ", 4)) { // 4
82 continue;
83 }
84 }
85 std::string pluginName = "ffmpegMux_" + std::string(outputFormat->name);
86 Plugin::Ffmpeg::ReplaceDelimiter(".,|-<> ", '_', pluginName);
87 Plugin::MuxerPluginDef def;
88 if (!UpdatePluginCapability(outputFormat, def)) {
89 continue;
90 }
91 def.name = pluginName;
92 def.description = "ffmpeg muxer";
93 def.rank = 100; // 100
94 def.creator = [](const std::string& name) -> std::shared_ptr<Plugin::MuxerPlugin> {
95 return std::make_shared<FFMux::FFmpegMuxerPlugin>(name);
96 };
97 if (reg->AddPlugin(def) != Plugin::Status::OK) {
98 MEDIA_LOG_W("fail to add plugin %" PUBLIC_LOG "s", pluginName.c_str());
99 continue;
100 }
101 g_pluginOutputFmt[pluginName] = std::shared_ptr<AVOutputFormat>(const_cast<AVOutputFormat*>(outputFormat),
102 [](AVOutputFormat* ptr) {}); // do not delete
103 }
104 return Plugin::Status::OK;
105 }
__anon61fc822d0402null106 PLUGIN_DEFINITION(FFmpegMuxers, Plugin::LicenseType::LGPL, RegisterMuxerPlugins, [] {g_pluginOutputFmt.clear();})
107
SetCodecByMime(const AVOutputFormat * fmt,const std::string & mime,AVStream * stream)108 Plugin::Status SetCodecByMime(const AVOutputFormat* fmt, const std::string& mime, AVStream* stream)
109 {
110 AVCodecID id = AV_CODEC_ID_NONE;
111 if (!FFCodecMap::Mime2CodecId(mime, id)) {
112 MEDIA_LOG_E("mime %" PUBLIC_LOG "s has no corresponding codec id", mime.c_str());
113 return Plugin::Status::ERROR_UNSUPPORTED_FORMAT;
114 }
115 auto ptr = avcodec_find_encoder(id);
116 if (ptr == nullptr) {
117 MEDIA_LOG_E("codec of mime %" PUBLIC_LOG "s is not founder as encoder", mime.c_str());
118 return Plugin::Status::ERROR_UNSUPPORTED_FORMAT;
119 }
120 bool matched = true;
121 switch (ptr->type) {
122 case AVMEDIA_TYPE_VIDEO:
123 matched = id == fmt->video_codec;
124 break;
125 case AVMEDIA_TYPE_AUDIO:
126 matched = id == fmt->audio_codec;
127 break;
128 case AVMEDIA_TYPE_SUBTITLE:
129 matched = id == fmt->subtitle_codec;
130 break;
131 default:
132 matched = false;
133 }
134 if (!matched) {
135 MEDIA_LOG_E("codec of mime %" PUBLIC_LOG "s is not matched with %" PUBLIC_LOG "s muxer",
136 mime.c_str(), fmt->name);
137 return Plugin::Status::ERROR_UNSUPPORTED_FORMAT;
138 }
139 stream->codecpar->codec_id = id;
140 stream->codecpar->codec_type = ptr->type;
141 return Plugin::Status::OK;
142 }
143
SetCodecOfTrack(const AVOutputFormat * fmt,AVStream * stream,const Plugin::TagMap & tagMap)144 Plugin::Status SetCodecOfTrack(const AVOutputFormat* fmt, AVStream* stream, const Plugin::TagMap& tagMap)
145 {
146 using namespace OHOS::Media::Plugin;
147 if (tagMap.count(Tag::MIME) == 0) {
148 MEDIA_LOG_E("mime is missing!");
149 return Status::ERROR_UNSUPPORTED_FORMAT;
150 }
151 auto& value = tagMap.find(Tag::MIME)->second;
152 if (!value.SameTypeWith(typeid(std::string))) {
153 return Status::ERROR_MISMATCHED_TYPE;
154 }
155 // todo specially for audio/mpeg audio/mpeg we should check mpegversion and mpeglayer
156
157 return SetCodecByMime(fmt, Plugin::AnyCast<std::string>(value), stream);
158 }
159
160 template<typename T, typename U>
SetSingleParameter(Tag tag,const Plugin::TagMap & tagMap,U & target,std::function<U (T)> func)161 Plugin::Status SetSingleParameter(Tag tag, const Plugin::TagMap& tagMap, U& target, std::function<U(T)> func)
162 {
163 auto ite = tagMap.find(tag);
164 if (ite != std::end(tagMap)) {
165 if (!ite->second.SameTypeWith(typeid(T))) {
166 MEDIA_LOG_E("tag %" PUBLIC_LOG "d type mismatched", tag);
167 return Plugin::Status::ERROR_MISMATCHED_TYPE;
168 }
169 target = func(Plugin::AnyCast<T>(ite->second));
170 }
171 return Plugin::Status::OK;
172 }
173
SetParameterOfAuTrack(AVStream * stream,const Plugin::TagMap & tagMap)174 Plugin::Status SetParameterOfAuTrack(AVStream* stream, const Plugin::TagMap& tagMap)
175 {
176 #define RETURN_IF_NOT_OK(ret) \
177 do { \
178 if ((ret) != Status::OK) { \
179 return ret; \
180 } \
181 } while (0)
182
183 using namespace Plugin;
184 using namespace Ffmpeg;
185 auto ret = SetSingleParameter<AudioSampleFormat, int32_t>(Tag::AUDIO_SAMPLE_FORMAT, tagMap,
186 stream->codecpar->format, Trans2FFmepgFormat);
187 std::function<int32_t(uint32_t)> ui2iFunc = [](uint32_t i) {return i;};
188
189 RETURN_IF_NOT_OK(ret);
190 ret = SetSingleParameter<uint32_t, int32_t>(Tag::AUDIO_CHANNELS, tagMap, stream->codecpar->channels,
191 ui2iFunc);
192 RETURN_IF_NOT_OK(ret);
193 ret = SetSingleParameter<uint32_t, int32_t>(Tag::AUDIO_SAMPLE_RATE, tagMap, stream->codecpar->sample_rate,
194 ui2iFunc);
195 RETURN_IF_NOT_OK(ret);
196 ret = SetSingleParameter<uint32_t, int32_t>(Tag::AUDIO_SAMPLE_PER_FRAME, tagMap, stream->codecpar->frame_size,
197 ui2iFunc);
198 RETURN_IF_NOT_OK(ret);
199 ret = SetSingleParameter<AudioChannelLayout, uint64_t>(Tag::AUDIO_CHANNEL_LAYOUT, tagMap,
200 stream->codecpar->channel_layout, [](AudioChannelLayout layout) {return (uint64_t)layout;});
201 return ret;
202 #undef RETURN_IF_NOT_OK
203 }
204
SetParameterOfVdTrack(AVStream * stream,const Plugin::TagMap & tagMap)205 Plugin::Status SetParameterOfVdTrack(AVStream* stream, const Plugin::TagMap& tagMap)
206 {
207 // todo add video
208 MEDIA_LOG_E("should add vd tack parameter setter");
209 return Plugin::Status::ERROR_UNKNOWN;
210 }
211
SetParameterOfSubTitleTrack(AVStream * stream,const Plugin::TagMap & tagMap)212 Plugin::Status SetParameterOfSubTitleTrack(AVStream* stream, const Plugin::TagMap& tagMap)
213 {
214 // todo add subtitle
215 MEDIA_LOG_E("should add subtitle tack parameter setter");
216 return Plugin::Status::ERROR_UNKNOWN;
217 }
218
ResetCodecParameter(AVCodecParameters * par)219 void ResetCodecParameter (AVCodecParameters* par)
220 {
221 av_freep(&par->extradata);
222 memset_s(par, sizeof(*par), 0, sizeof(*par));
223 par->codec_type = AVMEDIA_TYPE_UNKNOWN;
224 par->codec_id = AV_CODEC_ID_NONE;
225 par->format = -1;
226 par->profile = FF_PROFILE_UNKNOWN;
227 par->level = FF_LEVEL_UNKNOWN;
228 par->field_order = AV_FIELD_UNKNOWN;
229 par->color_range = AVCOL_RANGE_UNSPECIFIED;
230 par->color_primaries = AVCOL_PRI_UNSPECIFIED;
231 par->color_trc = AVCOL_TRC_UNSPECIFIED;
232 par->color_space = AVCOL_SPC_UNSPECIFIED;
233 par->chroma_location = AVCHROMA_LOC_UNSPECIFIED;
234 par->sample_aspect_ratio = AVRational {0, 1};
235 }
236
SetTagsOfTrack(const AVOutputFormat * fmt,AVStream * stream,const Plugin::TagMap & tagMap)237 Plugin::Status SetTagsOfTrack(const AVOutputFormat* fmt, AVStream* stream, const Plugin::TagMap& tagMap)
238 {
239 using namespace OHOS::Media::Plugin;
240 if (stream == nullptr) {
241 return Status::ERROR_INVALID_PARAMETER;
242 }
243 ResetCodecParameter(stream->codecpar);
244 // firstly mime
245 auto ret = SetCodecOfTrack(fmt, stream, tagMap);
246 if (ret != Status::OK) {
247 return ret;
248 }
249 if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { // audio
250 ret = SetParameterOfAuTrack(stream, tagMap);
251 } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { // video
252 ret = SetParameterOfVdTrack(stream, tagMap);
253 } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { // subtitle
254 ret = SetParameterOfSubTitleTrack(stream, tagMap);
255 } else {
256 MEDIA_LOG_W("unknown codec type of stream %" PUBLIC_LOG "d", stream->index);
257 }
258 if (ret != Status::OK) {
259 return ret;
260 }
261 // others
262 ret = SetSingleParameter<int64_t, int64_t>(Tag::MEDIA_BITRATE, tagMap, stream->codecpar->bit_rate,
263 [](int64_t rate) {return rate;});
264 if (ret != Status::OK) {
265 return ret;
266 }
267
268 // extra data
269 auto ite = tagMap.find(Tag::MEDIA_CODEC_CONFIG);
270 if (ite != std::end(tagMap)) {
271 if (!ite->second.SameTypeWith(typeid(std::vector<uint8_t>))) {
272 MEDIA_LOG_E("tag %" PUBLIC_LOG_D32 " type mismatched", Tag::MEDIA_CODEC_CONFIG);
273 return Plugin::Status::ERROR_MISMATCHED_TYPE;
274 }
275 const auto* extraData = Plugin::AnyCast<std::vector<uint8_t>>(&(ite->second));
276 stream->codecpar->extradata = static_cast<uint8_t *>(av_mallocz(
277 extraData->size() + AV_INPUT_BUFFER_PADDING_SIZE));
278 if (stream->codecpar->extradata == nullptr) {
279 return Status::ERROR_NO_MEMORY;
280 }
281 memcpy_s(stream->codecpar->extradata, extraData->size(), extraData->data(), extraData->size());
282 stream->codecpar->extradata_size = extraData->size();
283 }
284 return Status::OK;
285 }
286
SetTagsOfGeneral(AVFormatContext * fmtCtx,const Plugin::TagMap & tags)287 Plugin::Status SetTagsOfGeneral(AVFormatContext* fmtCtx, const Plugin::TagMap& tags)
288 {
289 for (const auto& pair: tags) {
290 std::string metaName;
291 if (!Plugin::Ffmpeg::FindAvMetaNameByTag(pair.first, metaName)) {
292 MEDIA_LOG_I("tag %" PUBLIC_LOG "d will not written as general meta", pair.first);
293 continue;
294 }
295 if (!pair.second.SameTypeWith(typeid(std::string))) {
296 continue;
297 }
298 auto value = Plugin::AnyCast<std::string>(pair.second);
299 av_dict_set(&fmtCtx->metadata, metaName.c_str(), value.c_str(), 0);
300 }
301 return Plugin::Status::OK;
302 }
303 }
304
305 namespace OHOS {
306 namespace Media {
307 namespace FFMux {
308 using namespace OHOS::Media::Plugin;
309
FFmpegMuxerPlugin(std::string name)310 FFmpegMuxerPlugin::FFmpegMuxerPlugin(std::string name) : MuxerPlugin(std::move(name)) {}
311
~FFmpegMuxerPlugin()312 FFmpegMuxerPlugin::~FFmpegMuxerPlugin()
313 {
314 Release();
315 }
Release()316 Status FFmpegMuxerPlugin::Release()
317 {
318 outputFormat_.reset();
319 cachePacket_.reset();
320 formatContext_.reset();
321 return Status::OK;
322 }
323
InitFormatCtxLocked()324 Status FFmpegMuxerPlugin::InitFormatCtxLocked()
325 {
326 if (formatContext_ == nullptr) {
327 auto ioCtx = InitAvIoCtx();
328 if (ioCtx == nullptr) {
329 return Status::ERROR_NO_MEMORY;
330 }
331 auto fmt = avformat_alloc_context();
332 if (fmt == nullptr) {
333 return Status::ERROR_NO_MEMORY;
334 }
335 fmt->pb = ioCtx;
336 fmt->oformat = outputFormat_.get();
337 fmt->flags = static_cast<uint32_t>(fmt->flags) | static_cast<uint32_t>(AVFMT_FLAG_CUSTOM_IO);
338 formatContext_ = std::shared_ptr<AVFormatContext>(fmt, [](AVFormatContext* ptr) {
339 if (ptr) {
340 DeInitAvIoCtx(ptr->pb);
341 avformat_free_context(ptr);
342 }
343 });
344 }
345 return Status::OK;
346 }
347
Init()348 Status FFmpegMuxerPlugin::Init()
349 {
350 MEDIA_LOG_D("Init entered.");
351 if (g_pluginOutputFmt.count(pluginName_) == 0) {
352 return Status::ERROR_UNSUPPORTED_FORMAT;
353 }
354 outputFormat_ = g_pluginOutputFmt[pluginName_];
355 auto pkt = av_packet_alloc();
356 cachePacket_ = std::shared_ptr<AVPacket> (pkt, [] (AVPacket* packet) {av_packet_free(&packet);});
357 OSAL::ScopedLock lock(fmtMutex_);
358 return InitFormatCtxLocked();
359 }
360
Deinit()361 Status FFmpegMuxerPlugin::Deinit()
362 {
363 return Release();
364 }
365
InitAvIoCtx()366 AVIOContext* FFmpegMuxerPlugin::InitAvIoCtx()
367 {
368 constexpr int bufferSize = 4096; // 4096
369 auto buffer = static_cast<unsigned char*>(av_malloc(bufferSize));
370 if (buffer == nullptr) {
371 MEDIA_LOG_E("AllocAVIOContext failed to av_malloc...");
372 return nullptr;
373 }
374 AVIOContext* avioContext = avio_alloc_context(buffer, bufferSize, AVIO_FLAG_WRITE, static_cast<void*>(&ioContext_),
375 IoRead, IoWrite, IoSeek);
376 if (avioContext == nullptr) {
377 MEDIA_LOG_E("AllocAVIOContext failed to avio_alloc_context...");
378 av_free(buffer);
379 return nullptr;
380 }
381 avioContext->seekable = AVIO_SEEKABLE_NORMAL;
382 return avioContext;
383 }
384
DeInitAvIoCtx(AVIOContext * ptr)385 void FFmpegMuxerPlugin::DeInitAvIoCtx(AVIOContext* ptr)
386 {
387 if (ptr != nullptr) {
388 ptr->opaque = nullptr;
389 av_freep(&ptr->buffer);
390 avio_context_free(&ptr);
391 }
392 }
393
Prepare()394 Status FFmpegMuxerPlugin::Prepare()
395 {
396 for (const auto& pair: trackParameters_) {
397 SetTagsOfTrack(outputFormat_.get(), formatContext_->streams[pair.first], pair.second);
398 }
399 SetTagsOfGeneral(formatContext_.get(), generalParameters_);
400 return Status::OK;
401 }
ResetIoCtx(IOContext & ioContext)402 void FFmpegMuxerPlugin::ResetIoCtx(IOContext& ioContext)
403 {
404 ioContext.dataSink_.reset();
405 ioContext.pos_ = 0;
406 ioContext.end_ = 0;
407 }
Reset()408 Status FFmpegMuxerPlugin::Reset()
409 {
410 ResetIoCtx(ioContext_);
411 generalParameters_.clear();
412 trackParameters_.clear();
413 OSAL::ScopedLock lock(fmtMutex_);
414 if (outputFormat_->deinit) {
415 outputFormat_->deinit(formatContext_.get());
416 }
417 formatContext_.reset();
418 return InitFormatCtxLocked();
419 }
420
GetParameter(Tag tag,ValueType & value)421 Status FFmpegMuxerPlugin::GetParameter(Tag tag, ValueType &value)
422 {
423 return PluginBase::GetParameter(tag, value);
424 }
425
GetTrackParameter(uint32_t trackId,Tag tag,Plugin::ValueType & value)426 Status FFmpegMuxerPlugin::GetTrackParameter(uint32_t trackId, Tag tag, Plugin::ValueType& value)
427 {
428 return Status::ERROR_UNIMPLEMENTED;
429 }
430
SetParameter(Tag tag,const ValueType & value)431 Status FFmpegMuxerPlugin::SetParameter(Tag tag, const ValueType &value)
432 {
433 generalParameters_[tag] = value;
434 return Status::OK;
435 }
436
SetTrackParameter(uint32_t trackId,Tag tag,const Plugin::ValueType & value)437 Status FFmpegMuxerPlugin::SetTrackParameter(uint32_t trackId, Tag tag, const Plugin::ValueType& value)
438 {
439 if (trackId >= formatContext_->nb_streams) {
440 return Status::ERROR_INVALID_PARAMETER;
441 }
442 if (trackParameters_.count(trackId) == 0) {
443 trackParameters_.insert({trackId, Plugin::TagMap()});
444 }
445 trackParameters_[trackId][tag] = value;
446 return Status::OK;
447 }
448
AddTrack(uint32_t & trackId)449 Status FFmpegMuxerPlugin::AddTrack(uint32_t &trackId)
450 {
451 OSAL::ScopedLock lock(fmtMutex_);
452 if (formatContext_ == nullptr) {
453 return Status::ERROR_WRONG_STATE;
454 }
455 auto st = avformat_new_stream(formatContext_.get(), nullptr);
456 if (st == nullptr) {
457 return Status::ERROR_NO_MEMORY;
458 }
459 st->codecpar->codec_type = AVMEDIA_TYPE_UNKNOWN;
460 st->codecpar->codec_id = AV_CODEC_ID_NONE;
461 trackId = st->index;
462 return Status::OK;
463 }
464
SetDataSink(const std::shared_ptr<DataSink> & dataSink)465 Status FFmpegMuxerPlugin::SetDataSink(const std::shared_ptr<DataSink> &dataSink)
466 {
467 ioContext_.dataSink_ = dataSink;
468 return Status::OK;
469 }
470
WriteHeader()471 Status FFmpegMuxerPlugin::WriteHeader()
472 {
473 if (ioContext_.dataSink_ == nullptr || outputFormat_ == nullptr) {
474 return Status::ERROR_WRONG_STATE;
475 }
476 OSAL::ScopedLock lock(fmtMutex_);
477 if (formatContext_ == nullptr) {
478 return Status::ERROR_WRONG_STATE;
479 }
480 int ret = avformat_write_header(formatContext_.get(), nullptr);
481 if (ret < 0) {
482 MEDIA_LOG_E("failed to write header %" PUBLIC_LOG "s", AVStrError(ret).c_str());
483 return Status::ERROR_UNKNOWN;
484 }
485 return Status::OK;
486 }
487
WriteFrame(const std::shared_ptr<Plugin::Buffer> & buffer)488 Status FFmpegMuxerPlugin::WriteFrame(const std::shared_ptr<Plugin::Buffer>& buffer)
489 {
490 if (buffer == nullptr || buffer->IsEmpty()) {
491 return Status::ERROR_INVALID_PARAMETER;
492 }
493 uint32_t trackId = buffer->trackID;
494 if (trackId >= formatContext_->nb_streams) {
495 return Status::ERROR_INVALID_PARAMETER;
496 }
497 (void)memset_s(cachePacket_.get(), sizeof(AVPacket), 0, sizeof(AVPacket));
498 auto memory = buffer->GetMemory();
499 cachePacket_->data = const_cast<uint8_t*>(memory->GetReadOnlyData());
500 cachePacket_->size = memory->GetSize();
501 cachePacket_->stream_index = trackId;
502 cachePacket_->pts = ConvertTimeToFFmpeg(buffer->pts, formatContext_->streams[trackId]->time_base);
503 cachePacket_->dts = cachePacket_->pts;
504 cachePacket_->flags = 0;
505 if (buffer->flag & BUFFER_FLAG_KEY_FRAME) {
506 cachePacket_->flags |= AV_PKT_FLAG_KEY;
507 }
508 cachePacket_->duration = ConvertTimeToFFmpeg(buffer->duration, formatContext_->streams[trackId]->time_base);
509 av_write_frame(formatContext_.get(), cachePacket_.get());
510 av_packet_unref(cachePacket_.get());
511 return Status::OK;
512 }
513
WriteTrailer()514 Status FFmpegMuxerPlugin::WriteTrailer()
515 {
516 if (ioContext_.dataSink_ == nullptr || outputFormat_ == nullptr) {
517 return Status::ERROR_WRONG_STATE;
518 }
519 OSAL::ScopedLock lock(fmtMutex_);
520 if (formatContext_ == nullptr) {
521 return Status::ERROR_WRONG_STATE;
522 }
523 av_write_frame(formatContext_.get(), nullptr); // flush out cache data
524 int ret = av_write_trailer(formatContext_.get());
525 if (ret != 0) {
526 MEDIA_LOG_E("failed to write trailer %" PUBLIC_LOG "s", AVStrError(ret).c_str());
527 }
528 avio_flush(formatContext_->pb);
529 return Status::OK;
530 }
531
SetCallback(Callback * cb)532 Status FFmpegMuxerPlugin::SetCallback(Callback* cb)
533 {
534 return Status::END_OF_STREAM;
535 }
536
GetAllocator()537 std::shared_ptr<Allocator> FFmpegMuxerPlugin::GetAllocator()
538 {
539 return {};
540 }
541
IoRead(void * opaque,uint8_t * buf,int bufSize)542 int32_t FFmpegMuxerPlugin::IoRead(void* opaque, uint8_t* buf, int bufSize)
543 {
544 (void)opaque;
545 (void)buf;
546 (void)bufSize;
547 return 0;
548 }
IoWrite(void * opaque,uint8_t * buf,int bufSize)549 int32_t FFmpegMuxerPlugin::IoWrite(void* opaque, uint8_t* buf, int bufSize)
550 {
551 auto ioCtx = static_cast<IOContext*>(opaque);
552 if (ioCtx && ioCtx->dataSink_) {
553 auto buffer = std::make_shared<Buffer>();
554 auto bufferMem = buffer->AllocMemory(nullptr, bufSize);
555 buffer->GetMemory()->Write(buf, bufSize, 0); // copy to buffer
556 auto res = ioCtx->dataSink_->WriteAt(ioCtx->pos_, buffer);
557 if (res == Status::OK) {
558 ioCtx->pos_ += bufferMem->GetSize();
559 if (ioCtx->pos_ > ioCtx->end_) {
560 ioCtx->end_ = ioCtx->pos_;
561 }
562 return bufferMem->GetSize();
563 }
564 return 0;
565 }
566 return -1;
567 }
568
IoSeek(void * opaque,int64_t offset,int whence)569 int64_t FFmpegMuxerPlugin::IoSeek(void* opaque, int64_t offset, int whence)
570 {
571 auto ioContext = static_cast<IOContext*>(opaque);
572 uint64_t newPos = 0;
573 switch (whence) {
574 case SEEK_SET:
575 newPos = static_cast<uint64_t>(offset);
576 ioContext->pos_ = newPos;
577 MEDIA_LOG_I("AVSeek whence: %" PUBLIC_LOG "d, pos = %" PUBLIC_LOG PRId64 ", newPos = %" PUBLIC_LOG
578 PRIu64, whence, offset, newPos);
579 break;
580 case SEEK_CUR:
581 newPos = ioContext->pos_ + offset;
582 MEDIA_LOG_I("AVSeek whence: %" PUBLIC_LOG "d, pos = %" PUBLIC_LOG PRId64 ", newPos = %" PUBLIC_LOG
583 PRIu64, whence, offset, newPos);
584 break;
585 case SEEK_END:
586 case AVSEEK_SIZE:
587 newPos = ioContext->end_ + offset;
588 MEDIA_LOG_I("AVSeek seek end whence: %" PUBLIC_LOG "d, pos = %" PUBLIC_LOG PRId64, whence, offset);
589 break;
590 default:
591 MEDIA_LOG_E("AVSeek unexpected whence: %" PUBLIC_LOG "d", whence);
592 break;
593 }
594 if (whence != AVSEEK_SIZE) {
595 ioContext->pos_ = newPos;
596 }
597 MEDIA_LOG_I("current offset: %" PUBLIC_LOG PRId64 ", new pos: %" PUBLIC_LOG PRIu64, ioContext->pos_, newPos);
598 return newPos;
599 }
600 } // FFMux
601 } // Media
602 } // OHOS