diff --git a/data/formats/avi/Demuxer.h b/data/formats/avi/Demuxer.h new file mode 100644 index 0000000..cc8f791 --- /dev/null +++ b/data/formats/avi/Demuxer.h @@ -0,0 +1,158 @@ +#pragma once + +#include "structs.h" + +// https://docs.microsoft.com/en-us/previous-versions//ms779636(v=vs.85)?redirectedfrom=MSDN +namespace AVI { + + template class Demuxer { + + Source& src; + + bool valid = true; + AVITypeHeader riff; + AVITypeHeader hdrl; + AVICommonHeader avih; + AVIMainHeader main_hdr; + + struct State { + + AVIStreamHeader streamHeaders[2]; + + struct Video { + uint32_t streamID; + BitMapInfoHeader fmt; + } video; + + struct Audio { + uint32_t streamID; + WaveFormat fmt; + } audio; + + ChunkInfo moviInfo; + ChunkInfo idxlInfo; + + uint32_t totalSec; + + } state; + + public: + + Demuxer(Source& src) : src(src) { + + read(riff); + if (memcmp(riff.fourcc, "RIFF", 4) != 0) {valid = false; return;} + + read(hdrl); + if (memcmp(hdrl.fourcc, "LIST", 4) != 0) {valid = false; return;} + if (memcmp(hdrl.type, "hdrl", 4) != 0) {valid = false; return;} + + read(avih); + if (memcmp(avih.fourcc, "avih", 4) != 0) {valid = false; return;} + if (avih.length != sizeof(main_hdr)) {valid = false; return;} + + read(main_hdr); + + uint32_t posOfStreamStart = pos; + + for (uint32_t i = 0; i < main_hdr.streams; ++i) { + + bool curStreamIsVideo = false; + + AVITypeHeader strl; + read(strl); + if (memcmp(strl.fourcc, "LIST", 4) != 0) {valid = false; return;} + if (memcmp(strl.type, "strl", 4) != 0) {valid = false; return;} + + AVICommonHeader strh; + read(strh); + if (memcmp(strh.fourcc, "strh", 4) != 0) {valid = false; return;} + + AVIStreamHeader stream_hdr; + read(stream_hdr); + if (memcmp(stream_hdr.type, "vids", 4) == 0) { + state.video.streamID = i; + state.streamHeaders[i] = stream_hdr; + curStreamIsVideo = true; + } else if (memcmp(stream_hdr.type, "auds", 4) == 0) { + state.audio.streamID = i; + state.streamHeaders[i] = stream_hdr; + } else { + valid = false; return; + } + + AVICommonHeader strf; + read(strf); + if (curStreamIsVideo) { + BitMapInfoHeader bmih; + read(bmih); + state.video.fmt = bmih; + } else { + WaveFormat wf; + read(state.audio.fmt); + } + + // seek to the next stream + seek(posOfStreamStart + 8 + strl.length); + + } + + 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); + + printf("nn"); + + } + + bool isValid() const { + return valid; + } + + private: + + uint32_t pos = 0; + + void seek(uint32_t newPos) { + pos = 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(pos, size, (uint8_t*)dst); + pos += size; + } + + }; + +} diff --git a/data/formats/avi/structs.h b/data/formats/avi/structs.h new file mode 100644 index 0000000..84fa208 --- /dev/null +++ b/data/formats/avi/structs.h @@ -0,0 +1,104 @@ +#pragma once + +typedef uint8_t FOURCC[4]; + +struct AVICommonHeader { + FOURCC fourcc; + uint32_t length; +}; + +struct AVITypeHeader { + FOURCC fourcc; + uint32_t length; + FOURCC type; +}; + +struct AVIMainHeader { + uint32_t ms_per_frame; + uint32_t max_bytes_per_sec; + uint32_t padding_granularity; + uint32_t flags; + uint32_t total_frames; + uint32_t initial_frames; + uint32_t streams; + uint32_t suggested_buffer_size; + uint32_t width; + uint32_t height; + uint32_t reserved[4]; +}; + +struct AVIStreamHeader { + //FOURCC fourcc; + //uint32_t length; + FOURCC type; + FOURCC fcc_handler; + uint32_t flags; + uint16_t priority; + uint16_t language; + uint32_t initial_frames; + uint32_t scale; + uint32_t rate; + uint32_t start; + uint32_t length; + uint32_t suggested_buffer_size; + uint32_t quality; + uint32_t sample_size; + struct { + uint16_t left; + uint16_t top; + uint16_t right; + uint16_t bottom; + } rcFrame; +}; + +struct BitMapInfoHeader { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + int16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +struct RGBQUAD { + uint8_t rgbBlue; + uint8_t rgbGreen; + uint8_t rgbRed; + uint8_t rgbReserved; +}; + +struct BitMapInfo { + BitMapInfoHeader bmi_header; + RGBQUAD bmi_colors[1]; +}; + +struct WaveFormat { + uint16_t format_tag; + uint16_t channels; + uint32_t samples_per_sec; + uint32_t avg_bytes_per_sec; + uint16_t block_align; + uint16_t size; +}; + +struct AVIIndexEntry { + uint32_t ckid; + uint32_t flags; + uint32_t chunk_offset; + uint32_t chunk_length; +}; + + + + + +struct ChunkInfo { + uint32_t offset; + uint32_t entry_offset; + uint32_t size; +}; diff --git a/ext/sd/AccessHelper.h b/ext/sd/AccessHelper.h index cd6f9a4..ba889d9 100644 --- a/ext/sd/AccessHelper.h +++ b/ext/sd/AccessHelper.h @@ -31,22 +31,21 @@ public: uint32_t written = 0; LBA512 addrLBA = addr / SEC_SIZE; // LBA address uint16_t offset = (addr - addrLBA*SEC_SIZE);// 0 when addr is SEC_SIZE-byte aligned - uint8_t buf[SEC_SIZE]; while(size) { if (offset || size < SEC_SIZE) { // non-aligned / non-full-block write // read the whole sector - if (!readBlock(addrLBA, buf)) {return written;} + if (!readBlockToCache(addrLBA)) {return written;} // merge in the new data const uint32_t toModify = min(SEC_SIZE-offset, size); - for (uint16_t i = 0; i < toModify; ++i) {buf[i+offset] = src[i+written];} + for (uint16_t i = 0; i < toModify; ++i) {cache.buf[i+offset] = src[i+written];} offset = 0; // write back the modified sector - if (!writeBlock(addrLBA, buf)) {return written;} + if (!writeBlock(addrLBA, cache.buf)) {return written;} ++addrLBA; size -= toModify; @@ -57,6 +56,9 @@ public: // write a full block if (!writeBlock(addrLBA, &src[written])) {return written;} + // written block was in cache? -> drop + if (addrLBA == cache.lba) {cache.lba = 0xFFFFFFFF;} + ++addrLBA; size -= SEC_SIZE; written += SEC_SIZE; @@ -92,7 +94,9 @@ public: } else { // full block read - if (!readBlock(addrLBA, &dst[read])) {return read;} + //if (!readBlock(addrLBA, &dst[read])) {return read;} + if (!readBlockToCache(addrLBA)) {return read;} + memcpy(&dst[read], cache.buf, SEC_SIZE); ++addrLBA; size -= SEC_SIZE; read += SEC_SIZE; @@ -105,17 +109,27 @@ public: } - /** read a single block of SEC_SIZE bytes. addr = byteAddr/512 */ - bool readBlock(LBA512 addr, uint8_t* dst) { - return dev.readBlock(addr, dst); + struct Cache { + LBA512 lba = 0xFFFFFFFF; + uint8_t buf[SEC_SIZE]; + } cache; + + /** read a single block of SEC_SIZE bytes into the CACHE. addr = byteAddr/512 */ + bool readBlockToCache(LBA512 addr) { + if (cache.lba == addr) {return true;} // already cached + if (dev.readBlock(addr, cache.buf)) { + cache.lba = addr; + return true; + } + return false; } /** read a single block of SEC_SIZE bytes. addr = byteAddr/512, write only a fraction of the 512 bytes into dst (skip+len) */ bool readBlock(LBA512 addr, uint8_t* dst, uint16_t skip, uint16_t len) { - uint8_t buf[SEC_SIZE]; - if (!dev.readBlock(addr, buf)) {return false;} + + if (!readBlockToCache(addr)) {return false;} for (int i = 0; i < len; ++i) { - *dst = buf[i+skip]; + *dst = cache.buf[i+skip]; ++dst; } return true; diff --git a/ext/sd/CMakeLists.txt b/ext/sd/CMakeLists.txt index cfe3668..851b02f 100755 --- a/ext/sd/CMakeLists.txt +++ b/ext/sd/CMakeLists.txt @@ -19,6 +19,9 @@ INCLUDE_DIRECTORIES( FILE(GLOB HEADERS ./*.h ./*/*.h + + /apps/ESP8266lib/data/formats/avi/Demuxer.h + /apps/ESP8266lib/data/formats/avi/structs.h ) diff --git a/ext/sd/main.cpp b/ext/sd/main.cpp index 06487b5..3c1e6a6 100644 --- a/ext/sd/main.cpp +++ b/ext/sd/main.cpp @@ -81,8 +81,25 @@ public: //#define logInfo(fmt, ...) std::string s = fmt::format(FMT_STRING(fmt), __VA_ARGS__); +#include "/apps/ESP8266lib/data/formats/avi/Demuxer.h" + int main(int argc, char** argv) { + { + + // ffmpeg -i '/mnt/ssss/anime/Toaru Majutsu no Index/[Eclipse] Toaru Majutsu no Index - 07 (1280x720 h264) [0255D83A].mkv' -r 15 -ss 30 -vf scale=480x320 -c:v mjpeg -c:a mp3 -ar 44100 /tmp/ram/1.avi + + Simu simu("/tmp/ram/1.avi"); + AccessHelper ah(simu); + AVI::Demuxer demux(ah); + + bool valid = demux.isValid(); + std::cout << valid << std::endl; + + return 0; + + } + //logInfo("{:s}", "I am not a number") //std::string s = fmt::format(FMT_STRING("{:s}"), "I am not a number");