REDRIVER2/src_rebuild/utils/video_source/ReadAVI.cpp
Ilya Shurumov 481856c02a - fix DealWithHorn
- disable ReadAVI verbosity
2020-09-22 02:31:50 +06:00

709 lines
20 KiB
C++

/*
* ReadAVI.cpp
*
*
* Copyright (c) 2004-2013, Michael Kohn <mike@mikekohn.net> http://www.mikekohn.net/
*
* Copyright (c) 2018, olegvedi@gmail.com (C++ implementation and adds)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the author nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "ReadAVI.h"
#include <iostream>
#include <string.h>
#define ZEROIZE(x) {memset(&x, 0, sizeof(x));}
using namespace std;
// #define VERBOSE
#define DBG_LVL_DEF 0
#ifdef VERBOSE
#define MSG_DBG(dl, format, ...) {if(dl <= DBG_LVL_DEF ) fprintf(stdout, format ,##__VA_ARGS__);}
#else
#define MSG_DBG(dl, format, ...)
#endif
ReadAVI::chunk_type_int_t ReadAVI::chunk_types[ChunkTypesCnt] = {
{"db", ctype_uncompressed_video_frame },
{"dc", ctype_compressed_video_frame },
{"pc", ctype_palette_change },
{"wb", ctype_audio_data } };
ReadAVI::ReadAVI(const char* filename)
{
data_buf = NULL;
stream_format_vid.palette = NULL;
data_buf_size = 0;
//inFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
inFile.open(filename, ios_base::ate | ios_base::in | ios_base::binary);
fileSize = inFile.tellg();
inFile.close();
inFile.open(filename, ios_base::in | ios_base::binary);
try {
parse_riff();
}
catch (std::system_error& e) { //file corrupted?
std::cerr << e.code().message().c_str() << "\n";
if (index_entries.size() == 0) {
free();
throw;
}
}
}
ReadAVI::~ReadAVI()
{
free();
}
void ReadAVI::free()
{
if (inFile.is_open()) {
inFile.close();
}
delete[] stream_format_vid.palette;
delete[] data_buf;
}
void ReadAVI::check_data_buf(unsigned size)
{
if (data_buf_size < size) {
delete[] data_buf;
data_buf = new unsigned char[size + 1];
data_buf_size = size;
}
}
int ReadAVI::GetFrameFromIndex(frame_entry_t* frame_entry)
{
if (frame_entry->pointer >= index_entries.size()) {
MSG_DBG(2, "Pointer out of range\n");
return -5;
}
for (unsigned i = frame_entry->pointer; i < index_entries.size(); i++) {
if (index_entries[i].type & frame_entry->type) {
check_data_buf(index_entries[i].dwChunkLength);
try {
inFile.seekg(index_entries[i].dwChunkOffset, ios_base::beg);
read_chars_bin(data_buf, index_entries[i].dwChunkLength);
}
catch (...) {
return -1;
}
frame_entry->buf = data_buf;
frame_entry->pointer = i + 1;
frame_entry->stream_num = index_entries[i].stream_num;
frame_entry->type = index_entries[i].type;
return index_entries[i].dwChunkLength;
}
}
MSG_DBG(2, "No more frames\n");
return -1;
}
#ifdef VERBOSE
int ReadAVI::hex_dump_chunk(int chunk_len)
{
char chars[17];
unsigned char c;
int ch, n;
chars[16] = 0;
for (n = 0; n < chunk_len; n++) {
if ((n % 16) == 0) {
if (n != 0)
MSG_DBG(2, "%s\n", chars);
MSG_DBG(2, " ");
memset(chars, ' ', 16);
}
read_chars_bin(&c, 1);
ch = c;
if (ch == EOF)
break;
MSG_DBG(2, "%02x ", ch);
if (ch >= ' ' && ch <= 126) {
chars[n % 16] = ch;
}
else {
chars[n % 16] = '.';
}
}
if ((n % 16) != 0) {
for (ch = n % 16; ch < 16; ch++) {
MSG_DBG(2, " ");
}
}
MSG_DBG(2, "%s\n", chars);
return 0;
}
#endif
int ReadAVI::decodeCkid(char* ckid, chunk_type_t* chunk_type)
{
int stream = (ckid[0] - '0') * 10 + (ckid[1] - '0');
for (int i = 0; i < ChunkTypesCnt; i++) {
if (chunk_types[i].type_id[0] == ckid[2] && chunk_types[i].type_id[1] == ckid[3]) {
*chunk_type = chunk_types[i].type;
return stream;
}
}
return -1;
}
int ReadAVI::parse_idx1(int chunk_len)
{
index_entry_t index_entry;
index_entry.type = ctype_none;
int t;
MSG_DBG(2, " IDX1\n");
MSG_DBG(2, " -------------------------------\n");
MSG_DBG(2, " stream type dwFlags dwChunkOffset dwChunkLength\n");
for (t = 0; t < chunk_len / 16; t++) {
char buf[5];
read_chars(buf, 4);
index_entry.stream_num = decodeCkid(buf, &index_entry.type);
index_entry.dwFlags = read_int();
index_entry.dwChunkOffset = read_int() + movi_offset + 0x10;
index_entry.dwChunkLength = read_int();
MSG_DBG(2, " %d %d 0x%08x 0x%08x 0x%08x\n", index_entry.stream_num, index_entry.type,
index_entry.dwFlags, index_entry.dwChunkOffset, index_entry.dwChunkLength);
index_entries.push_back(index_entry);
}
MSG_DBG(2, "\n");
return 0;
}
int ReadAVI::read_avi_header()
{
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
avi_header.TimeBetweenFrames = read_int();
avi_header.MaximumDataRate = read_int();
avi_header.PaddingGranularity = read_int();
avi_header.Flags = read_int();
avi_header.TotalNumberOfFrames = read_int();
avi_header.NumberOfInitialFrames = read_int();
avi_header.NumberOfStreams = read_int();
avi_header.SuggestedBufferSize = read_int();
avi_header.Width = read_int();
avi_header.Height = read_int();
avi_header.TimeScale = read_int();
avi_header.DataRate = read_int();
avi_header.StartTime = read_int();
avi_header.DataLength = read_int();
MSG_DBG(2, " offset=0x%lx\n", offset);
MSG_DBG(2, " TimeBetweenFrames: %d\n", avi_header.TimeBetweenFrames);
MSG_DBG(2, " MaximumDataRate: %d\n", avi_header.MaximumDataRate);
MSG_DBG(2, " PaddingGranularity: %d\n", avi_header.PaddingGranularity);
MSG_DBG(2, " Flags: %d\n", avi_header.Flags);
MSG_DBG(2, " TotalNumberOfFrames: %d\n", avi_header.TotalNumberOfFrames);
MSG_DBG(2, " NumberOfInitialFrames: %d\n", avi_header.NumberOfInitialFrames);
MSG_DBG(2, " NumberOfStreams: %d\n", avi_header.NumberOfStreams);
MSG_DBG(2, " SuggestedBufferSize: %d\n", avi_header.SuggestedBufferSize);
MSG_DBG(2, " Width: %d\n", avi_header.Width);
MSG_DBG(2, " Height: %d\n", avi_header.Height);
MSG_DBG(2, " TimeScale: %d\n", avi_header.TimeScale);
MSG_DBG(2, " DataRate: %d\n", avi_header.DataRate);
MSG_DBG(2, " StartTime: %d\n", avi_header.StartTime);
MSG_DBG(2, " DataLength: %d\n", avi_header.DataLength);
return 0;
}
static void print_data_handler(unsigned char* handler)
{
int t;
for (t = 0; t < 4; t++) {
if ((handler[t] >= 'a' && handler[t] <= 'z') || (handler[t] >= 'A' && handler[t] <= 'Z')
|| (handler[t] >= '0' && handler[t] <= '9'))
{
MSG_DBG(2, "%c", handler[t]);
}
else {
MSG_DBG(2, "[0x%02x]", handler[t]);
}
}
}
int ReadAVI::read_stream_header(stream_header_t* sheader)
{
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
read_chars(sheader->DataType, 4);
read_chars(sheader->DataHandler, 4);
sheader->Flags = read_int();
sheader->Priority = read_int();
sheader->InitialFrames = read_int();
sheader->TimeScale = read_int();
sheader->DataRate = read_int();
sheader->StartTime = read_int();
sheader->DataLength = read_int();
sheader->SuggestedBufferSize = read_int();
sheader->Quality = read_int();
sheader->SampleSize = read_int();
MSG_DBG(2, " offset=0x%lx\n", offset);
MSG_DBG(2, " DataType: %s\n", sheader->DataType);
MSG_DBG(2, " DataHandler: ");
print_data_handler((unsigned char*)sheader->DataHandler);
MSG_DBG(2, "\n");
MSG_DBG(2, " Flags: %d\n", sheader->Flags);
MSG_DBG(2, " Priority: %d\n", sheader->Priority);
MSG_DBG(2, " InitialFrames: %d\n", sheader->InitialFrames);
MSG_DBG(2, " TimeScale: %d\n", sheader->TimeScale);
MSG_DBG(2, " DataRate: %d\n", sheader->DataRate);
MSG_DBG(2, " StartTime: %d\n", sheader->StartTime);
MSG_DBG(2, " DataLength: %d\n", sheader->DataLength);
MSG_DBG(2, " SuggestedBufferSize: %d\n", sheader->SuggestedBufferSize);
MSG_DBG(2, " Quality: %d\n", sheader->Quality);
MSG_DBG(2, " SampleSize: %d\n", sheader->SampleSize);
return 0;
}
int ReadAVI::read_stream_format()
{
int t, r, g, b;
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
stream_format_vid.header_size = read_int();
stream_format_vid.image_width = read_int();
stream_format_vid.image_height = read_int();
stream_format_vid.number_of_planes = read_word();
stream_format_vid.bits_per_pixel = read_word();
read_chars(stream_format_vid.compression_type, 4);
//stream_format_vid.compression_type = read_int();
stream_format_vid.image_size_in_bytes = read_int();
stream_format_vid.x_pels_per_meter = read_int();
stream_format_vid.y_pels_per_meter = read_int();
stream_format_vid.colors_used = read_int();
stream_format_vid.colors_important = read_int();
stream_format_vid.palette = 0;
if (stream_format_vid.colors_important != 0) {
stream_format_vid.palette = new int[stream_format_vid.colors_important];
for (t = 0; t < stream_format_vid.colors_important; t++) {
unsigned char buf[3];
read_chars_bin(buf, 3);
b = buf[0];
g = buf[1];
r = buf[2];
stream_format_vid.palette[t] = (r << 16) + (g << 8) + b;
}
}
MSG_DBG(2, " offset=0x%lx\n", offset);
MSG_DBG(2, " header_size: %d\n", stream_format_vid.header_size);
MSG_DBG(2, " image_width: %d\n", stream_format_vid.image_width);
MSG_DBG(2, " image_height: %d\n", stream_format_vid.image_height);
MSG_DBG(2, " number_of_planes: %d\n", stream_format_vid.number_of_planes);
MSG_DBG(2, " bits_per_pixel: %d\n", stream_format_vid.bits_per_pixel);
MSG_DBG(2, " compression_type: %s\n", stream_format_vid.compression_type);
/* MSG_DBG(2, " compression_type: %04x (%c%c%c%c)\n", stream_format_vid.compression_type,
((stream_format_vid.compression_type) & 255), ((stream_format_vid.compression_type >> 8) & 255),
((stream_format_vid.compression_type >> 16) & 255), ((stream_format_vid.compression_type >> 24) & 255));
*/
MSG_DBG(2, " image_size_in_bytes: %d\n", stream_format_vid.image_size_in_bytes);
MSG_DBG(2, " x_pels_per_meter: %d\n", stream_format_vid.x_pels_per_meter);
MSG_DBG(2, " y_pels_per_meter: %d\n", stream_format_vid.y_pels_per_meter);
MSG_DBG(2, " colors_used: %d\n", stream_format_vid.colors_used);
MSG_DBG(2, " colors_important: %d\n", stream_format_vid.colors_important);
return 0;
}
int ReadAVI::read_stream_format_auds()
{
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
stream_format_auds.format = read_word();
stream_format_auds.channels = read_word();
stream_format_auds.samples_per_second = read_int();
stream_format_auds.bytes_per_second = read_int();
int block_align = read_word();
stream_format_auds.block_size_of_data = read_word();
stream_format_auds.bits_per_sample = read_word();
//stream_format_auds.extended_size=read_word();
MSG_DBG(2, " offset=0x%lx\n", offset);
MSG_DBG(2, " format: %d\n", stream_format_auds.format);
MSG_DBG(2, " channels: %d\n", stream_format_auds.channels);
MSG_DBG(2, " samples_per_second: %d\n", stream_format_auds.samples_per_second);
MSG_DBG(2, " bytes_per_second: %d\n", stream_format_auds.bytes_per_second);
MSG_DBG(2, " block_align: %d\n", block_align);
MSG_DBG(2, " block_size_of_data: %d\n", stream_format_auds.block_size_of_data);
MSG_DBG(2, " bits_per_sample: %d\n", stream_format_auds.bits_per_sample);
return 0;
}
int ReadAVI::parse_hdrl_list()
{
char chunk_id[5];
int chunk_size;
char chunk_type[5];
int end_of_chunk;
int next_chunk;
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
int stream_type = 0; // 0=video 1=sound
read_chars(chunk_id, 4);
chunk_size = read_int();
read_chars(chunk_type, 4);
MSG_DBG(2, " AVI Header LIST (id=%s size=%d type=%s offset=0x%lx)\n", chunk_id, chunk_size, chunk_type, offset);
MSG_DBG(2, " {\n");
end_of_chunk = chunk_size - 4 + inFile.tellg();
if ((end_of_chunk % 4) != 0) {
//MSG_DBG(2, "Adjusting end of chunk %d\n", end_of_chunk);
//end_of_chunk=end_of_chunk+(4-(end_of_chunk%4));
//MSG_DBG(2, "Adjusting end of chunk %d\n", end_of_chunk);
}
if (strcmp(chunk_id, "JUNK") == 0) {
inFile.seekg(end_of_chunk, ios_base::beg);
MSG_DBG(2, " }\n");
return 0;
}
while (inFile.tellg() < end_of_chunk) {
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
read_chars(chunk_type, 4);
chunk_size = read_int();
next_chunk = chunk_size + inFile.tellg();
if ((chunk_size % 4) != 0) {
//MSG_DBG(2, "Chunk size not a multiple of 4?\n");
//chunk_size=chunk_size+(4-(chunk_size%4));
}
MSG_DBG(2, " %.4s (size=%d offset=0x%lx)\n", chunk_type, chunk_size, offset);
MSG_DBG(2, " {\n");
if (strcmp("strh", chunk_type) == 0)
{
long marker = inFile.tellg();
char buffer[5];
read_chars(buffer, 4);
inFile.seekg(marker, ios_base::beg);
if (strcmp(buffer, "vids") == 0)
{
stream_type = 0;
read_stream_header(&stream_header_vid);
}
else if (strcmp(buffer, "auds") == 0)
{
stream_type = 1;
read_stream_header(&stream_header_auds);
}
else
{
MSG_DBG(2, "Unknown stream type %s\n", buffer);
return -1;
}
}
else if (strcmp("strf", chunk_type) == 0)
{
if (stream_type == 0) {
read_stream_format();
}
else {
read_stream_format_auds();
}
}
else if (strcmp("strd", chunk_type) == 0)
{
}
else
{
MSG_DBG(2, " Unknown chunk type: %s\n", chunk_type);
// skip_chunk();
}
MSG_DBG(2, " }\n");
inFile.seekg(next_chunk, ios_base::beg);
}
MSG_DBG(2, " }\n");
inFile.seekg(end_of_chunk, ios_base::beg);
return 0;
}
int ReadAVI::parse_movi(int size)
{
char chunk_id[5];
long end_of_chunk;
index_entry_t index_entry;
int blk_size;
do {
long offset = inFile.tellg();
/*
if (fileSize - offset < 8)
break;
*/
read_chars(chunk_id, 4);
index_entry.stream_num = decodeCkid(chunk_id, &index_entry.type);
if (index_entry.stream_num < 0) {
inFile.seekg(offset - 4, ios_base::beg);
break;
}
index_entry.dwChunkLength = read_int();
if (!(avi_header.Flags & AVIF_HASINDEX)) {
index_entry.dwChunkOffset = offset + 8;
index_entries.push_back(index_entry);
}
MSG_DBG(3, " AVI Movi Chunk ( id=%s(%d-%d) chunk_size=%d offset=0x%lx)\n", chunk_id, index_entry.type,
index_entry.stream_num, index_entry.dwChunkLength, offset);
MSG_DBG(3, " {\n");
end_of_chunk = index_entry.dwChunkLength + inFile.tellg();
end_of_chunk = (end_of_chunk + 1) & ~1;
/* if ((end_of_chunk % 4) != 0) {
end_of_chunk = end_of_chunk + (4 - (end_of_chunk % 4));
}
*/
// MSG_DBG(2, "end_of_chunk:%d %X\n", end_of_chunk, end_of_chunk);
inFile.seekg(end_of_chunk, ios_base::beg);
MSG_DBG(3, " }\n");
blk_size = end_of_chunk - offset;
size -= blk_size;
MSG_DBG(3, "size:%d \n", size);
} while (size > 7);
return 0;
}
int ReadAVI::parse_hdrl(unsigned int size)
{
char chunk_id[5];
int chunk_size;
int end_of_chunk;
long offset = inFile.tellg();
read_chars(chunk_id, 4);
chunk_size = read_int();
MSG_DBG(2, " AVI Header Chunk (id=%s size=%d offset=0x%lx)\n", chunk_id, chunk_size, offset);
MSG_DBG(2, " {\n");
end_of_chunk = chunk_size + inFile.tellg();
if ((end_of_chunk % 4) != 0) {
end_of_chunk = end_of_chunk + (4 - (end_of_chunk % 4));
}
read_avi_header();
MSG_DBG(2, " }\n");
while (inFile.tellg() < offset + size - 4) {
//MSG_DBG(2, "Should end at 0x%lx 0x%lx\n",offset+size,inFile.tellg());
parse_hdrl_list();
}
return 0;
}
int ReadAVI::parse_riff()
{
char chunk_id[5];
int chunk_size;
char chunk_type[5];
int end_of_chunk, end_of_subchunk;
ZEROIZE(avi_header);
ZEROIZE(stream_header_vid);
ZEROIZE(stream_format_vid);
ZEROIZE(stream_header_auds);
ZEROIZE(stream_format_auds);
#ifdef VERBOSE
long offset = inFile.tellg();
#endif
read_chars(chunk_id, 4);
chunk_size = read_int();
read_chars(chunk_type, 4);
MSG_DBG(2, "RIFF Chunk (id=%s size=%d type=%s offset=0x%lx)\n", chunk_id, chunk_size, chunk_type, offset);
MSG_DBG(2, "{\n");
if (strcmp("RIFF", chunk_id) != 0)
{
MSG_DBG(2, "Not a RIFF file.\n");
return 1;
}
else if (strcmp("AVI ", chunk_type) != 0)
{
MSG_DBG(2, "Not an AVI file.\n");
return 1;
}
end_of_chunk = chunk_size - 4 + inFile.tellg();
while (inFile.tellg() < end_of_chunk) {
long offset = inFile.tellg();
read_chars(chunk_id, 4);
chunk_size = read_int();
end_of_subchunk = chunk_size + inFile.tellg();
if (strcmp("JUNK", chunk_id) == 0 || strcmp("PAD ", chunk_id) == 0) {
chunk_type[0] = 0;
}
else {
read_chars(chunk_type, 4);
}
MSG_DBG(2, " New Chunk (id=%s size=%d type=%s offset=0x%lx)\n", chunk_id, chunk_size, chunk_type, offset);
MSG_DBG(2, " {\n");
fflush(stdout);
if (strcmp("JUNK", chunk_id) == 0 || strcmp("PAD ", chunk_id) == 0) {
if ((chunk_size % 4) != 0) {
chunk_size = chunk_size + (4 - (chunk_size % 4));
}
#ifdef VERBOSE
hex_dump_chunk(chunk_size);
#endif
}
else if (strcmp("INFO", chunk_type) == 0) {
if ((chunk_size % 4) != 0) {
chunk_size = chunk_size + (4 - (chunk_size % 4));
}
#ifdef VERBOSE
hex_dump_chunk(chunk_size);
#endif
}
else if (strcmp("hdrl", chunk_type) == 0) {
parse_hdrl(chunk_size);
/* skip_chunk(); */
}
else if (strcmp("movi", chunk_type) == 0) {
movi_offset = offset;
parse_movi(chunk_size);
}
else if (strcmp("idx1", chunk_id) == 0) {
inFile.seekg(inFile.tellg() - std::streamoff(4), ios_base::beg);
parse_idx1(chunk_size);
}/* else if (strcmp("indx", chunk_id) == 0) {
inFile.seekg(inFile.tellg() - 4L, ios_base::beg);
parse_indx(chunk_size);
}*/else {
MSG_DBG(2, " Unknown chunk at %d (%4s)\n", (int)inFile.tellg() - 8, chunk_type);
if (chunk_size == 0)
break;
}
inFile.seekg(end_of_subchunk, ios_base::beg);
MSG_DBG(2, " }\n");
}
if (stream_format_vid.palette) {
delete[] stream_format_vid.palette;
stream_format_vid.palette = NULL;
}
MSG_DBG(2, "}\n");
return 0;
}
int ReadAVI::read_int()
{
int c;
unsigned char buf[4];
inFile.read((char*)buf, 4);
c = buf[0];
c = c + (buf[1] << 8);
c = c + (buf[2] << 16);
c = c + (buf[3] << 24);
return c;
}
int ReadAVI::read_word()
{
int c;
unsigned char buf[2];
inFile.read((char*)buf, 2);
c = buf[0];
c = c + (buf[1] << 8);
return c;
}
void ReadAVI::read_chars(char* s, int count)
{
inFile.read(s, count);
s[count] = 0;
}
void ReadAVI::read_chars_bin(unsigned char* s, int count)
{
inFile.read((char*)s, count);
}