1 // MediaParser.cpp: Media file parser, for Gnash.
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
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.
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>
26 #include "GnashSleep.h" // for usleep.
29 // Define this to get debugging output from MediaParser
30 //#define GNASH_DEBUG_MEDIAPARSER
35 MediaParser::MediaParser(std::auto_ptr
<IOChannel
> stream
)
37 _parsingComplete(false),
40 _bufferTime(100), // 100 ms
42 _parserThreadStartBarrier(2),
43 _parserThreadKillRequested(false),
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();
61 MediaParser::getBufferLength() const
63 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
64 boost::mutex::scoped_lock
lock(_qMutex
);
66 return getBufferLengthNoLock();
71 MediaParser::isBufferEmpty() const
73 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
74 boost::mutex::scoped_lock
lock(_qMutex
);
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
>();
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();
106 MediaParser::videoBufferLength() const
108 if (_videoFrames
.empty()) return 0;
110 log_debug("videoBufferLength: %d - %d == %d",
111 _videoFrames
.back()->timestamp(), _videoFrames
.front()->timestamp(),
112 _videoFrames
.back()->timestamp() - _videoFrames
.front()->timestamp());
114 return _videoFrames
.back()->timestamp() - _videoFrames
.front()->timestamp();
118 MediaParser::audioBufferLength() const
120 if (_audioFrames
.empty()) return 0;
122 log_debug("audioBufferLength: %d - %d == %d",
123 _audioFrames
.back()->timestamp
, _audioFrames
.front()->timestamp
,
124 _audioFrames
.back()->timestamp
- _audioFrames
.front()->timestamp
);
126 return _audioFrames
.back()->timestamp
- _audioFrames
.front()->timestamp
;
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();
142 if (!_videoInfo
.get() || _videoFrames
.empty()) return 0;
143 return _videoFrames
.front();
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();
158 if (_videoFrames
.empty())
160 if (_audioFrames
.empty())
166 ts
= _audioFrames
.front()->timestamp
;
172 if (_audioFrames
.empty())
174 ts
= _videoFrames
.front()->timestamp();
179 ts
= std::min(_videoFrames
.front()->timestamp(),
180 _audioFrames
.front()->timestamp
);
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();
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();
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?
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();
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?
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;
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();
268 if (!_audioInfo
.get() || _audioFrames
.empty()) return 0;
269 return _audioFrames
.front();
273 MediaParser::stopParserThread()
275 if ( _parserThread
.get() )
277 requestParserThreadKill();
278 _parserThread
->join();
279 _parserThread
.reset();
283 MediaParser::~MediaParser()
287 for (VideoFrames::iterator i
=_videoFrames
.begin(),
288 e
=_videoFrames
.end(); i
!=e
; ++i
)
293 for (AudioFrames::iterator i
=_audioFrames
.begin(),
294 e
=_audioFrames
.end(); i
!=e
; ++i
)
301 MediaParser::clearBuffers()
303 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
304 boost::mutex::scoped_lock
lock(_qMutex
);
307 for (VideoFrames::iterator i
=_videoFrames
.begin(),
308 e
=_videoFrames
.end(); i
!=e
; ++i
)
313 for (AudioFrames::iterator i
=_audioFrames
.begin(),
314 e
=_audioFrames
.end(); i
!=e
; ++i
)
319 _audioFrames
.clear();
320 _videoFrames
.clear();
322 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer
326 MediaParser::pushEncodedAudioFrame(std::auto_ptr
<EncodedAudioFrame
> frame
)
328 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
329 boost::mutex::scoped_lock
lock(_qMutex
);
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() ) {
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;
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(),
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
366 MediaParser::pushEncodedVideoFrame(std::auto_ptr
<EncodedVideoFrame
> frame
)
368 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
369 boost::mutex::scoped_lock
lock(_qMutex
);
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() ) {
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;
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(),
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
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
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
435 MediaParser::parserLoop()
437 _parserThreadStartBarrier
.wait();
438 while (!parserThreadKillRequested())
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
);
454 MediaParser::fetchMetaTags(OrderedMetaTags
& /*tags*/, boost::uint64_t /*ts*/)
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
;
470 operator<< (std::ostream
& os
, const videoCodecType
& t
)
474 case VIDEO_CODEC_H263
:
477 case VIDEO_CODEC_SCREENVIDEO
:
480 case VIDEO_CODEC_VP6
:
483 case VIDEO_CODEC_VP6A
:
486 case VIDEO_CODEC_SCREENVIDEO2
:
487 os
<< "Screenvideo2";
489 case VIDEO_CODEC_H264
:
493 os
<< "unknown/invalid codec " << static_cast<int>(t
);
500 operator<< (std::ostream
& os
, const audioCodecType
& t
)
504 case AUDIO_CODEC_RAW
:
507 case AUDIO_CODEC_ADPCM
:
510 case AUDIO_CODEC_MP3
:
513 case AUDIO_CODEC_UNCOMPRESSED
:
514 os
<< "Uncompressed";
516 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO
:
517 os
<< "Nellymoser 8Hz mono";
519 case AUDIO_CODEC_NELLYMOSER
:
522 case AUDIO_CODEC_AAC
:
523 os
<< "Advanced Audio Coding";
525 case AUDIO_CODEC_SPEEX
:
529 os
<< "unknown/invalid codec " << static_cast<int>(t
);
535 } // end of gnash::media namespace
536 } // end of gnash namespace