TinyGPU
Loading...
Searching...
No Matches
AVIWriter.h
Go to the documentation of this file.
1#pragma once
2
3#include <stddef.h>
4#include <stdint.h>
5
6#include "ISurface.h"
7#include "Print.h"
8
9namespace tinygpu {
10
11/**
12 * @brief Writes a simple AVI movie containing RGB565 frames.
13 *
14 * The implementation writes frames directly to the output stream as they are
15 * added. When the expected frame count is provided in begin(), the AVI headers
16 * and optional idx1 index can be precomputed without buffering the frame data.
17 * Frames are encoded as uncompressed 16-bit BI_BITFIELDS video using RGB565
18 * channel masks.
19 *
20 */
21template <typename RGB_T = RGB565>
22class AVIWriter {
23 public:
24 /// Creates an AVI writer that streams data to the provided Print output.
25 AVIWriter(Print& out) : out_(out) {}
26 /// Finalizes the AVI stream if it is still open.
28
29 /// Starts a new AVI stream using the surface dimensions and optional frame
30 /// count.
31 bool begin(const ISurface<RGB_T>& surface, uint16_t fps = 30,
32 uint32_t frameCount = 0) {
33 end();
34
35 width_ = static_cast<uint32_t>(surface.width());
36 height_ = static_cast<uint32_t>(surface.height());
37 if (width_ == 0 || height_ == 0) {
39 return false;
40 }
41
42 fps_ = fps == 0 ? 1 : fps;
45 expectedFrameCount_ = frameCount;
46 frameCount_ = 0;
47 isOpen_ = true;
48
50 return add(surface);
51 }
52
53 /// Finalizes the AVI stream and writes the optional frame index.
54 void end() {
55 if (!isOpen_) {
56 return;
57 }
58
59 if (expectedFrameCount_ != 0) {
61 }
63 }
64
65 /// Appends one RGB565 frame to the AVI stream.
66 bool add(const ISurface<RGB_T>& surface) {
67 if (!isOpen_ || surface.width() != width_ || surface.height() != height_) {
68 return false;
69 }
70
72 return false;
73 }
74
75 writeFourCC("00db");
77 writeFrame(surface);
78 if ((frameSize_ & 0x1U) != 0U) {
79 writeU8(0);
80 }
81
83 return true;
84 }
85
86 protected:
88 uint32_t width_ = 0;
89 uint32_t height_ = 0;
90 uint16_t fps_ = 30;
91 uint32_t rowStride_ = 0;
92 uint32_t frameSize_ = 0;
93 uint32_t expectedFrameCount_ = 0;
94 uint32_t frameCount_ = 0;
95 bool isOpen_ = false;
96
97 /// Returns the padded byte stride of one RGB565 frame row.
98 static uint32_t frameRowStride(uint32_t width) {
99 return ((width * 2U) + 3U) & ~0x3U;
100 }
101
102 /// Returns the data size rounded up to an even AVI chunk length.
103 static uint32_t paddedChunkSize(uint32_t dataSize) {
104 return dataSize + (dataSize & 0x1U);
105 }
106
107 /// Resets the writer state after finishing or aborting a stream.
108 void resetState() {
109 width_ = 0;
110 height_ = 0;
111 fps_ = 30;
112 rowStride_ = 0;
113 frameSize_ = 0;
115 frameCount_ = 0;
116 isOpen_ = false;
117 }
118
119 /// Writes the RIFF, AVI, stream, and movi headers.
120 void writeHeader() {
121 const uint32_t avihSize = 56;
122 const uint32_t strhSize = 56;
123 const uint32_t strfSize = 52;
124 const uint32_t avihChunkSize = 8 + avihSize;
125 const uint32_t strhChunkSize = 8 + strhSize;
126 const uint32_t strfChunkSize = 8 + strfSize;
127 const uint32_t strlListSize = 4 + strhChunkSize + strfChunkSize;
128 const uint32_t strlChunkSize = 8 + strlListSize;
129 const uint32_t hdrlListSize = 4 + avihChunkSize + strlChunkSize;
130 const uint32_t hdrlChunkSize = 8 + hdrlListSize;
131 const uint32_t frameChunkSize = 8 + paddedChunkSize(frameSize_);
132 const uint32_t headerFrameCount = expectedFrameCount_;
133 const uint32_t moviListSize =
134 expectedFrameCount_ == 0 ? 4
135 : 4 + (frameChunkSize * expectedFrameCount_);
136 const uint32_t moviChunkSize = 8 + moviListSize;
137 const uint32_t idx1Size =
139 const uint32_t idx1ChunkSize = expectedFrameCount_ == 0 ? 0 : 8 + idx1Size;
140 const uint32_t riffSize =
142 ? 4 + hdrlChunkSize + moviChunkSize
143 : 4 + hdrlChunkSize + moviChunkSize + idx1ChunkSize;
144 const uint32_t microsecondsPerFrame = 1000000UL / fps_;
145 const uint32_t maxBytesPerSecond = frameSize_ * fps_;
146 const uint32_t suggestedBufferSize = frameSize_;
147
148 writeFourCC("RIFF"); // RIFF chunk id
149 writeU32(riffSize); // RIFF chunk size
150 writeFourCC("AVI "); // AVI form type
151
152 writeFourCC("LIST"); // LIST chunk id
153 writeU32(hdrlListSize); // hdrl list size
154 writeFourCC("hdrl"); // header list type
155
156 writeFourCC("avih"); // main AVI header chunk id
157 writeU32(avihSize); // main AVI header size
158 writeU32(microsecondsPerFrame); // dwMicroSecPerFrame
159 writeU32(maxBytesPerSecond); // dwMaxBytesPerSec
160 writeU32(0); // dwPaddingGranularity
161 writeU32(0x10); // dwFlags (AVIF_HASINDEX when indexed)
162 writeU32(headerFrameCount); // dwTotalFrames
163 writeU32(0); // dwInitialFrames
164 writeU32(1); // dwStreams
165 writeU32(suggestedBufferSize); // dwSuggestedBufferSize
166 writeU32(width_); // dwWidth
167 writeU32(height_); // dwHeight
168 writeU32(0); // dwReserved[0]
169 writeU32(0); // dwReserved[1]
170 writeU32(0); // dwReserved[2]
171 writeU32(0); // dwReserved[3]
172
173 writeFourCC("LIST"); // LIST chunk id
174 writeU32(strlListSize); // strl list size
175 writeFourCC("strl"); // stream list type
176
177 writeFourCC("strh"); // stream header chunk id
178 writeU32(strhSize); // stream header size
179 writeFourCC("vids"); // fccType = video stream
180 writeFourCC("DIB "); // fccHandler = uncompressed DIB
181 writeU32(0); // dwFlags
182 writeU16(0); // wPriority
183 writeU16(0); // wLanguage
184 writeU32(0); // dwInitialFrames
185 writeU32(1); // dwScale
186 writeU32(fps_); // dwRate
187 writeU32(0); // dwStart
188 writeU32(headerFrameCount); // dwLength
189 writeU32(suggestedBufferSize); // dwSuggestedBufferSize
190 writeU32(0xFFFFFFFFUL); // dwQuality
191 writeU32(0); // dwSampleSize
192 writeU16(0); // rcFrame.left
193 writeU16(0); // rcFrame.top
194 writeU16(static_cast<uint16_t>(width_)); // rcFrame.right
195 writeU16(static_cast<uint16_t>(height_)); // rcFrame.bottom
196
197 writeFourCC("strf"); // stream format chunk id
198 writeU32(strfSize); // stream format size
199 writeU32(40); // biSize
200 writeU32(width_); // biWidth
201 writeU32(height_); // biHeight
202 writeU16(1); // biPlanes
203 writeU16(16); // biBitCount
204 writeU32(3); // biCompression = BI_BITFIELDS
205 writeU32(frameSize_); // biSizeImage
206 writeU32(0); // biXPelsPerMeter
207 writeU32(0); // biYPelsPerMeter
208 writeU32(0); // biClrUsed
209 writeU32(0); // biClrImportant
210 writeU32(0xF800); // red mask
211 writeU32(0x07E0); // green mask
212 writeU32(0x001F); // blue mask
213
214 writeFourCC("LIST"); // LIST chunk id
215 writeU32(moviListSize); // movi list size
216 writeFourCC("movi"); // movi list type
217 }
218
219 /// Writes one surface as a bottom-up padded RGB565 AVI frame.
220 void writeFrame(const ISurface<RGB_T>& surface) {
221 const uint32_t width = static_cast<uint32_t>(surface.width());
222 const uint32_t height = static_cast<uint32_t>(surface.height());
223 const uint32_t rowStride = frameRowStride(width);
224 const uint8_t padByte = 0;
225
226 for (uint32_t row = 0; row < height; ++row) {
227 const uint32_t sourceY = height - 1U - row;
228 for (uint32_t x = 0; x < width; ++x) {
229 writeU16(surface.getPixel(x, sourceY).getValue());
230 }
231 for (uint32_t padding = width * 2U; padding < rowStride; ++padding) {
232 writeBytes(&padByte, 1);
233 }
234 }
235 }
236
237 /// Writes the idx1 frame index when a frame count was provided.
238 void writeIndex() {
239 const uint32_t frameDataSize = frameSize_;
240 const uint32_t idx1Size = expectedFrameCount_ * 16U;
241
242 writeFourCC("idx1");
243 writeU32(idx1Size);
244
245 uint32_t moviOffset = 4;
246 for (uint32_t frame = 0; frame < expectedFrameCount_; ++frame) {
247 writeFourCC("00db");
248 writeU32(0x10);
249 writeU32(moviOffset);
250 writeU32(frameDataSize);
251 moviOffset += 8 + paddedChunkSize(frameDataSize);
252 }
253 }
254
255 /// Writes a four-character AVI chunk identifier.
256 void writeFourCC(const char* value) {
257 writeBytes(reinterpret_cast<const uint8_t*>(value), 4);
258 }
259
260 /// Writes a single byte to the output stream.
261 void writeU8(uint8_t value) { out_.write(value); }
262
263 /// Writes a 16-bit little-endian value to the output stream.
264 void writeU16(uint16_t value) {
265 const uint8_t bytes[2] = {static_cast<uint8_t>(value & 0xFFU),
266 static_cast<uint8_t>((value >> 8) & 0xFFU)};
267 writeBytes(bytes, sizeof(bytes));
268 }
269
270 /// Writes a 32-bit little-endian value to the output stream.
271 void writeU32(uint32_t value) {
272 const uint8_t bytes[4] = {static_cast<uint8_t>(value & 0xFFU),
273 static_cast<uint8_t>((value >> 8) & 0xFFU),
274 static_cast<uint8_t>((value >> 16) & 0xFFU),
275 static_cast<uint8_t>((value >> 24) & 0xFFU)};
276 writeBytes(bytes, sizeof(bytes));
277 }
278
279 /// Writes a raw byte sequence to the output stream.
280 void writeBytes(const uint8_t* data, size_t length) {
281 out_.write(data, length);
282 }
283};
284
285} // namespace tinygpu
Writes a simple AVI movie containing RGB565 frames.
Definition: AVIWriter.h:22
uint32_t width_
Definition: AVIWriter.h:88
void writeIndex()
Writes the idx1 frame index when a frame count was provided.
Definition: AVIWriter.h:238
uint32_t frameSize_
Definition: AVIWriter.h:92
static uint32_t paddedChunkSize(uint32_t dataSize)
Returns the data size rounded up to an even AVI chunk length.
Definition: AVIWriter.h:103
uint32_t rowStride_
Definition: AVIWriter.h:91
uint16_t fps_
Definition: AVIWriter.h:90
void writeHeader()
Writes the RIFF, AVI, stream, and movi headers.
Definition: AVIWriter.h:120
void writeU8(uint8_t value)
Writes a single byte to the output stream.
Definition: AVIWriter.h:261
bool begin(const ISurface< RGB_T > &surface, uint16_t fps=30, uint32_t frameCount=0)
Definition: AVIWriter.h:31
uint32_t height_
Definition: AVIWriter.h:89
void writeU32(uint32_t value)
Writes a 32-bit little-endian value to the output stream.
Definition: AVIWriter.h:271
void writeU16(uint16_t value)
Writes a 16-bit little-endian value to the output stream.
Definition: AVIWriter.h:264
void writeFrame(const ISurface< RGB_T > &surface)
Writes one surface as a bottom-up padded RGB565 AVI frame.
Definition: AVIWriter.h:220
void resetState()
Resets the writer state after finishing or aborting a stream.
Definition: AVIWriter.h:108
static uint32_t frameRowStride(uint32_t width)
Returns the padded byte stride of one RGB565 frame row.
Definition: AVIWriter.h:98
uint32_t expectedFrameCount_
Definition: AVIWriter.h:93
bool isOpen_
Definition: AVIWriter.h:95
void writeBytes(const uint8_t *data, size_t length)
Writes a raw byte sequence to the output stream.
Definition: AVIWriter.h:280
bool add(const ISurface< RGB_T > &surface)
Appends one RGB565 frame to the AVI stream.
Definition: AVIWriter.h:66
uint32_t frameCount_
Definition: AVIWriter.h:94
void end()
Finalizes the AVI stream and writes the optional frame index.
Definition: AVIWriter.h:54
void writeFourCC(const char *value)
Writes a four-character AVI chunk identifier.
Definition: AVIWriter.h:256
AVIWriter(Print &out)
Creates an AVI writer that streams data to the provided Print output.
Definition: AVIWriter.h:25
~AVIWriter()
Finalizes the AVI stream if it is still open.
Definition: AVIWriter.h:27
Print & out_
Definition: AVIWriter.h:87
RGB color stored in 16-bit RGB565 format.
Definition: RGB565.h:13
Definition: AVIWriter.h:9