123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- #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;
- }
|