clarify that software double buffering is for AGG only.
[gnash.git] / libmedia / MediaParser.cpp
blob94804fa0faaa91f5abaa54b6c5fa0e6d8b2511a9
1 // MediaParser.cpp: Media file parser, for Gnash.
2 //
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "MediaParser.h"
23 #include <boost/bind.hpp>
25 #include "log.h"
26 #include "GnashSleep.h" // for usleep.
27 #include "Id3Info.h"
29 // Define this to get debugging output from MediaParser
30 //#define GNASH_DEBUG_MEDIAPARSER
32 namespace gnash {
33 namespace media {
35 MediaParser::MediaParser(std::auto_ptr<IOChannel> stream)
37 _parsingComplete(false),
38 _bytesLoaded(0),
39 _stream(stream),
40 _bufferTime(100), // 100 ms
41 _parserThread(0),
42 _parserThreadStartBarrier(2),
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.reset(new boost::thread(
55 boost::bind(parserLoopStarter, this)));
56 _parserThreadStartBarrier.wait();
57 #endif
60 boost::uint64_t
61 MediaParser::getBufferLength() const
63 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
64 boost::mutex::scoped_lock lock(_qMutex);
65 #endif
66 return getBufferLengthNoLock();
69 /* public */
70 bool
71 MediaParser::isBufferEmpty() const
73 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
74 boost::mutex::scoped_lock lock(_qMutex);
75 #endif
76 return _videoFrames.empty() && _audioFrames.empty();
79 boost::optional<Id3Info>
80 MediaParser::getId3Info() const
82 log_error("No ID3 support implemented in this MediaParser");
83 return boost::optional<Id3Info>();
86 boost::uint64_t
87 MediaParser::getBufferLengthNoLock() const
89 bool hasVideo = _videoInfo.get();
90 bool hasAudio = _audioInfo.get();
92 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
94 if (hasVideo && hasAudio) {
95 return std::min(audioBufferLength(), videoBufferLength());
98 if (hasVideo) return videoBufferLength();
100 if (hasAudio) return audioBufferLength();
102 return 0;
105 boost::uint64_t
106 MediaParser::videoBufferLength() const
108 if (_videoFrames.empty()) return 0;
109 #if 0 // debugging
110 log_debug("videoBufferLength: %d - %d == %d",
111 _videoFrames.back()->timestamp(), _videoFrames.front()->timestamp(),
112 _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp());
113 #endif
114 return _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp();
117 boost::uint64_t
118 MediaParser::audioBufferLength() const
120 if (_audioFrames.empty()) return 0;
121 #if 0 // debugging
122 log_debug("audioBufferLength: %d - %d == %d",
123 _audioFrames.back()->timestamp, _audioFrames.front()->timestamp,
124 _audioFrames.back()->timestamp - _audioFrames.front()->timestamp);
125 #endif
126 return _audioFrames.back()->timestamp - _audioFrames.front()->timestamp;
129 /*private*/
130 const EncodedVideoFrame*
131 MediaParser::peekNextVideoFrame() const
133 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
134 // TODO: assert _qMutex is locked by this thread
135 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
136 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
138 const_cast<MediaParser*>(this)->parseNextChunk();
140 #endif
142 if (!_videoInfo.get() || _videoFrames.empty()) return 0;
143 return _videoFrames.front();
146 bool
147 MediaParser::nextFrameTimestamp(boost::uint64_t& ts) const
149 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
150 boost::mutex::scoped_lock lock(_qMutex);
151 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
152 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
154 const_cast<MediaParser*>(this)->parseNextChunk();
156 #endif
158 if (_videoFrames.empty())
160 if (_audioFrames.empty())
162 return false;
164 else
166 ts = _audioFrames.front()->timestamp;
167 return true;
170 else
172 if (_audioFrames.empty())
174 ts = _videoFrames.front()->timestamp();
175 return true;
177 else
179 ts = std::min(_videoFrames.front()->timestamp(),
180 _audioFrames.front()->timestamp);
181 return true;
186 bool
187 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts) const
189 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
190 boost::mutex::scoped_lock lock(_qMutex);
191 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
192 const EncodedVideoFrame* ef = peekNextVideoFrame();
193 if ( ! ef ) return false;
194 ts = ef->timestamp();
195 return true;
198 std::auto_ptr<EncodedVideoFrame>
199 MediaParser::nextVideoFrame()
201 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
202 boost::mutex::scoped_lock lock(_qMutex);
203 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
204 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
206 const_cast<MediaParser*>(this)->parseNextChunk();
208 #endif
210 std::auto_ptr<EncodedVideoFrame> ret;
211 if (_videoFrames.empty()) return ret;
212 ret.reset(_videoFrames.front());
213 _videoFrames.pop_front();
214 #ifdef GNASH_DEBUG_MEDIAPARSER
215 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
216 #endif // GNASH_DEBUG_MEDIAPARSER
217 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
218 return ret;
221 std::auto_ptr<EncodedAudioFrame>
222 MediaParser::nextAudioFrame()
224 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
225 boost::mutex::scoped_lock lock(_qMutex);
226 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
227 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
229 const_cast<MediaParser*>(this)->parseNextChunk();
231 #endif
233 std::auto_ptr<EncodedAudioFrame> ret;
234 if (_audioFrames.empty()) return ret;
235 ret.reset(_audioFrames.front());
236 _audioFrames.pop_front();
237 #ifdef GNASH_DEBUG_MEDIAPARSER
238 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
239 #endif // GNASH_DEBUG_MEDIAPARSER
240 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
241 return ret;
244 bool
245 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts) const
247 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
248 boost::mutex::scoped_lock lock(_qMutex);
249 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
250 const EncodedAudioFrame* ef = peekNextAudioFrame();
251 if ( ! ef ) return false;
252 ts = ef->timestamp;
253 return true;
256 /*private*/
257 const EncodedAudioFrame*
258 MediaParser::peekNextAudioFrame() const
260 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
261 // TODO: assert _qMutex is locked by this thread
262 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
263 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
265 const_cast<MediaParser*>(this)->parseNextChunk();
267 #endif
268 if (!_audioInfo.get() || _audioFrames.empty()) return 0;
269 return _audioFrames.front();
272 void
273 MediaParser::stopParserThread()
275 if ( _parserThread.get() )
277 requestParserThreadKill();
278 _parserThread->join();
279 _parserThread.reset();
283 MediaParser::~MediaParser()
285 stopParserThread();
287 for (VideoFrames::iterator i=_videoFrames.begin(),
288 e=_videoFrames.end(); i!=e; ++i)
290 delete (*i);
293 for (AudioFrames::iterator i=_audioFrames.begin(),
294 e=_audioFrames.end(); i!=e; ++i)
296 delete (*i);
300 void
301 MediaParser::clearBuffers()
303 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
304 boost::mutex::scoped_lock lock(_qMutex);
305 #endif
307 for (VideoFrames::iterator i=_videoFrames.begin(),
308 e=_videoFrames.end(); i!=e; ++i)
310 delete (*i);
313 for (AudioFrames::iterator i=_audioFrames.begin(),
314 e=_audioFrames.end(); i!=e; ++i)
316 delete (*i);
319 _audioFrames.clear();
320 _videoFrames.clear();
322 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
325 void
326 MediaParser::pushEncodedAudioFrame(std::auto_ptr<EncodedAudioFrame> frame)
328 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
329 boost::mutex::scoped_lock lock(_qMutex);
330 #endif
332 // Find location to insert this new frame to, so that
333 // timestamps are sorted
335 AudioFrames::iterator loc = _audioFrames.end();
336 if ( ! _audioFrames.empty() ) {
337 size_t gap=0;
338 AudioFrames::reverse_iterator i=_audioFrames.rbegin();
339 for (AudioFrames::reverse_iterator e=_audioFrames.rend(); i!=e; ++i)
341 if ( (*i)->timestamp <= frame->timestamp ) break;
342 ++gap;
345 loc = i.base();
347 if ( gap ) {
348 log_debug("Timestamp of last %d/%d audio frames in queue "
349 "greater then timestamp in the frame being "
350 "inserted to it (%d).", gap, _audioFrames.size(),
351 frame->timestamp);
355 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
356 _audioFrames.insert(loc, frame.release());
358 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
359 // if the push reaches a "buffer full" condition, or if we find the parsing
360 // to be completed, wait to be waken up
361 waitIfNeeded(lock);
362 #endif
365 void
366 MediaParser::pushEncodedVideoFrame(std::auto_ptr<EncodedVideoFrame> frame)
368 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
369 boost::mutex::scoped_lock lock(_qMutex);
370 #endif
372 // Find location to insert this new frame to, so that
373 // timestamps are sorted
375 VideoFrames::iterator loc = _videoFrames.end();
376 if ( ! _videoFrames.empty() ) {
377 size_t gap=0;
378 VideoFrames::reverse_iterator i=_videoFrames.rbegin();
379 for (VideoFrames::reverse_iterator e=_videoFrames.rend(); i!=e; ++i)
381 if ( (*i)->timestamp() <= frame->timestamp() ) break;
382 ++gap;
385 loc = i.base();
387 if ( gap ) {
388 log_debug("Timestamp of last %d/%d video frames in queue "
389 "greater then timestamp() in the frame being "
390 "inserted to it (%d).", gap, _videoFrames.size(),
391 frame->timestamp());
395 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
396 _videoFrames.insert(loc, frame.release());
398 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
399 waitIfNeeded(lock); // if the push reaches a "buffer full" condition, wait to be waken up
400 #endif
403 void
404 MediaParser::waitIfNeeded(boost::mutex::scoped_lock& lock)
406 // We hold a lock on the queue here...
407 bool pc=parsingCompleted();
408 bool ic=indexingCompleted();
409 bool bf=bufferFull();
410 if (( pc || (bf && ic)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
412 #ifdef GNASH_DEBUG_MEDIAPARSER
413 log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc, bf);
414 #endif // GNASH_DEBUG_MEDIAPARSER
415 _parserThreadWakeup.wait(lock);
416 #ifdef GNASH_DEBUG_MEDIAPARSER
417 log_debug("Parser thread finished waiting on wakeup lock");
418 #endif // GNASH_DEBUG_MEDIAPARSER
422 bool
423 MediaParser::bufferFull() const
425 // Callers are expected to hold a lock on _qMutex
426 int bl = getBufferLengthNoLock();
427 int bt = getBufferTime();
428 #ifdef GNASH_DEBUG_MEDIAPARSER
429 log_debug("MediaParser::bufferFull: %d/%d", bl, bt);
430 #endif // GNASH_DEBUG_MEDIAPARSER
431 return bl > bt;
434 void
435 MediaParser::parserLoop()
437 _parserThreadStartBarrier.wait();
438 while (!parserThreadKillRequested())
440 parseNextChunk();
441 gnashSleep(100); // thread switch
443 // check for parsing complete
444 // TODO: have a setParsingComplete() function
445 // exposed in base class for taking care
446 // of this on appropriate time.
447 boost::mutex::scoped_lock lock(_qMutex);
448 waitIfNeeded(lock);
453 void
454 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, boost::uint64_t /*ts*/)
459 std::ostream&
460 operator<< (std::ostream& os, const VideoInfo& vi)
462 os << "codec:" << vi.codec << " (type " << vi.type << ") - "
463 << "size:" << vi.width << "x" << vi.height << " - "
464 << "frameRate:" << vi.frameRate << " - "
465 << "duration:" << vi.duration;
466 return os;
469 std::ostream&
470 operator<< (std::ostream& os, const videoCodecType& t)
472 switch (t)
474 case VIDEO_CODEC_H263:
475 os << "H263";
476 break;
477 case VIDEO_CODEC_SCREENVIDEO:
478 os << "Screenvideo";
479 break;
480 case VIDEO_CODEC_VP6:
481 os << "VP6";
482 break;
483 case VIDEO_CODEC_VP6A:
484 os << "VP6A";
485 break;
486 case VIDEO_CODEC_SCREENVIDEO2:
487 os << "Screenvideo2";
488 break;
489 case VIDEO_CODEC_H264:
490 os << "H264";
491 break;
492 default:
493 os << "unknown/invalid codec " << static_cast<int>(t);
494 break;
496 return os;
499 std::ostream&
500 operator<< (std::ostream& os, const audioCodecType& t)
502 switch (t)
504 case AUDIO_CODEC_RAW:
505 os << "Raw";
506 break;
507 case AUDIO_CODEC_ADPCM:
508 os << "ADPCM";
509 break;
510 case AUDIO_CODEC_MP3:
511 os << "MP3";
512 break;
513 case AUDIO_CODEC_UNCOMPRESSED:
514 os << "Uncompressed";
515 break;
516 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO:
517 os << "Nellymoser 8Hz mono";
518 break;
519 case AUDIO_CODEC_NELLYMOSER:
520 os << "Nellymoser";
521 break;
522 case AUDIO_CODEC_AAC:
523 os << "Advanced Audio Coding";
524 break;
525 case AUDIO_CODEC_SPEEX:
526 os << "Speex";
527 break;
528 default:
529 os << "unknown/invalid codec " << static_cast<int>(t);
530 break;
532 return os;
535 } // end of gnash::media namespace
536 } // end of gnash namespace
538 #undef PADDING_BYTES
539 #undef READ_CHUNKS