#pragma once #include "structs.h" #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; struct State { AVIStreamHeader streamHeaders[2]; struct Video { uint32_t streamID; BitMapInfo fmt; } video; struct Audio { uint32_t streamID; WaveFormat fmt; } audio; ChunkInfo moviInfo; ChunkInfo idxlInfo; uint32_t totalSec; } state; public: Demuxer(Source& src) : src(src) { RIFFHeader riff; read(riff); if (riff.type != FOURCC("RIFF") || riff.content != FOURCC("AVI ")) {return;} AVIList hdrl; read(hdrl); if (hdrl.type != FOURCC("LIST") || hdrl.content != FOURCC("hdrl")) {return;} uint32_t posAfterStreams = pos + (hdrl.size - 4); 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;} } // seek to next seek(startOfNextStream); } dumpState(); // 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); } // 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; } // just ignore unknown chunks seek(pos + chunk.size); continue; } } 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 { return valid; } private: //uint32_t pos = 0; void seek(uint32_t newPos) { //pos = newPos; src.seekTo(newPos); } template void read(T& dst) { read(sizeof(T), &dst); } /** read x bytes from the input into dst */ template void read(uint32_t size, T* dst) { 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; } 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()); } } }; }