worked on avi, working now? :P
This commit is contained in:
@@ -4,16 +4,30 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/previous-versions//ms779636(v=vs.85)?redirectedfrom=MSDN
|
// 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 {
|
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 {
|
template <typename Source> class Demuxer {
|
||||||
|
|
||||||
Source& src;
|
Source& src;
|
||||||
|
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
AVITypeHeader riff;
|
//AVITypeHeader riff;
|
||||||
AVITypeHeader hdrl;
|
//AVITypeHeader hdrl;
|
||||||
AVIMainHeader avih;
|
//AVIMainHeader avih;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
|
||||||
@@ -40,98 +54,106 @@ namespace AVI {
|
|||||||
|
|
||||||
Demuxer(Source& src) : src(src) {
|
Demuxer(Source& src) : src(src) {
|
||||||
|
|
||||||
|
RIFFHeader riff;
|
||||||
read(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);
|
AVIList hdrl;
|
||||||
if (memcmp(hdrl.fourcc, "LIST", 4) != 0) {valid = false; return;}
|
read(hdrl);
|
||||||
if (memcmp(hdrl.type, "hdrl", 4) != 0) {valid = false; return;}
|
if (hdrl.type != FOURCC("LIST") || hdrl.content != FOURCC("hdrl")) {return;}
|
||||||
|
uint32_t posAfterStreams = pos + (hdrl.size - 4);
|
||||||
|
|
||||||
read(avih);
|
AVIMainHeader avih;
|
||||||
if (memcmp(avih.fourcc, "avih", 4) != 0) {valid = false; return;}
|
read(avih);
|
||||||
if (avih.cb != 56) {valid = false; return;}
|
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;
|
// seek to next
|
||||||
bool curStreamIsVideo = false;
|
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;
|
dumpState();
|
||||||
read(strh);
|
|
||||||
if (memcmp(strh.fourcc, "strh", 4) != 0) {valid = false; return;}
|
|
||||||
|
|
||||||
if (memcmp(strh.type, "vids", 4) == 0) {
|
// continue reading after hdrl
|
||||||
state.video.streamID = i;
|
seek(posAfterStreams);
|
||||||
state.streamHeaders[i] = strh;
|
|
||||||
curStreamIsVideo = true;
|
// there might be an INFO list before the movi list
|
||||||
} else if (memcmp(strh.type, "auds", 4) == 0) {
|
|
||||||
state.audio.streamID = i;
|
AVIList movi = readUntilList("movi");
|
||||||
state.streamHeaders[i] = strh;
|
|
||||||
} else {
|
std::cout << "movi data: " << movi.size << " bytes" << std::endl;
|
||||||
valid = false; return;
|
|
||||||
|
|
||||||
|
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!)
|
||||||
uint8_t xx[8];
|
if (chunk.type == "LIST") {
|
||||||
read(xx);
|
seek(pos + sizeof(AVIList) - sizeof(AVIChunk));
|
||||||
|
continue;
|
||||||
// strf, depends on type (audio/video)
|
|
||||||
if (curStreamIsVideo) {
|
|
||||||
read(state.video.fmt); // BitmapInfoHeader
|
|
||||||
} else {
|
|
||||||
read(state.audio.fmt); // WaveFormatEx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// seek to the next stream
|
// just ignore unknown chunks
|
||||||
seek(posOfStreamStart + 8 + strl.length);
|
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 {
|
bool isValid() const {
|
||||||
@@ -140,10 +162,11 @@ namespace AVI {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint32_t pos = 0;
|
//uint32_t pos = 0;
|
||||||
|
|
||||||
void seek(uint32_t newPos) {
|
void seek(uint32_t newPos) {
|
||||||
pos = newPos;
|
//pos = newPos;
|
||||||
|
src.seekTo(newPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void read(T& dst) {
|
template <typename T> void read(T& dst) {
|
||||||
@@ -152,16 +175,46 @@ namespace AVI {
|
|||||||
|
|
||||||
/** read x bytes from the input into dst */
|
/** read x bytes from the input into dst */
|
||||||
template <typename T> void read(uint32_t size, T* dst) {
|
template <typename T> void read(uint32_t size, T* dst) {
|
||||||
src.read(pos, size, (uint8_t*)dst);
|
src.read(size, (uint8_t*)dst);
|
||||||
pos += size;
|
//pos += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dumpState() {
|
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;
|
AVIList readUntilList(const char* content) {
|
||||||
std::cout << "audio @" << state.audio.streamID << ": " << state.audio.fmt.channels << " channels @ " << state.audio.fmt.samples_per_sec << " Hz" << std::endl;
|
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,72 @@
|
|||||||
#pragma once
|
#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 {
|
//struct AVICommonHeader {
|
||||||
// FOURCC fourcc;
|
// FOURCC fourcc;
|
||||||
// uint32_t length;
|
// uint32_t length;
|
||||||
//};
|
//};
|
||||||
|
|
||||||
struct AVITypeHeader {
|
//struct AVITypeHeader {
|
||||||
FOURCC fourcc;
|
// FOURCC fourcc;
|
||||||
uint32_t length;
|
// uint32_t length;
|
||||||
|
// FOURCC type;
|
||||||
|
//};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct AVIChunk {
|
||||||
|
|
||||||
FOURCC type;
|
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 {
|
struct AVIList : AVIChunk {
|
||||||
FOURCC fourcc;
|
|
||||||
uint32_t cb;
|
// 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 {
|
||||||
|
|
||||||
|
// <data from AVIChunk>
|
||||||
|
|
||||||
uint32_t ms_per_frame;
|
uint32_t ms_per_frame;
|
||||||
uint32_t max_bytes_per_sec;
|
uint32_t max_bytes_per_sec;
|
||||||
uint32_t padding_granularity;
|
uint32_t padding_granularity;
|
||||||
@@ -27,6 +78,7 @@ struct AVIMainHeader {
|
|||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
uint32_t reserved[4];
|
uint32_t reserved[4];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AVIRect {
|
struct AVIRect {
|
||||||
@@ -36,11 +88,12 @@ struct AVIRect {
|
|||||||
uint16_t bottom;
|
uint16_t bottom;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AVIStreamHeader {
|
struct AVIStreamHeader : AVIChunk {
|
||||||
FOURCC fourcc;
|
|
||||||
uint32_t cb;
|
// <data from AVIChunk>
|
||||||
FOURCC type;
|
|
||||||
FOURCC fcc_handler;
|
FOURCC fccType; // "vids" or "auds" or "txts"
|
||||||
|
FOURCC fccHandler; // video format, e.g. "MJPG"
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint16_t priority;
|
uint16_t priority;
|
||||||
uint16_t language;
|
uint16_t language;
|
||||||
@@ -53,6 +106,10 @@ struct AVIStreamHeader {
|
|||||||
uint32_t quality;
|
uint32_t quality;
|
||||||
uint32_t sample_size;
|
uint32_t sample_size;
|
||||||
AVIRect rcFrame;
|
AVIRect rcFrame;
|
||||||
|
|
||||||
|
bool isVideo() const {return fccType == FOURCC("vids");}
|
||||||
|
bool isAudio() const {return fccType == FOURCC("auds");}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BitMapInfoHeader {
|
struct BitMapInfoHeader {
|
||||||
@@ -76,12 +133,14 @@ struct RGBQUAD {
|
|||||||
uint8_t rgbReserved;
|
uint8_t rgbReserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BitMapInfo {
|
struct BitMapInfo : AVIChunk {
|
||||||
|
// <data from AVIChunk>
|
||||||
BitMapInfoHeader bmi_header;
|
BitMapInfoHeader bmi_header;
|
||||||
RGBQUAD bmi_colors[1];
|
RGBQUAD bmi_colors[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WaveFormat {
|
struct WaveFormat : AVIChunk {
|
||||||
|
// <data from AVIChunk>
|
||||||
uint16_t format_tag;
|
uint16_t format_tag;
|
||||||
uint16_t channels;
|
uint16_t channels;
|
||||||
uint32_t samples_per_sec;
|
uint32_t samples_per_sec;
|
||||||
|
|||||||
Reference in New Issue
Block a user