diff options
Diffstat (limited to 'Src/nsavi/metadata.cpp')
-rw-r--r-- | Src/nsavi/metadata.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/Src/nsavi/metadata.cpp b/Src/nsavi/metadata.cpp new file mode 100644 index 00000000..8ffedb56 --- /dev/null +++ b/Src/nsavi/metadata.cpp @@ -0,0 +1,314 @@ +#include "Metadata.h" + +nsavi::Metadata::Metadata(nsavi::avi_reader *_reader) : ParserBase(_reader) +{ + info_found = NOT_READ; + info = 0; +} + + +// 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; +} +// 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; +} + +int nsavi::Metadata::GetDuration(int *time_ms) +{ + uint32_t riff_type; + int ret = GetRIFFType(&riff_type); + if (ret) + return ret; + + if (riff_type != ' IVA') + return READ_INVALID_DATA; + + nsavi::HeaderList header_list; + ret = GetHeaderList(&header_list); + if (ret) + return ret; + + int duration=-1; + for (uint32_t i=0;i!=header_list.stream_list_size;i++) + { + const nsavi::STRL &stream = header_list.stream_list[i]; + if (stream.stream_header) + { + if (stream.stream_header->stream_type == nsavi::stream_type_audio) + { + if (stream.stream_header->length && !stream.stream_header->sample_size && stream.stream_header->rate) + duration = (uint64_t)stream.stream_header->length * (uint64_t)stream.stream_header->scale * 1000ULL / (uint64_t)stream.stream_header->rate; + } + else if (stream.stream_header->stream_type == nsavi::stream_type_video && stream.stream_header->rate) + { + if (duration == -1) + duration = (uint64_t)stream.stream_header->length * (uint64_t)stream.stream_header->scale * 1000ULL / (uint64_t)stream.stream_header->rate; + } + } + } + + *time_ms = duration; + + return nsavi::READ_OK; +} + +int nsavi::Metadata::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; + } + + info_found = PARSED; + } + else + { + ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read); + if (ret) + return ret; + bytes_available -= bytes_read; + } + break; + 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; + } + } + } + + 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::Metadata::GetInfo(nsavi::Info **out_info) +{ +if (riff_parsed != PARSED) + return READ_INVALID_CALL; + + if (riff_parsed == PARSE_RESYNC) + reader->Seek(riff_start); + + if (info_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 (bytes_available) + { + if (bytes_available < 8) + { + info_found = NOT_FOUND; + return READ_NOT_FOUND; + } + uint32_t bytes_read; + riff_chunk chunk; + int ret = ReadChunk(reader, &chunk, info_found, &bytes_read); + if (ret) + return ret; + + bytes_available -= bytes_read; + if (bytes_available < chunk.size) + { + info_found = PARSE_ERROR; + return READ_INVALID_DATA; + } + switch(chunk.id) + { + case 'TSIL': // list chunk + switch(chunk.type) + { + case 'lrdh': // parse this if we havn't already + if (header_list_parsed != PARSED) + { + ret = ParseHeaderList(chunk.size, &bytes_read); + if (ret) + return ret; + bytes_available -= bytes_read; + header_list_parsed = PARSED; + } + else + { + ret = SkipChunk(reader, &chunk, info_found, &bytes_read); + if (ret) + return ret; + bytes_available -= bytes_read; + } + break; + case 'OFNI': // INFO + if (!info) + { + info = new nsavi::Info(); + if (!info) + { + info_found = PARSE_ERROR; + return READ_OUT_OF_MEMORY; + } + + ret = info->Read(reader, chunk.size); + if (ret) + { + header_list_parsed = PARSE_ERROR; + return ret; + } + + info_found = PARSED; + *out_info = info; + return nsavi::READ_OK; + } + else + { + ret = SkipChunk(reader, &chunk, info_found, &bytes_read); + if (ret) + return ret; + bytes_available -= bytes_read; + } + break; + default: // skip anything we don't understand + ret = SkipChunk(reader, &chunk, info_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, &chunk, info_found, &bytes_read); + if (ret) + return ret; + bytes_available -= bytes_read; + break; + } + } + } + + if (info_found == PARSED) + { + *out_info = info; + return READ_OK; + } + + return READ_INVALID_CALL; +}
\ No newline at end of file |