2#include <ossia/detail/config.hpp>
4#include <ossia/audio/audio_parameter.hpp>
5#include <ossia/dataflow/audio_stretch_mode.hpp>
6#include <ossia/dataflow/graph_node.hpp>
7#include <ossia/dataflow/nodes/media.hpp>
8#include <ossia/dataflow/nodes/sound.hpp>
9#include <ossia/dataflow/port.hpp>
10#include <ossia/detail/libav.hpp>
11#include <ossia/detail/pod_vector.hpp>
17#include <libavcodec/avcodec.h>
18#include <libavformat/avformat.h>
19#include <libavutil/frame.h>
20#include <libavutil/mem.h>
21#include <libswresample/swresample.h>
26class sound_libav final :
public ossia::sound_node
33 : packet{av_packet_alloc()}
34 , frame{av_frame_alloc()}
36 m_outlets.push_back(&audio_out);
43 av_frame_free(&frame);
44 av_packet_free(&packet);
47 std::string label() const noexcept
override {
return "sound_libav"; }
49 void set_start(std::size_t v) { start = v; }
51 void set_upmix(std::size_t v) { upmix = v; }
53 void set_sound(libav_handle hdl)
57 m_handle = std::move(hdl);
60 m_channel_q = boost::circular_buffer<float>(8192 * m_handle.channels());
63 void transport(time_value flicks)
override
67 m_handle.format, m_handle.codec, m_handle.stream, flicks.impl, AVSEEK_FLAG_ANY);
70 void transport(time_value flicks,
const ossia::tick_transport_info& tinfo)
override
75 int64_t target_flicks = flicks.impl;
76 const double abs_tempo = std::abs(tinfo.current_tempo);
77 if(m_resampler.stretch() && tempo > 0.0 && abs_tempo > 0.0)
79 target_flicks = int64_t(
double(flicks.impl) * abs_tempo / tempo);
82 m_handle.format, m_handle.codec, m_handle.stream, target_flicks,
86 void fetch_from_libav(
int samples_to_write)
88 const std::size_t channels = this->channels();
92 auto floats_to_write = channels * samples_to_write;
93 while(m_channel_q.size() < floats_to_write)
96 if(m_channel_q.capacity() < 4 * floats_to_write)
98 m_channel_q.set_capacity(4 * floats_to_write);
102 auto fmt_ctx = m_handle.format;
103 auto codec_ctx = m_handle.codec;
104 auto stream = m_handle.stream;
108 av_packet_unref(packet);
109 ret = av_read_frame(fmt_ctx, packet);
111 while(ret >= 0 && ret != AVERROR(EOF) && packet->stream_index != stream->index)
113 av_packet_unref(packet);
114 ret = av_read_frame(fmt_ctx, packet);
116 if(ret == AVERROR(EOF))
126 ret = avcodec_send_packet(codec_ctx, packet);
129 ret = avcodec_receive_frame(codec_ctx, frame);
132 const int samples = frame->nb_samples;
133 m_tmp.resize(samples * channels, boost::container::default_init);
134 float* out_ptr = m_tmp.data();
135 const int read_samples = swr_convert(
136 m_handle.resample, (uint8_t**)&out_ptr, samples,
137 (
const uint8_t**)frame->extended_data, samples);
140 m_channel_q.end(), out_ptr, out_ptr + read_samples * channels);
147 template <
typename T>
149 fetch_audio(int64_t start, int64_t samples_to_write, T** audio_array_base)
noexcept
151 const std::size_t channels = this->channels();
155 fetch_from_libav(samples_to_write);
158 for(
int k = 0; k < samples_to_write; k++)
160 for(std::size_t chan = 0; chan < channels; chan++)
162 if(m_channel_q.size() > 0)
164 audio_array_base[chan][k] = m_channel_q.front();
165 m_channel_q.pop_front();
169 audio_array_base[chan][k] = 0.;
175 template <
typename T>
176 void fetch_audio_backward(
177 int64_t start, int64_t samples_to_write, T** audio_array_base)
noexcept
179 const std::size_t channels = this->channels();
190 int64_t backward_start = start - samples_to_write + 1;
191 if(backward_start < 0)
196 const int64_t sample_rate = m_handle.stream->codecpar->sample_rate;
200 constexpr int64_t flicks_per_second = 705600000LL;
201 int64_t flicks_pos = backward_start * flicks_per_second / sample_rate;
204 ossia::seek_to_flick(
205 m_handle.format, m_handle.codec, m_handle.stream, flicks_pos, AVSEEK_FLAG_BACKWARD);
209 fetch_from_libav(samples_to_write);
212 for(int64_t k = 0; k < samples_to_write; k++)
214 for(std::size_t chan = 0; chan < channels; chan++)
216 if(m_channel_q.size() > 0)
218 audio_array_base[chan][k] = m_channel_q.front();
219 m_channel_q.pop_front();
223 audio_array_base[chan][k] = 0.;
229 for(std::size_t chan = 0; chan < channels; chan++)
231 std::reverse(audio_array_base[chan], audio_array_base[chan] + samples_to_write);
235 void run(
const ossia::token_request& t, ossia::exec_state_facade e)
noexcept override
240 const auto channels = m_handle.channels();
241 const auto len = m_handle.totalPCMFrameCount();
243 ossia::audio_port& ap = *audio_out;
244 ap.set_channels(std::max((std::size_t)upmix, (std::size_t)channels));
246 const auto [samples_to_read, samples_to_write]
247 = snd::sample_info(e.bufferSize(), e.modelToSamples(), t);
248 if(samples_to_write <= 0)
251 assert(samples_to_write > 0);
253 const auto samples_offset = t.physical_start(e.modelToSamples());
257 if(t.prev_date < m_prev_date)
262 m_prev_date = t.prev_date;
264 transport(t.prev_date);
269 if(t.prev_date > m_prev_date)
272 m_prev_date = t.prev_date;
274 transport(t.prev_date);
278 for(
int chan = 0; chan < channels; chan++)
280 ap.channel(chan).resize(e.bufferSize());
283 const double stretch_ratio = update_stretch(t, e);
284 const double abs_stretch_ratio = std::abs(stretch_ratio);
287 *
this, t, e, stretch_ratio, channels, len, samples_to_read, samples_to_write,
290 const bool start_discontinuous = t.start_discontinuous || (m_last_stretch > 70.);
291 const bool end_discontinuous = t.end_discontinuous || (abs_stretch_ratio > 70.);
292 if(abs_stretch_ratio > 70. && m_last_stretch > 70.)
295 for(std::size_t i = 0; i < channels; i++)
297 ossia::snd::do_zero(ap.channel(i), samples_offset, samples_to_write);
303 for(
int chan = 0; chan < channels; chan++)
307 start_discontinuous, end_discontinuous, ap.channel(chan), samples_offset,
312 ossia::snd::perform_upmix(this->upmix, channels, ap);
313 ossia::snd::perform_start_offset(this->start, ap);
315 m_prev_date = t.date;
316 m_last_stretch = abs_stretch_ratio;
319 [[nodiscard]] std::size_t channels()
const
321 return m_handle ? m_handle.channels() : 0;
323 [[nodiscard]] std::size_t duration()
const
325 return m_handle ? m_handle.totalPCMFrameCount() : 0;
329 libav_handle m_handle{};
331 ossia::audio_outlet audio_out;
336 ossia::pod_vector<float> m_tmp{};
337 boost::circular_buffer<float> m_channel_q;
The time_value class.
Definition ossia/editor/scenario/time_value.hpp:30