xref: /aosp_15_r20/external/libwebm/vttdemux.cc (revision 103e46e4cd4b6efcf6001f23fa8665fb110abf8d)
1*103e46e4SHarish Mahendrakar // Copyright (c) 2012 The WebM project authors. All Rights Reserved.
2*103e46e4SHarish Mahendrakar //
3*103e46e4SHarish Mahendrakar // Use of this source code is governed by a BSD-style license
4*103e46e4SHarish Mahendrakar // that can be found in the LICENSE file in the root of the source
5*103e46e4SHarish Mahendrakar // tree. An additional intellectual property rights grant can be found
6*103e46e4SHarish Mahendrakar // in the file PATENTS.  All contributing project authors may
7*103e46e4SHarish Mahendrakar // be found in the AUTHORS file in the root of the source tree.
8*103e46e4SHarish Mahendrakar 
9*103e46e4SHarish Mahendrakar #include <cstdio>
10*103e46e4SHarish Mahendrakar #include <cstdlib>
11*103e46e4SHarish Mahendrakar #include <cstring>
12*103e46e4SHarish Mahendrakar #include <map>
13*103e46e4SHarish Mahendrakar #include <memory>
14*103e46e4SHarish Mahendrakar #include <string>
15*103e46e4SHarish Mahendrakar #include <utility>
16*103e46e4SHarish Mahendrakar 
17*103e46e4SHarish Mahendrakar #include "mkvparser/mkvparser.h"
18*103e46e4SHarish Mahendrakar #include "mkvparser/mkvreader.h"
19*103e46e4SHarish Mahendrakar #include "webvtt/webvttparser.h"
20*103e46e4SHarish Mahendrakar 
21*103e46e4SHarish Mahendrakar using std::string;
22*103e46e4SHarish Mahendrakar 
23*103e46e4SHarish Mahendrakar namespace libwebm {
24*103e46e4SHarish Mahendrakar namespace vttdemux {
25*103e46e4SHarish Mahendrakar 
26*103e46e4SHarish Mahendrakar typedef long long mkvtime_t;  // NOLINT
27*103e46e4SHarish Mahendrakar typedef long long mkvpos_t;  // NOLINT
28*103e46e4SHarish Mahendrakar typedef std::unique_ptr<mkvparser::Segment> segment_ptr_t;
29*103e46e4SHarish Mahendrakar 
30*103e46e4SHarish Mahendrakar // WebVTT metadata tracks have a type (encoded in the CodecID for the track).
31*103e46e4SHarish Mahendrakar // We use |type| to synthesize a filename for the out-of-band WebVTT |file|.
32*103e46e4SHarish Mahendrakar struct MetadataInfo {
33*103e46e4SHarish Mahendrakar   enum Type { kSubtitles, kCaptions, kDescriptions, kMetadata, kChapters } type;
34*103e46e4SHarish Mahendrakar   FILE* file;
35*103e46e4SHarish Mahendrakar };
36*103e46e4SHarish Mahendrakar 
37*103e46e4SHarish Mahendrakar // We use a map, indexed by track number, to collect information about
38*103e46e4SHarish Mahendrakar // each track in the input file.
39*103e46e4SHarish Mahendrakar typedef std::map<long, MetadataInfo> metadata_map_t;  // NOLINT
40*103e46e4SHarish Mahendrakar 
41*103e46e4SHarish Mahendrakar // The distinguished key value we use to store the chapters
42*103e46e4SHarish Mahendrakar // information in the metadata map.
43*103e46e4SHarish Mahendrakar enum { kChaptersKey = 0 };
44*103e46e4SHarish Mahendrakar 
45*103e46e4SHarish Mahendrakar // The data from the original WebVTT Cue is stored as a WebM block.
46*103e46e4SHarish Mahendrakar // The FrameParser is used to parse the lines of text out from the
47*103e46e4SHarish Mahendrakar // block, in order to reconstruct the original WebVTT Cue.
48*103e46e4SHarish Mahendrakar class FrameParser : public libwebvtt::LineReader {
49*103e46e4SHarish Mahendrakar  public:
50*103e46e4SHarish Mahendrakar   //  Bind the FrameParser instance to a WebM block.
51*103e46e4SHarish Mahendrakar   explicit FrameParser(const mkvparser::BlockGroup* block_group);
52*103e46e4SHarish Mahendrakar   virtual ~FrameParser();
53*103e46e4SHarish Mahendrakar 
54*103e46e4SHarish Mahendrakar   // The Webm block (group) to which this instance is bound.  We
55*103e46e4SHarish Mahendrakar   // treat the payload of the block as a stream of characters.
56*103e46e4SHarish Mahendrakar   const mkvparser::BlockGroup* const block_group_;
57*103e46e4SHarish Mahendrakar 
58*103e46e4SHarish Mahendrakar  protected:
59*103e46e4SHarish Mahendrakar   // Read the next character from the character stream (the payload
60*103e46e4SHarish Mahendrakar   // of the WebM block).  We increment the stream pointer |pos_| as
61*103e46e4SHarish Mahendrakar   // each character from the stream is consumed.
62*103e46e4SHarish Mahendrakar   virtual int GetChar(char* c);
63*103e46e4SHarish Mahendrakar 
64*103e46e4SHarish Mahendrakar   // End-of-line handling requires that we put a character back into
65*103e46e4SHarish Mahendrakar   // the stream.  Here we need only decrement the stream pointer |pos_|
66*103e46e4SHarish Mahendrakar   // to unconsume the character.
67*103e46e4SHarish Mahendrakar   virtual void UngetChar(char c);
68*103e46e4SHarish Mahendrakar 
69*103e46e4SHarish Mahendrakar   // The current position in the character stream (the payload of the block).
70*103e46e4SHarish Mahendrakar   mkvpos_t pos_;
71*103e46e4SHarish Mahendrakar 
72*103e46e4SHarish Mahendrakar   // The position of the end of the character stream. When the current
73*103e46e4SHarish Mahendrakar   // position |pos_| equals the end position |pos_end_|, the entire
74*103e46e4SHarish Mahendrakar   // stream (block payload) has been consumed and end-of-stream is indicated.
75*103e46e4SHarish Mahendrakar   mkvpos_t pos_end_;
76*103e46e4SHarish Mahendrakar 
77*103e46e4SHarish Mahendrakar  private:
78*103e46e4SHarish Mahendrakar   // Disable copy ctor and copy assign
79*103e46e4SHarish Mahendrakar   FrameParser(const FrameParser&);
80*103e46e4SHarish Mahendrakar   FrameParser& operator=(const FrameParser&);
81*103e46e4SHarish Mahendrakar };
82*103e46e4SHarish Mahendrakar 
83*103e46e4SHarish Mahendrakar // The data from the original WebVTT Cue is stored as an MKV Chapters
84*103e46e4SHarish Mahendrakar // Atom element (the cue payload is stored as a Display sub-element).
85*103e46e4SHarish Mahendrakar // The ChapterAtomParser is used to parse the lines of text out from
86*103e46e4SHarish Mahendrakar // the String sub-element of the Display element (though it would be
87*103e46e4SHarish Mahendrakar // admittedly odd if there were more than one line).
88*103e46e4SHarish Mahendrakar class ChapterAtomParser : public libwebvtt::LineReader {
89*103e46e4SHarish Mahendrakar  public:
90*103e46e4SHarish Mahendrakar   explicit ChapterAtomParser(const mkvparser::Chapters::Display* display);
91*103e46e4SHarish Mahendrakar   virtual ~ChapterAtomParser();
92*103e46e4SHarish Mahendrakar 
93*103e46e4SHarish Mahendrakar   const mkvparser::Chapters::Display* const display_;
94*103e46e4SHarish Mahendrakar 
95*103e46e4SHarish Mahendrakar  protected:
96*103e46e4SHarish Mahendrakar   // Read the next character from the character stream (the title
97*103e46e4SHarish Mahendrakar   // member of the atom's display).  We increment the stream pointer
98*103e46e4SHarish Mahendrakar   // |str_| as each character from the stream is consumed.
99*103e46e4SHarish Mahendrakar   virtual int GetChar(char* c);
100*103e46e4SHarish Mahendrakar 
101*103e46e4SHarish Mahendrakar   // End-of-line handling requires that we put a character back into
102*103e46e4SHarish Mahendrakar   // the stream.  Here we need only decrement the stream pointer |str_|
103*103e46e4SHarish Mahendrakar   // to unconsume the character.
104*103e46e4SHarish Mahendrakar   virtual void UngetChar(char c);
105*103e46e4SHarish Mahendrakar 
106*103e46e4SHarish Mahendrakar   // The current position in the character stream (the title of the
107*103e46e4SHarish Mahendrakar   // atom's display).
108*103e46e4SHarish Mahendrakar   const char* str_;
109*103e46e4SHarish Mahendrakar 
110*103e46e4SHarish Mahendrakar   // The position of the end of the character stream. When the current
111*103e46e4SHarish Mahendrakar   // position |str_| equals the end position |str_end_|, the entire
112*103e46e4SHarish Mahendrakar   // stream (title of the display) has been consumed and end-of-stream
113*103e46e4SHarish Mahendrakar   // is indicated.
114*103e46e4SHarish Mahendrakar   const char* str_end_;
115*103e46e4SHarish Mahendrakar 
116*103e46e4SHarish Mahendrakar  private:
117*103e46e4SHarish Mahendrakar   ChapterAtomParser(const ChapterAtomParser&);
118*103e46e4SHarish Mahendrakar   ChapterAtomParser& operator=(const ChapterAtomParser&);
119*103e46e4SHarish Mahendrakar };
120*103e46e4SHarish Mahendrakar 
121*103e46e4SHarish Mahendrakar // Parse the EBML header of the WebM input file, to determine whether we
122*103e46e4SHarish Mahendrakar // actually have a WebM file.  Returns false if this is not a WebM file.
123*103e46e4SHarish Mahendrakar bool ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos);
124*103e46e4SHarish Mahendrakar 
125*103e46e4SHarish Mahendrakar // Parse the Segment of the input file and load all of its clusters.
126*103e46e4SHarish Mahendrakar // Returns false if there was an error parsing the file.
127*103e46e4SHarish Mahendrakar bool ParseSegment(mkvparser::IMkvReader* reader, mkvpos_t pos,
128*103e46e4SHarish Mahendrakar                   segment_ptr_t* segment);
129*103e46e4SHarish Mahendrakar 
130*103e46e4SHarish Mahendrakar // If |segment| has a Chapters element (in which case, there will be a
131*103e46e4SHarish Mahendrakar // corresponding entry in |metadata_map|), convert the MKV chapters to
132*103e46e4SHarish Mahendrakar // WebVTT chapter cues and write them to the output file.  Returns
133*103e46e4SHarish Mahendrakar // false on error.
134*103e46e4SHarish Mahendrakar bool WriteChaptersFile(const metadata_map_t& metadata_map,
135*103e46e4SHarish Mahendrakar                        const mkvparser::Segment* segment);
136*103e46e4SHarish Mahendrakar 
137*103e46e4SHarish Mahendrakar // Convert an MKV Chapters Atom to a WebVTT cue and write it to the
138*103e46e4SHarish Mahendrakar // output |file|.  Returns false on error.
139*103e46e4SHarish Mahendrakar bool WriteChaptersCue(FILE* file, const mkvparser::Chapters* chapters,
140*103e46e4SHarish Mahendrakar                       const mkvparser::Chapters::Atom* atom,
141*103e46e4SHarish Mahendrakar                       const mkvparser::Chapters::Display* display);
142*103e46e4SHarish Mahendrakar 
143*103e46e4SHarish Mahendrakar // Write the Cue Identifier line of the WebVTT cue, if it's present.
144*103e46e4SHarish Mahendrakar // Returns false on error.
145*103e46e4SHarish Mahendrakar bool WriteChaptersCueIdentifier(FILE* file,
146*103e46e4SHarish Mahendrakar                                 const mkvparser::Chapters::Atom* atom);
147*103e46e4SHarish Mahendrakar 
148*103e46e4SHarish Mahendrakar // Use the timecodes from the chapters |atom| to write just the
149*103e46e4SHarish Mahendrakar // timings line of the WebVTT cue.  Returns false on error.
150*103e46e4SHarish Mahendrakar bool WriteChaptersCueTimings(FILE* file, const mkvparser::Chapters* chapters,
151*103e46e4SHarish Mahendrakar                              const mkvparser::Chapters::Atom* atom);
152*103e46e4SHarish Mahendrakar 
153*103e46e4SHarish Mahendrakar // Parse the String sub-element of the |display| and write the payload
154*103e46e4SHarish Mahendrakar // of the WebVTT cue.  Returns false on error.
155*103e46e4SHarish Mahendrakar bool WriteChaptersCuePayload(FILE* file,
156*103e46e4SHarish Mahendrakar                              const mkvparser::Chapters::Display* display);
157*103e46e4SHarish Mahendrakar 
158*103e46e4SHarish Mahendrakar // Iterate over the tracks of the input file (and any chapters
159*103e46e4SHarish Mahendrakar // element) and cache information about each metadata track.
160*103e46e4SHarish Mahendrakar void BuildMap(const mkvparser::Segment* segment, metadata_map_t* metadata_map);
161*103e46e4SHarish Mahendrakar 
162*103e46e4SHarish Mahendrakar // For each track listed in the cache, synthesize its output filename
163*103e46e4SHarish Mahendrakar // and open a file handle that designates the out-of-band file.
164*103e46e4SHarish Mahendrakar // Returns false if we were unable to open an output file for a track.
165*103e46e4SHarish Mahendrakar bool OpenFiles(metadata_map_t* metadata_map, const char* filename);
166*103e46e4SHarish Mahendrakar 
167*103e46e4SHarish Mahendrakar // Close the file handle for each track in the cache.
168*103e46e4SHarish Mahendrakar void CloseFiles(metadata_map_t* metadata_map);
169*103e46e4SHarish Mahendrakar 
170*103e46e4SHarish Mahendrakar // Iterate over the clusters of the input file, and write a WebVTT cue
171*103e46e4SHarish Mahendrakar // for each metadata block.  Returns false if processing of a cluster
172*103e46e4SHarish Mahendrakar // failed.
173*103e46e4SHarish Mahendrakar bool WriteFiles(const metadata_map_t& m, mkvparser::Segment* s);
174*103e46e4SHarish Mahendrakar 
175*103e46e4SHarish Mahendrakar // Write the WebVTT header for each track in the cache.  We do this
176*103e46e4SHarish Mahendrakar // immediately before writing the actual WebVTT cues.  Returns false
177*103e46e4SHarish Mahendrakar // if the write failed.
178*103e46e4SHarish Mahendrakar bool InitializeFiles(const metadata_map_t& metadata_map);
179*103e46e4SHarish Mahendrakar 
180*103e46e4SHarish Mahendrakar // Iterate over the blocks of the |cluster|, writing a WebVTT cue to
181*103e46e4SHarish Mahendrakar // its associated output file for each block of metadata.  Returns
182*103e46e4SHarish Mahendrakar // false if processing a block failed, or there was a parse error.
183*103e46e4SHarish Mahendrakar bool ProcessCluster(const metadata_map_t& metadata_map,
184*103e46e4SHarish Mahendrakar                     const mkvparser::Cluster* cluster);
185*103e46e4SHarish Mahendrakar 
186*103e46e4SHarish Mahendrakar // Look up this track number in the cache, and if found (meaning this
187*103e46e4SHarish Mahendrakar // is a metadata track), write a WebVTT cue to the associated output
188*103e46e4SHarish Mahendrakar // file.  Returns false if writing the WebVTT cue failed.
189*103e46e4SHarish Mahendrakar bool ProcessBlockEntry(const metadata_map_t& metadata_map,
190*103e46e4SHarish Mahendrakar                        const mkvparser::BlockEntry* block_entry);
191*103e46e4SHarish Mahendrakar 
192*103e46e4SHarish Mahendrakar // Parse the lines of text from the |block_group| to reconstruct the
193*103e46e4SHarish Mahendrakar // original WebVTT cue, and write it to the associated output |file|.
194*103e46e4SHarish Mahendrakar // Returns false if there was an error writing to the output file.
195*103e46e4SHarish Mahendrakar bool WriteCue(FILE* file, const mkvparser::BlockGroup* block_group);
196*103e46e4SHarish Mahendrakar 
197*103e46e4SHarish Mahendrakar // Consume a line of text from the character stream, and if the line
198*103e46e4SHarish Mahendrakar // is not empty write the cue identifier to the associated output
199*103e46e4SHarish Mahendrakar // file.  Returns false if there was an error writing to the file.
200*103e46e4SHarish Mahendrakar bool WriteCueIdentifier(FILE* f, FrameParser* parser);
201*103e46e4SHarish Mahendrakar 
202*103e46e4SHarish Mahendrakar // Consume a line of text from the character stream (which holds any
203*103e46e4SHarish Mahendrakar // cue settings) and write the cue timings line for this cue to the
204*103e46e4SHarish Mahendrakar // associated output file.  Returns false if there was an error
205*103e46e4SHarish Mahendrakar // writing to the file.
206*103e46e4SHarish Mahendrakar bool WriteCueTimings(FILE* f, FrameParser* parser);
207*103e46e4SHarish Mahendrakar 
208*103e46e4SHarish Mahendrakar // Write the timestamp (representating either the start time or stop
209*103e46e4SHarish Mahendrakar // time of the cue) to the output file.  Returns false if there was an
210*103e46e4SHarish Mahendrakar // error writing to the file.
211*103e46e4SHarish Mahendrakar bool WriteCueTime(FILE* f, mkvtime_t time_ns);
212*103e46e4SHarish Mahendrakar 
213*103e46e4SHarish Mahendrakar // Consume the remaining lines of text from the character stream
214*103e46e4SHarish Mahendrakar // (these lines are the actual payload of the WebVTT cue), and write
215*103e46e4SHarish Mahendrakar // them to the associated output file.  Returns false if there was an
216*103e46e4SHarish Mahendrakar // error writing to the file.
217*103e46e4SHarish Mahendrakar bool WriteCuePayload(FILE* f, FrameParser* parser);
218*103e46e4SHarish Mahendrakar }  // namespace vttdemux
219*103e46e4SHarish Mahendrakar 
220*103e46e4SHarish Mahendrakar namespace vttdemux {
221*103e46e4SHarish Mahendrakar 
FrameParser(const mkvparser::BlockGroup * block_group)222*103e46e4SHarish Mahendrakar FrameParser::FrameParser(const mkvparser::BlockGroup* block_group)
223*103e46e4SHarish Mahendrakar     : block_group_(block_group) {
224*103e46e4SHarish Mahendrakar   const mkvparser::Block* const block = block_group->GetBlock();
225*103e46e4SHarish Mahendrakar   const mkvparser::Block::Frame& f = block->GetFrame(0);
226*103e46e4SHarish Mahendrakar 
227*103e46e4SHarish Mahendrakar   // The beginning and end of the character stream corresponds to the
228*103e46e4SHarish Mahendrakar   // position of this block's frame within the WebM input file.
229*103e46e4SHarish Mahendrakar 
230*103e46e4SHarish Mahendrakar   pos_ = f.pos;
231*103e46e4SHarish Mahendrakar   pos_end_ = f.pos + f.len;
232*103e46e4SHarish Mahendrakar }
233*103e46e4SHarish Mahendrakar 
~FrameParser()234*103e46e4SHarish Mahendrakar FrameParser::~FrameParser() {}
235*103e46e4SHarish Mahendrakar 
GetChar(char * c)236*103e46e4SHarish Mahendrakar int FrameParser::GetChar(char* c) {
237*103e46e4SHarish Mahendrakar   if (pos_ >= pos_end_)  // end-of-stream
238*103e46e4SHarish Mahendrakar     return 1;  // per the semantics of libwebvtt::Reader::GetChar
239*103e46e4SHarish Mahendrakar 
240*103e46e4SHarish Mahendrakar   const mkvparser::Cluster* const cluster = block_group_->GetCluster();
241*103e46e4SHarish Mahendrakar   const mkvparser::Segment* const segment = cluster->m_pSegment;
242*103e46e4SHarish Mahendrakar   mkvparser::IMkvReader* const reader = segment->m_pReader;
243*103e46e4SHarish Mahendrakar 
244*103e46e4SHarish Mahendrakar   unsigned char* const buf = reinterpret_cast<unsigned char*>(c);
245*103e46e4SHarish Mahendrakar   const int result = reader->Read(pos_, 1, buf);
246*103e46e4SHarish Mahendrakar 
247*103e46e4SHarish Mahendrakar   if (result < 0)  // error
248*103e46e4SHarish Mahendrakar     return -1;
249*103e46e4SHarish Mahendrakar 
250*103e46e4SHarish Mahendrakar   ++pos_;  // consume this character in the stream
251*103e46e4SHarish Mahendrakar   return 0;
252*103e46e4SHarish Mahendrakar }
253*103e46e4SHarish Mahendrakar 
UngetChar(char)254*103e46e4SHarish Mahendrakar void FrameParser::UngetChar(char /* c */) {
255*103e46e4SHarish Mahendrakar   // All we need to do here is decrement the position in the stream.
256*103e46e4SHarish Mahendrakar   // The next time GetChar is called the same character will be
257*103e46e4SHarish Mahendrakar   // re-read from the input file.
258*103e46e4SHarish Mahendrakar   --pos_;
259*103e46e4SHarish Mahendrakar }
260*103e46e4SHarish Mahendrakar 
ChapterAtomParser(const mkvparser::Chapters::Display * display)261*103e46e4SHarish Mahendrakar ChapterAtomParser::ChapterAtomParser(
262*103e46e4SHarish Mahendrakar     const mkvparser::Chapters::Display* display)
263*103e46e4SHarish Mahendrakar     : display_(display) {
264*103e46e4SHarish Mahendrakar   str_ = display->GetString();
265*103e46e4SHarish Mahendrakar   if (str_ == NULL)
266*103e46e4SHarish Mahendrakar     return;
267*103e46e4SHarish Mahendrakar   const size_t len = strlen(str_);
268*103e46e4SHarish Mahendrakar   str_end_ = str_ + len;
269*103e46e4SHarish Mahendrakar }
270*103e46e4SHarish Mahendrakar 
~ChapterAtomParser()271*103e46e4SHarish Mahendrakar ChapterAtomParser::~ChapterAtomParser() {}
272*103e46e4SHarish Mahendrakar 
GetChar(char * c)273*103e46e4SHarish Mahendrakar int ChapterAtomParser::GetChar(char* c) {
274*103e46e4SHarish Mahendrakar   if (str_ == NULL || str_ >= str_end_)  // end-of-stream
275*103e46e4SHarish Mahendrakar     return 1;  // per the semantics of libwebvtt::Reader::GetChar
276*103e46e4SHarish Mahendrakar 
277*103e46e4SHarish Mahendrakar   *c = *str_++;  // consume this character in the stream
278*103e46e4SHarish Mahendrakar   return 0;
279*103e46e4SHarish Mahendrakar }
280*103e46e4SHarish Mahendrakar 
UngetChar(char)281*103e46e4SHarish Mahendrakar void ChapterAtomParser::UngetChar(char /* c */) {
282*103e46e4SHarish Mahendrakar   // All we need to do here is decrement the position in the stream.
283*103e46e4SHarish Mahendrakar   // The next time GetChar is called the same character will be
284*103e46e4SHarish Mahendrakar   // re-read from the input file.
285*103e46e4SHarish Mahendrakar   --str_;
286*103e46e4SHarish Mahendrakar }
287*103e46e4SHarish Mahendrakar 
288*103e46e4SHarish Mahendrakar }  // namespace vttdemux
289*103e46e4SHarish Mahendrakar 
ParseHeader(mkvparser::IMkvReader * reader,mkvpos_t * pos)290*103e46e4SHarish Mahendrakar bool vttdemux::ParseHeader(mkvparser::IMkvReader* reader, mkvpos_t* pos) {
291*103e46e4SHarish Mahendrakar   mkvparser::EBMLHeader h;
292*103e46e4SHarish Mahendrakar   const mkvpos_t status = h.Parse(reader, *pos);
293*103e46e4SHarish Mahendrakar 
294*103e46e4SHarish Mahendrakar   if (status) {
295*103e46e4SHarish Mahendrakar     printf("error parsing EBML header\n");
296*103e46e4SHarish Mahendrakar     return false;
297*103e46e4SHarish Mahendrakar   }
298*103e46e4SHarish Mahendrakar 
299*103e46e4SHarish Mahendrakar   if (h.m_docType == NULL || strcmp(h.m_docType, "webm") != 0) {
300*103e46e4SHarish Mahendrakar     printf("bad doctype\n");
301*103e46e4SHarish Mahendrakar     return false;
302*103e46e4SHarish Mahendrakar   }
303*103e46e4SHarish Mahendrakar 
304*103e46e4SHarish Mahendrakar   return true;  // success
305*103e46e4SHarish Mahendrakar }
306*103e46e4SHarish Mahendrakar 
ParseSegment(mkvparser::IMkvReader * reader,mkvpos_t pos,segment_ptr_t * segment_ptr)307*103e46e4SHarish Mahendrakar bool vttdemux::ParseSegment(mkvparser::IMkvReader* reader, mkvpos_t pos,
308*103e46e4SHarish Mahendrakar                             segment_ptr_t* segment_ptr) {
309*103e46e4SHarish Mahendrakar   // We first create the segment object.
310*103e46e4SHarish Mahendrakar 
311*103e46e4SHarish Mahendrakar   mkvparser::Segment* p;
312*103e46e4SHarish Mahendrakar   const mkvpos_t create = mkvparser::Segment::CreateInstance(reader, pos, p);
313*103e46e4SHarish Mahendrakar 
314*103e46e4SHarish Mahendrakar   if (create) {
315*103e46e4SHarish Mahendrakar     printf("error parsing segment element\n");
316*103e46e4SHarish Mahendrakar     return false;
317*103e46e4SHarish Mahendrakar   }
318*103e46e4SHarish Mahendrakar 
319*103e46e4SHarish Mahendrakar   segment_ptr->reset(p);
320*103e46e4SHarish Mahendrakar 
321*103e46e4SHarish Mahendrakar   // Now parse all of the segment's sub-elements, in toto.
322*103e46e4SHarish Mahendrakar 
323*103e46e4SHarish Mahendrakar   const long status = p->Load();  // NOLINT
324*103e46e4SHarish Mahendrakar 
325*103e46e4SHarish Mahendrakar   if (status < 0) {
326*103e46e4SHarish Mahendrakar     printf("error loading segment\n");
327*103e46e4SHarish Mahendrakar     return false;
328*103e46e4SHarish Mahendrakar   }
329*103e46e4SHarish Mahendrakar 
330*103e46e4SHarish Mahendrakar   return true;
331*103e46e4SHarish Mahendrakar }
332*103e46e4SHarish Mahendrakar 
BuildMap(const mkvparser::Segment * segment,metadata_map_t * map_ptr)333*103e46e4SHarish Mahendrakar void vttdemux::BuildMap(const mkvparser::Segment* segment,
334*103e46e4SHarish Mahendrakar                         metadata_map_t* map_ptr) {
335*103e46e4SHarish Mahendrakar   metadata_map_t& m = *map_ptr;
336*103e46e4SHarish Mahendrakar   m.clear();
337*103e46e4SHarish Mahendrakar 
338*103e46e4SHarish Mahendrakar   if (segment->GetChapters()) {
339*103e46e4SHarish Mahendrakar     MetadataInfo info;
340*103e46e4SHarish Mahendrakar     info.file = NULL;
341*103e46e4SHarish Mahendrakar     info.type = MetadataInfo::kChapters;
342*103e46e4SHarish Mahendrakar 
343*103e46e4SHarish Mahendrakar     m[kChaptersKey] = info;
344*103e46e4SHarish Mahendrakar   }
345*103e46e4SHarish Mahendrakar 
346*103e46e4SHarish Mahendrakar   const mkvparser::Tracks* const tt = segment->GetTracks();
347*103e46e4SHarish Mahendrakar   if (tt == NULL)
348*103e46e4SHarish Mahendrakar     return;
349*103e46e4SHarish Mahendrakar 
350*103e46e4SHarish Mahendrakar   const long tc = tt->GetTracksCount();  // NOLINT
351*103e46e4SHarish Mahendrakar   if (tc <= 0)
352*103e46e4SHarish Mahendrakar     return;
353*103e46e4SHarish Mahendrakar 
354*103e46e4SHarish Mahendrakar   // Iterate over the tracks in the intput file.  We determine whether
355*103e46e4SHarish Mahendrakar   // a track holds metadata by inspecting its CodecID.
356*103e46e4SHarish Mahendrakar 
357*103e46e4SHarish Mahendrakar   for (long idx = 0; idx < tc; ++idx) {  // NOLINT
358*103e46e4SHarish Mahendrakar     const mkvparser::Track* const t = tt->GetTrackByIndex(idx);
359*103e46e4SHarish Mahendrakar 
360*103e46e4SHarish Mahendrakar     if (t == NULL)  // weird
361*103e46e4SHarish Mahendrakar       continue;
362*103e46e4SHarish Mahendrakar 
363*103e46e4SHarish Mahendrakar     const long tn = t->GetNumber();  // NOLINT
364*103e46e4SHarish Mahendrakar 
365*103e46e4SHarish Mahendrakar     if (tn <= 0)  // weird
366*103e46e4SHarish Mahendrakar       continue;
367*103e46e4SHarish Mahendrakar 
368*103e46e4SHarish Mahendrakar     const char* const codec_id = t->GetCodecId();
369*103e46e4SHarish Mahendrakar 
370*103e46e4SHarish Mahendrakar     if (codec_id == NULL)  // weird
371*103e46e4SHarish Mahendrakar       continue;
372*103e46e4SHarish Mahendrakar 
373*103e46e4SHarish Mahendrakar     MetadataInfo info;
374*103e46e4SHarish Mahendrakar     info.file = NULL;
375*103e46e4SHarish Mahendrakar 
376*103e46e4SHarish Mahendrakar     if (strcmp(codec_id, "D_WEBVTT/SUBTITLES") == 0) {
377*103e46e4SHarish Mahendrakar       info.type = MetadataInfo::kSubtitles;
378*103e46e4SHarish Mahendrakar     } else if (strcmp(codec_id, "D_WEBVTT/CAPTIONS") == 0) {
379*103e46e4SHarish Mahendrakar       info.type = MetadataInfo::kCaptions;
380*103e46e4SHarish Mahendrakar     } else if (strcmp(codec_id, "D_WEBVTT/DESCRIPTIONS") == 0) {
381*103e46e4SHarish Mahendrakar       info.type = MetadataInfo::kDescriptions;
382*103e46e4SHarish Mahendrakar     } else if (strcmp(codec_id, "D_WEBVTT/METADATA") == 0) {
383*103e46e4SHarish Mahendrakar       info.type = MetadataInfo::kMetadata;
384*103e46e4SHarish Mahendrakar     } else {
385*103e46e4SHarish Mahendrakar       continue;
386*103e46e4SHarish Mahendrakar     }
387*103e46e4SHarish Mahendrakar 
388*103e46e4SHarish Mahendrakar     m[tn] = info;  // create an entry in the cache for this track
389*103e46e4SHarish Mahendrakar   }
390*103e46e4SHarish Mahendrakar }
391*103e46e4SHarish Mahendrakar 
OpenFiles(metadata_map_t * metadata_map,const char * filename)392*103e46e4SHarish Mahendrakar bool vttdemux::OpenFiles(metadata_map_t* metadata_map, const char* filename) {
393*103e46e4SHarish Mahendrakar   if (metadata_map == NULL || metadata_map->empty())
394*103e46e4SHarish Mahendrakar     return false;
395*103e46e4SHarish Mahendrakar 
396*103e46e4SHarish Mahendrakar   if (filename == NULL)
397*103e46e4SHarish Mahendrakar     return false;
398*103e46e4SHarish Mahendrakar 
399*103e46e4SHarish Mahendrakar   // Find the position of the filename extension.  We synthesize the
400*103e46e4SHarish Mahendrakar   // output filename from the directory path and basename of the input
401*103e46e4SHarish Mahendrakar   // filename.
402*103e46e4SHarish Mahendrakar 
403*103e46e4SHarish Mahendrakar   const char* const ext = strrchr(filename, '.');
404*103e46e4SHarish Mahendrakar 
405*103e46e4SHarish Mahendrakar   if (ext == NULL)  // TODO(matthewjheaney): liberalize?
406*103e46e4SHarish Mahendrakar     return false;
407*103e46e4SHarish Mahendrakar 
408*103e46e4SHarish Mahendrakar   // Remember whether a track of this type has already been seen (the
409*103e46e4SHarish Mahendrakar   // map key) by keeping a count (the map item).  We quality the
410*103e46e4SHarish Mahendrakar   // output filename with the track number if there is more than one
411*103e46e4SHarish Mahendrakar   // track having a given type.
412*103e46e4SHarish Mahendrakar 
413*103e46e4SHarish Mahendrakar   std::map<MetadataInfo::Type, int> exists;
414*103e46e4SHarish Mahendrakar 
415*103e46e4SHarish Mahendrakar   typedef metadata_map_t::iterator iter_t;
416*103e46e4SHarish Mahendrakar 
417*103e46e4SHarish Mahendrakar   metadata_map_t& m = *metadata_map;
418*103e46e4SHarish Mahendrakar   const iter_t ii = m.begin();
419*103e46e4SHarish Mahendrakar   const iter_t j = m.end();
420*103e46e4SHarish Mahendrakar 
421*103e46e4SHarish Mahendrakar   // Make a first pass over the cache to determine whether there is
422*103e46e4SHarish Mahendrakar   // more than one track corresponding to a given metadata type.
423*103e46e4SHarish Mahendrakar 
424*103e46e4SHarish Mahendrakar   iter_t i = ii;
425*103e46e4SHarish Mahendrakar   while (i != j) {
426*103e46e4SHarish Mahendrakar     const metadata_map_t::value_type& v = *i++;
427*103e46e4SHarish Mahendrakar     const MetadataInfo& info = v.second;
428*103e46e4SHarish Mahendrakar     const MetadataInfo::Type type = info.type;
429*103e46e4SHarish Mahendrakar     ++exists[type];
430*103e46e4SHarish Mahendrakar   }
431*103e46e4SHarish Mahendrakar 
432*103e46e4SHarish Mahendrakar   // Make a second pass over the cache, synthesizing the filename of
433*103e46e4SHarish Mahendrakar   // each output file (from the input file basename, the input track
434*103e46e4SHarish Mahendrakar   // metadata type, and its track number if necessary), and then
435*103e46e4SHarish Mahendrakar   // opening a WebVTT output file having that filename.
436*103e46e4SHarish Mahendrakar 
437*103e46e4SHarish Mahendrakar   i = ii;
438*103e46e4SHarish Mahendrakar   while (i != j) {
439*103e46e4SHarish Mahendrakar     metadata_map_t::value_type& v = *i++;
440*103e46e4SHarish Mahendrakar     MetadataInfo& info = v.second;
441*103e46e4SHarish Mahendrakar     const MetadataInfo::Type type = info.type;
442*103e46e4SHarish Mahendrakar 
443*103e46e4SHarish Mahendrakar     // Start with the basename of the input file.
444*103e46e4SHarish Mahendrakar 
445*103e46e4SHarish Mahendrakar     string name(filename, ext);
446*103e46e4SHarish Mahendrakar 
447*103e46e4SHarish Mahendrakar     // Next append the metadata kind.
448*103e46e4SHarish Mahendrakar 
449*103e46e4SHarish Mahendrakar     switch (type) {
450*103e46e4SHarish Mahendrakar       case MetadataInfo::kSubtitles:
451*103e46e4SHarish Mahendrakar         name += "_SUBTITLES";
452*103e46e4SHarish Mahendrakar         break;
453*103e46e4SHarish Mahendrakar 
454*103e46e4SHarish Mahendrakar       case MetadataInfo::kCaptions:
455*103e46e4SHarish Mahendrakar         name += "_CAPTIONS";
456*103e46e4SHarish Mahendrakar         break;
457*103e46e4SHarish Mahendrakar 
458*103e46e4SHarish Mahendrakar       case MetadataInfo::kDescriptions:
459*103e46e4SHarish Mahendrakar         name += "_DESCRIPTIONS";
460*103e46e4SHarish Mahendrakar         break;
461*103e46e4SHarish Mahendrakar 
462*103e46e4SHarish Mahendrakar       case MetadataInfo::kMetadata:
463*103e46e4SHarish Mahendrakar         name += "_METADATA";
464*103e46e4SHarish Mahendrakar         break;
465*103e46e4SHarish Mahendrakar 
466*103e46e4SHarish Mahendrakar       case MetadataInfo::kChapters:
467*103e46e4SHarish Mahendrakar         name += "_CHAPTERS";
468*103e46e4SHarish Mahendrakar         break;
469*103e46e4SHarish Mahendrakar 
470*103e46e4SHarish Mahendrakar       default:
471*103e46e4SHarish Mahendrakar         return false;
472*103e46e4SHarish Mahendrakar     }
473*103e46e4SHarish Mahendrakar 
474*103e46e4SHarish Mahendrakar     // If there is more than one metadata track having a given type
475*103e46e4SHarish Mahendrakar     // (the WebVTT-in-WebM spec doesn't preclude this), then qualify
476*103e46e4SHarish Mahendrakar     // the output filename with the input track number.
477*103e46e4SHarish Mahendrakar 
478*103e46e4SHarish Mahendrakar     if (exists[type] > 1) {
479*103e46e4SHarish Mahendrakar       enum { kLen = 33 };
480*103e46e4SHarish Mahendrakar       char str[kLen];  // max 126 tracks, so only 4 chars really needed
481*103e46e4SHarish Mahendrakar #ifndef _MSC_VER
482*103e46e4SHarish Mahendrakar       snprintf(str, kLen, "%ld", v.first);  // track number
483*103e46e4SHarish Mahendrakar #else
484*103e46e4SHarish Mahendrakar       _snprintf_s(str, sizeof(str), kLen, "%ld", v.first);  // track number
485*103e46e4SHarish Mahendrakar #endif
486*103e46e4SHarish Mahendrakar       name += str;
487*103e46e4SHarish Mahendrakar     }
488*103e46e4SHarish Mahendrakar 
489*103e46e4SHarish Mahendrakar     // Finally append the output filename extension.
490*103e46e4SHarish Mahendrakar 
491*103e46e4SHarish Mahendrakar     name += ".vtt";
492*103e46e4SHarish Mahendrakar 
493*103e46e4SHarish Mahendrakar     // We have synthesized the full output filename, so attempt to
494*103e46e4SHarish Mahendrakar     // open the WebVTT output file.
495*103e46e4SHarish Mahendrakar 
496*103e46e4SHarish Mahendrakar     info.file = fopen(name.c_str(), "wb");
497*103e46e4SHarish Mahendrakar     const bool success = (info.file != NULL);
498*103e46e4SHarish Mahendrakar 
499*103e46e4SHarish Mahendrakar     if (!success) {
500*103e46e4SHarish Mahendrakar       printf("unable to open output file %s\n", name.c_str());
501*103e46e4SHarish Mahendrakar       return false;
502*103e46e4SHarish Mahendrakar     }
503*103e46e4SHarish Mahendrakar   }
504*103e46e4SHarish Mahendrakar 
505*103e46e4SHarish Mahendrakar   return true;
506*103e46e4SHarish Mahendrakar }
507*103e46e4SHarish Mahendrakar 
CloseFiles(metadata_map_t * metadata_map)508*103e46e4SHarish Mahendrakar void vttdemux::CloseFiles(metadata_map_t* metadata_map) {
509*103e46e4SHarish Mahendrakar   if (metadata_map == NULL)
510*103e46e4SHarish Mahendrakar     return;
511*103e46e4SHarish Mahendrakar 
512*103e46e4SHarish Mahendrakar   metadata_map_t& m = *metadata_map;
513*103e46e4SHarish Mahendrakar 
514*103e46e4SHarish Mahendrakar   typedef metadata_map_t::iterator iter_t;
515*103e46e4SHarish Mahendrakar 
516*103e46e4SHarish Mahendrakar   iter_t i = m.begin();
517*103e46e4SHarish Mahendrakar   const iter_t j = m.end();
518*103e46e4SHarish Mahendrakar 
519*103e46e4SHarish Mahendrakar   // Gracefully close each output file, to ensure all output gets
520*103e46e4SHarish Mahendrakar   // propertly flushed.
521*103e46e4SHarish Mahendrakar 
522*103e46e4SHarish Mahendrakar   while (i != j) {
523*103e46e4SHarish Mahendrakar     metadata_map_t::value_type& v = *i++;
524*103e46e4SHarish Mahendrakar     MetadataInfo& info = v.second;
525*103e46e4SHarish Mahendrakar 
526*103e46e4SHarish Mahendrakar     if (info.file != NULL) {
527*103e46e4SHarish Mahendrakar       fclose(info.file);
528*103e46e4SHarish Mahendrakar       info.file = NULL;
529*103e46e4SHarish Mahendrakar     }
530*103e46e4SHarish Mahendrakar   }
531*103e46e4SHarish Mahendrakar }
532*103e46e4SHarish Mahendrakar 
WriteFiles(const metadata_map_t & m,mkvparser::Segment * s)533*103e46e4SHarish Mahendrakar bool vttdemux::WriteFiles(const metadata_map_t& m, mkvparser::Segment* s) {
534*103e46e4SHarish Mahendrakar   // First write the WebVTT header.
535*103e46e4SHarish Mahendrakar 
536*103e46e4SHarish Mahendrakar   InitializeFiles(m);
537*103e46e4SHarish Mahendrakar 
538*103e46e4SHarish Mahendrakar   if (!WriteChaptersFile(m, s))
539*103e46e4SHarish Mahendrakar     return false;
540*103e46e4SHarish Mahendrakar 
541*103e46e4SHarish Mahendrakar   // Now iterate over the clusters, writing the WebVTT cue as we parse
542*103e46e4SHarish Mahendrakar   // each metadata block.
543*103e46e4SHarish Mahendrakar 
544*103e46e4SHarish Mahendrakar   const mkvparser::Cluster* cluster = s->GetFirst();
545*103e46e4SHarish Mahendrakar 
546*103e46e4SHarish Mahendrakar   while (cluster != NULL && !cluster->EOS()) {
547*103e46e4SHarish Mahendrakar     if (!ProcessCluster(m, cluster))
548*103e46e4SHarish Mahendrakar       return false;
549*103e46e4SHarish Mahendrakar 
550*103e46e4SHarish Mahendrakar     cluster = s->GetNext(cluster);
551*103e46e4SHarish Mahendrakar   }
552*103e46e4SHarish Mahendrakar 
553*103e46e4SHarish Mahendrakar   return true;
554*103e46e4SHarish Mahendrakar }
555*103e46e4SHarish Mahendrakar 
InitializeFiles(const metadata_map_t & m)556*103e46e4SHarish Mahendrakar bool vttdemux::InitializeFiles(const metadata_map_t& m) {
557*103e46e4SHarish Mahendrakar   // Write the WebVTT header for each output file in the cache.
558*103e46e4SHarish Mahendrakar 
559*103e46e4SHarish Mahendrakar   typedef metadata_map_t::const_iterator iter_t;
560*103e46e4SHarish Mahendrakar   iter_t i = m.begin();
561*103e46e4SHarish Mahendrakar   const iter_t j = m.end();
562*103e46e4SHarish Mahendrakar 
563*103e46e4SHarish Mahendrakar   while (i != j) {
564*103e46e4SHarish Mahendrakar     const metadata_map_t::value_type& v = *i++;
565*103e46e4SHarish Mahendrakar     const MetadataInfo& info = v.second;
566*103e46e4SHarish Mahendrakar     FILE* const f = info.file;
567*103e46e4SHarish Mahendrakar 
568*103e46e4SHarish Mahendrakar     if (fputs("WEBVTT\n", f) < 0) {
569*103e46e4SHarish Mahendrakar       printf("unable to initialize output file\n");
570*103e46e4SHarish Mahendrakar       return false;
571*103e46e4SHarish Mahendrakar     }
572*103e46e4SHarish Mahendrakar   }
573*103e46e4SHarish Mahendrakar 
574*103e46e4SHarish Mahendrakar   return true;
575*103e46e4SHarish Mahendrakar }
576*103e46e4SHarish Mahendrakar 
WriteChaptersFile(const metadata_map_t & m,const mkvparser::Segment * s)577*103e46e4SHarish Mahendrakar bool vttdemux::WriteChaptersFile(const metadata_map_t& m,
578*103e46e4SHarish Mahendrakar                                  const mkvparser::Segment* s) {
579*103e46e4SHarish Mahendrakar   const metadata_map_t::const_iterator info_iter = m.find(kChaptersKey);
580*103e46e4SHarish Mahendrakar   if (info_iter == m.end())  // no chapters, so nothing to do
581*103e46e4SHarish Mahendrakar     return true;
582*103e46e4SHarish Mahendrakar 
583*103e46e4SHarish Mahendrakar   const mkvparser::Chapters* const chapters = s->GetChapters();
584*103e46e4SHarish Mahendrakar   if (chapters == NULL)  // weird
585*103e46e4SHarish Mahendrakar     return true;
586*103e46e4SHarish Mahendrakar 
587*103e46e4SHarish Mahendrakar   const MetadataInfo& info = info_iter->second;
588*103e46e4SHarish Mahendrakar   FILE* const file = info.file;
589*103e46e4SHarish Mahendrakar 
590*103e46e4SHarish Mahendrakar   const int edition_count = chapters->GetEditionCount();
591*103e46e4SHarish Mahendrakar 
592*103e46e4SHarish Mahendrakar   if (edition_count <= 0)  // weird
593*103e46e4SHarish Mahendrakar     return true;  // nothing to do
594*103e46e4SHarish Mahendrakar 
595*103e46e4SHarish Mahendrakar   if (edition_count > 1) {
596*103e46e4SHarish Mahendrakar     // TODO(matthewjheaney): figure what to do here
597*103e46e4SHarish Mahendrakar     printf("more than one chapter edition detected\n");
598*103e46e4SHarish Mahendrakar     return false;
599*103e46e4SHarish Mahendrakar   }
600*103e46e4SHarish Mahendrakar 
601*103e46e4SHarish Mahendrakar   const mkvparser::Chapters::Edition* const edition = chapters->GetEdition(0);
602*103e46e4SHarish Mahendrakar 
603*103e46e4SHarish Mahendrakar   const int atom_count = edition->GetAtomCount();
604*103e46e4SHarish Mahendrakar 
605*103e46e4SHarish Mahendrakar   for (int idx = 0; idx < atom_count; ++idx) {
606*103e46e4SHarish Mahendrakar     const mkvparser::Chapters::Atom* const atom = edition->GetAtom(idx);
607*103e46e4SHarish Mahendrakar     const int display_count = atom->GetDisplayCount();
608*103e46e4SHarish Mahendrakar 
609*103e46e4SHarish Mahendrakar     if (display_count <= 0)
610*103e46e4SHarish Mahendrakar       continue;
611*103e46e4SHarish Mahendrakar 
612*103e46e4SHarish Mahendrakar     if (display_count > 1) {
613*103e46e4SHarish Mahendrakar       // TODO(matthewjheaney): handle case of multiple languages
614*103e46e4SHarish Mahendrakar       printf("more than 1 display in atom detected\n");
615*103e46e4SHarish Mahendrakar       return false;
616*103e46e4SHarish Mahendrakar     }
617*103e46e4SHarish Mahendrakar 
618*103e46e4SHarish Mahendrakar     const mkvparser::Chapters::Display* const display = atom->GetDisplay(0);
619*103e46e4SHarish Mahendrakar 
620*103e46e4SHarish Mahendrakar     if (const char* language = display->GetLanguage()) {
621*103e46e4SHarish Mahendrakar       if (strcmp(language, "eng") != 0) {
622*103e46e4SHarish Mahendrakar         // TODO(matthewjheaney): handle case of multiple languages.
623*103e46e4SHarish Mahendrakar 
624*103e46e4SHarish Mahendrakar         // We must create a separate webvtt file for each language.
625*103e46e4SHarish Mahendrakar         // This isn't a simple problem (which is why we defer it for
626*103e46e4SHarish Mahendrakar         // now), because there's nothing in the header that tells us
627*103e46e4SHarish Mahendrakar         // what languages we have as cues.  We must parse the displays
628*103e46e4SHarish Mahendrakar         // of each atom to determine that.
629*103e46e4SHarish Mahendrakar 
630*103e46e4SHarish Mahendrakar         // One solution is to make two passes over the input data.
631*103e46e4SHarish Mahendrakar         // First parse the displays, creating an in-memory cache of
632*103e46e4SHarish Mahendrakar         // all the chapter cues, sorted according to their language.
633*103e46e4SHarish Mahendrakar         // After we have read all of the chapter atoms from the input
634*103e46e4SHarish Mahendrakar         // file, we can then write separate output files for each
635*103e46e4SHarish Mahendrakar         // language.
636*103e46e4SHarish Mahendrakar 
637*103e46e4SHarish Mahendrakar         printf("only English-language chapter cues are supported\n");
638*103e46e4SHarish Mahendrakar         return false;
639*103e46e4SHarish Mahendrakar       }
640*103e46e4SHarish Mahendrakar     }
641*103e46e4SHarish Mahendrakar 
642*103e46e4SHarish Mahendrakar     if (!WriteChaptersCue(file, chapters, atom, display))
643*103e46e4SHarish Mahendrakar       return false;
644*103e46e4SHarish Mahendrakar   }
645*103e46e4SHarish Mahendrakar 
646*103e46e4SHarish Mahendrakar   return true;
647*103e46e4SHarish Mahendrakar }
648*103e46e4SHarish Mahendrakar 
WriteChaptersCue(FILE * f,const mkvparser::Chapters * chapters,const mkvparser::Chapters::Atom * atom,const mkvparser::Chapters::Display * display)649*103e46e4SHarish Mahendrakar bool vttdemux::WriteChaptersCue(FILE* f, const mkvparser::Chapters* chapters,
650*103e46e4SHarish Mahendrakar                                 const mkvparser::Chapters::Atom* atom,
651*103e46e4SHarish Mahendrakar                                 const mkvparser::Chapters::Display* display) {
652*103e46e4SHarish Mahendrakar   // We start a new cue by writing a cue separator (an empty line)
653*103e46e4SHarish Mahendrakar   // into the stream.
654*103e46e4SHarish Mahendrakar 
655*103e46e4SHarish Mahendrakar   if (fputc('\n', f) < 0)
656*103e46e4SHarish Mahendrakar     return false;
657*103e46e4SHarish Mahendrakar 
658*103e46e4SHarish Mahendrakar   // A WebVTT Cue comprises 3 things: a cue identifier, followed by
659*103e46e4SHarish Mahendrakar   // the cue timings, followed by the payload of the cue.  We write
660*103e46e4SHarish Mahendrakar   // each part of the cue in sequence.
661*103e46e4SHarish Mahendrakar 
662*103e46e4SHarish Mahendrakar   if (!WriteChaptersCueIdentifier(f, atom))
663*103e46e4SHarish Mahendrakar     return false;
664*103e46e4SHarish Mahendrakar 
665*103e46e4SHarish Mahendrakar   if (!WriteChaptersCueTimings(f, chapters, atom))
666*103e46e4SHarish Mahendrakar     return false;
667*103e46e4SHarish Mahendrakar 
668*103e46e4SHarish Mahendrakar   if (!WriteChaptersCuePayload(f, display))
669*103e46e4SHarish Mahendrakar     return false;
670*103e46e4SHarish Mahendrakar 
671*103e46e4SHarish Mahendrakar   return true;
672*103e46e4SHarish Mahendrakar }
673*103e46e4SHarish Mahendrakar 
WriteChaptersCueIdentifier(FILE * f,const mkvparser::Chapters::Atom * atom)674*103e46e4SHarish Mahendrakar bool vttdemux::WriteChaptersCueIdentifier(
675*103e46e4SHarish Mahendrakar     FILE* f, const mkvparser::Chapters::Atom* atom) {
676*103e46e4SHarish Mahendrakar   const char* const identifier = atom->GetStringUID();
677*103e46e4SHarish Mahendrakar 
678*103e46e4SHarish Mahendrakar   if (identifier == NULL)
679*103e46e4SHarish Mahendrakar     return true;  // nothing else to do
680*103e46e4SHarish Mahendrakar 
681*103e46e4SHarish Mahendrakar   if (fprintf(f, "%s\n", identifier) < 0)
682*103e46e4SHarish Mahendrakar     return false;
683*103e46e4SHarish Mahendrakar 
684*103e46e4SHarish Mahendrakar   return true;
685*103e46e4SHarish Mahendrakar }
686*103e46e4SHarish Mahendrakar 
WriteChaptersCueTimings(FILE * f,const mkvparser::Chapters * chapters,const mkvparser::Chapters::Atom * atom)687*103e46e4SHarish Mahendrakar bool vttdemux::WriteChaptersCueTimings(FILE* f,
688*103e46e4SHarish Mahendrakar                                        const mkvparser::Chapters* chapters,
689*103e46e4SHarish Mahendrakar                                        const mkvparser::Chapters::Atom* atom) {
690*103e46e4SHarish Mahendrakar   const mkvtime_t start_ns = atom->GetStartTime(chapters);
691*103e46e4SHarish Mahendrakar 
692*103e46e4SHarish Mahendrakar   if (start_ns < 0)
693*103e46e4SHarish Mahendrakar     return false;
694*103e46e4SHarish Mahendrakar 
695*103e46e4SHarish Mahendrakar   const mkvtime_t stop_ns = atom->GetStopTime(chapters);
696*103e46e4SHarish Mahendrakar 
697*103e46e4SHarish Mahendrakar   if (stop_ns < 0)
698*103e46e4SHarish Mahendrakar     return false;
699*103e46e4SHarish Mahendrakar 
700*103e46e4SHarish Mahendrakar   if (!WriteCueTime(f, start_ns))
701*103e46e4SHarish Mahendrakar     return false;
702*103e46e4SHarish Mahendrakar 
703*103e46e4SHarish Mahendrakar   if (fputs(" --> ", f) < 0)
704*103e46e4SHarish Mahendrakar     return false;
705*103e46e4SHarish Mahendrakar 
706*103e46e4SHarish Mahendrakar   if (!WriteCueTime(f, stop_ns))
707*103e46e4SHarish Mahendrakar     return false;
708*103e46e4SHarish Mahendrakar 
709*103e46e4SHarish Mahendrakar   if (fputc('\n', f) < 0)
710*103e46e4SHarish Mahendrakar     return false;
711*103e46e4SHarish Mahendrakar 
712*103e46e4SHarish Mahendrakar   return true;
713*103e46e4SHarish Mahendrakar }
714*103e46e4SHarish Mahendrakar 
WriteChaptersCuePayload(FILE * f,const mkvparser::Chapters::Display * display)715*103e46e4SHarish Mahendrakar bool vttdemux::WriteChaptersCuePayload(
716*103e46e4SHarish Mahendrakar     FILE* f, const mkvparser::Chapters::Display* display) {
717*103e46e4SHarish Mahendrakar   // Bind a Chapter parser object to the display, which allows us to
718*103e46e4SHarish Mahendrakar   // extract each line of text from the title-part of the display.
719*103e46e4SHarish Mahendrakar   ChapterAtomParser parser(display);
720*103e46e4SHarish Mahendrakar 
721*103e46e4SHarish Mahendrakar   int count = 0;  // count of lines of payload text written to output file
722*103e46e4SHarish Mahendrakar   for (string line;;) {
723*103e46e4SHarish Mahendrakar     const int e = parser.GetLine(&line);
724*103e46e4SHarish Mahendrakar 
725*103e46e4SHarish Mahendrakar     if (e < 0)  // error (only -- we allow EOS here)
726*103e46e4SHarish Mahendrakar       return false;
727*103e46e4SHarish Mahendrakar 
728*103e46e4SHarish Mahendrakar     if (line.empty())  // TODO(matthewjheaney): retain this check?
729*103e46e4SHarish Mahendrakar       break;
730*103e46e4SHarish Mahendrakar 
731*103e46e4SHarish Mahendrakar     if (fprintf(f, "%s\n", line.c_str()) < 0)
732*103e46e4SHarish Mahendrakar       return false;
733*103e46e4SHarish Mahendrakar 
734*103e46e4SHarish Mahendrakar     ++count;
735*103e46e4SHarish Mahendrakar   }
736*103e46e4SHarish Mahendrakar 
737*103e46e4SHarish Mahendrakar   if (count <= 0)  // WebVTT cue requires non-empty payload
738*103e46e4SHarish Mahendrakar     return false;
739*103e46e4SHarish Mahendrakar 
740*103e46e4SHarish Mahendrakar   return true;
741*103e46e4SHarish Mahendrakar }
742*103e46e4SHarish Mahendrakar 
ProcessCluster(const metadata_map_t & m,const mkvparser::Cluster * c)743*103e46e4SHarish Mahendrakar bool vttdemux::ProcessCluster(const metadata_map_t& m,
744*103e46e4SHarish Mahendrakar                               const mkvparser::Cluster* c) {
745*103e46e4SHarish Mahendrakar   // Visit the blocks in this cluster, writing a WebVTT cue for each
746*103e46e4SHarish Mahendrakar   // metadata block.
747*103e46e4SHarish Mahendrakar 
748*103e46e4SHarish Mahendrakar   const mkvparser::BlockEntry* block_entry;
749*103e46e4SHarish Mahendrakar 
750*103e46e4SHarish Mahendrakar   long result = c->GetFirst(block_entry);  // NOLINT
751*103e46e4SHarish Mahendrakar   if (result < 0) {
752*103e46e4SHarish Mahendrakar     printf("bad cluster (unable to get first block)\n");
753*103e46e4SHarish Mahendrakar     return false;
754*103e46e4SHarish Mahendrakar   }
755*103e46e4SHarish Mahendrakar 
756*103e46e4SHarish Mahendrakar   while (block_entry != NULL && !block_entry->EOS()) {
757*103e46e4SHarish Mahendrakar     if (!ProcessBlockEntry(m, block_entry))
758*103e46e4SHarish Mahendrakar       return false;
759*103e46e4SHarish Mahendrakar 
760*103e46e4SHarish Mahendrakar     result = c->GetNext(block_entry, block_entry);
761*103e46e4SHarish Mahendrakar     if (result < 0) {  // error
762*103e46e4SHarish Mahendrakar       printf("bad cluster (unable to get next block)\n");
763*103e46e4SHarish Mahendrakar       return false;
764*103e46e4SHarish Mahendrakar     }
765*103e46e4SHarish Mahendrakar   }
766*103e46e4SHarish Mahendrakar 
767*103e46e4SHarish Mahendrakar   return true;
768*103e46e4SHarish Mahendrakar }
769*103e46e4SHarish Mahendrakar 
ProcessBlockEntry(const metadata_map_t & m,const mkvparser::BlockEntry * block_entry)770*103e46e4SHarish Mahendrakar bool vttdemux::ProcessBlockEntry(const metadata_map_t& m,
771*103e46e4SHarish Mahendrakar                                  const mkvparser::BlockEntry* block_entry) {
772*103e46e4SHarish Mahendrakar   // If the track number for this block is in the cache, then we have
773*103e46e4SHarish Mahendrakar   // a metadata block, so write the WebVTT cue to the output file.
774*103e46e4SHarish Mahendrakar 
775*103e46e4SHarish Mahendrakar   const mkvparser::Block* const block = block_entry->GetBlock();
776*103e46e4SHarish Mahendrakar   const long long tn = block->GetTrackNumber();  // NOLINT
777*103e46e4SHarish Mahendrakar 
778*103e46e4SHarish Mahendrakar   typedef metadata_map_t::const_iterator iter_t;
779*103e46e4SHarish Mahendrakar   const iter_t i = m.find(static_cast<metadata_map_t::key_type>(tn));
780*103e46e4SHarish Mahendrakar 
781*103e46e4SHarish Mahendrakar   if (i == m.end())  // not a metadata track
782*103e46e4SHarish Mahendrakar     return true;  // nothing else to do
783*103e46e4SHarish Mahendrakar 
784*103e46e4SHarish Mahendrakar   if (block_entry->GetKind() != mkvparser::BlockEntry::kBlockGroup)
785*103e46e4SHarish Mahendrakar     return false;  // weird
786*103e46e4SHarish Mahendrakar 
787*103e46e4SHarish Mahendrakar   typedef mkvparser::BlockGroup BG;
788*103e46e4SHarish Mahendrakar   const BG* const block_group = static_cast<const BG*>(block_entry);
789*103e46e4SHarish Mahendrakar 
790*103e46e4SHarish Mahendrakar   const MetadataInfo& info = i->second;
791*103e46e4SHarish Mahendrakar   FILE* const f = info.file;
792*103e46e4SHarish Mahendrakar 
793*103e46e4SHarish Mahendrakar   return WriteCue(f, block_group);
794*103e46e4SHarish Mahendrakar }
795*103e46e4SHarish Mahendrakar 
WriteCue(FILE * f,const mkvparser::BlockGroup * block_group)796*103e46e4SHarish Mahendrakar bool vttdemux::WriteCue(FILE* f, const mkvparser::BlockGroup* block_group) {
797*103e46e4SHarish Mahendrakar   // Bind a FrameParser object to the block, which allows us to
798*103e46e4SHarish Mahendrakar   // extract each line of text from the payload of the block.
799*103e46e4SHarish Mahendrakar   FrameParser parser(block_group);
800*103e46e4SHarish Mahendrakar 
801*103e46e4SHarish Mahendrakar   // We start a new cue by writing a cue separator (an empty line)
802*103e46e4SHarish Mahendrakar   // into the stream.
803*103e46e4SHarish Mahendrakar 
804*103e46e4SHarish Mahendrakar   if (fputc('\n', f) < 0)
805*103e46e4SHarish Mahendrakar     return false;
806*103e46e4SHarish Mahendrakar 
807*103e46e4SHarish Mahendrakar   // A WebVTT Cue comprises 3 things: a cue identifier, followed by
808*103e46e4SHarish Mahendrakar   // the cue timings, followed by the payload of the cue.  We write
809*103e46e4SHarish Mahendrakar   // each part of the cue in sequence.
810*103e46e4SHarish Mahendrakar 
811*103e46e4SHarish Mahendrakar   if (!WriteCueIdentifier(f, &parser))
812*103e46e4SHarish Mahendrakar     return false;
813*103e46e4SHarish Mahendrakar 
814*103e46e4SHarish Mahendrakar   if (!WriteCueTimings(f, &parser))
815*103e46e4SHarish Mahendrakar     return false;
816*103e46e4SHarish Mahendrakar 
817*103e46e4SHarish Mahendrakar   if (!WriteCuePayload(f, &parser))
818*103e46e4SHarish Mahendrakar     return false;
819*103e46e4SHarish Mahendrakar 
820*103e46e4SHarish Mahendrakar   return true;
821*103e46e4SHarish Mahendrakar }
822*103e46e4SHarish Mahendrakar 
WriteCueIdentifier(FILE * f,FrameParser * parser)823*103e46e4SHarish Mahendrakar bool vttdemux::WriteCueIdentifier(FILE* f, FrameParser* parser) {
824*103e46e4SHarish Mahendrakar   string line;
825*103e46e4SHarish Mahendrakar   int e = parser->GetLine(&line);
826*103e46e4SHarish Mahendrakar 
827*103e46e4SHarish Mahendrakar   if (e)  // error or EOS
828*103e46e4SHarish Mahendrakar     return false;
829*103e46e4SHarish Mahendrakar 
830*103e46e4SHarish Mahendrakar   // If the cue identifier line is empty, this means that the original
831*103e46e4SHarish Mahendrakar   // WebVTT cue did not have a cue identifier, so we don't bother
832*103e46e4SHarish Mahendrakar   // writing an extra line terminator to the output file (though doing
833*103e46e4SHarish Mahendrakar   // so would be harmless).
834*103e46e4SHarish Mahendrakar 
835*103e46e4SHarish Mahendrakar   if (!line.empty()) {
836*103e46e4SHarish Mahendrakar     if (fputs(line.c_str(), f) < 0)
837*103e46e4SHarish Mahendrakar       return false;
838*103e46e4SHarish Mahendrakar 
839*103e46e4SHarish Mahendrakar     if (fputc('\n', f) < 0)
840*103e46e4SHarish Mahendrakar       return false;
841*103e46e4SHarish Mahendrakar   }
842*103e46e4SHarish Mahendrakar 
843*103e46e4SHarish Mahendrakar   return true;
844*103e46e4SHarish Mahendrakar }
845*103e46e4SHarish Mahendrakar 
WriteCueTimings(FILE * f,FrameParser * parser)846*103e46e4SHarish Mahendrakar bool vttdemux::WriteCueTimings(FILE* f, FrameParser* parser) {
847*103e46e4SHarish Mahendrakar   const mkvparser::BlockGroup* const block_group = parser->block_group_;
848*103e46e4SHarish Mahendrakar   const mkvparser::Cluster* const cluster = block_group->GetCluster();
849*103e46e4SHarish Mahendrakar   const mkvparser::Block* const block = block_group->GetBlock();
850*103e46e4SHarish Mahendrakar 
851*103e46e4SHarish Mahendrakar   // A WebVTT Cue "timings" line comprises two parts: the start and
852*103e46e4SHarish Mahendrakar   // stop time for this cue, followed by the (optional) cue settings,
853*103e46e4SHarish Mahendrakar   // such as orientation of the rendered text or its size.  Only the
854*103e46e4SHarish Mahendrakar   // settings part of the cue timings line is stored in the WebM
855*103e46e4SHarish Mahendrakar   // block.  We reconstruct the start and stop times of the WebVTT cue
856*103e46e4SHarish Mahendrakar   // from the timestamp and duration of the WebM block.
857*103e46e4SHarish Mahendrakar 
858*103e46e4SHarish Mahendrakar   const mkvtime_t start_ns = block->GetTime(cluster);
859*103e46e4SHarish Mahendrakar 
860*103e46e4SHarish Mahendrakar   if (!WriteCueTime(f, start_ns))
861*103e46e4SHarish Mahendrakar     return false;
862*103e46e4SHarish Mahendrakar 
863*103e46e4SHarish Mahendrakar   if (fputs(" --> ", f) < 0)
864*103e46e4SHarish Mahendrakar     return false;
865*103e46e4SHarish Mahendrakar 
866*103e46e4SHarish Mahendrakar   const mkvtime_t duration_timecode = block_group->GetDurationTimeCode();
867*103e46e4SHarish Mahendrakar 
868*103e46e4SHarish Mahendrakar   if (duration_timecode < 0)
869*103e46e4SHarish Mahendrakar     return false;
870*103e46e4SHarish Mahendrakar 
871*103e46e4SHarish Mahendrakar   const mkvparser::Segment* const segment = cluster->m_pSegment;
872*103e46e4SHarish Mahendrakar   const mkvparser::SegmentInfo* const info = segment->GetInfo();
873*103e46e4SHarish Mahendrakar 
874*103e46e4SHarish Mahendrakar   if (info == NULL)
875*103e46e4SHarish Mahendrakar     return false;
876*103e46e4SHarish Mahendrakar 
877*103e46e4SHarish Mahendrakar   const mkvtime_t timecode_scale = info->GetTimeCodeScale();
878*103e46e4SHarish Mahendrakar 
879*103e46e4SHarish Mahendrakar   if (timecode_scale <= 0)
880*103e46e4SHarish Mahendrakar     return false;
881*103e46e4SHarish Mahendrakar 
882*103e46e4SHarish Mahendrakar   const mkvtime_t duration_ns = duration_timecode * timecode_scale;
883*103e46e4SHarish Mahendrakar   const mkvtime_t stop_ns = start_ns + duration_ns;
884*103e46e4SHarish Mahendrakar 
885*103e46e4SHarish Mahendrakar   if (!WriteCueTime(f, stop_ns))
886*103e46e4SHarish Mahendrakar     return false;
887*103e46e4SHarish Mahendrakar 
888*103e46e4SHarish Mahendrakar   string line;
889*103e46e4SHarish Mahendrakar   int e = parser->GetLine(&line);
890*103e46e4SHarish Mahendrakar 
891*103e46e4SHarish Mahendrakar   if (e)  // error or EOS
892*103e46e4SHarish Mahendrakar     return false;
893*103e46e4SHarish Mahendrakar 
894*103e46e4SHarish Mahendrakar   if (!line.empty()) {
895*103e46e4SHarish Mahendrakar     if (fputc(' ', f) < 0)
896*103e46e4SHarish Mahendrakar       return false;
897*103e46e4SHarish Mahendrakar 
898*103e46e4SHarish Mahendrakar     if (fputs(line.c_str(), f) < 0)
899*103e46e4SHarish Mahendrakar       return false;
900*103e46e4SHarish Mahendrakar   }
901*103e46e4SHarish Mahendrakar 
902*103e46e4SHarish Mahendrakar   if (fputc('\n', f) < 0)
903*103e46e4SHarish Mahendrakar     return false;
904*103e46e4SHarish Mahendrakar 
905*103e46e4SHarish Mahendrakar   return true;
906*103e46e4SHarish Mahendrakar }
907*103e46e4SHarish Mahendrakar 
WriteCueTime(FILE * f,mkvtime_t time_ns)908*103e46e4SHarish Mahendrakar bool vttdemux::WriteCueTime(FILE* f, mkvtime_t time_ns) {
909*103e46e4SHarish Mahendrakar   mkvtime_t ms = time_ns / 1000000;  // WebVTT time has millisecond resolution
910*103e46e4SHarish Mahendrakar 
911*103e46e4SHarish Mahendrakar   mkvtime_t sec = ms / 1000;
912*103e46e4SHarish Mahendrakar   ms -= sec * 1000;
913*103e46e4SHarish Mahendrakar 
914*103e46e4SHarish Mahendrakar   mkvtime_t min = sec / 60;
915*103e46e4SHarish Mahendrakar   sec -= 60 * min;
916*103e46e4SHarish Mahendrakar 
917*103e46e4SHarish Mahendrakar   mkvtime_t hr = min / 60;
918*103e46e4SHarish Mahendrakar   min -= 60 * hr;
919*103e46e4SHarish Mahendrakar 
920*103e46e4SHarish Mahendrakar   if (hr > 0) {
921*103e46e4SHarish Mahendrakar     if (fprintf(f, "%02lld:", hr) < 0)
922*103e46e4SHarish Mahendrakar       return false;
923*103e46e4SHarish Mahendrakar   }
924*103e46e4SHarish Mahendrakar 
925*103e46e4SHarish Mahendrakar   if (fprintf(f, "%02lld:%02lld.%03lld", min, sec, ms) < 0)
926*103e46e4SHarish Mahendrakar     return false;
927*103e46e4SHarish Mahendrakar 
928*103e46e4SHarish Mahendrakar   return true;
929*103e46e4SHarish Mahendrakar }
930*103e46e4SHarish Mahendrakar 
WriteCuePayload(FILE * f,FrameParser * parser)931*103e46e4SHarish Mahendrakar bool vttdemux::WriteCuePayload(FILE* f, FrameParser* parser) {
932*103e46e4SHarish Mahendrakar   int count = 0;  // count of lines of payload text written to output file
933*103e46e4SHarish Mahendrakar   for (string line;;) {
934*103e46e4SHarish Mahendrakar     const int e = parser->GetLine(&line);
935*103e46e4SHarish Mahendrakar 
936*103e46e4SHarish Mahendrakar     if (e < 0)  // error (only -- we allow EOS here)
937*103e46e4SHarish Mahendrakar       return false;
938*103e46e4SHarish Mahendrakar 
939*103e46e4SHarish Mahendrakar     if (line.empty())  // TODO(matthewjheaney): retain this check?
940*103e46e4SHarish Mahendrakar       break;
941*103e46e4SHarish Mahendrakar 
942*103e46e4SHarish Mahendrakar     if (fprintf(f, "%s\n", line.c_str()) < 0)
943*103e46e4SHarish Mahendrakar       return false;
944*103e46e4SHarish Mahendrakar 
945*103e46e4SHarish Mahendrakar     ++count;
946*103e46e4SHarish Mahendrakar   }
947*103e46e4SHarish Mahendrakar 
948*103e46e4SHarish Mahendrakar   if (count <= 0)  // WebVTT cue requires non-empty payload
949*103e46e4SHarish Mahendrakar     return false;
950*103e46e4SHarish Mahendrakar 
951*103e46e4SHarish Mahendrakar   return true;
952*103e46e4SHarish Mahendrakar }
953*103e46e4SHarish Mahendrakar 
954*103e46e4SHarish Mahendrakar }  // namespace libwebm
955*103e46e4SHarish Mahendrakar 
main(int argc,const char * argv[])956*103e46e4SHarish Mahendrakar int main(int argc, const char* argv[]) {
957*103e46e4SHarish Mahendrakar   if (argc != 2) {
958*103e46e4SHarish Mahendrakar     printf("usage: vttdemux <webmfile>\n");
959*103e46e4SHarish Mahendrakar     return EXIT_SUCCESS;
960*103e46e4SHarish Mahendrakar   }
961*103e46e4SHarish Mahendrakar 
962*103e46e4SHarish Mahendrakar   const char* const filename = argv[1];
963*103e46e4SHarish Mahendrakar   mkvparser::MkvReader reader;
964*103e46e4SHarish Mahendrakar 
965*103e46e4SHarish Mahendrakar   int e = reader.Open(filename);
966*103e46e4SHarish Mahendrakar 
967*103e46e4SHarish Mahendrakar   if (e) {  // error
968*103e46e4SHarish Mahendrakar     printf("unable to open file\n");
969*103e46e4SHarish Mahendrakar     return EXIT_FAILURE;
970*103e46e4SHarish Mahendrakar   }
971*103e46e4SHarish Mahendrakar 
972*103e46e4SHarish Mahendrakar   libwebm::vttdemux::mkvpos_t pos;
973*103e46e4SHarish Mahendrakar 
974*103e46e4SHarish Mahendrakar   if (!libwebm::vttdemux::ParseHeader(&reader, &pos))
975*103e46e4SHarish Mahendrakar     return EXIT_FAILURE;
976*103e46e4SHarish Mahendrakar 
977*103e46e4SHarish Mahendrakar   libwebm::vttdemux::segment_ptr_t segment_ptr;
978*103e46e4SHarish Mahendrakar 
979*103e46e4SHarish Mahendrakar   if (!libwebm::vttdemux::ParseSegment(&reader, pos, &segment_ptr))
980*103e46e4SHarish Mahendrakar     return EXIT_FAILURE;
981*103e46e4SHarish Mahendrakar 
982*103e46e4SHarish Mahendrakar   libwebm::vttdemux::metadata_map_t metadata_map;
983*103e46e4SHarish Mahendrakar 
984*103e46e4SHarish Mahendrakar   BuildMap(segment_ptr.get(), &metadata_map);
985*103e46e4SHarish Mahendrakar 
986*103e46e4SHarish Mahendrakar   if (metadata_map.empty()) {
987*103e46e4SHarish Mahendrakar     printf("no WebVTT metadata found\n");
988*103e46e4SHarish Mahendrakar     return EXIT_FAILURE;
989*103e46e4SHarish Mahendrakar   }
990*103e46e4SHarish Mahendrakar 
991*103e46e4SHarish Mahendrakar   if (!OpenFiles(&metadata_map, filename)) {
992*103e46e4SHarish Mahendrakar     CloseFiles(&metadata_map);  // nothing to flush, so not strictly necessary
993*103e46e4SHarish Mahendrakar     return EXIT_FAILURE;
994*103e46e4SHarish Mahendrakar   }
995*103e46e4SHarish Mahendrakar 
996*103e46e4SHarish Mahendrakar   if (!WriteFiles(metadata_map, segment_ptr.get())) {
997*103e46e4SHarish Mahendrakar     CloseFiles(&metadata_map);  // might as well flush what we do have
998*103e46e4SHarish Mahendrakar     return EXIT_FAILURE;
999*103e46e4SHarish Mahendrakar   }
1000*103e46e4SHarish Mahendrakar 
1001*103e46e4SHarish Mahendrakar   CloseFiles(&metadata_map);
1002*103e46e4SHarish Mahendrakar 
1003*103e46e4SHarish Mahendrakar   return EXIT_SUCCESS;
1004*103e46e4SHarish Mahendrakar }
1005