2#include <ossia/detail/config.hpp>
4#if __has_include(<libavcodec/avcodec.h>) && \
5 __has_include(<libavformat/avformat.h>) && \
6 __has_include(<libavdevice/avdevice.h>) && \
7 __has_include(<libavutil/frame.h>) && \
8 __has_include(<libswresample/swresample.h>) && \
9 __has_include(<libswscale/swscale.h>)
11#define OSSIA_HAS_LIBAV 1
12#include <ossia/detail/flicks.hpp>
15#include <libavcodec/avcodec.h>
16#include <libavformat/avformat.h>
17#include <libavutil/channel_layout.h>
18#include <libavutil/version.h>
19#include <libswresample/swresample.h>
25static constexpr const int OSSIA_LIBAV_SEEK_ROUGH = 0b00000100000000000000000000000000;
26inline bool seek_to_flick(
27 AVFormatContext* format, AVCodecContext* codec, AVStream* stream, int64_t flicks,
30 constexpr auto flicks_tb = AVRational{1, ossia::flicks_per_second<int64_t>};
31 constexpr auto av_tb = AVRational{1, AV_TIME_BASE};
33 const auto dts = av_rescale_q_rnd(flicks, flicks_tb, av_tb, AVRounding::AV_ROUND_DOWN);
38 avio_flush(format->pb);
39 avformat_flush(format);
41 avcodec_flush_buffers(codec);
43 if(flags & OSSIA_LIBAV_SEEK_ROUGH)
46 flags &= ~OSSIA_LIBAV_SEEK_ROUGH;
47 if(av_seek_frame(format, -1, dts, flags) < 0)
53 if(avformat_seek_file(format, -1, INT64_MIN, dts, INT64_MAX, flags) < 0)
60 avcodec_flush_buffers(codec);
65static inline int avstream_get_audio_channels(AVStream& stream)
noexcept
67#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
68 return stream.codecpar->ch_layout.nb_channels;
70 return stream.codecpar->channels;
77 = AVFormatContext* (*)(
const char* path,
void*& io_owner,
void (*&io_free)(
void*));
78inline libav_open_hook& libav_custom_open() noexcept
80 static libav_open_hook hook =
nullptr;
86 AVFormatContext* format{};
88 AVCodecContext* codec{};
89 SwrContext* resample{};
93 void (*io_free)(
void*){};
95 libav_handle() =
default;
96 libav_handle(
const libav_handle& other) =
delete;
97 libav_handle(libav_handle&& other)
99 format = other.format;
100 other.format =
nullptr;
101 stream = other.stream;
102 other.stream =
nullptr;
104 other.codec =
nullptr;
105 resample = other.resample;
106 other.resample =
nullptr;
107 output_rate = other.output_rate;
108 other.output_rate = 0;
109 io_owner = other.io_owner;
110 other.io_owner =
nullptr;
111 io_free = other.io_free;
112 other.io_free =
nullptr;
114 libav_handle& operator=(
const libav_handle& other) =
delete;
115 libav_handle& operator=(libav_handle&& other)
117 format = other.format;
118 other.format =
nullptr;
119 stream = other.stream;
120 other.stream =
nullptr;
122 other.codec =
nullptr;
123 resample = other.resample;
124 other.resample =
nullptr;
125 output_rate = other.output_rate;
126 other.output_rate = 0;
127 io_owner = other.io_owner;
128 other.io_owner =
nullptr;
129 io_free = other.io_free;
130 other.io_free =
nullptr;
147 avcodec_free_context(&codec);
151 avformat_close_input(&format);
152 avformat_free_context(format);
163 void open(
const std::string& path,
int stream_index,
int target_rate)
noexcept
165 if(
auto hook = libav_custom_open())
166 format = hook(path.c_str(), io_owner, io_free);
169 && avformat_open_input(&format, path.c_str(),
nullptr,
nullptr) != 0)
174 if(avformat_find_stream_info(format,
nullptr) < 0)
179 if(stream_index < 0 || stream_index >=
int(format->nb_streams))
184 if(format->streams[stream_index]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
190 stream = format->streams[stream_index];
192 auto cdc = avcodec_find_decoder(stream->codecpar->codec_id);
199 codec = avcodec_alloc_context3(cdc);
200 if(avcodec_parameters_to_context(codec, stream->codecpar) < 0)
206 if(avcodec_open2(codec, cdc,
nullptr) < 0)
212 AVSampleFormat out_sample_fmt{AV_SAMPLE_FMT_FLT};
213 AVSampleFormat in_sample_fmt{(AVSampleFormat)stream->codecpar->format};
214 int in_sample_rate = stream->codecpar->sample_rate;
215 int out_sample_rate = target_rate == 0 ? in_sample_rate : target_rate;
217#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
218 AVChannelLayout out_layout{stream->codecpar->ch_layout};
219 AVChannelLayout in_layout{stream->codecpar->ch_layout};
222 &resample, &out_layout, out_sample_fmt, out_sample_rate, &in_layout,
223 in_sample_fmt, in_sample_rate, 0,
nullptr);
225 int64_t out_layout = stream->codecpar->channel_layout;
226 int64_t in_layout = stream->codecpar->channel_layout;
228 resample = swr_alloc_set_opts(
229 resample, out_layout, out_sample_fmt, out_sample_rate, in_layout, in_sample_fmt,
230 in_sample_rate, 0,
nullptr);
234 if(!resample || swr_init(resample) < 0)
239 output_rate = out_sample_rate;
242 int rate() const noexcept {
return stream->codecpar->sample_rate; }
245 int out_rate() const noexcept {
return output_rate > 0 ? output_rate : rate(); }
248 int out_capacity_for(
int in_samples)
const noexcept
250 const int in_rate = rate();
251 if(!resample || in_rate <= 0)
253 const int64_t delay = swr_get_delay(resample, in_rate);
254 return int(av_rescale_rnd(delay + in_samples, out_rate(), in_rate, AV_ROUND_UP));
258 void flush_resampler() noexcept
263 int channels() const noexcept {
return avstream_get_audio_channels(*stream); }
265 int64_t totalPCMFrameCount() const noexcept
267 if(stream->duration > 0)
269 return stream->duration;
271 else if(format->duration > 0)
273 double seconds = format->duration / double(AV_TIME_BASE);
274 double frames = seconds * stream->codecpar->sample_rate;
277 else if(stream->nb_frames > 0)
280 stream->nb_frames, stream->r_frame_rate,
281 AVRational{1, stream->codecpar->sample_rate});
289 operator bool() const noexcept {
return bool(format); }
291 void fetch(int64_t frame,
int samples_to_write,
auto func)
294 ossia::seek_to_flick(
295 format, codec, stream,
296 ossia::flicks_per_second<double> * frame / stream->codecpar->sample_rate,
297 AVSEEK_FLAG_BACKWARD);
299 const std::size_t channels = this->channels();
300 std::vector<float> tmp;
305 while(processed < samples_to_write)
309 auto packet = av_packet_alloc();
312 ret = av_read_frame(format, packet);
314 while(ret >= 0 && ret != AVERROR(EOF) && packet->stream_index != stream->index)
316 av_packet_unref(packet);
317 ret = av_read_frame(format, packet);
319 if(ret == AVERROR(EOF))
329 ret = avcodec_send_packet(codec, packet);
332 auto avframe = av_frame_alloc();
333 ret = avcodec_receive_frame(codec, avframe);
339 const int64_t av_frame_start = avframe->best_effort_timestamp;
340 const int samples = avframe->nb_samples;
344 const int64_t offset = (frame < av_frame_start) ? 0 : (frame - av_frame_start);
345 if(offset >= samples)
348 av_frame_free(&avframe);
349 av_packet_unref(packet);
350 av_packet_free(&packet);
357 tmp.resize(samples * channels);
358 float* out_ptr = tmp.data();
361 int read_samples = swr_convert(
362 resample, (uint8_t**)&out_ptr, samples,
363 (
const uint8_t**)avframe->extended_data, samples);
365 auto end = tmp.data() + tmp.size();
367 read_samples -= offset;
368 if(read_samples <= 0)
370 av_frame_free(&avframe);
371 av_packet_unref(packet);
372 av_packet_free(&packet);
376 out_ptr += offset * channels;
378 for(
int i = 0; i < read_samples; i++)
383 if(processed == samples_to_write)
385 av_frame_free(&avframe);
386 av_packet_unref(packet);
387 av_packet_free(&packet);
396 frame = avframe->best_effort_timestamp + avframe->nb_samples;
398 av_frame_free(&avframe);
400 av_packet_unref(packet);
401 av_packet_free(&packet);