add gnash_la_CPPFLAGS to see if it makes distcheck happy
[gnash.git] / libmedia / MediaParser.cpp
blob4748018a02ebd2625cea6649aa670885d11e9b05
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 /* public */
68 bool
69 MediaParser::isBufferEmpty() const
71 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
72 boost::mutex::scoped_lock lock(_qMutex);
73 #endif
74 return _videoFrames.empty() && _audioFrames.empty();
78 boost::uint64_t
79 MediaParser::getBufferLengthNoLock() const
81 bool hasVideo = _videoInfo.get();
82 bool hasAudio = _audioInfo.get();
84 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
86 if (hasVideo && hasAudio) {
87 return std::min(audioBufferLength(), videoBufferLength());
90 if (hasVideo) return videoBufferLength();
92 if (hasAudio) return audioBufferLength();
94 return 0;
97 boost::uint64_t
98 MediaParser::videoBufferLength() const
100 if (_videoFrames.empty()) return 0;
101 #if 0 // debugging
102 log_debug("videoBufferLength: %d - %d == %d",
103 _videoFrames.back()->timestamp(), _videoFrames.front()->timestamp(),
104 _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp());
105 #endif
106 return _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp();
109 boost::uint64_t
110 MediaParser::audioBufferLength() const
112 if (_audioFrames.empty()) return 0;
113 #if 0 // debugging
114 log_debug("audioBufferLength: %d - %d == %d",
115 _audioFrames.back()->timestamp, _audioFrames.front()->timestamp,
116 _audioFrames.back()->timestamp - _audioFrames.front()->timestamp);
117 #endif
118 return _audioFrames.back()->timestamp - _audioFrames.front()->timestamp;
121 /*private*/
122 const EncodedVideoFrame*
123 MediaParser::peekNextVideoFrame() const
125 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
126 // TODO: assert _qMutex is locked by this thread
127 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
128 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
130 const_cast<MediaParser*>(this)->parseNextChunk();
132 #endif
134 if (!_videoInfo.get() || _videoFrames.empty()) return 0;
135 return _videoFrames.front();
138 bool
139 MediaParser::nextFrameTimestamp(boost::uint64_t& ts) const
141 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
142 boost::mutex::scoped_lock lock(_qMutex);
143 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
144 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
146 const_cast<MediaParser*>(this)->parseNextChunk();
148 #endif
150 if (_videoFrames.empty())
152 if (_audioFrames.empty())
154 return false;
156 else
158 ts = _audioFrames.front()->timestamp;
159 return true;
162 else
164 if (_audioFrames.empty())
166 ts = _videoFrames.front()->timestamp();
167 return true;
169 else
171 ts = std::min(_videoFrames.front()->timestamp(),
172 _audioFrames.front()->timestamp);
173 return true;
178 bool
179 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts) const
181 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
182 boost::mutex::scoped_lock lock(_qMutex);
183 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
184 const EncodedVideoFrame* ef = peekNextVideoFrame();
185 if ( ! ef ) return false;
186 ts = ef->timestamp();
187 return true;
190 std::auto_ptr<EncodedVideoFrame>
191 MediaParser::nextVideoFrame()
193 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
194 boost::mutex::scoped_lock lock(_qMutex);
195 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
196 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
198 const_cast<MediaParser*>(this)->parseNextChunk();
200 #endif
202 std::auto_ptr<EncodedVideoFrame> ret;
203 if (_videoFrames.empty()) return ret;
204 ret.reset(_videoFrames.front());
205 _videoFrames.pop_front();
206 #ifdef GNASH_DEBUG_MEDIAPARSER
207 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
208 #endif // GNASH_DEBUG_MEDIAPARSER
209 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
210 return ret;
213 std::auto_ptr<EncodedAudioFrame>
214 MediaParser::nextAudioFrame()
216 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
217 boost::mutex::scoped_lock lock(_qMutex);
218 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
219 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
221 const_cast<MediaParser*>(this)->parseNextChunk();
223 #endif
225 std::auto_ptr<EncodedAudioFrame> ret;
226 if (_audioFrames.empty()) return ret;
227 ret.reset(_audioFrames.front());
228 _audioFrames.pop_front();
229 #ifdef GNASH_DEBUG_MEDIAPARSER
230 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
231 #endif // GNASH_DEBUG_MEDIAPARSER
232 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
233 return ret;
236 bool
237 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts) const
239 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
240 boost::mutex::scoped_lock lock(_qMutex);
241 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
242 const EncodedAudioFrame* ef = peekNextAudioFrame();
243 if ( ! ef ) return false;
244 ts = ef->timestamp;
245 return true;
248 /*private*/
249 const EncodedAudioFrame*
250 MediaParser::peekNextAudioFrame() const
252 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
253 // TODO: assert _qMutex is locked by this thread
254 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
255 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
257 const_cast<MediaParser*>(this)->parseNextChunk();
259 #endif
260 if (!_audioInfo.get() || _audioFrames.empty()) return 0;
261 return _audioFrames.front();
264 void
265 MediaParser::stopParserThread()
267 if ( _parserThread.get() )
269 requestParserThreadKill();
270 _parserThread->join();
271 _parserThread.reset();
275 MediaParser::~MediaParser()
277 stopParserThread();
279 for (VideoFrames::iterator i=_videoFrames.begin(),
280 e=_videoFrames.end(); i!=e; ++i)
282 delete (*i);
285 for (AudioFrames::iterator i=_audioFrames.begin(),
286 e=_audioFrames.end(); i!=e; ++i)
288 delete (*i);
292 void
293 MediaParser::clearBuffers()
295 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
296 boost::mutex::scoped_lock lock(_qMutex);
297 #endif
299 for (VideoFrames::iterator i=_videoFrames.begin(),
300 e=_videoFrames.end(); i!=e; ++i)
302 delete (*i);
305 for (AudioFrames::iterator i=_audioFrames.begin(),
306 e=_audioFrames.end(); i!=e; ++i)
308 delete (*i);
311 _audioFrames.clear();
312 _videoFrames.clear();
314 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
317 void
318 MediaParser::pushEncodedAudioFrame(std::auto_ptr<EncodedAudioFrame> frame)
320 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
321 boost::mutex::scoped_lock lock(_qMutex);
322 #endif
324 // Find location to insert this new frame to, so that
325 // timestamps are sorted
327 AudioFrames::iterator loc = _audioFrames.end();
328 if ( ! _audioFrames.empty() ) {
329 size_t gap=0;
330 AudioFrames::reverse_iterator i=_audioFrames.rbegin();
331 for (AudioFrames::reverse_iterator e=_audioFrames.rend(); i!=e; ++i)
333 if ( (*i)->timestamp <= frame->timestamp ) break;
334 ++gap;
337 loc = i.base();
339 if ( gap ) {
340 log_debug("Timestamp of last %d/%d audio frames in queue "
341 "greater then timestamp in the frame being "
342 "inserted to it (%d).", gap, _audioFrames.size(),
343 frame->timestamp);
347 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
348 _audioFrames.insert(loc, frame.release());
350 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
351 // if the push reaches a "buffer full" condition, or if we find the parsing
352 // to be completed, wait to be waken up
353 waitIfNeeded(lock);
354 #endif
357 void
358 MediaParser::pushEncodedVideoFrame(std::auto_ptr<EncodedVideoFrame> frame)
360 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
361 boost::mutex::scoped_lock lock(_qMutex);
362 #endif
364 // Find location to insert this new frame to, so that
365 // timestamps are sorted
367 VideoFrames::iterator loc = _videoFrames.end();
368 if ( ! _videoFrames.empty() ) {
369 size_t gap=0;
370 VideoFrames::reverse_iterator i=_videoFrames.rbegin();
371 for (VideoFrames::reverse_iterator e=_videoFrames.rend(); i!=e; ++i)
373 if ( (*i)->timestamp() <= frame->timestamp() ) break;
374 ++gap;
377 loc = i.base();
379 if ( gap ) {
380 log_debug("Timestamp of last %d/%d video frames in queue "
381 "greater then timestamp() in the frame being "
382 "inserted to it (%d).", gap, _videoFrames.size(),
383 frame->timestamp());
387 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
388 _videoFrames.insert(loc, frame.release());
390 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
391 waitIfNeeded(lock); // if the push reaches a "buffer full" condition, wait to be waken up
392 #endif
395 void
396 MediaParser::waitIfNeeded(boost::mutex::scoped_lock& lock)
398 // We hold a lock on the queue here...
399 bool pc=parsingCompleted();
400 bool ic=indexingCompleted();
401 bool bf=bufferFull();
402 if (( pc || (bf && ic)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
404 #ifdef GNASH_DEBUG_MEDIAPARSER
405 log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc, bf);
406 #endif // GNASH_DEBUG_MEDIAPARSER
407 _parserThreadWakeup.wait(lock);
408 #ifdef GNASH_DEBUG_MEDIAPARSER
409 log_debug("Parser thread finished waiting on wakeup lock");
410 #endif // GNASH_DEBUG_MEDIAPARSER
414 bool
415 MediaParser::bufferFull() const
417 // Callers are expected to hold a lock on _qMutex
418 int bl = getBufferLengthNoLock();
419 int bt = getBufferTime();
420 #ifdef GNASH_DEBUG_MEDIAPARSER
421 log_debug("MediaParser::bufferFull: %d/%d", bl, bt);
422 #endif // GNASH_DEBUG_MEDIAPARSER
423 return bl > bt;
426 void
427 MediaParser::parserLoop()
429 _parserThreadStartBarrier.wait();
430 while (!parserThreadKillRequested())
432 parseNextChunk();
433 gnashSleep(100); // thread switch
435 // check for parsing complete
436 // TODO: have a setParsingComplete() function
437 // exposed in base class for taking care
438 // of this on appropriate time.
439 boost::mutex::scoped_lock lock(_qMutex);
440 waitIfNeeded(lock);
445 void
446 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, boost::uint64_t /*ts*/)
451 std::ostream&
452 operator<< (std::ostream& os, const VideoInfo& vi)
454 os << "codec:" << vi.codec << " (type " << vi.type << ") - "
455 << "size:" << vi.width << "x" << vi.height << " - "
456 << "frameRate:" << vi.frameRate << " - "
457 << "duration:" << vi.duration;
458 return os;
461 std::ostream&
462 operator<< (std::ostream& os, const videoCodecType& t)
464 switch (t)
466 case VIDEO_CODEC_H263:
467 os << "H263";
468 break;
469 case VIDEO_CODEC_SCREENVIDEO:
470 os << "Screenvideo";
471 break;
472 case VIDEO_CODEC_VP6:
473 os << "VP6";
474 break;
475 case VIDEO_CODEC_VP6A:
476 os << "VP6A";
477 break;
478 case VIDEO_CODEC_SCREENVIDEO2:
479 os << "Screenvideo2";
480 break;
481 case VIDEO_CODEC_H264:
482 os << "H264";
483 break;
484 default:
485 os << "unknown/invalid codec " << static_cast<int>(t);
486 break;
488 return os;
491 std::ostream&
492 operator<< (std::ostream& os, const audioCodecType& t)
494 switch (t)
496 case AUDIO_CODEC_RAW:
497 os << "Raw";
498 break;
499 case AUDIO_CODEC_ADPCM:
500 os << "ADPCM";
501 break;
502 case AUDIO_CODEC_MP3:
503 os << "MP3";
504 break;
505 case AUDIO_CODEC_UNCOMPRESSED:
506 os << "Uncompressed";
507 break;
508 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO:
509 os << "Nellymoser 8Hz mono";
510 break;
511 case AUDIO_CODEC_NELLYMOSER:
512 os << "Nellymoser";
513 break;
514 case AUDIO_CODEC_AAC:
515 os << "Advanced Audio Coding";
516 break;
517 case AUDIO_CODEC_SPEEX:
518 os << "Speex";
519 break;
520 default:
521 os << "unknown/invalid codec " << static_cast<int>(t);
522 break;
524 return os;
527 } // end of gnash::media namespace
528 } // end of gnash namespace
530 #undef PADDING_BYTES
531 #undef READ_CHUNKS