21static constexpr unsigned AVI_HEADER_SIZE = 500;
24 unsigned height_,
unsigned channels_,
unsigned freq_)
25 : file(filename,
"wb")
26 , codec(width_, height_)
32 std::array<uint8_t, AVI_HEADER_SIZE> dummy = {};
42 std::string filename = file.
getURL();
51 std::array<uint8_t, AVI_HEADER_SIZE> avi_header = {};
52 unsigned header_pos = 0;
54 auto AVIOUT4 = [&](std::string_view s) {
55 assert(s.size() == 4);
59 auto AVIOUTw = [&](uint16_t w) {
61 header_pos +=
sizeof(w);
63 auto AVIOUTd = [&](uint32_t d) {
65 header_pos +=
sizeof(d);
68 auto len1 = s.size() + 1;
70 header_pos += narrow<unsigned>((len1 + 1) & ~1);
73 bool hasAudio = audioRate != 0;
77 AVIOUTd(AVI_HEADER_SIZE + written - 8 +
unsigned(index.size() *
sizeof(
Endian::L32)));
80 auto main_list = header_pos;
86 AVIOUTd(uint32_t(1000000 / fps));
92 AVIOUTd(hasAudio? 2 : 1);
103 AVIOUTd(4 + 8 + 56 + 8 + 40);
114 AVIOUTd(uint32_t(1000000 * fps));
118 AVIOUTd(uint32_t(~0));
130 AVIOUTd(width * height * 4);
138 uint16_t bitsPerSample = 16;
139 unsigned bytesPerSample = bitsPerSample / 8;
140 unsigned bytesPerFragment = bytesPerSample * channels;
141 unsigned bytesPerSecond = audioRate * bytesPerFragment;
142 unsigned fragments = audioWritten / channels;
146 AVIOUTd(4 + 8 + 56 + 8 + 16);
157 AVIOUTd(bytesPerFragment);
158 AVIOUTd(bytesPerSecond);
162 AVIOUTd(
unsigned(~0));
163 AVIOUTd(bytesPerFragment);
170 AVIOUTw(narrow<uint16_t>(channels));
172 AVIOUTd(bytesPerSecond);
173 AVIOUTw(narrow<uint16_t>(bytesPerFragment));
174 AVIOUTw(bitsPerSample);
190 constexpr size_t size = (4 + 1 + 2 + 1 + 2 + 1) + 22;
191 std::array<char, size> dateStr;
192 time_t
t = time(
nullptr);
193 struct tm *tm = localtime(&
t);
194 size_t dateLen = snprintf(dateStr.data(),
sizeof(dateStr),
"%04d-%02d-%02d", 1900 + tm->tm_year,
195 tm->tm_mon + 1, tm->tm_mday);
196 assert(dateLen <
size);
199 AVIOUTd(narrow<uint32_t>(
201 + (4 + 4 + ((versionStr.size() + 1 + 1) & ~1))
202 + (4 + 4 + ((dateLen + 1 + 1) & ~1))
206 AVIOUTd(
unsigned(versionStr.size()) + 1);
209 AVIOUTd(
unsigned(dateLen) + 1);
216 auto nMain = header_pos - main_list - 4;
217 auto nJunk = AVI_HEADER_SIZE - 8 - 12 - header_pos;
222 header_pos = main_list;
224 header_pos = AVI_HEADER_SIZE - 12;
227 AVIOUTd(written + 4);
232 unsigned idxSize = unsigned(index.size()) *
sizeof(
Endian::L32);
233 index[0] = (
'i' << 0) | (
'd' << 8) | (
'x' << 16) | (
'1' << 24);
234 index[1] = idxSize - 8;
235 file.
write(std::span{index});
237 file.
write(avi_header);
243void AviWriter::addAviChunk(std::span<const char, 4> tag,
size_t size_,
const void* data,
unsigned flags)
246 std::array<char, 4>
t;
251 auto size = uint32_t(size_);
255 file.
write(std::span{&chunk, 1});
257 file.
write(std::span{
static_cast<const uint8_t*
>(data),
size});
258 unsigned pos = written + 4;
261 std::array<uint8_t, 1> padding = {0};
266 size_t idxSize = index.size();
267 index.resize(idxSize + 4);
268 memcpy(&index[idxSize], tag.data(), tag.size());
269 index[idxSize + 1] = flags;
270 index[idxSize + 2] = pos;
271 index[idxSize + 3] =
size;
276 bool keyFrame = (frames++ % 300 == 0);
278 addAviChunk(subspan<4>(
"00dc"), buffer.size(), buffer.data(), keyFrame ? 0x10 : 0x0);
280 if (!audio.empty()) {
281 assert((audio.size() % channels) == 0);
282 assert(audioRate != 0);
286 auto buf = to_vector<Endian::L16>(audio);
287 addAviChunk(subspan<4>(
"01wb"), audio.size_bytes(), buf.data(), 0);
289 addAviChunk(subspan<4>(
"01wb"), audio.size_bytes(), audio.data(), 0);
291 audioWritten += narrow<uint32_t>(audio.size());
AviWriter(const Filename &filename, unsigned width, unsigned height, unsigned channels, unsigned freq)
void addFrame(FrameSource *video, std::span< const int16_t > audio)
void close()
Close the current file.
void seek(size_t pos)
Move read/write pointer to the specified position.
void write(std::span< const uint8_t > buffer)
Write to file.
const std::string & getURL() const
Returns the URL of this file object.
This class represents a filename.
Interface for getting lines from a video frame.
static std::string full()
static constexpr std::string_view CODEC_4CC
std::span< const uint8_t > compressFrame(bool keyFrame, FrameSource *frame)
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr const char * data() const
ALWAYS_INLINE void write_UA_L32(void *p, uint32_t x)
ALWAYS_INLINE void write_UA_L16(void *p, uint16_t x)
EndianT< uint32_t, ConvLittle< BIG > > L32
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
int unlink(zstring_view path)
Call unlink() in a platform-independent manner.
This file implemented 3 utility functions:
auto copy(InputRange &&range, OutputIter out)
size_t size(std::string_view utf8)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)