Compare commits
1 Commits
libwebm-1.
...
sandbox/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dca6be3a32 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
*.MKV
|
||||
core
|
||||
*.a
|
||||
*.d
|
||||
*.so*
|
||||
*.o
|
||||
*~
|
||||
@@ -17,7 +16,3 @@ Release
|
||||
*.sdf
|
||||
*.opensdf
|
||||
ipch
|
||||
dumpvtt
|
||||
sample
|
||||
samplemuxer
|
||||
vttdemux
|
||||
|
||||
13
Android.mk
13
Android.mk
@@ -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)
|
||||
|
||||
10
Makefile
10
Makefile
@@ -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
|
||||
|
||||
800
mkvmuxer.cpp
800
mkvmuxer.cpp
File diff suppressed because it is too large
Load Diff
347
mkvmuxer.hpp
347
mkvmuxer.hpp
@@ -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
|
||||
|
||||
136
mkvmuxerutil.cpp
136
mkvmuxerutil.cpp
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
1018
mkvparser.cpp
1018
mkvparser.cpp
File diff suppressed because it is too large
Load Diff
151
mkvparser.hpp
151
mkvparser.hpp
@@ -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
|
||||
|
||||
@@ -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
259
mkvstream.cc
Normal 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
90
mkvstream.h
Normal 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
42
netparse.cc
Normal 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) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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&);
|
||||
|
||||
317
vttdemux.cc
317
vttdemux.cc
@@ -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) {
|
||||
|
||||
18
webmids.hpp
18
webmids.hpp
@@ -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,18 +113,6 @@ 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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user