* Copyright (C) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define HST_LOG_TAG "MediaMuxer"
#include "media_muxer.h"
#include <set>
#include <fcntl.h>
#include <unistd.h>
#include <string>
#include <unordered_map>
#include "securec.h"
#include "meta/mime_type.h"
#include "plugin/plugin_manager_v2.h"
#include "common/log.h"
#include "data_sink_fd.h"
#include "data_sink_file.h"
#include "event_info_extented_key.h"
#include "statistics_event_reporter.h"
namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_MUXER, "HiStreamer" };
}
namespace {
using namespace OHOS::Media;
using namespace Plugins;
constexpr int32_t ERR_TRACK_INDEX = -1;
constexpr uint32_t MAX_BUFFER_COUNT = 10;
const std::unordered_map<OutputFormat, std::set<std::string>> MUX_FORMAT_INFO = {
{OutputFormat::MPEG_4, {MimeType::AUDIO_MPEG, MimeType::AUDIO_AAC,
MimeType::VIDEO_AVC, MimeType::VIDEO_MPEG4,
MimeType::VIDEO_HEVC,
MimeType::IMAGE_JPG, MimeType::IMAGE_PNG,
MimeType::IMAGE_BMP, MimeType::TIMED_METADATA}},
{OutputFormat::M4A, {MimeType::AUDIO_AAC,
MimeType::IMAGE_JPG, MimeType::IMAGE_PNG,
MimeType::IMAGE_BMP}},
{OutputFormat::AMR, {MimeType::AUDIO_AMR_NB, MimeType::AUDIO_AMR_WB}},
{OutputFormat::MP3, {MimeType::AUDIO_MPEG, MimeType::IMAGE_JPG}},
{OutputFormat::WAV, {MimeType::AUDIO_RAW, MimeType::AUDIO_G711MU}},
{OutputFormat::AAC, {MimeType::AUDIO_AAC}},
{OutputFormat::FLAC, {MimeType::AUDIO_FLAC, MimeType::IMAGE_JPG,
MimeType::IMAGE_PNG, MimeType::IMAGE_BMP}},
{OutputFormat::OGG, {MimeType::AUDIO_OPUS, MimeType::AUDIO_VORBIS}},
{OutputFormat::FLV, {MimeType::AUDIO_AAC, MimeType::AUDIO_AVS3DA,
MimeType::VIDEO_AVC, MimeType::VIDEO_HEVC}},
};
const std::map<std::string, std::set<std::string>> MUX_MIME_INFO = {
{MimeType::AUDIO_MPEG, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT}},
{MimeType::AUDIO_AAC, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT}},
{MimeType::AUDIO_RAW, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT, Tag::AUDIO_SAMPLE_FORMAT}},
{MimeType::AUDIO_G711MU, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT, Tag::MEDIA_BITRATE}},
{MimeType::AUDIO_FLAC, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT}},
{MimeType::AUDIO_OPUS, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT, Tag::MEDIA_CODEC_CONFIG}},
{MimeType::AUDIO_VORBIS, {Tag::AUDIO_SAMPLE_RATE, Tag::AUDIO_CHANNEL_COUNT, Tag::MEDIA_CODEC_CONFIG}},
{MimeType::AUDIO_AVS3DA, {Tag::AUDIO_SAMPLE_RATE}},
{MimeType::VIDEO_AVC, {Tag::VIDEO_WIDTH, Tag::VIDEO_HEIGHT}},
{MimeType::VIDEO_MPEG4, {Tag::VIDEO_WIDTH, Tag::VIDEO_HEIGHT}},
{MimeType::VIDEO_HEVC, {Tag::VIDEO_WIDTH, Tag::VIDEO_HEIGHT}},
{MimeType::IMAGE_JPG, {Tag::VIDEO_WIDTH, Tag::VIDEO_HEIGHT}},
{MimeType::IMAGE_PNG, {Tag::VIDEO_WIDTH, Tag::VIDEO_HEIGHT}},
{MimeType::IMAGE_BMP, {Tag::VIDEO_WIDTH, Tag::VIDEO_HEIGHT}},
{MimeType::TIMED_METADATA, {Tag::TIMED_METADATA_KEY, Tag::TIMED_METADATA_SRC_TRACK}},
};
const std::map<std::string, std::set<std::string>> MUX_MIME_INFO_EXT = {
{MimeType::AUDIO_AAC, {Tag::AUDIO_AAC_IS_ADTS, Tag::MEDIA_PROFILE}},
};
const std::set<std::string> MUX_AUXILIARY_TRACK_INFO = {
Tag::REFERENCE_TRACK_IDS, Tag::TRACK_REFERENCE_TYPE, Tag::TRACK_DESCRIPTION,
};
}
namespace OHOS {
namespace Media {
MediaMuxer::MediaMuxer(int32_t appUid, int32_t appPid)
: appUid_(appUid), appPid_(appPid), format_(Plugins::OutputFormat::DEFAULT)
{
MEDIA_LOG_D("0x%{public}06" PRIXPTR " instances create", FAKE_POINTER(this));
}
MediaMuxer::~MediaMuxer()
{
MEDIA_LOG_D("Destroy");
if (state_ == State::STARTED) {
Stop();
}
appUid_ = -1;
appPid_ = -1;
muxer_ = nullptr;
tracks_.clear();
MEDIA_LOG_D("0x%{public}06" PRIXPTR " instances destroy", FAKE_POINTER(this));
}
Status MediaMuxer::Init(int32_t fd, Plugins::OutputFormat format)
{
MEDIA_LOG_I("Init");
std::lock_guard<std::mutex> lock(mutex_);
FALSE_RETURN_V_MSG_E(state_ == State::UNINITIALIZED, Status::ERROR_WRONG_STATE,
"The state is not UNINITIALIZED, the current state is %{public}s.", StateConvert(state_).c_str());
FALSE_RETURN_V_MSG_E(fd >= 0, Status::ERROR_INVALID_PARAMETER, "The fd %{public}d is error!", fd);
uint32_t fdPermission = static_cast<uint32_t>(fcntl(fd, F_GETFL, 0));
FALSE_RETURN_V_MSG_E((fdPermission & O_WRONLY) == O_WRONLY || (fdPermission & O_RDWR) == O_RDWR,
Status::ERROR_INVALID_PARAMETER, "No permission to write fd.");
FALSE_RETURN_V_MSG_E(lseek(fd, 0, SEEK_CUR) != -1, Status::ERROR_INVALID_PARAMETER,
"The fd is not seekable.");
format_ = format == Plugins::OutputFormat::DEFAULT ? Plugins::OutputFormat::MPEG_4 : format;
muxer_ = CreatePlugin(format_);
if (muxer_ != nullptr) {
state_ = State::INITIALIZED;
muxer_->SetCallback(this);
MEDIA_LOG_I("The state is INITIALIZED");
} else {
MEDIA_LOG_E("The state is UNINITIALIZED");
}
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED, Status::ERROR_WRONG_STATE,
"The state is UNINITIALIZED");
return muxer_->SetDataSink(std::make_shared<DataSinkFd>(fd));
}
Status MediaMuxer::Init(FILE *file, Plugins::OutputFormat format)
{
MEDIA_LOG_I("Init");
std::lock_guard<std::mutex> lock(mutex_);
FALSE_RETURN_V_MSG_E(state_ == State::UNINITIALIZED, Status::ERROR_WRONG_STATE,
"The state is not UNINITIALIZED, the current state is %{public}s.", StateConvert(state_).c_str());
FALSE_RETURN_V_MSG_E(file != nullptr, Status::ERROR_INVALID_PARAMETER, "The file handle is null!");
FALSE_RETURN_V_MSG_E(fseek(file, 0L, SEEK_CUR) >= 0, Status::ERROR_INVALID_PARAMETER,
"The file handle is not seekable.");
format_ = format == Plugins::OutputFormat::DEFAULT ? Plugins::OutputFormat::MPEG_4 : format;
muxer_ = CreatePlugin(format_);
if (muxer_ != nullptr) {
state_ = State::INITIALIZED;
muxer_->SetCallback(this);
MEDIA_LOG_I("The state is INITIALIZED");
} else {
MEDIA_LOG_E("The state is UNINITIALIZED");
}
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED, Status::ERROR_WRONG_STATE,
"The state is UNINITIALIZED");
return muxer_->SetDataSink(std::make_shared<DataSinkFile>(file));
}
Status MediaMuxer::SetParameter(const std::shared_ptr<Meta> ¶m)
{
MEDIA_LOG_I("SetParameter");
std::lock_guard<std::mutex> lock(mutex_);
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED, Status::ERROR_WRONG_STATE,
"The state is not INITIALIZED, the interface must be called after constructor and before Start(). "
"The current state is %{public}s.", StateConvert(state_).c_str());
return muxer_->SetParameter(param);
}
Status MediaMuxer::SetUserMeta(const std::shared_ptr<Meta> &userMeta)
{
MEDIA_LOG_I("SetUserMeta");
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED || state_ == State::STARTED, Status::ERROR_WRONG_STATE,
"The state is not INITIALIZED, the interface must be called at initialized or started state. "
"The current state is %{public}s.", StateConvert(state_).c_str());
std::lock_guard<std::mutex> lock(mutex_);
std::vector<std::string> keys;
userMeta->GetKeys(keys);
for (auto& k: keys) {
if (k.compare("com.openharmony.recorder.timestamp") == 0) {
MEDIA_LOG_I("set com.openharmony.recorder.timestamp");
return muxer_->SetUserMeta(userMeta);
}
}
return muxer_->SetUserMeta(userMeta);
}
Status MediaMuxer::AddTrack(int32_t &trackIndex, const std::shared_ptr<Meta> &trackDesc)
{
MEDIA_LOG_I("AddTrack");
std::lock_guard<std::mutex> lock(mutex_);
trackIndex = ERR_TRACK_INDEX;
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED, Status::ERROR_WRONG_STATE,
"The state is not INITIALIZED, the interface must be called after constructor and before Start(). "
"The current state is %{public}s.", StateConvert(state_).c_str());
std::string mimeType = {};
FALSE_RETURN_V_MSG_E(trackDesc->Get<Tag::MIME_TYPE>(mimeType), Status::ERROR_INVALID_DATA,
"The track format does not contain mime.");
FALSE_RETURN_V_MSG_E(CanAddTrack(mimeType), Status::ERROR_UNSUPPORTED_FORMAT,
"The track mime is unsupported: %{public}s.", mimeType.c_str());
FALSE_RETURN_V_MSG_E(CheckKeys(mimeType, trackDesc), Status::ERROR_INVALID_DATA,
"The track format keys not contained.");
FALSE_RETURN_V_MSG_E(CheckKeysExt(mimeType, trackDesc, format_), Status::ERROR_INVALID_DATA,
"The track format keys not contained.");
MEDIA_LOG_I("The track is %{public}s.", mimeType.c_str());
int32_t trackId = -1;
Status ret = muxer_->AddTrack(trackId, trackDesc);
FALSE_RETURN_V_MSG_E(ret == Status::NO_ERROR, ret, "AddTrack failed! %{public}s.", mimeType.c_str());
FALSE_RETURN_V_MSG_E(trackId >= 0, Status::ERROR_INVALID_DATA,
"The track index is greater than or equal to 0.");
trackIndex = static_cast<int32_t>(tracks_.size());
sptr<Track> track = new Track();
track->trackId_ = trackId;
track->mimeType_ = mimeType;
track->trackDesc_ = trackDesc;
track->bufferQ_ = AVBufferQueue::Create(MAX_BUFFER_COUNT, MemoryType::UNKNOWN_MEMORY, mimeType);
track->producer_ = track->bufferQ_->GetProducer();
track->consumer_ = track->bufferQ_->GetConsumer();
tracks_.emplace_back(track);
return Status::NO_ERROR;
}
sptr<AVBufferQueueProducer> MediaMuxer::GetInputBufferQueue(uint32_t trackIndex)
{
MEDIA_LOG_I("GetInputBufferQueue");
std::lock_guard<std::mutex> lock(mutex_);
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED, nullptr,
"The state is not INITIALIZED, the interface must be called after AddTrack() and before Start(). "
"The current state is %{public}s.", StateConvert(state_).c_str());
FALSE_RETURN_V_MSG_E(trackIndex < tracks_.size(), nullptr,
"The track index does not exist, the interface must be called after AddTrack() and before Start().");
return tracks_[trackIndex]->producer_;
}
Status MediaMuxer::WriteSample(uint32_t trackIndex, const std::shared_ptr<AVBuffer> &sample)
{
std::lock_guard<std::mutex> lock(mutex_);
FALSE_RETURN_V_MSG_E(state_ == State::STARTED, Status::ERROR_WRONG_STATE,
"The state is not STARTED, the interface must be called after Start() and before Stop(). "
"The current state is %{public}s", StateConvert(state_).c_str());
FALSE_RETURN_V_MSG_E(trackIndex < tracks_.size(), Status::ERROR_INVALID_DATA,
"The track index does not exist, the interface must be called after AddTrack() and before Start().");
FALSE_RETURN_V_MSG_E(sample != nullptr && sample->memory_ != nullptr, Status::ERROR_INVALID_DATA,
"Invalid sample");
MEDIA_LOG_D("WriteSample track:" PUBLIC_LOG_U32 ", pts:" PUBLIC_LOG_D64 ", size:" PUBLIC_LOG_D32
", flags:" PUBLIC_LOG_U32, trackIndex, sample->pts_, sample->memory_->GetSize(), sample->flag_);
std::shared_ptr<AVBuffer> buffer = nullptr;
AVBufferConfig avBufferConfig;
avBufferConfig.size = sample->memory_->GetSize();
avBufferConfig.memoryType = MemoryType::VIRTUAL_MEMORY;
Status ret = tracks_[trackIndex]->producer_->RequestBuffer(buffer, avBufferConfig, -1);
FALSE_RETURN_V_MSG_E(ret == Status::OK && buffer != nullptr, Status::ERROR_NO_MEMORY,
"Request buffer failed.");
buffer->pts_ = sample->pts_;
buffer->dts_ = sample->dts_;
buffer->flag_ = sample->flag_;
buffer->duration_ = sample->duration_;
*buffer->meta_.get() = *sample->meta_.get();
if (sample->memory_ != nullptr && sample->memory_->GetSize() > 0) {
int32_t retInt = buffer->memory_->Write(sample->memory_->GetAddr(), sample->memory_->GetSize(), 0);
FALSE_RETURN_V_MSG_E(retInt > 0, Status::ERROR_NO_MEMORY, "Write sample in buffer failed.");
} else {
MEDIA_LOG_W("No data in the sample.");
buffer->memory_->SetSize(0);
}
return tracks_[trackIndex]->producer_->PushBuffer(buffer, true);
}
Status MediaMuxer::Start()
{
MEDIA_LOG_I("Start");
std::lock_guard<std::mutex> lock(mutex_);
FALSE_RETURN_V_MSG_E(state_ == State::INITIALIZED, Status::ERROR_WRONG_STATE,
"The state is not INITIALIZED, the interface must be called after AddTrack() and before WriteSample(). "
"The current state is %{public}s.", StateConvert(state_).c_str());
FALSE_RETURN_V_MSG_E(tracks_.size() > 0, Status::ERROR_INVALID_OPERATION,
"The track count is error, count is %{public}zu.", tracks_.size());
Status ret = muxer_->Start();
FALSE_RETURN_V_MSG_E(ret == Status::NO_ERROR, ret, "Start failed!");
state_ = State::STARTED;
for (const auto& track : tracks_) {
track->SetBufferAvailableListener(this);
sptr<IConsumerListener> listener = track;
track->consumer_->SetBufferAvailableListener(listener);
}
StartThread("OS_MUXER_WRITE");
return Status::NO_ERROR;
}
void MediaMuxer::StartThread(const std::string &name)
{
threadName_ = name;
if (thread_ != nullptr) {
MEDIA_LOG_W("Started already! [%{public}s]", threadName_.c_str());
return;
}
isThreadExit_ = false;
thread_ = std::make_unique<std::thread>(&MediaMuxer::ThreadProcessor, this);
MEDIA_LOG_D("The thread started! [%{public}s]", threadName_.c_str());
}
Status MediaMuxer::Stop()
{
MEDIA_LOG_I("Stop");
std::lock_guard<std::mutex> lock(mutex_);
if (state_ == State::STOPPED) {
MEDIA_LOG_W("The current state is STOPPED!");
return Status::ERROR_INVALID_OPERATION;
}
FALSE_RETURN_V_MSG_E(state_ == State::STARTED, Status::ERROR_WRONG_STATE,
"The state is not STARTED. The current state is %{public}s.", StateConvert(state_).c_str());
state_ = State::STOPPED;
for (auto& track : tracks_) {
sptr<IConsumerListener> listener = nullptr;
track->consumer_->SetBufferAvailableListener(listener);
}
StopThread();
PrintWriteCount();
Status ret = muxer_->Stop();
FALSE_RETURN_V_MSG_E(ret == Status::NO_ERROR, ret, "Stop failed!");
MEDIA_LOG_I("Stopped successfully.");
return Status::NO_ERROR;
}
void MediaMuxer::StopThread()
{
if (isThreadExit_) {
MEDIA_LOG_D("Stopped already! [%{public}s]", threadName_.c_str());
return;
}
{
std::lock_guard<std::mutex> lock(mutexBufferAvailable_);
isThreadExit_ = true;
condBufferAvailable_.notify_all();
}
MEDIA_LOG_D("Stopping ! [%{public}s]", threadName_.c_str());
if (thread_ != nullptr) {
if (thread_->joinable()) {
thread_->join();
}
thread_ = nullptr;
}
}
void MediaMuxer::ThreadProcessor()
{
MEDIA_LOG_D("Enter ThreadProcessor [%{public}s]", threadName_.c_str());
constexpr int32_t timeoutMs = 500;
constexpr uint32_t nameSizeMax = 15;
pthread_setname_np(pthread_self(), threadName_.substr(0, nameSizeMax).c_str());
int32_t trackCount = static_cast<int32_t>(tracks_.size());
for (;;) {
if (isThreadExit_ && bufferAvailableCount_ <= 0) {
MEDIA_LOG_D("Exit ThreadProcessor [%{public}s]", threadName_.c_str());
return;
}
{
std::unique_lock<std::mutex> lock(mutexBufferAvailable_);
condBufferAvailable_.wait_for(lock, std::chrono::milliseconds(timeoutMs),
[this] { return isThreadExit_ || bufferAvailableCount_ > 0; });
}
int32_t trackIdx = -1;
std::shared_ptr<AVBuffer> buffer1 = nullptr;
for (int i = 0; i < trackCount; ++i) {
std::shared_ptr<AVBuffer> buffer2 = tracks_[i]->GetBuffer();
if ((buffer1 != nullptr && buffer2 != nullptr && buffer1->pts_ > buffer2->pts_) ||
(buffer1 == nullptr && buffer2 != nullptr)) {
buffer1 = buffer2;
trackIdx = i;
}
}
if (buffer1 != nullptr) {
muxer_->WriteSample(tracks_[trackIdx]->trackId_, tracks_[trackIdx]->curBuffer_);
tracks_[trackIdx]->ReleaseBuffer();
tracks_[trackIdx]->writeCount_++;
}
MEDIA_LOG_D("Track " PUBLIC_LOG_S " 2 bufferAvailableCount_ :" PUBLIC_LOG_D32,
threadName_.c_str(), bufferAvailableCount_.load());
}
}
void MediaMuxer::OnBufferAvailable()
{
std::unique_lock<std::mutex> lock(mutexBufferAvailable_);
++bufferAvailableCount_;
condBufferAvailable_.notify_one();
MEDIA_LOG_D("Track " PUBLIC_LOG_S " 1 bufferAvailableCount_ :" PUBLIC_LOG_D32,
threadName_.c_str(), bufferAvailableCount_.load());
}
void MediaMuxer::ReleaseBuffer()
{
std::unique_lock<std::mutex> lock(mutexBufferAvailable_);
--bufferAvailableCount_;
}
std::shared_ptr<AVBuffer> MediaMuxer::Track::GetBuffer()
{
if (curBuffer_ == nullptr && bufferAvailableCount_ > 0) {
Status ret = consumer_->AcquireBuffer(curBuffer_);
if (ret != Status::OK) {
MEDIA_LOG_E("Track " PUBLIC_LOG_S " lost " PUBLIC_LOG_D32 " frames",
mimeType_.c_str(), bufferAvailableCount_.load());
--bufferAvailableCount_;
listener_->ReleaseBuffer();
}
}
return curBuffer_;
}
void MediaMuxer::Track::ReleaseBuffer()
{
if (curBuffer_ != nullptr) {
consumer_->ReleaseBuffer(curBuffer_);
curBuffer_ = nullptr;
--bufferAvailableCount_;
listener_->ReleaseBuffer();
}
}
void MediaMuxer::Track::SetBufferAvailableListener(MediaMuxer *listener)
{
listener_ = listener;
}
void MediaMuxer::Track::OnBufferAvailable()
{
++bufferAvailableCount_;
MEDIA_LOG_D("Track " PUBLIC_LOG_S " bufferAvailableCount_ :" PUBLIC_LOG_D32,
mimeType_.c_str(), bufferAvailableCount_.load());
listener_->OnBufferAvailable();
}
std::shared_ptr<Plugins::MuxerPlugin> MediaMuxer::CreatePlugin(Plugins::OutputFormat format)
{
static const std::unordered_map<Plugins::OutputFormat, std::string> table = {
{Plugins::OutputFormat::DEFAULT, MimeType::MEDIA_MP4},
{Plugins::OutputFormat::MPEG_4, MimeType::MEDIA_MP4},
{Plugins::OutputFormat::M4A, MimeType::MEDIA_M4A},
{Plugins::OutputFormat::AMR, MimeType::MEDIA_AMR},
{Plugins::OutputFormat::MP3, MimeType::MEDIA_MP3},
{Plugins::OutputFormat::WAV, MimeType::MEDIA_WAV},
{Plugins::OutputFormat::AAC, MimeType::MEDIA_AAC},
{Plugins::OutputFormat::FLAC, MimeType::MEDIA_FLAC},
{Plugins::OutputFormat::OGG, MimeType::MEDIA_OGG},
{Plugins::OutputFormat::FLV, MimeType::MEDIA_FLV},
};
FALSE_RETURN_V_MSG_E(table.find(format) != table.end(), nullptr,
"The output format %{public}d is not supported!", format);
auto plugin = Plugins::PluginManagerV2::Instance().CreatePluginByMime(Plugins::PluginType::MUXER, table.at(format));
if (plugin == nullptr) {
return nullptr;
}
Media::Meta meta;
meta.SetData(MediaAVCodec::EventInfoExtentedKey::MUXER_FORMAT.data(), table.at(format));
MediaAVCodec::StatisticsEventReporter::Report(MediaAVCodec::StatisticsEventType::CONTAINER_FORMAT_INFO, meta);
return std::reinterpret_pointer_cast<Plugins::MuxerPlugin>(plugin);
}
bool MediaMuxer::CanAddTrack(const std::string &mimeType)
{
auto it = MUX_FORMAT_INFO.find(format_);
if (it == MUX_FORMAT_INFO.end()) {
return false;
}
return it->second.find(mimeType) != it->second.end();
}
bool MediaMuxer::CheckKeys(const std::string &mimeType, const std::shared_ptr<Meta> &trackDesc)
{
bool ret = true;
auto it = MUX_MIME_INFO.find(mimeType);
if (it == MUX_MIME_INFO.end()) {
return ret;
}
for (auto &key : it->second) {
if (trackDesc->Find(key.c_str()) == trackDesc->end()) {
ret = false;
MEDIA_LOG_E("The format key %{public}s not contained.", key.data());
}
}
return ret;
}
bool MediaMuxer::CheckKeysExt(const std::string &mimeType, const std::shared_ptr<Meta> &trackDesc,
Plugins::OutputFormat format)
{
bool ret = true;
if (format == Plugins::OutputFormat::AAC) {
auto it = MUX_MIME_INFO_EXT.find(mimeType);
if (it == MUX_MIME_INFO_EXT.end()) {
return ret;
}
for (auto &key : it->second) {
if (trackDesc->Find(key.c_str()) == trackDesc->end()) {
ret = false;
MEDIA_LOG_E("The format key %{public}s not contained.", key.data());
}
}
}
if (!ret) {
return ret;
}
Plugins::MediaType mediaType = Plugins::MediaType::UNKNOWN;
if (!trackDesc->Get<Tag::MEDIA_TYPE>(mediaType)) {
MEDIA_LOG_W("missing media type");
}
if (mediaType == Plugins::MediaType::AUXILIARY) {
for (auto &key : MUX_AUXILIARY_TRACK_INFO) {
if (trackDesc->Find(key.c_str()) == trackDesc->end()) {
ret = false;
MEDIA_LOG_E("The auxiliary track key %{public}s not contained.", key.data());
}
}
}
return ret;
}
std::string MediaMuxer::StateConvert(State state)
{
static const std::map<State, std::string> table = {
{State::UNINITIALIZED, "UNINITIALIZED"},
{State::INITIALIZED, "INITIALIZED"},
{State::STARTED, "STARTED"},
{State::STOPPED, "STOPPED"},
};
auto it = table.find(state);
if (it != table.end()) {
return it->second;
}
return "";
}
void MediaMuxer::OnEvent(const PluginEvent &event)
{
MEDIA_LOG_D("OnEvent");
}
void MediaMuxer::PrintWriteCount()
{
for (auto& track : tracks_) {
MEDIA_LOG_I("The track is %{public}s, the count of writes is %{public}" PRIu64,
track->mimeType_.c_str(), track->writeCount_);
}
}
}
}