• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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