Commit 0ad03adf authored by moto's avatar moto Committed by Facebook GitHub Bot
Browse files

Refactor AVDictionary clean up (#2507)

Summary:
Small clean up in ffmpeg binding code.

1. Make `get_option_dict` and `clean_up_dict` public utility
2. Merge the exception into `clean_up_dict`
3. Get rid of custom string join function and use `c10::Join`.

Pull Request resolved: https://github.com/pytorch/audio/pull/2507

Reviewed By: hwangjeff

Differential Revision: D37466022

Pulled By: mthrok

fbshipit-source-id: 44b769ac6ff1ab20e6d6ae086cd1447deacb5969
parent d50ed521
...@@ -83,7 +83,7 @@ class StreamReaderInterfaceTest(_MediaSourceMixin, TempDirMixin, TorchaudioTestC ...@@ -83,7 +83,7 @@ class StreamReaderInterfaceTest(_MediaSourceMixin, TempDirMixin, TorchaudioTestC
options.update({k: k for k in invalid_keys}) options.update({k: k for k in invalid_keys})
with self.assertRaises(RuntimeError) as ctx: with self.assertRaises(RuntimeError) as ctx:
StreamReader(self.get_src(), option=options) StreamReader(self.get_src(), option=options)
assert all(f'"{k}"' in str(ctx.exception) for k in invalid_keys) assert all(k in str(ctx.exception) for k in invalid_keys)
def test_src_info(self): def test_src_info(self):
"""`get_src_stream_info` properly fetches information""" """`get_src_stream_info` properly fetches information"""
......
...@@ -9,13 +9,8 @@ namespace torchaudio { ...@@ -9,13 +9,8 @@ namespace torchaudio {
namespace ffmpeg { namespace ffmpeg {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// AVFormatContext // AVDictionary
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void AVFormatContextDeleter::operator()(AVFormatContext* p) {
avformat_close_input(&p);
};
namespace {
AVDictionary* get_option_dict(const OptionDict& option) { AVDictionary* get_option_dict(const OptionDict& option) {
AVDictionary* opt = nullptr; AVDictionary* opt = nullptr;
...@@ -25,33 +20,23 @@ AVDictionary* get_option_dict(const OptionDict& option) { ...@@ -25,33 +20,23 @@ AVDictionary* get_option_dict(const OptionDict& option) {
return opt; return opt;
} }
std::vector<std::string> clean_up_dict(AVDictionary* p) { void clean_up_dict(AVDictionary* p) {
std::vector<std::string> ret; std::vector<std::string> unused_keys;
// Check and copy unused keys, clean up the original dictionary // Check and copy unused keys, clean up the original dictionary
AVDictionaryEntry* t = nullptr; AVDictionaryEntry* t = nullptr;
do { while ((t = av_dict_get(p, "", t, AV_DICT_IGNORE_SUFFIX))) {
t = av_dict_get(p, "", t, AV_DICT_IGNORE_SUFFIX); unused_keys.emplace_back(t->key);
if (t) { }
ret.emplace_back(t->key);
}
} while (t);
av_dict_free(&p); av_dict_free(&p);
return ret;
}
std::string join(std::vector<std::string> vars) { if (!unused_keys.empty()) {
std::stringstream ks; throw std::runtime_error(
for (size_t i = 0; i < vars.size(); ++i) { "Unexpected options: " + c10::Join(", ", unused_keys));
if (i == 0) {
ks << "\"" << vars[i] << "\"";
} else {
ks << ", \"" << vars[i] << "\"";
}
} }
return ks.str();
} }
namespace {
// https://github.com/FFmpeg/FFmpeg/blob/4e6debe1df7d53f3f59b37449b82265d5c08a172/doc/APIchanges#L252-L260 // https://github.com/FFmpeg/FFmpeg/blob/4e6debe1df7d53f3f59b37449b82265d5c08a172/doc/APIchanges#L252-L260
// Starting from libavformat 59 (ffmpeg 5), // Starting from libavformat 59 (ffmpeg 5),
// AVInputFormat is const and related functions expect constant. // AVInputFormat is const and related functions expect constant.
...@@ -63,6 +48,13 @@ std::string join(std::vector<std::string> vars) { ...@@ -63,6 +48,13 @@ std::string join(std::vector<std::string> vars) {
} // namespace } // namespace
////////////////////////////////////////////////////////////////////////////////
// AVFormatContext
////////////////////////////////////////////////////////////////////////////////
void AVFormatContextDeleter::operator()(AVFormatContext* p) {
avformat_close_input(&p);
};
AVFormatContextPtr get_input_format_context( AVFormatContextPtr get_input_format_context(
const std::string& src, const std::string& src,
const c10::optional<std::string>& device, const c10::optional<std::string>& device,
...@@ -93,12 +85,7 @@ AVFormatContextPtr get_input_format_context( ...@@ -93,12 +85,7 @@ AVFormatContextPtr get_input_format_context(
AVDictionary* opt = get_option_dict(option); AVDictionary* opt = get_option_dict(option);
int ret = avformat_open_input(&pFormat, src.c_str(), pInput, &opt); int ret = avformat_open_input(&pFormat, src.c_str(), pInput, &opt);
clean_up_dict(opt);
auto unused_keys = clean_up_dict(opt);
if (unused_keys.size()) {
throw std::runtime_error("Unexpected options: " + join(unused_keys));
}
if (ret < 0) if (ret < 0)
throw std::runtime_error( throw std::runtime_error(
...@@ -293,15 +280,12 @@ void init_codec_context( ...@@ -293,15 +280,12 @@ void init_codec_context(
AVDictionary* opts = get_option_dict(decoder_option); AVDictionary* opts = get_option_dict(decoder_option);
ret = avcodec_open2(pCodecContext, pCodec, &opts); ret = avcodec_open2(pCodecContext, pCodec, &opts);
clean_up_dict(opts);
if (ret < 0) { if (ret < 0) {
throw std::runtime_error( throw std::runtime_error(
"Failed to initialize CodecContext: " + av_err2string(ret)); "Failed to initialize CodecContext: " + av_err2string(ret));
} }
auto unused_keys = clean_up_dict(opts);
if (unused_keys.size()) {
throw std::runtime_error(
"Unexpected decoder options: " + join(unused_keys));
}
if (pParams->codec_type == AVMEDIA_TYPE_AUDIO && !pParams->channel_layout) if (pParams->codec_type == AVMEDIA_TYPE_AUDIO && !pParams->channel_layout)
pParams->channel_layout = pParams->channel_layout =
......
...@@ -60,6 +60,18 @@ class Wrapper { ...@@ -60,6 +60,18 @@ class Wrapper {
} }
}; };
////////////////////////////////////////////////////////////////////////////////
// AVDictionary
////////////////////////////////////////////////////////////////////////////////
// Since AVDictionaries are relocated by FFmpeg APIs it does not suit to
// IIRC-semantic. Instead we provide helper functions.
// Convert standard dict to FFmpeg native type
AVDictionary* get_option_dict(const OptionDict& option);
// Clean up the dict after use. If there is an unsed key, throw runtime error
void clean_up_dict(AVDictionary* p);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// AVFormatContext // AVFormatContext
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment