MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VideoStreamContext.hpp
Go to the documentation of this file.
1#pragma once
2
4
5extern "C" {
6struct AVCodecContext;
7struct SwsContext;
8struct AVFrame;
9}
10
11namespace MayaFlux::IO {
12
13/**
14 * @class VideoStreamContext
15 * @brief RAII owner of one video stream's codec and pixel-format scaler state.
16 *
17 * Encapsulates all video-stream-specific FFmpeg objects:
18 * - AVCodecContext for the selected video stream
19 * - SwsContext for pixel-format conversion and optional rescaling
20 * - Cached video parameters: width, height, frame_rate, total_frames, pixel_format
21 *
22 * Does NOT own AVFormatContext — that belongs to FFmpegDemuxContext.
23 * Packet reading is always delegated to the demuxer's format_context;
24 * this context only decodes and converts packets it receives.
25 *
26 * The default output pixel format is AV_PIX_FMT_RGBA (4 bytes per pixel),
27 * chosen for direct compatibility with Vulkan's VK_FORMAT_R8G8B8A8_UNORM
28 * and the MayaFlux TextureBuffer / VKImage pipeline. For HDR workflows
29 * or compute-shader ingestion, callers can request AV_PIX_FMT_RGBAF32
30 * or other formats via the target_format parameter.
31 *
32 * Destruction order (enforced in destructor):
33 * sws_context → codec_context
34 * The associated FFmpegDemuxContext must outlive this object.
35 */
36class MAYAFLUX_API VideoStreamContext {
37public:
38 VideoStreamContext() = default;
40
45
46 // =========================================================================
47 // Lifecycle
48 // =========================================================================
49
50 /**
51 * @brief Open the video stream from an already-probed demux context.
52 *
53 * Finds the best video stream, allocates and opens the codec context,
54 * caches video parameters, and initialises the SwsContext for conversion
55 * to the target pixel format (default AV_PIX_FMT_RGBA).
56 *
57 * @param demux Open demux context (must outlive this object).
58 * @param target_width Output width in pixels; 0 = keep source width.
59 * @param target_height Output height in pixels; 0 = keep source height.
60 * @param target_format Target AVPixelFormat; negative = AV_PIX_FMT_RGBA.
61 * @return True on success.
62 */
63 bool open(const FFmpegDemuxContext& demux,
64 uint32_t target_width = 0,
65 uint32_t target_height = 0,
66 int target_format = -1);
67
68 /**
69 * @brief Open codec only, without initialising the SwsContext scaler.
70 *
71 * Intended for live capture devices (dshow, v4l2, avfoundation) where
72 * pix_fmt is AV_PIX_FMT_NONE until the first decoded frame arrives and
73 * sws_getContext therefore cannot be called at open time.
74 *
75 * After this call is_codec_valid() returns true; is_valid() returns false
76 * until rebuild_scaler_from_frame() has been called successfully.
77 *
78 * @param demux Open demux context (must outlive this object).
79 * @param target_width Desired output width (0 = use frame width).
80 * @param target_height Desired output height (0 = use frame height).
81 * @param target_format Desired AVPixelFormat (negative = AV_PIX_FMT_RGBA).
82 * @return True if codec was opened successfully.
83 */
84 [[nodiscard]] bool open_device(const FFmpegDemuxContext& demux,
85 uint32_t target_width = 0,
86 uint32_t target_height = 0,
87 int target_format = -1);
88
89 /**
90 * @brief True if the codec context is open and ready to receive packets.
91 * Does NOT require the SwsContext scaler to be initialised.
92 * Use this check in live-capture paths instead of is_valid().
93 */
94 [[nodiscard]] bool is_codec_valid() const
95 {
96 return codec_context && stream_index >= 0;
97 }
98
99 /**
100 * @brief Release codec and scaler resources.
101 * Safe to call multiple times.
102 */
103 void close();
104
105 /**
106 * @brief True if the codec and scaler are ready for decoding.
107 */
108 [[nodiscard]] bool is_valid() const
109 {
110 return codec_context && sws_context && stream_index >= 0;
111 }
112
113 /**
114 * @brief Rebuild the SwsContext using the pixel format resolved from a live
115 * decoded frame.
116 *
117 * dshow and similar capture devices on Windows leave pix_fmt as
118 * AV_PIX_FMT_NONE until the first decoded frame arrives. Call this once
119 * from the camera's frame-receive loop on the very first valid AVFrame to
120 * finalise the scaler before calling sws_scale.
121 *
122 * @param frame The first successfully decoded AVFrame.
123 * @param target_width Desired output width (0 = keep frame width).
124 * @param target_height Desired output height (0 = keep frame height).
125 * @param target_format Desired AVPixelFormat (negative = AV_PIX_FMT_RGBA).
126 * @return True if the scaler was (re)initialised successfully.
127 */
128 [[nodiscard]] bool rebuild_scaler_from_frame(
129 const AVFrame* frame,
130 uint32_t target_width = 0,
131 uint32_t target_height = 0,
132 int target_format = -1);
133
134 // =========================================================================
135 // Codec flush
136 // =========================================================================
137
138 /**
139 * @brief Flush codec internal buffers (call after a seek).
140 */
141 void flush_codec();
142
143 // =========================================================================
144 // Stream-level metadata extraction
145 // =========================================================================
146
147 /**
148 * @brief Populate stream-specific fields into an existing FileMetadata.
149 *
150 * Appends codec name, pixel format, dimensions, frame rate, bit_rate, etc.
151 *
152 * @param demux The demux context that owns the format_context.
153 * @param out Metadata struct to append into.
154 */
155 void extract_stream_metadata(const FFmpegDemuxContext& demux, FileMetadata& out) const;
156
157 /**
158 * @brief Extract keyframe positions as FileRegion entries.
159 * @param demux The demux context.
160 * @return Vector of FileRegion with type="keyframe".
161 */
162 [[nodiscard]] std::vector<FileRegion> extract_keyframe_regions(const FFmpegDemuxContext& demux) const;
163
164 // =========================================================================
165 // Error
166 // =========================================================================
167
168 [[nodiscard]] const std::string& last_error() const { return m_last_error; }
169
170 // =========================================================================
171 // Owned handles — accessible to VideoFileReader for decode loops
172 // =========================================================================
173
174 AVCodecContext* codec_context = nullptr; ///< Owned; freed in destructor.
175 SwsContext* sws_context = nullptr; ///< Owned; freed in destructor.
176
177 int stream_index = -1;
178 uint64_t total_frames {};
179 uint32_t width {}; ///< Source width in pixels.
180 uint32_t height {}; ///< Source height in pixels.
181 uint32_t out_width {}; ///< Output width after scaling.
182 uint32_t out_height {}; ///< Output height after scaling.
183 double frame_rate {}; ///< Average frame rate (fps).
184 int src_pixel_format = -1; ///< Source AVPixelFormat.
185 int out_pixel_format = -1; ///< Output AVPixelFormat.
186 uint32_t out_bytes_per_pixel = 4; ///< Bytes per pixel in output format.
187 int out_linesize {}; ///< Output row stride in bytes.
188
189 uint32_t target_width {}; ///< Requested output width (0 = source).
190 uint32_t target_height {}; ///< Requested output height (0 = source).
191 int target_format = -1; ///< Requested AVPixelFormat (negative = RGBA).
192
193private:
194 std::string m_last_error;
195
196 /**
197 * @brief Allocate and initialise the SwsContext for pixel format conversion.
198 * @param target_width Desired output width (0 = source width).
199 * @param target_height Desired output height (0 = source height).
200 * @param target_format Desired AVPixelFormat (negative = AV_PIX_FMT_RGBA).
201 * @return True on success.
202 */
203 bool setup_scaler(uint32_t target_width, uint32_t target_height, int target_format);
204};
205
206} // namespace MayaFlux::IO
RAII owner of a single AVFormatContext and associated demux state.
VideoStreamContext(const VideoStreamContext &)=delete
const std::string & last_error() const
VideoStreamContext(VideoStreamContext &&)=delete
VideoStreamContext & operator=(VideoStreamContext &&)=delete
VideoStreamContext & operator=(const VideoStreamContext &)=delete
bool is_codec_valid() const
True if the codec context is open and ready to receive packets.
bool is_valid() const
True if the codec and scaler are ready for decoding.
RAII owner of one video stream's codec and pixel-format scaler state.
Generic metadata structure for any file type.