Loading...
Searching...
No Matches
LibavStreamInput.hpp
1#pragma once
2#include <Media/Libav.hpp>
3#if SCORE_HAS_LIBAV
4
5#include <Video/ExternalInput.hpp>
6#include <Video/FrameQueue.hpp>
7#include <Video/LibavInterrupt.hpp>
8#include <Video/Rescale.hpp>
9
10extern "C" {
11#include <libavformat/avformat.h>
12#include <libswresample/swresample.h>
13}
14
15#include <ossia/detail/pod_vector.hpp>
16
17#include <score_plugin_media_export.h>
18
19#include <atomic>
20#include <chrono>
21#include <map>
22#include <string>
23#include <thread>
24#include <vector>
25
26namespace Video
27{
28
29// Lock-free ring buffer for audio, shared between decoder thread and audio engine.
30// Same design as Gfx::GStreamer::gstreamer_pipeline::AudioBuffer.
31// FIXME use a proper one, not this shit.
32struct SCORE_PLUGIN_MEDIA_EXPORT AudioRingBuffer
33{
34 int sample_rate{48000};
35 int num_channels{2};
36
37 static constexpr std::size_t ring_size = 65536;
38 std::vector<std::vector<float>> ring; // [channel][ring_size]
39 std::atomic<std::size_t> write_pos{0};
40 std::atomic<std::size_t> read_pos{0};
41
42 // Set by the audio parameter to point at its backing storage
43 std::vector<ossia::float_vector>* output_data{};
44
45 void init(int nchannels);
46 void write_planar(float** data, int num_samples, int channels);
47 void write_interleaved_float(const float* data, int num_samples, int channels);
48 void write_interleaved_s16(const int16_t* data, int num_samples, int channels);
49 void read_into_output(int block_size);
50};
51
52// Demuxes any URL/file/device via avformat, decoding both video and audio.
53// Video frames go into a FrameQueue (standard ExternalInput pattern).
54// Audio samples go into an AudioRingBuffer for the audio engine.
55class SCORE_PLUGIN_MEDIA_EXPORT LibavStreamInput final
56 : public ExternalInput
57 , public LibAVDecoder
58{
59public:
60 LibavStreamInput() noexcept;
61 ~LibavStreamInput() noexcept;
62
63 bool load(const std::string& url) noexcept;
64 bool load(
65 const std::string& url,
66 const std::map<std::string, std::string>& options) noexcept;
67
68 // Open the format context and discover streams (populates width/height/etc).
69 // Keeps the context alive for later start().
70 bool probe() noexcept;
71
72 bool start() noexcept override;
73 void stop() noexcept override;
74
75 AVFrame* dequeue_frame() noexcept override;
76 void release_frame(AVFrame* frame) noexcept override;
77
78 bool has_audio() const noexcept { return m_audioStream != nullptr; }
79 AudioRingBuffer& audio_buffer() noexcept { return m_audioBuf; }
80 const std::string& url() const noexcept { return m_url; }
81
82private:
83 void buffer_thread() noexcept;
84 void close_file() noexcept;
85 bool open_streams() noexcept;
86 void close_streams() noexcept;
87 void decode_audio_packet(AVPacket& packet) noexcept;
88
89 std::string m_url;
90 std::map<std::string, std::string> m_options;
91
92 // Audio decoding state
93 AVStream* m_audioStream{};
94 const AVCodec* m_audioCodec{};
95 AVCodecContext* m_audioCodecContext{};
96 AVFrame* m_audioFrame{};
97 AudioRingBuffer m_audioBuf;
98
100 static const constexpr auto probe_timeout = std::chrono::milliseconds(10000);
101
102 LibavInterrupt m_interrupt;
103
104 std::thread m_thread;
105 std::atomic_bool m_running{};
106
107 // Looping: -1 = infinite, 0 = no loop, >0 = N remaining loops.
108 // Extracted from the "loop" key in options (not passed to FFmpeg).
109 int m_loop{0};
110
111 // True for file-based sources that need producer-side frame pacing.
112 // False for real-time sources (network, devices, lavfi) where I/O naturally paces.
113 bool m_needsPacing{false};
114};
115
116}
117#endif
Definition ExternalInput.hpp:11
Deadline for the blocking libav I/O of one AVFormatContext.
Definition LibavInterrupt.hpp:30
Definition LibavStreamInput.hpp:58
Definition LibavStreamInput.hpp:33
Definition Rescale.hpp:49