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;
61 using FOURCC =
char[4];
66 uint32_t dwMicroSecPerFrame;
67 uint32_t dwMaxBytesPerSec;
68 uint32_t dwPaddingGranularity;
70 uint32_t dwTotalFrames;
71 uint32_t dwInitialFrames;
73 uint32_t dwSuggestedBufferSize;
76 uint32_t dwReserved[4];
90 uint32_t dwInitialFrames;
95 uint32_t dwSuggestedBufferSize;
97 uint32_t dwSampleSize;
107 uint32_t biCompression;
108 uint32_t biSizeImage;
109 uint64_t biXPelsPerMeter;
110 uint64_t biYPelsPerMeter;
112 uint32_t biClrImportant;
118 uint32_t nSamplesPerSec;
119 uint32_t nAvgBytesPerSec;
120 uint16_t nBlockAlign;
121 uint16_t wBitsPerSample;
133 enum StreamContentType { Audio, Video };
135 enum ParseObjectType { AVIList, AVIChunk, AVIStreamData };
158 void set(
size_t currentPos,
StrView id,
size_t size, ParseObjectType type) {
159 set(currentPos,
id.c_str(), size, type);
162 void set(
size_t currentPos,
const char *
id,
size_t size,
163 ParseObjectType type) {
166 start_pos = currentPos;
171 end_pos = currentPos + data_size + 4;
174 memcpy(chunk_id,
id, 4);
179 const char *id() {
return chunk_id; }
180 size_t size() {
return data_size; }
182 ParseObjectType type() {
return object_type; }
184 switch (object_type) {
186 return isAudio() || isVideo();
212 return object_type == AVIStreamData ? (chunk_id[1] << 8) | chunk_id[0] : 0;
215 return object_type == AVIStreamData
216 ? chunk_id[2] ==
'w' && chunk_id[3] ==
'b'
219 bool isVideoUncompressed() {
220 return object_type == AVIStreamData
221 ? chunk_id[2] ==
'd' && chunk_id[3] ==
'b'
224 bool isVideoCompressed() {
225 return object_type == AVIStreamData
226 ? chunk_id[2] ==
'd' && chunk_id[3] ==
'c'
229 bool isVideo() {
return isVideoCompressed() || isVideoUncompressed(); }
233 char chunk_id[5] = {};
234 ParseObjectType object_type;
252 parse_buffer.resize(bufferSize);
253 p_decoder = ©_decoder;
258 int bufferSize = 1024) {
259 parse_buffer.resize(bufferSize);
260 p_decoder = audioDecoder;
262 if (videoOut !=
nullptr) {
263 setOutputVideoStream(*videoOut);
268 if (p_output_audio !=
nullptr)
269 delete p_output_audio;
272 bool begin()
override {
273 parse_state = ParseHeader;
274 header_is_avi =
false;
275 is_parsing_active =
true;
277 header_is_avi =
false;
278 stream_header_idx = -1;
279 is_metadata_ready =
false;
290 void setMute(
bool mute) { is_mute = mute; }
292 virtual void setOutputVideoStream(VideoOutput &out_stream) {
293 p_output_video = &out_stream;
296 virtual size_t write(
const uint8_t *data,
size_t len)
override {
297 LOGD(
"write: %d", (
int)len);
298 int result = parse_buffer.writeArray((uint8_t *)data, len);
299 if (is_parsing_active) {
303 while (parse_buffer.available() > 4) {
309 parse_buffer.clear();
311 is_parsing_active =
false;
317 operator bool()
override {
return is_parsing_active; }
319 void end()
override { is_parsing_active =
false; };
330 const char *videoFormat() {
return video_format; }
353 bool header_is_avi =
false;
354 bool is_parsing_active =
true;
355 ParseState parse_state = ParseHeader;
358 int stream_header_idx = -1;
367 long open_subchunk_len = 0;
368 long current_pos = 0;
369 long movi_end_pos = 0;
372 char video_format[5] = {0};
373 bool is_metadata_ready =
false;
374 bool (*validation_cb)(AVIDecoder &avi) =
nullptr;
375 bool is_mute =
false;
376 CopyDecoder copy_decoder;
377 AudioDecoder *p_decoder =
nullptr;
378 int video_seconds = 0;
379 VideoAudioSync defaultSynch;
380 VideoAudioSync *p_synch = &defaultSynch;
382 bool isCurrentStreamAudio() {
383 return strncmp(stream_header[stream_header_idx].fccType,
"auds", 4) == 0;
386 bool isCurrentStreamVideo() {
387 return strncmp(stream_header[stream_header_idx].fccType,
"vids", 4) == 0;
393 switch (parse_state) {
395 result = parseHeader();
397 parse_state = ParseHdrl;
402 result = hdrl.isValid();
404 parse_state = ParseAvih;
410 result = avih.isValid();
412 main_header = *(avih.asAVIMainHeader(parse_buffer.data()));
413 stream_header.resize(main_header.dwStreams);
415 parse_state = ParseStrl;
422 stream_header[++stream_header_idx] =
423 *(strh.asAVIStreamHeader(parse_buffer.data()));
425 parse_state = ParseStrf;
430 if (isCurrentStreamAudio()) {
431 audio_info = *(strf.asAVIAudioFormat(parse_buffer.data()));
434 content_types.push_back(Audio);
436 }
else if (isCurrentStreamVideo()) {
437 video_info = *(strf.asAVIVideoFormat(parse_buffer.data()));
439 LOGI(
"videoFormat: %s", videoFormat());
440 content_types.push_back(Video);
446 parse_state = AfterStrf;
451 int pos = parse_buffer.indexOf(
"LIST");
455 if (StrView(tmp.id()).equals(
"strl")) {
456 parse_state = ParseStrl;
457 }
else if (StrView(tmp.id()).equals(
"movi")) {
458 parse_state = ParseMovi;
461 consume(tmp.size() + LIST_HEADER_SIZE);
467 consume(parse_buffer.available() - 4);
473 if (StrView(movi.id()).equals(
"movi")) {
475 is_metadata_ready =
true;
477 is_parsing_active = (validation_cb(*
this));
479 movi_end_pos = movi.end_pos;
480 parse_state = SubChunk;
489 if (StrView(hdrl.id()).equals(
"rec")) {
494 current_stream_data = parseAVIStreamData();
495 parse_state = SubChunkContinue;
496 open_subchunk_len = current_stream_data.open;
497 if (current_stream_data.isVideo()) {
498 LOGI(
"video:[%d]->[%d]", (
int)current_stream_data.start_pos,
499 (
int)current_stream_data.end_pos);
500 if (p_output_video !=
nullptr)
501 p_output_video->beginFrame(current_stream_data.open);
502 }
else if (current_stream_data.isAudio()) {
503 LOGI(
"audio:[%d]->[%d]", (
int)current_stream_data.start_pos,
504 (
int)current_stream_data.end_pos);
506 LOGW(
"unknown subchunk at %d", (
int)current_pos);
511 case SubChunkContinue: {
513 if (open_subchunk_len == 0) {
514 if (current_stream_data.isVideo() && p_output_video !=
nullptr) {
515 uint32_t time_used_ms = p_output_video->endFrame();
516 p_synch->delayVideoFrame(main_header.dwMicroSecPerFrame, time_used_ms);
519 parse_state = ParseIgnore;
521 parse_state = ParseRec;
523 if (current_pos >= movi_end_pos) {
524 parse_state = ParseIgnore;
526 parse_state = SubChunk;
534 parse_buffer.clear();
544 void setupAudioInfo() {
545 info.
channels = audio_info.nChannels;
550 if (p_decoder !=
nullptr) {
551 p_decoder->setAudioInfo(info);
552 info = p_decoder->audioInfo();
554 notifyAudioChange(info);
557 void setupVideoInfo() {
558 memcpy(video_format, stream_header[stream_header_idx].fccHandler, 4);
559 AVIStreamHeader *vh = &stream_header[stream_header_idx];
560 if (vh->dwScale <= 0) {
563 int rate = vh->dwRate / vh->dwScale;
564 video_seconds = rate <= 0 ? 0 : vh->dwLength / rate;
565 LOGI(
"videoSeconds: %d seconds", video_seconds);
569 long to_write = min((
long)parse_buffer.available(), open_subchunk_len);
570 if (current_stream_data.isAudio()) {
571 LOGD(
"audio %d", (
int)to_write);
573 p_synch->writeAudio(p_output_audio, parse_buffer.data(), to_write);
575 open_subchunk_len -= to_write;
578 }
else if (current_stream_data.isVideo()) {
579 LOGD(
"video %d", (
int)to_write);
580 if (p_output_video !=
nullptr)
581 p_output_video->write(parse_buffer.data(), to_write);
582 open_subchunk_len -= to_write;
590 bool header_is_avi =
false;
592 if (
getStr(0, 4).equals(
"RIFF")) {
594 uint32_t header_file_size =
getInt(4);
596 result.set(current_pos,
"AVI ", header_file_size, AVIChunk);
597 processStack(result);
603 return header_is_avi;
610 result.set(current_pos,
getStr(0, 4), 0, AVIChunk);
618 if (
getStr(0, 4).equals(
id)) {
619 result.set(current_pos,
id, 0, AVIChunk);
627 if (list_id.
equals(
id) &&
getStr(0, 3).equals(
"LIST")) {
628 result.set(current_pos,
getStr(8, 4),
getInt(4), AVIList);
636 if (
getStr(0, 4).equals(
"LIST")) {
637 result.set(current_pos,
getStr(8, 4),
getInt(4), AVIList);
645 int chunk_size =
getInt(4);
646 if (
getStr(0, 4).equals(
id) && parse_buffer.size() >= chunk_size) {
647 result.set(current_pos,
id, chunk_size, AVIChunk);
648 processStack(result);
657 if (
getStr(0, 4).equals(
"LIST") &&
getStr(8, 4).equals(
id)) {
659 result.set(current_pos,
id, size, AVIList);
660 processStack(result);
669 result.set(current_pos,
getStr(0, 4), size, AVIStreamData);
670 if (result.isValid()) {
671 processStack(result);
677 void processStack(ParseObject &result) {
679 object_stack.push(result);
680 spaces.
setChars(
' ', object_stack.size());
681 LOGD(
"%s - %s (%d-%d) size:%d", spaces.
c_str(), result.id(),
682 (
int)result.start_pos, (
int)result.end_pos, (
int)result.data_size);
685 void cleanupStack() {
688 object_stack.peek(current);
689 while (current.end_pos <= current_pos) {
690 object_stack.pop(current);
691 object_stack.peek(current);
697 str.setCapacity(len + 1);
698 const char *data = (
const char *)parse_buffer.data();
699 str.
copyFrom((data + offset), len, 5);
706 uint32_t *result = (uint32_t *)(parse_buffer.data() + offset);
713 parse_buffer.consume(len);
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition: AudioFormat.h:19
Definition: ContainerAVI.h:63