reduce verbosity
[gnash.git] / libmedia / MediaParser.cpp
blob74bbd9aa1a88b67faa301dcce34e45840ae9a13a
1 // MediaParser.cpp: Media file parser, for Gnash.
2 //
3 // Copyright (C) 2007, 2008, 2009, 2010 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"
22 #include "log.h"
23 #include "GnashSleep.h" // for usleep.
25 #include <boost/bind.hpp>
27 // Define this to get debugging output from MediaParser
28 //#define GNASH_DEBUG_MEDIAPARSER
30 namespace gnash {
31 namespace media {
33 MediaParser::MediaParser(std::auto_ptr<IOChannel> stream)
35 _parsingComplete(false),
36 _bytesLoaded(0),
37 _stream(stream),
38 _bufferTime(100), // 100 ms
39 _parserThread(0),
40 _parserThreadStartBarrier(2),
41 _parserThreadKillRequested(false),
42 _seekRequest(false)
46 /*protected*/
47 void
48 MediaParser::startParserThread()
50 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
51 log_debug("Starting MediaParser thread");
52 _parserThread.reset(new boost::thread(
53 boost::bind(parserLoopStarter, this)));
54 _parserThreadStartBarrier.wait();
55 #endif
58 boost::uint64_t
59 MediaParser::getBufferLength() const
61 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
62 boost::mutex::scoped_lock lock(_qMutex);
63 #endif
64 return getBufferLengthNoLock();
67 boost::uint64_t
68 MediaParser::getBufferLengthNoLock() const
70 bool hasVideo = _videoInfo.get();
71 bool hasAudio = _audioInfo.get();
73 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
75 if (hasVideo && hasAudio) {
76 return std::min(audioBufferLength(), videoBufferLength());
79 if (hasVideo) return videoBufferLength();
81 if (hasAudio) return audioBufferLength();
83 return 0;
86 boost::uint64_t
87 MediaParser::videoBufferLength() const
89 if (_videoFrames.empty()) return 0;
90 #if 0 // debugging
91 log_debug("videoBufferLength: %d - %d == %d",
92 _videoFrames.back()->timestamp(), _videoFrames.front()->timestamp(),
93 _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp());
94 #endif
95 return _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp();
98 boost::uint64_t
99 MediaParser::audioBufferLength() const
101 if (_audioFrames.empty()) return 0;
102 #if 0 // debugging
103 log_debug("audioBufferLength: %d - %d == %d",
104 _audioFrames.back()->timestamp, _audioFrames.front()->timestamp,
105 _audioFrames.back()->timestamp - _audioFrames.front()->timestamp);
106 #endif
107 return _audioFrames.back()->timestamp - _audioFrames.front()->timestamp;
110 const EncodedVideoFrame*
111 MediaParser::peekNextVideoFrame() const
113 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
114 boost::mutex::scoped_lock lock(_qMutex);
115 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
116 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
118 const_cast<MediaParser*>(this)->parseNextChunk();
120 #endif
122 if (!_videoInfo.get() || _videoFrames.empty()) return 0;
123 return _videoFrames.front();
126 bool
127 MediaParser::nextFrameTimestamp(boost::uint64_t& ts) const
129 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
130 boost::mutex::scoped_lock lock(_qMutex);
131 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
132 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
134 const_cast<MediaParser*>(this)->parseNextChunk();
136 #endif
138 if (_videoFrames.empty())
140 if (_audioFrames.empty())
142 return false;
144 else
146 ts = _audioFrames.front()->timestamp;
147 return true;
150 else
152 if (_audioFrames.empty())
154 ts = _videoFrames.front()->timestamp();
155 return true;
157 else
159 ts = std::min(_videoFrames.front()->timestamp(),
160 _audioFrames.front()->timestamp);
161 return true;
166 bool
167 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts) const
169 const EncodedVideoFrame* ef = peekNextVideoFrame();
170 if ( ! ef ) return false;
171 ts = ef->timestamp();
172 return true;
175 std::auto_ptr<EncodedVideoFrame>
176 MediaParser::nextVideoFrame()
178 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
179 boost::mutex::scoped_lock lock(_qMutex);
180 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
181 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
183 const_cast<MediaParser*>(this)->parseNextChunk();
185 #endif
187 std::auto_ptr<EncodedVideoFrame> ret;
188 if (_videoFrames.empty()) return ret;
189 ret.reset(_videoFrames.front());
190 _videoFrames.pop_front();
191 #ifdef GNASH_DEBUG_MEDIAPARSER
192 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
193 #endif // GNASH_DEBUG_MEDIAPARSER
194 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
195 return ret;
198 std::auto_ptr<EncodedAudioFrame>
199 MediaParser::nextAudioFrame()
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() && _audioInfo.get() && _audioFrames.empty())
206 const_cast<MediaParser*>(this)->parseNextChunk();
208 #endif
210 std::auto_ptr<EncodedAudioFrame> ret;
211 if (_audioFrames.empty()) return ret;
212 ret.reset(_audioFrames.front());
213 _audioFrames.pop_front();
214 #ifdef GNASH_DEBUG_MEDIAPARSER
215 log_debug("nextAudioFrame: 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 bool
222 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts) const
224 const EncodedAudioFrame* ef = peekNextAudioFrame();
225 if ( ! ef ) return false;
226 ts = ef->timestamp;
227 return true;
230 const EncodedAudioFrame*
231 MediaParser::peekNextAudioFrame() const
233 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
234 boost::mutex::scoped_lock lock(_qMutex);
235 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
236 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
238 const_cast<MediaParser*>(this)->parseNextChunk();
240 #endif
241 if (!_audioInfo.get() || _audioFrames.empty()) return 0;
242 return _audioFrames.front();
245 void
246 MediaParser::stopParserThread()
248 if ( _parserThread.get() )
250 requestParserThreadKill();
251 _parserThread->join();
252 _parserThread.reset();
256 MediaParser::~MediaParser()
258 stopParserThread();
260 for (VideoFrames::iterator i=_videoFrames.begin(),
261 e=_videoFrames.end(); i!=e; ++i)
263 delete (*i);
266 for (AudioFrames::iterator i=_audioFrames.begin(),
267 e=_audioFrames.end(); i!=e; ++i)
269 delete (*i);
273 void
274 MediaParser::clearBuffers()
276 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
277 boost::mutex::scoped_lock lock(_qMutex);
278 #endif
280 for (VideoFrames::iterator i=_videoFrames.begin(),
281 e=_videoFrames.end(); i!=e; ++i)
283 delete (*i);
286 for (AudioFrames::iterator i=_audioFrames.begin(),
287 e=_audioFrames.end(); i!=e; ++i)
289 delete (*i);
292 _audioFrames.clear();
293 _videoFrames.clear();
295 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
298 void
299 MediaParser::pushEncodedAudioFrame(std::auto_ptr<EncodedAudioFrame> frame)
301 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
302 boost::mutex::scoped_lock 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, frame.release());
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::auto_ptr<EncodedVideoFrame> frame)
341 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
342 boost::mutex::scoped_lock 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, frame.release());
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(boost::mutex::scoped_lock& 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 int bl = getBufferLengthNoLock();
400 int 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 _parserThreadStartBarrier.wait();
411 while (!parserThreadKillRequested())
413 parseNextChunk();
414 gnashSleep(100); // thread switch
416 // check for parsing complete
417 // TODO: have a setParsingComplete() function
418 // exposed in base class for taking care
419 // of this on appropriate time.
420 boost::mutex::scoped_lock lock(_qMutex);
421 waitIfNeeded(lock);
426 void
427 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, boost::uint64_t /*ts*/)
432 std::ostream&
433 operator<< (std::ostream& os, const VideoInfo& vi)
435 os << "codec:" << vi.codec << " (type " << vi.type << ") - "
436 << "size:" << vi.width << "x" << vi.height << " - "
437 << "frameRate:" << vi.frameRate << " - "
438 << "duration:" << vi.duration;
439 return os;
442 std::ostream&
443 operator<< (std::ostream& os, const videoCodecType& t)
445 switch (t)
447 case VIDEO_CODEC_H263:
448 os << "H263";
449 break;
450 case VIDEO_CODEC_SCREENVIDEO:
451 os << "Screenvideo";
452 break;
453 case VIDEO_CODEC_VP6:
454 os << "VP6";
455 break;
456 case VIDEO_CODEC_VP6A:
457 os << "VP6A";
458 break;
459 case VIDEO_CODEC_SCREENVIDEO2:
460 os << "Screenvideo2";
461 break;
462 case VIDEO_CODEC_H264:
463 os << "H264";
464 break;
465 default:
466 os << "unknown/invalid codec " << static_cast<int>(t);
467 break;
469 return os;
472 std::ostream&
473 operator<< (std::ostream& os, const audioCodecType& t)
475 switch (t)
477 case AUDIO_CODEC_RAW:
478 os << "Raw";
479 break;
480 case AUDIO_CODEC_ADPCM:
481 os << "ADPCM";
482 break;
483 case AUDIO_CODEC_MP3:
484 os << "MP3";
485 break;
486 case AUDIO_CODEC_UNCOMPRESSED:
487 os << "Uncompressed";
488 break;
489 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO:
490 os << "Nellymoser 8Hz mono";
491 break;
492 case AUDIO_CODEC_NELLYMOSER:
493 os << "Nellymoser";
494 break;
495 case AUDIO_CODEC_AAC:
496 os << "Advanced Audio Coding";
497 break;
498 case AUDIO_CODEC_SPEEX:
499 os << "Speex";
500 break;
501 default:
502 os << "unknown/invalid codec " << static_cast<int>(t);
503 break;
505 return os;
508 } // end of gnash::media namespace
509 } // end of gnash namespace
511 #undef PADDING_BYTES
512 #undef READ_CHUNKS