@@ -69,19 +69,21 @@ def __init__(self, sample_rate: int | None = None, num_channels: int | None = No
6969 )
7070 self ._requested_sample_rate = sample_rate
7171 self ._drained = False
72- # Where the output stream starts, and how far into it we are. The
73- # resampler's delay line means a chunk does not begin at the pts of the
74- # frame that produced it, so we count emitted samples instead of
75- # forwarding the input's timestamp.
76- self ._origin_seconds : float | None = None
72+
73+ self ._first_frame_pts_seconds : float | None = None
7774 self ._out_sample_rate : int | None = None
7875 self ._num_emitted_samples = 0
7976
8077 def _wrap (self , data ) -> AudioSamples :
8178 assert self ._out_sample_rate is not None # mypy
82- assert self ._origin_seconds is not None # mypy
79+ assert self ._first_frame_pts_seconds is not None # mypy
80+ # We manually compute the pts of all frames but the first one: when
81+ # resampling happens, libswresample buffers samples and emits them
82+ # later. Not all the samples of the first frame are necessarily emitted
83+ # immediately, they may be emitted with the next frame. Without this,
84+ # we'd fail the test_audio_converter_pts_is_contiguous() test.
8385 pts_seconds = (
84- self ._origin_seconds + self ._num_emitted_samples / self ._out_sample_rate
86+ self ._first_frame_pts_seconds + self ._num_emitted_samples / self ._out_sample_rate
8587 )
8688 self ._num_emitted_samples += data .shape [1 ]
8789 return AudioSamples (
@@ -102,8 +104,8 @@ def convert(self, raw_samples: RawAudioSamples) -> AudioSamples:
102104 "This AudioConverter has been drained. Call reset() to convert "
103105 "more samples."
104106 )
105- if self ._origin_seconds is None :
106- self ._origin_seconds = raw_samples .pts_seconds
107+ if self ._first_frame_pts_seconds is None :
108+ self ._first_frame_pts_seconds = raw_samples .pts_seconds
107109 self ._out_sample_rate = (
108110 self ._requested_sample_rate
109111 if self ._requested_sample_rate is not None
@@ -120,7 +122,7 @@ def drain(self) -> AudioSamples:
120122 Skipping this loses the end of the stream. It returns an empty result
121123 when no resampling is being done, since nothing is held back then.
122124 """
123- if self ._origin_seconds is None :
125+ if self ._first_frame_pts_seconds is None :
124126 raise RuntimeError (
125127 "This AudioConverter hasn't converted any samples, so there is "
126128 "nothing to drain."
@@ -134,6 +136,6 @@ def reset(self) -> None:
134136 demuxer seeked, and after ``drain()``."""
135137 _blocks_audio_converter_reset (self ._handle )
136138 self ._drained = False
137- self ._origin_seconds = None
139+ self ._first_frame_pts_seconds = None
138140 self ._out_sample_rate = None
139141 self ._num_emitted_samples = 0
0 commit comments