mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
FFmpeg : logging (#2774)
* added a couple more audio examples * logging DONE * bug fix on certain compilers * use a C-style stack allocated array. This reduces heap allocations * reduce stack string size. replace '\n' rather than remove * - register_ffmpeg() doesn't need to return anything - refactored loggers - added verbose option * bug fix * unused variable warning * Update docs * Update docs --------- Co-authored-by: pf <pf@me> Co-authored-by: Adrià Arrufat <1671644+arrufat@users.noreply.github.com>
This commit is contained in:
parent
5f7026ab52
commit
53c4be9026
@ -29,6 +29,22 @@ namespace dlib
|
||||
- Returns a string description of a channel layout, where layout is e.g. AV_CH_LAYOUT_STEREO
|
||||
!*/
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
dlib::logger& logger_ffmpeg();
|
||||
/*!
|
||||
ensures
|
||||
- Returns a global logger used by the internal ffmpeg libraries.
|
||||
- You may set the logging level using .set_level() to supress or enable certain logs.
|
||||
!*/
|
||||
|
||||
dlib::logger& logger_dlib_wrapper();
|
||||
/*!
|
||||
ensures
|
||||
- Returns a global logger used by dlib's ffmpeg wrappers.
|
||||
- You may set the logging level using .set_level() to supress or enable certain logs.
|
||||
!*/
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
class frame
|
||||
|
@ -75,8 +75,7 @@ namespace dlib
|
||||
decoder_extractor(
|
||||
const args& a,
|
||||
av_ptr<AVCodecContext> pCodecCtx_,
|
||||
const AVCodec* codec,
|
||||
std::shared_ptr<logger> log_
|
||||
const AVCodec* codec
|
||||
);
|
||||
|
||||
bool is_open() const noexcept;
|
||||
@ -108,7 +107,6 @@ namespace dlib
|
||||
resizer resizer_image;
|
||||
resampler resizer_audio;
|
||||
std::queue<frame> frame_queue;
|
||||
std::shared_ptr<logger> log;
|
||||
};
|
||||
}
|
||||
|
||||
@ -154,7 +152,6 @@ namespace dlib
|
||||
details::av_ptr<AVCodecParserContext> parser;
|
||||
details::av_ptr<AVPacket> packet;
|
||||
details::decoder_extractor extractor;
|
||||
std::shared_ptr<logger> log;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
@ -236,7 +233,6 @@ namespace dlib
|
||||
int stream_id_video{-1};
|
||||
int stream_id_audio{-1};
|
||||
std::queue<frame> frame_queue;
|
||||
std::shared_ptr<logger> log;
|
||||
} st;
|
||||
};
|
||||
|
||||
@ -262,9 +258,8 @@ namespace dlib
|
||||
inline decoder_extractor::decoder_extractor(
|
||||
const args& a,
|
||||
av_ptr<AVCodecContext> pCodecCtx_,
|
||||
const AVCodec* codec,
|
||||
std::shared_ptr<logger> log_
|
||||
) : log(log_)
|
||||
const AVCodec* codec
|
||||
)
|
||||
{
|
||||
args_ = a;
|
||||
avframe = make_avframe();
|
||||
@ -279,7 +274,7 @@ namespace dlib
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
(*log) << LERROR << "avcodec_open2() failed : " << get_av_error(ret).c_str();
|
||||
logger_dlib_wrapper() << LERROR << "avcodec_open2() failed : " << get_av_error(ret).c_str();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -358,7 +353,7 @@ namespace dlib
|
||||
} else {
|
||||
pCodecCtx = nullptr;
|
||||
state = EXTRACT_ERROR;
|
||||
(*log) << LERROR << "avcodec_send_packet() failed : " << get_av_error(ret);
|
||||
logger_dlib_wrapper() << LERROR << "avcodec_send_packet() failed : " << get_av_error(ret);
|
||||
}
|
||||
};
|
||||
|
||||
@ -378,7 +373,7 @@ namespace dlib
|
||||
{
|
||||
pCodecCtx = nullptr;
|
||||
state = EXTRACT_ERROR;
|
||||
(*log) << LERROR << "avcodec_receive_frame() failed : " << get_av_error(ret);
|
||||
logger_dlib_wrapper() << LERROR << "avcodec_receive_frame() failed : " << get_av_error(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -454,24 +449,23 @@ namespace dlib
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
inline decoder::decoder(const args &a)
|
||||
: log(std::make_shared<logger>("ffmpeg::decoder"))
|
||||
{
|
||||
using namespace details;
|
||||
|
||||
DLIB_ASSERT(a.args_codec.codec != AV_CODEC_ID_NONE || a.args_codec.codec_name != "", "At least args_codec.codec or args_codec.codec_name must be set");
|
||||
|
||||
const bool init = details::register_ffmpeg::get();
|
||||
details::register_ffmpeg();
|
||||
|
||||
const AVCodec* pCodec = nullptr;
|
||||
|
||||
if (a.args_codec.codec != AV_CODEC_ID_NONE)
|
||||
pCodec = init ? avcodec_find_decoder(a.args_codec.codec) : nullptr;
|
||||
pCodec = avcodec_find_decoder(a.args_codec.codec);
|
||||
else if (!a.args_codec.codec_name.empty())
|
||||
pCodec = init ? avcodec_find_decoder_by_name(a.args_codec.codec_name.c_str()) : nullptr;
|
||||
pCodec = avcodec_find_decoder_by_name(a.args_codec.codec_name.c_str());
|
||||
|
||||
if (!pCodec)
|
||||
{
|
||||
(*log) << LERROR
|
||||
logger_dlib_wrapper() << LERROR
|
||||
<< "Codec "
|
||||
<< avcodec_get_name(a.args_codec.codec)
|
||||
<< " / "
|
||||
@ -484,14 +478,14 @@ namespace dlib
|
||||
|
||||
if (!pCodecCtx)
|
||||
{
|
||||
(*log) << LERROR << "avcodec_alloc_context3() failed to allocate codec context for " << pCodec->name;
|
||||
logger_dlib_wrapper() << LERROR << "avcodec_alloc_context3() failed to allocate codec context for " << pCodec->name;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pCodecCtx->codec_id == AV_CODEC_ID_AAC)
|
||||
pCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
|
||||
|
||||
extractor = decoder_extractor{{a.args_codec, a.args_image, a.args_audio, pCodecCtx->time_base}, std::move(pCodecCtx), pCodec, log};
|
||||
extractor = decoder_extractor{{a.args_codec, a.args_image, a.args_audio, pCodecCtx->time_base}, std::move(pCodecCtx), pCodec};
|
||||
if (!extractor.is_open())
|
||||
return;
|
||||
|
||||
@ -504,7 +498,7 @@ namespace dlib
|
||||
parser.reset(av_parser_init(pCodec->id));
|
||||
if (!parser)
|
||||
{
|
||||
(*log) << LERROR << "av_parser_init() failed codec " << pCodec->name << " not found";
|
||||
logger_dlib_wrapper() << LERROR << "av_parser_init() failed codec " << pCodec->name << " not found";
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -550,7 +544,7 @@ namespace dlib
|
||||
);
|
||||
|
||||
if (ret < 0)
|
||||
return fail(*log, "AV : error while parsing encoded buffer");
|
||||
return fail("AV : error while parsing encoded buffer");
|
||||
|
||||
encoded += ret;
|
||||
nencoded -= ret;
|
||||
@ -676,10 +670,9 @@ namespace dlib
|
||||
using namespace std::chrono;
|
||||
using namespace details;
|
||||
|
||||
const bool init = details::register_ffmpeg::get();
|
||||
details::register_ffmpeg();
|
||||
|
||||
st = {};
|
||||
st.log = std::make_shared<logger>("ffmpeg::demuxer");
|
||||
st.args_ = a;
|
||||
|
||||
AVFormatContext* pFormatCtx = avformat_alloc_context();
|
||||
@ -730,7 +723,7 @@ namespace dlib
|
||||
opts.get());
|
||||
|
||||
if (ret != 0)
|
||||
return fail(*st.log, "avformat_open_input() failed with error : ", get_av_error(ret));
|
||||
return fail("avformat_open_input() failed with error : ", get_av_error(ret));
|
||||
|
||||
if (opts.size() > 0)
|
||||
{
|
||||
@ -744,7 +737,7 @@ namespace dlib
|
||||
ret = avformat_find_stream_info(st.pFormatCtx.get(), NULL);
|
||||
|
||||
if (ret < 0)
|
||||
return fail(*st.log, "avformat_find_stream_info() failed with error : ", get_av_error(ret));
|
||||
return fail("avformat_find_stream_info() failed with error : ", get_av_error(ret));
|
||||
|
||||
const auto setup_stream = [&](bool is_video)
|
||||
{
|
||||
@ -757,26 +750,26 @@ namespace dlib
|
||||
return true; //You might be asking for both video and audio but only video is available. That's OK. Just provide video.
|
||||
|
||||
else if (stream_id == AVERROR_DECODER_NOT_FOUND)
|
||||
return fail(*st.log, "av_find_best_stream() : decoder not found for stream type : ", av_get_media_type_string(media_type));
|
||||
return fail("av_find_best_stream() : decoder not found for stream type : ", av_get_media_type_string(media_type));
|
||||
|
||||
else if (stream_id < 0)
|
||||
return fail(*st.log, "av_find_best_stream() failed : ", get_av_error(stream_id));
|
||||
return fail("av_find_best_stream() failed : ", get_av_error(stream_id));
|
||||
|
||||
av_ptr<AVCodecContext> pCodecCtx{avcodec_alloc_context3(pCodec)};
|
||||
|
||||
if (!pCodecCtx)
|
||||
return fail(*st.log, "avcodec_alloc_context3() failed to allocate codec context for ", pCodec->name);
|
||||
return fail("avcodec_alloc_context3() failed to allocate codec context for ", pCodec->name);
|
||||
|
||||
const int ret = avcodec_parameters_to_context(pCodecCtx.get(), st.pFormatCtx->streams[stream_id]->codecpar);
|
||||
if (ret < 0)
|
||||
return fail(*st.log, "avcodec_parameters_to_context() failed : ", get_av_error(ret));
|
||||
return fail("avcodec_parameters_to_context() failed : ", get_av_error(ret));
|
||||
|
||||
if (pCodecCtx->codec_type == AVMEDIA_TYPE_VIDEO)
|
||||
{
|
||||
if (pCodecCtx->height == 0 ||
|
||||
pCodecCtx->width == 0 ||
|
||||
pCodecCtx->pix_fmt == AV_PIX_FMT_NONE)
|
||||
return fail(*st.log, "Codec parameters look wrong : (h,w,pixel_fmt) : (",
|
||||
return fail("Codec parameters look wrong : (h,w,pixel_fmt) : (",
|
||||
pCodecCtx->height, ",",
|
||||
pCodecCtx->width, ",",
|
||||
get_pixel_fmt_str(pCodecCtx->pix_fmt), ")");
|
||||
@ -788,13 +781,13 @@ namespace dlib
|
||||
if (pCodecCtx->sample_rate == 0 ||
|
||||
pCodecCtx->sample_fmt == AV_SAMPLE_FMT_NONE ||
|
||||
channel_layout_empty(pCodecCtx.get()))
|
||||
return fail(*st.log,"Codec parameters look wrong :",
|
||||
return fail("Codec parameters look wrong :",
|
||||
" sample_rate : ", pCodecCtx->sample_rate,
|
||||
" sample format : ", get_audio_fmt_str(pCodecCtx->sample_fmt),
|
||||
" channel layout : ", get_channel_layout_str(pCodecCtx.get()));
|
||||
}
|
||||
else
|
||||
return fail(*st.log,"Unrecognized media type ", pCodecCtx->codec_type);
|
||||
return fail("Unrecognized media type ", pCodecCtx->codec_type);
|
||||
|
||||
if (is_video)
|
||||
{
|
||||
@ -804,7 +797,7 @@ namespace dlib
|
||||
args.args_image = st.args_.args_image;
|
||||
args.time_base = st.pFormatCtx->streams[stream_id]->time_base;
|
||||
return args;
|
||||
}(), std::move(pCodecCtx), pCodec, st.log};
|
||||
}(), std::move(pCodecCtx), pCodec};
|
||||
|
||||
st.stream_id_video = stream_id;
|
||||
}
|
||||
@ -816,7 +809,7 @@ namespace dlib
|
||||
args.args_audio = st.args_.args_audio;
|
||||
args.time_base = st.pFormatCtx->streams[stream_id]->time_base;
|
||||
return args;
|
||||
}(), std::move(pCodecCtx), pCodec, st.log};
|
||||
}(), std::move(pCodecCtx), pCodec};
|
||||
|
||||
st.stream_id_audio = stream_id;
|
||||
}
|
||||
@ -831,12 +824,12 @@ namespace dlib
|
||||
return false;
|
||||
|
||||
if (!st.channel_audio.is_open() && !st.channel_video.is_open())
|
||||
return fail(*st.log, "At least one of video and audio channels must be enabled");
|
||||
return fail("At least one of video and audio channels must be enabled");
|
||||
|
||||
populate_metadata();
|
||||
|
||||
st.packet = make_avpacket();
|
||||
return init;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool demuxer::object_alive() const noexcept
|
||||
@ -903,7 +896,7 @@ namespace dlib
|
||||
return false;
|
||||
|
||||
else if (ret < 0)
|
||||
return fail(*st.log, "av_read_frame() failed : ", get_av_error(ret));
|
||||
return fail("av_read_frame() failed : ", get_av_error(ret));
|
||||
|
||||
if (st.packet->stream_index == st.stream_id_video)
|
||||
channel = &st.channel_video;
|
||||
|
@ -91,8 +91,7 @@ namespace dlib
|
||||
|
||||
encoder(
|
||||
const args& a,
|
||||
std::function<bool(AVCodecContext*,AVPacket*)> sink,
|
||||
std::shared_ptr<logger> log_
|
||||
std::function<bool(AVCodecContext*,AVPacket*)> sink
|
||||
);
|
||||
|
||||
bool open();
|
||||
@ -106,7 +105,6 @@ namespace dlib
|
||||
details::resampler resizer_audio;
|
||||
details::audio_fifo fifo;
|
||||
std::function<bool(AVCodecContext*,AVPacket*)> sink;
|
||||
std::shared_ptr<logger> log;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
@ -177,7 +175,6 @@ namespace dlib
|
||||
std::chrono::system_clock::time_point connecting_time{};
|
||||
std::chrono::system_clock::time_point connected_time{};
|
||||
std::chrono::system_clock::time_point last_read_time{};
|
||||
std::shared_ptr<logger> log;
|
||||
} st;
|
||||
};
|
||||
|
||||
@ -207,8 +204,7 @@ namespace dlib
|
||||
|
||||
inline void check_properties(
|
||||
const AVCodec* pCodec,
|
||||
AVCodecContext* pCodecCtx,
|
||||
logger& log
|
||||
AVCodecContext* pCodecCtx
|
||||
)
|
||||
{
|
||||
// Video properties
|
||||
@ -227,7 +223,7 @@ namespace dlib
|
||||
|
||||
if (!framerate_supported)
|
||||
{
|
||||
log << LINFO
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "Requested framerate "
|
||||
<< pCodecCtx->framerate.num / pCodecCtx->framerate.den
|
||||
<< " not supported. Changing to default "
|
||||
@ -252,7 +248,7 @@ namespace dlib
|
||||
|
||||
if (!pix_fmt_supported)
|
||||
{
|
||||
log << LINFO
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "Requested pixel format "
|
||||
<< av_get_pix_fmt_name(pCodecCtx->pix_fmt)
|
||||
<< " not supported. Changing to default "
|
||||
@ -278,7 +274,7 @@ namespace dlib
|
||||
|
||||
if (!sample_rate_supported)
|
||||
{
|
||||
log << LINFO
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "Requested sample rate "
|
||||
<< pCodecCtx->sample_rate
|
||||
<< " not supported. Changing to default "
|
||||
@ -303,7 +299,7 @@ namespace dlib
|
||||
|
||||
if (!sample_fmt_supported)
|
||||
{
|
||||
log << LINFO
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "Requested sample format "
|
||||
<< av_get_sample_fmt_name(pCodecCtx->sample_fmt)
|
||||
<< " not supported. Changing to default "
|
||||
@ -329,7 +325,7 @@ namespace dlib
|
||||
|
||||
if (!channel_layout_supported)
|
||||
{
|
||||
log << LINFO
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "Channel layout "
|
||||
<< details::get_channel_layout_str(pCodecCtx)
|
||||
<< " not supported. Changing to default "
|
||||
@ -354,7 +350,7 @@ namespace dlib
|
||||
|
||||
if (!channel_layout_supported)
|
||||
{
|
||||
log << LINFO
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "Channel layout "
|
||||
<< details::get_channel_layout_str(pCodecCtx)
|
||||
<< " not supported. Changing to default "
|
||||
@ -372,17 +368,15 @@ namespace dlib
|
||||
std::function<bool(std::size_t, const char*)> sink
|
||||
) : encoder(a, [sink](AVCodecContext*, AVPacket* pkt) {
|
||||
return sink(pkt->size, (const char*)pkt->data);
|
||||
}, std::make_shared<logger>("ffmpeg::encoder"))
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
inline encoder::encoder(
|
||||
const args& a,
|
||||
std::function<bool(AVCodecContext*,AVPacket*)> sink_,
|
||||
std::shared_ptr<logger> log_
|
||||
std::function<bool(AVCodecContext*,AVPacket*)> sink_
|
||||
) : args_(a),
|
||||
sink(std::move(sink_)),
|
||||
log(log_)
|
||||
sink(std::move(sink_))
|
||||
{
|
||||
if (!open())
|
||||
pCodecCtx = nullptr;
|
||||
@ -400,22 +394,22 @@ namespace dlib
|
||||
|
||||
DLIB_CASSERT(sink != nullptr, "must provide an appriate sink callback");
|
||||
|
||||
const bool init = details::register_ffmpeg::get(); // This must be used somewhere otherwise compiler might optimize it away.
|
||||
details::register_ffmpeg();
|
||||
|
||||
packet = make_avpacket();
|
||||
const AVCodec* pCodec = nullptr;
|
||||
|
||||
if (args_.args_codec.codec != AV_CODEC_ID_NONE)
|
||||
pCodec = init ? avcodec_find_encoder(args_.args_codec.codec) : nullptr;
|
||||
pCodec = avcodec_find_encoder(args_.args_codec.codec);
|
||||
else if (!args_.args_codec.codec_name.empty())
|
||||
pCodec = init ? avcodec_find_encoder_by_name(args_.args_codec.codec_name.c_str()) : nullptr;
|
||||
pCodec = avcodec_find_encoder_by_name(args_.args_codec.codec_name.c_str());
|
||||
|
||||
if (!pCodec)
|
||||
return fail(*log, "Codec ", avcodec_get_name(args_.args_codec.codec), " or ", args_.args_codec.codec_name, " not found");
|
||||
return fail("Codec ", avcodec_get_name(args_.args_codec.codec), " or ", args_.args_codec.codec_name, " not found");
|
||||
|
||||
pCodecCtx.reset(avcodec_alloc_context3(pCodec));
|
||||
if (!pCodecCtx)
|
||||
return fail(*log, "AV : failed to allocate codec context for ", pCodec->name, " : likely ran out of memory");
|
||||
return fail("AV : failed to allocate codec context for ", pCodec->name, " : likely ran out of memory");
|
||||
|
||||
if (args_.args_codec.bitrate > 0)
|
||||
pCodecCtx->bit_rate = args_.args_codec.bitrate;
|
||||
@ -431,14 +425,14 @@ namespace dlib
|
||||
args_.args_image.fmt == AV_PIX_FMT_NONE ||
|
||||
args_.args_image.framerate <= 0)
|
||||
{
|
||||
return fail(*log, pCodec->name, " is an image codec. height, width, fmt (pixel format) and framerate must be set");
|
||||
return fail(pCodec->name, " is an image codec. height, width, fmt (pixel format) and framerate must be set");
|
||||
}
|
||||
|
||||
pCodecCtx->height = args_.args_image.h;
|
||||
pCodecCtx->width = args_.args_image.w;
|
||||
pCodecCtx->pix_fmt = args_.args_image.fmt;
|
||||
pCodecCtx->framerate = AVRational{args_.args_image.framerate, 1};
|
||||
check_properties(pCodec, pCodecCtx.get(), *log);
|
||||
check_properties(pCodec, pCodecCtx.get());
|
||||
pCodecCtx->time_base = inv(pCodecCtx->framerate);
|
||||
|
||||
//don't know what src options are, but at least dst options are set
|
||||
@ -451,13 +445,13 @@ namespace dlib
|
||||
args_.args_audio.channel_layout <= 0 ||
|
||||
args_.args_audio.fmt == AV_SAMPLE_FMT_NONE)
|
||||
{
|
||||
return fail(*log, pCodec->name, " is an audio codec. sample_rate, channel_layout and fmt (sample format) must be set");
|
||||
return fail(pCodec->name, " is an audio codec. sample_rate, channel_layout and fmt (sample format) must be set");
|
||||
}
|
||||
|
||||
pCodecCtx->sample_rate = args_.args_audio.sample_rate;
|
||||
pCodecCtx->sample_fmt = args_.args_audio.fmt;
|
||||
set_layout(pCodecCtx.get(), args_.args_audio.channel_layout);
|
||||
check_properties(pCodec, pCodecCtx.get(), *log);
|
||||
check_properties(pCodec, pCodecCtx.get());
|
||||
pCodecCtx->time_base = AVRational{ 1, pCodecCtx->sample_rate };
|
||||
|
||||
if (pCodecCtx->codec_id == AV_CODEC_ID_AAC) {
|
||||
@ -474,7 +468,7 @@ namespace dlib
|
||||
av_dict opt = args_.args_codec.codec_options;
|
||||
const int ret = avcodec_open2(pCodecCtx.get(), pCodec, opt.get());
|
||||
if (ret < 0)
|
||||
return fail(*log, "avcodec_open2() failed : ", get_av_error(ret));
|
||||
return fail("avcodec_open2() failed : ", get_av_error(ret));
|
||||
|
||||
if (pCodec->type == AVMEDIA_TYPE_AUDIO)
|
||||
{
|
||||
@ -561,7 +555,7 @@ namespace dlib
|
||||
} else {
|
||||
open_ = false;
|
||||
state = ENCODE_ERROR;
|
||||
(*log) << LERROR << "avcodec_send_frame() failed : " << get_av_error(ret);
|
||||
logger_dlib_wrapper() << LERROR << "avcodec_send_frame() failed : " << get_av_error(ret);
|
||||
}
|
||||
};
|
||||
|
||||
@ -581,7 +575,7 @@ namespace dlib
|
||||
{
|
||||
open_ = false;
|
||||
state = ENCODE_ERROR;
|
||||
(*log) << LERROR << "avcodec_receive_packet() failed : " << get_av_error(ret);
|
||||
logger_dlib_wrapper() << LERROR << "avcodec_receive_packet() failed : " << get_av_error(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -656,11 +650,10 @@ namespace dlib
|
||||
using namespace details;
|
||||
|
||||
st = {};
|
||||
st.log = std::make_shared<logger>("ffmpeg::muxer");
|
||||
st.args_ = a;
|
||||
|
||||
if (!st.args_.enable_audio && !st.args_.enable_image)
|
||||
return fail(*st.log, "You need to set at least one of `enable_audio` or `enable_image`");
|
||||
return fail("You need to set at least one of `enable_audio` or `enable_image`");
|
||||
|
||||
{
|
||||
st.connecting_time = system_clock::now();
|
||||
@ -672,7 +665,7 @@ namespace dlib
|
||||
int ret = avformat_alloc_output_context2(&pFormatCtx, nullptr, format_name, filename);
|
||||
|
||||
if (ret < 0)
|
||||
return fail(*st.log, "avformat_alloc_output_context2() failed : ", get_av_error(ret));
|
||||
return fail("avformat_alloc_output_context2() failed : ", get_av_error(ret));
|
||||
|
||||
st.pFormatCtx.reset(pFormatCtx);
|
||||
}
|
||||
@ -703,8 +696,7 @@ namespace dlib
|
||||
const auto handle_packet =
|
||||
[
|
||||
pFormatCtx = st.pFormatCtx.get(),
|
||||
stream_id = stream_counter,
|
||||
log = st.log
|
||||
stream_id = stream_counter
|
||||
]
|
||||
(
|
||||
AVCodecContext* pCodecCtx,
|
||||
@ -716,7 +708,7 @@ namespace dlib
|
||||
pkt->stream_index = stream_id;
|
||||
int ret = av_interleaved_write_frame(pFormatCtx, pkt);
|
||||
if (ret < 0)
|
||||
(*log) << LERROR << "av_interleaved_write_frame() failed : " << get_av_error(ret);
|
||||
logger_dlib_wrapper() << LERROR << "av_interleaved_write_frame() failed : " << get_av_error(ret);
|
||||
return ret == 0;
|
||||
};
|
||||
|
||||
@ -729,18 +721,18 @@ namespace dlib
|
||||
supported.codec_name == args.args_codec.codec_name;
|
||||
}) == end(supported_codecs))
|
||||
{
|
||||
(*st.log) << LERROR
|
||||
<< "Codec " << avcodec_get_name(args.args_codec.codec) << " or " << args.args_codec.codec_name
|
||||
<< " cannot be stored in this file";
|
||||
(*st.log) << LINFO
|
||||
<< "List of supported codecs for muxer " << st.pFormatCtx->oformat->name << " in this installation of ffmpeg:";
|
||||
logger_dlib_wrapper() << LERROR
|
||||
<< "Codec " << avcodec_get_name(args.args_codec.codec) << " or " << args.args_codec.codec_name
|
||||
<< " cannot be stored in this file";
|
||||
logger_dlib_wrapper() << LINFO
|
||||
<< "List of supported codecs for muxer " << st.pFormatCtx->oformat->name << " in this installation of ffmpeg:";
|
||||
for (const auto& supported : supported_codecs)
|
||||
(*st.log) << LINFO << " " << supported.codec_name;
|
||||
logger_dlib_wrapper() << LINFO << " " << supported.codec_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Codec is supported by muxer, so create encoder
|
||||
enc = encoder(args, handle_packet, st.log);
|
||||
enc = encoder(args, handle_packet);
|
||||
|
||||
if (!enc.is_open())
|
||||
return false;
|
||||
@ -748,7 +740,7 @@ namespace dlib
|
||||
AVStream* stream = avformat_new_stream(st.pFormatCtx.get(), enc.pCodecCtx->codec);
|
||||
|
||||
if (!stream)
|
||||
return fail(*st.log, "avformat_new_stream() failed");
|
||||
return fail("avformat_new_stream() failed");
|
||||
|
||||
stream->id = stream_counter;
|
||||
stream->time_base = enc.pCodecCtx->time_base;
|
||||
@ -757,7 +749,7 @@ namespace dlib
|
||||
int ret = avcodec_parameters_from_context(stream->codecpar, enc.pCodecCtx.get());
|
||||
|
||||
if (ret < 0)
|
||||
return fail(*st.log, "avcodec_parameters_from_context() failed : ", get_av_error(ret));
|
||||
return fail("avcodec_parameters_from_context() failed : ", get_av_error(ret));
|
||||
|
||||
return true;
|
||||
};
|
||||
@ -786,7 +778,7 @@ namespace dlib
|
||||
int ret = avio_open2(&st.pFormatCtx->pb, st.args_.filepath.c_str(), AVIO_FLAG_WRITE, &st.pFormatCtx->interrupt_callback, opt.get());
|
||||
|
||||
if (ret < 0)
|
||||
return fail(*st.log, "avio_open2() failed : ", get_av_error(ret));
|
||||
return fail("avio_open2() failed : ", get_av_error(ret));
|
||||
}
|
||||
|
||||
av_dict opt = st.args_.format_options;
|
||||
@ -794,7 +786,7 @@ namespace dlib
|
||||
int ret = avformat_write_header(st.pFormatCtx.get(), opt.get());
|
||||
|
||||
if (ret < 0)
|
||||
return fail(*st.log, "avformat_write_header() failed : ", get_av_error(ret));
|
||||
return fail("avformat_write_header() failed : ", get_av_error(ret));
|
||||
|
||||
st.connected_time = system_clock::now();
|
||||
|
||||
@ -833,7 +825,7 @@ namespace dlib
|
||||
if (f.is_image())
|
||||
{
|
||||
if (!st.encoder_image.is_open())
|
||||
return fail(*st.log, "frame is an image type but image encoder is not initialized");
|
||||
return fail("frame is an image type but image encoder is not initialized");
|
||||
|
||||
return st.encoder_image.push(std::move(f));
|
||||
}
|
||||
@ -841,7 +833,7 @@ namespace dlib
|
||||
else if (f.is_audio())
|
||||
{
|
||||
if (!st.encoder_audio.is_open())
|
||||
return fail(*st.log, "frame is of audio type but audio encoder is not initialized");
|
||||
return fail("frame is of audio type but audio encoder is not initialized");
|
||||
|
||||
return st.encoder_audio.push(std::move(f));
|
||||
}
|
||||
@ -860,7 +852,7 @@ namespace dlib
|
||||
|
||||
const int ret = av_write_trailer(st.pFormatCtx.get());
|
||||
if (ret < 0)
|
||||
(*st.log) << LERROR << "av_write_trailer() failed : " << details::get_av_error(ret);
|
||||
logger_dlib_wrapper() << LERROR << "av_write_trailer() failed : " << details::get_av_error(ret);
|
||||
|
||||
if ((st.pFormatCtx->oformat->flags & AVFMT_NOFILE) == 0)
|
||||
avio_closep(&st.pFormatCtx->pb);
|
||||
|
@ -35,6 +35,7 @@ extern "C" {
|
||||
#include <libavutil/channel_layout.h>
|
||||
#include <libavutil/audio_fifo.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/log.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavformat/avformat.h>
|
||||
@ -53,28 +54,7 @@ namespace dlib
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
class register_ffmpeg
|
||||
{
|
||||
/*!
|
||||
WHAT THIS OBJECT REPRESENTS
|
||||
The point of this class is to statically register ffmpeg globally
|
||||
and ensure its done only ONCE.
|
||||
In C++17 you can have inline variables and this can be done much easier.
|
||||
In C++14 and below you need something like this class.
|
||||
!*/
|
||||
|
||||
public:
|
||||
|
||||
static bool get()
|
||||
{
|
||||
static const bool v = register_library();
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static bool register_library();
|
||||
};
|
||||
void register_ffmpeg();
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -236,6 +216,11 @@ namespace dlib
|
||||
std::string get_audio_fmt_str(AVSampleFormat fmt);
|
||||
std::string get_channel_layout_str(uint64_t layout);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
dlib::logger& logger_ffmpeg();
|
||||
dlib::logger& logger_dlib_wrapper();
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace details
|
||||
@ -421,9 +406,9 @@ namespace dlib
|
||||
namespace details
|
||||
{
|
||||
template<class... Args>
|
||||
inline bool fail(logger& out, Args&&... args)
|
||||
inline bool fail(Args&&... args)
|
||||
{
|
||||
auto ret = out << LERROR;
|
||||
auto ret = logger_dlib_wrapper() << LERROR;
|
||||
#ifdef __cpp_fold_expressions
|
||||
((ret << args),...);
|
||||
#else
|
||||
@ -684,21 +669,83 @@ namespace dlib
|
||||
|
||||
namespace details
|
||||
{
|
||||
inline dlib::logger& logger_ffmpeg_private()
|
||||
{
|
||||
static dlib::logger GLOBAL("ffmpeg.internal");
|
||||
return GLOBAL;
|
||||
}
|
||||
}
|
||||
|
||||
inline dlib::logger& logger_ffmpeg()
|
||||
{
|
||||
details::register_ffmpeg();
|
||||
return details::logger_ffmpeg_private();
|
||||
}
|
||||
|
||||
inline dlib::logger& logger_dlib_wrapper()
|
||||
{
|
||||
static dlib::logger GLOBAL("ffmpeg.dlib");
|
||||
return GLOBAL;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
inline bool register_ffmpeg::register_library()
|
||||
namespace details
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
inline void register_ffmpeg()
|
||||
{
|
||||
avdevice_register_all();
|
||||
static const bool REGISTERED = []
|
||||
{
|
||||
avdevice_register_all();
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L91
|
||||
avcodec_register_all();
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L91
|
||||
avcodec_register_all();
|
||||
#endif
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
|
||||
av_register_all();
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
|
||||
av_register_all();
|
||||
#endif
|
||||
return true;
|
||||
|
||||
av_log_set_callback([](void* ptr, int level, const char *fmt, va_list vl)
|
||||
{
|
||||
auto& logger = details::logger_ffmpeg_private();
|
||||
|
||||
char line[256] = {0};
|
||||
static int print_prefix = 1;
|
||||
|
||||
// Not sure if copying to vl2 is required by internal ffmpeg functions do this...
|
||||
va_list vl2;
|
||||
va_copy(vl2, vl);
|
||||
int size = av_log_format_line2(ptr, level, fmt, vl2, &line[0], sizeof(line), &print_prefix);
|
||||
va_end(vl2);
|
||||
|
||||
// Remove all '\n' since dlib's logger already adds one
|
||||
size = std::min<int>(size, sizeof(line) - 1);
|
||||
line[size] = '\0';
|
||||
for (int i = size - 1 ; i >= 0 ; --i)
|
||||
if (line[i] == '\n')
|
||||
line[i] = ' ';
|
||||
|
||||
switch(level)
|
||||
{
|
||||
case AV_LOG_PANIC:
|
||||
case AV_LOG_FATAL: logger << LFATAL << line; break;
|
||||
case AV_LOG_ERROR: logger << LERROR << line; break;
|
||||
case AV_LOG_WARNING: logger << LWARN << line; break;
|
||||
case AV_LOG_INFO:
|
||||
case AV_LOG_VERBOSE: logger << LINFO << line; break;
|
||||
case AV_LOG_DEBUG: logger << LDEBUG << line; break;
|
||||
case AV_LOG_TRACE: logger << LTRACE << line; break;
|
||||
default: break;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}();
|
||||
(void)REGISTERED;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
@ -1192,17 +1239,17 @@ namespace dlib
|
||||
{
|
||||
const static auto protocols = []
|
||||
{
|
||||
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
|
||||
details::register_ffmpeg();
|
||||
std::vector<std::string> protocols;
|
||||
void* opaque = nullptr;
|
||||
const char* name = 0;
|
||||
while (init && (name = avio_enum_protocols(&opaque, 0)))
|
||||
while ((name = avio_enum_protocols(&opaque, 0)))
|
||||
protocols.emplace_back(name);
|
||||
|
||||
opaque = nullptr;
|
||||
name = 0;
|
||||
|
||||
while (init && (name = avio_enum_protocols(&opaque, 1)))
|
||||
while ((name = avio_enum_protocols(&opaque, 1)))
|
||||
protocols.emplace_back(name);
|
||||
|
||||
return protocols;
|
||||
@ -1217,16 +1264,16 @@ namespace dlib
|
||||
{
|
||||
const static auto demuxers = []
|
||||
{
|
||||
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
|
||||
details::register_ffmpeg();
|
||||
std::vector<std::string> demuxers;
|
||||
const AVInputFormat* demuxer = nullptr;
|
||||
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
|
||||
while (init && (demuxer = av_iformat_next(demuxer)))
|
||||
while ((demuxer = av_iformat_next(demuxer)))
|
||||
#else
|
||||
void* opaque = nullptr;
|
||||
while (init && (demuxer = av_demuxer_iterate(&opaque)))
|
||||
while ((demuxer = av_demuxer_iterate(&opaque)))
|
||||
#endif
|
||||
demuxers.push_back(demuxer->name);
|
||||
|
||||
@ -1257,17 +1304,17 @@ namespace dlib
|
||||
{
|
||||
const static auto ret = []
|
||||
{
|
||||
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
|
||||
details::register_ffmpeg();
|
||||
|
||||
std::vector<muxer_details> all_details;
|
||||
const AVOutputFormat* muxer = nullptr;
|
||||
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
|
||||
while (init && (muxer = av_oformat_next(muxer)))
|
||||
while ((muxer = av_oformat_next(muxer)))
|
||||
#else
|
||||
void* opaque = nullptr;
|
||||
while (init && (muxer = av_muxer_iterate(&opaque)))
|
||||
while ((muxer = av_muxer_iterate(&opaque)))
|
||||
#endif
|
||||
{
|
||||
muxer_details details;
|
||||
@ -1288,17 +1335,17 @@ namespace dlib
|
||||
{
|
||||
const static auto ret = []
|
||||
{
|
||||
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
|
||||
details::register_ffmpeg();
|
||||
std::vector<codec_details> details;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
|
||||
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L91
|
||||
AVCodec* codec = nullptr;
|
||||
while (init && (codec = av_codec_next(codec)))
|
||||
while ((codec = av_codec_next(codec)))
|
||||
#else
|
||||
const AVCodec* codec = nullptr;
|
||||
void* opaque = nullptr;
|
||||
while (init && (codec = av_codec_iterate(&opaque)))
|
||||
while ((codec = av_codec_iterate(&opaque)))
|
||||
#endif
|
||||
{
|
||||
codec_details detail;
|
||||
@ -1339,7 +1386,7 @@ namespace dlib
|
||||
{
|
||||
const static auto ret = []
|
||||
{
|
||||
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
|
||||
details::register_ffmpeg();
|
||||
std::vector<device_details> devices;
|
||||
|
||||
#if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100)
|
||||
@ -1350,7 +1397,7 @@ namespace dlib
|
||||
|
||||
AVInputFormatPtr device{nullptr};
|
||||
|
||||
while (init && (device = av_input_audio_device_next(device)))
|
||||
while ((device = av_input_audio_device_next(device)))
|
||||
{
|
||||
device_details details;
|
||||
details.device_type = device->name;
|
||||
@ -1360,7 +1407,7 @@ namespace dlib
|
||||
|
||||
device = nullptr;
|
||||
|
||||
while (init && (device = av_input_video_device_next(device)))
|
||||
while ((device = av_input_video_device_next(device)))
|
||||
{
|
||||
device_details details;
|
||||
details.device_type = device->name;
|
||||
@ -1410,7 +1457,7 @@ namespace dlib
|
||||
{
|
||||
const static auto ret = []
|
||||
{
|
||||
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
|
||||
details::register_ffmpeg();
|
||||
std::vector<device_details> devices;
|
||||
|
||||
#if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100)
|
||||
@ -1421,7 +1468,7 @@ namespace dlib
|
||||
|
||||
AVOutputFormatPtr device{nullptr};
|
||||
|
||||
while (init && (device = av_output_audio_device_next(device)))
|
||||
while ((device = av_output_audio_device_next(device)))
|
||||
{
|
||||
device_details details;
|
||||
details.device_type = std::string(device->name);
|
||||
@ -1431,7 +1478,7 @@ namespace dlib
|
||||
|
||||
device = nullptr;
|
||||
|
||||
while (init && (device = av_output_video_device_next(device)))
|
||||
while ((device = av_output_video_device_next(device)))
|
||||
{
|
||||
device_details details;
|
||||
details.device_type = std::string(device->name);
|
||||
|
@ -9,21 +9,24 @@
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <chrono>
|
||||
#include <dlib/media.h>
|
||||
#include <dlib/gui_widgets.h>
|
||||
#include <dlib/cmd_line_parser.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dlib;
|
||||
using namespace std::chrono;
|
||||
|
||||
int main(const int argc, const char** argv)
|
||||
try
|
||||
{
|
||||
command_line_parser parser;
|
||||
parser.add_option("i", "input video", 1);
|
||||
parser.add_option("i", "input video", 1);
|
||||
parser.add_option("verbose", "enable all internal ffmpeg logging");
|
||||
parser.set_group_name("Help Options");
|
||||
parser.add_option("h", "alias of --help");
|
||||
parser.add_option("help", "display this message and exit");
|
||||
parser.add_option("h", "alias of --help");
|
||||
parser.add_option("help", "display this message and exit");
|
||||
|
||||
parser.parse(argc, argv);
|
||||
const char* one_time_opts[] = {"i"};
|
||||
@ -35,6 +38,12 @@ try
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parser.option("verbose"))
|
||||
{
|
||||
ffmpeg::logger_dlib_wrapper().set_level(LALL);
|
||||
ffmpeg::logger_ffmpeg().set_level(LALL);
|
||||
}
|
||||
|
||||
const std::string filepath = get_option(parser, "i", "");
|
||||
|
||||
image_window win;
|
||||
@ -129,8 +138,10 @@ try
|
||||
array2d<rgb_pixel> img;
|
||||
size_t audio_samples{0};
|
||||
|
||||
const auto start = high_resolution_clock::now();
|
||||
while (cap.read(frame))
|
||||
{
|
||||
convert(frame, img);
|
||||
if (frame.is_image() && frame.pixfmt() == AV_PIX_FMT_RGB24)
|
||||
{
|
||||
convert(frame, img);
|
||||
@ -143,6 +154,9 @@ try
|
||||
printf("\r\tDecoding %zu samples", audio_samples); fflush(stdout);
|
||||
}
|
||||
}
|
||||
const auto stop = high_resolution_clock::now();
|
||||
printf("Ran in %f s\n", duration_cast<microseconds>(stop - start).count() * 1e-6);
|
||||
|
||||
|
||||
printf("\n");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user