| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "media/audio/media_audio_ffmpeg_loader.h"
- #include "base/bytes.h"
- #include "core/file_location.h"
- #include "ffmpeg/ffmpeg_utility.h"
- #include "media/media_common.h"
- extern "C" {
- #include <libavfilter/buffersink.h>
- #include <libavfilter/buffersrc.h>
- } // extern "C"
- namespace Media {
- namespace {
- using FFmpeg::AvErrorWrap;
- using FFmpeg::LogError;
- } // namespace
- #if !DA_FFMPEG_NEW_CHANNEL_LAYOUT
- uint64_t AbstractFFMpegLoader::ComputeChannelLayout(
- uint64_t channel_layout,
- int channels) {
- if (channel_layout) {
- if (av_get_channel_layout_nb_channels(channel_layout) == channels) {
- return channel_layout;
- }
- }
- return av_get_default_channel_layout(channels);
- }
- #endif // !DA_FFMPEG_NEW_CHANNEL_LAYOUT
- int64 AbstractFFMpegLoader::Mul(int64 value, AVRational rational) {
- return value * rational.num / rational.den;
- }
- bool AbstractFFMpegLoader::open(crl::time positionMs, float64 speed) {
- if (!AudioPlayerLoader::openFile()) {
- return false;
- }
- ioBuffer = (uchar *)av_malloc(FFmpeg::kAVBlockSize);
- if (!_data.isEmpty()) {
- ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::ReadData, 0, &AbstractFFMpegLoader::SeekData);
- } else if (!_bytes.empty()) {
- ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::ReadBytes, 0, &AbstractFFMpegLoader::SeekBytes);
- } else {
- ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::ReadFile, 0, &AbstractFFMpegLoader::SeekFile);
- }
- fmtContext = avformat_alloc_context();
- if (!fmtContext) {
- LogError(u"avformat_alloc_context"_q);
- return false;
- }
- fmtContext->pb = ioContext;
- if (AvErrorWrap error = avformat_open_input(&fmtContext, 0, 0, 0)) {
- ioBuffer = nullptr;
- LogError(u"avformat_open_input"_q, error);
- return false;
- }
- _opened = true;
- if (AvErrorWrap error = avformat_find_stream_info(fmtContext, 0)) {
- LogError(u"avformat_find_stream_info"_q, error);
- return false;
- }
- streamId = av_find_best_stream(fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
- if (streamId < 0) {
- FFmpeg::LogError(u"av_find_best_stream"_q, AvErrorWrap(streamId));
- return false;
- }
- const auto stream = fmtContext->streams[streamId];
- const auto params = stream->codecpar;
- _samplesFrequency = params->sample_rate;
- if (stream->duration != AV_NOPTS_VALUE) {
- _duration = Mul(stream->duration * 1000, stream->time_base);
- } else {
- _duration = Mul(fmtContext->duration * 1000, { 1, AV_TIME_BASE });
- }
- _startedAtSample = (positionMs * _samplesFrequency) / 1000LL;
- return true;
- }
- AbstractFFMpegLoader::~AbstractFFMpegLoader() {
- if (_opened) {
- avformat_close_input(&fmtContext);
- }
- if (ioContext) {
- av_freep(&ioContext->buffer);
- av_freep(&ioContext);
- } else if (ioBuffer) {
- av_freep(&ioBuffer);
- }
- if (fmtContext) avformat_free_context(fmtContext);
- }
- int AbstractFFMpegLoader::ReadData(void *opaque, uint8_t *buf, int buf_size) {
- auto l = reinterpret_cast<AbstractFFMpegLoader *>(opaque);
- auto nbytes = qMin(l->_data.size() - l->_dataPos, int32(buf_size));
- if (nbytes <= 0) {
- return AVERROR_EOF;
- }
- memcpy(buf, l->_data.constData() + l->_dataPos, nbytes);
- l->_dataPos += nbytes;
- return nbytes;
- }
- int64_t AbstractFFMpegLoader::SeekData(void *opaque, int64_t offset, int whence) {
- auto l = reinterpret_cast<AbstractFFMpegLoader *>(opaque);
- int32 newPos = -1;
- switch (whence) {
- case SEEK_SET: newPos = offset; break;
- case SEEK_CUR: newPos = l->_dataPos + offset; break;
- case SEEK_END: newPos = l->_data.size() + offset; break;
- case AVSEEK_SIZE: {
- // Special whence for determining filesize without any seek.
- return l->_data.size();
- } break;
- }
- if (newPos < 0 || newPos > l->_data.size()) {
- return -1;
- }
- l->_dataPos = newPos;
- return l->_dataPos;
- }
- int AbstractFFMpegLoader::ReadBytes(void *opaque, uint8_t *buf, int buf_size) {
- auto l = reinterpret_cast<AbstractFFMpegLoader *>(opaque);
- auto nbytes = qMin(static_cast<int>(l->_bytes.size()) - l->_dataPos, buf_size);
- if (nbytes <= 0) {
- return AVERROR_EOF;
- }
- memcpy(buf, l->_bytes.data() + l->_dataPos, nbytes);
- l->_dataPos += nbytes;
- return nbytes;
- }
- int64_t AbstractFFMpegLoader::SeekBytes(void *opaque, int64_t offset, int whence) {
- auto l = reinterpret_cast<AbstractFFMpegLoader *>(opaque);
- int32 newPos = -1;
- switch (whence) {
- case SEEK_SET: newPos = offset; break;
- case SEEK_CUR: newPos = l->_dataPos + offset; break;
- case SEEK_END: newPos = static_cast<int>(l->_bytes.size()) + offset; break;
- case AVSEEK_SIZE:
- {
- // Special whence for determining filesize without any seek.
- return l->_bytes.size();
- } break;
- }
- if (newPos < 0 || newPos > l->_bytes.size()) {
- return -1;
- }
- l->_dataPos = newPos;
- return l->_dataPos;
- }
- int AbstractFFMpegLoader::ReadFile(void *opaque, uint8_t *buf, int buf_size) {
- auto l = reinterpret_cast<AbstractFFMpegLoader *>(opaque);
- int ret = l->_f.read((char *)(buf), buf_size);
- switch (ret) {
- case -1: return AVERROR_EXTERNAL;
- case 0: return AVERROR_EOF;
- default: return ret;
- }
- }
- int64_t AbstractFFMpegLoader::SeekFile(void *opaque, int64_t offset, int whence) {
- auto l = reinterpret_cast<AbstractFFMpegLoader *>(opaque);
- switch (whence) {
- case SEEK_SET: return l->_f.seek(offset) ? l->_f.pos() : -1;
- case SEEK_CUR: return l->_f.seek(l->_f.pos() + offset) ? l->_f.pos() : -1;
- case SEEK_END: return l->_f.seek(l->_f.size() + offset) ? l->_f.pos() : -1;
- case AVSEEK_SIZE:
- {
- // Special whence for determining filesize without any seek.
- return l->_f.size();
- } break;
- }
- return -1;
- }
- AbstractAudioFFMpegLoader::AbstractAudioFFMpegLoader(
- const Core::FileLocation &file,
- const QByteArray &data,
- bytes::vector &&buffer)
- : AbstractFFMpegLoader(file, data, std::move(buffer))
- , _frame(FFmpeg::MakeFramePointer()) {
- }
- void AbstractAudioFFMpegLoader::dropFramesTill(int64 samples) {
- const auto isAfter = [&](const EnqueuedFrame &frame) {
- return frame.position > samples;
- };
- const auto from = begin(_framesQueued);
- const auto after = ranges::find_if(_framesQueued, isAfter);
- if (from == after) {
- return;
- }
- const auto till = after - 1;
- const auto erasing = till - from;
- if (erasing > 0) {
- if (_framesQueuedIndex >= 0) {
- Assert(_framesQueuedIndex >= erasing);
- _framesQueuedIndex -= erasing;
- }
- _framesQueued.erase(from, till);
- if (_framesQueued.empty()) {
- _framesQueuedIndex = -1;
- }
- }
- }
- int64 AbstractAudioFFMpegLoader::startReadingQueuedFrames(float64 newSpeed) {
- changeSpeedFilter(newSpeed);
- if (_framesQueued.empty()) {
- _framesQueuedIndex = -1;
- return -1;
- }
- _framesQueuedIndex = 0;
- return _framesQueued.front().position;
- }
- bool AbstractAudioFFMpegLoader::initUsingContext(
- not_null<AVCodecContext*> context,
- float64 speed) {
- _swrSrcSampleFormat = context->sample_fmt;
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- const AVChannelLayout mono = AV_CHANNEL_LAYOUT_MONO;
- const AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO;
- const auto useMono = !av_channel_layout_compare(
- &context->ch_layout,
- &mono);
- const auto useStereo = !av_channel_layout_compare(
- &context->ch_layout,
- &stereo);
- const auto copyDstChannelLayout = [&] {
- av_channel_layout_copy(&_swrDstChannelLayout, &context->ch_layout);
- };
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- const auto layout = ComputeChannelLayout(
- context->channel_layout,
- context->channels);
- if (!layout) {
- LOG(("Audio Error: Unknown channel layout %1 for %2 channels."
- ).arg(context->channel_layout
- ).arg(context->channels
- ));
- return false;
- }
- const auto useMono = (layout == AV_CH_LAYOUT_MONO);
- const auto useStereo = (layout == AV_CH_LAYOUT_STEREO);
- const auto copyDstChannelLayout = [&] {
- _swrDstChannelLayout = layout;
- };
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- if (useMono) {
- switch (_swrSrcSampleFormat) {
- case AV_SAMPLE_FMT_U8:
- case AV_SAMPLE_FMT_U8P:
- _swrDstSampleFormat = _swrSrcSampleFormat;
- copyDstChannelLayout();
- _outputChannels = 1;
- _outputSampleSize = 1;
- _outputFormat = AL_FORMAT_MONO8;
- break;
- case AV_SAMPLE_FMT_S16:
- case AV_SAMPLE_FMT_S16P:
- _swrDstSampleFormat = _swrSrcSampleFormat;
- copyDstChannelLayout();
- _outputChannels = 1;
- _outputSampleSize = sizeof(uint16);
- _outputFormat = AL_FORMAT_MONO16;
- break;
- }
- } else if (useStereo) {
- switch (_swrSrcSampleFormat) {
- case AV_SAMPLE_FMT_U8:
- _swrDstSampleFormat = _swrSrcSampleFormat;
- copyDstChannelLayout();
- _outputChannels = 2;
- _outputSampleSize = 2;
- _outputFormat = AL_FORMAT_STEREO8;
- break;
- case AV_SAMPLE_FMT_S16:
- _swrDstSampleFormat = _swrSrcSampleFormat;
- copyDstChannelLayout();
- _outputChannels = 2;
- _outputSampleSize = 2 * sizeof(uint16);
- _outputFormat = AL_FORMAT_STEREO16;
- break;
- }
- }
- createSpeedFilter(speed);
- return true;
- }
- auto AbstractAudioFFMpegLoader::replaceFrameAndRead(
- FFmpeg::FramePointer frame)
- -> ReadResult {
- _frame = std::move(frame);
- return readFromReadyFrame();
- }
- auto AbstractAudioFFMpegLoader::readFromReadyContext(
- not_null<AVCodecContext*> context)
- -> ReadResult {
- if (_filterGraph) {
- AvErrorWrap error = av_buffersink_get_frame(
- _filterSink,
- _filteredFrame.get());
- if (!error) {
- if (!_filteredFrame->nb_samples) {
- return ReadError::Retry;
- }
- return bytes::const_span(
- reinterpret_cast<const bytes::type*>(
- _filteredFrame->extended_data[0]),
- _filteredFrame->nb_samples * _outputSampleSize);
- } else if (error.code() == AVERROR_EOF) {
- return ReadError::EndOfFile;
- } else if (error.code() != AVERROR(EAGAIN)) {
- LogError(u"av_buffersink_get_frame"_q, error);
- return ReadError::Other;
- }
- }
- using Enqueued = not_null<const EnqueuedFrame*>;
- const auto queueResult = fillFrameFromQueued();
- if (queueResult == ReadError::RetryNotQueued) {
- return ReadError::RetryNotQueued;
- } else if (const auto enqueued = std::get_if<Enqueued>(&queueResult)) {
- const auto raw = (*enqueued)->frame.get();
- Assert(frameHasDesiredFormat(raw));
- return readOrBufferForFilter(raw, (*enqueued)->samples);
- }
- const auto queueError = v::get<ReadError>(queueResult);
- AvErrorWrap error = (queueError == ReadError::EndOfFile)
- ? AVERROR_EOF
- : avcodec_receive_frame(context, _frame.get());
- if (!error) {
- return readFromReadyFrame();
- }
- if (error.code() == AVERROR_EOF) {
- enqueueFramesFinished();
- if (!_filterGraph) {
- return ReadError::EndOfFile;
- }
- AvErrorWrap error = av_buffersrc_add_frame(_filterSrc, nullptr);
- if (!error) {
- return ReadError::Retry;
- }
- LogError(u"av_buffersrc_add_frame"_q, error);
- return ReadError::Other;
- } else if (error.code() != AVERROR(EAGAIN)) {
- LogError(u"avcodec_receive_frame"_q, error);
- return ReadError::Other;
- }
- return ReadError::Wait;
- }
- auto AbstractAudioFFMpegLoader::fillFrameFromQueued()
- -> std::variant<not_null<const EnqueuedFrame*>, ReadError> {
- if (_framesQueuedIndex == _framesQueued.size()) {
- _framesQueuedIndex = -1;
- return ReadError::RetryNotQueued;
- } else if (_framesQueuedIndex < 0) {
- return ReadError::Wait;
- }
- const auto &queued = _framesQueued[_framesQueuedIndex];
- ++_framesQueuedIndex;
- if (!queued.frame) {
- return ReadError::EndOfFile;
- }
- return &queued;
- }
- bool AbstractAudioFFMpegLoader::frameHasDesiredFormat(
- not_null<AVFrame*> frame) const {
- const auto sameChannelLayout = [&] {
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- return !av_channel_layout_compare(
- &frame->ch_layout,
- &_swrDstChannelLayout);
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- const auto frameChannelLayout = ComputeChannelLayout(
- frame->channel_layout,
- frame->channels);
- return (frameChannelLayout == _swrDstChannelLayout);
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- };
- return true
- && (frame->format == _swrDstSampleFormat)
- && (frame->sample_rate == _swrDstRate)
- && sameChannelLayout();
- }
- bool AbstractAudioFFMpegLoader::initResampleForFrame() {
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- const auto bad = !_frame->ch_layout.nb_channels;
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- const auto frameChannelLayout = ComputeChannelLayout(
- _frame->channel_layout,
- _frame->channels);
- const auto bad = !frameChannelLayout;
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- if (bad) {
- LOG(("Audio Error: "
- "Unknown channel layout for frame in file '%1', "
- "data size '%2'"
- ).arg(_file.name()
- ).arg(_data.size()
- ));
- return false;
- } else if (_frame->format == -1) {
- LOG(("Audio Error: "
- "Unknown frame format in file '%1', data size '%2'"
- ).arg(_file.name()
- ).arg(_data.size()
- ));
- return false;
- } else if (_swrContext) {
- const auto sameChannelLayout = [&] {
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- return !av_channel_layout_compare(
- &_frame->ch_layout,
- &_swrSrcChannelLayout);
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- return (frameChannelLayout == _swrSrcChannelLayout);
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- };
- if (true
- && (_frame->format == _swrSrcSampleFormat)
- && (_frame->sample_rate == _swrSrcRate)
- && sameChannelLayout()) {
- return true;
- }
- swr_close(_swrContext);
- }
- _swrSrcSampleFormat = static_cast<AVSampleFormat>(_frame->format);
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- av_channel_layout_copy(&_swrSrcChannelLayout, &_frame->ch_layout);
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- _swrSrcChannelLayout = frameChannelLayout;
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- _swrSrcRate = _frame->sample_rate;
- return initResampleUsingFormat();
- }
- bool AbstractAudioFFMpegLoader::initResampleUsingFormat() {
- AvErrorWrap error = 0;
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- error = swr_alloc_set_opts2(
- &_swrContext,
- &_swrDstChannelLayout,
- _swrDstSampleFormat,
- _swrDstRate,
- &_swrSrcChannelLayout,
- _swrSrcSampleFormat,
- _swrSrcRate,
- 0,
- nullptr);
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- _swrContext = swr_alloc_set_opts(
- _swrContext,
- _swrDstChannelLayout,
- _swrDstSampleFormat,
- _swrDstRate,
- _swrSrcChannelLayout,
- _swrSrcSampleFormat,
- _swrSrcRate,
- 0,
- nullptr);
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- if (error || !_swrContext) {
- LogError(u"swr_alloc_set_opts2"_q, error);
- return false;
- } else if (AvErrorWrap error = swr_init(_swrContext)) {
- LogError(u"swr_init"_q, error);
- return false;
- }
- _resampledFrame = nullptr;
- _resampledFrameCapacity = 0;
- return true;
- }
- bool AbstractAudioFFMpegLoader::ensureResampleSpaceAvailable(int samples) {
- const auto enlarge = (_resampledFrameCapacity < samples);
- if (!_resampledFrame) {
- _resampledFrame = FFmpeg::MakeFramePointer();
- } else if (enlarge || !av_frame_is_writable(_resampledFrame.get())) {
- av_frame_unref(_resampledFrame.get());
- } else {
- return true;
- }
- const auto allocate = std::max(samples, int(av_rescale_rnd(
- FFmpeg::kAVBlockSize / _outputSampleSize,
- _swrDstRate,
- _swrSrcRate,
- AV_ROUND_UP)));
- _resampledFrame->sample_rate = _swrDstRate;
- _resampledFrame->format = _swrDstSampleFormat;
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- av_channel_layout_copy(
- &_resampledFrame->ch_layout,
- &_swrDstChannelLayout);
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- _resampledFrame->channel_layout = _swrDstChannelLayout;
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- _resampledFrame->nb_samples = allocate;
- if (AvErrorWrap error = av_frame_get_buffer(_resampledFrame.get(), 0)) {
- LogError(u"av_frame_get_buffer"_q, error);
- return false;
- }
- _resampledFrameCapacity = allocate;
- return true;
- }
- bool AbstractAudioFFMpegLoader::changeSpeedFilter(float64 speed) {
- speed = std::clamp(speed, kSpeedMin, kSpeedMax);
- if (EqualSpeeds(_filterSpeed, speed)) {
- return false;
- }
- avfilter_graph_free(&_filterGraph);
- const auto guard = gsl::finally([&] {
- if (!_filterGraph) {
- _filteredFrame = nullptr;
- _filterSpeed = 1.;
- }
- });
- createSpeedFilter(speed);
- return true;
- }
- void AbstractAudioFFMpegLoader::createSpeedFilter(float64 speed) {
- Expects(!_filterGraph);
- if (EqualSpeeds(speed, 1.)) {
- return;
- }
- const auto abuffer = avfilter_get_by_name("abuffer");
- const auto abuffersink = avfilter_get_by_name("abuffersink");
- const auto atempo = avfilter_get_by_name("atempo");
- if (!abuffer || !abuffersink || !atempo) {
- LOG(("FFmpeg Error: Could not find abuffer / abuffersink /atempo."));
- return;
- }
- auto graph = avfilter_graph_alloc();
- if (!graph) {
- LOG(("FFmpeg Error: Unable to create filter graph."));
- return;
- }
- const auto guard = gsl::finally([&] {
- avfilter_graph_free(&graph);
- });
- _filterSrc = avfilter_graph_alloc_filter(graph, abuffer, "src");
- _atempo = avfilter_graph_alloc_filter(graph, atempo, "atempo");
- _filterSink = avfilter_graph_alloc_filter(graph, abuffersink, "sink");
- if (!_filterSrc || !atempo || !_filterSink) {
- LOG(("FFmpeg Error: "
- "Could not allocate abuffer / abuffersink /atempo."));
- return;
- }
- char layout[64] = { 0 };
- #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
- av_channel_layout_describe(
- &_swrDstChannelLayout,
- layout,
- sizeof(layout));
- #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- av_get_channel_layout_string(
- layout,
- sizeof(layout),
- 0,
- _swrDstChannelLayout);
- #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
- av_opt_set(
- _filterSrc,
- "channel_layout",
- layout,
- AV_OPT_SEARCH_CHILDREN);
- av_opt_set_sample_fmt(
- _filterSrc,
- "sample_fmt",
- _swrDstSampleFormat,
- AV_OPT_SEARCH_CHILDREN);
- av_opt_set_q(
- _filterSrc,
- "time_base",
- AVRational{ 1, _swrDstRate },
- AV_OPT_SEARCH_CHILDREN);
- av_opt_set_int(
- _filterSrc,
- "sample_rate",
- _swrDstRate,
- AV_OPT_SEARCH_CHILDREN);
- av_opt_set_double(
- _atempo,
- "tempo",
- speed,
- AV_OPT_SEARCH_CHILDREN);
- AvErrorWrap error = 0;
- if ((error = avfilter_init_str(_filterSrc, nullptr))) {
- LogError(u"avfilter_init_str(src)"_q, error);
- return;
- } else if ((error = avfilter_init_str(_atempo, nullptr))) {
- LogError(u"avfilter_init_str(atempo)"_q, error);
- avfilter_graph_free(&graph);
- return;
- } else if ((error = avfilter_init_str(_filterSink, nullptr))) {
- LogError(u"avfilter_init_str(sink)"_q, error);
- avfilter_graph_free(&graph);
- return;
- } else if ((error = avfilter_link(_filterSrc, 0, _atempo, 0))) {
- LogError(u"avfilter_link(src->atempo)"_q, error);
- avfilter_graph_free(&graph);
- return;
- } else if ((error = avfilter_link(_atempo, 0, _filterSink, 0))) {
- LogError(u"avfilter_link(atempo->sink)"_q, error);
- avfilter_graph_free(&graph);
- return;
- } else if ((error = avfilter_graph_config(graph, nullptr))) {
- LogError("avfilter_link(atempo->sink)"_q, error);
- avfilter_graph_free(&graph);
- return;
- }
- _filterGraph = base::take(graph);
- _filteredFrame = FFmpeg::MakeFramePointer();
- _filterSpeed = speed;
- }
- void AbstractAudioFFMpegLoader::enqueueNormalFrame(
- not_null<AVFrame*> frame,
- int64 samples) {
- if (_framesQueuedIndex >= 0) {
- return;
- }
- if (!samples) {
- samples = frame->nb_samples;
- }
- _framesQueued.push_back({
- .position = startedAtSample() + _framesQueuedSamples,
- .samples = samples,
- .frame = FFmpeg::DuplicateFramePointer(frame),
- });
- _framesQueuedSamples += samples;
- }
- void AbstractAudioFFMpegLoader::enqueueFramesFinished() {
- if (_framesQueuedIndex >= 0) {
- return;
- }
- _framesQueued.push_back({
- .position = startedAtSample() + _framesQueuedSamples,
- });
- }
- auto AbstractAudioFFMpegLoader::readFromReadyFrame()
- -> ReadResult {
- const auto raw = _frame.get();
- if (frameHasDesiredFormat(raw)) {
- if (!raw->nb_samples) {
- return ReadError::Retry;
- }
- return readOrBufferForFilter(raw, raw->nb_samples);
- } else if (!initResampleForFrame()) {
- return ReadError::Other;
- }
- const auto maxSamples = av_rescale_rnd(
- swr_get_delay(_swrContext, _swrSrcRate) + _frame->nb_samples,
- _swrDstRate,
- _swrSrcRate,
- AV_ROUND_UP);
- if (!ensureResampleSpaceAvailable(maxSamples)) {
- return ReadError::Other;
- }
- const auto samples = swr_convert(
- _swrContext,
- (uint8_t**)_resampledFrame->extended_data,
- maxSamples,
- (const uint8_t **)_frame->extended_data,
- _frame->nb_samples);
- if (AvErrorWrap error = samples) {
- LogError(u"swr_convert"_q, error);
- return ReadError::Other;
- } else if (!samples) {
- return ReadError::Retry;
- }
- return readOrBufferForFilter(_resampledFrame.get(), samples);
- }
- auto AbstractAudioFFMpegLoader::readOrBufferForFilter(
- not_null<AVFrame*> frame,
- int64 samplesOverride)
- -> ReadResult {
- enqueueNormalFrame(frame, samplesOverride);
- const auto was = frame->nb_samples;
- frame->nb_samples = samplesOverride;
- const auto guard = gsl::finally([&] {
- frame->nb_samples = was;
- });
- if (!_filterGraph) {
- return bytes::const_span(
- reinterpret_cast<const bytes::type*>(frame->extended_data[0]),
- frame->nb_samples * _outputSampleSize);
- }
- AvErrorWrap error = av_buffersrc_add_frame_flags(
- _filterSrc,
- frame,
- AV_BUFFERSRC_FLAG_KEEP_REF);
- if (error) {
- LogError(u"av_buffersrc_add_frame_flags"_q, error);
- return ReadError::Other;
- }
- return ReadError::Retry;
- }
- AbstractAudioFFMpegLoader::~AbstractAudioFFMpegLoader() {
- if (_filterGraph) {
- avfilter_graph_free(&_filterGraph);
- }
- if (_swrContext) {
- swr_free(&_swrContext);
- }
- }
- FFMpegLoader::FFMpegLoader(
- const Core::FileLocation &file,
- const QByteArray &data,
- bytes::vector &&buffer)
- : AbstractAudioFFMpegLoader(file, data, std::move(buffer)) {
- }
- bool FFMpegLoader::open(crl::time positionMs, float64 speed) {
- return AbstractFFMpegLoader::open(positionMs)
- && openCodecContext()
- && initUsingContext(_codecContext, speed)
- && seekTo(positionMs);
- }
- bool FFMpegLoader::openCodecContext() {
- _codecContext = avcodec_alloc_context3(nullptr);
- if (!_codecContext) {
- LOG(("Audio Error: "
- "Unable to avcodec_alloc_context3 for file '%1', data size '%2'"
- ).arg(_file.name()
- ).arg(_data.size()
- ));
- return false;
- }
- const auto stream = fmtContext->streams[streamId];
- AvErrorWrap error = avcodec_parameters_to_context(
- _codecContext,
- stream->codecpar);
- if (error) {
- LogError(u"avcodec_parameters_to_context"_q, error);
- return false;
- }
- _codecContext->pkt_timebase = stream->time_base;
- av_opt_set_int(_codecContext, "refcounted_frames", 1, 0);
- if (AvErrorWrap error = avcodec_open2(_codecContext, codec, 0)) {
- LogError(u"avcodec_open2"_q, error);
- return false;
- }
- return true;
- }
- bool FFMpegLoader::seekTo(crl::time positionMs) {
- if (positionMs) {
- const auto stream = fmtContext->streams[streamId];
- const auto timeBase = stream->time_base;
- const auto timeStamp = (positionMs * timeBase.den)
- / (1000LL * timeBase.num);
- const auto flags1 = AVSEEK_FLAG_ANY;
- if (av_seek_frame(fmtContext, streamId, timeStamp, flags1) < 0) {
- const auto flags2 = 0;
- if (av_seek_frame(fmtContext, streamId, timeStamp, flags2) < 0) {
- }
- }
- }
- return true;
- }
- FFMpegLoader::ReadResult FFMpegLoader::readMore() {
- if (_readTillEnd) {
- return ReadError::EndOfFile;
- }
- const auto readResult = readFromReadyContext(_codecContext);
- if (readResult != ReadError::Wait) {
- if (readResult == ReadError::EndOfFile) {
- _readTillEnd = true;
- }
- return readResult;
- }
- if (AvErrorWrap error = av_read_frame(fmtContext, &_packet)) {
- if (error.code() != AVERROR_EOF) {
- LogError(u"av_read_frame"_q, error);
- return ReadError::Other;
- }
- error = avcodec_send_packet(_codecContext, nullptr); // drain
- if (!error) {
- return ReadError::Retry;
- }
- LogError(u"avcodec_send_packet"_q, error);
- return ReadError::Other;
- }
- if (_packet.stream_index == streamId) {
- AvErrorWrap error = avcodec_send_packet(_codecContext, &_packet);
- if (error) {
- av_packet_unref(&_packet);
- LogError(u"avcodec_send_packet"_q, error);
- // There is a sample voice message where skipping such packet
- // results in a crash (read_access to nullptr) in swr_convert().
- //if (error.code() == AVERROR_INVALIDDATA) {
- // return ReadResult::Retry; // try to skip bad packet
- //}
- return ReadError::Other;
- }
- }
- av_packet_unref(&_packet);
- return ReadError::Retry;
- }
- FFMpegLoader::~FFMpegLoader() {
- if (_codecContext) {
- avcodec_free_context(&_codecContext);
- }
- }
- } // namespace Media
|