1 // MediaParser.cpp: Media file parser, for Gnash.
2 //
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016
4 // Free Software Foundation, Inc.
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20
21
22 #include "MediaParser.h"
23
24 #include <functional>
25 #include <thread>
26
27 #include "log.h"
28 #include "GnashSleep.h" // for usleep.
29 #include "Id3Info.h"
30
31 // Define this to get debugging output from MediaParser
32 //#define GNASH_DEBUG_MEDIAPARSER
33
34 namespace gnash {
35 namespace media {
36
MediaParser(std::unique_ptr<IOChannel> stream)37 MediaParser::MediaParser(std::unique_ptr<IOChannel> stream)
38 :
39 _parsingComplete(false),
40 _bytesLoaded(0),
41 _stream(std::move(stream)),
42 _bufferTime(100), // 100 ms
43 _parserThreadKillRequested(false),
44 _seekRequest(false)
45 {
46 }
47
48 /*protected*/
49 void
startParserThread()50 MediaParser::startParserThread()
51 {
52 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
53 log_debug("Starting MediaParser thread");
54 _parserThread = std::thread(&MediaParser::parserLoop, this);
55 #endif
56 }
57
58 std::uint64_t
getBufferLength() const59 MediaParser::getBufferLength() const
60 {
61 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
62 std::lock_guard<std::mutex> lock(_qMutex);
63 #endif
64 return getBufferLengthNoLock();
65 }
66
67 /* public */
68 bool
isBufferEmpty() const69 MediaParser::isBufferEmpty() const
70 {
71 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
72 std::lock_guard<std::mutex> lock(_qMutex);
73 #endif
74 return _videoFrames.empty() && _audioFrames.empty();
75 }
76
77 boost::optional<Id3Info>
getId3Info() const78 MediaParser::getId3Info() const
79 {
80 log_error(_("No ID3 support implemented in this MediaParser"));
81 return boost::optional<Id3Info>();
82 }
83
84 std::uint64_t
getBufferLengthNoLock() const85 MediaParser::getBufferLengthNoLock() const
86 {
87 bool hasVideo = _videoInfo.get();
88 bool hasAudio = _audioInfo.get();
89
90 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
91
92 if (hasVideo && hasAudio) {
93 return std::min(audioBufferLength(), videoBufferLength());
94 }
95
96 if (hasVideo) return videoBufferLength();
97
98 if (hasAudio) return audioBufferLength();
99
100 return 0;
101 }
102
103 std::uint64_t
videoBufferLength() const104 MediaParser::videoBufferLength() const
105 {
106 if (_videoFrames.empty()) return 0;
107 #if 0 // debugging
108 log_debug("videoBufferLength: %d - %d == %d",
109 _videoFrames.back()->timestamp(), _videoFrames.front()->timestamp(),
110 _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp());
111 #endif
112 return _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp();
113 }
114
115 std::uint64_t
audioBufferLength() const116 MediaParser::audioBufferLength() const
117 {
118 if (_audioFrames.empty()) return 0;
119 #if 0 // debugging
120 log_debug("audioBufferLength: %d - %d == %d",
121 _audioFrames.back()->timestamp, _audioFrames.front()->timestamp,
122 _audioFrames.back()->timestamp - _audioFrames.front()->timestamp);
123 #endif
124 return _audioFrames.back()->timestamp - _audioFrames.front()->timestamp;
125 }
126
127 /*private*/
128 const EncodedVideoFrame*
peekNextVideoFrame() const129 MediaParser::peekNextVideoFrame() const
130 {
131 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
132 // TODO: assert _qMutex is locked by this thread
133 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
134 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
135 {
136 const_cast<MediaParser*>(this)->parseNextChunk();
137 }
138 #endif
139
140 if (!_videoInfo.get() || _videoFrames.empty()) return nullptr;
141 return _videoFrames.front().get();
142 }
143
144 bool
nextFrameTimestamp(std::uint64_t & ts) const145 MediaParser::nextFrameTimestamp(std::uint64_t& ts) const
146 {
147 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
148 std::lock_guard<std::mutex> lock(_qMutex);
149 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
150 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
151 {
152 const_cast<MediaParser*>(this)->parseNextChunk();
153 }
154 #endif
155
156 if (_videoFrames.empty())
157 {
158 if (_audioFrames.empty())
159 {
160 return false;
161 }
162 else
163 {
164 ts = _audioFrames.front()->timestamp;
165 return true;
166 }
167 }
168 else
169 {
170 if (_audioFrames.empty())
171 {
172 ts = _videoFrames.front()->timestamp();
173 return true;
174 }
175 else
176 {
177 ts = std::min(_videoFrames.front()->timestamp(),
178 _audioFrames.front()->timestamp);
179 return true;
180 }
181 }
182 }
183
184 bool
nextVideoFrameTimestamp(std::uint64_t & ts) const185 MediaParser::nextVideoFrameTimestamp(std::uint64_t& ts) const
186 {
187 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
188 std::lock_guard<std::mutex> lock(_qMutex);
189 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
190 const EncodedVideoFrame* ef = peekNextVideoFrame();
191 if ( ! ef ) return false;
192 ts = ef->timestamp();
193 return true;
194 }
195
196 std::unique_ptr<EncodedVideoFrame>
nextVideoFrame()197 MediaParser::nextVideoFrame()
198 {
199 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
200 std::lock_guard<std::mutex> lock(_qMutex);
201 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
202 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
203 {
204 const_cast<MediaParser*>(this)->parseNextChunk();
205 }
206 #endif
207
208 std::unique_ptr<EncodedVideoFrame> ret;
209 if (_videoFrames.empty()) return ret;
210 ret = std::move(_videoFrames.front());
211 _videoFrames.pop_front();
212 #ifdef GNASH_DEBUG_MEDIAPARSER
213 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
214 #endif // GNASH_DEBUG_MEDIAPARSER
215 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
216 return ret;
217 }
218
219 std::unique_ptr<EncodedAudioFrame>
nextAudioFrame()220 MediaParser::nextAudioFrame()
221 {
222 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
223 std::lock_guard<std::mutex> lock(_qMutex);
224 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
225 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
226 {
227 const_cast<MediaParser*>(this)->parseNextChunk();
228 }
229 #endif
230
231 std::unique_ptr<EncodedAudioFrame> ret;
232 if (_audioFrames.empty()) return ret;
233 ret = std::move(_audioFrames.front());
234 _audioFrames.pop_front();
235 #ifdef GNASH_DEBUG_MEDIAPARSER
236 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
237 #endif // GNASH_DEBUG_MEDIAPARSER
238 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
239 return ret;
240 }
241
242 bool
nextAudioFrameTimestamp(std::uint64_t & ts) const243 MediaParser::nextAudioFrameTimestamp(std::uint64_t& ts) const
244 {
245 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
246 std::lock_guard<std::mutex> lock(_qMutex);
247 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
248 const EncodedAudioFrame* ef = peekNextAudioFrame();
249 if ( ! ef ) return false;
250 ts = ef->timestamp;
251 return true;
252 }
253
254 /*private*/
255 const EncodedAudioFrame*
peekNextAudioFrame() const256 MediaParser::peekNextAudioFrame() const
257 {
258 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
259 // TODO: assert _qMutex is locked by this thread
260 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
261 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
262 {
263 const_cast<MediaParser*>(this)->parseNextChunk();
264 }
265 #endif
266 if (!_audioInfo.get() || _audioFrames.empty()) return nullptr;
267 return _audioFrames.front().get();
268 }
269
270 void
stopParserThread()271 MediaParser::stopParserThread()
272 {
273 if ( _parserThread.joinable() )
274 {
275 requestParserThreadKill();
276 _parserThread.join();
277 }
278 }
279
~MediaParser()280 MediaParser::~MediaParser()
281 {
282 stopParserThread();
283 }
284
285 void
clearBuffers()286 MediaParser::clearBuffers()
287 {
288 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
289 std::lock_guard<std::mutex> lock(_qMutex);
290 #endif
291
292 _audioFrames.clear();
293 _videoFrames.clear();
294
295 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
296 }
297
298 void
pushEncodedAudioFrame(std::unique_ptr<EncodedAudioFrame> frame)299 MediaParser::pushEncodedAudioFrame(std::unique_ptr<EncodedAudioFrame> frame)
300 {
301 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
302 std::unique_lock<std::mutex> lock(_qMutex);
303 #endif
304
305 // Find location to insert this new frame to, so that
306 // timestamps are sorted
307 //
308 AudioFrames::iterator loc = _audioFrames.end();
309 if ( ! _audioFrames.empty() ) {
310 size_t gap=0;
311 AudioFrames::reverse_iterator i=_audioFrames.rbegin();
312 for (AudioFrames::reverse_iterator e=_audioFrames.rend(); i!=e; ++i)
313 {
314 if ( (*i)->timestamp <= frame->timestamp ) break;
315 ++gap;
316 }
317
318 loc = i.base();
319
320 if ( gap ) {
321 log_debug("Timestamp of last %d/%d audio frames in queue "
322 "greater then timestamp in the frame being "
323 "inserted to it (%d).", gap, _audioFrames.size(),
324 frame->timestamp);
325 }
326 }
327
328 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
329 _audioFrames.insert(loc, std::move(frame));
330
331 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
332 // if the push reaches a "buffer full" condition, or if we find the parsing
333 // to be completed, wait to be waken up
334 waitIfNeeded(lock);
335 #endif
336 }
337
338 void
pushEncodedVideoFrame(std::unique_ptr<EncodedVideoFrame> frame)339 MediaParser::pushEncodedVideoFrame(std::unique_ptr<EncodedVideoFrame> frame)
340 {
341 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
342 std::unique_lock<std::mutex> lock(_qMutex);
343 #endif
344
345 // Find location to insert this new frame to, so that
346 // timestamps are sorted
347 //
348 VideoFrames::iterator loc = _videoFrames.end();
349 if ( ! _videoFrames.empty() ) {
350 size_t gap=0;
351 VideoFrames::reverse_iterator i=_videoFrames.rbegin();
352 for (VideoFrames::reverse_iterator e=_videoFrames.rend(); i!=e; ++i)
353 {
354 if ( (*i)->timestamp() <= frame->timestamp() ) break;
355 ++gap;
356 }
357
358 loc = i.base();
359
360 if ( gap ) {
361 log_debug("Timestamp of last %d/%d video frames in queue "
362 "greater then timestamp() in the frame being "
363 "inserted to it (%d).", gap, _videoFrames.size(),
364 frame->timestamp());
365 }
366 }
367
368 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
369 _videoFrames.insert(loc, std::move(frame));
370
371 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
372 waitIfNeeded(lock); // if the push reaches a "buffer full" condition, wait to be waken up
373 #endif
374 }
375
376 void
waitIfNeeded(std::unique_lock<std::mutex> & lock)377 MediaParser::waitIfNeeded(std::unique_lock<std::mutex>& lock)
378 {
379 // We hold a lock on the queue here...
380 bool pc=parsingCompleted();
381 bool ic=indexingCompleted();
382 bool bf=bufferFull();
383 if (( pc || (bf && ic)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
384 {
385 #ifdef GNASH_DEBUG_MEDIAPARSER
386 log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc, bf);
387 #endif // GNASH_DEBUG_MEDIAPARSER
388 _parserThreadWakeup.wait(lock);
389 #ifdef GNASH_DEBUG_MEDIAPARSER
390 log_debug("Parser thread finished waiting on wakeup lock");
391 #endif // GNASH_DEBUG_MEDIAPARSER
392 }
393 }
394
395 bool
bufferFull() const396 MediaParser::bufferFull() const
397 {
398 // Callers are expected to hold a lock on _qMutex
399 std::uint64_t bl = getBufferLengthNoLock();
400 std::uint64_t bt = getBufferTime();
401 #ifdef GNASH_DEBUG_MEDIAPARSER
402 log_debug("MediaParser::bufferFull: %d/%d", bl, bt);
403 #endif // GNASH_DEBUG_MEDIAPARSER
404 return bl > bt;
405 }
406
407 void
parserLoop()408 MediaParser::parserLoop()
409 {
410 while (!parserThreadKillRequested())
411 {
412 parseNextChunk();
413 gnashSleep(100); // thread switch
414
415 // check for parsing complete
416 // TODO: have a setParsingComplete() function
417 // exposed in base class for taking care
418 // of this on appropriate time.
419 std::unique_lock<std::mutex> lock(_qMutex);
420 waitIfNeeded(lock);
421 }
422 }
423
424
425 void
fetchMetaTags(OrderedMetaTags &,std::uint64_t)426 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, std::uint64_t /*ts*/)
427 {
428 }
429
430
431 std::ostream&
operator <<(std::ostream & os,const VideoInfo & vi)432 operator<< (std::ostream& os, const VideoInfo& vi)
433 {
434 os << "codec:" << vi.codec << " (type " << vi.type << ") - "
435 << "size:" << vi.width << "x" << vi.height << " - "
436 << "frameRate:" << vi.frameRate << " - "
437 << "duration:" << vi.duration;
438 return os;
439 }
440
441 std::ostream&
operator <<(std::ostream & os,const videoCodecType & t)442 operator<< (std::ostream& os, const videoCodecType& t)
443 {
444 switch (t)
445 {
446 case VIDEO_CODEC_H263:
447 os << "H263";
448 break;
449 case VIDEO_CODEC_SCREENVIDEO:
450 os << "Screenvideo";
451 break;
452 case VIDEO_CODEC_VP6:
453 os << "VP6";
454 break;
455 case VIDEO_CODEC_VP6A:
456 os << "VP6A";
457 break;
458 case VIDEO_CODEC_SCREENVIDEO2:
459 os << "Screenvideo2";
460 break;
461 case VIDEO_CODEC_H264:
462 os << "H264";
463 break;
464 default:
465 os << "unknown/invalid codec " << static_cast<int>(t);
466 break;
467 }
468 return os;
469 }
470
471 } // end of gnash::media namespace
472 } // end of gnash namespace
473
474 #undef PADDING_BYTES
475 #undef READ_CHUNKS
476