|
|
@@ -1,13 +1,67 @@
|
|
|
#include "audio_encoder.h"
|
|
|
|
|
|
#include <QDebug>
|
|
|
+#include <algorithm>
|
|
|
+
|
|
|
+std::vector<std::string> Encoder<MediaType::AUDIO>::_usableEncoders;
|
|
|
+
|
|
|
+const std::vector<std::string>& Encoder<MediaType::AUDIO>::GetUsableEncoders()
|
|
|
+{
|
|
|
+ if (_usableEncoders.empty()) {
|
|
|
+ _FindUsableEncoders();
|
|
|
+ }
|
|
|
+ return _usableEncoders;
|
|
|
+}
|
|
|
+
|
|
|
+void Encoder<MediaType::AUDIO>::_FindUsableEncoders()
|
|
|
+{
|
|
|
+ qDebug() << "AudioEncoder: Finding usable encoders...";
|
|
|
+
|
|
|
+ Param param;
|
|
|
+ param.bitRate = 128000; // 128 kbps
|
|
|
+ Encoder encoder;
|
|
|
+ AVFormatContext* fmtCtx = nullptr;
|
|
|
+ __CheckNo(avformat_alloc_output_context2(&fmtCtx, nullptr, nullptr, "test.mp4") >= 0);
|
|
|
+
|
|
|
+ for (const auto& name : _encoderNames) {
|
|
|
+ param.name = name;
|
|
|
+ qDebug() << "AudioEncoder: Testing encoder:" << name;
|
|
|
+ if (encoder.Open(param, fmtCtx)) {
|
|
|
+ _usableEncoders.push_back(name);
|
|
|
+ qDebug() << "AudioEncoder: Encoder" << name << "is usable";
|
|
|
+ } else {
|
|
|
+ qDebug() << "AudioEncoder: Encoder" << name << "failed to open";
|
|
|
+ }
|
|
|
+ encoder.Close();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 简单的优先级排序:AAC优先,其次Opus,最后MP3
|
|
|
+ if (!_usableEncoders.empty()) {
|
|
|
+ auto priority = [](const std::string& n) -> int {
|
|
|
+ if (n == "aac" || n == "libfdk_aac") return 0; // 与mp4/flv/rtmp兼容性最好
|
|
|
+ if (n == "libopus" || n == "opus") return 1; // 需容器/推流支持
|
|
|
+ if (n == "libmp3lame") return 2; // 一般不用于flv
|
|
|
+ return 100;
|
|
|
+ };
|
|
|
+ std::sort(_usableEncoders.begin(), _usableEncoders.end(), [&](const std::string& a, const std::string& b) {
|
|
|
+ int pa = priority(a);
|
|
|
+ int pb = priority(b);
|
|
|
+ if (pa != pb) return pa < pb;
|
|
|
+ return a < b;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ qDebug() << "AudioEncoder: Found" << _usableEncoders.size() << "usable encoders";
|
|
|
+ Free(fmtCtx, [&fmtCtx] { avformat_free_context(fmtCtx); });
|
|
|
+}
|
|
|
|
|
|
bool Encoder<MediaType::AUDIO>::Open(const Param& audioParam, AVFormatContext* fmtCtx)
|
|
|
{
|
|
|
Close();
|
|
|
_isOpen = false;
|
|
|
|
|
|
- qDebug() << "AudioEncoder::Open: Initializing audio encoder with bitrate" << audioParam.bitRate;
|
|
|
+ qDebug() << "AudioEncoder::Open: Initializing audio encoder with bitrate" << audioParam.bitRate
|
|
|
+ << ", name:" << (audioParam.name.empty() ? "<auto>" : audioParam.name.c_str());
|
|
|
|
|
|
if (!_Init(audioParam, fmtCtx)) {
|
|
|
qDebug() << "AudioEncoder::Open failed: Audio encoder initialization failed";
|
|
|
@@ -28,6 +82,7 @@ bool Encoder<MediaType::AUDIO>::Open(const Param& audioParam, AVFormatContext* f
|
|
|
qDebug() << "AudioEncoder::Open: Audio encoder opened successfully";
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
void Encoder<MediaType::AUDIO>::Close()
|
|
|
{
|
|
|
if (_codecCtx != nullptr) {
|
|
|
@@ -35,10 +90,20 @@ void Encoder<MediaType::AUDIO>::Close()
|
|
|
}
|
|
|
Free(_codecCtx, [this] { avcodec_free_context(&_codecCtx); });
|
|
|
}
|
|
|
+
|
|
|
bool Encoder<MediaType::AUDIO>::_Init(const Param& audioParam, AVFormatContext* fmtCtx)
|
|
|
{
|
|
|
- // codec
|
|
|
- __CheckBool(_codec = avcodec_find_encoder(AV_CODEC_ID_AAC));
|
|
|
+ // codec 选择:优先按名称,其次回退到容器默认音频编码器,再次回退到AAC
|
|
|
+ if (!audioParam.name.empty()) {
|
|
|
+ _codec = avcodec_find_encoder_by_name(audioParam.name.c_str());
|
|
|
+ }
|
|
|
+ if (!_codec && fmtCtx && fmtCtx->oformat && fmtCtx->oformat->audio_codec != AV_CODEC_ID_NONE) {
|
|
|
+ _codec = avcodec_find_encoder(fmtCtx->oformat->audio_codec);
|
|
|
+ }
|
|
|
+ if (!_codec) {
|
|
|
+ _codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
|
|
|
+ }
|
|
|
+ __CheckBool(_codec);
|
|
|
// codeccontext
|
|
|
__CheckBool(_codecCtx = avcodec_alloc_context3(_codec));
|
|
|
_codecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
|
|
|
@@ -46,8 +111,8 @@ bool Encoder<MediaType::AUDIO>::_Init(const Param& audioParam, AVFormatContext*
|
|
|
_codecCtx->sample_rate = AUDIO_SAMPLE_RATE;
|
|
|
AVChannelLayout layout;
|
|
|
layout.order = AV_CHANNEL_ORDER_NATIVE;
|
|
|
- layout.nb_channels = 1;
|
|
|
- layout.u.mask = AV_CH_LAYOUT_MONO;
|
|
|
+ layout.nb_channels = AUDIO_CHANNEL;
|
|
|
+ layout.u.mask = (AUDIO_CHANNEL == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO);
|
|
|
av_channel_layout_copy(&_codecCtx->ch_layout, &layout);
|
|
|
if (fmtCtx->oformat->flags & AVFMT_GLOBALHEADER) {
|
|
|
_codecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|