Skip to content

Commit 2baf884

Browse files
committed
use comma-separated decoder list now, use semi-colon to separate encodings
1 parent 2db74ab commit 2baf884

File tree

7 files changed

+108
-123
lines changed

7 files changed

+108
-123
lines changed

include/ffmpeg_encoder_decoder/decoder.hpp

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -105,30 +105,13 @@ class Decoder
105105
* \brief Initializes the decoder for a given codec and libav decoder.
106106
*
107107
* Initializes the decoder, with multiple decoders to pick from.
108-
* If the name of the libav decoder string is empty, a suitable libav decoder
109-
* will be picked, or the initialization will fail if none is available.
110108
* \param codec the codec (encoding) from the first packet. Can never change!
111109
* \param callback the function to call when frame has been decoded.
112-
* \param decoder the name of the libav decoder to use. If empty string,
113-
* the decoder will try to find a suitable one based on the encoding.
110+
* \param decoder the name of the libav decoder to use.
114111
* \return true if initialized successfully.
115112
*/
116113
bool initialize(const std::string & codec, Callback callback, const std::string & decoder);
117114

118-
/**
119-
* \ brief initializes the decoder, trying different libavdecoders in order.
120-
*
121-
* Initialize decoder with multiple libav decoders to pick from.
122-
* If decoders.empty() a default decoder will be chosen (if available).
123-
* \param codec the codec (encoding) from the first packet. Can never change!
124-
* \param callback the function to call when frame has been decoded.
125-
* \param decoders names of the libav decoders to try sequentially. If empty()
126-
* the decoder will try to find a suitable one based on the codec.
127-
* \return true if successful
128-
*/
129-
bool initialize(
130-
const std::string & codec, Callback callback, const std::vector<std::string> & decoders);
131-
132115
/**
133116
* \brief Sets the ROS output message encoding format.
134117
*
@@ -197,9 +180,9 @@ class Decoder
197180
* Finds the name of all hardware and software decoders (combined)
198181
* that match a certain codec (or encoder).
199182
* \param codec name of the codec, i.e. h264, hevc etc
200-
* \return vector with names of matching libav decoders
183+
* \return string with comma-separated list of libav decoders
201184
*/
202-
static std::vector<std::string> findDecoders(const std::string & codec);
185+
static std::string findDecoders(const std::string & codec);
203186

204187
/**
205188
* \brief Enables or disables performance measurements. Poorly tested, may be broken.
@@ -236,15 +219,12 @@ class Decoder
236219
getDefaultEncoderToDecoderMap();
237220

238221
private:
239-
bool initSingleDecoder(const std::string & decoder);
240-
bool initDecoder(const std::vector<std::string> & decoders);
241-
std::vector<std::string> filterDecoders(
242-
const std::string & encoding, const std::vector<std::string> & decoders,
243-
const std::vector<std::string> & valid_decoders);
222+
bool doInitDecoder(const std::string & encoding, const std::string & decoder);
223+
bool initDecoder(const std::string & encoding, const std::string & decoders);
244224
int receiveFrame();
245225
int convertFrameToMessage(const AVFrame * frame, const ImagePtr & image);
246226
void setAVOption(const std::string & field, const std::string & value);
247-
227+
void setEncoding(const std::string & encoding);
248228
// --------------- variables
249229
rclcpp::Logger logger_;
250230
Callback callback_;

include/ffmpeg_encoder_decoder/encoder.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ class Encoder
269269
* \param callback the function to call for handling encoded packets
270270
* \param encoding the ros encoding string, e.g. bayer_rggb8, rgb8 ...
271271
*/
272-
bool initialize(int width, int height, Callback callback, const std::string & encoding);
272+
bool initialize(
273+
int width, int height, Callback callback, const std::string & encoding = std::string());
273274
/**
274275
* \brief sets ROS logger to use for info/error messages
275276
* \param logger the logger to use for messages
@@ -383,6 +384,7 @@ class Encoder
383384
AVRational timeBase_{1, 100};
384385
AVRational frameRate_{100, 1};
385386
bool usesHardwareFrames_{false};
387+
std::string avSourcePixelFormat_;
386388
// ------ libav state
387389
AVCodecContext * codecContext_{nullptr};
388390
AVBufferRef * hwDeviceContext_{nullptr};

include/ffmpeg_encoder_decoder/utils.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ void find_decoders(
112112
const std::string & codec, std::vector<std::string> * hw_decoders,
113113
std::vector<std::string> * sw_decoders);
114114

115+
/**
116+
* \brief finds the names of all available decoders
117+
* for a given codec (or encoder)
118+
* \param codec the codec / encoding to find decoders for
119+
* \return string with comma separated list of libav decoder names
120+
*/
121+
122+
std::string find_decoders(const std::string & codec);
123+
/**
124+
* \brief filters a string with comma-separated decoders and
125+
*
126+
* \param codec the codec / encoding to filter for
127+
* \param decoders string with comma-separated list of decoder names
128+
* \return string with comma separated list of libav decoder names
129+
*/
130+
std::string filter_decoders(const std::string & codec, const std::string & decoders);
131+
115132
/**
116133
* \brief gets list of names of all libav supported device types
117134
*

src/decoder.cpp

Lines changed: 44 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ void Decoder::reset()
5050
outputFrame_ = NULL;
5151
hwPixFormat_ = AV_PIX_FMT_NONE;
5252
outputMsgEncoding_ = "";
53+
packetEncoding_ = "";
5354
}
5455

5556
void Decoder::setOutputMessageEncoding(const std::string & output_encoding)
@@ -58,61 +59,29 @@ void Decoder::setOutputMessageEncoding(const std::string & output_encoding)
5859
outputMsgEncoding_ = output_encoding;
5960
}
6061

61-
bool Decoder::initialize(
62-
const std::string & encoding, Callback callback, const std::string & decoder)
62+
static std::vector<std::string> splitEncoding(const std::string & encoding)
6363
{
64-
return (initialize(
65-
encoding, callback,
66-
decoder.empty() ? std::vector<std::string>() : std::vector<std::string>{decoder}));
64+
return (utils::split_by_char(encoding, ';'));
6765
}
6866

69-
bool Decoder::initialize(
70-
const std::string & encoding, Callback callback, const std::vector<std::string> & decoders)
67+
void Decoder::setEncoding(const std::string & encoding)
7168
{
72-
callback_ = callback;
7369
packetEncoding_ = encoding;
74-
const auto split = utils::split_by_char(encoding, '/');
70+
const auto split = splitEncoding(encoding);
7571
if (outputMsgEncoding_.empty()) {
7672
// assume orig was bgr8
77-
outputMsgEncoding_ = split.size() > 1 ? split[1] : "bgr8";
73+
outputMsgEncoding_ = split.size() == 4 ? split[3] : "bgr8";
7874
RCLCPP_INFO_STREAM(
7975
logger_,
80-
"output image encoding: " << outputMsgEncoding_ << ((split.size() > 1) ? "" : " (default)"));
81-
}
82-
const auto all_decoders = findDecoders(split[0]);
83-
if (all_decoders.empty()) {
84-
RCLCPP_ERROR_STREAM(logger_, "no decoders discovered for code:c " << split[0]);
85-
throw(std::runtime_error("no decoders discovered for codec: " + split[0]));
86-
}
87-
if (decoders.empty()) { // try all libav-discovered decoders
88-
std::string decoders_str;
89-
for (const auto & decoder : all_decoders) {
90-
decoders_str += " " + decoder;
91-
}
92-
RCLCPP_INFO_STREAM(logger_, "trying discovered decoders in order:" << decoders_str);
93-
return (initDecoder(all_decoders));
94-
}
95-
const auto good_decoders = filterDecoders(encoding, decoders, all_decoders);
96-
if (good_decoders.empty()) {
97-
return (false);
76+
"output image encoding: " << outputMsgEncoding_ << ((split.size() == 4) ? "" : " (default)"));
9877
}
99-
return (initDecoder(good_decoders));
10078
}
10179

102-
std::vector<std::string> Decoder::filterDecoders(
103-
const std::string & encoding, const std::vector<std::string> & decoders,
104-
const std::vector<std::string> & valid_decoders)
80+
bool Decoder::initialize(
81+
const std::string & encoding, Callback callback, const std::string & decoder)
10582
{
106-
std::vector<std::string> good_decoders;
107-
for (const auto & dec : decoders) { // filter for decoders matching codec
108-
if (std::find(valid_decoders.begin(), valid_decoders.end(), dec) != valid_decoders.end()) {
109-
good_decoders.push_back(dec);
110-
} else {
111-
RCLCPP_WARN_STREAM(
112-
logger_, "configured decoder: " << dec << " cannot handle encoding: " << encoding);
113-
}
114-
}
115-
return (good_decoders);
83+
callback_ = callback;
84+
return (initDecoder(encoding, decoder));
11685
}
11786

11887
static AVBufferRef * hw_decoder_init(
@@ -232,46 +201,32 @@ enum AVPixelFormat get_format(struct AVCodecContext * avctx, const enum AVPixelF
232201
return AV_PIX_FMT_NONE;
233202
}
234203

235-
bool Decoder::initDecoder(const std::vector<std::string> & decoders)
204+
bool Decoder::initDecoder(const std::string & encoding, const std::string & decoder)
236205
{
237-
if (decoders.empty()) {
238-
RCLCPP_ERROR_STREAM(logger_, "no decoders configured for this encoding!");
239-
throw(std::runtime_error("no decoders configured for this encoding!"));
206+
const AVCodec * codec = avcodec_find_decoder_by_name(decoder.c_str());
207+
if (!codec) {
208+
RCLCPP_WARN_STREAM(logger_, "decoder " << decoder << " cannot decode " << encoding);
209+
return (false);
240210
}
241-
for (const auto & decoder : decoders) {
242-
const AVCodec * codec = avcodec_find_decoder_by_name(decoder.c_str());
243-
if (codec) {
244-
// use the decoder if it either is software, or has working
245-
// hardware support
246-
if (codec->capabilities & AV_CODEC_CAP_HARDWARE) {
247-
const AVCodecHWConfig * hwConfig = avcodec_get_hw_config(codec, 0);
248-
if (hwConfig) {
249-
if (initSingleDecoder(decoder)) {
250-
return (true);
251-
}
252-
} else {
253-
RCLCPP_INFO_STREAM(logger_, "ignoring decoder with no hardware config: " << decoder);
254-
}
255-
} else {
256-
if (initSingleDecoder(decoder)) {
257-
return (true);
258-
}
259-
}
260-
} else {
261-
RCLCPP_WARN_STREAM(logger_, "unknown decoder: " << decoder);
211+
// use the decoder if it either is software, or has working
212+
// hardware support
213+
const AVCodecHWConfig * hwConfig = nullptr;
214+
if (codec->capabilities & AV_CODEC_CAP_HARDWARE) {
215+
hwConfig = avcodec_get_hw_config(codec, 0);
216+
if (!hwConfig) {
217+
RCLCPP_INFO_STREAM(logger_, "ignoring decoder with no hardware config: " << decoder);
218+
return (false);
262219
}
263220
}
264-
if (decoders.size() > 1) {
265-
RCLCPP_ERROR_STREAM(logger_, "none of these requested decoders works: ");
266-
for (const auto & decoder : decoders) {
267-
RCLCPP_ERROR_STREAM(logger_, " " << decoder);
268-
}
221+
if (!doInitDecoder(encoding, decoder)) {
222+
return (false);
269223
}
270-
throw(std::runtime_error("cannot find matching decoder!"));
224+
return (true);
271225
}
272226

273-
bool Decoder::initSingleDecoder(const std::string & decoder)
227+
bool Decoder::doInitDecoder(const std::string & encoding, const std::string & decoder)
274228
{
229+
setEncoding(encoding);
275230
try {
276231
const AVCodec * codec = avcodec_find_decoder_by_name(decoder.c_str());
277232
if (!codec) {
@@ -373,7 +328,6 @@ int Decoder::receiveFrame()
373328
}
374329
}
375330
AVFrame * frame = isAcc ? cpuFrame_ : swFrame_;
376-
377331
if (frame->width == ctx->width && frame->height == ctx->height) {
378332
// prepare the decoded message
379333
ImagePtr image(new Image());
@@ -504,26 +458,28 @@ void Decoder::setAVOption(const std::string & field, const std::string & value)
504458
}
505459
}
506460

507-
const std::unordered_map<std::string, std::string> & Decoder::getDefaultEncoderToDecoderMap()
508-
{
509-
RCLCPP_INFO_STREAM(
510-
rclcpp::get_logger("ffmpeg_decoder"),
511-
"default map is deprecated, use findDecoders() or pass empty string instead!");
512-
throw(std::runtime_error("default map is deprecated!"));
513-
}
514-
515461
void Decoder::findDecoders(
516462
const std::string & codec, std::vector<std::string> * hw_decoders,
517463
std::vector<std::string> * sw_decoders)
518464
{
519465
utils::find_decoders(codec, hw_decoders, sw_decoders);
520466
}
521467

522-
std::vector<std::string> Decoder::findDecoders(const std::string & codec)
468+
std::string Decoder::findDecoders(const std::string & codec)
523469
{
524-
std::vector<std::string> sw_decoders, all_decoders;
525-
utils::find_decoders(codec, &all_decoders, &sw_decoders);
526-
all_decoders.insert(all_decoders.end(), sw_decoders.begin(), sw_decoders.end());
527-
return (all_decoders);
470+
return (utils::find_decoders(codec));
528471
}
472+
473+
// -------------- deprecated, DO NOT USE ------------------
474+
const std::unordered_map<std::string, std::string> & Decoder::getDefaultEncoderToDecoderMap()
475+
{
476+
static const std::unordered_map<std::string, std::string> defaultMap{
477+
{{"h264_nvenc", "h264"},
478+
{"libx264", "h264"},
479+
{"hevc_nvenc", "hevc_cuvid"},
480+
{"h264_nvmpi", "h264"},
481+
{"h264_vaapi", "h264"}}};
482+
return (defaultMap);
483+
}
484+
529485
} // namespace ffmpeg_encoder_decoder

src/encoder.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,6 @@ void Encoder::openHardwareDevice(
144144
logger_, "user overriding software pix fmt " << utils::pix(frames_ctx->sw_format));
145145
RCLCPP_INFO_STREAM(logger_, "with " << utils::pix(pixFormat_));
146146
frames_ctx->sw_format = pixFormat_; // override default at your own risk!
147-
} else {
148-
RCLCPP_INFO_STREAM(
149-
logger_, "using software pixel format: " << utils::pix(frames_ctx->sw_format));
150147
}
151148
if (frames_ctx->sw_format == AV_PIX_FMT_NONE) {
152149
av_buffer_unref(&hwframe_ctx_ref);
@@ -198,10 +195,9 @@ enum AVPixelFormat Encoder::findMatchingSourceFormat(
198195
return (utils::ros_to_av_pix_format(rosSrcFormat));
199196
}
200197

201-
void Encoder::doOpenCodec(int width, int height, const std::string &)
198+
void Encoder::doOpenCodec(int width, int height, const std::string & origEncoding)
202199
{
203200
int err = 0;
204-
encoding_ = codec_ + "/" + cvBridgeTargetFormat_;
205201

206202
codecContext_ = nullptr;
207203
if (encoder_.empty()) {
@@ -255,6 +251,9 @@ void Encoder::doOpenCodec(int width, int height, const std::string &)
255251
: utils::get_preferred_pixel_format(usesHardwareFrames_, pixFmts);
256252
codecContext_->sw_pix_fmt = codecContext_->pix_fmt;
257253
}
254+
avSourcePixelFormat_ = utils::pix(codecContext_->sw_pix_fmt);
255+
RCLCPP_INFO_STREAM(logger_, "using av_source_pixel_format: " << avSourcePixelFormat_);
256+
258257
std::stringstream ss;
259258
for (const auto & kv : avOptions_) {
260259
setAVOption(kv.first, kv.second);
@@ -322,6 +321,8 @@ void Encoder::doOpenCodec(int width, int height, const std::string &)
322321
throw(std::runtime_error("cannot allocate sws context"));
323322
}
324323
}
324+
encoding_ = codec_ + ";" + avSourcePixelFormat_ + ";" + cvBridgeTargetFormat_ + ";" +
325+
(origEncoding.empty() ? cvBridgeTargetFormat_ : origEncoding);
325326
}
326327

327328
void Encoder::setAVOption(const std::string & field, const std::string & value)

src/utils.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,35 @@ void find_decoders(
294294
find_decoders(real_encoding, sw_decoders, false);
295295
}
296296

297+
std::string find_decoders(const std::string & codec)
298+
{
299+
std::string decoders;
300+
std::vector<std::string> hw_dec;
301+
std::vector<std::string> sw_dec;
302+
find_decoders(codec, &hw_dec, &sw_dec);
303+
for (const auto & s : hw_dec) {
304+
decoders += (decoders.empty() ? "" : ",") + s;
305+
}
306+
for (const auto & s : sw_dec) {
307+
decoders += (decoders.empty() ? "" : ",") + s;
308+
}
309+
return decoders;
310+
}
311+
312+
std::string filter_decoders(const std::string & codec, const std::string & decoders)
313+
{
314+
std::string filtered;
315+
const auto valid_decoders = split_by_char(find_decoders(codec), ',');
316+
for (const auto & dec : split_by_char(decoders, ',')) {
317+
for (const auto & valid : valid_decoders) {
318+
if (dec == valid) {
319+
filtered += (filtered.empty() ? "" : ",") + dec;
320+
}
321+
}
322+
}
323+
return (filtered);
324+
}
325+
297326
std::string find_codec(const std::string & encoder)
298327
{
299328
const AVCodec * c = find_by_name(encoder);

0 commit comments

Comments
 (0)