From 15556c64aea5cf9494c97b43f4a3ad7aed4ea83d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 10 May 2021 00:44:08 +1000 Subject: [PATCH] encoder: write audio in segments I'll be honest, this probably makes no noticeable difference at all. It should be faster and more efficient, but whether that translates into any tangible improvement is yet to be seen. --- app/codec/encoder.cpp | 6 - app/codec/encoder.h | 6 +- app/codec/ffmpeg/ffmpegdecoder.cpp | 14 +- app/codec/ffmpeg/ffmpegencoder.cpp | 323 +++++++++++++++++++---------- app/codec/ffmpeg/ffmpegencoder.h | 8 +- app/codec/oiio/oiioencoder.cpp | 3 +- app/codec/oiio/oiioencoder.h | 3 +- app/common/ffmpegutils.cpp | 14 +- app/common/ffmpegutils.h | 2 +- app/task/export/export.cpp | 43 +++- app/task/export/export.h | 6 +- app/task/render/render.cpp | 19 +- 12 files changed, 295 insertions(+), 152 deletions(-) diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp index ba109d678..5392fff9d 100644 --- a/app/codec/encoder.cpp +++ b/app/codec/encoder.cpp @@ -82,12 +82,6 @@ QString Encoder::FilenameRemoveDigitPlaceholder(QString filename) return filename.remove(kImageSequenceRemoveDigits); } -void Encoder::WriteAudio(AudioParams pcm_info, const QString &pcm_filename) -{ - QFile f(pcm_filename); - WriteAudio(pcm_info, &f); -} - EncodingParams::EncodingParams() : video_enabled_(false), video_bit_rate_(0), diff --git a/app/codec/encoder.h b/app/codec/encoder.h index f8693aa26..5bcb8738b 100644 --- a/app/codec/encoder.h +++ b/app/codec/encoder.h @@ -29,6 +29,7 @@ #include "codec/exportcodec.h" #include "codec/exportformat.h" #include "codec/frame.h" +#include "codec/samplebuffer.h" #include "common/timerange.h" #include "render/audioparams.h" #include "render/videoparams.h" @@ -172,10 +173,7 @@ public slots: virtual bool Open() = 0; virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) = 0; - virtual void WriteAudio(olive::AudioParams pcm_info, - QIODevice *file) = 0; - void WriteAudio(olive::AudioParams pcm_info, - const QString& pcm_filename); + virtual bool WriteAudio(olive::SampleBufferPtr audio) = 0; virtual void Close() = 0; diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 3bdfae0aa..34c21ea1b 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -434,7 +434,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn QString FFmpegDecoder::FFmpegError(int error_code) { char err[1024]; - av_strerror(error_code, err, 1024); + av_strerror(error_code, err, 512); return QStringLiteral("%1 %2").arg(QString::number(error_code), err); } @@ -487,8 +487,8 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar if (ret == AVERROR_EOF) { success = true; } else { - char err_str[50]; - av_strerror(ret, err_str, 50); + char err_str[512]; + av_strerror(ret, err_str, 512); qWarning() << "Failed to conform:" << ret << err_str; } break; @@ -507,8 +507,8 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar frame->nb_samples); if (nb_samples < 0) { - char err_str[50]; - av_strerror(nb_samples, err_str, 50); + char err_str[512]; + av_strerror(nb_samples, err_str, 512); qWarning() << "libswresample failed with error:" << nb_samples << err_str; break; } @@ -1020,8 +1020,8 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index) // Open codec error_code = avcodec_open2(codec_ctx_, codec, &opts_); if (error_code < 0) { - char buf[50]; - av_strerror(error_code, buf, 50); + char buf[512]; + av_strerror(error_code, buf, 512); qCritical() << "Failed to open codec" << codec->id << error_code << buf; return false; } diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 440c7d7c2..761614cff 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -40,6 +40,7 @@ FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) : audio_stream_(nullptr), audio_codec_ctx_(nullptr), audio_resample_ctx_(nullptr), + audio_frame_(nullptr), open_(false) { } @@ -132,10 +133,10 @@ bool FFmpegEncoder::Open() // This is the equivalent pixel format above as an AVPixelFormat that swscale can understand AVPixelFormat src_alpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_, - VideoParams::kRGBAChannelCount); + VideoParams::kRGBAChannelCount); AVPixelFormat src_noalpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_, - VideoParams::kRGBChannelCount); + VideoParams::kRGBChannelCount); if (src_alpha_pix_fmt == AV_PIX_FMT_NONE || src_noalpha_pix_fmt == AV_PIX_FMT_NONE) { SetError(tr("Failed to find suitable pixel format for this buffer")); @@ -171,9 +172,10 @@ bool FFmpegEncoder::Open() } // Initialize an audio stream if it's enabled - if (params().audio_enabled() - && !InitializeStream(AVMEDIA_TYPE_AUDIO, &audio_stream_, &audio_codec_ctx_, params().audio_codec())) { - return false; + if (params().audio_enabled()) { + if (!InitializeStream(AVMEDIA_TYPE_AUDIO, &audio_stream_, &audio_codec_ctx_, params().audio_codec())) { + return false; + } } av_dump_format(fmt_ctx_, 0, filename_c_str, 1); @@ -261,107 +263,147 @@ fail: return success; } +bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio) +{ + if (!InitializeResampleContext(audio)) { + qCritical() << "Failed to initialize resample context"; + return false; + } + + bool result = true; + + // Create input buffer + int input_sample_count = 0; + uint8_t** input_data = nullptr; + if (audio) { + input_sample_count = audio->sample_count(); + int input_linesize; + + av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio->audio_params().channel_count(), + input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format(), true), 0); + + for (int i=0; iaudio_params().channel_count(); i++) { + memcpy(input_data[i], audio->data(i), input_sample_count * audio->audio_params().bytes_per_sample_per_channel()); + } + } + + // Create output buffer + int output_sample_count = input_sample_count ? swr_get_out_samples(audio_resample_ctx_, input_sample_count) : 102400; + uint8_t** output_data = nullptr; + int output_linesize; + av_samples_alloc_array_and_samples(&output_data, &output_linesize, audio_stream_->codecpar->channels, + output_sample_count, static_cast(audio_stream_->codecpar->format), 0); + + // Perform conversion + int converted = swr_convert(audio_resample_ctx_, output_data, output_sample_count, const_cast(input_data), input_sample_count); + if (converted > 0) { + // Split sample buffer into frames + for (int i=0; inb_samples) { + int copy_offset = audio_frame_offset_; + int frame_remaining_samples = audio_frame_->nb_samples - copy_offset; + int converted_remaining_samples = output_sample_count - i; + + int copy_length = qMin(frame_remaining_samples, converted_remaining_samples); + + av_samples_copy(audio_frame_->data, output_data, copy_offset, i, + copy_length, + audio_frame_->channels, static_cast(audio_frame_->format)); + + if (copy_length != frame_remaining_samples && input_data) { + // Frame didn't get all the samples it needed, save them for later + audio_frame_offset_ += copy_length; + } else { + // Got all the samples we needed, write the frame + audio_frame_->pts = audio_write_count_; + + if (!input_data) { + // Assume flushing and make this frame's samples = the amount copied + audio_frame_->nb_samples = copy_offset + copy_length; + qDebug() << "Writing" << audio_frame_->nb_samples << "flushed samples"; + } + + WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_); + audio_write_count_ += audio_frame_->nb_samples; + audio_frame_offset_ = 0; + } + } + } else if (converted < 0) { + FFmpegError(tr("Failed to resample audio"), converted); + result = false; + } + + // Free buffers created + if (output_data) { + av_freep(&output_data[0]); + av_freep(&output_data); + } + + if (input_data) { + av_freep(&input_data[0]); + av_freep(&input_data); + } + + return result; +} +/* void FFmpegEncoder::WriteAudio(AudioParams pcm_info, QIODevice* file) { - if (file->open(QFile::ReadOnly)) { - // Divide PCM stream into AVFrames - // See if the codec defines a number of samples per frame - int maximum_frame_samples = audio_codec_ctx_->frame_size; - if (!maximum_frame_samples) { - // If not, use another frame size - if (params().video_enabled()) { - // If we're encoding video, use enough samples to cover roughly one frame of video - maximum_frame_samples = params().audio_params().time_to_samples(params().video_params().frame_rate_as_time_base()); - } else { - // If no video, just use an arbitrary number - maximum_frame_samples = 256; - } + + // Keep track of sample count to use as each frame's timebase + int sample_counter = 0; + + while (true) { + // Calculate how many samples should input this frame + int64_t samples_needed = av_rescale_rnd(maximum_frame_samples + swr_get_delay(swr_ctx, pcm_info.sample_rate()), + audio_codec_ctx_->sample_rate, + pcm_info.sample_rate(), + AV_ROUND_UP); + + // Calculate how many bytes this is + int max_read = pcm_info.samples_to_bytes(samples_needed); + + // Read bytes from PCM + QByteArray input_data = file->read(max_read); + + // Use swresample to convert the data into the correct format + const char* input_data_array = input_data.constData(); + int converted = swr_convert(swr_ctx, + + // output data + frame->data, + + // output sample count (maximum amount of samples in output) + maximum_frame_samples, + + // input data + reinterpret_cast(&input_data_array), + + // input sample count (maximum amount of samples we read from pcm file) + pcm_info.bytes_to_samples(input_data.size())); + + // Update the frame's number of samples to the amount we actually received + frame->nb_samples = converted; + + // Update frame timestamp + frame->pts = sample_counter; + + // Increment timestamp for the next frame by the amount of samples in this one + sample_counter += converted; + + // Write the frame + if (!WriteAVFrame(frame, audio_codec_ctx_, audio_stream_)) { + qCritical() << "Failed to write audio AVFrame"; + break; } - SwrContext* swr_ctx = swr_alloc_set_opts(nullptr, - static_cast(audio_codec_ctx_->channel_layout), - audio_codec_ctx_->sample_fmt, - audio_codec_ctx_->sample_rate, - static_cast(pcm_info.channel_layout()), - FFmpegUtils::GetFFmpegSampleFormat(pcm_info.format()), - pcm_info.sample_rate(), - 0, - nullptr); - - swr_init(swr_ctx); - - // Loop through PCM queueing write events - AVFrame* frame = av_frame_alloc(); - - // Set up frame and allocate its buffers - frame->channel_layout = audio_codec_ctx_->channel_layout; - frame->nb_samples = maximum_frame_samples; - frame->format = audio_codec_ctx_->sample_fmt; - av_frame_get_buffer(frame, 0); - - // Keep track of sample count to use as each frame's timebase - int sample_counter = 0; - - while (true) { - // Calculate how many samples should input this frame - int64_t samples_needed = av_rescale_rnd(maximum_frame_samples + swr_get_delay(swr_ctx, pcm_info.sample_rate()), - audio_codec_ctx_->sample_rate, - pcm_info.sample_rate(), - AV_ROUND_UP); - - // Calculate how many bytes this is - int max_read = pcm_info.samples_to_bytes(samples_needed); - - // Read bytes from PCM - QByteArray input_data = file->read(max_read); - - // Use swresample to convert the data into the correct format - const char* input_data_array = input_data.constData(); - int converted = swr_convert(swr_ctx, - - // output data - frame->data, - - // output sample count (maximum amount of samples in output) - maximum_frame_samples, - - // input data - reinterpret_cast(&input_data_array), - - // input sample count (maximum amount of samples we read from pcm file) - pcm_info.bytes_to_samples(input_data.size())); - - // Update the frame's number of samples to the amount we actually received - frame->nb_samples = converted; - - // Update frame timestamp - frame->pts = sample_counter; - - // Increment timestamp for the next frame by the amount of samples in this one - sample_counter += converted; - - // Write the frame - if (!WriteAVFrame(frame, audio_codec_ctx_, audio_stream_)) { - qCritical() << "Failed to write audio AVFrame"; - break; - } - - // Break if we've reached the end point - if (file->atEnd()) { - break; - } + // Break if we've reached the end point + if (file->atEnd()) { + break; } - - av_frame_free(&frame); - - swr_free(&swr_ctx); - - file->close(); - } else { - qWarning() << "Failed to open audio IO device for encoding"; } } +*/ void FFmpegEncoder::Close() { @@ -376,6 +418,16 @@ void FFmpegEncoder::Close() open_ = false; } + if (audio_resample_ctx_) { + swr_init(audio_resample_ctx_); + audio_resample_ctx_ = nullptr; + } + + if (audio_frame_) { + av_frame_free(&audio_frame_); + audio_frame_ = nullptr; + } + if (video_alpha_scale_ctx_) { sws_freeContext(video_alpha_scale_ctx_); video_alpha_scale_ctx_ = nullptr; @@ -400,15 +452,19 @@ void FFmpegEncoder::Close() // NOTE: This also frees video_stream_ and audio_stream_ avformat_free_context(fmt_ctx_); fmt_ctx_ = nullptr; + video_stream_ = nullptr; + audio_stream_ = nullptr; } } void FFmpegEncoder::FFmpegError(const QString& context, int error_code) { - char err[128]; - av_strerror(error_code, err, 128); + char err[1024]; + av_strerror(error_code, err, 1024); - SetError(tr("%1: %2 %3").arg(context, err, QString::number(error_code))); + QString formatted_err = tr("%1: %2 %3").arg(context, formatted_err, QString::number(error_code)); + qDebug() << formatted_err; + SetError(formatted_err); } bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext* codec_ctx, AVStream* stream) @@ -569,9 +625,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV // Set custom options { - QHash::const_iterator i; - - for (i=params().video_opts().begin();i!=params().video_opts().end();i++) { + for (auto i=params().video_opts().begin();i!=params().video_opts().end();i++) { av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN); } @@ -674,6 +728,8 @@ void FFmpegEncoder::FlushEncoders() } if (audio_codec_ctx_) { + WriteAudio(nullptr); + FlushCodecCtx(audio_codec_ctx_, audio_stream_); } } @@ -700,4 +756,63 @@ void FFmpegEncoder::FlushCodecCtx(AVCodecContext *codec_ctx, AVStream* stream) av_packet_free(&pkt); } +bool FFmpegEncoder::InitializeResampleContext(SampleBufferPtr audio) +{ + if (audio_resample_ctx_) { + return true; + } + + // Create resample context + audio_resample_ctx_ = swr_alloc_set_opts(nullptr, + static_cast(audio_codec_ctx_->channel_layout), + audio_codec_ctx_->sample_fmt, + audio_codec_ctx_->sample_rate, + static_cast(audio->audio_params().channel_layout()), + FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format(), true), + audio->audio_params().sample_rate(), + 0, + nullptr); + if (!audio_resample_ctx_) { + return false; + } + + int err = swr_init(audio_resample_ctx_); + if (err < 0) { + FFmpegError(tr("Failed to create resampling context"), err); + return false; + } + + int max_frame_samples = audio_codec_ctx_->frame_size; + if (!max_frame_samples) { + // If not, use another frame size + if (params().video_enabled()) { + // If we're encoding video, use enough samples to cover roughly one frame of video + max_frame_samples = params().audio_params().time_to_samples(params().video_params().frame_rate_as_time_base()); + } else { + // If no video, just use an arbitrary number + max_frame_samples = 256; + } + } + + audio_frame_ = av_frame_alloc(); + if (!audio_frame_) { + return false; + } + + audio_frame_->channel_layout = audio_codec_ctx_->channel_layout; + audio_frame_->format = audio_codec_ctx_->sample_fmt; + audio_frame_->nb_samples = max_frame_samples; + + err = av_frame_get_buffer(audio_frame_, 0); + if (err < 0) { + FFmpegError(tr("Failed to create audio frame"), err); + return false; + } + + audio_frame_offset_ = 0; + audio_write_count_ = 0; + + return true; +} + } diff --git a/app/codec/ffmpeg/ffmpegencoder.h b/app/codec/ffmpeg/ffmpegencoder.h index 9d87e1a24..24d50e7fc 100644 --- a/app/codec/ffmpeg/ffmpegencoder.h +++ b/app/codec/ffmpeg/ffmpegencoder.h @@ -44,8 +44,7 @@ public: virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override; - virtual void WriteAudio(olive::AudioParams pcm_info, - QIODevice *file) override; + virtual bool WriteAudio(olive::SampleBufferPtr audio) override; virtual void Close() override; @@ -74,6 +73,8 @@ private: void FlushEncoders(); void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream); + bool InitializeResampleContext(SampleBufferPtr audio); + AVFormatContext* fmt_ctx_; AVStream* video_stream_; @@ -85,6 +86,9 @@ private: AVStream* audio_stream_; AVCodecContext* audio_codec_ctx_; SwrContext* audio_resample_ctx_; + AVFrame* audio_frame_; + int audio_frame_offset_; + int audio_write_count_; bool open_; diff --git a/app/codec/oiio/oiioencoder.cpp b/app/codec/oiio/oiioencoder.cpp index 917b6df23..ff9d9191f 100644 --- a/app/codec/oiio/oiioencoder.cpp +++ b/app/codec/oiio/oiioencoder.cpp @@ -62,9 +62,10 @@ bool OIIOEncoder::WriteFrame(FramePtr frame, rational time) return true; } -void OIIOEncoder::WriteAudio(AudioParams pcm_info, QIODevice *file) +bool OIIOEncoder::WriteAudio(SampleBufferPtr audio) { // Do nothing + return false; } void OIIOEncoder::Close() diff --git a/app/codec/oiio/oiioencoder.h b/app/codec/oiio/oiioencoder.h index d15a9ec95..555efe067 100644 --- a/app/codec/oiio/oiioencoder.h +++ b/app/codec/oiio/oiioencoder.h @@ -35,8 +35,7 @@ public slots: virtual bool Open() override; virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override; - virtual void WriteAudio(olive::AudioParams pcm_info, - QIODevice *file) override; + virtual bool WriteAudio(SampleBufferPtr audio) override; virtual void Close() override; diff --git a/app/common/ffmpegutils.cpp b/app/common/ffmpegutils.cpp index 9228e177e..6bc8b037f 100644 --- a/app/common/ffmpegutils.cpp +++ b/app/common/ffmpegutils.cpp @@ -67,21 +67,21 @@ AudioParams::Format FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp return AudioParams::kFormatInvalid; } -AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt) +AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt, bool planar) { switch (smp_fmt) { case AudioParams::kFormatUnsigned8: - return AV_SAMPLE_FMT_U8; + return planar ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8; case AudioParams::kFormatSigned16: - return AV_SAMPLE_FMT_S16; + return planar ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16; case AudioParams::kFormatSigned32: - return AV_SAMPLE_FMT_S32; + return planar ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32; case AudioParams::kFormatSigned64: - return AV_SAMPLE_FMT_S64; + return planar ? AV_SAMPLE_FMT_S64P : AV_SAMPLE_FMT_S64; case AudioParams::kFormatFloat32: - return AV_SAMPLE_FMT_FLT; + return planar ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT; case AudioParams::kFormatFloat64: - return AV_SAMPLE_FMT_DBL; + return planar ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL; case AudioParams::kFormatInvalid: case AudioParams::kFormatCount: break; diff --git a/app/common/ffmpegutils.h b/app/common/ffmpegutils.h index d8121bdce..3e5ff105d 100644 --- a/app/common/ffmpegutils.h +++ b/app/common/ffmpegutils.h @@ -55,7 +55,7 @@ public: /** * @brief Returns an FFmpeg sample format type for a given native type */ - static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt); + static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt, bool planar = false); }; } diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index 9f694723f..6be0ae097 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -99,10 +99,6 @@ bool ExportTask::Run() params_.color_transform()); } - if (params_.audio_enabled()) { - audio_data_.SetParameters(audio_params()); - } - // Start render process TimeRangeList video_range, audio_range; @@ -112,7 +108,6 @@ bool ExportTask::Run() if (params_.audio_enabled()) { audio_range = {range}; - audio_data_.SetLength(range.length()); } Render(color_manager_, video_range, audio_range, RenderMode::kOnline, nullptr, @@ -121,13 +116,13 @@ bool ExportTask::Run() bool success = true; - if (params_.audio_enabled()) { - // Write audio data now - encoder_->WriteAudio(audio_params(), audio_data_.CreatePlaybackDevice(encoder_)); - } - encoder_->Close(); + if (!encoder_->GetError().isEmpty()) { + SetError(encoder_->GetError()); + success = false; + } + delete encoder_; // If cancelled, delete the file we made, which is always a file we created since we write to a @@ -187,7 +182,33 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples adjusted_range -= params_.custom_range().in(); } - audio_data_.WritePCM(adjusted_range, samples, QDateTime::currentMSecsSinceEpoch()); + if (adjusted_range.in() == audio_time_) { + WriteAudioLoop(adjusted_range, samples); + } else { + audio_map_.insert(adjusted_range, samples); + } +} + +void ExportTask::WriteAudioLoop(const TimeRange& time, SampleBufferPtr samples) +{ + encoder_->WriteAudio(samples); + audio_time_ = time.out(); + + for (auto it=audio_map_.begin(); it!=audio_map_.end(); it++) { + TimeRange t = it.key(); + SampleBufferPtr s = it.value(); + + if (t.in() == audio_time_) { + // Erase from audio map since we're just about to write it + audio_map_.erase(it); + + // Call recursively to write the next sample buffer + WriteAudioLoop(t, s); + + // Break out of loop + break; + } + } } } diff --git a/app/task/export/export.h b/app/task/export/export.h index a29f2ce4f..b02691b54 100644 --- a/app/task/export/export.h +++ b/app/task/export/export.h @@ -48,8 +48,12 @@ protected: } private: + void WriteAudioLoop(const TimeRange &time, SampleBufferPtr samples); + QHash time_map_; + QHash audio_map_; + ColorManager* color_manager_; ExportParams params_; @@ -60,7 +64,7 @@ private: int64_t frame_time_; - AudioPlaybackCache audio_data_; + rational audio_time_; }; diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index da5336024..badf1faed 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -57,17 +57,24 @@ bool RenderTask::Render(ColorManager* manager, qint64 job_time = QDateTime::currentMSecsSinceEpoch(); // Queue audio jobs - foreach (const TimeRange& r, audio_range) { + foreach (const TimeRange& range, audio_range) { // Don't count audio progress, since it's generally a lot faster than video and is weighted at // 50%, which makes the progress bar look weird to the uninitiated //total_length += r.length().toDouble(); - IncrementRunningTickets(); + rational r = range.in(); + while (r != range.out()) { + rational end = qMin(range.out(), r+1); + TimeRange this_range(r, end); - RenderTicketWatcher* watcher = new RenderTicketWatcher(); - watcher->setProperty("range", QVariant::fromValue(r)); - PrepareWatcher(watcher, &watcher_thread); - watcher->SetTicket(RenderManager::instance()->RenderAudio(viewer_, r, audio_params_, false)); + RenderTicketWatcher* watcher = new RenderTicketWatcher(); + watcher->setProperty("range", QVariant::fromValue(this_range)); + PrepareWatcher(watcher, &watcher_thread); + IncrementRunningTickets(); + watcher->SetTicket(RenderManager::instance()->RenderAudio(viewer_, this_range, audio_params_, false)); + + r = end; + } } // Look up hashes