21static constexpr unsigned AVI_HEADER_SIZE = 500;
24 unsigned height_,
unsigned bpp,
unsigned channels_,
26 : file(filename,
"wb")
27 , codec(width_, height_, bpp)
33 std::array<uint8_t, AVI_HEADER_SIZE> dummy = {};
43 std::string filename = file.
getURL();
52 std::array<uint8_t, AVI_HEADER_SIZE> avi_header = {};
53 unsigned header_pos = 0;
55 auto AVIOUT4 = [&](std::string_view s) {
56 assert(s.size() == 4);
60 auto AVIOUTw = [&](uint16_t w) {
62 header_pos +=
sizeof(w);
64 auto AVIOUTd = [&](uint32_t d) {
66 header_pos +=
sizeof(d);
69 auto len1 = s.size() + 1;
71 header_pos += narrow<unsigned>((len1 + 1) & ~1);
74 bool hasAudio = audioRate != 0;
78 AVIOUTd(AVI_HEADER_SIZE + written - 8 +
unsigned(index.size() *
sizeof(
Endian::L32)));
81 auto main_list = header_pos;
87 AVIOUTd(uint32_t(1000000 / fps));
93 AVIOUTd(hasAudio? 2 : 1);
104 AVIOUTd(4 + 8 + 56 + 8 + 40);
115 AVIOUTd(uint32_t(1000000 * fps));
119 AVIOUTd(uint32_t(~0));
131 AVIOUTd(width * height * 4);
139 uint16_t bitsPerSample = 16;
140 unsigned bytesPerSample = bitsPerSample / 8;
141 unsigned bytesPerFragment = bytesPerSample * channels;
142 unsigned bytesPerSecond = audioRate * bytesPerFragment;
143 unsigned fragments = audioWritten / channels;
147 AVIOUTd(4 + 8 + 56 + 8 + 16);
158 AVIOUTd(bytesPerFragment);
159 AVIOUTd(bytesPerSecond);
163 AVIOUTd(
unsigned(~0));
164 AVIOUTd(bytesPerFragment);
171 AVIOUTw(narrow<uint16_t>(channels));
173 AVIOUTd(bytesPerSecond);
174 AVIOUTw(narrow<uint16_t>(bytesPerFragment));
175 AVIOUTw(bitsPerSample);
191 constexpr size_t size = (4 + 1 + 2 + 1 + 2 + 1) + 22;
192 std::array<char, size> dateStr;
193 time_t
t = time(
nullptr);
194 struct tm *tm = localtime(&
t);
195 size_t dateLen = snprintf(dateStr.data(),
sizeof(dateStr),
"%04d-%02d-%02d", 1900 + tm->tm_year,
196 tm->tm_mon + 1, tm->tm_mday);
197 assert(dateLen <
size);
200 AVIOUTd(narrow<uint32_t>(
202 + (4 + 4 + ((versionStr.size() + 1 + 1) & ~1))
203 + (4 + 4 + ((dateLen + 1 + 1) & ~1))
207 AVIOUTd(
unsigned(versionStr.size()) + 1);
210 AVIOUTd(
unsigned(dateLen) + 1);
217 auto nMain = header_pos - main_list - 4;
218 auto nJunk = AVI_HEADER_SIZE - 8 - 12 - header_pos;
223 header_pos = main_list;
225 header_pos = AVI_HEADER_SIZE - 12;
228 AVIOUTd(written + 4);
233 unsigned idxSize = unsigned(index.size()) *
sizeof(
Endian::L32);
234 index[0] = (
'i' << 0) | (
'd' << 8) | (
'x' << 16) | (
'1' << 24);
235 index[1] = idxSize - 8;
236 file.
write(std::span{index});
238 file.
write(avi_header);
244void AviWriter::addAviChunk(std::span<const char, 4> tag,
size_t size_,
const void* data,
unsigned flags)
247 std::array<char, 4>
t;
252 auto size = uint32_t(size_);
256 file.
write(std::span{&chunk, 1});
258 file.
write(std::span{
static_cast<const uint8_t*
>(data),
size});
259 unsigned pos = written + 4;
262 std::array<uint8_t, 1> padding = {0};
267 size_t idxSize = index.size();
268 index.resize(idxSize + 4);
269 memcpy(&index[idxSize], tag.data(), tag.size());
270 index[idxSize + 1] = flags;
271 index[idxSize + 2] = pos;
272 index[idxSize + 3] =
size;
277 bool keyFrame = (frames++ % 300 == 0);
279 addAviChunk(subspan<4>(
"00dc"), buffer.size(), buffer.data(), keyFrame ? 0x10 : 0x0);
281 if (!audio.empty()) {
282 assert((audio.size() % channels) == 0);
283 assert(audioRate != 0);
287 auto buf = to_vector<Endian::L16>(audio);
288 addAviChunk(subspan<4>(
"01wb"), audio.size_bytes(), buf.data(), 0);
290 addAviChunk(subspan<4>(
"01wb"), audio.size_bytes(), audio.data(), 0);
292 audioWritten += narrow<uint32_t>(audio.size());
AviWriter(const Filename &filename, unsigned width, unsigned height, unsigned bpp, 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)