Compare commits

..

1 Commits

Author SHA1 Message Date
Matthew Heaney
dca6be3a32 mkvstream example - initial release
Change-Id: Iea0402764f62f47dc4e2c6e717867673acd043f3
2012-10-12 14:40:03 -07:00
21 changed files with 684 additions and 2901 deletions

7
.gitignore vendored
View File

@@ -2,7 +2,6 @@
*.MKV
core
*.a
*.d
*.so*
*.o
*~
@@ -16,8 +15,4 @@ Debug
Release
*.sdf
*.opensdf
ipch
dumpvtt
sample
samplemuxer
vttdemux
ipch

View File

@@ -1,10 +1,13 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= libwebm
LOCAL_MODULE:= libmkvparser
LOCAL_SRC_FILES:= mkvparser.cpp \
mkvreader.cpp \
mkvmuxer.cpp \
mkvmuxerutil.cpp \
mkvwriter.cpp
mkvreader.cpp
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= mkvparser
LOCAL_SRC_FILES:= sample.cpp
LOCAL_STATIC_LIBRARIES:= libmkvparser
include $(BUILD_EXECUTABLE)

View File

@@ -1,5 +1,5 @@
CXX := g++
CXXFLAGS := -W -Wall -g -MMD -MP
CXXFLAGS := -W -Wall -g
LIBWEBMA := libwebm.a
LIBWEBMSO := libwebm.so
WEBMOBJS := mkvparser.o mkvreader.o mkvmuxer.o mkvmuxerutil.o mkvwriter.o
@@ -10,8 +10,6 @@ OBJECTS2 := sample_muxer.o vttreader.o webvttparser.o sample_muxer_metadata.o
OBJECTS3 := dumpvtt.o vttreader.o webvttparser.o
OBJECTS4 := vttdemux.o webvttparser.o
INCLUDES := -I.
DEPS := $(WEBMOBJS:.o=.d) $(OBJECTS1:.o=.d) $(OBJECTS2:.o=.d)
DEPS += $(OBJECTS3:.o=.d) $(OBJECTS4:.o=.d)
EXES := samplemuxer sample dumpvtt vttdemux
all: $(EXES)
@@ -46,8 +44,4 @@ libwebm.so: $(OBJSSO)
$(CXX) -c $(CXXFLAGS) -fPIC $(INCLUDES) $< -o $@
clean:
$(RM) -f $(OBJECTS1) $(OBJECTS2) $(OBJECTS3) $(OBJECTS4) $(OBJSA) $(OBJSSO) $(LIBWEBMA) $(LIBWEBMSO) $(EXES) $(DEPS) Makefile.bak
ifneq ($(MAKECMDGOALS), clean)
-include $(DEPS)
endif
$(RM) -f $(OBJECTS1) $(OBJECTS2) $(OBJECTS3) $(OBJECTS4) $(OBJSA) $(OBJSSO) $(LIBWEBMA) $(LIBWEBMSO) $(EXES) Makefile.bak

File diff suppressed because it is too large Load Diff

View File

@@ -14,14 +14,9 @@
// For a description of the WebM elements see
// http://www.webmproject.org/code/specs/container/.
namespace mkvparser {
class IMkvReader;
} // end namespace
namespace mkvmuxer {
class MkvWriter;
class Segment;
///////////////////////////////////////////////////////////////
// Interface used by the mkvmuxer to write out the Mkv data.
@@ -59,10 +54,6 @@ class IMkvWriter {
// before any other libwebm writing functions are called.
bool WriteEbmlHeader(IMkvWriter* writer);
// Copies in Chunk from source to destination between the given byte positions
bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst,
int64 start, int64 size);
///////////////////////////////////////////////////////////////
// Class to hold data the will be written to a block.
class Frame {
@@ -160,10 +151,7 @@ class Cues {
// Returns the cue point by index. Returns NULL if there is no cue point
// match.
CuePoint* GetCueByIndex(int32 index) const;
// Returns the total size of the Cues element
uint64 Size();
const CuePoint* GetCueByIndex(int32 index) const;
// Output the Cues element to the writer. Returns true on success.
bool Write(IMkvWriter* writer) const;
@@ -278,8 +266,7 @@ class ContentEncoding {
// Track element.
class Track {
public:
// The |seed| parameter is used to synthesize a UID for the track.
explicit Track(unsigned int* seed);
Track();
virtual ~Track();
// Adds a ContentEncoding element to the Track. Returns true on success.
@@ -307,17 +294,12 @@ class Track {
const uint8* codec_private() const { return codec_private_; }
void set_language(const char* language);
const char* language() const { return language_; }
void set_max_block_additional_id(uint64 max_block_additional_id) {
max_block_additional_id_ = max_block_additional_id;
}
uint64 max_block_additional_id() const { return max_block_additional_id_; }
void set_name(const char* name);
const char* name() const { return name_; }
void set_number(uint64 number) { number_ = number; }
uint64 number() const { return number_; }
void set_type(uint64 type) { type_ = type; }
uint64 type() const { return type_; }
void set_uid(uint64 uid) { uid_ = uid; }
uint64 uid() const { return uid_; }
uint64 codec_private_length() const { return codec_private_length_; }
@@ -326,15 +308,17 @@ class Track {
}
private:
// Returns a random number to be used for the Track UID.
static uint64 MakeUID();
// Track element names
char* codec_id_;
uint8* codec_private_;
char* language_;
uint64 max_block_additional_id_;
char* name_;
uint64 number_;
uint64 type_;
uint64 uid_;
const uint64 uid_;
// Size of the CodecPrivate data in bytes.
uint64 codec_private_length_;
@@ -345,6 +329,9 @@ class Track {
// Number of ContentEncoding elements added.
uint32 content_encoding_entries_size_;
// Flag telling if the rand call was seeded.
static bool is_seeded_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track);
};
@@ -361,13 +348,7 @@ class VideoTrack : public Track {
kSideBySideRightIsFirst = 11
};
enum AlphaMode {
kNoAlpha = 0,
kAlpha = 1
};
// The |seed| parameter is used to synthesize a UID for the track.
explicit VideoTrack(unsigned int* seed);
VideoTrack();
virtual ~VideoTrack();
// Returns the size in bytes for the payload of the Track element plus the
@@ -380,9 +361,6 @@ class VideoTrack : public Track {
// Sets the video's stereo mode. Returns true on success.
bool SetStereoMode(uint64 stereo_mode);
// Sets the video's alpha mode. Returns true on success.
bool SetAlphaMode(uint64 alpha_mode);
void set_display_height(uint64 height) { display_height_ = height; }
uint64 display_height() const { return display_height_; }
void set_display_width(uint64 width) { display_width_ = width; }
@@ -392,7 +370,6 @@ class VideoTrack : public Track {
void set_height(uint64 height) { height_ = height; }
uint64 height() const { return height_; }
uint64 stereo_mode() { return stereo_mode_; }
uint64 alpha_mode() { return alpha_mode_; }
void set_width(uint64 width) { width_ = width; }
uint64 width() const { return width_; }
@@ -406,7 +383,6 @@ class VideoTrack : public Track {
double frame_rate_;
uint64 height_;
uint64 stereo_mode_;
uint64 alpha_mode_;
uint64 width_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack);
@@ -416,8 +392,7 @@ class VideoTrack : public Track {
// Track that has audio specific elements.
class AudioTrack : public Track {
public:
// The |seed| parameter is used to synthesize a UID for the track.
explicit AudioTrack(unsigned int* seed);
AudioTrack();
virtual ~AudioTrack();
// Returns the size in bytes for the payload of the Track element plus the
@@ -493,170 +468,6 @@ class Tracks {
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks);
};
///////////////////////////////////////////////////////////////
// Chapter element
//
class Chapter {
public:
// Set the identifier for this chapter. (This corresponds to the
// Cue Identifier line in WebVTT.)
// TODO(matthewjheaney): the actual serialization of this item in
// MKV is pending.
bool set_id(const char* id);
// Converts the nanosecond start and stop times of this chapter to
// their corresponding timecode values, and stores them that way.
void set_time(const Segment& segment,
uint64 start_time_ns,
uint64 end_time_ns);
// Add a title string to this chapter, per the semantics described
// here:
// http://www.matroska.org/technical/specs/index.html
//
// The title ("chapter string") is a UTF-8 string.
//
// The language has ISO 639-2 representation, described here:
// http://www.loc.gov/standards/iso639-2/englangn.html
// http://www.loc.gov/standards/iso639-2/php/English_list.php
// If you specify NULL as the language value, this implies
// English ("eng").
//
// The country value corresponds to the codes listed here:
// http://www.iana.org/domains/root/db/
//
// The function returns false if the string could not be allocated.
bool add_string(const char* title,
const char* language,
const char* country);
private:
friend class Chapters;
// For storage of chapter titles that differ by language.
class Display {
public:
// Establish representation invariant for new Display object.
void Init();
// Reclaim resources, in anticipation of destruction.
void Clear();
// Copies the title to the |title_| member. Returns false on
// error.
bool set_title(const char* title);
// Copies the language to the |language_| member. Returns false
// on error.
bool set_language(const char* language);
// Copies the country to the |country_| member. Returns false on
// error.
bool set_country(const char* country);
// If |writer| is non-NULL, serialize the Display sub-element of
// the Atom into the stream. Returns the Display element size on
// success, 0 if error.
uint64 WriteDisplay(IMkvWriter* writer) const;
private:
char* title_;
char* language_;
char* country_;
};
Chapter();
~Chapter();
// Establish the representation invariant for a newly-created
// Chapter object. The |seed| parameter is used to create the UID
// for this chapter atom.
void Init(unsigned int* seed);
// Copies this Chapter object to a different one. This is used when
// expanding a plain array of Chapter objects (see Chapters).
void ShallowCopy(Chapter* dst) const;
// Reclaim resources used by this Chapter object, pending its
// destruction.
void Clear();
// If there is no storage remaining on the |displays_| array for a
// new display object, creates a new, longer array and copies the
// existing Display objects to the new array. Returns false if the
// array cannot be expanded.
bool ExpandDisplaysArray();
// If |writer| is non-NULL, serialize the Atom sub-element into the
// stream. Returns the total size of the element on success, 0 if
// error.
uint64 WriteAtom(IMkvWriter* writer) const;
// The string identifier for this chapter (corresponds to WebVTT cue
// identifier).
char* id_;
// Start timecode of the chapter.
uint64 start_timecode_;
// Stop timecode of the chapter.
uint64 end_timecode_;
// The binary identifier for this chapter.
uint64 uid_;
// The Atom element can contain multiple Display sub-elements, as
// the same logical title can be rendered in different languages.
Display* displays_;
// The physical length (total size) of the |displays_| array.
int displays_size_;
// The logical length (number of active elements) on the |displays_|
// array.
int displays_count_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapter);
};
///////////////////////////////////////////////////////////////
// Chapters element
//
class Chapters {
public:
Chapters();
~Chapters();
Chapter* AddChapter(unsigned int* seed);
// Returns the number of chapters that have been added.
int Count() const;
// Output the Chapters element to the writer. Returns true on success.
bool Write(IMkvWriter* writer) const;
private:
// Expands the chapters_ array if there is not enough space to contain
// another chapter object. Returns true on success.
bool ExpandChaptersArray();
// If |writer| is non-NULL, serialize the Edition sub-element of the
// Chapters element into the stream. Returns the Edition element
// size on success, 0 if error.
uint64 WriteEdition(IMkvWriter* writer) const;
// Total length of the chapters_ array.
int chapters_size_;
// Number of active chapters on the chapters_ array.
int chapters_count_;
// Array for storage of chapter objects.
Chapter* chapters_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapters);
};
///////////////////////////////////////////////////////////////
// Cluster element
//
@@ -688,28 +499,6 @@ class Cluster {
uint64 timecode, // timecode units (absolute)
bool is_key);
// Adds a frame to be output in the file. The frame is written out through
// |writer_| if successful. Returns true on success.
// Inputs:
// frame: Pointer to the data
// length: Length of the data
// additional: Pointer to the additional data
// additional_length: Length of the additional data
// add_id: Value of BlockAddID element
// track_number: Track to add the data to. Value returned by Add track
// functions. The range of allowed values is [1, 126].
// abs_timecode: Absolute (not relative to cluster) timestamp of the
// frame, expressed in timecode units.
// is_key: Flag telling whether or not this frame is a key frame.
bool AddFrameWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 abs_timecode,
bool is_key);
// Writes a frame of metadata to the output medium; returns true on
// success.
// Inputs:
@@ -740,7 +529,6 @@ class Cluster {
// Returns the size in bytes for the entire Cluster element.
uint64 Size() const;
int64 size_position() const { return size_position_; }
int32 blocks_added() const { return blocks_added_; }
uint64 payload_size() const { return payload_size_; }
int64 position_for_cues() const { return position_for_cues_; }
@@ -756,18 +544,6 @@ class Cluster {
int64 timecode,
uint64 generic_arg);
// Signature that matches WriteBlockWithAdditional
// in the muxer utilities package.
typedef uint64 (*WriteBlockAdditional)(IMkvWriter* writer,
const uint8* data,
uint64 length,
const uint8* additional,
uint64 add_id,
uint64 additional_length,
uint64 track_number,
int64 timecode,
uint64 is_key);
// Used to implement AddFrame and AddMetadata.
bool DoWriteBlock(const uint8* frame,
uint64 length,
@@ -776,17 +552,6 @@ class Cluster {
uint64 generic_arg,
WriteBlock write_block);
// Used to implement AddFrameWithAdditional
bool DoWriteBlockWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 absolute_timecode,
uint64 generic_arg,
WriteBlockAdditional write_block);
// Outputs the Cluster header to |writer_|. Returns true on success.
bool WriteClusterHeader();
@@ -835,26 +600,14 @@ class SeekHead {
// Writes out SeekHead and SeekEntry elements. Returns true on success.
bool Finalize(IMkvWriter* writer) const;
// Returns the id of the Seek Entry at the given index. Returns -1 if index is
// out of range.
uint32 GetId(int index) const;
// Returns the position of the Seek Entry at the given index. Returns -1 if
// index is out of range.
uint64 GetPosition(int index) const;
// Sets the Seek Entry id and position at given index.
// Returns true on success.
bool SetSeekEntry(int index, uint32 id, uint64 position);
// Reserves space by writing out a Void element which will be updated with
// a SeekHead element later. Returns true on success.
bool Write(IMkvWriter* writer);
// We are going to put a cap on the number of Seek Entries.
const static int32 kSeekEntryCount = 5;
private:
// We are going to put a cap on the number of Seek Entries.
const static int32 kSeekEntryCount = 4;
// Returns the maximum size in bytes of one seek entry.
uint64 MaxEntrySize() const;
@@ -926,11 +679,6 @@ class Segment {
kFile = 0x2
};
enum CuesPosition {
kAfterClusters = 0x0, // Position Cues after Clusters - Default
kBeforeClusters = 0x1 // Position Cues before Clusters
};
const static uint64 kDefaultMaxClusterDuration = 30000000000ULL;
Segment();
@@ -953,15 +701,9 @@ class Segment {
// the track number.
uint64 AddAudioTrack(int32 sample_rate, int32 channels, int32 number);
// Adds an empty chapter to the chapters of this segment. Returns
// non-NULL on success. After adding the chapter, the caller should
// populate its fields via the Chapter member functions.
Chapter* AddChapter();
// Adds a cue point to the Cues element. |timestamp| is the time in
// nanoseconds of the cue's time. |track| is the Track of the Cue. This
// function must be called after AddFrame to calculate the correct
// BlockNumber for the CuePoint. Returns true on success.
// nanoseconds of the cue's time. |track| is the Track of the Cue. Returns
// true on success.
bool AddCuePoint(uint64 timestamp, uint64 track);
// Adds a frame to be output in the file. Returns true on success.
@@ -998,44 +740,17 @@ class Segment {
uint64 timestamp_ns,
uint64 duration_ns);
bool AddFrameWithAdditional(const uint8* frame,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
uint64 timestamp,
bool is_key);
// Adds a video track to the segment. Returns the number of the track on
// success, 0 on error. |number| is the number to use for the video track.
// |number| must be >= 0. If |number| == 0 then the muxer will decide on
// the track number.
uint64 AddVideoTrack(int32 width, int32 height, int32 number);
// This function must be called after Finalize() if you need a copy of the
// output with Cues written before the Clusters. It will return false if the
// writer is not seekable of if chunking is set to true.
// Input parameters:
// reader - an IMkvReader object created with the same underlying file of the
// current writer object. Make sure to close the existing writer
// object before creating this so that all the data is properly
// flushed and available for reading.
// writer - an IMkvWriter object pointing to a *different* file than the one
// pointed by the current writer object. This file will contain the
// Cues element before the Clusters.
bool CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader,
IMkvWriter* writer);
// Sets which track to use for the Cues element. Must have added the track
// before calling this function. Returns true on success. |track_number| is
// returned by the Add track functions.
bool CuesTrack(uint64 track_number);
// This will force the muxer to create a new Cluster when the next frame is
// added.
void ForceNewClusterOnNextFrame();
// Writes out any frames that have not been written out. Finalizes the last
// cluster. May update the size and duration of the segment. May output the
// Cues element. May finalize the SeekHead element. Returns true on success.
@@ -1045,7 +760,6 @@ class Segment {
Cues* GetCues() { return &cues_; }
// Returns the Segment Information object.
const SegmentInfo* GetSegmentInfo() const { return &segment_info_; }
SegmentInfo* GetSegmentInfo() { return &segment_info_; }
// Search the Tracks and return the track that matches |track_number|.
@@ -1078,7 +792,6 @@ class Segment {
uint64 max_cluster_size() const { return max_cluster_size_; }
void set_mode(Mode mode) { mode_ = mode; }
Mode mode() const { return mode_; }
CuesPosition cues_position() const { return cues_position_; }
bool output_cues() const { return output_cues_; }
const SegmentInfo* segment_info() const { return &segment_info_; }
@@ -1133,30 +846,11 @@ class Segment {
// was necessary but creation was not successful.
bool DoNewClusterProcessing(uint64 track_num, uint64 timestamp_ns, bool key);
// Adjusts Cue Point values (to place Cues before Clusters) so that they
// reflect the correct offsets.
void MoveCuesBeforeClusters();
// This function recursively computes the correct cluster offsets (this is
// done to move the Cues before Clusters). It recursively updates the change
// in size (which indicates a change in cluster offset) until no sizes change.
// Parameters:
// diff - indicates the difference in size of the Cues element that needs to
// accounted for.
// index - index in the list of Cues which is currently being adjusted.
// cue_size - size of the Cues element.
void MoveCuesBeforeClustersHelper(uint64 diff, int index, uint64* cue_size);
// Seeds the random number generator used to make UIDs.
unsigned int seed_;
// WebM elements
Cues cues_;
SeekHead seek_head_;
SegmentInfo segment_info_;
Tracks tracks_;
Chapters chapters_;
// Number of chunks written.
int chunk_count_;
@@ -1183,9 +877,6 @@ class Segment {
// Base filename for the chunked files.
char* chunking_base_name_;
// File position offset where the Clusters end.
int64 cluster_end_offset_;
// List of clusters.
Cluster** cluster_list_;
@@ -1195,15 +886,9 @@ class Segment {
// Number of clusters in the cluster list.
int32 cluster_list_size_;
// Indicates whether Cues should be written before or after Clusters
CuesPosition cues_position_;
// Track number that is associated with the cues element for this segment.
uint64 cues_track_;
// Tells the muxer to force a new cluster on the next Block.
bool force_new_cluster_;
// List of stored audio frames. These variables are used to store frames so
// the muxer can follow the guideline "Audio blocks that contain the video
// key frame's timecode should be in the same cluster as the video key frame

View File

@@ -8,19 +8,13 @@
#include "mkvmuxerutil.hpp"
#ifdef __ANDROID__
#include <fcntl.h>
#endif
#include <cmath>
#include <cstdio>
#ifdef _WIN32
#define _CRT_RAND_S
#endif
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <limits>
#include <new>
#include "mkvwriter.hpp"
@@ -87,12 +81,12 @@ uint64 EbmlElementSize(uint64 type, uint64 value) {
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, float /* value */ ) {
uint64 EbmlElementSize(uint64 type, float value) {
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += sizeof(float);
ebml_size += sizeof(value);
// Size of Datasize
ebml_size++;
@@ -334,7 +328,7 @@ uint64 WriteSimpleBlock(IMkvWriter* writer,
// is a signed, 16-bit integer). However, as a simplification we
// only permit non-negative cluster-relative timestamps for blocks.
if (timecode < 0 || timecode > kMaxBlockTimecode)
if (timecode < 0 || timecode > std::numeric_limits<int16>::max())
return false;
if (WriteID(writer, kMkvSimpleBlock))
@@ -464,95 +458,6 @@ uint64 WriteMetadataBlock(IMkvWriter* writer,
return blockg_elem_size;
}
// Writes a WebM Block with Additional. The structure is as follows
// Indentation shows sub-levels
// BlockGroup
// Block
// Data
// BlockAdditions
// BlockMore
// BlockAddID
// 1 (Denotes Alpha)
// BlockAdditional
// Data
uint64 WriteBlockWithAdditional(IMkvWriter* writer,
const uint8* data,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
int64 timecode,
uint64 is_key) {
if (!data || !additional || length < 1 || additional_length < 1)
return 0;
const uint64 block_payload_size = 4 + length;
const uint64 block_elem_size = EbmlMasterElementSize(kMkvBlock,
block_payload_size) +
block_payload_size;
const uint64 block_additional_elem_size = EbmlElementSize(kMkvBlockAdditional,
additional,
additional_length);
const uint64 block_addid_elem_size = EbmlElementSize(kMkvBlockAddID, add_id);
const uint64 block_more_payload_size = block_addid_elem_size +
block_additional_elem_size;
const uint64 block_more_elem_size = EbmlMasterElementSize(
kMkvBlockMore,
block_more_payload_size) +
block_more_payload_size;
const uint64 block_additions_payload_size = block_more_elem_size;
const uint64 block_additions_elem_size = EbmlMasterElementSize(
kMkvBlockAdditions,
block_additions_payload_size) +
block_additions_payload_size;
const uint64 block_group_payload_size = block_elem_size +
block_additions_elem_size;
const uint64 block_group_elem_size = EbmlMasterElementSize(
kMkvBlockGroup,
block_group_payload_size) +
block_group_payload_size;
if (!WriteEbmlMasterElement(writer, kMkvBlockGroup,
block_group_payload_size))
return 0;
if (!WriteEbmlMasterElement(writer, kMkvBlock, block_payload_size))
return 0;
if (WriteUInt(writer, track_number))
return 0;
if (SerializeInt(writer, timecode, 2))
return 0;
uint64 flags = 0;
if (is_key)
flags |= 0x80;
if (SerializeInt(writer, flags, 1))
return 0;
if (writer->Write(data, static_cast<uint32>(length)))
return 0;
if (!WriteEbmlMasterElement(writer, kMkvBlockAdditions,
block_additions_payload_size))
return 0;
if (!WriteEbmlMasterElement(writer, kMkvBlockMore, block_more_payload_size))
return 0;
if (!WriteEbmlElement(writer, kMkvBlockAddID, add_id))
return 0;
if (!WriteEbmlElement(writer, kMkvBlockAdditional,
additional, additional_length))
return 0;
return block_group_elem_size;
}
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
if (!writer)
return false;
@@ -592,39 +497,8 @@ uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
void GetVersion(int32* major, int32* minor, int32* build, int32* revision) {
*major = 0;
*minor = 2;
*build = 1;
*build = 0;
*revision = 0;
}
} // namespace mkvmuxer
mkvmuxer::uint64 mkvmuxer::MakeUID(unsigned int* seed) {
uint64 uid = 0;
for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values
uid <<= 8;
// TODO(fgalligan): Move random number generation to platform specific code.
#ifdef _WIN32
(void)seed;
unsigned int random_value;
const errno_t e = rand_s(&random_value);
(void)e;
const int32 nn = random_value;
#elif __ANDROID__
int32 temp_num = 1;
int fd = open("/dev/urandom", O_RDONLY);
if (fd != -1) {
read(fd, &temp_num, sizeof(int32));
close(fd);
}
const int32 nn = temp_num;
#else
const int32 nn = rand_r(seed);
#endif
const int32 n = 0xFF & (nn >> 4); // throw away low-order bits
uid |= n;
}
return uid;
}

View File

@@ -16,14 +16,11 @@ namespace mkvmuxer {
class IMkvWriter;
const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
const int64 kMaxBlockTimecode = 0x07FFFLL;
// Writes out |value| in Big Endian order. Returns 0 on success.
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
// Returns the size in bytes of the element.
int32 GetUIntSize(uint64 value);
int32 GetCodedUIntSize(uint64 value);
uint64 EbmlMasterElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, float value);
@@ -90,29 +87,6 @@ uint64 WriteMetadataBlock(IMkvWriter* writer,
int64 timecode,
uint64 duration_timecode);
// Output an Mkv Block with BlockAdditional data.
// Inputs:
// data: Pointer to the data.
// length: Length of the data.
// additional: Pointer to the additional data
// additional_length: Length of the additional data.
// add_id: Value of BlockAddID element.
// track_number: Track to add the data to. Value returned by Add track
// functions. Only values in the range [1, 126] are
// permitted.
// timecode: Relative timecode of the Block. Only values in the
// range [0, 2^15) are permitted.
// is_key: Non-zero value specifies that frame is a key frame.
uint64 WriteBlockWithAdditional(IMkvWriter* writer,
const uint8* data,
uint64 length,
const uint8* additional,
uint64 additional_length,
uint64 add_id,
uint64 track_number,
int64 timecode,
uint64 is_key);
// Output a void element. |size| must be the entire size in bytes that will be
// void. The function will calculate the size of the void header and subtract
// it from |size|.
@@ -122,10 +96,6 @@ uint64 WriteVoidElement(IMkvWriter* writer, uint64 size);
// and |revision|.
void GetVersion(int32* major, int32* minor, int32* build, int32* revision);
// Returns a random number to be used for UID, using |seed| to seed
// the random-number generator (see POSIX rand_r() for semantics).
uint64 MakeUID(unsigned int* seed);
} //end namespace mkvmuxer
#endif // MKVMUXERUTIL_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -217,7 +217,6 @@ public:
unsigned long long algo;
unsigned char* settings;
long long settings_len;
};
// ContentEncAESSettings element names
@@ -254,15 +253,6 @@ public:
// element.
unsigned long GetCompressionCount() const;
// Parses the ContentCompression element from |pReader|. |start| is the
// starting offset of the ContentCompression payload. |size| is the size in
// bytes of the ContentCompression payload. |compression| is where the parsed
// values will be stored.
long ParseCompressionEntry(long long start,
long long size,
IMkvReader* pReader,
ContentCompression* compression);
// Returns ContentEncryption represented by |idx|. Returns NULL if |idx|
// is out of bounds.
const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const;
@@ -333,12 +323,7 @@ public:
long long element_size,
Track*&);
enum Type {
kVideo = 1,
kAudio = 2,
kSubtitle = 0x11,
kMetadata = 0x21
};
enum Type { kVideo = 1, kAudio = 2 };
Segment* const m_pSegment;
const long long m_element_start;
@@ -349,12 +334,10 @@ public:
long GetNumber() const;
unsigned long long GetUid() const;
const char* GetNameAsUTF8() const;
const char* GetLanguage() const;
const char* GetCodecNameAsUTF8() const;
const char* GetCodecId() const;
const unsigned char* GetCodecPrivate(size_t&) const;
bool GetLacing() const;
unsigned long long GetDefaultDuration() const;
const BlockEntry* GetEOS() const;
@@ -378,9 +361,7 @@ public:
long type;
long number;
unsigned long long uid;
unsigned long long defaultDuration;
char* nameAsUTF8;
char* language;
char* codecId;
char* codecNameAsUTF8;
unsigned char* codecPrivate;
@@ -479,6 +460,7 @@ public:
double GetSamplingRate() const;
long long GetChannels() const;
long long GetBitDepth() const;
long Seek(long long time_ns, const BlockEntry*&) const;
private:
double m_rate;
@@ -529,131 +511,6 @@ private:
};
class Chapters
{
Chapters(const Chapters&);
Chapters& operator=(const Chapters&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
Chapters(
Segment*,
long long payload_start,
long long payload_size,
long long element_start,
long long element_size);
~Chapters();
long Parse();
class Atom;
class Edition;
class Display
{
friend class Atom;
Display();
Display(const Display&);
~Display();
Display& operator=(const Display&);
public:
const char* GetString() const;
const char* GetLanguage() const;
const char* GetCountry() const;
private:
void Init();
void ShallowCopy(Display&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
char* m_string;
char* m_language;
char* m_country;
};
class Atom
{
friend class Edition;
Atom();
Atom(const Atom&);
~Atom();
Atom& operator=(const Atom&);
public:
unsigned long long GetUID() const;
const char* GetStringUID() const;
long long GetStartTimecode() const;
long long GetStopTimecode() const;
long long GetStartTime(const Chapters*) const;
long long GetStopTime(const Chapters*) const;
int GetDisplayCount() const;
const Display* GetDisplay(int index) const;
private:
void Init();
void ShallowCopy(Atom&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
static long long GetTime(const Chapters*, long long timecode);
long ParseDisplay(IMkvReader*, long long pos, long long size);
bool ExpandDisplaysArray();
char* m_string_uid;
unsigned long long m_uid;
long long m_start_timecode;
long long m_stop_timecode;
Display* m_displays;
int m_displays_size;
int m_displays_count;
};
class Edition
{
friend class Chapters;
Edition();
Edition(const Edition&);
~Edition();
Edition& operator=(const Edition&);
public:
int GetAtomCount() const;
const Atom* GetAtom(int index) const;
private:
void Init();
void ShallowCopy(Edition&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
long ParseAtom(IMkvReader*, long long pos, long long size);
bool ExpandAtomsArray();
Atom* m_atoms;
int m_atoms_size;
int m_atoms_count;
};
int GetEditionCount() const;
const Edition* GetEdition(int index) const;
private:
long ParseEdition(long long pos, long long size);
bool ExpandEditionsArray();
Edition* m_editions;
int m_editions_size;
int m_editions_count;
};
class SegmentInfo
{
SegmentInfo(const SegmentInfo&);
@@ -954,8 +811,8 @@ private:
class Segment
{
friend class Cues;
friend class Track;
friend class VideoTrack;
friend class AudioTrack;
Segment(const Segment&);
Segment& operator=(const Segment&);
@@ -1006,7 +863,6 @@ public:
const Tracks* GetTracks() const;
const SegmentInfo* GetInfo() const;
const Cues* GetCues() const;
const Chapters* GetChapters() const;
long long GetDuration() const;
@@ -1034,7 +890,6 @@ private:
SegmentInfo* m_pInfo;
Tracks* m_pTracks;
Cues* m_pCues;
Chapters* m_pChapters;
Cluster** m_clusters;
long m_clusterCount; //number of entries for which m_index >= 0
long m_clusterPreloadCount; //number of entries for which m_index < 0

View File

@@ -25,6 +25,7 @@ public:
int Open(const char*);
void Close();
bool IsOpen() const;
virtual int Read(long long position, long length, unsigned char* buffer);
virtual int Length(long long* total, long long* available);

259
mkvstream.cc Normal file
View File

@@ -0,0 +1,259 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "./mkvstream.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
MkvStream::MkvStream() : file_(NULL) {
}
MkvStream::~MkvStream() {
Close();
}
int MkvStream::Open(const char* filename) {
if (filename == NULL || file_ != NULL)
return -1;
file_ = fopen(filename, "rb");
if (file_ == NULL)
return -1;
total_ = -1; // means "end-of-file not reached yet"
avail_ = 0;
// Establish invariant
cache_.push_back(Page());
Page& page = cache_.back();
page.off_ = 0;
const int e = page.Read(this);
if (e < 0) {
Close();
return -1;
}
return 0;
}
void MkvStream::Close() {
if (file_) {
fclose(file_);
file_ = NULL;
cache_.clear();
}
}
int MkvStream::Read(long long pos, long len, unsigned char* buf) {
if (file_ == NULL)
return -1;
assert(!cache_.empty()); // invariant
assert(avail_ >= cache_.back().off_);
// For now, we only support sequential reading of streams, with no
// jumps backwards in the stream. Were this a real network cache,
// we would have to purge the cache then reissue another fetch over
// the wire. (To be precise, this operation would report a cache
// miss to the parser, which would be reported back to the caller as
// an underflow. The caller would then call PopulateCache directly,
// then re-try the parse.)
if (pos < 0)
return -1; // bad arg
if (pos < cache_.front().off_)
return -1; // attempt to read non-sequentially
if (total_ >= 0 && pos > total_)
return -1; // attempt to read beyond end-of-stream
if (len < 0)
return -1; // bad arg
if (len == 0)
return 0;
const long long end = pos + len;
if (total_ >= 0 && end > total_)
return -1; // attempt to read beyond end of stream
// Over a wire, network reads are slow, or block completely, which
// is not acceptable (e.g. it would leave you unable to pump windows
// messages). Hence the need for a cache. If we won't have enough
// data in the cache to service this read call, we report underflow
// to the parser, which in turn reports it back to the caller. The
// caller is then expected to populate the cache, using whatever
// mechanism is appropriate for the application (e.g. post an async
// read and wait for completion), and then re-try the parse. This
// is a simulator, so all the caller needs to do here is call
// PopulateCache, but in a real application with real network I/O,
// populating the cache can get very complex (especially when
// seeking is supported).
if (end > avail_) // not enough data in the cache
return mkvparser::E_BUFFER_NOT_FULL;
if (buf == NULL)
return -1;
typedef cache_t::const_iterator iter_t;
const iter_t i = cache_.begin();
const iter_t j = cache_.end();
const iter_t kk = std::upper_bound(i, j, pos, Page::Less());
iter_t k = --iter_t(kk);
while (len > 0) {
assert(pos + len == end);
const long long page_off = k->off_;
assert(page_off <= pos);
long long page_end = page_off + Page::kSize;
if (page_end > end)
page_end = end;
const unsigned char* const src = k->buf_ + (pos - page_off);
const long long count_ = page_end - pos;
assert(count_ <= len);
const size_t count = static_cast<size_t>(count_);
memcpy(buf, src, count);
pos += count;
len -= count;
buf += count;
}
assert(pos == end);
return 0;
}
int MkvStream::Length(long long* total, long long* avail) {
if (file_ == NULL || total == NULL || avail == NULL)
return -1;
*total = total_;
*avail = avail_;
return 0;
}
int MkvStream::PopulateCache(long long pos, long requested_len) {
if (file_ == NULL)
return -1;
assert(!cache_.empty());
assert(avail_ >= 0);
assert(total_ < 0 || total_ == avail_);
if (pos < 0)
return -1;
if (pos < cache_.front().off_)
return -1; // attempt to read non-sequentially
if (requested_len < 0)
return -1;
if (requested_len == 0)
return 0; //TODO(matthewjheaney): ensure pos in cache?
// Simulate a network read, which might not return all
// requested bytes immediately:
const long actual_len = 1 + rand() % requested_len;
const long long end = pos + actual_len;
long long off = cache_.back().off_;
assert(off % Page::kSize == 0);
assert(off <= avail_);
while (total_ < 0 && avail_ < end) {
cache_.push_back(Page());
Page& page = cache_.back();
off += Page::kSize;
page.off_ = off;
const int e = page.Read(this);
if (e < 0) // error
return -1;
assert(e == 0 || total_ >= 0);
}
return 0;
}
int MkvStream::PurgeCache(long long pos) {
if (file_ == NULL)
return -1;
if (pos < 0)
return -1;
assert(!cache_.empty());
if (pos < cache_.front().off_)
return 0;
typedef cache_t::iterator iter_t;
iter_t i = cache_.begin();
const iter_t j = cache_.end();
const iter_t kk = std::upper_bound(i, j, pos, Page::Less());
const iter_t k = --iter_t(kk);
while (i != k)
cache_.erase(i++);
return 0;
}
int MkvStream::Page::Read(MkvStream* stream) {
assert(stream);
assert(stream->total_ < 0);
assert(stream->avail_ >= 0);
assert(off_ % kSize == 0);
assert(off_ == stream->avail_);
FILE* const f = stream->file_;
assert(f);
unsigned char* dst = buf_;
for (int i = 0; i < kSize; ++i) {
const int c = fgetc(f);
if (c == EOF) {
if (!feof(f))
return -1;
stream->total_ = stream->avail_;
return 1;
}
*dst++ = static_cast<unsigned char>(c);
++stream->avail_;
}
return 0;
}

90
mkvstream.h Normal file
View File

@@ -0,0 +1,90 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef MKVSTREAM_H_
#define MKVSTREAM_H_
#include <cstdio>
#include <deque>
#include "./mkvparser.hpp"
class MkvStream : public mkvparser::IMkvReader {
public:
MkvStream();
virtual ~MkvStream();
// Open the file identified by |filename| in read-only mode, as a
// binary stream of bytes. Returns 0 on success, negative if error.
int Open(const char* filename);
// Closes the file stream. Note that the stream is automatically
// closed when the MkvStream object is destroyed.
void Close();
// Fetches |len| bytes of data from the cache, started at absolute
// stream position |pos|, reading into buffer |buf|. Returns
// negative value if error (including mkvparser::E_BUFFER_NOT_FULL,
// to indicate that not all of the requested bytes were in the
// cache), 0 on success (all requested bytes were returned).
virtual int Read(long long pos, long len, unsigned char* buf);
// The |total| argument indicates how many total bytes are in the
// stream. This network simulator sets |total| to -1 until we reach
// end-of-stream, at which point |total| is set to the file size.
// The |available| argument indicates how much of the stream has been
// consumed. Returns negative on error, 0 on success.
virtual int Length(long long* total, long long* available);
// Read |len| bytes from the file stream into the cache, starting
// at absolute file position |pos|. This is a network simulator
// so the actual number of bytes read into the cache might be less
// than requested. Returns negative if error, 0 on success.
int PopulateCache(long long pos, long len);
// Notify this reader that the stream up to (but not including)
// offset |pos| has been parsed and is no longer of interest,
// hence that portion of the stream can be removed from the cache.
// Returns negative if error, 0 on success.
int PurgeCache(long long pos);
private:
MkvStream(const MkvStream&);
MkvStream& operator=(const MkvStream&);
struct Page {
int Read(MkvStream*);
enum { kSize = 1024 };
unsigned char buf_[kSize];
long long off_;
struct Less {
bool operator()(const Page& page, long long pos) const {
return (page.off_ < pos);
}
bool operator()(long long pos, const Page& page) const {
return (pos < page.off_);
}
bool operator()(const Page& lhs, const Page& rhs) const {
return (lhs.off_ < rhs.off_);
}
};
};
FILE* file_;
typedef std::deque<Page> cache_t;
cache_t cache_;
long long total_;
long long avail_;
};
#endif

42
netparse.cc Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
//#include <cstdio>
//#include <cstdlib>
#include "./mkvparser.hpp"
#include "./mkvstream.h"
namespace {
int ParserEbmlHeader(long long* pos);
}
int main(int argc, const char* argv[]) {
if (argc != 2) {
fprintf(stdout, "usage: netparse <mkvfile>\n");
return EXIT_SUCCESS;
}
MkvStream reader;
const char* const filename = argv[1];
int e = reader.Open(filename);
if (e) {
fprintf(stdout, "open failed\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
namespace {
int ParserEbmlHeader(long long* pos) {
}
}

View File

@@ -53,7 +53,6 @@ void Usage() {
printf("\n");
printf("Cues options:\n");
printf(" -output_cues_block_number <int> >0 outputs cue block number\n");
printf(" -cues_before_clusters <int> >0 puts Cues before Clusters\n");
printf("\n");
printf("Metadata options:\n");
printf(" -webvtt-subtitles <vttfile> "
@@ -64,8 +63,6 @@ void Usage() {
"add WebVTT descriptions as metadata track\n");
printf(" -webvtt-metadata <vttfile> "
"add WebVTT subtitles as metadata track\n");
printf(" -webvtt-chapters <vttfile> "
"add WebVTT chapters as MKV chapters element\n");
}
struct MetadataFile {
@@ -101,14 +98,13 @@ int ParseArgWebVTT(
metadata_files_t* metadata_files) {
int& i = *argv_index;
enum { kCount = 5 };
enum { kCount = 4 };
struct Arg { const char* name; SampleMuxerMetadata::Kind kind; };
const Arg args[kCount] = {
{ "-webvtt-subtitles", SampleMuxerMetadata::kSubtitles },
{ "-webvtt-captions", SampleMuxerMetadata::kCaptions },
{ "-webvtt-descriptions", SampleMuxerMetadata::kDescriptions },
{ "-webvtt-metadata", SampleMuxerMetadata::kMetadata },
{ "-webvtt-chapters", SampleMuxerMetadata::kChapters }
{ "-webvtt-metadata", SampleMuxerMetadata::kMetadata }
};
for (int idx = 0; idx < kCount; ++idx) {
@@ -146,14 +142,13 @@ int main(int argc, char* argv[]) {
bool output_audio = true;
bool live_mode = false;
bool output_cues = true;
bool cues_before_clusters = false;
bool cues_on_video_track = true;
bool cues_on_audio_track = false;
uint64 max_cluster_duration = 0;
uint64 max_cluster_size = 0;
bool switch_tracks = false;
int audio_track_number = 0; // 0 tells muxer to decide.
int video_track_number = 0; // 0 tells muxer to decide.
int audio_track_number = 0; // 0 tells muxer to decide.
int video_track_number = 0; // 0 tells muxer to decide.
bool chunking = false;
const char* chunk_name = NULL;
@@ -184,8 +179,6 @@ int main(int argc, char* argv[]) {
live_mode = strtol(argv[++i], &end, 10) == 0 ? false : true;
} else if (!strcmp("-output_cues", argv[i]) && i < argc_check) {
output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true;
} else if (!strcmp("-cues_before_clusters", argv[i]) && i < argc_check) {
cues_before_clusters = strtol(argv[++i], &end, 10) == 0 ? false : true;
} else if (!strcmp("-cues_on_video_track", argv[i]) && i < argc_check) {
cues_on_video_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
if (cues_on_video_track)
@@ -263,8 +256,7 @@ int main(int argc, char* argv[]) {
// Set muxer header info
mkvmuxer::MkvWriter writer;
char* temp_file = tmpnam(NULL);
if (!writer.Open(cues_before_clusters ? temp_file : output)) {
if (!writer.Open(output)) {
printf("\n Filename is invalid or error while opening.\n");
return EXIT_FAILURE;
}
@@ -299,8 +291,8 @@ int main(int argc, char* argv[]) {
// Set Tracks element attributes
const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
unsigned long i = 0;
uint64 vid_track = 0; // no track added
uint64 aud_track = 0; // no track added
uint64 vid_track = 0; // no track added
uint64 aud_track = 0; // no track added
using mkvparser::Track;
@@ -416,9 +408,6 @@ int main(int argc, char* argv[]) {
if (!LoadMetadataFiles(metadata_files, &metadata))
return EXIT_FAILURE;
if (!metadata.AddChapters())
return EXIT_FAILURE;
// Set Cues element attributes
mkvmuxer::Cues* const cues = muxer_segment.GetCues();
cues->set_output_block_number(output_cues_block_number);
@@ -438,7 +427,8 @@ int main(int argc, char* argv[]) {
long status = cluster->GetFirst(block_entry);
if (status) {
if (status)
{
printf("\n Could not get first block of cluster.\n");
return EXIT_FAILURE;
}
@@ -493,7 +483,8 @@ int main(int argc, char* argv[]) {
status = cluster->GetNext(block_entry, block_entry);
if (status) {
if (status)
{
printf("\n Could not get next block of cluster.\n");
return EXIT_FAILURE;
}
@@ -507,34 +498,13 @@ int main(int argc, char* argv[]) {
if (!metadata.Write(-1))
return EXIT_FAILURE;
if (!muxer_segment.Finalize()) {
printf("Finalization of segment failed.\n");
return EXIT_FAILURE;
}
reader.Close();
writer.Close();
if (cues_before_clusters) {
if (reader.Open(temp_file)) {
printf("\n Filename is invalid or error while opening.\n");
return EXIT_FAILURE;
}
if (!writer.Open(output)) {
printf("\n Filename is invalid or error while opening.\n");
return EXIT_FAILURE;
}
if (!muxer_segment.CopyAndMoveCuesBeforeClusters(&reader, &writer)) {
printf("\n Unable to copy and move cues before clusters.\n");
return EXIT_FAILURE;
}
reader.Close();
writer.Close();
remove(temp_file);
}
muxer_segment.Finalize();
delete [] data;
delete parser_segment;
writer.Close();
reader.Close();
return EXIT_SUCCESS;
}

View File

@@ -164,9 +164,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="sample_muxer.cpp" />
<ClCompile Include="sample_muxer_metadata.cc" />
<ClCompile Include="vttreader.cc" />
<ClCompile Include="webvttparser.cc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="libwebm_2010.vcxproj">
@@ -174,11 +171,6 @@
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="sample_muxer_metadata.h" />
<ClInclude Include="vttreader.h" />
<ClInclude Include="webvttparser.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View File

@@ -2,13 +2,5 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="sample_muxer.cpp" />
<ClCompile Include="sample_muxer_metadata.cc" />
<ClCompile Include="vttreader.cc" />
<ClCompile Include="webvttparser.cc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="sample_muxer_metadata.h" />
<ClInclude Include="vttreader.h" />
<ClInclude Include="webvttparser.h" />
</ItemGroup>
</Project>

View File

@@ -2,6 +2,8 @@
#include <string>
#include "vttreader.h"
using std::string;
SampleMuxerMetadata::SampleMuxerMetadata() : segment_(NULL) {
}
@@ -14,9 +16,6 @@ bool SampleMuxerMetadata::Init(mkvmuxer::Segment* segment) {
}
bool SampleMuxerMetadata::Load(const char* file, Kind kind) {
if (kind == kChapters)
return LoadChapters(file);
mkvmuxer::uint64 track_num;
if (!AddTrack(kind, &track_num)) {
@@ -27,21 +26,6 @@ bool SampleMuxerMetadata::Load(const char* file, Kind kind) {
return Parse(file, kind, track_num);
}
bool SampleMuxerMetadata::AddChapters() {
typedef cue_list_t::const_iterator iter_t;
iter_t i = chapter_cues_.begin();
const iter_t j = chapter_cues_.end();
while (i != j) {
const cue_t& chapter = *i++;
if (!AddChapter(chapter))
return false;
}
return true;
}
bool SampleMuxerMetadata::Write(mkvmuxer::int64 time_ns) {
typedef cues_set_t::iterator iter_t;
@@ -65,129 +49,6 @@ bool SampleMuxerMetadata::Write(mkvmuxer::int64 time_ns) {
return true;
}
bool SampleMuxerMetadata::LoadChapters(const char* file) {
if (!chapter_cues_.empty()) {
printf("Support for more than one chapters file is not yet implemented\n");
return false;
}
cue_list_t cues;
if (!ParseChapters(file, &cues))
return false;
// TODO(matthewjheaney): support more than one chapters file
chapter_cues_.swap(cues);
return true;
}
bool SampleMuxerMetadata::ParseChapters(
const char* file,
cue_list_t* cues_ptr) {
cue_list_t& cues = *cues_ptr;
cues.clear();
libwebvtt::VttReader r;
int e = r.Open(file);
if (e) {
printf("Unable to open WebVTT file: \"%s\"\n", file);
return false;
}
libwebvtt::Parser p(&r);
e = p.Init();
if (e < 0) { // error
printf("Error parsing WebVTT file: \"%s\"\n", file);
return false;
}
libwebvtt::Time t;
t.hours = -1;
for (;;) {
cue_t c;
e = p.Parse(&c);
if (e < 0) { // error
printf("Error parsing WebVTT file: \"%s\"\n", file);
return false;
}
if (e > 0) // EOF
return true;
if (c.start_time < t) {
printf("bad WebVTT cue timestamp (out-of-order)\n");
return false;
}
if (c.stop_time < c.start_time) {
printf("bad WebVTT cue timestamp (stop < start)\n");
return false;
}
t = c.start_time;
cues.push_back(c);
}
}
bool SampleMuxerMetadata::AddChapter(const cue_t& cue) {
// TODO(matthewjheaney): support language and country
mkvmuxer::Chapter* const chapter = segment_->AddChapter();
if (chapter == NULL) {
printf("Unable to add chapter\n");
return false;
}
if (cue.identifier.empty()) {
chapter->set_id(NULL);
} else {
const char* const id = cue.identifier.c_str();
if (!chapter->set_id(id)) {
printf("Unable to set chapter id\n");
return false;
}
}
typedef libwebvtt::presentation_t time_ms_t;
const time_ms_t start_time_ms = cue.start_time.presentation();
const time_ms_t stop_time_ms = cue.stop_time.presentation();
enum { kNsPerMs = 1000000 };
const mkvmuxer::uint64 start_time_ns = start_time_ms * kNsPerMs;
const mkvmuxer::uint64 stop_time_ns = stop_time_ms * kNsPerMs;
chapter->set_time(*segment_, start_time_ns, stop_time_ns);
typedef libwebvtt::Cue::payload_t::const_iterator iter_t;
iter_t i = cue.payload.begin();
const iter_t j = cue.payload.end();
std::string title;
for (;;) {
title += *i++;
if (i == j)
break;
enum { kLF = '\x0A' };
title += kLF;
}
if (!chapter->add_string(title.c_str(), NULL, NULL)) {
printf("Unable to set chapter title\n");
return false;
}
return true;
}
bool SampleMuxerMetadata::AddTrack(
Kind kind,
mkvmuxer::uint64* track_num) {
@@ -293,7 +154,7 @@ bool SampleMuxerMetadata::Parse(
}
}
void SampleMuxerMetadata::MakeFrame(const cue_t& c, std::string* pf) {
void SampleMuxerMetadata::MakeFrame(const cue_t& c, string* pf) {
pf->clear();
WriteCueIdentifier(c.identifier, pf);
WriteCueSettings(c.settings, pf);
@@ -301,15 +162,15 @@ void SampleMuxerMetadata::MakeFrame(const cue_t& c, std::string* pf) {
}
void SampleMuxerMetadata::WriteCueIdentifier(
const std::string& identifier,
std::string* pf) {
const string& identifier,
string* pf) {
pf->append(identifier);
pf->push_back('\x0A'); // LF
}
void SampleMuxerMetadata::WriteCueSettings(
const cue_t::settings_t& settings,
std::string* pf) {
string* pf) {
if (settings.empty()) {
pf->push_back('\x0A'); // LF
return;
@@ -338,14 +199,14 @@ void SampleMuxerMetadata::WriteCueSettings(
void SampleMuxerMetadata::WriteCuePayload(
const cue_t::payload_t& payload,
std::string* pf) {
string* pf) {
typedef cue_t::payload_t::const_iterator iter_t;
iter_t i = payload.begin();
const iter_t j = payload.end();
while (i != j) {
const std::string& line = *i++;
const string& line = *i++;
pf->append(line);
pf->push_back('\x0A'); // LF
}
@@ -368,7 +229,7 @@ bool SampleMuxerMetadata::SortableCue::Write(
// Metadata blocks always specify the block duration.
const mkvmuxer::int64 duration_ns = stop_ns - start_ns;
std::string frame;
string frame;
MakeFrame(cue, &frame);
typedef const mkvmuxer::uint8* data_t;

View File

@@ -21,8 +21,7 @@ class SampleMuxerMetadata {
kSubtitles,
kCaptions,
kDescriptions,
kMetadata,
kChapters
kMetadata
};
SampleMuxerMetadata();
@@ -32,12 +31,10 @@ class SampleMuxerMetadata {
bool Init(mkvmuxer::Segment* segment);
// Parse the WebVTT file |filename| having the indicated |kind|, and
// create a corresponding track (or chapters element) in the
// segment. Returns false on error.
// create a corresponding track in the segment. Returns false on
// error.
bool Load(const char* filename, Kind kind);
bool AddChapters();
// Write any WebVTT cues whose time is less or equal to |time_ns| as
// a metadata block in its corresponding track. If |time_ns| is
// negative, write all remaining cues. Returns false on error.
@@ -77,21 +74,6 @@ class SampleMuxerMetadata {
};
typedef std::multiset<SortableCue> cues_set_t;
typedef std::list<cue_t> cue_list_t;
// Parse the WebVTT cues in the named |file|, returning false on
// error. We handle chapters as a special case, because they are
// stored in their own, dedicated level-1 element.
bool LoadChapters(const char* file);
// Parse the WebVTT chapters in |file| to populate |cues|. Returns
// false on error.
static bool ParseChapters(const char* file,
cue_list_t* cues);
// Adds WebVTT cue |chapter| to the chapters element of the output
// file's segment element. Returns false on error.
bool AddChapter(const cue_t& chapter);
// Add a metadata track to the segment having the indicated |kind|,
// returning the |track_num| that has been chosen for this track.
@@ -123,10 +105,6 @@ class SampleMuxerMetadata {
// Set of cues ordered by time and then by track number.
cues_set_t cues_set_;
// The cues that will be used to populate the Chapters level-1
// element of the output file.
cue_list_t chapter_cues_;
// Disable copy ctor and copy assign.
SampleMuxerMetadata(const SampleMuxerMetadata&);
SampleMuxerMetadata& operator=(const SampleMuxerMetadata&);

View File

@@ -26,12 +26,7 @@ typedef std::auto_ptr<mkvparser::Segment> segment_ptr_t;
// WebVTT metadata tracks have a type (encoded in the CodecID for the track).
// We use |type| to synthesize a filename for the out-of-band WebVTT |file|.
struct MetadataInfo {
enum Type {
kSubtitles,
kCaptions,
kDescriptions,
kMetadata,
kChapters } type;
enum Type { kSubtitles, kCaptions, kDescriptions, kMetadata } type;
FILE* file;
};
@@ -39,10 +34,6 @@ struct MetadataInfo {
// each track in the input file.
typedef std::map<long, MetadataInfo> metadata_map_t; // NOLINT
// The distinguished key value we use to store the chapters
// information in the metadata map.
enum { kChaptersKey = 0 };
// The data from the original WebVTT Cue is stored as a WebM block.
// The FrameParser is used to parse the lines of text out from the
// block, in order to reconstruct the original WebVTT Cue.
@@ -81,44 +72,6 @@ class FrameParser : public libwebvtt::LineReader {
FrameParser& operator=(const FrameParser&);
};
// The data from the original WebVTT Cue is stored as an MKV Chapters
// Atom element (the cue payload is stored as a Display sub-element).
// The ChapterAtomParser is used to parse the lines of text out from
// the String sub-element of the Display element (though it would be
// admittedly odd if there were more than one line).
class ChapterAtomParser : public libwebvtt::LineReader {
public:
explicit ChapterAtomParser(const mkvparser::Chapters::Display* display);
virtual ~ChapterAtomParser();
const mkvparser::Chapters::Display* const display_;
protected:
// Read the next character from the character stream (the title
// member of the atom's display). We increment the stream pointer
// |str_| as each character from the stream is consumed.
virtual int GetChar(char* c);
// End-of-line handling requires that we put a character back into
// the stream. Here we need only decrement the stream pointer |str_|
// to unconsume the character.
virtual void UngetChar(char c);
// The current position in the character stream (the title of the
// atom's display).
const char* str_;
// The position of the end of the character stream. When the current
// position |str_| equals the end position |str_end_|, the entire
// stream (title of the display) has been consumed and end-of-stream
// is indicated.
const char* str_end_;
private:
ChapterAtomParser(const ChapterAtomParser&);
ChapterAtomParser& operator=(const ChapterAtomParser&);
};
// Parse the EBML header of the WebM input file, to determine whether we
// actually have a WebM file. Returns false if this is not a WebM file.
bool ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos);
@@ -130,43 +83,8 @@ bool ParseSegment(
mkvpos_t pos,
segment_ptr_t* segment);
// If |segment| has a Chapters element (in which case, there will be a
// corresponding entry in |metadata_map|), convert the MKV chapters to
// WebVTT chapter cues and write them to the output file. Returns
// false on error.
bool WriteChaptersFile(
const metadata_map_t& metadata_map,
const mkvparser::Segment* segment);
// Convert an MKV Chapters Atom to a WebVTT cue and write it to the
// output |file|. Returns false on error.
bool WriteChaptersCue(
FILE* file,
const mkvparser::Chapters* chapters,
const mkvparser::Chapters::Atom* atom,
const mkvparser::Chapters::Display* display);
// Write the Cue Identifier line of the WebVTT cue, if it's present.
// Returns false on error.
bool WriteChaptersCueIdentifier(
FILE* file,
const mkvparser::Chapters::Atom* atom);
// Use the timecodes from the chapters |atom| to write just the
// timings line of the WebVTT cue. Returns false on error.
bool WriteChaptersCueTimings(
FILE* file,
const mkvparser::Chapters* chapters,
const mkvparser::Chapters::Atom* atom);
// Parse the String sub-element of the |display| and write the payload
// of the WebVTT cue. Returns false on error.
bool WriteChaptersCuePayload(
FILE* file,
const mkvparser::Chapters::Display* display);
// Iterate over the tracks of the input file (and any chapters
// element) and cache information about each metadata track.
// Iterate over the tracks of the input file and cache information about
// each metadata track.
void BuildMap(const mkvparser::Segment* segment, metadata_map_t* metadata_map);
// For each track listed in the cache, synthesize its output filename
@@ -266,8 +184,8 @@ int main(int argc, const char* argv[]) {
BuildMap(segment_ptr.get(), &metadata_map);
if (metadata_map.empty()) {
printf("no WebVTT metadata found\n");
return EXIT_FAILURE;
printf("no metadata tracks found\n");
return EXIT_FAILURE; // TODO(matthewjheaney): correct result?
}
if (!OpenFiles(&metadata_map, filename)) {
@@ -327,32 +245,6 @@ void FrameParser::UngetChar(char /* c */ ) {
--pos_;
}
ChapterAtomParser::ChapterAtomParser(
const mkvparser::Chapters::Display* display)
: display_(display) {
str_ = display->GetString();
const size_t len = strlen(str_);
str_end_ = str_ + len;
}
ChapterAtomParser::~ChapterAtomParser() {
}
int ChapterAtomParser::GetChar(char* c) {
if (str_ >= str_end_) // end-of-stream
return 1; // per the semantics of libwebvtt::Reader::GetChar
*c = *str_++; // consume this character in the stream
return 0;
}
void ChapterAtomParser::UngetChar(char /* c */ ) {
// All we need to do here is decrement the position in the stream.
// The next time GetChar is called the same character will be
// re-read from the input file.
--str_;
}
} // namespace vttdemux
bool vttdemux::ParseHeader(
@@ -405,17 +297,6 @@ bool vttdemux::ParseSegment(
void vttdemux::BuildMap(
const mkvparser::Segment* segment,
metadata_map_t* map_ptr) {
metadata_map_t& m = *map_ptr;
m.clear();
if (segment->GetChapters()) {
MetadataInfo info;
info.file = NULL;
info.type = MetadataInfo::kChapters;
m[kChaptersKey] = info;
}
const mkvparser::Tracks* const tt = segment->GetTracks();
if (tt == NULL)
return;
@@ -424,6 +305,8 @@ void vttdemux::BuildMap(
if (tc <= 0)
return;
metadata_map_t& m = *map_ptr;
// Iterate over the tracks in the intput file. We determine whether
// a track holds metadata by inspecting its CodecID.
@@ -433,11 +316,6 @@ void vttdemux::BuildMap(
if (t == NULL) // weird
continue;
const long tn = t->GetNumber(); // NOLINT
if (tn <= 0) // weird
continue;
const char* const codec_id = t->GetCodecId();
if (codec_id == NULL) // weird
@@ -458,6 +336,7 @@ void vttdemux::BuildMap(
continue;
}
const long tn = t->GetNumber(); // NOLINT
m[tn] = info; // create an entry in the cache for this track
}
}
@@ -536,10 +415,6 @@ bool vttdemux::OpenFiles(metadata_map_t* metadata_map, const char* filename) {
name += "_METADATA";
break;
case MetadataInfo::kChapters:
name += "_CHAPTERS";
break;
default:
return false;
}
@@ -601,9 +476,6 @@ bool vttdemux::WriteFiles(const metadata_map_t& m, mkvparser::Segment* s) {
InitializeFiles(m);
if (!WriteChaptersFile(m, s))
return false;
// Now iterate over the clusters, writing the WebVTT cue as we parse
// each metadata block.
@@ -640,179 +512,6 @@ bool vttdemux::InitializeFiles(const metadata_map_t& m) {
return true;
}
bool vttdemux::WriteChaptersFile(
const metadata_map_t& m,
const mkvparser::Segment* s) {
const metadata_map_t::const_iterator info_iter = m.find(kChaptersKey);
if (info_iter == m.end()) // no chapters, so nothing to do
return true;
const mkvparser::Chapters* const chapters = s->GetChapters();
if (chapters == NULL) // weird
return true;
const MetadataInfo& info = info_iter->second;
FILE* const file = info.file;
const int edition_count = chapters->GetEditionCount();
if (edition_count <= 0) // weird
return true; // nothing to do
if (edition_count > 1) {
// TODO(matthewjheaney): figure what to do here
printf("more than one chapter edition detected\n");
return false;
}
const mkvparser::Chapters::Edition* const edition = chapters->GetEdition(0);
const int atom_count = edition->GetAtomCount();
for (int idx = 0; idx < atom_count; ++idx) {
const mkvparser::Chapters::Atom* const atom = edition->GetAtom(idx);
const int display_count = atom->GetDisplayCount();
if (display_count <= 0)
continue;
if (display_count > 1) {
// TODO(matthewjheaney): handle case of multiple languages
printf("more than 1 display in atom detected\n");
return false;
}
const mkvparser::Chapters::Display* const display = atom->GetDisplay(0);
if (const char* language = display->GetLanguage()) {
if (strcmp(language, "eng") != 0) {
// TODO(matthewjheaney): handle case of multiple languages.
// We must create a separate webvtt file for each language.
// This isn't a simple problem (which is why we defer it for
// now), because there's nothing in the header that tells us
// what languages we have as cues. We must parse the displays
// of each atom to determine that.
// One solution is to make two passes over the input data.
// First parse the displays, creating an in-memory cache of
// all the chapter cues, sorted according to their language.
// After we have read all of the chapter atoms from the input
// file, we can then write separate output files for each
// language.
printf("only English-language chapter cues are supported\n");
return false;
}
}
if (!WriteChaptersCue(file, chapters, atom, display))
return false;
}
return true;
}
bool vttdemux::WriteChaptersCue(
FILE* f,
const mkvparser::Chapters* chapters,
const mkvparser::Chapters::Atom* atom,
const mkvparser::Chapters::Display* display) {
// We start a new cue by writing a cue separator (an empty line)
// into the stream.
if (fputc('\n', f) < 0)
return false;
// A WebVTT Cue comprises 3 things: a cue identifier, followed by
// the cue timings, followed by the payload of the cue. We write
// each part of the cue in sequence.
if (!WriteChaptersCueIdentifier(f, atom))
return false;
if (!WriteChaptersCueTimings(f, chapters, atom))
return false;
if (!WriteChaptersCuePayload(f, display))
return false;
return true;
}
bool vttdemux::WriteChaptersCueIdentifier(
FILE* f,
const mkvparser::Chapters::Atom* atom) {
const char* const identifier = atom->GetStringUID();
if (identifier == NULL)
return true; // nothing else to do
if (fprintf(f, "%s\n", identifier) < 0)
return false;
return true;
}
bool vttdemux::WriteChaptersCueTimings(
FILE* f,
const mkvparser::Chapters* chapters,
const mkvparser::Chapters::Atom* atom) {
const mkvtime_t start_ns = atom->GetStartTime(chapters);
if (start_ns < 0)
return false;
const mkvtime_t stop_ns = atom->GetStopTime(chapters);
if (stop_ns < 0)
return false;
if (!WriteCueTime(f, start_ns))
return false;
if (fputs(" --> ", f) < 0)
return false;
if (!WriteCueTime(f, stop_ns))
return false;
if (fputc('\n', f) < 0)
return false;
return true;
}
bool vttdemux::WriteChaptersCuePayload(
FILE* f,
const mkvparser::Chapters::Display* display) {
// Bind a Chapter parser object to the display, which allows us to
// extract each line of text from the title-part of the display.
ChapterAtomParser parser(display);
int count = 0; // count of lines of payload text written to output file
for (string line;;) {
const int e = parser.GetLine(&line);
if (e < 0) // error (only -- we allow EOS here)
return false;
if (line.empty()) // TODO(matthewjheaney): retain this check?
break;
if (fprintf(f, "%s\n", line.c_str()) < 0)
return false;
++count;
}
if (count <= 0) // WebVTT cue requires non-empty payload
return false;
return true;
}
bool vttdemux::ProcessCluster(
const metadata_map_t& m,
const mkvparser::Cluster* c) {

View File

@@ -53,10 +53,6 @@ enum MkvId {
kMkvReferenceBlock = 0xFB,
kMkvLaceNumber = 0xCC,
kMkvSimpleBlock = 0xA3,
kMkvBlockAdditions = 0x75A1,
kMkvBlockMore = 0xA6,
kMkvBlockAddID = 0xEE,
kMkvBlockAdditional = 0xA5,
//Track
kMkvTracks = 0x1654AE6B,
kMkvTrackEntry = 0xAE,
@@ -73,12 +69,10 @@ enum MkvId {
kMkvCodecID = 0x86,
kMkvCodecPrivate = 0x63A2,
kMkvCodecName = 0x258688,
kMkvMaxBlockAdditionID = 0x55EE,
//video
kMkvVideo = 0xE0,
kMkvFlagInterlaced = 0x9A,
kMkvStereoMode = 0x53B8,
kMkvAlphaMode = 0x53C0,
kMkvPixelWidth = 0xB0,
kMkvPixelHeight = 0xBA,
kMkvPixelCropBottom = 0x54AA,
@@ -119,20 +113,8 @@ enum MkvId {
kMkvCueTrack = 0xF7,
kMkvCueClusterPosition = 0xF1,
kMkvCueBlockNumber = 0x5378,
//Chapters
kMkvChapters = 0x1043A770,
kMkvEditionEntry = 0x45B9,
kMkvChapterAtom = 0xB6,
kMkvChapterUID = 0x73C4,
kMkvChapterStringUID = 0x5654,
kMkvChapterTimeStart = 0x91,
kMkvChapterTimeEnd = 0x92,
kMkvChapterDisplay = 0x80,
kMkvChapString = 0x85,
kMkvChapLanguage = 0x437C,
kMkvChapCountry = 0x437E
};
} // end namespace mkvmuxer
#endif // WEBMIDS_HPP
#endif // WEBMIDS_HPP

View File

@@ -9,6 +9,8 @@
#include "./webvttparser.h" // NOLINT
#include <climits>
using std::string;
namespace libwebvtt {
enum {
@@ -25,11 +27,11 @@ Reader::~Reader() {
LineReader::~LineReader() {
}
int LineReader::GetLine(std::string* line_ptr) {
int LineReader::GetLine(string* line_ptr) {
if (line_ptr == NULL)
return -1;
std::string& ln = *line_ptr;
string& ln = *line_ptr;
ln.clear();
// Consume characters from the stream, until we
@@ -65,9 +67,6 @@ int LineReader::GetLine(std::string* line_ptr) {
if (c == kCR)
break; // handle the hard end-of-line case outside of loop
if (c == '\xFE' || c == '\xFF') // not UTF-8
return -1;
// To defend against pathological or malicious streams, we
// cap the line length at some arbitrarily-large value:
enum { kMaxLineLength = 10000 }; // arbitrary
@@ -136,7 +135,7 @@ int Parser::Init() {
return -1;
}
std::string line;
string line;
e = GetLine(&line);
@@ -179,7 +178,7 @@ int Parser::Parse(Cue* cue) {
// Parse first non-blank line
std::string line;
string line;
int e;
for (;;) {
@@ -198,9 +197,9 @@ int Parser::Parse(Cue* cue) {
// may not appear in the cue identifier line.
const char kArrow[] = "-->";
std::string::size_type arrow_pos = line.find(kArrow);
string::size_type arrow_pos = line.find(kArrow);
if (arrow_pos != std::string::npos) {
if (arrow_pos != string::npos) {
// We found a timings line, which implies that we don't have a cue
// identifier.
@@ -222,7 +221,7 @@ int Parser::Parse(Cue* cue) {
arrow_pos = line.find(kArrow);
if (arrow_pos == std::string::npos) // not a timings line
if (arrow_pos == string::npos) // not a timings line
return -1;
}
@@ -304,23 +303,23 @@ int Parser::ParseBOM() {
}
int Parser::ParseTimingsLine(
std::string* line_ptr,
std::string::size_type arrow_pos,
string* line_ptr,
string::size_type arrow_pos,
Time* start_time,
Time* stop_time,
Cue::settings_t* settings) {
if (line_ptr == NULL)
return -1;
std::string& line = *line_ptr;
string& line = *line_ptr;
if (arrow_pos == std::string::npos || arrow_pos >= line.length())
if (arrow_pos == string::npos || arrow_pos >= line.length())
return -1;
// Place a NUL character at the start of the arrow token, in
// order to demarcate the start time from remainder of line.
line[arrow_pos] = kNUL;
std::string::size_type idx = 0;
string::size_type idx = 0;
int e = ParseTime(line, &idx, start_time);
if (e) // error
@@ -354,15 +353,15 @@ int Parser::ParseTimingsLine(
}
int Parser::ParseTime(
const std::string& line,
std::string::size_type* idx_ptr,
const string& line,
string::size_type* idx_ptr,
Time* time) {
if (idx_ptr == NULL)
return -1;
std::string::size_type& idx = *idx_ptr;
string::size_type& idx = *idx_ptr;
if (idx == std::string::npos || idx >= line.length())
if (idx == string::npos || idx >= line.length())
return -1;
if (time == NULL)
@@ -468,18 +467,7 @@ int Parser::ParseTime(
// We have parsed the hours, minutes, and seconds.
// We must now parse the milliseconds.
char c = line[idx];
// TODO(matthewjheaney): one option here is to slightly relax the
// syntax rules for WebVTT timestamps, to permit the comma character
// to also be used as the seconds/milliseconds separator. This
// would handle streams that use localization conventions for
// countries in Western Europe. For now we obey the rules specified
// in the WebVTT spec (allow "full stop" only).
const bool have_milliseconds = (c == '.');
if (!have_milliseconds) {
if (line[idx] != '.') { // no milliseconds
t.milliseconds = 0;
} else {
++idx; // consume FULL STOP
@@ -503,7 +491,7 @@ int Parser::ParseTime(
// We have parsed the time proper. We must check for any
// junk that immediately follows the time specifier.
c = line[idx];
const char c = line[idx];
if (c != kNUL && c != kSPACE && c != kTAB)
return -1;
@@ -512,12 +500,12 @@ int Parser::ParseTime(
}
int Parser::ParseSettings(
const std::string& line,
std::string::size_type idx,
const string& line,
string::size_type idx,
Cue::settings_t* settings) {
settings->clear();
if (idx == std::string::npos || idx >= line.length())
if (idx == string::npos || idx >= line.length())
return -1;
for (;;) {
@@ -586,39 +574,32 @@ int Parser::ParseSettings(
}
int Parser::ParseNumber(
const std::string& line,
std::string::size_type* idx_ptr) {
const string& line,
string::size_type* idx_ptr) {
if (idx_ptr == NULL)
return -1;
std::string::size_type& idx = *idx_ptr;
string::size_type& idx = *idx_ptr;
if (idx == std::string::npos || idx >= line.length())
if (idx == string::npos || idx >= line.length())
return -1;
if (!isdigit(line[idx]))
return -1;
int result = 0;
long long val = 0; // NOLINT
while (isdigit(line[idx])) {
const char c = line[idx];
const int i = c - '0';
val *= 10;
val += static_cast<int>(line[idx] - '0');
if (result > INT_MAX / 10)
if (val > INT_MAX)
return -1;
result *= 10;
if (result > INT_MAX - i)
return -1;
result += i;
++idx;
}
return result;
return static_cast<int>(val);
}
bool Time::operator==(const Time& rhs) const {