mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 10:25:46 -04:00
Initial community commit
This commit is contained in:
108
Src/nsmkv/Attachments.cpp
Normal file
108
Src/nsmkv/Attachments.cpp
Normal 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
47
Src/nsmkv/Attachments.h
Normal 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
9
Src/nsmkv/Chapters.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "Chapters.h"
|
||||
|
||||
nsmkv::Chapters::Chapters(void)
|
||||
{
|
||||
}
|
||||
|
||||
nsmkv::Chapters::~Chapters(void)
|
||||
{
|
||||
}
|
22
Src/nsmkv/Chapters.h
Normal file
22
Src/nsmkv/Chapters.h
Normal 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
213
Src/nsmkv/Cluster.cpp
Normal 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
106
Src/nsmkv/Cluster.h
Normal 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
205
Src/nsmkv/Cues.cpp
Normal 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
76
Src/nsmkv/Cues.h
Normal 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
111
Src/nsmkv/Lacing.cpp
Normal 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
19
Src/nsmkv/Lacing.h
Normal 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
230
Src/nsmkv/SeekTable.cpp
Normal 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
54
Src/nsmkv/SeekTable.h
Normal 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
235
Src/nsmkv/SegmentInfo.cpp
Normal 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
107
Src/nsmkv/SegmentInfo.h
Normal 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
202
Src/nsmkv/Tags.cpp
Normal 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
32
Src/nsmkv/Tags.h
Normal 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
561
Src/nsmkv/Tracks.cpp
Normal 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
292
Src/nsmkv/Tracks.h
Normal 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
52
Src/nsmkv/ebml_float.cpp
Normal 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
6
Src/nsmkv/ebml_float.h
Normal 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
15
Src/nsmkv/ebml_signed.cpp
Normal 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
5
Src/nsmkv/ebml_signed.h
Normal 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);
|
13
Src/nsmkv/ebml_unsigned.cpp
Normal file
13
Src/nsmkv/ebml_unsigned.cpp
Normal 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;
|
||||
}
|
||||
|
6
Src/nsmkv/ebml_unsigned.h
Normal file
6
Src/nsmkv/ebml_unsigned.h
Normal 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);
|
55
Src/nsmkv/file_mkv_reader.cpp
Normal file
55
Src/nsmkv/file_mkv_reader.cpp
Normal 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;
|
||||
}
|
22
Src/nsmkv/file_mkv_reader.h
Normal file
22
Src/nsmkv/file_mkv_reader.h
Normal 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;
|
||||
};
|
29
Src/nsmkv/global_elements.cpp
Normal file
29
Src/nsmkv/global_elements.cpp
Normal 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;
|
||||
}
|
15
Src/nsmkv/global_elements.h
Normal file
15
Src/nsmkv/global_elements.h
Normal 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
138
Src/nsmkv/header.cpp
Normal 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
79
Src/nsmkv/header.h
Normal 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
226
Src/nsmkv/main.cpp
Normal 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
8
Src/nsmkv/mkv_date.cpp
Normal 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
5
Src/nsmkv/mkv_date.h
Normal 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
43
Src/nsmkv/mkv_reader.h
Normal 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
10
Src/nsmkv/nsmkv.h
Normal 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
19
Src/nsmkv/nsmkv.sln
Normal 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
254
Src/nsmkv/nsmkv.vcxproj
Normal 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>
|
132
Src/nsmkv/nsmkv.vcxproj.filters
Normal file
132
Src/nsmkv/nsmkv.vcxproj.filters
Normal 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
154
Src/nsmkv/read.cpp
Normal 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
18
Src/nsmkv/read.h
Normal 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
14
Src/nsmkv/segment.h
Normal 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
91
Src/nsmkv/vint.cpp
Normal 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
14
Src/nsmkv/vint.h
Normal 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);
|
Reference in New Issue
Block a user