224 lines
4.7 KiB
C++
224 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include "structs.h"
|
|
#include <iostream>
|
|
|
|
// 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 <typename Source> 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 <typename T> void read(T& dst) {
|
|
read(sizeof(T), &dst);
|
|
}
|
|
|
|
/** read x bytes from the input into dst */
|
|
template <typename T> 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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|