arduino-audio-tools
Loading...
Searching...
No Matches
ContainerAVI.h
1#pragma once
2#include <string.h>
3#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
4#include "AudioTools/CoreAudio/AudioBasic/Str.h"
6#include "AudioTools/Video/Video.h"
7#include "AudioTools/CoreAudio/Buffers.h"
8
9#define LIST_HEADER_SIZE 12
10#define CHUNK_HEADER_SIZE 8
11
12namespace audio_tools {
13
22public:
23 size_t writeArray(uint8_t *data, size_t len) {
24 int to_write = min(availableToWrite(), (size_t)len);
25 memmove(vector.data() + available_byte_count, data, to_write);
26 available_byte_count += to_write;
27 return to_write;
28 }
29 void consume(int size) {
30 memmove(vector.data(), &vector[size], available_byte_count - size);
31 available_byte_count -= size;
32 }
33 void resize(int size) { vector.resize(size + 4); }
34
35 uint8_t *data() { return vector.data(); }
36
37 size_t availableToWrite() { return size() - available_byte_count; }
38
39 size_t available() { return available_byte_count; }
40
41 void clear() {
42 available_byte_count = 0;
43 memset(vector.data(), 0, vector.size());
44 }
45
46 bool isEmpty() { return available_byte_count == 0; }
47
48 size_t size() { return vector.size(); }
49
50 long indexOf(const char *str) {
51 uint8_t *ptr = (uint8_t *)memmem(vector.data(), available_byte_count, str,
52 strlen(str));
53 return ptr == nullptr ? -1l : ptr - vector.data();
54 }
55
56protected:
57 Vector<uint8_t> vector{0};
58 size_t available_byte_count = 0;
59};
60
63using FOURCC = char[4];
64
66 // FOURCC fcc;
67 // uint32_t cb;
68 uint32_t dwMicroSecPerFrame;
69 uint32_t dwMaxBytesPerSec;
70 uint32_t dwPaddingGranularity;
71 uint32_t dwFlags;
72 uint32_t dwTotalFrames;
73 uint32_t dwInitialFrames;
74 uint32_t dwStreams;
75 uint32_t dwSuggestedBufferSize;
76 uint32_t dwWidth;
77 uint32_t dwHeight;
78 uint32_t dwReserved[4];
79};
80
81struct RECT {
82 uint32_t dwWidth;
83 uint32_t dwHeight;
84};
85
87 FOURCC fccType;
88 FOURCC fccHandler;
89 uint32_t dwFlags;
90 uint16_t wPriority;
91 uint16_t wLanguage;
92 uint32_t dwInitialFrames;
93 uint32_t dwScale;
94 uint32_t dwRate;
95 uint32_t dwStart;
96 uint32_t dwLength;
97 uint32_t dwSuggestedBufferSize;
98 uint32_t dwQuality;
99 uint32_t dwSampleSize;
100 RECT rcFrame;
101};
102
104 uint32_t biSize;
105 uint64_t biWidth;
106 uint64_t biHeight;
107 uint16_t biPlanes;
108 uint16_t biBitCount;
109 uint32_t biCompression;
110 uint32_t biSizeImage;
111 uint64_t biXPelsPerMeter;
112 uint64_t biYPelsPerMeter;
113 uint32_t biClrUsed;
114 uint32_t biClrImportant;
115};
116
118 AudioFormat wFormatTag;
119 uint16_t nChannels;
120 uint32_t nSamplesPerSec;
121 uint32_t nAvgBytesPerSec;
122 uint16_t nBlockAlign;
123 uint16_t wBitsPerSample;
124 uint16_t cbSize;
125};
126
127// struct WAVFormat {
128// uint16_t wFormatTag;
129// uint16_t nChannels;
130// uint32_t nSamplesPerSec;
131// uint32_t nAvgBytesPerSec;
132// uint16_t nBlockAlign;
133// };
134
135enum StreamContentType { Audio, Video };
136
137enum ParseObjectType { AVIList, AVIChunk, AVIStreamData };
138
139enum ParseState {
140 ParseHeader,
141 ParseHdrl,
142 ParseAvih,
143 ParseStrl,
144 SubChunkContinue,
145 SubChunk,
146 ParseRec,
147 ParseStrf,
148 AfterStrf,
149 ParseMovi,
150 ParseIgnore,
151};
152
160public:
161 void set(size_t currentPos, StrView id, size_t size, ParseObjectType type) {
162 set(currentPos, id.c_str(), size, type);
163 }
164
165 void set(size_t currentPos, const char *id, size_t size,
166 ParseObjectType type) {
167 object_type = type;
168 data_size = size;
169 start_pos = currentPos;
170 // allign on word
171 if (size % 2 != 0) {
172 data_size++;
173 }
174 end_pos = currentPos + data_size + 4;
175 // save FOURCC
176 if (id != nullptr) {
177 memcpy(chunk_id, id, 4);
178 chunk_id[4] = 0;
179 }
180 open = data_size;
181 }
182 const char *id() { return chunk_id; }
183 size_t size() { return data_size; }
184
185 ParseObjectType type() { return object_type; }
186 bool isValid() {
187 switch (object_type) {
188 case AVIStreamData:
189 return isAudio() || isVideo();
190 case AVIChunk:
191 return open > 0;
192 case AVIList:
193 return true;
194 }
195 return false;
196 }
197
198 // for Chunk
199 AVIMainHeader *asAVIMainHeader(void *ptr) { return (AVIMainHeader *)ptr; }
200 AVIStreamHeader *asAVIStreamHeader(void *ptr) {
201 return (AVIStreamHeader *)ptr;
202 }
203 WAVFormatX *asAVIAudioFormat(void *ptr) { return (WAVFormatX *)ptr; }
204 BitmapInfoHeader *asAVIVideoFormat(void *ptr) {
205 return (BitmapInfoHeader *)ptr;
206 }
207
208 size_t open;
209 size_t end_pos;
210 size_t start_pos;
211 size_t data_size;
212
213 // for AVIStreamData
214 int streamNumber() {
215 return object_type == AVIStreamData ? (chunk_id[1] << 8) | chunk_id[0] : 0;
216 }
217 bool isAudio() {
218 return object_type == AVIStreamData
219 ? chunk_id[2] == 'w' && chunk_id[3] == 'b'
220 : false;
221 }
222 bool isVideoUncompressed() {
223 return object_type == AVIStreamData
224 ? chunk_id[2] == 'd' && chunk_id[3] == 'b'
225 : false;
226 }
227 bool isVideoCompressed() {
228 return object_type == AVIStreamData
229 ? chunk_id[2] == 'd' && chunk_id[3] == 'c'
230 : false;
231 }
232 bool isVideo() { return isVideoCompressed() || isVideoUncompressed(); }
233
234protected:
235 // ParseBuffer data_buffer;
236 char chunk_id[5] = {};
237 ParseObjectType object_type;
238};
239
253public:
254 AVIDecoder(int bufferSize = 1024) {
255 parse_buffer.resize(bufferSize);
256 p_decoder = &copy_decoder;
257 p_output_audio = new EncodedAudioOutput(&copy_decoder);
258 }
259
260 AVIDecoder(AudioDecoder *audioDecoder, VideoOutput *videoOut = nullptr,
261 int bufferSize = 1024) {
262 parse_buffer.resize(bufferSize);
263 p_decoder = audioDecoder;
264 p_output_audio = new EncodedAudioOutput(audioDecoder);
265 if (videoOut != nullptr) {
266 setOutputVideoStream(*videoOut);
267 }
268 }
269
270 ~AVIDecoder() {
271 if (p_output_audio != nullptr)
272 delete p_output_audio;
273 }
274
275 bool begin() override {
276 parse_state = ParseHeader;
277 header_is_avi = false;
278 is_parsing_active = true;
279 current_pos = 0;
280 header_is_avi = false;
281 stream_header_idx = -1;
282 is_metadata_ready = false;
283 return true;
284 }
285
287 virtual void setOutput(Print &out_stream) override {
288 // p_output_audio = &out_stream;
289 p_output_audio->setOutput(&out_stream);
290 }
291
293 void setMute(bool mute) { is_mute = mute; }
294
295 virtual void setOutputVideoStream(VideoOutput &out_stream) {
296 p_output_video = &out_stream;
297 }
298
299 virtual size_t write(const uint8_t *data, size_t len) override {
300 LOGD("write: %d", (int)len);
301 int result = parse_buffer.writeArray((uint8_t *)data, len);
302 if (is_parsing_active) {
303 // we expect the first parse to succeed
304 if (parse()) {
305 // if so we process the parse_buffer
306 while (parse_buffer.available() > 4) {
307 if (!parse())
308 break;
309 }
310 } else {
311 LOGD("Parse Error");
312 parse_buffer.clear();
313 result = len;
314 is_parsing_active = false;
315 }
316 }
317 return result;
318 }
319
320 operator bool() override { return is_parsing_active; }
321
322 void end() override { is_parsing_active = false; };
323
325 AVIMainHeader mainHeader() { return main_header; }
326
328 AVIStreamHeader streamHeader(int idx) { return stream_header[idx]; }
329
331 BitmapInfoHeader aviVideoInfo() { return video_info; };
332
333 const char *videoFormat() { return video_format; }
334
336 WAVFormatX aviAudioInfo() { return audio_info; }
337
339 AudioFormat audioFormat() { return audio_info.wFormatTag; }
340
342 bool isMetadataReady() { return is_metadata_ready; }
345 void setValidationCallback(bool (*cb)(AVIDecoder &avi)) {
346 validation_cb = cb;
347 }
348
350 int videoSeconds() { return video_seconds; }
351
353 void setVideoAudioSync(VideoAudioSync *yourSync) { p_synch = yourSync; }
354
355protected:
356 bool header_is_avi = false;
357 bool is_parsing_active = true;
358 ParseState parse_state = ParseHeader;
359 ParseBuffer parse_buffer;
360 AVIMainHeader main_header;
361 int stream_header_idx = -1;
362 Vector<AVIStreamHeader> stream_header;
363 BitmapInfoHeader video_info;
364 WAVFormatX audio_info;
365 Vector<StreamContentType> content_types;
366 Stack<ParseObject> object_stack;
367 ParseObject current_stream_data;
368 EncodedAudioOutput *p_output_audio = nullptr;
369 VideoOutput *p_output_video = nullptr;
370 long open_subchunk_len = 0;
371 long current_pos = 0;
372 long movi_end_pos = 0;
373 Str spaces;
374 Str str;
375 char video_format[5] = {0};
376 bool is_metadata_ready = false;
377 bool (*validation_cb)(AVIDecoder &avi) = nullptr;
378 bool is_mute = false;
379 CopyDecoder copy_decoder;
380 AudioDecoder *p_decoder = nullptr;
381 int video_seconds = 0;
382 VideoAudioSync defaultSynch;
383 VideoAudioSync *p_synch = &defaultSynch;
384
385 bool isCurrentStreamAudio() {
386 return strncmp(stream_header[stream_header_idx].fccType, "auds", 4) == 0;
387 }
388
389 bool isCurrentStreamVideo() {
390 return strncmp(stream_header[stream_header_idx].fccType, "vids", 4) == 0;
391 }
392
393 // we return true if at least one parse step was successful
394 bool parse() {
395 bool result = true;
396 switch (parse_state) {
397 case ParseHeader: {
398 result = parseHeader();
399 if (result)
400 parse_state = ParseHdrl;
401 } break;
402
403 case ParseHdrl: {
404 ParseObject hdrl = parseList("hdrl");
405 result = hdrl.isValid();
406 if (result) {
407 parse_state = ParseAvih;
408 }
409 } break;
410
411 case ParseAvih: {
412 ParseObject avih = parseChunk("avih");
413 result = avih.isValid();
414 if (result) {
415 main_header = *(avih.asAVIMainHeader(parse_buffer.data()));
416 stream_header.resize(main_header.dwStreams);
417 consume(avih.size());
418 parse_state = ParseStrl;
419 }
420 } break;
421
422 case ParseStrl: {
423 ParseObject strl = parseList("strl");
424 ParseObject strh = parseChunk("strh");
425 stream_header[++stream_header_idx] =
426 *(strh.asAVIStreamHeader(parse_buffer.data()));
427 consume(strh.size());
428 parse_state = ParseStrf;
429 } break;
430
431 case ParseStrf: {
432 ParseObject strf = parseChunk("strf");
433 if (isCurrentStreamAudio()) {
434 audio_info = *(strf.asAVIAudioFormat(parse_buffer.data()));
435 setupAudioInfo();
436 LOGI("audioFormat: %d (%x)", (int)audioFormat(),(int)audioFormat());
437 content_types.push_back(Audio);
438 consume(strf.size());
439 } else if (isCurrentStreamVideo()) {
440 video_info = *(strf.asAVIVideoFormat(parse_buffer.data()));
441 setupVideoInfo();
442 LOGI("videoFormat: %s", videoFormat());
443 content_types.push_back(Video);
444 video_format[4] = 0;
445 consume(strf.size());
446 } else {
447 result = false;
448 }
449 parse_state = AfterStrf;
450 } break;
451
452 case AfterStrf: {
453 // ignore all data until we find a new List
454 int pos = parse_buffer.indexOf("LIST");
455 if (pos >= 0) {
456 consume(pos);
457 ParseObject tmp = tryParseList();
458 if (StrView(tmp.id()).equals("strl")) {
459 parse_state = ParseStrl;
460 } else if (StrView(tmp.id()).equals("movi")) {
461 parse_state = ParseMovi;
462 } else {
463 // e.g. ignore info
464 consume(tmp.size() + LIST_HEADER_SIZE);
465 }
466 } else {
467 // no valid data, so throw it away, we keep the last 4 digits in case
468 // if it contains the beginning of a LIST
469 cleanupStack();
470 consume(parse_buffer.available() - 4);
471 }
472 } break;
473
474 case ParseMovi: {
475 ParseObject movi = tryParseList();
476 if (StrView(movi.id()).equals("movi")) {
477 consume(LIST_HEADER_SIZE);
478 is_metadata_ready = true;
479 if (validation_cb)
480 is_parsing_active = (validation_cb(*this));
481 processStack(movi);
482 movi_end_pos = movi.end_pos;
483 parse_state = SubChunk;
484 // trigger new write
485 result = false;
486 }
487 } break;
488
489 case SubChunk: {
490 // rec is optinal
491 ParseObject hdrl = tryParseList();
492 if (StrView(hdrl.id()).equals("rec")) {
493 consume(CHUNK_HEADER_SIZE);
494 processStack(hdrl);
495 }
496
497 current_stream_data = parseAVIStreamData();
498 parse_state = SubChunkContinue;
499 open_subchunk_len = current_stream_data.open;
500 if (current_stream_data.isVideo()) {
501 LOGI("video:[%d]->[%d]", (int)current_stream_data.start_pos,
502 (int)current_stream_data.end_pos);
503 if (p_output_video != nullptr)
504 p_output_video->beginFrame(current_stream_data.open);
505 } else if (current_stream_data.isAudio()) {
506 LOGI("audio:[%d]->[%d]", (int)current_stream_data.start_pos,
507 (int)current_stream_data.end_pos);
508 } else {
509 LOGW("unknown subchunk at %d", (int)current_pos);
510 }
511
512 } break;
513
514 case SubChunkContinue: {
515 writeData();
516 if (open_subchunk_len == 0) {
517 if (current_stream_data.isVideo() && p_output_video != nullptr) {
518 uint32_t time_used_ms = p_output_video->endFrame();
519 p_synch->delayVideoFrame(main_header.dwMicroSecPerFrame, time_used_ms);
520 }
521 if (tryParseChunk("idx").isValid()) {
522 parse_state = ParseIgnore;
523 } else if (tryParseList("rec").isValid()) {
524 parse_state = ParseRec;
525 } else {
526 if (current_pos >= movi_end_pos) {
527 parse_state = ParseIgnore;
528 } else {
529 parse_state = SubChunk;
530 }
531 }
532 }
533 } break;
534
535 case ParseIgnore: {
536 LOGD("ParseIgnore");
537 parse_buffer.clear();
538 } break;
539
540 default:
541 result = false;
542 break;
543 }
544 return result;
545 }
546
547 void setupAudioInfo() {
548 info.channels = audio_info.nChannels;
549 info.bits_per_sample = audio_info.wBitsPerSample;
550 info.sample_rate = audio_info.nSamplesPerSec;
551 info.logInfo();
552 // adjust the audio info if necessary
553 if (p_decoder != nullptr) {
554 p_decoder->setAudioInfo(info);
555 info = p_decoder->audioInfo();
556 }
557 notifyAudioChange(info);
558 }
559
560 void setupVideoInfo() {
561 memcpy(video_format, stream_header[stream_header_idx].fccHandler, 4);
562 AVIStreamHeader *vh = &stream_header[stream_header_idx];
563 if (vh->dwScale <= 0) {
564 vh->dwScale = 1;
565 }
566 int rate = vh->dwRate / vh->dwScale;
567 video_seconds = rate <= 0 ? 0 : vh->dwLength / rate;
568 LOGI("videoSeconds: %d seconds", video_seconds);
569 }
570
571 void writeData() {
572 long to_write = min((long)parse_buffer.available(), open_subchunk_len);
573 if (current_stream_data.isAudio()) {
574 LOGD("audio %d", (int)to_write);
575 if (!is_mute){
576 p_synch->writeAudio(p_output_audio, parse_buffer.data(), to_write);
577 }
578 open_subchunk_len -= to_write;
579 cleanupStack();
580 consume(to_write);
581 } else if (current_stream_data.isVideo()) {
582 LOGD("video %d", (int)to_write);
583 if (p_output_video != nullptr)
584 p_output_video->write(parse_buffer.data(), to_write);
585 open_subchunk_len -= to_write;
586 cleanupStack();
587 consume(to_write);
588 }
589 }
590
591 // 'RIFF' fileSize fileType (data)
592 bool parseHeader() {
593 bool header_is_avi = false;
594 int headerSize = 12;
595 if (getStr(0, 4).equals("RIFF")) {
596 ParseObject result;
597 uint32_t header_file_size = getInt(4);
598 header_is_avi = getStr(8, 4).equals("AVI ");
599 result.set(current_pos, "AVI ", header_file_size, AVIChunk);
600 processStack(result);
601 consume(headerSize);
602
603 } else {
604 LOGE("parseHeader");
605 }
606 return header_is_avi;
607 }
608
612 ParseObject result;
613 result.set(current_pos, getStr(0, 4), 0, AVIChunk);
614 return result;
615 }
616
619 ParseObject tryParseChunk(const char *id) {
620 ParseObject result;
621 if (getStr(0, 4).equals(id)) {
622 result.set(current_pos, id, 0, AVIChunk);
623 }
624 return result;
625 }
626
627 ParseObject tryParseList(const char *id) {
628 ParseObject result;
629 StrView &list_id = getStr(8, 4);
630 if (list_id.equals(id) && getStr(0, 3).equals("LIST")) {
631 result.set(current_pos, getStr(8, 4), getInt(4), AVIList);
632 }
633 return result;
634 }
635
638 ParseObject result;
639 if (getStr(0, 4).equals("LIST")) {
640 result.set(current_pos, getStr(8, 4), getInt(4), AVIList);
641 }
642 return result;
643 }
644
646 ParseObject parseChunk(const char *id) {
647 ParseObject result;
648 int chunk_size = getInt(4);
649 if (getStr(0, 4).equals(id) && parse_buffer.size() >= chunk_size) {
650 result.set(current_pos, id, chunk_size, AVIChunk);
651 processStack(result);
652 consume(CHUNK_HEADER_SIZE);
653 }
654 return result;
655 }
656
658 ParseObject parseList(const char *id) {
659 ParseObject result;
660 if (getStr(0, 4).equals("LIST") && getStr(8, 4).equals(id)) {
661 int size = getInt(4);
662 result.set(current_pos, id, size, AVIList);
663 processStack(result);
664 consume(LIST_HEADER_SIZE);
665 }
666 return result;
667 }
668
669 ParseObject parseAVIStreamData() {
670 ParseObject result;
671 int size = getInt(4);
672 result.set(current_pos, getStr(0, 4), size, AVIStreamData);
673 if (result.isValid()) {
674 processStack(result);
675 consume(8);
676 }
677 return result;
678 }
679
680 void processStack(ParseObject &result) {
681 cleanupStack();
682 object_stack.push(result);
683 spaces.setChars(' ', object_stack.size());
684 LOGD("%s - %s (%d-%d) size:%d", spaces.c_str(), result.id(),
685 (int)result.start_pos, (int)result.end_pos, (int)result.data_size);
686 }
687
688 void cleanupStack() {
689 ParseObject current;
690 // make sure that we remove the object from the stack of we past the end
691 object_stack.peek(current);
692 while (current.end_pos <= current_pos) {
693 object_stack.pop(current);
694 object_stack.peek(current);
695 }
696 }
697
699 StrView &getStr(int offset, int len) {
700 str.setCapacity(len + 1);
701 const char *data = (const char *)parse_buffer.data();
702 str.copyFrom((data + offset), len, 5);
703
704 return str;
705 }
706
708 uint32_t getInt(int offset) {
709 uint32_t *result = (uint32_t *)(parse_buffer.data() + offset);
710 return *result;
711 }
712
714 void consume(int len) {
715 current_pos += len;
716 parse_buffer.consume(len);
717 }
718};
719
720} // namespace audio_tools
WAV Audio Formats used by Microsoft e.g. in AVI video files.
AVI Container Decoder which can be fed with small chunks of data. The minimum length must be bigger t...
Definition ContainerAVI.h:252
StrView & getStr(int offset, int len)
Provides the string at the indicated byte offset with the indicated length.
Definition ContainerAVI.h:699
ParseObject parseList(const char *id)
We load the indicated list from the current data.
Definition ContainerAVI.h:658
void setVideoAudioSync(VideoAudioSync *yourSync)
Replace the synchronization logic with your implementation.
Definition ContainerAVI.h:353
ParseObject parseChunk(const char *id)
We load the indicated chunk from the current data.
Definition ContainerAVI.h:646
AudioFormat audioFormat()
Provides the audio_info.wFormatTag.
Definition ContainerAVI.h:339
AVIMainHeader mainHeader()
Provides the information from the main header chunk.
Definition ContainerAVI.h:325
int videoSeconds()
Provide the length of the video in seconds.
Definition ContainerAVI.h:350
uint32_t getInt(int offset)
Provides the int32 at the indicated byte offset.
Definition ContainerAVI.h:708
ParseObject tryParseList()
We try to parse the actual state for any list.
Definition ContainerAVI.h:637
bool isMetadataReady()
Returns true if all metadata has been parsed and is available.
Definition ContainerAVI.h:342
BitmapInfoHeader aviVideoInfo()
Provides the video information.
Definition ContainerAVI.h:331
void consume(int len)
We remove the processed bytes from the beginning of the buffer.
Definition ContainerAVI.h:714
virtual void setOutput(Print &out_stream) override
Defines the audio output stream - usually called by EncodedAudioStream.
Definition ContainerAVI.h:287
ParseObject tryParseChunk()
Definition ContainerAVI.h:611
void setValidationCallback(bool(*cb)(AVIDecoder &avi))
Definition ContainerAVI.h:345
WAVFormatX aviAudioInfo()
Provides the audio information.
Definition ContainerAVI.h:336
AVIStreamHeader streamHeader(int idx)
Provides the information from the stream header chunks.
Definition ContainerAVI.h:328
ParseObject tryParseChunk(const char *id)
Definition ContainerAVI.h:619
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
Parent class for all container formats.
Definition AudioCodecsBase.h:86
A more natural Print class to process encoded data (aac, wav, mp3...). Just define the output and the...
Definition AudioEncoded.h:21
void setOutput(Print *outputStream)
Defines the output.
Definition AudioEncoded.h:104
We try to keep the necessary buffer for parsing as small as possible, The data() method provides the ...
Definition ContainerAVI.h:21
Represents a LIST or a CHUNK: The ParseObject represents the current parsing result....
Definition ContainerAVI.h:159
Definition NoArduino.h:62
LIFO Stack which is based on a List.
Definition Stack.h:14
Str which keeps the data on the heap. We grow the allocated memory only if the copy source is not fit...
Definition Str.h:24
void copyFrom(const char *source, int len, int maxlen=0)
assigns a memory buffer
Definition Str.h:96
void setChars(char c, int len)
Fills the string with len chars.
Definition Str.h:108
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual bool equals(const char *str)
checks if the string equals indicated parameter string
Definition StrView.h:165
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:379
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Logic to Synchronize video and audio output: This is the minimum implementatin which actually does no...
Definition Video.h:37
Abstract class for video playback. This class is used to assemble a complete video frame in memory.
Definition Video.h:21
char[4] FOURCC
Four-character code identifier for AVI format.
Definition ContainerAVI.h:63
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:19
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Definition ContainerAVI.h:65
Definition ContainerAVI.h:86
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61
Definition ContainerAVI.h:103
Definition ContainerAVI.h:81
Definition ContainerAVI.h:117