Fix test for bug #32625
[gnash.git] / libmedia / MediaParser.cpp
blob006896320d6afb773f3d441d93414b1acc420f5e
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 /*private*/
111 const EncodedVideoFrame*
112 MediaParser::peekNextVideoFrame() const
114 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
115 // TODO: assert _qMutex is locked by this thread
116 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
117 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
119 const_cast<MediaParser*>(this)->parseNextChunk();
121 #endif
123 if (!_videoInfo.get() || _videoFrames.empty()) return 0;
124 return _videoFrames.front();
127 bool
128 MediaParser::nextFrameTimestamp(boost::uint64_t& ts) const
130 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
131 boost::mutex::scoped_lock lock(_qMutex);
132 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
133 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
135 const_cast<MediaParser*>(this)->parseNextChunk();
137 #endif
139 if (_videoFrames.empty())
141 if (_audioFrames.empty())
143 return false;
145 else
147 ts = _audioFrames.front()->timestamp;
148 return true;
151 else
153 if (_audioFrames.empty())
155 ts = _videoFrames.front()->timestamp();
156 return true;
158 else
160 ts = std::min(_videoFrames.front()->timestamp(),
161 _audioFrames.front()->timestamp);
162 return true;
167 bool
168 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts) const
170 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
171 boost::mutex::scoped_lock lock(_qMutex);
172 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
173 const EncodedVideoFrame* ef = peekNextVideoFrame();
174 if ( ! ef ) return false;
175 ts = ef->timestamp();
176 return true;
179 std::auto_ptr<EncodedVideoFrame>
180 MediaParser::nextVideoFrame()
182 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
183 boost::mutex::scoped_lock lock(_qMutex);
184 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
185 while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
187 const_cast<MediaParser*>(this)->parseNextChunk();
189 #endif
191 std::auto_ptr<EncodedVideoFrame> ret;
192 if (_videoFrames.empty()) return ret;
193 ret.reset(_videoFrames.front());
194 _videoFrames.pop_front();
195 #ifdef GNASH_DEBUG_MEDIAPARSER
196 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
197 #endif // GNASH_DEBUG_MEDIAPARSER
198 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
199 return ret;
202 std::auto_ptr<EncodedAudioFrame>
203 MediaParser::nextAudioFrame()
205 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
206 boost::mutex::scoped_lock lock(_qMutex);
207 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
208 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
210 const_cast<MediaParser*>(this)->parseNextChunk();
212 #endif
214 std::auto_ptr<EncodedAudioFrame> ret;
215 if (_audioFrames.empty()) return ret;
216 ret.reset(_audioFrames.front());
217 _audioFrames.pop_front();
218 #ifdef GNASH_DEBUG_MEDIAPARSER
219 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
220 #endif // GNASH_DEBUG_MEDIAPARSER
221 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
222 return ret;
225 bool
226 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts) const
228 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
229 boost::mutex::scoped_lock lock(_qMutex);
230 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
231 const EncodedAudioFrame* ef = peekNextAudioFrame();
232 if ( ! ef ) return false;
233 ts = ef->timestamp;
234 return true;
237 /*private*/
238 const EncodedAudioFrame*
239 MediaParser::peekNextAudioFrame() const
241 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
242 // TODO: assert _qMutex is locked by this thread
243 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
244 while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
246 const_cast<MediaParser*>(this)->parseNextChunk();
248 #endif
249 if (!_audioInfo.get() || _audioFrames.empty()) return 0;
250 return _audioFrames.front();
253 void
254 MediaParser::stopParserThread()
256 if ( _parserThread.get() )
258 requestParserThreadKill();
259 _parserThread->join();
260 _parserThread.reset();
264 MediaParser::~MediaParser()
266 stopParserThread();
268 for (VideoFrames::iterator i=_videoFrames.begin(),
269 e=_videoFrames.end(); i!=e; ++i)
271 delete (*i);
274 for (AudioFrames::iterator i=_audioFrames.begin(),
275 e=_audioFrames.end(); i!=e; ++i)
277 delete (*i);
281 void
282 MediaParser::clearBuffers()
284 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
285 boost::mutex::scoped_lock lock(_qMutex);
286 #endif
288 for (VideoFrames::iterator i=_videoFrames.begin(),
289 e=_videoFrames.end(); i!=e; ++i)
291 delete (*i);
294 for (AudioFrames::iterator i=_audioFrames.begin(),
295 e=_audioFrames.end(); i!=e; ++i)
297 delete (*i);
300 _audioFrames.clear();
301 _videoFrames.clear();
303 _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
306 void
307 MediaParser::pushEncodedAudioFrame(std::auto_ptr<EncodedAudioFrame> frame)
309 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
310 boost::mutex::scoped_lock lock(_qMutex);
311 #endif
313 // Find location to insert this new frame to, so that
314 // timestamps are sorted
316 AudioFrames::iterator loc = _audioFrames.end();
317 if ( ! _audioFrames.empty() ) {
318 size_t gap=0;
319 AudioFrames::reverse_iterator i=_audioFrames.rbegin();
320 for (AudioFrames::reverse_iterator e=_audioFrames.rend(); i!=e; ++i)
322 if ( (*i)->timestamp <= frame->timestamp ) break;
323 ++gap;
326 loc = i.base();
328 if ( gap ) {
329 log_debug("Timestamp of last %d/%d audio frames in queue "
330 "greater then timestamp in the frame being "
331 "inserted to it (%d).", gap, _audioFrames.size(),
332 frame->timestamp);
336 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
337 _audioFrames.insert(loc, frame.release());
339 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
340 // if the push reaches a "buffer full" condition, or if we find the parsing
341 // to be completed, wait to be waken up
342 waitIfNeeded(lock);
343 #endif
346 void
347 MediaParser::pushEncodedVideoFrame(std::auto_ptr<EncodedVideoFrame> frame)
349 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
350 boost::mutex::scoped_lock lock(_qMutex);
351 #endif
353 // Find location to insert this new frame to, so that
354 // timestamps are sorted
356 VideoFrames::iterator loc = _videoFrames.end();
357 if ( ! _videoFrames.empty() ) {
358 size_t gap=0;
359 VideoFrames::reverse_iterator i=_videoFrames.rbegin();
360 for (VideoFrames::reverse_iterator e=_videoFrames.rend(); i!=e; ++i)
362 if ( (*i)->timestamp() <= frame->timestamp() ) break;
363 ++gap;
366 loc = i.base();
368 if ( gap ) {
369 log_debug("Timestamp of last %d/%d video frames in queue "
370 "greater then timestamp() in the frame being "
371 "inserted to it (%d).", gap, _videoFrames.size(),
372 frame->timestamp());
376 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
377 _videoFrames.insert(loc, frame.release());
379 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
380 waitIfNeeded(lock); // if the push reaches a "buffer full" condition, wait to be waken up
381 #endif
384 void
385 MediaParser::waitIfNeeded(boost::mutex::scoped_lock& lock)
387 // We hold a lock on the queue here...
388 bool pc=parsingCompleted();
389 bool ic=indexingCompleted();
390 bool bf=bufferFull();
391 if (( pc || (bf && ic)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
393 #ifdef GNASH_DEBUG_MEDIAPARSER
394 log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc, bf);
395 #endif // GNASH_DEBUG_MEDIAPARSER
396 _parserThreadWakeup.wait(lock);
397 #ifdef GNASH_DEBUG_MEDIAPARSER
398 log_debug("Parser thread finished waiting on wakeup lock");
399 #endif // GNASH_DEBUG_MEDIAPARSER
403 bool
404 MediaParser::bufferFull() const
406 // Callers are expected to hold a lock on _qMutex
407 int bl = getBufferLengthNoLock();
408 int bt = getBufferTime();
409 #ifdef GNASH_DEBUG_MEDIAPARSER
410 log_debug("MediaParser::bufferFull: %d/%d", bl, bt);
411 #endif // GNASH_DEBUG_MEDIAPARSER
412 return bl > bt;
415 void
416 MediaParser::parserLoop()
418 _parserThreadStartBarrier.wait();
419 while (!parserThreadKillRequested())
421 parseNextChunk();
422 gnashSleep(100); // thread switch
424 // check for parsing complete
425 // TODO: have a setParsingComplete() function
426 // exposed in base class for taking care
427 // of this on appropriate time.
428 boost::mutex::scoped_lock lock(_qMutex);
429 waitIfNeeded(lock);
434 void
435 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, boost::uint64_t /*ts*/)
440 std::ostream&
441 operator<< (std::ostream& os, const VideoInfo& vi)
443 os << "codec:" << vi.codec << " (type " << vi.type << ") - "
444 << "size:" << vi.width << "x" << vi.height << " - "
445 << "frameRate:" << vi.frameRate << " - "
446 << "duration:" << vi.duration;
447 return os;
450 std::ostream&
451 operator<< (std::ostream& os, const videoCodecType& t)
453 switch (t)
455 case VIDEO_CODEC_H263:
456 os << "H263";
457 break;
458 case VIDEO_CODEC_SCREENVIDEO:
459 os << "Screenvideo";
460 break;
461 case VIDEO_CODEC_VP6:
462 os << "VP6";
463 break;
464 case VIDEO_CODEC_VP6A:
465 os << "VP6A";
466 break;
467 case VIDEO_CODEC_SCREENVIDEO2:
468 os << "Screenvideo2";
469 break;
470 case VIDEO_CODEC_H264:
471 os << "H264";
472 break;
473 default:
474 os << "unknown/invalid codec " << static_cast<int>(t);
475 break;
477 return os;
480 std::ostream&
481 operator<< (std::ostream& os, const audioCodecType& t)
483 switch (t)
485 case AUDIO_CODEC_RAW:
486 os << "Raw";
487 break;
488 case AUDIO_CODEC_ADPCM:
489 os << "ADPCM";
490 break;
491 case AUDIO_CODEC_MP3:
492 os << "MP3";
493 break;
494 case AUDIO_CODEC_UNCOMPRESSED:
495 os << "Uncompressed";
496 break;
497 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO:
498 os << "Nellymoser 8Hz mono";
499 break;
500 case AUDIO_CODEC_NELLYMOSER:
501 os << "Nellymoser";
502 break;
503 case AUDIO_CODEC_AAC:
504 os << "Advanced Audio Coding";
505 break;
506 case AUDIO_CODEC_SPEEX:
507 os << "Speex";
508 break;
509 default:
510 os << "unknown/invalid codec " << static_cast<int>(t);
511 break;
513 return os;
516 } // end of gnash::media namespace
517 } // end of gnash namespace
519 #undef PADDING_BYTES
520 #undef READ_CHUNKS