Update with current status
[gnash.git] / libmedia / MediaParser.cpp
blob8f40fb9a3fdfcd928de58d5d430b9d2cb04ff614
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.
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.
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
22 #include "MediaParser.h"
24 #include <functional>
25 #include <thread>
27 #include "log.h"
28 #include "GnashSleep.h" // for usleep.
29 #include "Id3Info.h"
31 // Define this to get debugging output from MediaParser
32 //#define GNASH_DEBUG_MEDIAPARSER
34 namespace gnash {
35 namespace media {
37 MediaParser::MediaParser(std::unique_ptr<IOChannel> stream)
39 _parsingComplete(false),
40 _bytesLoaded(0),
41 _stream(std::move(stream)),
42 _bufferTime(100), // 100 ms
43 _parserThreadKillRequested(false),
44 _seekRequest(false)
48 /*protected*/
49 void
50 MediaParser::startParserThread()
52 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
53 log_debug("Starting MediaParser thread");
54 _parserThread = std::thread(&MediaParser::parserLoop, this);
55 #endif
58 std::uint64_t
59 MediaParser::getBufferLength() const
61 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
62 std::lock_guard<std::mutex> lock(_qMutex);
63 #endif
64 return getBufferLengthNoLock();
67 /* public */
68 bool
69 MediaParser::isBufferEmpty() const
71 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
72 std::lock_guard<std::mutex> lock(_qMutex);
73 #endif
74 return _videoFrames.empty() && _audioFrames.empty();
77 boost::optional<Id3Info>
78 MediaParser::getId3Info() const
80 log_error(_("No ID3 support implemented in this MediaParser"));
81 return boost::optional<Id3Info>();
84 std::uint64_t
85 MediaParser::getBufferLengthNoLock() const
87 bool hasVideo = _videoInfo.get();
88 bool hasAudio = _audioInfo.get();
90 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
92 if (hasVideo && hasAudio) {
93 return std::min(audioBufferLength(), videoBufferLength());
96 if (hasVideo) return videoBufferLength();
98 if (hasAudio) return audioBufferLength();
100 return 0;
103 std::uint64_t
104 MediaParser::videoBufferLength() const
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();
115 std::uint64_t
116 MediaParser::audioBufferLength() const
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;
127 /*private*/
128 const EncodedVideoFrame*
129 MediaParser::peekNextVideoFrame() const
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())
136 const_cast<MediaParser*>(this)->parseNextChunk();
138 #endif
140 if (!_videoInfo.get() || _videoFrames.empty()) return nullptr;
141 return _videoFrames.front().get();
144 bool
145 MediaParser::nextFrameTimestamp(std::uint64_t& ts) const
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())
152 const_cast<MediaParser*>(this)->parseNextChunk();
154 #endif
156 if (_videoFrames.empty())
158 if (_audioFrames.empty())
160 return false;
162 else
164 ts = _audioFrames.front()->timestamp;
165 return true;
168 else
170 if (_audioFrames.empty())
172 ts = _videoFrames.front()->timestamp();
173 return true;
175 else
177 ts = std::min(_videoFrames.front()->timestamp(),
178 _audioFrames.front()->timestamp);
179 return true;
184 bool
185 MediaParser::nextVideoFrameTimestamp(std::uint64_t& ts) const
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;
196 std::unique_ptr<EncodedVideoFrame>
197 MediaParser::nextVideoFrame()
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())
204 const_cast<MediaParser*>(this)->parseNextChunk();
206 #endif
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;
219 std::unique_ptr<EncodedAudioFrame>
220 MediaParser::nextAudioFrame()
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())
227 const_cast<MediaParser*>(this)->parseNextChunk();
229 #endif
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;
242 bool
243 MediaParser::nextAudioFrameTimestamp(std::uint64_t& ts) const
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;
254 /*private*/
255 const EncodedAudioFrame*
256 MediaParser::peekNextAudioFrame() const
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())
263 const_cast<MediaParser*>(this)->parseNextChunk();
265 #endif
266 if (!_audioInfo.get() || _audioFrames.empty()) return nullptr;
267 return _audioFrames.front().get();
270 void
271 MediaParser::stopParserThread()
273 if ( _parserThread.joinable() )
275 requestParserThreadKill();
276 _parserThread.join();
280 MediaParser::~MediaParser()
282 stopParserThread();
285 void
286 MediaParser::clearBuffers()
288 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
289 std::lock_guard<std::mutex> lock(_qMutex);
290 #endif
292 _audioFrames.clear();
293 _videoFrames.clear();
295 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
298 void
299 MediaParser::pushEncodedAudioFrame(std::unique_ptr<EncodedAudioFrame> frame)
301 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
302 std::unique_lock<std::mutex> lock(_qMutex);
303 #endif
305 // Find location to insert this new frame to, so that
306 // timestamps are sorted
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)
314 if ( (*i)->timestamp <= frame->timestamp ) break;
315 ++gap;
318 loc = i.base();
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);
328 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
329 _audioFrames.insert(loc, std::move(frame));
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
338 void
339 MediaParser::pushEncodedVideoFrame(std::unique_ptr<EncodedVideoFrame> frame)
341 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
342 std::unique_lock<std::mutex> lock(_qMutex);
343 #endif
345 // Find location to insert this new frame to, so that
346 // timestamps are sorted
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)
354 if ( (*i)->timestamp() <= frame->timestamp() ) break;
355 ++gap;
358 loc = i.base();
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());
368 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
369 _videoFrames.insert(loc, std::move(frame));
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
376 void
377 MediaParser::waitIfNeeded(std::unique_lock<std::mutex>& lock)
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 ?
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
395 bool
396 MediaParser::bufferFull() const
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;
407 void
408 MediaParser::parserLoop()
410 while (!parserThreadKillRequested())
412 parseNextChunk();
413 gnashSleep(100); // thread switch
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);
425 void
426 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, std::uint64_t /*ts*/)
431 std::ostream&
432 operator<< (std::ostream& os, const VideoInfo& vi)
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;
441 std::ostream&
442 operator<< (std::ostream& os, const videoCodecType& t)
444 switch (t)
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;
468 return os;
471 } // end of gnash::media namespace
472 } // end of gnash namespace
474 #undef PADDING_BYTES
475 #undef READ_CHUNKS