"tests/experimental/vscode:/vscode.git/clone" did not exist on "15d4cf15f608be5d923b7a1b1ddaa5541f4ce069"
Commit 04875eef authored by moto's avatar moto Committed by Facebook GitHub Bot
Browse files

Make Streamer fail if an invalid option is provided (#2263)

Summary:
`torchaudio.prototype.io.Streamer` class takes context dependant options
as `option` argument in the form of mappings of strings.

Currently there is no check if the provided options were valid for
the given input.

This commit adds the check and raise an error if an invalid erro is given.

This is analogous to `ffmpeg` command error handling.

```
$ ffmpeg -foo
...
Unrecognized option 'foo'.
```

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

Reviewed By: hwangjeff

Differential Revision: D34495111

Pulled By: mthrok

fbshipit-source-id: cd068de0dc1d1273bdd5d40312c3faccb47b253f
parent 17c6af7f
...@@ -19,11 +19,11 @@ def _name_func(func, _, params): ...@@ -19,11 +19,11 @@ def _name_func(func, _, params):
else: else:
strs.append(str(arg)) strs.append(str(arg))
# sanitize the test name # sanitize the test name
name = "_".join(strs).replace(".", "_") name = "_".join(strs)
return f"{func.__name__}_{name}" return parameterized.to_safe_name(f"{func.__name__}_{name}")
def nested_params(*params_set): def nested_params(*params_set, name_func=_name_func):
"""Generate the cartesian product of the given list of parameters. """Generate the cartesian product of the given list of parameters.
Args: Args:
......
...@@ -35,6 +35,24 @@ class StreamerInterfaceTest(TempDirMixin, TorchaudioTestCase): ...@@ -35,6 +35,24 @@ class StreamerInterfaceTest(TempDirMixin, TorchaudioTestCase):
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
Streamer("foobar") Streamer("foobar")
@nested_params(
[
("foo",),
(
"foo",
"bar",
),
],
[{}, {"sample_rate": "16000"}],
)
def test_streamer_invalide_option(self, invalid_keys, options):
"""When invalid options are given, Streamer raises an exception with these keys"""
options.update({k: k for k in invalid_keys})
src = get_video_asset()
with self.assertRaises(RuntimeError) as ctx:
Streamer(src, option=options)
assert all(f'"{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"""
s = Streamer(get_video_asset()) s = Streamer(get_video_asset())
......
#include <torchaudio/csrc/ffmpeg/ffmpeg.h> #include <torchaudio/csrc/ffmpeg/ffmpeg.h>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <vector>
namespace torchaudio { namespace torchaudio {
namespace ffmpeg { namespace ffmpeg {
...@@ -12,6 +15,43 @@ void AVFormatContextDeleter::operator()(AVFormatContext* p) { ...@@ -12,6 +15,43 @@ void AVFormatContextDeleter::operator()(AVFormatContext* p) {
}; };
namespace { namespace {
AVDictionary* get_option_dict(
const std::map<std::string, std::string>& option) {
AVDictionary* opt = nullptr;
for (auto& it : option) {
av_dict_set(&opt, it.first.c_str(), it.second.c_str(), 0);
}
return opt;
}
std::vector<std::string> clean_up_dict(AVDictionary* p) {
std::vector<std::string> ret;
// Check and copy unused keys, clean up the original dictionary
AVDictionaryEntry* t = nullptr;
do {
t = av_dict_get(p, "", t, AV_DICT_IGNORE_SUFFIX);
if (t) {
ret.emplace_back(t->key);
}
} while (t);
av_dict_free(&p);
return ret;
}
std::string join(std::vector<std::string> vars) {
std::stringstream ks;
for (size_t i = 0; i < vars.size(); ++i) {
if (i == 0) {
ks << "\"" << vars[i] << "\"";
} else {
ks << ", \"" << vars[i] << "\"";
}
}
return ks.str();
}
AVFormatContext* get_format_context( AVFormatContext* get_format_context(
const std::string& src, const std::string& src,
const std::string& device, const std::string& device,
...@@ -19,14 +59,14 @@ AVFormatContext* get_format_context( ...@@ -19,14 +59,14 @@ AVFormatContext* get_format_context(
AVFormatContext* pFormat = NULL; AVFormatContext* pFormat = NULL;
AVInputFormat* pInput = AVInputFormat* pInput =
device.empty() ? NULL : av_find_input_format(device.c_str()); device.empty() ? NULL : av_find_input_format(device.c_str());
AVDictionary* opt = get_option_dict(option);
int ret = avformat_open_input(&pFormat, src.c_str(), pInput, &opt);
AVDictionary* dict = NULL; auto unused_keys = clean_up_dict(opt);
for (auto& it : option) {
av_dict_set(&dict, it.first.c_str(), it.second.c_str(), 0);
}
int ret = avformat_open_input(&pFormat, src.c_str(), pInput, &dict); if (unused_keys.size()) {
av_dict_free(&dict); throw std::runtime_error("Unexpected options: " + join(unused_keys));
}
if (ret < 0) if (ret < 0)
throw std::runtime_error( throw std::runtime_error(
......
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