diff --git a/app/audio/audiomanager.cpp b/app/audio/audiomanager.cpp index 174f37fb4..f58449848 100644 --- a/app/audio/audiomanager.cpp +++ b/app/audio/audiomanager.cpp @@ -77,7 +77,7 @@ int InputCallback(const void *input, void *output, unsigned long frameCount, con s->set_sample_count(frameCount); s->set_audio_params(f->params().audio_params()); - f->WriteAudioData(f->params().audio_params(), false, reinterpret_cast(&input), frameCount); + f->WriteAudioData(f->params().audio_params(), reinterpret_cast(&input), frameCount); return paContinue; } @@ -115,16 +115,22 @@ void AudioManager::ClearBufferedOutput() PaSampleFormat AudioManager::GetPortAudioSampleFormat(AudioParams::Format fmt) { switch (fmt) { - case AudioParams::kFormatUnsigned8: + case AudioParams::kFormatUnsigned8Packed: + case AudioParams::kFormatUnsigned8Planar: return paUInt8; - case AudioParams::kFormatSigned16: + case AudioParams::kFormatSigned16Packed: + case AudioParams::kFormatSigned16Planar: return paInt16; - case AudioParams::kFormatSigned32: + case AudioParams::kFormatSigned32Packed: + case AudioParams::kFormatSigned32Planar: return paInt32; - case AudioParams::kFormatFloat32: + case AudioParams::kFormatFloat32Packed: + case AudioParams::kFormatFloat32Planar: return paFloat32; - case AudioParams::kFormatSigned64: - case AudioParams::kFormatFloat64: + case AudioParams::kFormatSigned64Packed: + case AudioParams::kFormatSigned64Planar: + case AudioParams::kFormatFloat64Packed: + case AudioParams::kFormatFloat64Planar: case AudioParams::kFormatInvalid: case AudioParams::kFormatCount: break; diff --git a/app/audio/packedprocessor.cpp b/app/audio/packedprocessor.cpp index f748da5c2..91fd25a5c 100644 --- a/app/audio/packedprocessor.cpp +++ b/app/audio/packedprocessor.cpp @@ -42,10 +42,10 @@ bool PackedProcessor::Open(const AudioParams ¶ms) swr_ctx_ = swr_alloc_set_opts(nullptr, params.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(params.format(), false), + FFmpegUtils::GetFFmpegSampleFormat(AudioParams::GetPackedEquivalent(params.format())), params.sample_rate(), params.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(params.format(), true), + FFmpegUtils::GetFFmpegSampleFormat(params.format()), params.sample_rate(), 0, nullptr); diff --git a/app/audio/planarprocessor.cpp b/app/audio/planarprocessor.cpp index abd2be85b..0d5222b76 100644 --- a/app/audio/planarprocessor.cpp +++ b/app/audio/planarprocessor.cpp @@ -42,10 +42,10 @@ bool PlanarProcessor::Open(const AudioParams ¶ms) swr_ctx_ = swr_alloc_set_opts(nullptr, params.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(params.format(), true), + FFmpegUtils::GetFFmpegSampleFormat(AudioParams::GetPlanarEquivalent(params.format())), params.sample_rate(), params.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(params.format(), false), + FFmpegUtils::GetFFmpegSampleFormat(params.format()), params.sample_rate(), 0, nullptr); diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp index eec326526..06b36c3bb 100644 --- a/app/codec/encoder.cpp +++ b/app/codec/encoder.cpp @@ -320,7 +320,8 @@ Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f) case ExportFormat::kFormatDNxHD: case ExportFormat::kFormatMatroska: case ExportFormat::kFormatQuickTime: - case ExportFormat::kFormatMPEG4: + case ExportFormat::kFormatMPEG4Video: + case ExportFormat::kFormatMPEG4Audio: case ExportFormat::kFormatWAV: case ExportFormat::kFormatAIFF: case ExportFormat::kFormatMP3: @@ -350,4 +351,9 @@ QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const return QStringList(); } +std::vector Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const +{ + return std::vector(); +} + } diff --git a/app/codec/encoder.h b/app/codec/encoder.h index ace7bf7a0..102b0430e 100644 --- a/app/codec/encoder.h +++ b/app/codec/encoder.h @@ -155,6 +155,7 @@ public: static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms); virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const; + virtual std::vector GetSampleFormatsForCodec(ExportCodec::Codec c) const; const EncodingParams& params() const; diff --git a/app/codec/exportcodec.cpp b/app/codec/exportcodec.cpp index 254a9989c..e1c76ad4c 100644 --- a/app/codec/exportcodec.cpp +++ b/app/codec/exportcodec.cpp @@ -103,4 +103,33 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c) return false; } +bool ExportCodec::IsCodecLossless(Codec c) +{ + switch (c) { + case kCodecPCM: + case kCodecFLAC: + return true; + case kCodecDNxHD: + case kCodecH264: + case kCodecH264rgb: + case kCodecH265: + case kCodecProRes: + case kCodecCineform: + case kCodecMP2: + case kCodecMP3: + case kCodecAAC: + case kCodecVorbis: + case kCodecOpus: + case kCodecVP9: + case kCodecSRT: + case kCodecOpenEXR: + case kCodecPNG: + case kCodecTIFF: + case kCodecCount: + break; + } + + return false; +} + } diff --git a/app/codec/exportcodec.h b/app/codec/exportcodec.h index f4d17dfa0..a57abc294 100644 --- a/app/codec/exportcodec.h +++ b/app/codec/exportcodec.h @@ -33,8 +33,8 @@ class ExportCodec : public QObject { Q_OBJECT public: + // Only append to this list (never insert) because indexes are used in serialized files enum Codec { - // Video codecs kCodecDNxHD, kCodecH264, kCodecH264rgb, @@ -45,8 +45,6 @@ public: kCodecCineform, kCodecTIFF, kCodecVP9, - - // Audio codecs kCodecMP2, kCodecMP3, kCodecAAC, @@ -54,8 +52,6 @@ public: kCodecOpus, kCodecVorbis, kCodecFLAC, - - // Subtitle codecs kCodecSRT, kCodecCount @@ -65,6 +61,8 @@ public: static bool IsCodecAStillImage(Codec c); + static bool IsCodecLossless(Codec c); + }; } diff --git a/app/codec/exportformat.cpp b/app/codec/exportformat.cpp index 59561d50b..14f3e8446 100644 --- a/app/codec/exportformat.cpp +++ b/app/codec/exportformat.cpp @@ -31,8 +31,10 @@ QString ExportFormat::GetName(olive::ExportFormat::Format f) return tr("DNxHD"); case kFormatMatroska: return tr("Matroska Video"); - case kFormatMPEG4: + case kFormatMPEG4Video: return tr("MPEG-4 Video"); + case kFormatMPEG4Audio: + return tr("MPEG-4 Audio"); case kFormatOpenEXR: return tr("OpenEXR"); case kFormatPNG: @@ -70,8 +72,10 @@ QString ExportFormat::GetExtension(ExportFormat::Format f) return QStringLiteral("mxf"); case kFormatMatroska: return QStringLiteral("mkv"); - case kFormatMPEG4: + case kFormatMPEG4Video: return QStringLiteral("mp4"); + case kFormatMPEG4Audio: + return QStringLiteral("m4a"); case kFormatOpenEXR: return QStringLiteral("exr"); case kFormatPNG: @@ -108,7 +112,7 @@ QList ExportFormat::GetVideoCodecs(ExportFormat::Format f) return {ExportCodec::kCodecDNxHD}; case kFormatMatroska: return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9}; - case kFormatMPEG4: + case kFormatMPEG4Video: return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265}; case kFormatOpenEXR: return {ExportCodec::kCodecOpenEXR}; @@ -122,6 +126,7 @@ QList ExportFormat::GetVideoCodecs(ExportFormat::Format f) return {ExportCodec::kCodecVP9}; case kFormatOgg: case kFormatWAV: + case kFormatMPEG4Audio: case kFormatAIFF: case kFormatMP3: case kFormatFLAC: @@ -141,7 +146,8 @@ QList ExportFormat::GetAudioCodecs(ExportFormat::Format f) return {ExportCodec::kCodecPCM}; case kFormatMatroska: return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC}; - case kFormatMPEG4: + case kFormatMPEG4Video: + case kFormatMPEG4Audio: return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3}; case kFormatQuickTime: return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM}; @@ -177,7 +183,8 @@ QList ExportFormat::GetSubtitleCodecs(Format f) { switch (f) { case kFormatDNxHD: - case kFormatMPEG4: + case kFormatMPEG4Video: + case kFormatMPEG4Audio: case kFormatOpenEXR: case kFormatQuickTime: case kFormatPNG: @@ -211,4 +218,18 @@ QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, Export return list; } +std::vector ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c) +{ + std::vector f; + Encoder *e = Encoder::CreateFromFormat(format, EncodingParams()); + + if (e) { + f = e->GetSampleFormatsForCodec(c); + delete e; + } + + + return f; +} + } diff --git a/app/codec/exportformat.h b/app/codec/exportformat.h index 36be9db58..acc603c8c 100644 --- a/app/codec/exportformat.h +++ b/app/codec/exportformat.h @@ -33,10 +33,11 @@ class ExportFormat : public QObject { Q_OBJECT public: + // Only append to this list (never insert) because indexes are used in serialized files enum Format { kFormatDNxHD, kFormatMatroska, - kFormatMPEG4, + kFormatMPEG4Video, kFormatOpenEXR, kFormatQuickTime, kFormatPNG, @@ -48,6 +49,7 @@ public: kFormatOgg, kFormatWebM, kFormatSRT, + kFormatMPEG4Audio, kFormatCount }; @@ -59,6 +61,7 @@ public: static QList GetSubtitleCodecs(ExportFormat::Format f); static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c); + static std::vector GetSampleFormatsForCodec(Format f, ExportCodec::Codec c); }; diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 14bcf0e1e..04f889a34 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -455,7 +455,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector &filenames, cons // Create resampling context SwrContext* resampler = swr_alloc_set_opts(nullptr, params.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(params.format(), true), + FFmpegUtils::GetFFmpegSampleFormat(params.format()), params.sample_rate(), channel_layout, static_cast(instance_.avstream()->codecpar->format), diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 01985e0d0..19db89732 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -62,6 +62,38 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const return pix_fmts; } +std::vector FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const +{ + std::vector f; + + if (c == ExportCodec::kCodecPCM) { + // FFmpeg lists these as separate codecs so we need custom functionality here + // We list signed 16 first because ExportDialog will always use the first element by default + // (beacuse first element is the "default" in FFmpeg) + f = { + AudioParams::kFormatSigned16Packed, + AudioParams::kFormatUnsigned8Packed, + AudioParams::kFormatSigned32Packed, + AudioParams::kFormatSigned64Packed, + AudioParams::kFormatFloat32Packed, + AudioParams::kFormatFloat64Packed + }; + } else { + const AVCodec* codec_info = GetEncoder(c, AudioParams::kFormatInvalid); + + if (codec_info && codec_info->sample_fmts) { + for (int i=0; codec_info->sample_fmts[i]!=-1; i++) { + AudioParams::Format this_format = FFmpegUtils::GetNativeSampleFormat(static_cast(codec_info->sample_fmts[i])); + if (this_format != AudioParams::kFormatInvalid) { + f.push_back(this_format); + } + } + } + } + + return f; +} + bool FFmpegEncoder::Open() { if (open_) { @@ -246,14 +278,14 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio) 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); + input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format()), 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()); } } - result = WriteAudioData(audio->audio_params(), true, const_cast(input_data), input_sample_count); + result = WriteAudioData(audio->audio_params(), const_cast(input_data), input_sample_count); if (input_data) { av_freep(&input_data[0]); @@ -263,9 +295,9 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio) return result; } -bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, bool planar, const uint8_t **input_data, int input_sample_count) +bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_t **input_data, int input_sample_count) { - if (!InitializeResampleContext(audio_params, planar)) { + if (!InitializeResampleContext(audio_params)) { qCritical() << "Failed to initialize resample context"; return false; } @@ -783,7 +815,7 @@ void FFmpegEncoder::FlushCodecCtx(AVCodecContext *codec_ctx, AVStream* stream) av_packet_free(&pkt); } -bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio, bool planar) +bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio) { if (audio_resample_ctx_) { return true; @@ -795,7 +827,7 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio, bool pla audio_codec_ctx_->sample_fmt, audio_codec_ctx_->sample_rate, static_cast(audio.channel_layout()), - FFmpegUtils::GetFFmpegSampleFormat(audio.format(), planar), + FFmpegUtils::GetFFmpegSampleFormat(audio.format()), audio.sample_rate(), 0, nullptr); @@ -875,18 +907,24 @@ const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, AudioParams::Form switch (aformat) { case AudioParams::kFormatInvalid: case AudioParams::kFormatCount: + case AudioParams::kFormatUnsigned8Planar: + case AudioParams::kFormatSigned16Planar: + case AudioParams::kFormatSigned32Planar: + case AudioParams::kFormatSigned64Planar: + case AudioParams::kFormatFloat32Planar: + case AudioParams::kFormatFloat64Planar: break; - case AudioParams::kFormatUnsigned8: + case AudioParams::kFormatUnsigned8Packed: return avcodec_find_encoder(AV_CODEC_ID_PCM_U8); - case AudioParams::kFormatSigned16: + case AudioParams::kFormatSigned16Packed: return avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE); - case AudioParams::kFormatSigned32: + case AudioParams::kFormatSigned32Packed: return avcodec_find_encoder(AV_CODEC_ID_PCM_S32LE); - case AudioParams::kFormatSigned64: + case AudioParams::kFormatSigned64Packed: return avcodec_find_encoder(AV_CODEC_ID_PCM_S64LE); - case AudioParams::kFormatFloat32: + case AudioParams::kFormatFloat32Packed: return avcodec_find_encoder(AV_CODEC_ID_PCM_F32LE); - case AudioParams::kFormatFloat64: + case AudioParams::kFormatFloat64Packed: return avcodec_find_encoder(AV_CODEC_ID_PCM_F64LE); } break; diff --git a/app/codec/ffmpeg/ffmpegencoder.h b/app/codec/ffmpeg/ffmpegencoder.h index 1bbc1ac54..a38955f3f 100644 --- a/app/codec/ffmpeg/ffmpegencoder.h +++ b/app/codec/ffmpeg/ffmpegencoder.h @@ -41,13 +41,15 @@ public: virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override; + virtual std::vector GetSampleFormatsForCodec(ExportCodec::Codec c) const override; + virtual bool Open() override; virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override; virtual bool WriteAudio(olive::SampleBufferPtr audio) override; - bool WriteAudioData(const AudioParams &audio_params, bool planar, const uint8_t **data, int input_sample_count); + bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count); virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override; @@ -78,7 +80,7 @@ private: void FlushEncoders(); void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream); - bool InitializeResampleContext(const AudioParams &audio, bool planar); + bool InitializeResampleContext(const AudioParams &audio); static const AVCodec *GetEncoder(ExportCodec::Codec c, AudioParams::Format aformat); diff --git a/app/common/ffmpegutils.cpp b/app/common/ffmpegutils.cpp index 6bc8b037f..90c84e620 100644 --- a/app/common/ffmpegutils.cpp +++ b/app/common/ffmpegutils.cpp @@ -42,23 +42,29 @@ AudioParams::Format FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp { switch (smp_fmt) { case AV_SAMPLE_FMT_U8: - return AudioParams::kFormatUnsigned8; + return AudioParams::kFormatUnsigned8Packed; case AV_SAMPLE_FMT_S16: - return AudioParams::kFormatSigned16; + return AudioParams::kFormatSigned16Packed; case AV_SAMPLE_FMT_S32: - return AudioParams::kFormatSigned32; + return AudioParams::kFormatSigned32Packed; case AV_SAMPLE_FMT_S64: - return AudioParams::kFormatSigned64; + return AudioParams::kFormatSigned64Packed; case AV_SAMPLE_FMT_FLT: - return AudioParams::kFormatFloat32; + return AudioParams::kFormatFloat32Packed; case AV_SAMPLE_FMT_DBL: - return AudioParams::kFormatFloat64; + return AudioParams::kFormatFloat64Packed; case AV_SAMPLE_FMT_U8P : + return AudioParams::kFormatUnsigned8Planar; case AV_SAMPLE_FMT_S16P: + return AudioParams::kFormatSigned16Planar; case AV_SAMPLE_FMT_S32P: + return AudioParams::kFormatSigned32Planar; case AV_SAMPLE_FMT_S64P: + return AudioParams::kFormatSigned64Planar; case AV_SAMPLE_FMT_FLTP: + return AudioParams::kFormatFloat32Planar; case AV_SAMPLE_FMT_DBLP: + return AudioParams::kFormatFloat64Planar; case AV_SAMPLE_FMT_NONE: case AV_SAMPLE_FMT_NB: break; @@ -67,21 +73,33 @@ AudioParams::Format FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp return AudioParams::kFormatInvalid; } -AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt, bool planar) +AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt) { switch (smp_fmt) { - case AudioParams::kFormatUnsigned8: - return planar ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8; - case AudioParams::kFormatSigned16: - return planar ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16; - case AudioParams::kFormatSigned32: - return planar ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32; - case AudioParams::kFormatSigned64: - return planar ? AV_SAMPLE_FMT_S64P : AV_SAMPLE_FMT_S64; - case AudioParams::kFormatFloat32: - return planar ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT; - case AudioParams::kFormatFloat64: - return planar ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL; + case AudioParams::kFormatUnsigned8Packed: + return AV_SAMPLE_FMT_U8; + case AudioParams::kFormatSigned16Packed: + return AV_SAMPLE_FMT_S16; + case AudioParams::kFormatSigned32Packed: + return AV_SAMPLE_FMT_S32; + case AudioParams::kFormatSigned64Packed: + return AV_SAMPLE_FMT_S64; + case AudioParams::kFormatFloat32Packed: + return AV_SAMPLE_FMT_FLT; + case AudioParams::kFormatFloat64Packed: + return AV_SAMPLE_FMT_DBL; + case AudioParams::kFormatUnsigned8Planar: + return AV_SAMPLE_FMT_U8P; + case AudioParams::kFormatSigned16Planar: + return AV_SAMPLE_FMT_S16P; + case AudioParams::kFormatSigned32Planar: + return AV_SAMPLE_FMT_S32P; + case AudioParams::kFormatSigned64Planar: + return AV_SAMPLE_FMT_S64P; + case AudioParams::kFormatFloat32Planar: + return AV_SAMPLE_FMT_FLTP; + case AudioParams::kFormatFloat64Planar: + return AV_SAMPLE_FMT_DBLP; case AudioParams::kFormatInvalid: case AudioParams::kFormatCount: break; diff --git a/app/common/ffmpegutils.h b/app/common/ffmpegutils.h index 2262dc299..8208e56ad 100644 --- a/app/common/ffmpegutils.h +++ b/app/common/ffmpegutils.h @@ -56,7 +56,7 @@ public: /** * @brief Returns an FFmpeg sample format type for a given native type */ - static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt, bool planar = false); + static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt); }; } diff --git a/app/config/config.cpp b/app/config/config.cpp index f52b0c175..d114b0a10 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -125,7 +125,7 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM); SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); - SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kInt, AudioParams::kFormatSigned16); + SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kInt, AudioParams::kFormatSigned16Packed); SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320); SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0))); diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index c7a467d5c..8723634e2 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -194,8 +194,8 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : SetDefaultFilename(); // Set defaults - previously_selected_format_ = ExportFormat::kFormatMPEG4; - format_combobox_->SetFormat(ExportFormat::kFormatMPEG4); + previously_selected_format_ = ExportFormat::kFormatMPEG4Video; + format_combobox_->SetFormat(ExportFormat::kFormatMPEG4Video); connect(format_combobox_, &ExportFormatComboBox::FormatChanged, this, &ExportDialog::FormatChanged); FormatChanged(format_combobox_->GetFormat()); @@ -211,7 +211,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : video_tab_->pixel_format_field()->SetPixelFormat(static_cast(Config::Current()[QStringLiteral("OnlinePixelFormat")].toInt())); video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing()); audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate()); - audio_tab_->sample_format_combobox()->SetSampleFormat(ap.format()); + audio_tab_->sample_format_combobox()->SetAttemptToRestoreFormat(false); audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout()); video_aspect_ratio_ = static_cast(vp.width()) / static_cast(vp.height()); @@ -514,7 +514,7 @@ ExportParams ExportDialog::GenerateParams() const video_tab_->interlaced_combobox()->GetInterlaceMode(), 1); - AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(), + AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->GetSampleRate(), audio_tab_->channel_layout_combobox()->GetChannelLayout(), audio_tab_->sample_format_combobox()->GetSampleFormat()); diff --git a/app/dialog/export/exportaudiotab.cpp b/app/dialog/export/exportaudiotab.cpp index 70ef8d19a..860052a7c 100644 --- a/app/dialog/export/exportaudiotab.cpp +++ b/app/dialog/export/exportaudiotab.cpp @@ -27,6 +27,8 @@ namespace olive { +const int ExportAudioTab::kDefaultBitRate = 320; + ExportAudioTab::ExportAudioTab(QWidget* parent) : QWidget(parent) { @@ -40,6 +42,8 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) : layout->addWidget(new QLabel(tr("Codec:")), row, 0); codec_combobox_ = new QComboBox(); + connect(codec_combobox_, static_cast(&QComboBox::currentIndexChanged), this, &ExportAudioTab::UpdateSampleFormats); + connect(codec_combobox_, static_cast(&QComboBox::currentIndexChanged), this, &ExportAudioTab::UpdateBitRateEnabled); layout->addWidget(codec_combobox_, row, 1); row++; @@ -70,7 +74,7 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) : bit_rate_slider_ = new IntegerSlider(); bit_rate_slider_->SetMinimum(32); bit_rate_slider_->SetMaximum(320); - bit_rate_slider_->SetValue(320); + bit_rate_slider_->SetValue(kDefaultBitRate); bit_rate_slider_->SetFormat(tr("%1 kbps")); layout->addWidget(bit_rate_slider_, row, 1); @@ -81,11 +85,36 @@ int ExportAudioTab::SetFormat(ExportFormat::Format format) { QList acodecs = ExportFormat::GetAudioCodecs(format); setEnabled(!acodecs.isEmpty()); + codec_combobox_->blockSignals(true); codec_combobox_->clear(); foreach (ExportCodec::Codec acodec, acodecs) { codec_combobox_->addItem(ExportCodec::GetCodecName(acodec), acodec); } + codec_combobox_->blockSignals(false); + fmt_ = format; + + UpdateSampleFormats(); + UpdateBitRateEnabled(); + return acodecs.size(); } +void ExportAudioTab::UpdateSampleFormats() +{ + auto fmts = ExportFormat::GetSampleFormatsForCodec(fmt_, GetCodec()); + sample_format_combobox_->SetAvailableFormats(fmts); +} + +void ExportAudioTab::UpdateBitRateEnabled() +{ + bool uses_bitrate = !ExportCodec::IsCodecLossless(GetCodec()); + bit_rate_slider_->setEnabled(uses_bitrate); + + if (!uses_bitrate) { + bit_rate_slider_->SetTristate(); + } else { + bit_rate_slider_->SetValue(kDefaultBitRate ); + } +} + } diff --git a/app/dialog/export/exportaudiotab.h b/app/dialog/export/exportaudiotab.h index 241db8e8b..9cd8faac2 100644 --- a/app/dialog/export/exportaudiotab.h +++ b/app/dialog/export/exportaudiotab.h @@ -76,12 +76,20 @@ public slots: int SetFormat(ExportFormat::Format format); private: + ExportFormat::Format fmt_; QComboBox* codec_combobox_; SampleRateComboBox* sample_rate_combobox_; ChannelLayoutComboBox* channel_layout_combobox_; SampleFormatComboBox *sample_format_combobox_; IntegerSlider* bit_rate_slider_; + static const int kDefaultBitRate; + +private slots: + void UpdateSampleFormats(); + + void UpdateBitRateEnabled(); + }; } diff --git a/app/dialog/preferences/tabs/preferencesaudiotab.cpp b/app/dialog/preferences/tabs/preferencesaudiotab.cpp index 8a8b99a83..c8760ae8a 100644 --- a/app/dialog/preferences/tabs/preferencesaudiotab.cpp +++ b/app/dialog/preferences/tabs/preferencesaudiotab.cpp @@ -103,6 +103,7 @@ PreferencesAudioTab::PreferencesAudioTab() fmt_layout->addWidget(record_format_combo_); record_options_ = new ExportAudioTab(); + record_options_->SetFormat(record_format_combo_->GetFormat()); record_options_->SetCodec(static_cast(OLIVE_CONFIG("AudioRecordingCodec").toInt())); record_options_->sample_rate_combobox()->SetSampleRate(OLIVE_CONFIG("AudioRecordingSampleRate").toInt()); record_options_->channel_layout_combobox()->SetChannelLayout(OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong()); @@ -111,8 +112,6 @@ PreferencesAudioTab::PreferencesAudioTab() recording_layout->addWidget(record_options_); connect(record_format_combo_, &ExportFormatComboBox::FormatChanged, record_options_, &ExportAudioTab::SetFormat); - - record_options_->SetFormat(record_format_combo_->GetFormat()); } QHBoxLayout* refresh_layout = new QHBoxLayout(); diff --git a/app/render/audioparams.cpp b/app/render/audioparams.cpp index f90b70aa9..3b5db0230 100644 --- a/app/render/audioparams.cpp +++ b/app/render/audioparams.cpp @@ -52,7 +52,7 @@ const QVector AudioParams::kSupportedChannelLayouts = { AV_CH_LAYOUT_7POINT1 }; -const AudioParams::Format AudioParams::kInternalFormat = AudioParams::kFormatFloat32; +const AudioParams::Format AudioParams::kInternalFormat = AudioParams::kFormatFloat32Planar; bool AudioParams::operator==(const AudioParams &other) const { @@ -144,15 +144,21 @@ int AudioParams::channel_count() const int AudioParams::bytes_per_sample_per_channel() const { switch (format_) { - case kFormatUnsigned8: + case kFormatUnsigned8Packed: + case kFormatUnsigned8Planar: return 1; - case kFormatSigned16: + case kFormatSigned16Packed: + case kFormatSigned16Planar: return 2; - case kFormatSigned32: - case kFormatFloat32: + case kFormatSigned32Packed: + case kFormatSigned32Planar: + case kFormatFloat32Packed: + case kFormatFloat32Planar: return 4; - case kFormatSigned64: - case kFormatFloat64: + case kFormatSigned64Packed: + case kFormatSigned64Planar: + case kFormatFloat64Packed: + case kFormatFloat64Planar: return 8; case kFormatInvalid: case kFormatCount: @@ -247,24 +253,30 @@ QString AudioParams::ChannelLayoutToString(const uint64_t &layout) QString AudioParams::FormatToString(const Format &f) { switch (f) { - case kFormatUnsigned8: - return QCoreApplication::translate("AudioParams", "Unsigned 8-bit"); - break; - case kFormatSigned16: - return QCoreApplication::translate("AudioParams", "Signed 16-bit"); - break; - case kFormatSigned32: - return QCoreApplication::translate("AudioParams", "Signed 32-bit"); - break; - case kFormatSigned64: - return QCoreApplication::translate("AudioParams", "Signed 64-bit"); - break; - case kFormatFloat32: - return QCoreApplication::translate("AudioParams", "Float 32-bit"); - break; - case kFormatFloat64: - return QCoreApplication::translate("AudioParams", "Float 64-bit"); - break; + case kFormatUnsigned8Packed: + return QCoreApplication::translate("AudioParams", "Unsigned 8-bit (Packed)"); + case kFormatSigned16Packed: + return QCoreApplication::translate("AudioParams", "Signed 16-bit (Packed)"); + case kFormatSigned32Packed: + return QCoreApplication::translate("AudioParams", "Signed 32-bit (Packed)"); + case kFormatSigned64Packed: + return QCoreApplication::translate("AudioParams", "Signed 64-bit (Packed)"); + case kFormatFloat32Packed: + return QCoreApplication::translate("AudioParams", "Float 32-bit (Packed)"); + case kFormatFloat64Packed: + return QCoreApplication::translate("AudioParams", "Float 64-bit (Packed)"); + case kFormatUnsigned8Planar: + return QCoreApplication::translate("AudioParams", "Unsigned 8-bit (Planar)"); + case kFormatSigned16Planar: + return QCoreApplication::translate("AudioParams", "Signed 16-bit (Planar)"); + case kFormatSigned32Planar: + return QCoreApplication::translate("AudioParams", "Signed 32-bit (Planar)"); + case kFormatSigned64Planar: + return QCoreApplication::translate("AudioParams", "Signed 64-bit (Planar)"); + case kFormatFloat32Planar: + return QCoreApplication::translate("AudioParams", "Float 32-bit (Planar)"); + case kFormatFloat64Planar: + return QCoreApplication::translate("AudioParams", "Float 64-bit (Planar)"); case kFormatInvalid: case kFormatCount: @@ -274,4 +286,74 @@ QString AudioParams::FormatToString(const Format &f) return QCoreApplication::translate("AudioParams", "Unknown (0x%1)").arg(f, 1, 16); } +AudioParams::Format AudioParams::GetPackedEquivalent(Format fmt) +{ + switch (fmt) { + + // For packed input, just return input + case kFormatUnsigned8Packed: + case kFormatSigned16Packed: + case kFormatSigned32Packed: + case kFormatSigned64Packed: + case kFormatFloat32Packed: + case kFormatFloat64Packed: + return fmt; + + // Convert to packed + case kFormatUnsigned8Planar: + return kFormatUnsigned8Packed; + case kFormatSigned16Planar: + return kFormatSigned16Packed; + case kFormatSigned32Planar: + return kFormatSigned32Packed; + case kFormatSigned64Planar: + return kFormatSigned64Packed; + case kFormatFloat32Planar: + return kFormatFloat32Packed; + case kFormatFloat64Planar: + return kFormatFloat64Packed; + + case kFormatInvalid: + case kFormatCount: + break; + } + + return kFormatInvalid; +} + +AudioParams::Format AudioParams::GetPlanarEquivalent(Format fmt) +{ + switch (fmt) { + + // Convert to planar + case kFormatUnsigned8Packed: + return kFormatUnsigned8Planar; + case kFormatSigned16Packed: + return kFormatSigned16Planar; + case kFormatSigned32Packed: + return kFormatSigned32Planar; + case kFormatSigned64Packed: + return kFormatSigned64Planar; + case kFormatFloat32Packed: + return kFormatFloat32Planar; + case kFormatFloat64Packed: + return kFormatFloat64Planar; + + // For planar input, just return input + case kFormatUnsigned8Planar: + case kFormatSigned16Planar: + case kFormatSigned32Planar: + case kFormatSigned64Planar: + case kFormatFloat32Planar: + case kFormatFloat64Planar: + return fmt; + + case kFormatInvalid: + case kFormatCount: + break; + } + + return kFormatInvalid; +} + } diff --git a/app/render/audioparams.h b/app/render/audioparams.h index cd0fd94b9..9213fd872 100644 --- a/app/render/audioparams.h +++ b/app/render/audioparams.h @@ -35,30 +35,54 @@ namespace olive { class AudioParams { public: + // Only append to this list (never insert) because indexes are used in serialized files enum Format { /// Invalid kFormatInvalid = -1, /// 8-bit unsigned integer - kFormatUnsigned8, + kFormatUnsigned8Planar, /// 16-bit signed integer - kFormatSigned16, + kFormatSigned16Planar, /// 32-bit signed integer - kFormatSigned32, + kFormatSigned32Planar, /// 64-bit signed integer - kFormatSigned64, + kFormatSigned64Planar, /// 32-bit float - kFormatFloat32, + kFormatFloat32Planar, /// 64-bit float - kFormatFloat64, + kFormatFloat64Planar, + + /// 8-bit unsigned integer + kFormatUnsigned8Packed, + + /// 16-bit signed integer + kFormatSigned16Packed, + + /// 32-bit signed integer + kFormatSigned32Packed, + + /// 64-bit signed integer + kFormatSigned64Packed, + + /// 32-bit float + kFormatFloat32Packed, + + /// 64-bit float + kFormatFloat64Packed, /// Total format count - kFormatCount + kFormatCount, + + kPlanarStart = kFormatUnsigned8Planar, + kPackedStart = kFormatUnsigned8Packed, + kPlanarEnd = kPackedStart, + kPackedEnd = kFormatCount }; static const Format kInternalFormat; @@ -202,6 +226,9 @@ public: static QString FormatToString(const Format &f); + static AudioParams::Format GetPackedEquivalent(AudioParams::Format fmt); + static AudioParams::Format GetPlanarEquivalent(AudioParams::Format fmt); + private: void set_default_footage_parameters() { diff --git a/app/widget/standardcombos/sampleformatcombobox.h b/app/widget/standardcombos/sampleformatcombobox.h index a1e610f7e..d30418f17 100644 --- a/app/widget/standardcombos/sampleformatcombobox.h +++ b/app/widget/standardcombos/sampleformatcombobox.h @@ -32,13 +32,28 @@ class SampleFormatComboBox : public QComboBox Q_OBJECT public: SampleFormatComboBox(QWidget* parent = nullptr) : - QComboBox(parent) + QComboBox(parent), + attempt_to_restore_format_(true) { - // Set up preview formats - for (int i=0;i(i); + } - this->addItem(AudioParams::FormatToString(smp_fmt), smp_fmt); + void SetAttemptToRestoreFormat(bool e) { attempt_to_restore_format_ = e; } + + void SetAvailableFormats(const std::vector &formats) + { + AudioParams::Format tmp; + + if (attempt_to_restore_format_) { + tmp = GetSampleFormat(); + } + + clear(); + foreach (const AudioParams::Format &of, formats) { + AddFormatItem(of); + } + + if (attempt_to_restore_format_) { + SetSampleFormat(tmp); } } @@ -57,6 +72,14 @@ public: } } +private: + void AddFormatItem(AudioParams::Format f) + { + this->addItem(AudioParams::FormatToString(f), f); + } + + bool attempt_to_restore_format_; + }; }