in to get it to the right length.
*/
- int64_t const video_length_in_audio_frames = ((int64_t) _fs->dcp_length() * _fs->target_sample_rate() / _fs->frames_per_second());
+ int64_t const video_length_in_audio_frames = ((int64_t) _fs->dcp_length() * _fs->audio_sample_rate() / _fs->frames_per_second());
int64_t const audio_short_by_frames = video_length_in_audio_frames - _audio_frames_processed;
_log->log (
}
/* Update the number of audio frames we've pushed to the encoder */
- _audio_frames_processed += frames;
+ _audio_frames_processed += audio->frames ();
Audio (audio);
}
, _audio_codec (0)
, _subtitle_codec_context (0)
, _subtitle_codec (0)
- , _first_video_pts (-1)
- , _first_audio_pts (-1)
{
setup_general ();
setup_video ();
FFmpegDecoder::do_pass ()
{
int r = av_read_frame (_format_context, &_packet);
+
if (r < 0) {
if (r != AVERROR_EOF) {
throw DecodeError ("error on av_read_frame");
return true;
}
+ double const pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base) * _packet.pts;
+
if (_packet.stream_index == _video_stream) {
- if (_first_video_pts == -1) {
- _first_video_pts = _packet.pts;
+ if (!_first_video) {
+ _first_video = pts_seconds;
}
int frame_finished;
process_video (_frame);
}
- } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio && (_first_video_pts != -1 && _packet.pts > _first_video_pts)) {
+ } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio && _first_video && _first_video.get() <= pts_seconds) {
- /* Note: We only decode audio if we've had our first video packet through, and if this
- packet comes after it. Until then it is thrown away.
+ /* Note: We only decode audio if we've had our first video packet through, and if it
+ was before this packet. Until then audio is thrown away.
*/
-
- if (_first_audio_pts == -1) {
- _first_audio_pts = _packet.pts;
-
+
+ if (!_first_audio) {
+ _first_audio = pts_seconds;
+
/* This is our first audio packet, and if we've arrived here we must have had our
first video packet. Push some silence to make up the gap between our first
video packet and our first audio.
*/
- AVStream* v = _format_context->streams[_video_stream];
- AVStream* a = _format_context->streams[_audio_stream];
+ /* frames of silence that we must push */
+ int const s = rint ((_first_audio.get() - _first_video.get()) * audio_sample_rate ());
- assert (v->time_base.num == a->time_base.num);
- assert (v->time_base.den == a->time_base.den);
-
- /* samples of silence that we must push */
- int const s = rint (av_q2d (v->time_base) * (_first_audio_pts - _first_video_pts) * audio_sample_rate ());
-
_log->log (
String::compose (
- "First video at %1, first audio at %2, pushing %3 samples of silence",
- _first_video_pts, _first_audio_pts, s
+ "First video at %1, first audio at %2, pushing %3 frames of silence for %4 channels (%5 bytes per sample)",
+ _first_video.get(), _first_audio.get(), s, audio_channels(), bytes_per_audio_sample()
)
);
-
+
/* hence bytes */
int const b = s * audio_channels() * bytes_per_audio_sample();
-
+
/* XXX: this assumes that it won't be too much, and there are shaky assumptions
that all sound representations are silent with memset()ed zero data.
*/
memset (silence, 0, b);
process_audio (silence, b);
}
-
+
avcodec_get_frame_defaults (_frame);
int frame_finished;
int const data_size = av_samples_get_buffer_size (
0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
);
-
+
assert (_audio_codec_context->channels == _fs->audio_channels());
process_audio (_frame->data[0], data_size);
}
-
+
} else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) {
int got_subtitle;
#include <string>
#include <stdint.h>
#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libpostproc/postprocess.h>
AVPacket _packet;
- int64_t _first_video_pts;
- int64_t _first_audio_pts;
+ boost::optional<double> _first_video;
+ boost::optional<double> _first_audio;
};
return VIDEO;
}
+/** @return The sampling rate that we will resample the audio to */
int
-FilmState::target_sample_rate () const
+FilmState::target_audio_sample_rate () const
{
/* Resample to a DCI-approved sample rate */
double t = dcp_audio_sample_rate (_audio_sample_rate);
std::string thumb_base (int) const;
int thumb_frame (int) const;
- int target_sample_rate () const;
+ int target_audio_sample_rate () const;
void write_metadata () const;
void read_metadata ();
#ifdef HAVE_SWRESAMPLE
, _swr_context (0)
#endif
+ , _audio_frames_written (0)
, _process_end (false)
{
/* Create sound output files with .tmp suffixes; we will rename
void
J2KWAVEncoder::process_begin (int64_t audio_channel_layout)
{
- if (_fs->audio_sample_rate() != _fs->target_sample_rate()) {
+ if (_fs->audio_sample_rate() != _fs->target_audio_sample_rate()) {
#ifdef HAVE_SWRESAMPLE
stringstream s;
- s << "Will resample audio from " << _fs->audio_sample_rate() << " to " << _fs->target_sample_rate();
+ s << "Will resample audio from " << _fs->audio_sample_rate() << " to " << _fs->target_audio_sample_rate();
_log->log (s.str ());
/* We will be using planar float data when we call the resampler */
0,
audio_channel_layout,
AV_SAMPLE_FMT_FLTP,
- _fs->target_sample_rate(),
+ _fs->target_audio_sample_rate(),
audio_channel_layout,
AV_SAMPLE_FMT_FLTP,
_fs->audio_sample_rate(),
swr_free (&_swr_context);
}
-#endif
+#endif
+
+ int const dcp_sr = dcp_audio_sample_rate (_fs->audio_sample_rate ());
+ int64_t const extra_audio_frames = dcp_sr - (_audio_frames_written % dcp_sr);
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (_fs->audio_channels(), extra_audio_frames));
+ silence->make_silent ();
+ write_audio (silence);
close_sound_files ();
if (_swr_context) {
/* Compute the resampled frames count and add 32 for luck */
- int const max_resampled_frames = ceil (audio->frames() * _fs->target_sample_rate() / _fs->audio_sample_rate()) + 32;
+ int const max_resampled_frames = ceil (audio->frames() * _fs->target_audio_sample_rate() / _fs->audio_sample_rate()) + 32;
resampled.reset (new AudioBuffers (_fs->audio_channels(), max_resampled_frames));
}
void
-J2KWAVEncoder::write_audio (shared_ptr<const AudioBuffers> audio) const
+J2KWAVEncoder::write_audio (shared_ptr<const AudioBuffers> audio)
{
for (int i = 0; i < _fs->audio_channels(); ++i) {
sf_write_float (_sound_files[i], audio->data(i), audio->frames());
}
+
+ _audio_frames_written += audio->frames ();
}
private:
- void write_audio (boost::shared_ptr<const AudioBuffers> audio) const;
+ void write_audio (boost::shared_ptr<const AudioBuffers> audio);
void encoder_thread (ServerDescription *);
void close_sound_files ();
void terminate_worker_threads ();
#endif
std::vector<SNDFILE*> _sound_files;
+ int64_t _audio_frames_written;
bool _process_end;
std::list<boost::shared_ptr<DCPVideoFrame> > _queue;
_frames = f;
}
+void
+AudioBuffers::make_silent ()
+{
+ for (int i = 0; i < _channels; ++i) {
+ for (int j = 0; j < _frames; ++j) {
+ _data[i][j] = 0;
+ }
+ }
+}
+
void
ensure_ui_thread ()
{
void set_frames (int f);
+ void make_silent ();
+
private:
/* no copy construction */
AudioBuffers (AudioBuffers const &);
fs.set_frames_per_second (24);
fs.set_audio_sample_rate (48000);
- BOOST_CHECK_EQUAL (fs.target_sample_rate(), 48000);
+ BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 48000);
fs.set_audio_sample_rate (44100);
- BOOST_CHECK_EQUAL (fs.target_sample_rate(), 48000);
+ BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 48000);
fs.set_audio_sample_rate (80000);
- BOOST_CHECK_EQUAL (fs.target_sample_rate(), 96000);
+ BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 96000);
fs.set_frames_per_second (23.976);
fs.set_audio_sample_rate (48000);
- BOOST_CHECK_EQUAL (fs.target_sample_rate(), 47952);
+ BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 47952);
fs.set_frames_per_second (29.97);
fs.set_audio_sample_rate (48000);
- BOOST_CHECK_EQUAL (fs.target_sample_rate(), 47952);
+ BOOST_CHECK_EQUAL (fs.target_audio_sample_rate(), 47952);
}
class TestJob : public Job