From f04742a1b02ec48ae5fc2b2b027a4ac9889ec86f Mon Sep 17 00:00:00 2001 From: kazu Date: Thu, 25 Feb 2021 07:11:56 +0100 Subject: [PATCH] worked on avi, working now? :P --- data/formats/avi/Demuxer.h | 217 +++++++++++++++++++++++-------------- data/formats/avi/structs.h | 87 ++++++++++++--- 2 files changed, 208 insertions(+), 96 deletions(-) diff --git a/data/formats/avi/Demuxer.h b/data/formats/avi/Demuxer.h index 72e7d8d..673f7fd 100644 --- a/data/formats/avi/Demuxer.h +++ b/data/formats/avi/Demuxer.h @@ -4,16 +4,30 @@ #include // https://docs.microsoft.com/en-us/previous-versions//ms779636(v=vs.85)?redirectedfrom=MSDN +// basically, everything is a CHUNK and a chunk has a type (4 bytes) and a length (4 bytes) namespace AVI { + enum class NextChunkType { + INVALID, + AUDIO, + VIDEO, + }; + + struct NextChunk { + NextChunkType type; + uint32_t size; + NextChunk() {} + NextChunk(NextChunkType type, uint32_t size) : type(type), size(size) {} + }; + template class Demuxer { Source& src; bool valid = true; - AVITypeHeader riff; - AVITypeHeader hdrl; - AVIMainHeader avih; + //AVITypeHeader riff; + //AVITypeHeader hdrl; + //AVIMainHeader avih; struct State { @@ -40,98 +54,106 @@ namespace AVI { Demuxer(Source& src) : src(src) { + RIFFHeader riff; read(riff); - if (memcmp(riff.fourcc, "RIFF", 4) != 0) {valid = false; return;} + if (riff.type != FOURCC("RIFF") || riff.content != FOURCC("AVI ")) {return;} - read(hdrl); - if (memcmp(hdrl.fourcc, "LIST", 4) != 0) {valid = false; return;} - if (memcmp(hdrl.type, "hdrl", 4) != 0) {valid = false; return;} + AVIList hdrl; + read(hdrl); + if (hdrl.type != FOURCC("LIST") || hdrl.content != FOURCC("hdrl")) {return;} + uint32_t posAfterStreams = pos + (hdrl.size - 4); - read(avih); - if (memcmp(avih.fourcc, "avih", 4) != 0) {valid = false; return;} - if (avih.cb != 56) {valid = false; return;} + AVIMainHeader avih; + read(avih); + if (avih.type != FOURCC("avih") || avih.size != 56) {return;} + + // parse all streams + for (uint32_t streamID = 0; streamID < avih.streams; ++streamID) { + + AVIList strl; + read(strl); + if (strl.type != "LIST" || strl.content != FOURCC("strl")) {return;} + + // determine where the next entry will start + const uint32_t startOfNextStream = pos + (strl.size - 4); + + AVIStreamHeader strh; + read(strh); + if (strh.type != FOURCC("strh")) {return;} + + // remember + state.streamHeaders[streamID] = strh; + + // read the type-specific stream format: strf + if (strh.isVideo()) { + state.video.streamID = streamID; + read(state.video.fmt); + if (state.video.fmt.type != FOURCC("strf")) {return;} + } else if (strh.isAudio()) { + state.audio.streamID = streamID; + read(state.audio.fmt); + if (state.video.fmt.type != FOURCC("strf")) {return;} - for (uint32_t i = 0; i < avih.streams; ++i) { + } - uint32_t posOfStreamStart = pos; - bool curStreamIsVideo = false; + // seek to next + seek(startOfNextStream); - AVITypeHeader strl; - read(strl); - if (memcmp(strl.fourcc, "LIST", 4) != 0) {valid = false; return;} - if (memcmp(strl.type, "strl", 4) != 0) {valid = false; return;} + } - AVIStreamHeader strh; - read(strh); - if (memcmp(strh.fourcc, "strh", 4) != 0) {valid = false; return;} + dumpState(); - if (memcmp(strh.type, "vids", 4) == 0) { - state.video.streamID = i; - state.streamHeaders[i] = strh; - curStreamIsVideo = true; - } else if (memcmp(strh.type, "auds", 4) == 0) { - state.audio.streamID = i; - state.streamHeaders[i] = strh; - } else { - valid = false; return; + // continue reading after hdrl + seek(posAfterStreams); + + // there might be an INFO list before the movi list + + AVIList movi = readUntilList("movi"); + + std::cout << "movi data: " << movi.size << " bytes" << std::endl; + + + printf("nn"); + + } + + /** determine the next (usable) Chunk in the data stream */ + NextChunk getNextChunk() { + + while(true) { + + AVIChunk chunk; + read(chunk); + + if (chunk.isAudioData()) { + return NextChunk(NextChunkType::AUDIO, chunk.size); + } + if (chunk.isVideoData()) { + return NextChunk(NextChunkType::VIDEO, chunk.size); } - // ???? - uint8_t xx[8]; - read(xx); - - // strf, depends on type (audio/video) - if (curStreamIsVideo) { - read(state.video.fmt); // BitmapInfoHeader - } else { - read(state.audio.fmt); // WaveFormatEx + // if this is a list, dive INTO it (do not skip it! it has meaningful content!) + if (chunk.type == "LIST") { + seek(pos + sizeof(AVIList) - sizeof(AVIChunk)); + continue; } - // seek to the next stream - seek(posOfStreamStart + 8 + strl.length); + // just ignore unknown chunks + seek(pos + chunk.size); + continue; } - dumpState(); - - uint8_t tmp[128]; - read(tmp); - - seek(pos-4); - - AVITypeHeader movi; - state.moviInfo.offset = pos; - read(movi); - state.moviInfo.entry_offset = pos; - state.moviInfo.size = movi.length - 4; - - seek(state.moviInfo.offset + 8 + movi.length); - - AVITypeHeader idxl; - state.idxlInfo.offset = pos; - read(idxl); - state.idxlInfo.entry_offset = pos; - state.idxlInfo.size = idxl.length - 4; - - // total length in seconds based on the video stream - // normally scale is 1 and rate = FPS - // for non-integer FPS, scale and rate vary accordingly - AVIStreamHeader& videoStreamHeader = state.streamHeaders[state.video.streamID]; - state.totalSec = videoStreamHeader.length * videoStreamHeader.scale / videoStreamHeader.rate; - - printf("nn"); - // p->total_sec = avi_video_calc_seconds(avi_parser_get_video_header(p)); - // goto end; - - - - //seek(state.streamHeaders[state.video.streamID].start); - //AVICommonHeader chead; - //read(chead); + } + void read(NextChunk nc, uint8_t* dst) { + //seek(pos + nc.size); + read(nc.size, dst); + if ((nc.size % 2) != 0) {seek(pos+1);} + printf("xx"); } bool isValid() const { @@ -140,10 +162,11 @@ namespace AVI { private: - uint32_t pos = 0; + //uint32_t pos = 0; void seek(uint32_t newPos) { - pos = newPos; + //pos = newPos; + src.seekTo(newPos); } template void read(T& dst) { @@ -152,16 +175,46 @@ namespace AVI { /** read x bytes from the input into dst */ template void read(uint32_t size, T* dst) { - src.read(pos, size, (uint8_t*)dst); - pos += size; + src.read(size, (uint8_t*)dst); + //pos += size; } void dumpState() { + std::cout << "video @" << state.video.streamID << ": " << state.video.fmt.bmi_header.biWidth << "x" << state.video.fmt.bmi_header.biHeight << " Format: " << state.streamHeaders[state.video.streamID].fccHandler.chars << std::endl; + std::cout << "audio @" << state.audio.streamID << ": " << state.audio.fmt.channels << " channels @ " << state.audio.fmt.samples_per_sec << " Hz" << " Format: " << state.audio.fmt.format_tag << std::endl; + } - std::cout << "video @" << state.video.streamID << ": " << state.video.fmt.bmi_header.biWidth << "x" << state.video.fmt.bmi_header.biHeight << std::endl; - std::cout << "audio @" << state.audio.streamID << ": " << state.audio.fmt.channels << " channels @ " << state.audio.fmt.samples_per_sec << " Hz" << std::endl; + AVIList readUntilList(const char* content) { + AVIChunk chunk; + + while(true) { + + const uint32_t startOfChunk = pos; + read(chunk); + + + // skip non-list chunks + if (chunk.type != FOURCC("LIST")) { + seek(pos + chunk.size); + continue; + } + + // is a list. seek back, before the chunk, to read it again, this time as LIST + seek(startOfChunk); + AVIList list; + read(list); + + // found! + if (list.content == FOURCC(content)) { + return list; + } + + // skip this list + seek(pos + list.getPayloadSize()); + + } } diff --git a/data/formats/avi/structs.h b/data/formats/avi/structs.h index a98dfe1..6cdf4be 100644 --- a/data/formats/avi/structs.h +++ b/data/formats/avi/structs.h @@ -1,21 +1,72 @@ #pragma once -typedef uint8_t FOURCC[4]; +//typedef uint8_t FOURCC[4]; + +union FOURCC { + + uint32_t u32; + uint8_t bytes[4]; + char chars[4]; + + FOURCC() {} + FOURCC(const char* str) : chars{str[0], str[1], str[2], str[3]} {} + + bool operator == (const FOURCC other) const {return u32 == other.u32;} + bool operator != (const FOURCC other) const {return u32 != other.u32;} + +}; + //struct AVICommonHeader { // FOURCC fourcc; // uint32_t length; //}; -struct AVITypeHeader { - FOURCC fourcc; - uint32_t length; +//struct AVITypeHeader { +// FOURCC fourcc; +// uint32_t length; +// FOURCC type; +//}; + + + +struct AVIChunk { + FOURCC type; + uint32_t size; + // uint8_t data[size] follows hereafter + + bool isJUNK() const {return type == FOURCC("JUNK");} + + /** is an audio data chunk? */ + bool isAudioData() const {return type.chars[2] == 'w' && type.chars[3] == 'b';} + + /** is a video data chunk? */ + bool isVideoData() const {return type.chars[2] == 'd' && type.chars[3] == 'c';} + }; -struct AVIMainHeader { - FOURCC fourcc; - uint32_t cb; +struct AVIList : AVIChunk { + + // AVIList is an AVIChunk with type "RIFF" or "LIST" + + FOURCC content; + // uint8_t data[size-4] follows hereafter, contains lists and chunks + + uint32_t getPayloadSize() const {return size-4;} + +}; + +struct RIFFHeader : AVIList { + +}; + + + +struct AVIMainHeader : AVIChunk { + + // + uint32_t ms_per_frame; uint32_t max_bytes_per_sec; uint32_t padding_granularity; @@ -27,6 +78,7 @@ struct AVIMainHeader { uint32_t width; uint32_t height; uint32_t reserved[4]; + }; struct AVIRect { @@ -36,11 +88,12 @@ struct AVIRect { uint16_t bottom; }; -struct AVIStreamHeader { - FOURCC fourcc; - uint32_t cb; - FOURCC type; - FOURCC fcc_handler; +struct AVIStreamHeader : AVIChunk { + + // + + FOURCC fccType; // "vids" or "auds" or "txts" + FOURCC fccHandler; // video format, e.g. "MJPG" uint32_t flags; uint16_t priority; uint16_t language; @@ -53,6 +106,10 @@ struct AVIStreamHeader { uint32_t quality; uint32_t sample_size; AVIRect rcFrame; + + bool isVideo() const {return fccType == FOURCC("vids");} + bool isAudio() const {return fccType == FOURCC("auds");} + }; struct BitMapInfoHeader { @@ -76,12 +133,14 @@ struct RGBQUAD { uint8_t rgbReserved; }; -struct BitMapInfo { +struct BitMapInfo : AVIChunk { + // BitMapInfoHeader bmi_header; RGBQUAD bmi_colors[1]; }; -struct WaveFormat { +struct WaveFormat : AVIChunk { + // uint16_t format_tag; uint16_t channels; uint32_t samples_per_sec;