Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add force transcoding to AAC #3099

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
113 changes: 12 additions & 101 deletions src/Codec/Transcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,106 +9,17 @@
*/

#if defined(ENABLE_FFMPEG)
#if !defined(_WIN32)
#include <dlfcn.h>
#endif
#include "Util/File.h"
#include "Util/uv_errno.h"
#include "Transcode.h"
#include "FFmpeg/Utils.h"
#include "Extension/AAC.h"
#include "Common/config.h"
#define MAX_DELAY_SECOND 3

using namespace std;
using namespace toolkit;

namespace mediakit {

static string ffmpeg_err(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
return errbuf;
}

std::shared_ptr<AVPacket> alloc_av_packet() {
auto pkt = std::shared_ptr<AVPacket>(av_packet_alloc(), [](AVPacket *pkt) {
av_packet_free(&pkt);
});
pkt->data = NULL; // packet data will be allocated by the encoder
pkt->size = 0;
return pkt;
}

//////////////////////////////////////////////////////////////////////////////////////////
static void on_ffmpeg_log(void *ctx, int level, const char *fmt, va_list args) {
GET_CONFIG(bool, enable_ffmpeg_log, General::kEnableFFmpegLog);
if (!enable_ffmpeg_log) {
return;
}
LogLevel lev;
switch (level) {
case AV_LOG_FATAL: lev = LError; break;
case AV_LOG_ERROR: lev = LError; break;
case AV_LOG_WARNING: lev = LWarn; break;
case AV_LOG_INFO: lev = LInfo; break;
case AV_LOG_VERBOSE: lev = LDebug; break;
case AV_LOG_DEBUG: lev = LDebug; break;
case AV_LOG_TRACE: lev = LTrace; break;
default: lev = LTrace; break;
}
LoggerWrapper::printLogV(::toolkit::getLogger(), lev, __FILE__, ctx ? av_default_item_name(ctx) : "NULL", level, fmt, args);
}

static bool setupFFmpeg_l() {
av_log_set_level(AV_LOG_TRACE);
av_log_set_flags(AV_LOG_PRINT_LEVEL);
av_log_set_callback(on_ffmpeg_log);
#if (LIBAVCODEC_VERSION_MAJOR < 58)
avcodec_register_all();
#endif
return true;
}

static void setupFFmpeg() {
static auto flag = setupFFmpeg_l();
}

static bool checkIfSupportedNvidia_l() {
#if !defined(_WIN32)
GET_CONFIG(bool, check_nvidia_dev, General::kCheckNvidiaDev);
if (!check_nvidia_dev) {
return false;
}
auto so = dlopen("libnvcuvid.so.1", RTLD_LAZY);
if (!so) {
WarnL << "libnvcuvid.so.1加载失败:" << get_uv_errmsg();
return false;
}
dlclose(so);

bool find_driver = false;
File::scanDir("/dev", [&](const string &path, bool is_dir) {
if (!is_dir && start_with(path, "/dev/nvidia")) {
//找到nvidia的驱动
find_driver = true;
return false;
}
return true;
}, false);

if (!find_driver) {
WarnL << "英伟达硬件编解码器驱动文件 /dev/nvidia* 不存在";
}
return find_driver;
#else
return false;
#endif
}

static bool checkIfSupportedNvidia() {
static auto ret = checkIfSupportedNvidia_l();
return ret;
}

//////////////////////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -316,7 +227,7 @@ static inline const AVCodec *getCodecByName(const std::vector<std::string> &code
}

FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std::vector<std::string> &codec_name) {
setupFFmpeg();
ffmpeg::setupFFmpeg();
const AVCodec *codec = nullptr;
const AVCodec *codec_default = nullptr;
if (!codec_name.empty()) {
Expand All @@ -328,7 +239,7 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
if (codec && codec->id == AV_CODEC_ID_H264) {
break;
}
if (checkIfSupportedNvidia()) {
if (ffmpeg::checkIfSupportedNvidia()) {
codec = getCodec({{"libopenh264"}, {AV_CODEC_ID_H264}, {"h264_qsv"}, {"h264_videotoolbox"}, {"h264_cuvid"}, {"h264_nvmpi"}});
} else {
codec = getCodec({{"libopenh264"}, {AV_CODEC_ID_H264}, {"h264_qsv"}, {"h264_videotoolbox"}, {"h264_nvmpi"}});
Expand All @@ -339,7 +250,7 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
if (codec && codec->id == AV_CODEC_ID_HEVC) {
break;
}
if (checkIfSupportedNvidia()) {
if (ffmpeg::checkIfSupportedNvidia()) {
codec = getCodec({{AV_CODEC_ID_HEVC}, {"hevc_qsv"}, {"hevc_videotoolbox"}, {"hevc_cuvid"}, {"hevc_nvmpi"}});
} else {
codec = getCodec({{AV_CODEC_ID_HEVC}, {"hevc_qsv"}, {"hevc_videotoolbox"}, {"hevc_nvmpi"}});
Expand Down Expand Up @@ -456,11 +367,11 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:

if (codec_default && codec_default != codec) {
//硬件编解码器打开失败,尝试软件的
WarnL << "打开解码器" << codec->name << "失败,原因是:" << ffmpeg_err(ret) << ", 再尝试打开解码器" << codec_default->name;
WarnL << "打开解码器" << codec->name << "失败,原因是:" << ffmpeg::ffmpeg_err(ret) << ", 再尝试打开解码器" << codec_default->name;
codec = codec_default;
continue;
}
throw std::runtime_error(StrPrinter << "打开解码器" << codec->name << "失败:" << ffmpeg_err(ret));
throw std::runtime_error(StrPrinter << "打开解码器" << codec->name << "失败:" << ffmpeg::ffmpeg_err(ret));
}
}

Expand All @@ -484,7 +395,7 @@ void FFmpegDecoder::flush() {
break;
}
if (ret < 0) {
WarnL << "avcodec_receive_frame failed:" << ffmpeg_err(ret);
WarnL << "avcodec_receive_frame failed:" << ffmpeg::ffmpeg_err(ret);
break;
}
onDecode(out_frame);
Expand Down Expand Up @@ -526,7 +437,7 @@ bool FFmpegDecoder::inputFrame(const Frame::Ptr &frame, bool live, bool async, b
bool FFmpegDecoder::decodeFrame(const char *data, size_t size, uint64_t dts, uint64_t pts, bool live, bool key_frame) {
TimeTicker2(30, TraceL);

auto pkt = alloc_av_packet();
auto pkt = ffmpeg::alloc_av_packet();
pkt->data = (uint8_t *) data;
pkt->size = size;
pkt->dts = dts;
Expand All @@ -538,7 +449,7 @@ bool FFmpegDecoder::decodeFrame(const char *data, size_t size, uint64_t dts, uin
auto ret = avcodec_send_packet(_context.get(), pkt.get());
if (ret < 0) {
if (ret != AVERROR_INVALIDDATA) {
WarnL << "avcodec_send_packet failed:" << ffmpeg_err(ret);
WarnL << "avcodec_send_packet failed:" << ffmpeg::ffmpeg_err(ret);
}
return false;
}
Expand All @@ -550,7 +461,7 @@ bool FFmpegDecoder::decodeFrame(const char *data, size_t size, uint64_t dts, uin
break;
}
if (ret < 0) {
WarnL << "avcodec_receive_frame failed:" << ffmpeg_err(ret);
WarnL << "avcodec_receive_frame failed:" << ffmpeg::ffmpeg_err(ret);
break;
}
if (live && pts - out_frame->get()->pts > MAX_DELAY_SECOND * 1000 && _ticker.createdTime() > 10 * 1000) {
Expand Down Expand Up @@ -614,7 +525,7 @@ FFmpegFrame::Ptr FFmpegSwr::inputFrame(const FFmpegFrame::Ptr &frame) {

int ret = 0;
if (0 != (ret = swr_convert_frame(_ctx, out->get(), frame->get()))) {
WarnL << "swr_convert_frame failed:" << ffmpeg_err(ret);
WarnL << "swr_convert_frame failed:" << ffmpeg::ffmpeg_err(ret);
return nullptr;
}
return out;
Expand Down Expand Up @@ -680,7 +591,7 @@ FFmpegFrame::Ptr FFmpegSws::inputFrame(const FFmpegFrame::Ptr &frame, int &ret,
}
}
if (0 >= (ret = sws_scale(_ctx, frame->get()->data, frame->get()->linesize, 0, frame->get()->height, out->get()->data, out->get()->linesize))) {
WarnL << "sws_scale failed:" << ffmpeg_err(ret);
WarnL << "sws_scale failed:" << ffmpeg::ffmpeg_err(ret);
return nullptr;
}

Expand Down
1 change: 1 addition & 0 deletions src/Common/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ const string kBenchmarkMode = "benchmark_mode";
const string kWaitTrackReady = "wait_track_ready";
const string kPlayTrack = "play_track";
const string kProxyUrl = "proxy_url";
const string kForceToAac = "force_to_aac";
} // namespace Client

} // namespace mediakit
Expand Down
20 changes: 20 additions & 0 deletions src/Common/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,34 +379,54 @@ extern const std::string kGopCache;
* rtsp/rtmp播放器、推流器相关设置名,
* 这些设置项都不是配置文件用
* 只用于设置某个播放器或推流器实例用
* rtsp/rtmp Player/Pusher instance config
* these config not for ini file
* just for PlayerBase/PusherBase instance
*/
namespace Client {
// 指定网卡ip
// Setting network card ip
extern const std::string kNetAdapter;
// 设置rtp传输类型,可选项有0(tcp,默认)、1(udp)、2(组播)
// 设置方法:player[PlayerBase::kRtpType] = 0/1/2;
// Setting rtp transmission type, optional 0 (tcp, default), 1 (udp), 2 (multicast)
// Setting method: player[PlayerBase::kRtpType] = 0/1/2;
extern const std::string kRtpType;
// rtsp认证用户名
// rtsp authentication username
extern const std::string kRtspUser;
// rtsp认证用用户密码,可以是明文也可以是md5,md5密码生成方式 md5(username:realm:password)
// password are used for RTSP authentication, which can be either plain text or MD5 encrypted. The MD5 password is generated using the following format: md5(username:realm:password).
extern const std::string kRtspPwd;
// rtsp认证用用户密码是否为md5类型
// Whether the user password used for RTSP authentication is MD5 type
extern const std::string kRtspPwdIsMD5;
// 握手超时时间,默认10,000 毫秒
// Handshake timeout time, default 10,000 milliseconds
extern const std::string kTimeoutMS;
// rtp/rtmp包接收超时时间,默认5000秒
// rtp/rtmp packet receive timeout time, default 5000 seconds
extern const std::string kMediaTimeoutMS;
// rtsp/rtmp心跳时间,默认5000毫秒
// rtsp/rtmp heartbeat time, default 5000 milliseconds
extern const std::string kBeatIntervalMS;
// 是否为性能测试模式,性能测试模式开启后不会解析rtp或rtmp包
// Whether it is performance test mode, rtp or rtmp packet will not be parsed after performance test mode is turned on
extern const std::string kBenchmarkMode;
// 播放器在触发播放成功事件时,是否等待所有track ready时再回调
// Should the player wait for all tracks to be ready before triggering the playback success event callback?
extern const std::string kWaitTrackReady;
// rtsp播放指定track,可选项有0(不指定,默认)、1(视频)、2(音频)
// 设置方法:player[Client::kPlayTrack] = 0/1/2;
// rtsp play specified track, optional 0 (not specified, default), 1 (video), 2 (audio)
// Setting method: player[Client::kPlayTrack] = 0/1/2;
extern const std::string kPlayTrack;
//设置代理url,目前只支持http协议
// Setting proxy url, currently only http protocol is supported
extern const std::string kProxyUrl;
//强制转码为AAC
// Force transcoding to AAC
extern const std::string kForceToAac;
} // namespace Client
} // namespace mediakit

Expand Down