Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

108
Src/nsmkv/Attachments.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "Attachments.h"
#include "read.h"
#include "global_elements.h"
static uint64_t ReadAttachedFile(nsmkv::MKVReader *reader, uint64_t size, nsmkv::AttachedFile &attached_file)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_attachments_filename:
{
char *utf8 = 0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
printf("Filename: %s\n", utf8);
attached_file.Own(attached_file.filename, utf8);
}
break;
case mkv_attachments_filemimetype:
{
char *utf8 = 0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
printf("File MIME Type: %s\n", utf8);
attached_file.Own(attached_file.mime_type, utf8);
}
break;
case mkv_attachments_filedata:
{
printf("File Data: binary size %I64u\n", node.size);
reader->Skip(node.size);
}
break;
case mkv_attachments_fileuid:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
printf("File UID: %I64x\n", val);
attached_file.file_uid = val;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
uint64_t nsmkv::ReadAttachment(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Attachments &attachments)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_attachments_attachedfile:
{
printf("Attachmented File\\n");
nsmkv::AttachedFile attached_file;
ReadAttachedFile(reader, node.size, attached_file);
}
break;
default:
ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}

47
Src/nsmkv/Attachments.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include <bfc/platform/types.h>
#include "mkv_reader.h"
// Attachments
const uint32_t mkv_segment_attachments = 0x941a469;
const uint32_t mkv_attachments_attachedfile = 0x21a7;
const uint32_t mkv_attachments_filename = 0x66e;
const uint32_t mkv_attachments_filemimetype = 0x660;
const uint32_t mkv_attachments_filedata =0x65c;
const uint32_t mkv_attachments_fileuid = 0x6ae;
namespace nsmkv
{
class AttachedFile
{
public:
AttachedFile()
{
file_uid=0;
filename=0;
mime_type=0;
}
~AttachedFile()
{
free(filename);
free(mime_type);
}
void Own(char *&field, char *value)
{
if (field)
free(field);
field = value;
}
uint64_t file_uid;
char *filename;
char *mime_type;
};
class Attachments
{
public:
};
uint64_t ReadAttachment(MKVReader *reader, uint64_t size, nsmkv::Attachments &attachments);
}

9
Src/nsmkv/Chapters.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "Chapters.h"
nsmkv::Chapters::Chapters(void)
{
}
nsmkv::Chapters::~Chapters(void)
{
}

22
Src/nsmkv/Chapters.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <bfc/platform/types.h>
#include <stdio.h>
// IDs
// these are slightly different from the matroska spec because we specify
// values after vint decoding and they specify before
const uint32_t mkv_segment_chapters = 0x43a770;
namespace nsmkv
{
class Chapters
{
public:
Chapters(void);
~Chapters(void);
};
// returns bytes read. 0 means EOF
uint64_t ReadChaptersInfo(FILE *f, uint64_t size, nsmkv::Chapters &chapters);
};

213
Src/nsmkv/Cluster.cpp Normal file
View File

@ -0,0 +1,213 @@
#include "Cluster.h"
#include "read.h"
#include "global_elements.h"
#include <winsock.h>
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadBlockGroup(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Block &block, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers)
{
uint64_t total_bytes_read=0;
while (size)
{
uint64_t ebml_start_position = reader->Tell(); // need this for recording the block binary start position
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_blockgroup_referenceblock:
{
int64_t val;
if (read_signed(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Reference Block: %I64d\n", val);
#endif
block.reference_block = val;
}
break;
case mkv_blockgroup_block:
{
#ifdef WA_VALIDATE
printf(" Block: binary size %I64u\n", node.size);
#endif
if (ReadBlockBinary(reader, node.size, block.binary, allowed_track_numbers, num_allowed_track_numbers) == 0)
return 0;
}
break;
case mkv_blockgroup_blockduration:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Block Duration: %I64u\n", val);
#endif
block.block_duration = val;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadCluster(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Cluster &cluster)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_cluster_timecode:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Time Code: %I64u\n", val);
cluster.time_code_found = true;
#endif
cluster.time_code = val;
}
break;
case mkv_cluster_blockgroup:
{
#ifdef WA_VALIDATE
printf(" Block Group\n");
#endif
Block block;
if (ReadBlockGroup(reader, node.size, block, 0, 0) == 0)
return 0;
}
break;
case mkv_cluster_simpleblock:
{
#ifdef WA_VALIDATE
printf(" Simple Block, size: %I64u\n", node.size);
#endif
BlockBinary bbinary;
if (ReadBlockBinary(reader, node.size, bbinary, 0, 0) == 0)
return 0;
// fseek64(f, node.size, SEEK_CUR);
}
break;
default:
ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
uint64_t nsmkv::ReadBlockBinary(nsmkv::MKVReader *reader, uint64_t size, nsmkv::BlockBinary &binary, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers)
{
uint64_t orig_size = size;
size_t bytes_read = (size_t)read_vint(reader, &binary.track_number);
if (!bytes_read)
return 0;
size-=bytes_read;
bool allowed_track=false;
if (num_allowed_track_numbers == (size_t)-1)
{
allowed_track=true;
}
else
{
for (size_t i=0;i!=num_allowed_track_numbers;i++)
{
if (binary.track_number == allowed_track_numbers[i])
{
allowed_track=true;
break;
}
}
}
#ifdef WA_VALIDATE
// if we are validating the file, force all tracks to go thru
allowed_track = true;
#endif
if (allowed_track && size >= 3)
{
uint16_t time_code_endian;
size_t bytes_read;
reader->Read(&time_code_endian, sizeof(uint16_t), &bytes_read);
if (bytes_read != sizeof(uint16_t))
return 0;
binary.time_code = htons(time_code_endian);
size-=sizeof(uint16_t);
uint8_t flags;
reader->Read(&flags, 1, &bytes_read);
if (bytes_read != 1)
return 0;
binary.flags = flags;
size -= sizeof(uint8_t);
binary.data_size = (size_t)size;
#ifndef WA_VALIDATE
binary.data = malloc((size_t)size);
if (!binary.data)
return 0;
reader->Read(binary.data, (size_t)size, &bytes_read);
if (bytes_read != size)
{
free(binary.data);
return 0;
}
#else
printf(" track number = %I64u\n",binary.track_number);
printf(" timecode = %u\n",binary.time_code);
printf(" flags = %u\n",binary.flags);
printf(" data_size = %I64u\n",size);
// if we are validating the nmk file we don't need to
// actually read the data, just skip past it
//fseek(reader, size, SEEK_CUR);
#endif
return orig_size;
}
else
{
reader->Skip(size);
return orig_size;
}
return 0;
}

106
Src/nsmkv/Cluster.h Normal file
View File

@ -0,0 +1,106 @@
#pragma once
#include "mkv_reader.h"
#include <bfc/platform/types.h>
const uint32_t mkv_segment_cluster = 0xf43b675;
const uint32_t mkv_cluster_timecode = 0x67;
const uint32_t mkv_cluster_blockgroup = 0x20;
const uint32_t mkv_cluster_simpleblock = 0x23;
const uint32_t mkv_blockgroup_referenceblock = 0x7b;
const uint32_t mkv_blockgroup_block = 0x21;
const uint32_t mkv_blockgroup_blockduration = 0x1b;
/* TODO: benski>
need to think this whole thing through.
Ideally, we would be able to enumerate through the clusters/blocks,
but the output plugin might have a different audio buffer size
than the size that the encoder assumed.
so the first attempt is going to be random access
and we'll see if there is a better way after implementation
*/
namespace nsmkv
{
class BlockBinary
{
public:
BlockBinary()
{
data=0;
data_size=0;
track_number=0;
flags=0;
time_code=0;
}
~BlockBinary()
{
free(data);
}
uint64_t track_number;
int16_t time_code;
uint8_t flags;
size_t data_size;
void *data; // maybe optionally allow the user to pass in the buffer to reduce mallocs
enum
{
LACE_MASK = 0x6,
XIPH_LACING= 0x2,
FIXED_LACING = 0x4,
EBML_LACING = 0x6,
NO_LACING = 0x0,
};
};
class Block
{
public:
Block()
#ifdef WA_VALIDATE
:
reference_block_found(false),
block_duration_found(false)
#endif
{
reference_block=0;
block_duration=0;
}
BlockBinary binary;
uint64_t reference_block;
uint64_t block_duration;
#ifdef WA_VALIDATE
bool reference_block_found;
bool block_duration_found;
#endif
};
class Cluster
{
public:
Cluster()
#ifdef WA_VALIDATE
:
time_code_found(false)
#endif
{
time_code = 0;
position = 0;
previous_size = 0;
}
uint64_t time_code;
uint64_t position;
uint64_t previous_size;
#ifdef WA_VALIDATE
bool time_code_found;
#endif
};
class Clusters
{
};
uint64_t ReadCluster(MKVReader *reader, uint64_t size, nsmkv::Cluster &cluster);
uint64_t ReadBlockBinary(MKVReader *reader, uint64_t size, nsmkv::BlockBinary &binary, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers);
uint64_t ReadBlockGroup(MKVReader *reader, uint64_t size, nsmkv::Block &block, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers);
}

205
Src/nsmkv/Cues.cpp Normal file
View File

@ -0,0 +1,205 @@
#include "Cues.h"
#include "read.h"
#include "global_elements.h"
nsmkv::CuePoint *nsmkv::Cues::GetCuePoint(uint64_t when, uint64_t current_time, int seek_direction)
{
CuePoint *last=0;
CuePoints::iterator itr;
for (itr=cue_points.begin(); itr!=cue_points.end();itr++)
{
CuePoint *cue_point = itr->second;
if (cue_point->cue_time == when)
{
if (seek_direction != SEEK_FORWARD || current_time < cue_point->cue_time)
return cue_point;
}
else if (cue_point->cue_time > when)
{
if (last)
{
if (seek_direction != SEEK_FORWARD || current_time < last->cue_time)
return last;
}
else
return cue_point;
}
last = cue_point;
}
return last; // will be 0 if we don't have any cue points, which is what we want.
}
nsmkv::CueTrackPosition *nsmkv::CuePoint::GetPosition(uint64_t track)
{
CueTrackPositions::iterator found = cue_track_positions.find(track);
if (found != cue_track_positions.end())
return found->second;
else
return 0;
}
// returns bytes read. 0 means EOF
static uint64_t ReadCueTrackPositions(nsmkv::MKVReader *reader, uint64_t size, nsmkv::CueTrackPosition &track_position)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_cuetrackpositions_cuetrack:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
track_position.track = val;
#ifdef WA_VALIDATE
printf(" Cue Track: %I64u\n", val);
track_position.track_found = true;
#endif
}
break;
case mkv_cuetrackpositions_cueclusterposition:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
track_position.cluster_position = val;
#ifdef WA_VALIDATE
printf(" Cue Cluster Position: %I64u\n", val);
track_position.cluster_position_found = true;
#endif
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
// returns bytes read. 0 means EOF
static uint64_t ReadCuePoint(nsmkv::MKVReader *reader, uint64_t size, nsmkv::CuePoint &cue_point)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_cuepoint_cuetime:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
cue_point.cue_time = val;
#ifdef WA_VALIDATE
printf(" Cue Time: %I64u\n", val);
cue_point.cue_time_found = true;
#endif
}
break;
case mkv_cuepoint_cuetrackpositions:
{
#ifdef WA_VALIDATE
printf(" Cue Track Positions\n");
#endif
nsmkv::CueTrackPosition *track_position = new nsmkv::CueTrackPosition;
if (ReadCueTrackPositions(reader, node.size, *track_position) == 0)
{
delete track_position;
return 0;
}
cue_point.cue_track_positions[track_position->track] = track_position;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadCues(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Cues &cues)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_cues_cuepoint:
{
#ifdef WA_VALIDATE
printf(" Cue Point\n");
#endif
CuePoint *cue_point = new CuePoint;
if (ReadCuePoint(reader, node.size, *cue_point) == 0)
{
delete cue_point;
return 0;
}
cues.cue_points[cue_point->cue_time] = cue_point;
}
break;
default:
ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}

76
Src/nsmkv/Cues.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include <bfc/platform/types.h>
#include "mkv_reader.h"
#include <map>
const uint32_t mkv_segment_cues = 0xc53bb6b;
const uint32_t mkv_cues_cuepoint = 0x3b;
const uint32_t mkv_cuepoint_cuetime=0x33;
const uint32_t mkv_cuepoint_cuetrackpositions = 0x37;
const uint32_t mkv_cuetrackpositions_cuetrack= 0x77;
const uint32_t mkv_cuetrackpositions_cueclusterposition=0x71;
namespace nsmkv
{
class CueTrackPosition
{
public:
CueTrackPosition()
#ifdef WA_VALIDATE
:
track_found(false),
cluster_position_found(false)
// block_number_found(false)
#endif
{
track=0;
cluster_position=0;
block_number=0;
}
uint64_t track;
uint64_t cluster_position;
uint64_t block_number;
#ifdef WA_VALIDATE
bool track_found;
bool cluster_position_found;
// bool block_number_found;
#endif
};
class CuePoint
{
public:
CuePoint()
#ifdef WA_VALIDATE
:
cue_time_found(false)
#endif
{
cue_time = 0;
}
CueTrackPosition *GetPosition(uint64_t track);
uint64_t cue_time;
typedef std::map<uint64_t, CueTrackPosition*> CueTrackPositions; // keyed on track number
CueTrackPositions cue_track_positions;
#ifdef WA_VALIDATE
bool cue_time_found;
#endif
};
class Cues
{
public:
enum
{
SEEK_WHATEVER=0,
SEEK_FORWARD=1,
SEEK_BACKWARD=2,
};
CuePoint *GetCuePoint(uint64_t when, uint64_t current_time=0, int seek_direction=SEEK_WHATEVER);
typedef std::map<uint64_t, CuePoint*> CuePoints; // use Map on cue_time to ensure order
CuePoints cue_points;
};
uint64_t ReadCues(MKVReader *reader, uint64_t size, nsmkv::Cues &cues);
}

111
Src/nsmkv/Lacing.cpp Normal file
View File

@ -0,0 +1,111 @@
#include "Lacing.h"
#include "Cluster.h"
#include "vint.h"
bool nsmkv::Lacing::GetState(uint8_t flags, const uint8_t *data, size_t data_len, nsmkv::LacingState *state)
{
// TODO: error check by making sure data_len doesn't go below 0
switch(flags & BlockBinary::LACE_MASK)
{
case BlockBinary::NO_LACING:
state->laces = 1;
state->positions[0] = 0;
state->sizes[0] = data_len;
#ifdef WA_VALIDATE
printf(" Lacing = NO_LACING\n");
#endif
return true;
case BlockBinary::EBML_LACING:
{
uint16_t number_of_frames = state->laces = *data++ + 1;
uint64_t delta = vint_get_number_bytes(data[0]);
state->sizes[0] = vint_read_ptr_len((uint8_t)delta, data);
delta++;
state->positions[0] = 1;
state->positions[1] = 1+state->sizes[0];
for (uint16_t i=1;i<number_of_frames-1;i++)
{
uint8_t this_len = vint_get_number_bytes(data[delta]);
state->sizes[i] = vsint_read_ptr_len(this_len, data+delta);
state->sizes[i] += state->sizes[i-1];
state->positions[i+1] = state->positions[i] + state->sizes[i];
delta+=this_len + 1;
}
state->sizes[number_of_frames-1] = data_len - state->positions[number_of_frames-1] - delta;
for (uint16_t i=0;i<number_of_frames;i++)
{
state->positions[i] += delta;
}
#ifdef WA_VALIDATE
printf(" Lacing = EBML_LACING\n");
#endif
}
return true;
case BlockBinary::XIPH_LACING:
{
const uint8_t *orig_data = data;
uint16_t number_of_frames=state->laces = *data++ + 1;
for (uint16_t i=0;i!=number_of_frames-1;i++)
{
size_t frame_len = 0;
do
{
frame_len += *data;
} while (data && *data++ == 255);
state->sizes[i] = frame_len;
}
uint64_t delta = data - orig_data;
uint64_t last_position = delta;
uint64_t last_size = 0;
for (uint16_t i=0;i!=number_of_frames;i++)
{
state->positions[i] = last_position + last_size;
last_position = state->positions[i];
last_size = state->sizes[i];
}
state->sizes[number_of_frames-1] = data_len - state->positions[number_of_frames-1];
#ifdef WA_VALIDATE
printf(" Lacing = XIPH LACING\n");
#endif
}
return true;
case BlockBinary::FIXED_LACING:
{
uint16_t number_of_frames=state->laces=data[0]+1;
uint32_t size_per_frame = (uint32_t)(data_len-1) / number_of_frames;
for (uint16_t i=0;i<number_of_frames;i++)
{
state->positions[i] = (uint64_t)(1 + size_per_frame*i);
state->sizes[i] = size_per_frame;
}
#ifdef WA_VALIDATE
printf(" Lacing = FIXED LACING\n");
#endif
}
return true;
default:
return false;
}
}
bool nsmkv::Lacing::GetFrame(uint16_t frame_number, const uint8_t *data, size_t data_len, const uint8_t **frame, size_t *frame_len, const LacingState *state)
{
if (frame_number >= state->laces)
return false;
const uint8_t *lace = data + state->positions[frame_number];
size_t lace_len = (size_t)state->sizes[frame_number];
if (lace < data // if the lace starts before our data
|| (state->positions[frame_number] + lace_len) > data_len) // or extends out past our data
{
return false;
}
*frame = lace;
*frame_len = lace_len;
return true;
}

19
Src/nsmkv/Lacing.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <bfc/platform/types.h>
namespace nsmkv
{
struct LacingState
{
uint16_t laces;
int64_t sizes[256]; // max 256 laces (stored as 8bit number and 1 added)
uint64_t positions[256];
};
namespace Lacing
{
bool GetState(uint8_t flags, const uint8_t *data, size_t data_len, LacingState *state);
bool GetFrame(uint16_t frame_number, const uint8_t *data, size_t data_len, const uint8_t **frame, size_t *frame_len, const LacingState *state);
}
}

230
Src/nsmkv/SeekTable.cpp Normal file
View File

@ -0,0 +1,230 @@
#include "SeekTable.h"
#include "read.h"
#include "global_elements.h"
#include "vint.h"
#include <stdio.h>
#include <assert.h>
#ifdef WA_VALIDATE
extern uint32_t num_seekhead_elements_found;
extern uint32_t num_seek_elements_found;
#endif
bool nsmkv::SeekTable::GetEntry(uint64_t id, uint64_t *position)
{
return EnumEntry(0, id, position);
}
bool nsmkv::SeekTable::EnumEntry(size_t i, uint64_t id, uint64_t *position)
{
SeekMap::iterator found = seekMap.find(id);
if (found != seekMap.end())
{
SeekEntries *entries = found->second;
if (entries && entries->size() > i)
{
*position = entries->at(i).position;
return true;
}
}
return false;
}
bool nsmkv::SeekTable::EnumEntry(size_t i, uint64_t id, SeekEntry **seek_entry)
{
SeekMap::iterator found = seekMap.find(id);
if (found != seekMap.end())
{
SeekEntries *entries = found->second;
if (entries && entries->size() > i)
{
*seek_entry = &entries->at(i);
return true;
}
}
return false;
}
void nsmkv::SeekTable::AddEntry(nsmkv::SeekEntry &entry, int flags)
{
// check for duplicates
size_t i=0;
SeekEntry *seek_entry;
while (EnumEntry(i++, entry.id, &seek_entry))
{
if (flags & ADDENTRY_SINGLE)
{
if (flags & ADDENTRY_FOUND)
seek_entry->position = entry.position;
return;
}
if (entry.position == seek_entry->position)
{
return;
}
}
SeekEntries *&entries = seekMap[entry.id];
if (!entries)
{
entries = new SeekEntries;
}
entries->push_back(entry);
#ifdef WA_VALIDATE
num_seek_elements_found++;
#endif
}
void nsmkv::SeekTable::Dump()
{
SeekMap::iterator itr;
for (itr=seekMap.begin();itr!=seekMap.end();itr++)
{
SeekEntries *entries = itr->second;
if (entries)
{
SeekEntries::iterator seekItr;
for (seekItr=entries->begin();seekItr!=entries->end();seekItr++)
{
SeekEntry &entry = *seekItr;
printf("Seek Entry -> id=%I64x, position=%I64u\n", entry.id, entry.position);
}
}
}
}
// returns bytes read. 0 means EOF
static uint64_t ReadSeek(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SeekTable &seekTable)
{
uint64_t total_bytes_read=0;
nsmkv::SeekEntry entry;
enum
{
ID_FIELD_PARSED = 0x1,
POSITION_FIELD_PARSED = 0x2,
};
int fields_parsed=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_metaseek_seekid:
{
uint8_t binary[9] = {0};
if (!node.size || node.size > 9)
{
#ifdef WA_VALIDATE
printf(" SeekID size invalid, size=%d\n",node.size);
#endif
assert(node.size<9);
return 0;
}
size_t bytes_read;
reader->Read(binary, (size_t)node.size, &bytes_read);
if (bytes_read != (size_t)node.size)
return 0;
#ifdef WA_VALIDATE
printf(" SeekID: %I64x\n", vint_read_ptr_len(node.size-1, binary));
#endif
entry.id = vint_read_ptr_len((uint8_t)node.size-1, binary);
fields_parsed |= ID_FIELD_PARSED;
}
break;
case mkv_metaseek_seekposition:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" SeekPosition: %I64u\n", val);
#endif
entry.position = val;
fields_parsed |= POSITION_FIELD_PARSED;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
if (fields_parsed == 0x3)
{
//entry.state = nsmkv::NOT_READ;
seekTable.AddEntry(entry);
}
#ifdef WA_VALIDATE
else if (fields_parsed == 0x2)
{
printf(" Seek only contains SeekPosition\n");
} else if (fields_parsed == 0x01)
{
printf(" Seek element only contains SeekID\n");
}
#endif
return total_bytes_read;
}
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadSeekHead(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SeekTable &seekTable)
{
uint64_t total_bytes_read=0;
#ifdef WA_VALIDATE
num_seekhead_elements_found++;
#endif
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_metaseek_seek:
{
#ifdef WA_VALIDATE
printf(" Seek\n");
#endif
ReadSeek(reader, node.size, seekTable);
}
break;
default:
ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}

54
Src/nsmkv/SeekTable.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include <map>
#include <vector>
#include "mkv_reader.h"
const uint32_t mkv_metaseek_seekhead = 0x14d9b74;
const uint32_t mkv_metaseek_seek=0xdbb;
const uint32_t mkv_metaseek_seekid = 0x13ab;
const uint32_t mkv_metaseek_seekposition=0x13ac;
/* this represents a seek table of other nodes in EBML
be careful, CuePoints (CuePoints.h) is for seeking to a certain time in the song
the SeekTable (SeekTable.h) is for fast indexing of the mkv file structure
*/
namespace nsmkv
{
class SeekEntry
{
public:
SeekEntry()
{
id=0;
position=0;
}
SeekEntry(uint64_t id, uint64_t position) : id(id), position(position)
{
}
uint64_t id; // ID of the EBML node
uint64_t position;
};
class SeekTable
{
public:
void AddEntry(SeekEntry &entry, int flags=0);
void Dump();
bool GetEntry(uint64_t id, uint64_t *position);
bool EnumEntry(size_t i, uint64_t id, uint64_t *position);
enum // flags for AddEntry
{
ADDENTRY_SINGLE = 0x1, // if there can only be one
ADDENTRY_FOUND = 0x2, // pass this is you physically found the entry in the file - this takes priority over the SeekHead
};
private:
bool EnumEntry(size_t i, uint64_t id, SeekEntry **seek_entry);
typedef std::vector<SeekEntry> SeekEntries;
typedef std::map<uint64_t, SeekEntries*> SeekMap;
SeekMap seekMap;
};
// returns bytes read. 0 means EOF
uint64_t ReadSeekHead(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SeekTable &seekTable);
}

235
Src/nsmkv/SegmentInfo.cpp Normal file
View File

@ -0,0 +1,235 @@
#include "SegmentInfo.h"
#include "read.h"
#include "global_elements.h"
#include <time.h>
#include <TCHAR.h>
#include <sys/stat.h>
int FileExists(const char * filename);
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadSegmentInfo(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SegmentInfo &segment_info)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_segmentinfo_timecodescale:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Time Code Scale: %I64u\n", val);
segment_info.time_code_scale_found = true;
#endif
segment_info.time_code_scale = val;
}
break;
case mkv_segmentinfo_muxingapp:
{
char *utf8 = 0;
if (node.size && read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
#ifdef WA_VALIDATE
printf(" Muxing App: %s\n", utf8);
segment_info.muxing_app_found = true;
#endif
segment_info.Own(segment_info.muxing_app, utf8);
}
break;
case mkv_segmentinfo_writingapp:
{
char *utf8 = 0;
if (node.size && read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
#ifdef WA_VALIDATE
printf(" Writing App: %s\n", utf8);
segment_info.writing_app_found = true;
#endif
segment_info.Own(segment_info.writing_app, utf8);
}
break;
case mkv_segmentinfo_duration:
{
double val;
if (read_float(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Duration: %g\n", val);
segment_info.duration_found = true;
#endif
segment_info.duration = val;
}
break;
case mkv_segmentinfo_dateutc:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
segment_info.production_date = val;
// value is in nanoseconds, relative to jan 01, 2001. ugh
__time64_t val_time = mkv_date_as_time_t(val);
#ifdef WA_VALIDATE
printf(" Date UTC: %s", _ctime64(&val_time));
segment_info.production_date_found = true;
#endif
}
break;
case mkv_segmentinfo_segmentuid:
{
#ifdef WA_VALIDATE
printf(" Segment UID: binary size %I64u\n", node.size);
segment_info.segment_uid_found = true;
#endif
if (node.size == 16)
{
size_t bytes_read;
reader->Read(&segment_info.segment_uid, (size_t)node.size, &bytes_read);
if (bytes_read != node.size)
return 0;
}
else // bad size, let's just skip it
{
reader->Skip(node.size);
}
}
break;
case mkv_segmentinfo_prevuid:
{
#ifdef WA_VALIDATE
printf(" Previous UID: binary size %I64u\n", node.size);
segment_info.prev_uid_found = true;
#endif
if (node.size == 16)
{
size_t bytes_read;
reader->Read(&segment_info.prev_uid, (size_t)node.size, &bytes_read);
if (bytes_read != node.size)
return 0;
}
else // bad size, let's just skip it
{
reader->Skip(node.size);
}
}
break;
case mkv_segmentinfo_nextuid:
{
#ifdef WA_VALIDATE
printf(" Next Segment UID: binary size %I64u\n", node.size);
segment_info.next_uid_found = true;
#endif
if (node.size == 16)
{
size_t bytes_read;
reader->Read(&segment_info.next_uid, (size_t)node.size, &bytes_read);
if (bytes_read != node.size)
return 0;
}
else // bad size, let's just skip it
{
reader->Skip(node.size);
}
}
break;
case mkv_segmentinfo_title:
{
char *utf8 = 0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
#ifdef WA_VALIDATE
printf(" Title: %s\n", utf8);
segment_info.title_found = true;
#endif
segment_info.Own(segment_info.title, utf8);
}
break;
case mkv_segmentinfo_prevfilename:
{
char *utf8 = 0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
#ifdef WA_VALIDATE
printf(" Previous Filename: %s\n", utf8);
segment_info.prev_filename_found = true;
if (FileExists(segment_info.prev_filename) != 0)
{
printf("****Specified previous filename not found");
}
#endif
segment_info.Own(segment_info.prev_filename,utf8);
}
break;
case mkv_segmentinfo_nextfilename:
{
char *utf8 = 0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
if (utf8)
#ifdef WA_VALIDATE
printf(" Next Filename: %s\n", utf8);
segment_info.next_filename_found = true;
if (FileExists(segment_info.next_filename) != 0)
{
printf("****Specified next filename not found");
}
#endif
segment_info.Own(segment_info.next_filename, utf8);
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
int nsmkv::SegmentInfo::GetDurationMilliseconds() const
{
double nanoseconds = (double)time_code_scale * duration;
double microseconds = nanoseconds / 1000.0;
double milliseconds = microseconds / 1000.0;
return (int)milliseconds;
}
uint64_t nsmkv::SegmentInfo::ConvertMillisecondsToTime(int milliseconds) const
{
double time_code = (double)milliseconds * 1000000.0 / (double)time_code_scale;
return (uint64_t)time_code;
}
int FileExists(const char * filename) {
int iStat;
struct _stat64 fileInfo;
// get the file attributes
return (iStat = _stat64(filename,&fileInfo));
}

107
Src/nsmkv/SegmentInfo.h Normal file
View File

@ -0,0 +1,107 @@
#pragma once
#include "mkv_date.h"
#include "mkv_reader.h"
#include <bfc/platform/guid.h>
/*
Time Scale: 986946
Muxing App: libebml v0.7.7 + libmatroska v0.8.1
Writing App: mkvmerge v2.0.2 ('You're My Flame') built on Sep 20 2007 09:35:09
Duration: 60257
Date UTC: Sun Nov 18 20:23:18 2007
Segment UID: binary size 16
*/
const uint32_t mkv_segment_segmentinfo = 0x549a966;
const uint32_t mkv_segmentinfo_timecodescale = 0xad7b1;
const uint32_t mkv_segmentinfo_muxingapp=0xd80;
const uint32_t mkv_segmentinfo_writingapp=0x1741;
const uint32_t mkv_segmentinfo_duration=0x489;
const uint32_t mkv_segmentinfo_dateutc=0x461;
const uint32_t mkv_segmentinfo_segmentuid=0x33a4;
const uint32_t mkv_segmentinfo_nextuid=0x1eb923;
const uint32_t mkv_segmentinfo_prevuid=0x1cb923;
const uint32_t mkv_segmentinfo_nextfilename=0x1e83bb;
const uint32_t mkv_segmentinfo_prevfilename=0x1c83ab;
const uint32_t mkv_segmentinfo_title=0x3ba9;
namespace nsmkv
{
class SegmentInfo
{
public:
SegmentInfo() :
time_code_scale(1000000),
muxing_app(0),
writing_app(0),
duration(0),
production_date(0),
segment_uid(INVALID_GUID),
next_uid(INVALID_GUID),
prev_uid(INVALID_GUID),
next_filename(0),
prev_filename(0),
title(0)
#ifdef WA_VALIDATE
,
time_code_scale_found(false),
muxing_app_found(false),
writing_app_found(false),
duration_found(false),
production_date_found(false),
segment_uid_found(false),
next_uid_found(false),
prev_uid_found(false),
next_filename_found(false),
prev_filename_found(false),
title_found(false)
#endif
{
}
~SegmentInfo()
{
free(muxing_app);
free(writing_app);
free(title);
}
void Own(char *&field, char *value)
{
if (field)
free(field);
field = value;
}
int GetDurationMilliseconds() const;
uint64_t ConvertMillisecondsToTime(int milliseconds) const;
uint64_t time_code_scale;
char *muxing_app;
char *writing_app;
char *title;
double duration;
mkv_date_t production_date;
GUID segment_uid;
GUID prev_uid;
GUID next_uid;
char *prev_filename;
char *next_filename;
#ifdef WA_VALIDATE
bool segment_uid_found;
bool prev_uid_found;
bool next_uid_found;
bool prev_filename_found;
bool next_filename_found;
bool time_code_scale_found;
bool duration_found;
bool muxing_app_found;
bool writing_app_found;
bool production_date_found;
bool title_found;
#endif
};
uint64_t ReadSegmentInfo(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SegmentInfo &segment_info);
}

202
Src/nsmkv/Tags.cpp Normal file
View File

@ -0,0 +1,202 @@
#include "Tags.h"
#include "global_elements.h"
#include "read.h"
nsmkv::Tags::Tags(void)
{
}
nsmkv::Tags::~Tags(void)
{
}
static uint64_t ReadSimpleTag(FILE *f, uint64_t size)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_tags_tag_simpletag_tagname:
{
char *utf8 = 0;
if (node.size && read_utf8((nsmkv::MKVReader*)f, node.size, &utf8) == 0)
return 0;
if (utf8)
printf(" Tag Name: %s\n", utf8);
free(utf8);
}
break;
case mkv_tags_tag_simpletag_tagstring:
{
char *utf8 = 0;
if (node.size && read_utf8((nsmkv::MKVReader*)f, node.size, &utf8) == 0)
return 0;
if (utf8)
printf(" Tag String: %s\n", utf8);
free(utf8);
}
break;
case mkv_tags_tag_simpletag_taglanguage:
{
char *utf8 = 0;
if (node.size && read_utf8((nsmkv::MKVReader*)f, node.size, &utf8) == 0)
return 0;
if (utf8)
printf(" Tag Language: %s\n", utf8);
free(utf8);
}
break;
case mkv_tags_tag_simpletag_tagdefault:
{
uint64_t val;
if (read_unsigned((nsmkv::MKVReader*)f, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Tag Default: %I64u\n", val);
#endif
}
break;
default:
nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
}
}
return total_bytes_read;
}
static uint64_t ReadTarget(FILE *f, uint64_t size)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_tags_tag_target_targettypevalue:
{
uint64_t val;
if (read_unsigned((nsmkv::MKVReader*)f, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Target Type Value: %I64u\n", val);
#endif
}
break;
default:
nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
}
}
return total_bytes_read;
}
static uint64_t ReadTag(FILE *f, uint64_t size)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_tags_tag_target:
{
ReadTarget(f, node.size);
}
break;
case mkv_tags_tag_simpletag:
{
ReadSimpleTag(f, node.size);
}
break;
default:
nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
}
}
return total_bytes_read;
}
uint64_t nsmkv::ReadTags(FILE *f, uint64_t size, nsmkv::Tags &tags)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_tags_tag:
{
ReadTag(f, node.size);
}
break;
default:
nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
}
}
return total_bytes_read;
}

32
Src/nsmkv/Tags.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <bfc/platform/types.h>
#include <stdio.h>
// IDs
// these are slightly different from the matroska spec because we specify
// values after vint decoding and they specify before
const uint32_t mkv_segment_tags = 0x254c367;
const uint32_t mkv_tags_tag = 0x3373;
const uint32_t mkv_tags_tag_target = 0x23c0;
const uint32_t mkv_tags_tag_target_targettypevalue = 0x28ca;
const uint32_t mkv_tags_tag_simpletag = 0x27c8;
const uint32_t mkv_tags_tag_simpletag_tagname = 0x5a3;
const uint32_t mkv_tags_tag_simpletag_tagstring = 0x487;
const uint32_t mkv_tags_tag_simpletag_taglanguage = 0x47a;
const uint32_t mkv_tags_tag_simpletag_tagdefault = 0x484;
namespace nsmkv
{
class Tags
{
public:
Tags(void);
~Tags(void);
};
// returns bytes read. 0 means EOF
uint64_t ReadTags(FILE *f, uint64_t size, nsmkv::Tags &tags);
};

561
Src/nsmkv/Tracks.cpp Normal file
View File

@ -0,0 +1,561 @@
#include "Tracks.h"
#include "read.h"
#include "global_elements.h"
// returns bytes read. 0 means EOF
static uint64_t ReadTracksVideo(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Video &video)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_video_pixelwidth:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Pixel Width: %I64u\n", val);
video.pixel_width_found=true;
#endif
video.pixel_width=val;
}
break;
case mkv_video_pixelheight:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Pixel Height: %I64u\n", val);
video.pixel_height_found=true;
#endif
video.pixel_height=val;
}
break;
case mkv_video_flaginterlaced:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Flag Interlaced: 0x%I64x\n", val);
video.flag_interlaced_found=true;
#endif
video.flag_interlaced = !!val;
}
break;
case mkv_video_displaywidth:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Display Width: %I64u\n", val);
video.display_width_found = true;
#endif
video.display_width = val;
}
break;
case mkv_video_displayheight:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Display Height: %I64u\n", val);
video.display_height_found = true;
#endif
video.display_height = val;
}
break;
case mkv_video_pixelcropleft:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Pixel Crop Left: %I64u\n", val);
video.pixel_crop_left_found = true;
#endif
video.pixel_crop_left = val;
}
break;
case mkv_video_pixelcroptop:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Pixel Crop Top: %I64u\n", val);
video.pixel_crop_top_found = true;
#endif
video.pixel_crop_top = val;
}
break;
case mkv_video_pixelcropbottom:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Pixel Crop Bottom: %I64u\n", val);
video.pixel_crop_bottom_found = true;
#endif
video.pixel_crop_bottom = val;
}
break;
case mkv_video_pixelcropright:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Pixel Crop Right: %I64u\n", val);
video.pixel_crop_right_found = true;
#endif
video.pixel_crop_right = val;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
// returns bytes read. 0 means EOF
static uint64_t ReadTracksAudio(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Audio &audio)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_audio_samplingfrequency:
{
double val;
if (read_float(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Sampling Frequency: %g\n", val);
audio.sampling_frequency_found = true;
#endif
audio.sampling_frequency = (uint64_t)val;
}
break;
case mkv_audio_channels:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Channels: %I64u\n", val);
audio.channels_found = true;
#endif
audio.channels = val;
}
break;
case mkv_audio_output_samplingfrequency:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Output Sampling Frequency: %I64u\n", val);
audio.output_sampling_frequency_found = true;
#endif
audio.output_sampling_frequency = val;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
static uint64_t ReadTrackEntry(nsmkv::MKVReader *reader, uint64_t size, nsmkv::TrackEntry &track_entry)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_tracks_tracknumber:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Track Number: %I64u\n", val);
track_entry.track_number_found = true;
#endif
track_entry.track_number = val;
}
break;
case mkv_tracks_trackuid:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Track UID: %I64u\n", val);
track_entry.track_uid_found = true;
#endif
track_entry.track_uid = val;
}
break;
case mkv_tracks_tracktype:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Track Type: 0x%I64x\n", val);
track_entry.track_type_found = true;
#endif
track_entry.track_type = val;
}
break;
case mkv_tracks_flagenabled:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Flag Enabled: 0x%I64x\n", val);
track_entry.flag_enabled_found = true;
#endif
track_entry.flag_enabled = !!val;
}
break;
case mkv_tracks_flagdefault:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Flag Default: 0x%I64x\n", val);
track_entry.flag_default_found = true;
#endif
track_entry.flag_default = !!val;
}
break;
case mkv_tracks_flagforced:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Flag Forced: 0x%I64x\n", val);
track_entry.flag_forced_found = true;
#endif
track_entry.flag_forced = !!val;
}
break;
case mkv_tracks_flaglacing:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Flag Lacing: 0x%I64x\n", val);
track_entry.flag_lacing_found = true;
#endif
track_entry.flag_lacing = !!val;
}
break;
case mkv_tracks_mincache:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Min Cache: %I64u\n", val);
track_entry.min_cache_found = true;
#endif
track_entry.min_cache = !!val;
}
break;
case mkv_tracks_tracktimecodescale:
{
double val;
if (read_float(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Track Time Code Scale: %g\n", val);
track_entry.track_timecode_scale_found = true;
#endif
track_entry.track_timecode_scale = val;
}
break;
case mkv_tracks_maxblockadditionid:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Max Block Addition ID: %I64u\n", val);
track_entry.max_block_additional_id_found=true;
#endif
track_entry.max_block_additional_id=val;
}
break;
case mkv_tracks_codecid:
{
char *utf8=0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
#ifdef WA_VALIDATE
if (utf8)
printf(" Codec ID: %s\n", utf8);
track_entry.codec_id_found = true;
#endif
track_entry.Own(track_entry.codec_id, utf8);
}
break;
case mkv_tracks_codecdecodeall:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Codec Decode All: %I64u\n", val);
track_entry.decode_all_found = true;
#endif
track_entry.decode_all = !!val;
}
break;
case mkv_tracks_defaultduration:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Default Duration: %I64u\n", val);
track_entry.default_duration_found = true;
#endif
track_entry.default_duration = val;
}
break;
case mkv_tracks_codecprivate:
{
#ifdef WA_VALIDATE
printf(" Codec Private: binary size %I64u\n", node.size);
#endif
void *codec_private = malloc((size_t)node.size);
if (!codec_private)
return 0;
size_t bytes_read;
reader->Read(codec_private, (size_t)node.size, &bytes_read);
if (bytes_read != node.size)
{
free(codec_private);
return 0;
}
track_entry.OwnCodecPrivate(codec_private, (size_t)node.size);
#ifdef WA_VALIDATE
track_entry.codec_private_found = true;
#endif
}
break;
case mkv_tracks_language:
{
char *utf8=0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
#ifdef WA_VALIDATE
if (utf8)
printf(" Codec Language: %s\n", utf8);
track_entry.language_found = true;
#endif
track_entry.Own(track_entry.language, utf8);
}
break;
case mkv_tracks_video:
{
#ifdef WA_VALIDATE
printf(" Video Settings\n");
#endif
if (ReadTracksVideo(reader, node.size, track_entry.video) == 0)
return 0;
}
break;
case mkv_tracks_audio:
{
#ifdef WA_VALIDATE
printf(" Audio Settings\n");
#endif
if (ReadTracksAudio(reader, node.size, track_entry.audio) == 0)
return 0;
}
break;
case mkv_tracks_name:
{
char *utf8=0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
#ifdef WA_VALIDATE
if (utf8)
printf(" Track Name: %s\n", utf8);
track_entry.name_found = true;
#endif
track_entry.Own(track_entry.name, utf8);
}
break;
case mkv_tracks_maxcache:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
#ifdef WA_VALIDATE
printf(" Max Cache: %I64u\n", val);
track_entry.max_cache_found = true;
#endif
track_entry.max_cache = val;
}
break;
default:
nsmkv::ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadTracks(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Tracks &tracks)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_tracks_trackentry:
{
#ifdef WA_VALIDATE
printf(" Track Entry\n");
#endif
TrackEntry *track_entry = new TrackEntry;
if (ReadTrackEntry(reader, node.size, *track_entry) == 0)
{
delete track_entry;
return 0;
}
tracks.tracks.push_back(track_entry);
}
break;
default:
ReadGlobal(reader, node.id, node.size);
}
}
return total_bytes_read;
}
const nsmkv::TrackEntry *nsmkv::Tracks::EnumTrack(size_t i) const
{
if (tracks.size() > i)
{
return tracks[i];
}
return 0;
}

292
Src/nsmkv/Tracks.h Normal file
View File

@ -0,0 +1,292 @@
#pragma once
#include <bfc/platform/types.h>
#include "mkv_reader.h"
#include <vector>
const uint32_t mkv_segment_tracks = 0x654ae6b;
const uint32_t mkv_tracks_trackentry = 0x2e;
const uint32_t mkv_tracks_tracknumber=0x57;
const uint32_t mkv_tracks_trackuid=0x33c5;
const uint32_t mkv_tracks_tracktype=0x3;
const uint32_t mkv_tracks_flagenabled=0x39;
const uint32_t mkv_tracks_flagdefault=0x8;
const uint32_t mkv_tracks_flagforced=0x15aa;
const uint32_t mkv_tracks_flaglacing=0x1c;
const uint32_t mkv_tracks_mincache=0x2de7;
const uint32_t mkv_tracks_tracktimecodescale=0x3314f;
const uint32_t mkv_tracks_maxblockadditionid=0x15ee;
const uint32_t mkv_tracks_codecid=0x6;
const uint32_t mkv_tracks_codecdecodeall=0x2A;
const uint32_t mkv_tracks_defaultduration=0x3e383;
const uint32_t mkv_tracks_codecprivate=0x23a2;
const uint32_t mkv_tracks_language=0x2b59c;
const uint32_t mkv_tracks_name=0x136e;
const uint32_t mkv_tracks_maxcache=0x2df8;
// Track - video settings
const uint32_t mkv_tracks_video=0x60;
const uint32_t mkv_video_pixelwidth = 0x30;
const uint32_t mkv_video_pixelheight = 0x3a;
const uint32_t mkv_video_flaginterlaced = 0x1a;
const uint32_t mkv_video_displaywidth = 0x14b0;
const uint32_t mkv_video_displayheight = 0x14ba;
const uint32_t mkv_video_pixelcropbottom = 0x14aa;
const uint32_t mkv_video_pixelcroptop = 0x14bb;
const uint32_t mkv_video_pixelcropleft = 0x14cc;
const uint32_t mkv_video_pixelcropright = 0x14dd;
// Track - audio settings;
const uint32_t mkv_tracks_audio=0x61;
const uint32_t mkv_audio_samplingfrequency=0x35;
const uint32_t mkv_audio_channels = 0x1f;
const uint32_t mkv_audio_output_samplingfrequency=0x38b5;
// Track Types
enum
{
mkv_track_type_video = 0x01,
mkv_track_type_audio = 0x02,
mkv_track_type_complex = 0x03, // i.e., combined video and audio
mkv_track_type_logo = 0x10,
mkv_track_type_subtitle = 0x11,
mkv_track_type_buttons = 0x12,
mkv_track_type_control = 0x20,
};
/* TODO: benski>
*/
namespace nsmkv
{
#pragma pack(push, 8)
struct VideoData
{
size_t struct_size;
uint64_t pixel_width;
uint64_t pixel_height;
uint64_t pixel_crop_bottom;
uint64_t pixel_crop_top;
uint64_t pixel_crop_left;
uint64_t pixel_crop_right;
uint64_t display_width;
uint64_t display_height;
uint64_t display_unit;
bool flag_interlaced;
};
#pragma pack(pop)
class Video : public VideoData
{
public:
Video()
#ifdef WA_VALIDATE
:
pixel_width_found(false),
pixel_height_found(false),
pixel_crop_bottom_found(false),
pixel_crop_top_found(false),
pixel_crop_left_found(false),
pixel_crop_right_found(false),
display_width_found(false),
display_height_found(false),
// display_unit_found(false),
flag_interlaced_found(false)
#endif
{
struct_size = sizeof(VideoData);
pixel_width=0;
pixel_height=0;
pixel_crop_bottom=0;
pixel_crop_top=0;
pixel_crop_left=0;
pixel_crop_right=0;
display_width=0;
display_height=0;
display_unit=0;
flag_interlaced=false;
}
#ifdef WA_VALIDATE
bool pixel_width_found;
bool pixel_height_found;
bool pixel_crop_bottom_found;
bool pixel_crop_top_found;
bool pixel_crop_left_found;
bool pixel_crop_right_found;
bool display_width_found;
bool display_height_found;
// bool display_unit_found;
bool flag_interlaced_found;
#endif
};
#pragma pack(push, 8)
struct AudioData
{
size_t struct_size;
uint64_t sampling_frequency;
uint64_t channels;
uint64_t bit_depth;
uint64_t output_sampling_frequency;
};
#pragma pack(pop)
class Audio : public AudioData
{
public:
Audio()
#ifdef WA_VALIDATE
:
sampling_frequency_found(false),
channels_found(false),
// bit_depth_found(false),
output_sampling_frequency_found(false)
#endif
{
struct_size = sizeof(AudioData);
sampling_frequency=8000;
channels=1;
bit_depth=0;
output_sampling_frequency=0;
}
#ifdef WA_VALIDATE
bool sampling_frequency_found;
bool channels_found;
// bool bit_depth_found;
bool output_sampling_frequency_found;
#endif
};
#pragma pack(push, 8)
struct TrackEntryData
{
size_t struct_size;
uint64_t track_number;
uint64_t track_uid;
uint64_t track_type;
bool flag_enabled;
bool flag_default;
bool flag_forced;
bool flag_lacing;
bool decode_all;
uint64_t min_cache;
uint64_t max_cache;
uint64_t default_duration;
double track_timecode_scale;
uint64_t max_block_additional_id;
char *name;
char *language;
char *codec_id;
void *codec_private;
size_t codec_private_len;
char *codec_name;
uint64_t attachment_link;
};
#pragma pack(pop)
class TrackEntry : public TrackEntryData
{
public:
TrackEntry()
#ifdef WA_VALIDATE
:
track_number_found(false),
track_uid_found(false),
track_type_found(false),
flag_enabled_found(false),
flag_default_found(false),
flag_forced_found(false),
flag_lacing_found(false),
min_cache_found(false),
max_cache_found(false),
default_duration_found(false),
track_timecode_scale_found(false),
max_block_additional_id_found(false),
decode_all_found(false),
name_found(false),
language_found(false),
codec_id_found(false),
codec_private_found(false),
codec_name_found(false)
// attachment_link_found(false)
#endif
{
struct_size = sizeof(TrackEntryData);
track_number = 0;
track_uid = 0;
track_type = 0;
flag_enabled = true;
flag_default = true;
flag_forced = false;
flag_lacing = false;
min_cache = 0;
max_cache = 0;
default_duration = 0;
track_timecode_scale = 0;
max_block_additional_id = 0;
decode_all = true;
name = 0;
language = 0;
codec_id = 0;
codec_private = 0;
codec_private_len = 0;
codec_name = 0;
attachment_link = 0;
}
~TrackEntry()
{
free(name);
free(language);
free(codec_id);
free(codec_private);
free(codec_name);
}
void Own(char *&field, char *value)
{
if (field)
free(field);
field = value;
}
void OwnCodecPrivate(void *_codec_private, size_t _codec_private_len)
{
free(codec_private);
codec_private=_codec_private;
codec_private_len = _codec_private_len;
}
Video video;
Audio audio;
#ifdef WA_VALIDATE
bool track_number_found;
bool track_uid_found;
bool track_type_found;
bool flag_enabled_found;
bool flag_default_found;
bool flag_forced_found;
bool flag_lacing_found;
bool min_cache_found;
bool max_cache_found;
bool default_duration_found;
bool track_timecode_scale_found;
bool max_block_additional_id_found;
bool decode_all_found;
bool name_found;
bool language_found;
bool codec_id_found;
bool codec_private_found;
bool codec_name_found;
// bool attachment_link_found;
#endif
};
class Tracks
{
public:
~Tracks()
{
//tracks.deleteAll();
for (auto obj : tracks)
{
delete obj;
}
tracks.clear();
}
const nsmkv::TrackEntry *EnumTrack(size_t i) const;
typedef std::vector<nsmkv::TrackEntry*> TrackEntryList;
TrackEntryList tracks;
};
uint64_t ReadTracks(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Tracks &tracks);
}

52
Src/nsmkv/ebml_float.cpp Normal file
View File

@ -0,0 +1,52 @@
#include "ebml_float.h"
double float_read_ptr_len(uint64_t len, const uint8_t *ptr)
{
// TODO: big endian support
if (len == 4)
{
float val;
uint8_t *dest = (uint8_t *)&val;
dest[3]=ptr[0];
dest[2]=ptr[1];
dest[1]=ptr[2];
dest[0]=ptr[3];
return val;
}
else if (len == 8)
{
double val;
uint8_t *dest = (uint8_t *)&val;
dest[7]=ptr[0];
dest[6]=ptr[1];
dest[5]=ptr[2];
dest[4]=ptr[3];
dest[3]=ptr[4];
dest[2]=ptr[5];
dest[1]=ptr[6];
dest[0]=ptr[7];
return val;
}
else if (len == 10)
{
long double val;
memset(&val, 0, sizeof(val));
uint8_t *dest = (uint8_t *)&val;
dest[9]=ptr[0];
dest[8]=ptr[1];
dest[7]=ptr[2];
dest[6]=ptr[3];
dest[5]=ptr[4];
dest[4]=ptr[5];
dest[3]=ptr[6];
dest[2]=ptr[7];
dest[1]=ptr[8];
dest[0]=ptr[9];
return val;
}
else
return 0;
}

6
Src/nsmkv/ebml_float.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <bfc/platform/types.h>
/* len is passed as uint64_t, but better be 8 or less! */
double float_read_ptr_len(uint64_t len, const uint8_t *ptr);

15
Src/nsmkv/ebml_signed.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "ebml_signed.h"
#include "ebml_unsigned.h"
int64_t signed_read_ptr_len(uint64_t len, const uint8_t *ptr)
{
int64_t val = -1;
uint8_t *dest = (uint8_t *)&val;
for (int64_t i=0;i!=len;i++)
{
dest[len-i-1]=ptr[i];
}
return val;
}

5
Src/nsmkv/ebml_signed.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <bfc/platform/types.h>
/* len is passed as uint64_t, but better be 8 or less! */
int64_t signed_read_ptr_len(uint64_t len, const uint8_t *ptr);

View File

@ -0,0 +1,13 @@
#include "ebml_unsigned.h"
uint64_t unsigned_read_ptr_len(uint64_t len, const uint8_t *ptr)
{
uint64_t val=*ptr++;
while (--len)
{
val <<= 8;
val |= *ptr++;
}
return val;
}

View File

@ -0,0 +1,6 @@
#pragma once
#include <bfc/platform/types.h>
/* len is passed as uint64_t, but better be 8 or less! */
uint64_t unsigned_read_ptr_len(uint64_t len, const uint8_t *ptr);

View File

@ -0,0 +1,55 @@
#include "file_mkv_reader.h"
MKVReaderFILE::MKVReaderFILE(FILE *f) : f(f)
{
}
MKVReaderFILE::MKVReaderFILE(const wchar_t *filename)
{
f = _wfopen(filename, L"rb");
}
int MKVReaderFILE::Read(void *buffer, size_t read_length, size_t *bytes_read)
{
*bytes_read = fread(buffer, 1, read_length, f);
return nsmkv::READ_OK;
}
int MKVReaderFILE::Peek(void *buffer, size_t read_length, size_t *bytes_read)
{
*bytes_read = fread(buffer, 1, read_length, f);
fseek(f, (long)(-read_length), SEEK_CUR);
return nsmkv::READ_OK;
}
int MKVReaderFILE::Seek(uint64_t position)
{
fsetpos(f, (const fpos_t *)&position);
return nsmkv::READ_OK;
}
uint64_t MKVReaderFILE::Tell()
{
uint64_t pos;
fgetpos(f, (fpos_t *)&pos);
return pos;
}
int MKVReaderFILE::Skip(uint64_t skip_bytes)
{
_fseeki64(f, skip_bytes, SEEK_CUR);
return nsmkv::READ_OK;
}
MKVReaderFILE::~MKVReaderFILE()
{
fclose(f);
}
uint64_t MKVReaderFILE::GetContentLength()
{
uint64_t old = Tell();
Seek(0);
uint64_t content_length = Tell();
Seek(old);
return content_length;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "mkv_reader.h"
#include <stdio.h>
class MKVReaderFILE : public nsmkv::MKVReader
{
public:
MKVReaderFILE(FILE *f);
MKVReaderFILE(const wchar_t *filename);
~MKVReaderFILE();
/* avi_reader implementation */
int Read(void *buffer, size_t read_length, size_t *bytes_read);
int Peek(void *buffer, size_t read_length, size_t *bytes_read);
int Seek(uint64_t position);
uint64_t Tell();
int Skip(uint64_t skip_bytes);
void GetFilename(wchar_t *fn, size_t fn_len) {}
uint64_t GetContentLength();
private:
FILE *f;
};

View File

@ -0,0 +1,29 @@
#include "global_elements.h"
#include "read.h"
uint64_t nsmkv::ReadGlobal(nsmkv::MKVReader *reader, uint64_t id, uint64_t size)
{
switch(id)
{
case mkv_void:
#ifdef WA_VALIDATE
printf("void (empty), size:%I64u\n", size);
#endif
reader->Skip(size);
return size;
default:
#ifdef WA_VALIDATE
printf("*** UNKNOWN ID *** ID:%I64x size:%I64u\n", id, size);
#endif
reader->Skip(size);
return size;
}
}
uint64_t nsmkv::SkipNode(nsmkv::MKVReader *reader, uint64_t id, uint64_t size)
{
reader->Skip(size);
return size;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "mkv_reader.h"
#include <bfc/platform/types.h>
// IDs
// these are slightly different from the matroska spec because we specify
// values after vint decoding and they specify before
const uint32_t mkv_void=0x6C;
const uint32_t mkv_crc=0x3F;
namespace nsmkv
{
// doesn't really do anything but fseek, but will output unknown values in debug mode
uint64_t ReadGlobal(nsmkv::MKVReader *reader, uint64_t id, uint64_t size);
uint64_t SkipNode(nsmkv::MKVReader *reader, uint64_t id, uint64_t size); // same thing as ReadGlobal but doesn't display unknown nodes
}

138
Src/nsmkv/header.cpp Normal file
View File

@ -0,0 +1,138 @@
#include "header.h"
#include "read.h"
#include "global_elements.h"
#ifdef WA_VALIDATE
extern uint64_t max_id_length;
extern uint64_t max_size_length;
#endif
// returns bytes read. 0 means EOF
uint64_t nsmkv::ReadHeader(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Header &header)
{
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_header_doctype:
{
char *utf8=0;
if (read_utf8(reader, node.size, &utf8) == 0)
return 0;
header.OwnDocType(utf8);
#ifdef WA_VALIDATE
header.doctype_found = true;
printf(" DocType: %s\n", header.doctype);
#endif
}
break;
case mkv_header_doctype_version:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
header.doctype_version = val;
#ifdef WA_VALIDATE
header.doctype_version_found = true;
printf(" DocType Version: %I64u\n", header.doctype_version);
#endif
}
break;
case mkv_header_doctype_read_version:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
header.doctype_read_version = val;
#ifdef WA_VALIDATE
header.doctype_read_version_found = true;
printf(" DocType Read Version: %I64u\n", header.doctype_read_version);
#endif
}
break;
case mkv_header_ebml_version:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
header.ebml_version = val;
#ifdef WA_VALIDATE
header.ebml_version_found = true;
printf(" EBML Version: %I64u\n", header.ebml_version);
#endif
}
break;
case mkv_header_ebml_read_version:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
header.ebml_read_version = val;
#ifdef WA_VALIDATE
header.ebml_read_version_found = true;
printf(" EBML Read Version: %I64u\n", header.ebml_read_version);
#endif
}
break;
case mkv_header_ebml_max_id_length:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
header.ebml_max_id_length = val;
#ifdef WA_VALIDATE
max_id_length = val;
header.ebml_max_id_length_found = true;
printf(" EBML Max ID Length: %I64u\n", header.ebml_max_id_length);
#endif
}
break;
case mkv_header_ebml_max_size_length:
{
uint64_t val;
if (read_unsigned(reader, node.size, &val) == 0)
return 0;
header.ebml_max_size_length = val;
#ifdef WA_VALIDATE
max_size_length = val;
header.ebml_max_size_length_found = true;
printf(" EBML Max Size Length: %I64u\n", header.ebml_max_size_length);
#endif
}
break;
default:
{
if (ReadGlobal(reader, node.id, node.size) == 0)
return 0;
}
}
}
return total_bytes_read;
}

79
Src/nsmkv/header.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#include <bfc/platform/types.h>
#include "mkv_reader.h"
// IDs
// these are slightly different from the matroska spec because we specify
// values after vint decoding and they specify before
const uint32_t mkv_header=0xa45dfa3;
const uint32_t mkv_header_ebml_version=0x286;
const uint32_t mkv_header_ebml_read_version=0x2f7;
const uint32_t mkv_header_ebml_max_id_length=0x2f2;
const uint32_t mkv_header_ebml_max_size_length=0x2f3;
const uint32_t mkv_header_doctype=0x282;
const uint32_t mkv_header_doctype_read_version=0x285;
const uint32_t mkv_header_doctype_version=0x287;
namespace nsmkv
{
class Header
{
public:
// defaults provided as per spec for matroska
// *_found variables indicate whether the field was found in the file
Header() :
ebml_version(1),
ebml_read_version(1),
ebml_max_id_length(4),
ebml_max_size_length(8),
doctype(0),
doctype_version(1),
doctype_read_version(1),
ebml_header_found(false)
#ifdef WA_VALIDATE
,
ebml_version_found(false),
ebml_read_version_found(false),
ebml_max_id_length_found(false),
ebml_max_size_length_found(false),
doctype_version_found(false),
doctype_found(false),
doctype_read_version_found(false)
#endif
{
}
~Header()
{
if (doctype)
free(doctype);
}
void OwnDocType(char *_doctype)
{
if (doctype)
free(doctype);
doctype = _doctype;
}
uint64_t ebml_version;
uint64_t ebml_read_version;
uint64_t ebml_max_id_length;
uint64_t ebml_max_size_length;
char *doctype;
uint64_t doctype_version;
uint64_t doctype_read_version;
bool ebml_header_found;
#ifdef WA_VALIDATE
bool ebml_version_found;
bool ebml_read_version_found;
bool ebml_max_id_length_found;
bool doctype_found;
bool ebml_max_size_length_found;
bool doctype_version_found;
bool doctype_read_version_found;
#endif
};
// returns bytes read. 0 means EOF
uint64_t ReadHeader(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Header &header);
};

226
Src/nsmkv/main.cpp Normal file
View File

@ -0,0 +1,226 @@
#include "vint.h"
#include "header.h"
#include "ebml_float.h"
#include "segment.h"
#include "ebml_unsigned.h"
#include "ebml_signed.h"
#include <stdio.h>
#include <assert.h>
#include "SeekTable.h"
#include <time.h>
#include "parser.h"
#include "SegmentInfo.h"
#include "global_elements.h"
#include "Tracks.h"
#include "Cluster.h"
#include "Cues.h"
#include "Chapters.h"
#include "Tags.h"
#include "read.h"
#include "Attachments.h"
using namespace nsmkv;
static nsmkv::SeekTable seekTable;
static nsmkv::Header header;
static SegmentInfo segment_info;
static Tracks tracks;
static Cues cues;
static Attachments attachments;
static Tags tags;
uint64_t max_id_length = header.ebml_max_id_length;
uint64_t max_size_length = header.ebml_max_size_length;
uint32_t num_seekhead_elements_found = 0;
uint32_t num_seek_elements_found = 0;
bool ebml_segment_found = false;
uint64_t segment_data_offset = 0;
// returns bytes read. 0 means EOF
uint64_t read_vsint(FILE *f, int64_t *val)
{
uint8_t data[9];
size_t bytes_read = fread(data, 1, 1, f);
if (bytes_read != 1)
return 0;
uint8_t length = vint_get_number_bytes(data[0]);
bytes_read = fread(data+1, 1, length, f);
if (bytes_read != length)
return 0;
*val = vsint_read_ptr_len(length+1, data);
return bytes_read+1;
}
char *read_utf8(FILE *f, size_t size)
{
char *doctype = (char *)malloc(size + 1);
if (doctype)
{
doctype[size]=0;
if (fread(doctype, 1, size, f) == size)
return doctype;
}
free(doctype);
return 0;
}
// returns bytes read. 0 means EOF
uint64_t ReadSegment(FILE *f, uint64_t size)
{
uint64_t total_bytes_read=0;
// store the segment element data offset for later use
segment_data_offset = ftell(f);
#ifdef WA_VALIDATE
printf("[%I64u] Segment element data offset\n", segment_data_offset);
#endif
while (size)
{
uint64_t this_position = ftell(f);
#ifdef WA_VALIDATE
printf("[%I64u] ", this_position);
#endif
ebml_node node;
uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_segment_attachments:
{
printf(" Attachments\n");
ReadAttachment((nsmkv::MKVReader*)f, node.size, attachments);
}
break;
case mkv_metaseek_seekhead:
{
printf(" SeekHead\n");
ReadSeekHead((nsmkv::MKVReader*)f, node.size, seekTable);
}
break;
case mkv_segment_segmentinfo:
{
printf(" SegmentInfo\n");
ReadSegmentInfo((nsmkv::MKVReader*)f, node.size, segment_info);
}
break;
case mkv_segment_tracks:
{
printf(" Tracks\n");
ReadTracks((nsmkv::MKVReader*)f, node.size, tracks);
}
break;
case mkv_segment_cues:
{
printf(" Cues\n");
ReadCues((nsmkv::MKVReader*)f, node.size, cues);
}
break;
case mkv_segment_cluster:
{
printf(" Clusters\n");
nsmkv::Cluster cluster;
ReadCluster((nsmkv::MKVReader*)f, node.size, cluster);
}
break;
case mkv_segment_chapters:
{
printf(" Chapters\n");
}
break;
case mkv_segment_tags:
{
printf(" Tags\n");
nsmkv::ReadTags(f, node.size, tags);
}
break;
default:
ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
}
}
return total_bytes_read;
}
int main()
{
// char *file_in = "\\\\o2d2\\ftp\\usr\\nullsoft\\test media\\mkv\\Ratatouille.2007.nHD.720.x264.NhaNc3.mkv";
//char *file_in = "\\\\o2d2\\ftp\\usr\\nullsoft\\test media\\mkv\\cham_mp4v_aac.mkv";
char *file_in = "c:/users/benski/desktop/metadata.mkv";
FILE *f = fopen(file_in, "rb");
if (f == NULL)
{
printf("****Error attempting to open file: %s\n",file_in);
return -1;
}
else
{
printf("Starting Processing of File: %s\n",file_in);
}
ebml_node node;
while (read_ebml_node((nsmkv::MKVReader*)f, &node))
{
switch(node.id)
{
case mkv_header:
if (header.ebml_header_found == false)
{
#ifdef WA_VALIDATE
printf("MKV header found, processing...\n");
#endif
header.ebml_header_found = true;
nsmkv::ReadHeader((nsmkv::MKVReader*)f, node.size, header);
}
else
{
#ifdef WA_VALIDATE
printf("Extra MKV header found, ignoring...\n");
#endif
nsmkv::Header extraHeader;
nsmkv::ReadHeader((nsmkv::MKVReader*)f, node.size, extraHeader);
}
break;
case mkv_segment:
printf("MKV Segment element found, processing\n");
#ifdef WA_VALIDATE
ebml_segment_found = true;
#endif
ReadSegment(f, node.size);
break;
default:
ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
}
}
// seekTable.Dump();
fclose(f);
printf("Number of SeekHead elements found: %I32u\n",num_seekhead_elements_found);
printf("Number of Seek elements found: %I32u\n",num_seek_elements_found);
}

8
Src/nsmkv/mkv_date.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "mkv_date.h"
__time64_t mkv_date_as_time_t(mkv_date_t val)
{
__time64_t val_time = (__time64_t)val / 1000ULL /*nano->micro*/ / 1000ULL /*micro->milli*/ / 1000ULL /*milli->second*/;
val_time+=978325200ULL;
return val_time;
}

5
Src/nsmkv/mkv_date.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <bfc/platform/types.h>
typedef uint64_t mkv_date_t;
__time64_t mkv_date_as_time_t(mkv_date_t d);

43
Src/nsmkv/mkv_reader.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <bfc/platform/types.h>
#include <stdio.h>
namespace nsmkv
{
// return codes from mkv_reader functions
enum
{
READ_OK = 0,
READ_EOF = 1,
READ_FAILED = 2,
READ_INVALID_DATA = 3, // read was successful but data didn't make any sense
READ_INVALID_CALL = 4, // wrong time to call this function
READ_NOT_FOUND = 5, // requested item doesn't exist in the file
READ_OUT_OF_MEMORY = 6, // some malloc failed and so we're aborting
READ_DISCONNECT = 7,
};
class MKVReader
{
public:
virtual int Read(void *buffer, size_t read_length, size_t *bytes_read)=0;
// TODO: need to put an upper bound on Peek buffer sizes
virtual int Peek(void *buffer, size_t read_length, size_t *bytes_read)=0;
// in_mkv will call this before descending into certain chunks that will be read entirely (e.g. avih)
// you aren't required to do anything in response
virtual void OverlappedHint(uint32_t read_length){}
virtual int Seek(uint64_t position)=0;
virtual uint64_t Tell()=0;
// skip ahead a certain number of bytes. equivalent to fseek(..., SEEK_CUR)
virtual int Skip(uint64_t skip_bytes)=0;
virtual uint64_t GetContentLength()=0;
virtual void GetFilename(wchar_t *fn, size_t len)=0;
};
}

10
Src/nsmkv/nsmkv.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "header.h"
#include "read.h"
#include "global_elements.h"
#include "segment.h"
#include "SeekTable.h"
#include "SegmentInfo.h"
#include "Tracks.h"
#include "Cluster.h"
#include "Lacing.h"

19
Src/nsmkv/nsmkv.sln Normal file
View File

@ -0,0 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsmkv", "nsmkv.vcproj", "{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Debug|Win32.ActiveCfg = Debug|Win32
{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Debug|Win32.Build.0 = Debug|Win32
{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Release|Win32.ActiveCfg = Release|Win32
{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

254
Src/nsmkv/nsmkv.vcxproj Normal file
View File

@ -0,0 +1,254 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}</ProjectGuid>
<RootNamespace>nsmkv</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>17.0.32505.173</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnableManifest>false</VcpkgEnableManifest>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgConfiguration>Debug</VcpkgConfiguration>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Vcpkg">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
<VcpkgConfiguration>Debug</VcpkgConfiguration>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Vcpkg">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;WA_VALIDATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../h264dec/ldecod/inc;../h264dec/lcommon/inc;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;WA_VALIDATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>../h264dec/ldecod/inc;../h264dec/lcommon/inc;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>../h264dec/ldecod/inc;../h264dec/lcommon/inc;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Attachments.cpp" />
<ClCompile Include="Chapters.cpp" />
<ClCompile Include="Cluster.cpp" />
<ClCompile Include="Cues.cpp" />
<ClCompile Include="ebml_float.cpp" />
<ClCompile Include="ebml_signed.cpp" />
<ClCompile Include="ebml_unsigned.cpp" />
<ClCompile Include="global_elements.cpp" />
<ClCompile Include="header.cpp" />
<ClCompile Include="Lacing.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="mkv_date.cpp" />
<ClCompile Include="read.cpp" />
<ClCompile Include="SeekTable.cpp" />
<ClCompile Include="SegmentInfo.cpp" />
<ClCompile Include="Tags.cpp" />
<ClCompile Include="Tracks.cpp" />
<ClCompile Include="vint.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Attachments.h" />
<ClInclude Include="Chapters.h" />
<ClInclude Include="Cluster.h" />
<ClInclude Include="Cues.h" />
<ClInclude Include="ebml_float.h" />
<ClInclude Include="ebml_signed.h" />
<ClInclude Include="ebml_unsigned.h" />
<ClInclude Include="global_elements.h" />
<ClInclude Include="header.h" />
<ClInclude Include="Lacing.h" />
<ClInclude Include="mkv_date.h" />
<ClInclude Include="read.h" />
<ClInclude Include="SeekTable.h" />
<ClInclude Include="segment.h" />
<ClInclude Include="SegmentInfo.h" />
<ClInclude Include="Tags.h" />
<ClInclude Include="Tracks.h" />
<ClInclude Include="vint.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wasabi\Wasabi.vcxproj">
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx</Extensions>
</Filter>
<Filter Include="ebml">
<UniqueIdentifier>{812f40e9-f7c9-40ae-9973-aa073d98f2ce}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Attachments.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Chapters.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Cluster.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Cues.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="global_elements.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="header.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Lacing.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mkv_date.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="read.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SeekTable.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SegmentInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Tags.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Tracks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vint.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ebml_float.cpp">
<Filter>ebml</Filter>
</ClCompile>
<ClCompile Include="ebml_signed.cpp">
<Filter>ebml</Filter>
</ClCompile>
<ClCompile Include="ebml_unsigned.cpp">
<Filter>ebml</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Attachments.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Chapters.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Cluster.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Cues.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="global_elements.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="header.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mkv_date.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="read.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SeekTable.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="segment.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SegmentInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Tags.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Tracks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vint.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ebml_float.h">
<Filter>ebml</Filter>
</ClInclude>
<ClInclude Include="ebml_signed.h">
<Filter>ebml</Filter>
</ClInclude>
<ClInclude Include="ebml_unsigned.h">
<Filter>ebml</Filter>
</ClInclude>
<ClInclude Include="Lacing.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

154
Src/nsmkv/read.cpp Normal file
View File

@ -0,0 +1,154 @@
#include "read.h"
#include "vint.h"
#include "ebml_float.h"
#include "ebml_unsigned.h"
#include "ebml_signed.h"
#include <limits.h>
#ifdef WA_VALIDATE
extern uint64_t max_id_length;
extern uint64_t max_size_length;
#endif
// returns bytes read. 0 means EOF
uint64_t read_vint(nsmkv::MKVReader *reader, uint64_t *val)
{
uint8_t data[9] = {0};
size_t bytes_read = 0;
reader->Read(data, 1, &bytes_read);
if (bytes_read != 1)
return 0;
uint8_t length = vint_get_number_bytes(data[0]);
reader->Read(data+1, length, &bytes_read);
if (bytes_read != length)
return 0;
*val = vint_read_ptr(data);
return bytes_read+1;
}
// returns bytes read. 0 means EOF
uint64_t read_ebml_node(nsmkv::MKVReader *reader, ebml_node *node)
{
uint64_t bytes_read = read_vint(reader, &node->id);
if (!bytes_read)
return 0;
bytes_read += read_vint(reader, &node->size);
return bytes_read;
}
uint64_t read_utf8(nsmkv::MKVReader *reader, uint64_t size, char **utf8)
{
if (utf8)
{
if (size == SIZE_MAX) // prevent integer overflow
return 0;
char *&val = *utf8;
val = (char *)calloc((size_t)size + 1, sizeof(char));
if (val)
{
val[size]=0;
size_t bytes_read;
reader->Read(val, (size_t)size, &bytes_read);
if (bytes_read != (size_t)size)
{
free(val);
return 0;
}
return size;
}
return 0; // actually, out of memory and not EOF, but still we should abort ASAP
}
else
{
reader->Skip(size);
return size;
}
}
#if 0
int fseek64(nsmkv::MKVReader *reader, int64_t pos, int whence)
{
switch(whence)
{
case SEEK_SET:
return fsetpos(f, &pos);
case SEEK_CUR:
{
fpos_t curpos=0;
int ret = fgetpos(f, &curpos);
if (ret != 0)
return ret;
pos+=curpos;
return fsetpos(f, &pos);
}
case SEEK_END:
{
return _fseeki64(f, pos, SEEK_END);
}
}
return 1;
}
int64_t ftell64(nsmkv::MKVReader *reader)
{
fpos_t pos;
if (fgetpos(f, &pos) == 0)
return pos;
else
return -1L;
}
#endif
uint64_t read_unsigned(nsmkv::MKVReader *reader, uint64_t size, uint64_t *val)
{
uint8_t data[8] = {0};
if (size == 0 || size > 8)
return 0;
size_t bytes_read = 0;
reader->Read(data, (size_t)size, &bytes_read);
if (bytes_read != size)
{
return 0;
}
*val = unsigned_read_ptr_len(size, data);
return size;
}
uint64_t read_float(nsmkv::MKVReader *reader, uint64_t size, double *val)
{
uint8_t data[10] = {0};
if (size == 0 || size > 10)
return 0;
size_t bytes_read = 0;
reader->Read(data, (size_t)size, &bytes_read);
if (bytes_read != size)
{
return 0;
}
*val = float_read_ptr_len(size, data);
return size;
}
uint64_t read_signed(nsmkv::MKVReader *reader, uint64_t size, int64_t *val)
{
uint8_t data[8] = {0};
if (size == 0 || size > 8)
return 0;
size_t bytes_read = 0;
reader->Read(data, (size_t)size, &bytes_read);
if (bytes_read != size)
{
return 0;
}
*val = signed_read_ptr_len(size, data);
return size;
}

18
Src/nsmkv/read.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <windows.h>
#include <bfc/platform/types.h>
#include "mkv_reader.h"
struct ebml_node
{
uint64_t id;
uint64_t size;
};
// returns bytes read. 0 means EOF
uint64_t read_ebml_node(nsmkv::MKVReader *reader, ebml_node *node);
uint64_t read_vint(nsmkv::MKVReader *reader, uint64_t *val);
uint64_t read_utf8(nsmkv::MKVReader *reader, uint64_t size, char **utf8);
uint64_t read_unsigned(nsmkv::MKVReader *reader, uint64_t size, uint64_t *val);
uint64_t read_float(nsmkv::MKVReader *reader, uint64_t size, double *val);
uint64_t read_signed(nsmkv::MKVReader *reader, uint64_t size, int64_t *val);

14
Src/nsmkv/segment.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <bfc/platform/types.h>
// IDs
// these are slightly different from the matroska spec because we specify
// values after vint decoding and they specify before
// Header
const uint32_t mkv_segment = 0x8538067;
// Cluster
// Cueing Data

91
Src/nsmkv/vint.cpp Normal file
View File

@ -0,0 +1,91 @@
#include <bfc/platform/types.h>
#ifdef _MSC_VER
#include <intrin.h>
static uint32_t __inline clz(uint32_t value)
{
DWORD leading_zero = 0;
if (_BitScanReverse(&leading_zero, value))
{
return 31 - leading_zero;
}
else
{
return 32;
}
}
#endif
uint8_t vint_get_number_bytes(uint8_t first_byte)
{
return (uint8_t)clz((uint32_t)first_byte) - 24;
}
static uint8_t masks[] =
{
0x7F, // 0111 1111
0x3F, // 0011 1111
0x1F, // 0001 1111
0x0F, // 0000 1111
0x07, // 0000 0111
0x03, // 0000 0011
0x01, // 0000 0001
0x00, // 0000 0000
};
/* call if you already know the len (e.g. from vint_get_number_bytes earlier */
uint64_t vint_read_ptr_len(uint8_t len, const uint8_t *ptr)
{
uint64_t ret = masks[len] & ptr[0];
while (len--)
{
ret <<= 8;
ret |= *++ptr;
}
return ret;
}
uint64_t vint_read_ptr(const uint8_t *ptr)
{
uint8_t len = vint_get_number_bytes(ptr[0]);
return vint_read_ptr_len(len, ptr);
}
bool vint_unknown_length(uint8_t len, const uint8_t *ptr)
{
if (masks[len] != (masks[len] & ptr[0]))
return false;
while (len--)
{
if (*++ptr == 0xFF)
return false;
}
return true;
}
static int64_t vsint_substr[] =
{
0x3F,
0x1FFF,
0x0FFFFF,
0x07FFFFFF,
0x03FFFFFFFF,
0x01FFFFFFFFFF,
0x00FFFFFFFFFFFF,
0x007FFFFFFFFFFFFF,
};
int64_t vsint_read_ptr_len(uint8_t len, const uint8_t *ptr)
{
uint64_t val = vint_read_ptr_len(len, ptr);
return val - vsint_substr[len];
}
int64_t vsint_read_ptr(const uint8_t *ptr)
{
uint8_t len = vint_get_number_bytes(ptr[0]);
uint64_t val = vint_read_ptr(ptr);
return val - vsint_substr[len];
}

14
Src/nsmkv/vint.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <bfc/platform/types.h>
uint8_t vint_get_number_bytes(uint8_t first_byte);
/* call if you already know the len (e.g. from vint_get_number_bytes earlier */
uint64_t vint_read_ptr_len(uint8_t len, const uint8_t *ptr);
int64_t vsint_read_ptr_len(uint8_t len, const uint8_t *ptr);
/* don't call this unless you're sure that you have enough room in the buffer! */
uint64_t vint_read_ptr(const uint8_t *ptr);
int64_t vsint_read_ptr(const uint8_t *ptr);
/* values encoded as all 1's are supposed to indicate 'unknown value' */
bool vint_unknown_length(uint8_t len, const uint8_t *ptr);