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"
9#define LIST_HEADER_SIZE 12
10#define CHUNK_HEADER_SIZE 8
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;
29 void consume(
int size) {
30 memmove(vector.data(), &vector[size], available_byte_count - size);
31 available_byte_count -= size;
33 void resize(
int size) { vector.resize(size + 4); }
35 uint8_t *data() {
return vector.data(); }
37 size_t availableToWrite() {
return size() - available_byte_count; }
39 size_t available() {
return available_byte_count; }
42 available_byte_count = 0;
43 memset(vector.data(), 0, vector.size());
46 bool isEmpty() {
return available_byte_count == 0; }
48 size_t size() {
return vector.size(); }
50 long indexOf(
const char *str) {
51 uint8_t *ptr = (uint8_t *)memmem(vector.data(), available_byte_count, str,
53 return ptr ==
nullptr ? -1l : ptr - vector.data();
58 size_t available_byte_count = 0;
68 uint32_t dwMicroSecPerFrame;
69 uint32_t dwMaxBytesPerSec;
70 uint32_t dwPaddingGranularity;
72 uint32_t dwTotalFrames;
73 uint32_t dwInitialFrames;
75 uint32_t dwSuggestedBufferSize;
78 uint32_t dwReserved[4];
92 uint32_t dwInitialFrames;
97 uint32_t dwSuggestedBufferSize;
99 uint32_t dwSampleSize;
109 uint32_t biCompression;
110 uint32_t biSizeImage;
111 uint64_t biXPelsPerMeter;
112 uint64_t biYPelsPerMeter;
114 uint32_t biClrImportant;
120 uint32_t nSamplesPerSec;
121 uint32_t nAvgBytesPerSec;
122 uint16_t nBlockAlign;
123 uint16_t wBitsPerSample;
135enum StreamContentType { Audio, Video };
137enum ParseObjectType { AVIList, AVIChunk, AVIStreamData };
161 void set(
size_t currentPos,
StrView id,
size_t size, ParseObjectType type) {
162 set(currentPos,
id.c_str(), size, type);
165 void set(
size_t currentPos,
const char *
id,
size_t size,
166 ParseObjectType type) {
169 start_pos = currentPos;
174 end_pos = currentPos + data_size + 4;
177 memcpy(chunk_id,
id, 4);
182 const char *id() {
return chunk_id; }
183 size_t size() {
return data_size; }
185 ParseObjectType type() {
return object_type; }
187 switch (object_type) {
189 return isAudio() || isVideo();
215 return object_type == AVIStreamData ? (chunk_id[1] << 8) | chunk_id[0] : 0;
218 return object_type == AVIStreamData
219 ? chunk_id[2] ==
'w' && chunk_id[3] ==
'b'
222 bool isVideoUncompressed() {
223 return object_type == AVIStreamData
224 ? chunk_id[2] ==
'd' && chunk_id[3] ==
'b'
227 bool isVideoCompressed() {
228 return object_type == AVIStreamData
229 ? chunk_id[2] ==
'd' && chunk_id[3] ==
'c'
232 bool isVideo() {
return isVideoCompressed() || isVideoUncompressed(); }
236 char chunk_id[5] = {};
237 ParseObjectType object_type;
255 parse_buffer.resize(bufferSize);
256 p_decoder = ©_decoder;
261 int bufferSize = 1024) {
262 parse_buffer.resize(bufferSize);
263 p_decoder = audioDecoder;
265 if (videoOut !=
nullptr) {
266 setOutputVideoStream(*videoOut);
271 if (p_output_audio !=
nullptr)
272 delete p_output_audio;
275 bool begin()
override {
276 parse_state = ParseHeader;
277 header_is_avi =
false;
278 is_parsing_active =
true;
280 header_is_avi =
false;
281 stream_header_idx = -1;
282 is_metadata_ready =
false;
293 void setMute(
bool mute) { is_mute = mute; }
295 virtual void setOutputVideoStream(VideoOutput &out_stream) {
296 p_output_video = &out_stream;
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) {
306 while (parse_buffer.available() > 4) {
312 parse_buffer.clear();
314 is_parsing_active =
false;
320 operator bool()
override {
return is_parsing_active; }
322 void end()
override { is_parsing_active =
false; };
333 const char *videoFormat() {
return video_format; }
356 bool header_is_avi =
false;
357 bool is_parsing_active =
true;
358 ParseState parse_state = ParseHeader;
361 int stream_header_idx = -1;
370 long open_subchunk_len = 0;
371 long current_pos = 0;
372 long movi_end_pos = 0;
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;
385 bool isCurrentStreamAudio() {
386 return strncmp(stream_header[stream_header_idx].fccType,
"auds", 4) == 0;
389 bool isCurrentStreamVideo() {
390 return strncmp(stream_header[stream_header_idx].fccType,
"vids", 4) == 0;
396 switch (parse_state) {
398 result = parseHeader();
400 parse_state = ParseHdrl;
405 result = hdrl.isValid();
407 parse_state = ParseAvih;
413 result = avih.isValid();
415 main_header = *(avih.asAVIMainHeader(parse_buffer.data()));
416 stream_header.resize(main_header.dwStreams);
418 parse_state = ParseStrl;
425 stream_header[++stream_header_idx] =
426 *(strh.asAVIStreamHeader(parse_buffer.data()));
428 parse_state = ParseStrf;
433 if (isCurrentStreamAudio()) {
434 audio_info = *(strf.asAVIAudioFormat(parse_buffer.data()));
437 content_types.push_back(Audio);
439 }
else if (isCurrentStreamVideo()) {
440 video_info = *(strf.asAVIVideoFormat(parse_buffer.data()));
442 LOGI(
"videoFormat: %s", videoFormat());
443 content_types.push_back(Video);
449 parse_state = AfterStrf;
454 int pos = parse_buffer.indexOf(
"LIST");
458 if (StrView(tmp.id()).equals(
"strl")) {
459 parse_state = ParseStrl;
460 }
else if (StrView(tmp.id()).equals(
"movi")) {
461 parse_state = ParseMovi;
464 consume(tmp.size() + LIST_HEADER_SIZE);
470 consume(parse_buffer.available() - 4);
476 if (StrView(movi.id()).equals(
"movi")) {
478 is_metadata_ready =
true;
480 is_parsing_active = (validation_cb(*
this));
482 movi_end_pos = movi.end_pos;
483 parse_state = SubChunk;
492 if (StrView(hdrl.id()).equals(
"rec")) {
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);
509 LOGW(
"unknown subchunk at %d", (
int)current_pos);
514 case SubChunkContinue: {
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);
522 parse_state = ParseIgnore;
524 parse_state = ParseRec;
526 if (current_pos >= movi_end_pos) {
527 parse_state = ParseIgnore;
529 parse_state = SubChunk;
537 parse_buffer.clear();
547 void setupAudioInfo() {
548 info.
channels = audio_info.nChannels;
553 if (p_decoder !=
nullptr) {
554 p_decoder->setAudioInfo(info);
555 info = p_decoder->audioInfo();
557 notifyAudioChange(info);
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) {
566 int rate = vh->dwRate / vh->dwScale;
567 video_seconds = rate <= 0 ? 0 : vh->dwLength / rate;
568 LOGI(
"videoSeconds: %d seconds", video_seconds);
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);
576 p_synch->writeAudio(p_output_audio, parse_buffer.data(), to_write);
578 open_subchunk_len -= 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;
593 bool header_is_avi =
false;
595 if (
getStr(0, 4).equals(
"RIFF")) {
597 uint32_t header_file_size =
getInt(4);
599 result.set(current_pos,
"AVI ", header_file_size, AVIChunk);
600 processStack(result);
606 return header_is_avi;
613 result.set(current_pos,
getStr(0, 4), 0, AVIChunk);
621 if (
getStr(0, 4).equals(
id)) {
622 result.set(current_pos,
id, 0, AVIChunk);
630 if (list_id.
equals(
id) &&
getStr(0, 3).equals(
"LIST")) {
631 result.set(current_pos,
getStr(8, 4),
getInt(4), AVIList);
639 if (
getStr(0, 4).equals(
"LIST")) {
640 result.set(current_pos,
getStr(8, 4),
getInt(4), AVIList);
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);
660 if (
getStr(0, 4).equals(
"LIST") &&
getStr(8, 4).equals(
id)) {
662 result.set(current_pos,
id, size, AVIList);
663 processStack(result);
672 result.set(current_pos,
getStr(0, 4), size, AVIStreamData);
673 if (result.isValid()) {
674 processStack(result);
680 void processStack(ParseObject &result) {
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);
688 void cleanupStack() {
691 object_stack.peek(current);
692 while (current.end_pos <= current_pos) {
693 object_stack.pop(current);
694 object_stack.peek(current);
700 str.setCapacity(len + 1);
701 const char *data = (
const char *)parse_buffer.data();
702 str.
copyFrom((data + offset), len, 5);
709 uint32_t *result = (uint32_t *)(parse_buffer.data() + offset);
716 parse_buffer.consume(len);
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
Definition ContainerAVI.h:65