Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
565
Src/nsavi/demuxer.cpp
Normal file
565
Src/nsavi/demuxer.cpp
Normal file
|
@ -0,0 +1,565 @@
|
|||
#include "demuxer.h"
|
||||
#include "read.h"
|
||||
#include "avi_reader.h"
|
||||
|
||||
static int GetStreamNumber(uint32_t id)
|
||||
{
|
||||
char *stream_data = (char *)(&id);
|
||||
if (!isxdigit(stream_data[0]) || !isxdigit(stream_data[1]))
|
||||
return -1;
|
||||
|
||||
stream_data[2] = 0;
|
||||
int stream_number = strtoul(stream_data, 0, 16);
|
||||
return stream_number;
|
||||
}
|
||||
|
||||
nsavi::Demuxer::Demuxer(nsavi::avi_reader *_reader) : ParserBase(_reader)
|
||||
{
|
||||
movie_found = NOT_READ;
|
||||
idx1_found = NOT_READ;
|
||||
info_found = NOT_READ;
|
||||
movie_start = 0;
|
||||
index = 0;
|
||||
info = 0;
|
||||
}
|
||||
|
||||
// reads a chunk and updates parse state variable on error
|
||||
static int ReadChunk(nsavi::avi_reader *reader, nsavi::riff_chunk *chunk, nsavi::ParseState &state, uint32_t *bytes_read)
|
||||
{
|
||||
int ret = nsavi::read_riff_chunk(reader, chunk, bytes_read);
|
||||
if (ret == nsavi::READ_EOF)
|
||||
{
|
||||
state = nsavi::NOT_FOUND;
|
||||
return nsavi::READ_NOT_FOUND;
|
||||
}
|
||||
else if (ret > nsavi::READ_OK)
|
||||
{
|
||||
state = nsavi::PARSE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
else if (ret < nsavi::READ_OK)
|
||||
{ // pass-thru return value from avi_reader
|
||||
state = nsavi::PARSE_RESYNC;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nsavi::READ_OK;
|
||||
}
|
||||
|
||||
// skips a chunk and updates a parser state variable on error
|
||||
static int SkipChunk(nsavi::avi_reader *reader, const nsavi::riff_chunk *chunk, nsavi::ParseState &state, uint32_t *bytes_read)
|
||||
{
|
||||
int ret = nsavi::skip_chunk(reader, chunk, bytes_read);
|
||||
if (ret == nsavi::READ_EOF)
|
||||
{
|
||||
state = nsavi::NOT_FOUND;
|
||||
return nsavi::READ_NOT_FOUND;
|
||||
}
|
||||
else if (ret > nsavi::READ_OK)
|
||||
{
|
||||
state = nsavi::PARSE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
else if (ret < nsavi::READ_OK)
|
||||
{ // pass-thru return value from avi_reader
|
||||
state = nsavi::PARSE_RESYNC;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nsavi::READ_OK;
|
||||
}
|
||||
|
||||
static int Read(nsavi::avi_reader *reader, void *buffer, uint32_t size, nsavi::ParseState &state, uint32_t *out_bytes_read)
|
||||
{
|
||||
uint32_t bytes_read;
|
||||
int ret = reader->Read(buffer, size, &bytes_read);
|
||||
if (ret > nsavi::READ_OK)
|
||||
{
|
||||
state = nsavi::PARSE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
else if (ret < nsavi::READ_OK)
|
||||
{ // pass-thru return value from avi_reader
|
||||
state = nsavi::PARSE_RESYNC;
|
||||
return ret;
|
||||
}
|
||||
else if (bytes_read != size)
|
||||
{
|
||||
state = nsavi::PARSE_ERROR;
|
||||
return nsavi::READ_EOF;
|
||||
}
|
||||
*out_bytes_read = bytes_read;
|
||||
return nsavi::READ_OK;
|
||||
}
|
||||
|
||||
int nsavi::Demuxer::GetHeaderList(HeaderList *header_list)
|
||||
{
|
||||
if (riff_parsed != PARSED)
|
||||
return READ_INVALID_CALL;
|
||||
|
||||
if (riff_parsed == PARSE_RESYNC)
|
||||
reader->Seek(riff_start);
|
||||
|
||||
if (header_list_parsed == NOT_READ)
|
||||
{
|
||||
// first, see how far we are into the file to properly bound our reads
|
||||
uint64_t start = reader->Tell();
|
||||
uint32_t bytes_available = riff_header.size;
|
||||
bytes_available -= (uint32_t)(start - riff_start);
|
||||
|
||||
while (bytes_available)
|
||||
{
|
||||
if (bytes_available < 8)
|
||||
{
|
||||
header_list_parsed = NOT_FOUND;
|
||||
return READ_NOT_FOUND;
|
||||
}
|
||||
uint32_t bytes_read;
|
||||
riff_chunk chunk;
|
||||
int ret = ReadChunk(reader, &chunk, header_list_parsed, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bytes_available -= bytes_read;
|
||||
if (bytes_available < chunk.size)
|
||||
{
|
||||
header_list_parsed = PARSE_ERROR;
|
||||
return READ_INVALID_DATA;
|
||||
}
|
||||
switch(chunk.id)
|
||||
{
|
||||
case 'TSIL': // list chunk
|
||||
switch(chunk.type)
|
||||
{
|
||||
case 'lrdh': // this is what we're looking for
|
||||
ret = ParseHeaderList(chunk.size, &bytes_read);
|
||||
if (ret == READ_OK)
|
||||
{
|
||||
header_list->avi_header = avi_header;
|
||||
header_list->stream_list = stream_list;
|
||||
header_list->stream_list_size = stream_list_size;
|
||||
header_list->odml_header = odml_header;
|
||||
}
|
||||
return ret;
|
||||
case 'OFNI': // INFO
|
||||
if (!info)
|
||||
{
|
||||
info = new nsavi::Info();
|
||||
if (!info)
|
||||
{
|
||||
header_list_parsed = PARSE_ERROR;
|
||||
return READ_OUT_OF_MEMORY;
|
||||
}
|
||||
ret = info->Read(reader, chunk.size);
|
||||
if (ret)
|
||||
{
|
||||
header_list_parsed = PARSE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
default: // skip anything we don't understand
|
||||
ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
bytes_available -= bytes_read;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default: // skip anything we don't understand
|
||||
case 'KNUJ': // skip junk chunks
|
||||
ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
bytes_available -= bytes_read;
|
||||
break;
|
||||
// TODO; case '1xdi': break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (header_list_parsed == PARSED)
|
||||
{
|
||||
header_list->avi_header = avi_header;
|
||||
header_list->stream_list = stream_list;
|
||||
header_list->stream_list_size = stream_list_size;
|
||||
header_list->odml_header = odml_header;
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
return READ_INVALID_CALL;
|
||||
}
|
||||
|
||||
int nsavi::Demuxer::FindMovieChunk()
|
||||
{
|
||||
if (riff_parsed != PARSED)
|
||||
return READ_INVALID_CALL;
|
||||
|
||||
if (header_list_parsed != READ_OK)
|
||||
return READ_INVALID_CALL;
|
||||
|
||||
if (movie_found == PARSED)
|
||||
return READ_OK;
|
||||
|
||||
if (movie_found == NOT_READ)
|
||||
{
|
||||
// first, see how far we are into the file to properly bound our reads
|
||||
uint64_t start = reader->Tell();
|
||||
uint32_t bytes_available = riff_header.size;
|
||||
bytes_available -= (uint32_t)(start - riff_start);
|
||||
while (movie_found == NOT_READ)
|
||||
{
|
||||
if (bytes_available < 8)
|
||||
{
|
||||
header_list_parsed = NOT_FOUND;
|
||||
return READ_NOT_FOUND;
|
||||
}
|
||||
uint32_t bytes_read;
|
||||
int ret = ReadChunk(reader, &movi_header, movie_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bytes_available -= bytes_read;
|
||||
if (bytes_available < movi_header.size)
|
||||
{
|
||||
movie_found = PARSE_ERROR;
|
||||
return READ_INVALID_DATA;
|
||||
}
|
||||
switch(movi_header.id)
|
||||
{
|
||||
// TODO: parse any other interesting chunks along the way
|
||||
case 'TSIL': // list chunk
|
||||
switch(movi_header.type)
|
||||
{
|
||||
case 'ivom':
|
||||
{
|
||||
movie_found = PARSED;
|
||||
movie_start = reader->Tell();
|
||||
return READ_OK;
|
||||
}
|
||||
break;
|
||||
case '1xdi': // index v1 chunk
|
||||
if (!index)
|
||||
{
|
||||
index = (nsavi::IDX1 *)malloc(idx1_header.size + sizeof(uint32_t));
|
||||
if (index)
|
||||
{
|
||||
ret = Read(reader, ((uint8_t *)index) + sizeof(uint32_t), idx1_header.size, idx1_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bytes_available-=bytes_read;
|
||||
index->index_count = idx1_header.size / sizeof(IDX1_INDEX);
|
||||
if ((idx1_header.size & 1) && bytes_available)
|
||||
{
|
||||
bytes_available--;
|
||||
reader->Skip(1);
|
||||
}
|
||||
idx1_found = PARSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
return READ_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SkipChunk(reader, &movi_header, movie_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
bytes_available -= bytes_read;
|
||||
}
|
||||
break;
|
||||
case 'OFNI': // INFO
|
||||
if (!info)
|
||||
{
|
||||
info = new nsavi::Info();
|
||||
if (!info)
|
||||
{
|
||||
movie_found = PARSE_ERROR;
|
||||
return READ_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
ret = info->Read(reader, movi_header.size);
|
||||
if (ret)
|
||||
{
|
||||
movie_found = PARSE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
default: // skip anything we don't understand
|
||||
ret = SkipChunk(reader, &movi_header, movie_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
bytes_available -= bytes_read;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: // skip anything we don't understand
|
||||
case 'KNUJ': // skip junk chunks
|
||||
ret = SkipChunk(reader, &movi_header, movie_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
bytes_available -= bytes_read;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nsavi::READ_NOT_FOUND; // TODO: not sure about this
|
||||
}
|
||||
int nsavi::Demuxer::SeekToMovieChunk(nsavi::avi_reader *reader)
|
||||
{
|
||||
return reader->Seek(movie_start);
|
||||
}
|
||||
|
||||
int nsavi::Demuxer::GetNextMovieChunk(nsavi::avi_reader *reader, void **data, uint32_t *chunk_size, uint32_t *chunk_type, int limit_stream_num)
|
||||
{
|
||||
ParseState no_state;
|
||||
if (movie_found == PARSED)
|
||||
{
|
||||
uint64_t start = reader->Tell();
|
||||
uint32_t bytes_available = movi_header.size;
|
||||
bytes_available -= (uint32_t)(start - movie_start);
|
||||
|
||||
uint32_t bytes_read;
|
||||
riff_chunk chunk;
|
||||
again:
|
||||
int ret = ReadChunk(reader, &chunk, no_state, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (chunk.id == 'TSIL' || chunk.id == 'FFIR')
|
||||
{
|
||||
goto again; // skip 'rec' chunk headers
|
||||
}
|
||||
if (chunk.id == 'KNUJ' || chunk.id == '1xdi')
|
||||
{
|
||||
SkipChunk(reader, &chunk, no_state, &bytes_read);
|
||||
goto again;
|
||||
|
||||
}
|
||||
if (limit_stream_num != 65536)
|
||||
{
|
||||
if (limit_stream_num != GetStreamNumber(chunk.id))
|
||||
{
|
||||
SkipChunk(reader, &chunk, no_state, &bytes_read);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
*data = malloc(chunk.size);
|
||||
if (!*data)
|
||||
return READ_OUT_OF_MEMORY;
|
||||
*chunk_size = chunk.size;
|
||||
*chunk_type = chunk.id;
|
||||
|
||||
|
||||
ret = Read(reader, *data, chunk.size, no_state, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((chunk.size & 1))
|
||||
{
|
||||
bytes_available--;
|
||||
reader->Skip(1);
|
||||
}
|
||||
return READ_OK;
|
||||
}
|
||||
else
|
||||
return READ_FAILED;
|
||||
|
||||
}
|
||||
|
||||
int nsavi::Demuxer::GetSeekTable(nsavi::IDX1 **out_index)
|
||||
{
|
||||
if (idx1_found == PARSED)
|
||||
{
|
||||
*out_index = index;
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
if (idx1_found == NOT_FOUND)
|
||||
{
|
||||
return READ_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (idx1_found != NOT_READ)
|
||||
return READ_FAILED;
|
||||
|
||||
uint64_t old_position = reader->Tell();
|
||||
|
||||
if (movie_found == PARSED)
|
||||
reader->Seek(movie_start+movi_header.size);
|
||||
else
|
||||
reader->Seek(riff_start);
|
||||
|
||||
uint64_t start = reader->Tell();
|
||||
uint32_t bytes_available = riff_header.size;
|
||||
bytes_available -= (uint32_t)(start - riff_start);
|
||||
|
||||
while (idx1_found == NOT_READ)
|
||||
{
|
||||
if (bytes_available < 8)
|
||||
{
|
||||
idx1_found = NOT_FOUND;
|
||||
reader->Seek(old_position);
|
||||
return READ_NOT_FOUND;
|
||||
}
|
||||
uint32_t bytes_read;
|
||||
int ret = ReadChunk(reader, &idx1_header, idx1_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bytes_available -= bytes_read;
|
||||
if (bytes_available == (idx1_header.size - 12)) // some stupid program has this bug
|
||||
{
|
||||
idx1_header.size-=12;
|
||||
}
|
||||
if (bytes_available < idx1_header.size)
|
||||
{
|
||||
idx1_found = PARSE_ERROR;
|
||||
reader->Seek(old_position);
|
||||
return READ_INVALID_DATA;
|
||||
}
|
||||
switch(idx1_header.id)
|
||||
{
|
||||
// TODO: parse any other interesting chunks along the way
|
||||
case '1xdi': // index v1 chunk
|
||||
index = (nsavi::IDX1 *)malloc(idx1_header.size + sizeof(uint32_t));
|
||||
if (index)
|
||||
{
|
||||
ret = Read(reader, ((uint8_t *)index) + sizeof(uint32_t), idx1_header.size, idx1_found, &bytes_read);
|
||||
if (ret)
|
||||
{
|
||||
reader->Seek(old_position);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes_available-=bytes_read;
|
||||
index->index_count = idx1_header.size / sizeof(IDX1_INDEX);
|
||||
if ((idx1_header.size & 1) && bytes_available)
|
||||
{
|
||||
bytes_available--;
|
||||
reader->Skip(1);
|
||||
}
|
||||
idx1_found = PARSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->Seek(old_position);
|
||||
return READ_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
break;
|
||||
default: // skip anything we don't understand
|
||||
case 'KNUJ': // skip junk chunks
|
||||
ret = SkipChunk(reader, &idx1_header, idx1_found, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
bytes_available -= bytes_read;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out_index = index;
|
||||
reader->Seek(old_position);
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
int nsavi::Demuxer::GetIndexChunk(nsavi::INDX **out_index, uint64_t offset)
|
||||
{
|
||||
nsavi::INDX *index = 0;
|
||||
uint64_t old_position = reader->Tell();
|
||||
reader->Seek(offset);
|
||||
ParseState dummy;
|
||||
uint32_t bytes_read;
|
||||
riff_chunk chunk;
|
||||
int ret = ReadChunk(reader, &chunk, dummy, &bytes_read);
|
||||
if (ret)
|
||||
return ret;
|
||||
index = (nsavi::INDX *)malloc(sizeof(uint32_t) + chunk.size);
|
||||
if (index)
|
||||
{
|
||||
ret = Read(reader, ((uint8_t *)index) + sizeof(uint32_t), chunk.size, dummy, &bytes_read);
|
||||
if (ret)
|
||||
{
|
||||
reader->Seek(old_position);
|
||||
return ret;
|
||||
}
|
||||
index->size_bytes=chunk.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->Seek(old_position);
|
||||
return READ_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
*out_index = index;
|
||||
reader->Seek(old_position);
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
static bool IsCodecChunk(uint32_t header)
|
||||
{
|
||||
char *blah = (char *)&header;
|
||||
if (blah[0] != 'i' && !isxdigit(blah[0]))
|
||||
return false;
|
||||
if (blah[1] != 'x' && !isxdigit(blah[1]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int nsavi::Demuxer::Seek(uint64_t offset, bool absolute, nsavi::avi_reader *reader)
|
||||
{
|
||||
/* verify index by reading the riff chunk and comparing position->chunk_id and position->size with the read chunk
|
||||
if it fails, we'll try the two following things
|
||||
1) try again without the -4
|
||||
2) try from the start of the file
|
||||
3) try from riff_start
|
||||
*/
|
||||
uint32_t bytes_read;
|
||||
uint32_t chunk_header=0;
|
||||
if (!reader)
|
||||
reader = this->reader;
|
||||
if (absolute)
|
||||
{
|
||||
reader->Seek(offset - 8);
|
||||
reader->Peek(&chunk_header, 4, &bytes_read);
|
||||
if (!IsCodecChunk(chunk_header))
|
||||
{
|
||||
reader->Skip(4);
|
||||
reader->Peek(&chunk_header, 4, &bytes_read);
|
||||
if (!IsCodecChunk(chunk_header))
|
||||
{
|
||||
reader->Skip(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->Seek(movie_start+offset - 4);
|
||||
reader->Peek(&chunk_header, 4, &bytes_read);
|
||||
if (!IsCodecChunk(chunk_header))
|
||||
{
|
||||
reader->Seek(offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
riff_chunk test;
|
||||
ParseState blah;
|
||||
uint32_t bytes_read;
|
||||
ReadChunk(f, &test, blah, &bytes_read);
|
||||
fseek64(f, movie_start+position->offset - 4, SEEK_SET);
|
||||
*/
|
||||
return READ_OK;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue