4 #include "AudioBasic/StrExt.h"
6 #include "Video/Video.h"
7 #include "AudioTools/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,
Str 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 void 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;
289 void setMute(
bool mute) { is_mute = mute; }
291 virtual void setOutputVideoStream(VideoOutput &out_stream) {
292 p_output_video = &out_stream;
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) {
302 while (parse_buffer.available() > 4) {
308 parse_buffer.clear();
310 is_parsing_active =
false;
316 operator bool()
override {
return is_parsing_active; }
318 void end()
override { is_parsing_active =
false; };
329 const char *videoFormat() {
return video_format; }
352 bool header_is_avi =
false;
353 bool is_parsing_active =
true;
354 ParseState parse_state = ParseHeader;
357 int stream_header_idx = -1;
366 long open_subchunk_len = 0;
367 long current_pos = 0;
368 long movi_end_pos = 0;
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;
381 bool isCurrentStreamAudio() {
382 return strncmp(stream_header[stream_header_idx].fccType,
"auds", 4) == 0;
385 bool isCurrentStreamVideo() {
386 return strncmp(stream_header[stream_header_idx].fccType,
"vids", 4) == 0;
392 switch (parse_state) {
394 result = parseHeader();
396 parse_state = ParseHdrl;
401 result = hdrl.isValid();
403 parse_state = ParseAvih;
409 result = avih.isValid();
411 main_header = *(avih.asAVIMainHeader(parse_buffer.data()));
412 stream_header.resize(main_header.dwStreams);
414 parse_state = ParseStrl;
421 stream_header[++stream_header_idx] =
422 *(strh.asAVIStreamHeader(parse_buffer.data()));
424 parse_state = ParseStrf;
429 if (isCurrentStreamAudio()) {
430 audio_info = *(strf.asAVIAudioFormat(parse_buffer.data()));
433 content_types.push_back(Audio);
435 }
else if (isCurrentStreamVideo()) {
436 video_info = *(strf.asAVIVideoFormat(parse_buffer.data()));
438 LOGI(
"videoFormat: %s", videoFormat());
439 content_types.push_back(Video);
445 parse_state = AfterStrf;
450 int pos = parse_buffer.indexOf(
"LIST");
454 if (Str(tmp.id()).equals(
"strl")) {
455 parse_state = ParseStrl;
456 }
else if (Str(tmp.id()).equals(
"movi")) {
457 parse_state = ParseMovi;
460 consume(tmp.size() + LIST_HEADER_SIZE);
466 consume(parse_buffer.available() - 4);
472 if (Str(movi.id()).equals(
"movi")) {
474 is_metadata_ready =
true;
476 is_parsing_active = (validation_cb(*
this));
478 movi_end_pos = movi.end_pos;
479 parse_state = SubChunk;
488 if (Str(hdrl.id()).equals(
"rec")) {
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);
505 LOGW(
"unknown subchunk at %d", (
int)current_pos);
510 case SubChunkContinue: {
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);
518 parse_state = ParseIgnore;
520 parse_state = ParseRec;
522 if (current_pos >= movi_end_pos) {
523 parse_state = ParseIgnore;
525 parse_state = SubChunk;
533 parse_buffer.clear();
543 void setupAudioInfo() {
544 info.channels = audio_info.nChannels;
545 info.bits_per_sample = audio_info.wBitsPerSample;
546 info.sample_rate = audio_info.nSamplesPerSec;
549 if (p_decoder !=
nullptr) {
550 p_decoder->setAudioInfo(info);
551 info = p_decoder->audioInfo();
554 p_notify->setAudioInfo(info);
558 void setupVideoInfo() {
559 memcpy(video_format, stream_header[stream_header_idx].fccHandler, 4);
560 AVIStreamHeader *vh = &stream_header[stream_header_idx];
561 if (vh->dwScale <= 0) {
564 int rate = vh->dwRate / vh->dwScale;
565 video_seconds = rate <= 0 ? 0 : vh->dwLength / rate;
566 LOGI(
"videoSeconds: %d seconds", video_seconds);
570 long to_write = min((
long)parse_buffer.available(), open_subchunk_len);
571 if (current_stream_data.isAudio()) {
572 LOGD(
"audio %d", (
int)to_write);
574 p_synch->writeAudio(p_output_audio, parse_buffer.data(), to_write);
576 open_subchunk_len -= to_write;
579 }
else if (current_stream_data.isVideo()) {
580 LOGD(
"video %d", (
int)to_write);
581 if (p_output_video !=
nullptr)
582 p_output_video->write(parse_buffer.data(), to_write);
583 open_subchunk_len -= to_write;
591 bool header_is_avi =
false;
593 if (
getStr(0, 4).equals(
"RIFF")) {
595 uint32_t header_file_size =
getInt(4);
597 result.set(current_pos,
"AVI ", header_file_size, AVIChunk);
598 processStack(result);
604 return header_is_avi;
611 result.set(current_pos,
getStr(0, 4), 0, AVIChunk);
619 if (
getStr(0, 4).equals(
id)) {
620 result.set(current_pos,
id, 0, AVIChunk);
628 if (list_id.
equals(
id) &&
getStr(0, 3).equals(
"LIST")) {
629 result.set(current_pos,
getStr(8, 4),
getInt(4), AVIList);
637 if (
getStr(0, 4).equals(
"LIST")) {
638 result.set(current_pos,
getStr(8, 4),
getInt(4), AVIList);
646 int chunk_size =
getInt(4);
647 if (
getStr(0, 4).equals(
id) && parse_buffer.size() >= chunk_size) {
648 result.set(current_pos,
id, chunk_size, AVIChunk);
649 processStack(result);
658 if (
getStr(0, 4).equals(
"LIST") &&
getStr(8, 4).equals(
id)) {
660 result.set(current_pos,
id, size, AVIList);
661 processStack(result);
670 result.set(current_pos,
getStr(0, 4), size, AVIStreamData);
671 if (result.isValid()) {
672 processStack(result);
678 void processStack(ParseObject &result) {
680 object_stack.push(result);
681 spaces.
setChars(
' ', object_stack.size());
682 LOGD(
"%s - %s (%d-%d) size:%d", spaces.
c_str(), result.id(),
683 (
int)result.start_pos, (
int)result.end_pos, (
int)result.data_size);
686 void cleanupStack() {
689 object_stack.peek(current);
690 while (current.end_pos <= current_pos) {
691 object_stack.pop(current);
692 object_stack.peek(current);
698 str.setCapacity(len + 1);
699 const char *data = (
const char *)parse_buffer.data();
700 str.
copyFrom((data + offset), len, 5);
707 uint32_t *result = (uint32_t *)(parse_buffer.data() + offset);
714 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