Skip to content

Commit 249270d

Browse files
authored
Better error message for unseekable formats (#1647)
1 parent 758e067 commit 249270d

4 files changed

Lines changed: 52 additions & 7 deletions

File tree

src/torchcodec/_core/SingleStreamDecoder.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <iostream>
1313
#include <limits>
1414
#include <numeric>
15+
#include <sstream>
1516
#include <string_view>
1617
#include "Demuxer.h"
1718
#include "Metadata.h"
@@ -24,6 +25,29 @@ extern "C" {
2425

2526
namespace facebook::torchcodec {
2627

28+
namespace {
29+
30+
// FFmpeg reports "this seek cannot be performed" as a bare -1, i.e. EPERM,
31+
// which renders as the very misleading "Operation not permitted". It covers
32+
// both a target that the demuxer can't reach and a demuxer with no seeking
33+
// support whatsoever.
34+
std::string get_seek_error_message(
35+
const AVFormatContext* format_context,
36+
int64_t desired_pts,
37+
int status) {
38+
std::stringstream ss;
39+
ss << "Could not seek file to pts=" << desired_pts << ": "
40+
<< get_ffmpeg_error_string_from_error_code(status) << ".";
41+
if (status == AVERROR(EPERM)) {
42+
ss << " This is either because that timestamp is out of range, or because"
43+
<< " the '" << format_context->iformat->name << "' format does not"
44+
<< " support seeking.";
45+
}
46+
return ss.str();
47+
}
48+
49+
} // namespace
50+
2751
// --------------------------------------------------------------------------
2852
// CONSTRUCTORS, INITIALIZATION, DESTRUCTORS
2953
// --------------------------------------------------------------------------
@@ -350,9 +374,7 @@ void SingleStreamDecoder::scan_file_and_update_metadata_and_index() {
350374
// Reset the seek-cursor back to the beginning.
351375
int status = avformat_seek_file(format_context_.get(), 0, INT64_MIN, 0, 0, 0);
352376
STD_TORCH_CHECK(
353-
status >= 0,
354-
"Could not seek file to pts=0: ",
355-
get_ffmpeg_error_string_from_error_code(status));
377+
status >= 0, get_seek_error_message(format_context_.get(), 0, status));
356378

357379
// Sort all frames by their pts.
358380
sort_all_frames();
@@ -1511,10 +1533,7 @@ bool SingleStreamDecoder::maybe_seek_to_before_desired_pts() {
15111533
0);
15121534
STD_TORCH_CHECK(
15131535
status >= 0,
1514-
"Could not seek file to pts=",
1515-
std::to_string(desired_pts),
1516-
": ",
1517-
get_ffmpeg_error_string_from_error_code(status));
1536+
get_seek_error_message(format_context_.get(), desired_pts, status));
15181537

15191538
decode_stats_.num_flushes++;
15201539
device_interface_->flush();

test/resources/sine_mono_mp3.swf

8.9 KB
Binary file not shown.

test/test_decoders.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
TESTSRC2_ODD_WIDTH_VP9,
144144
TESTSRC2_ODD_WIDTH_VP9_10BIT,
145145
TRANSPARENT_GIF,
146+
UNSEEKABLE_SWF,
146147
WAV_ODD_DATA_TRAILING_CHUNK,
147148
)
148149

@@ -2861,6 +2862,14 @@ def test_fresh_decoder_seek(self, tmp_path):
28612862
AudioEncoder(torch.rand(1, 1000), sample_rate=16000).to_file(path)
28622863
AudioDecoder(path).get_all_samples()
28632864

2865+
def test_unseekable_format(self):
2866+
decoder = AudioDecoder(UNSEEKABLE_SWF.path)
2867+
samples = decoder.get_all_samples()
2868+
assert samples.data.shape == (1, 89856)
2869+
2870+
with pytest.raises(RuntimeError, match="'swf' format does not support seeking"):
2871+
decoder.get_samples_played_in_range(start_seconds=1)
2872+
28642873
@pytest.mark.parametrize("asset", (NASA_AUDIO, NASA_AUDIO_MP3))
28652874
@pytest.mark.parametrize("stop_seconds", (None, "duration", 99999999))
28662875
def test_get_all_samples_with_range(self, asset, stop_seconds):

test/utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,3 +1710,20 @@ def sample_format(self) -> str:
17101710
)
17111711
},
17121712
)
1713+
1714+
# Generated with:
1715+
# ffmpeg -y -f lavfi -i "sine=frequency=440:duration=2" -c:a mp3 -b:a 32k -ar 44100 -ac 1 test/resources/sine_mono_mp3.swf
1716+
UNSEEKABLE_SWF = TestAudio(
1717+
filename="sine_mono_mp3.swf",
1718+
default_stream_index=0,
1719+
frames={0: {}},
1720+
stream_infos={
1721+
0: TestAudioStreamInfo(
1722+
sample_rate=44_100,
1723+
num_channels=1,
1724+
duration_seconds=2.2739909297052154,
1725+
num_frames=78,
1726+
sample_format="fltp",
1727+
)
1728+
},
1729+
)

0 commit comments

Comments
 (0)