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);
35 avio_flush(format->pb);
36 avformat_flush(format);
38 avcodec_flush_buffers(codec);
40 if(flags & OSSIA_LIBAV_SEEK_ROUGH)
43 flags &= ~OSSIA_LIBAV_SEEK_ROUGH;
44 if(av_seek_frame(format, -1, dts, flags) < 0)
50 if(avformat_seek_file(format, -1, INT64_MIN, dts, INT64_MAX, flags) < 0)
54 avio_flush(format->pb);
55 avformat_flush(format);
57 avcodec_flush_buffers(codec);
62static inline int avstream_get_audio_channels(AVStream& stream)
noexcept
64#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
65 return stream.codecpar->ch_layout.nb_channels;
67 return stream.codecpar->channels;
74 = AVFormatContext* (*)(
const char* path,
void*& io_owner,
void (*&io_free)(
void*));
75inline libav_open_hook& libav_custom_open() noexcept
77 static libav_open_hook hook =
nullptr;
83 AVFormatContext* format{};
85 AVCodecContext* codec{};
86 SwrContext* resample{};
88 void (*io_free)(
void*){};
90 libav_handle() =
default;
91 libav_handle(
const libav_handle& other) =
delete;
92 libav_handle(libav_handle&& other)
94 format = other.format;
95 other.format =
nullptr;
96 stream = other.stream;
97 other.stream =
nullptr;
99 other.codec =
nullptr;
100 resample = other.resample;
101 other.resample =
nullptr;
102 io_owner = other.io_owner;
103 other.io_owner =
nullptr;
104 io_free = other.io_free;
105 other.io_free =
nullptr;
107 libav_handle& operator=(
const libav_handle& other) =
delete;
108 libav_handle& operator=(libav_handle&& other)
110 format = other.format;
111 other.format =
nullptr;
112 stream = other.stream;
113 other.stream =
nullptr;
115 other.codec =
nullptr;
116 resample = other.resample;
117 other.resample =
nullptr;
118 io_owner = other.io_owner;
119 other.io_owner =
nullptr;
120 io_free = other.io_free;
121 other.io_free =
nullptr;
137 avcodec_free_context(&codec);
141 avformat_close_input(&format);
142 avformat_free_context(format);
153 void open(
const std::string& path,
int stream_index,
int target_rate)
noexcept
155 if(
auto hook = libav_custom_open())
156 format = hook(path.c_str(), io_owner, io_free);
159 && avformat_open_input(&format, path.c_str(),
nullptr,
nullptr) != 0)
164 if(avformat_find_stream_info(format,
nullptr) < 0)
169 if(stream_index < 0 || stream_index >=
int(format->nb_streams))
174 if(format->streams[stream_index]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
180 stream = format->streams[stream_index];
182 auto cdc = avcodec_find_decoder(stream->codecpar->codec_id);
189 codec = avcodec_alloc_context3(cdc);
190 if(avcodec_parameters_to_context(codec, stream->codecpar) < 0)
196 if(avcodec_open2(codec, cdc,
nullptr) < 0)
202 AVSampleFormat out_sample_fmt{AV_SAMPLE_FMT_FLT};
203 AVSampleFormat in_sample_fmt{(AVSampleFormat)stream->codecpar->format};
204 int in_sample_rate = stream->codecpar->sample_rate;
205 int out_sample_rate = target_rate == 0 ? in_sample_rate : target_rate;
207#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
208 AVChannelLayout out_layout{stream->codecpar->ch_layout};
209 AVChannelLayout in_layout{stream->codecpar->ch_layout};
212 &resample, &out_layout, out_sample_fmt, out_sample_rate, &in_layout,
213 in_sample_fmt, in_sample_rate, 0,
nullptr);
215 int64_t out_layout = stream->codecpar->channel_layout;
216 int64_t in_layout = stream->codecpar->channel_layout;
218 resample = swr_alloc_set_opts(
219 resample, out_layout, out_sample_fmt, out_sample_rate, in_layout, in_sample_fmt,
220 in_sample_rate, 0,
nullptr);
226 int rate() const noexcept {
return stream->codecpar->sample_rate; }
227 int channels() const noexcept {
return avstream_get_audio_channels(*stream); }
229 int64_t totalPCMFrameCount() const noexcept
231 if(stream->duration > 0)
233 return stream->duration;
235 else if(format->duration > 0)
237 double seconds = format->duration / double(AV_TIME_BASE);
238 double frames = seconds * stream->codecpar->sample_rate;
241 else if(stream->nb_frames > 0)
244 stream->nb_frames, stream->r_frame_rate,
245 AVRational{1, stream->codecpar->sample_rate});
253 operator bool() const noexcept {
return bool(format); }
255 void fetch(int64_t frame,
int samples_to_write,
auto func)
258 ossia::seek_to_flick(
259 format, codec, stream,
260 ossia::flicks_per_second<double> * frame / stream->codecpar->sample_rate,
263 const std::size_t channels = this->channels();
264 std::vector<float> tmp;
269 while(processed < samples_to_write)
273 auto packet = av_packet_alloc();
276 ret = av_read_frame(format, packet);
278 while(ret >= 0 && ret != AVERROR(EOF) && packet->stream_index != stream->index)
280 av_packet_unref(packet);
281 ret = av_read_frame(format, packet);
283 if(ret == AVERROR(EOF))
293 ret = avcodec_send_packet(codec, packet);
296 auto avframe = av_frame_alloc();
297 ret = avcodec_receive_frame(codec, avframe);
300 const int av_frame_start = avframe->best_effort_timestamp;
301 const int samples = avframe->nb_samples;
305 const int offset = (frame < av_frame_start) ? 0 : (frame - av_frame_start);
306 if(offset >= samples)
309 av_frame_free(&avframe);
310 av_packet_unref(packet);
311 av_packet_free(&packet);
318 tmp.resize(samples * channels);
319 float* out_ptr = tmp.data();
322 int read_samples = swr_convert(
323 resample, (uint8_t**)&out_ptr, samples,
324 (
const uint8_t**)avframe->extended_data, samples);
326 auto end = tmp.data() + tmp.size();
328 read_samples -= offset;
329 if(read_samples <= 0)
331 av_frame_free(&avframe);
332 av_packet_unref(packet);
333 av_packet_free(&packet);
337 out_ptr += offset * channels;
339 for(
int i = 0; i < read_samples; i++)
344 if(processed == samples_to_write)
346 av_frame_free(&avframe);
347 av_packet_unref(packet);
348 av_packet_free(&packet);
357 frame = avframe->best_effort_timestamp + avframe->nb_samples;
359 av_frame_free(&avframe);
361 av_packet_unref(packet);
362 av_packet_free(&packet);