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