1 // FLVParser.cpp: Flash Video 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 "FLVParser.h"
24 #include "GnashException.h"
25 #include "IOChannel.h"
26 #include "SimpleBuffer.h"
27 #include "GnashAlgorithm.h"
32 // Define the following macro the have seek() operations printed
33 //#define GNASH_DEBUG_SEEK 1
39 const size_t FLVParser::paddingBytes
;
40 const boost::uint16_t FLVParser::FLVAudioTag::flv_audio_rates
[] =
41 { 5500, 11000, 22050, 44100 };
43 FLVParser::FLVParser(std::auto_ptr
<IOChannel
> lt
)
46 _lastParsedPosition(0),
53 _indexingCompleted(false)
56 throw MediaException("FLVParser couldn't parse header from input");
62 FLVParser::~FLVParser()
68 // would be called by main thread
70 FLVParser::seek(boost::uint32_t& time
)
73 boost::mutex::scoped_lock
streamLock(_streamMutex
);
74 // we might obtain this lock while the parser is pushing the last
75 // encoded frame on the queue, or while it is waiting on the wakeup
78 // Setting _seekRequest to true will make the parser thread
79 // take care of cleaning up the buffers before going on with
80 // parsing, thus fixing the case in which streamLock was obtained
81 // while the parser was pushing to queue
84 if ( _cuePoints
.empty() )
86 log_debug("No known cue points yet, can't seek");
90 CuePointsMap::iterator it
= _cuePoints
.lower_bound(time
);
91 if ( it
== _cuePoints
.end() )
93 log_debug("No cue points greater or equal requested time %d", time
);
97 long lowerBoundPosition
= it
->second
;
98 log_debug("Seek requested to time %d triggered seek to cue point at "
99 "position %d and time %d", time
, it
->second
, it
->first
);
101 _lastParsedPosition
=lowerBoundPosition
;
102 _parsingComplete
=false; // or NetStream will send the Play.Stop event...
105 // Finally, clear the buffers.
106 // The call will also wake the parse up if it was sleeping.
107 // WARNING: a race condition might be pending here:
108 // If we handled to do all the seek work in the *small*
109 // time that the parser runs w/out mutex locked (ie:
110 // after it unlocked the stream mutex and before it locked
111 // the queue mutex), it will still push an old encoded frame
112 // to the queue; if the pushed frame alone makes it block
113 // again (bufferFull) we'll have a problem.
114 // Note though, that a single frame can't reach a bufferFull
115 // condition, as it takes at least two for anything != 0.
122 // would be called by parser thread
124 FLVParser::parseNextChunk()
126 bool indexOnly
= bufferFull(); // won't lock, but our caller locked...
127 return parseNextTag(indexOnly
);
130 // would be called by parser thread
132 FLVParser::indexAudioTag(const FLVTag
& tag
, boost::uint32_t thisTagPos
)
134 if ( _videoInfo
.get()) {
135 // if we have video we let that drive cue points
139 // we can theoretically seek anywhere, but
140 // let's just keep 5 seconds of distance
141 CuePointsMap::iterator it
= _cuePoints
.lower_bound(tag
.timestamp
);
142 if ( it
== _cuePoints
.end() || it
->first
- tag
.timestamp
>= 5000)
144 //log_debug("Added cue point at timestamp %d and position %d "
145 //"(audio frame)", tag.timestamp, thisTagPos);
146 _cuePoints
[tag
.timestamp
] = thisTagPos
;
151 FLVParser::indexVideoTag(const FLVTag
& tag
, const FLVVideoTag
& videotag
, boost::uint32_t thisTagPos
)
153 if ( videotag
.frametype
!= FLV_VIDEO_KEYFRAME
) {
157 //log_debug("Added cue point at timestamp %d and position %d "
158 //"(key video frame)", tag.timestamp, thisTagPos);
159 _cuePoints
[tag
.timestamp
] = thisTagPos
;
163 std::auto_ptr
<EncodedAudioFrame
>
164 FLVParser::parseAudioTag(const FLVTag
& flvtag
, const FLVAudioTag
& audiotag
, boost::uint32_t thisTagPos
)
166 std::auto_ptr
<EncodedAudioFrame
> frame
;
169 log_error(_("Unexpected audio tag found at offset %d FLV stream "
170 "advertising no audio in header. We'll warn only once for "
171 "each FLV, expecting any further audio tag."), thisTagPos
);
172 _audio
= true; // TOCHECK: is this safe ?
176 boost::uint32_t bodyLength
= flvtag
.body_size
;
178 if (audiotag
.codec
== AUDIO_CODEC_AAC
) {
179 boost::uint8_t packettype
= _stream
->read_byte();
180 header
= (packettype
== 0);
184 frame
= readAudioFrame(bodyLength
-1, flvtag
.timestamp
);
185 if ( ! frame
.get() ) {
186 log_error("could not read audio frame?");
189 // If this is the first audioframe no info about the
190 // audio format has been noted, so we do that now
191 if (!_audioInfo
.get()) {
192 _audioInfo
.reset(new AudioInfo(audiotag
.codec
, audiotag
.samplerate
,
193 audiotag
.samplesize
, audiotag
.stereo
, 0,
198 // The frame is 0-padded up to the end. It may be larger than
199 // this if fewer bytes were read than requested, but it is
201 const size_t bufSize
= frame
->dataSize
+ paddingBytes
;
203 boost::uint8_t* data
= new boost::uint8_t[bufSize
];
205 std::copy(frame
->data
.get(), frame
->data
.get() + bufSize
, data
);
207 _audioInfo
->extra
.reset(
208 new ExtraAudioInfoFlv(data
, frame
->dataSize
)
211 // The FAAD decoder will reject us if we pass the header buffer.
212 // It will receive the header via the extra audio info anyway.
220 std::auto_ptr
<EncodedVideoFrame
>
221 FLVParser::parseVideoTag(const FLVTag
& flvtag
, const FLVVideoTag
& videotag
, boost::uint32_t thisTagPos
)
224 log_error(_("Unexpected video tag found at offset %d of FLV stream "
225 "advertising no video in header. We'll warn only once per "
226 "FLV, expecting any further video tag."), thisTagPos
);
227 _video
= true; // TOCHECK: is this safe ?
231 boost::uint32_t bodyLength
= flvtag
.body_size
;
233 switch(videotag
.codec
) {
234 case VIDEO_CODEC_VP6
:
235 case VIDEO_CODEC_VP6A
:
237 _stream
->read_byte();
241 case VIDEO_CODEC_H264
:
243 boost::uint8_t packettype
= _stream
->read_byte();
244 IF_VERBOSE_PARSE( log_debug(_("AVC packet type: %d"),
245 (unsigned)packettype
) );
247 header
= (packettype
== 0);
249 // 24-bits value for composition time offset ignored for now.
250 boost::uint8_t tmp
[3];
251 _stream
->read(tmp
, 3);
260 std::auto_ptr
<EncodedVideoFrame
> frame
= readVideoFrame(bodyLength
-1,
262 if ( ! frame
.get() ) {
263 log_error("could not read video frame?");
266 // If this is the first videoframe no info about the
267 // video format has been noted, so we do that now
268 if ( ! _videoInfo
.get() ) {
269 _videoInfo
.reset(new VideoInfo(videotag
.codec
, 0, 0, 0, 0,
273 // The frame is 0-padded up to the end. It may be larger than
274 // this if fewer bytes were read than requested, but it is
276 const size_t bufSize
= frame
->dataSize() + paddingBytes
;
278 boost::uint8_t* data
= new boost::uint8_t[bufSize
];
280 std::copy(frame
->data(), frame
->data() + bufSize
, data
);
281 _videoInfo
->extra
.reset(
282 new ExtraVideoInfoFlv(data
, frame
->dataSize())
285 // Don't bother emitting the header buffer.
293 // would be called by parser thread
295 FLVParser::parseNextTag(bool index_only
)
297 // lock the stream while reading from it, so actionscript
298 // won't mess with the parser on seek or on getBytesLoaded
299 boost::mutex::scoped_lock
streamLock(_streamMutex
);
301 if ( index_only
&& _indexingCompleted
) return false;
302 if ( _parsingComplete
) return false;
307 _seekRequest
= false;
310 boost::uint64_t& position
= index_only
? _nextPosToIndex
: _lastParsedPosition
;
311 bool& completed
= index_only
? _indexingCompleted
: _parsingComplete
;
313 //log_debug("parseNextTag: _lastParsedPosition:%d, _nextPosToIndex:%d, index_only:%d", _lastParsedPosition, _nextPosToIndex, index_only);
315 unsigned long thisTagPos
= position
;
317 // Seek to next frame and skip the tag size
318 //log_debug("FLVParser::parseNextTag seeking to %d", thisTagPos+4);
319 if (!_stream
->seek(thisTagPos
+4))
321 log_error("FLVParser::parseNextTag: can't seek to %d", thisTagPos
+4);
326 //log_debug("FLVParser::parseNextTag seeked to %d", thisTagPos+4);
329 boost::uint8_t chunk
[12];
330 int actuallyRead
= _stream
->read(chunk
, 12);
331 if ( actuallyRead
< 12 )
334 log_error("FLVParser::parseNextTag: can't read tag info "
335 "(needed 12 bytes, only got %d)", actuallyRead
);
336 // else { assert(_stream->eof(); } ?
340 // update bytes loaded
341 boost::mutex::scoped_lock
lock(_bytesLoadedMutex
);
342 _bytesLoaded
= _stream
->tell();
346 FLVTag
flvtag(chunk
);
348 // May be _lastParsedPosition OR _nextPosToIndex
349 position
+= 15 + flvtag
.body_size
;
351 bool doIndex
= (_lastParsedPosition
+4 > _nextPosToIndex
) || index_only
;
352 if ( _lastParsedPosition
> _nextPosToIndex
)
354 //log_debug("::parseNextTag setting _nextPosToIndex=%d", _lastParsedPosition+4);
355 _nextPosToIndex
= _lastParsedPosition
;
358 if ( position
> _bytesLoaded
) {
359 boost::mutex::scoped_lock
lock(_bytesLoadedMutex
);
360 _bytesLoaded
= position
;
363 // check for empty tag
364 if (flvtag
.body_size
== 0) return true;
366 if (flvtag
.type
== FLV_AUDIO_TAG
)
368 FLVAudioTag
audiotag(chunk
[11]);
371 indexAudioTag(flvtag
, thisTagPos
);
378 std::auto_ptr
<EncodedAudioFrame
> frame
=
379 parseAudioTag(flvtag
, audiotag
, thisTagPos
);
383 // Release the stream lock
384 // *before* pushing the frame as that
385 // might block us waiting for buffers flush
387 // We've done using the stream for this tag parsing anyway
389 pushEncodedAudioFrame(frame
);
391 else if (flvtag
.type
== FLV_VIDEO_TAG
)
393 FLVVideoTag
videotag(chunk
[11]);
396 indexVideoTag(flvtag
, videotag
, thisTagPos
);
402 std::auto_ptr
<EncodedVideoFrame
> frame
=
403 parseVideoTag(flvtag
, videotag
, thisTagPos
);
408 // Release the stream lock
409 // *before* pushing the frame as that
410 // might block us waiting for buffers flush
413 pushEncodedVideoFrame(frame
);
416 else if (flvtag
.type
== FLV_META_TAG
)
418 if ( chunk
[11] != 2 )
420 // ::processTags relies on the first AMF0 value being a string...
421 log_unimpl(_("First byte of FLV_META_TAG is %d, expected "
422 "0x02 (STRING AMF0 type)"),
423 static_cast<int>(chunk
[11]));
425 // Extract information from the meta tag
426 std::auto_ptr
<SimpleBuffer
> metaTag(new SimpleBuffer(
427 flvtag
.body_size
-1));
428 size_t actuallyRead
= _stream
->read(metaTag
->data(),
429 flvtag
.body_size
- 1);
431 if ( actuallyRead
< flvtag
.body_size
-1 )
433 log_error("FLVParser::parseNextTag: can't read metaTag (%d) "
434 "body (needed %d bytes, only got %d)",
435 FLV_META_TAG
, flvtag
.body_size
, actuallyRead
);
438 metaTag
->resize(actuallyRead
);
440 boost::uint32_t terminus
= getUInt24(metaTag
->data() +
444 log_error(_("Corrupt FLV: Meta tag unterminated!"));
447 boost::mutex::scoped_lock
lock(_metaTagsMutex
);
448 _metaTags
.insert(std::make_pair(flvtag
.timestamp
, metaTag
.release()));
452 log_error(_("FLVParser::parseNextTag: unknown FLV tag type %d"),
457 _stream
->read(chunk
, 4);
458 boost::uint32_t prevtagsize
= chunk
[0] << 24 | chunk
[1] << 16 |
459 chunk
[2] << 8 | chunk
[3];
460 if (prevtagsize
!= flvtag
.body_size
+ 11) {
461 log_error(_("Corrupt FLV: previous tag size record (%1%) unexpected "
462 "(actual size: %2%)"), prevtagsize
, flvtag
.body_size
+ 11);
468 // would be called by MAIN thread
470 FLVParser::parseHeader()
472 assert(_stream
->tell() == static_cast<std::streampos
>(0));
474 // We only use 5 bytes of the header, because the last 4 bytes represent
475 // an integer which is always 1.
476 boost::uint8_t header
[9];
477 if ( _stream
->read(header
, 9) != 9 )
479 log_error("FLVParser::parseHeader: couldn't read 9 bytes of header");
483 _lastParsedPosition
= _bytesLoaded
= _nextPosToIndex
= 9;
485 if (!std::equal(header
, header
+ 3, "FLV")) {
489 const boost::uint8_t version
= header
[3];
491 // Parse the audio+video bitmask
492 _audio
= header
[4]&(1<<2);
493 _video
= header
[4]&(1<<0);
495 log_debug("Parsing FLV version %d, audio:%d, video:%d",
496 (int)version
, _audio
, _video
);
501 inline boost::uint32_t
502 FLVParser::getUInt24(boost::uint8_t* in
)
504 // The bits are in big endian order
505 return (in
[0] << 16) | (in
[1] << 8) | in
[2];
509 FLVParser::getBytesLoaded() const
511 boost::mutex::scoped_lock
lock(_bytesLoadedMutex
);
515 // would be called by parser thread
517 std::auto_ptr
<EncodedAudioFrame
>
518 FLVParser::readAudioFrame(boost::uint32_t dataSize
, boost::uint32_t timestamp
)
521 std::auto_ptr
<EncodedAudioFrame
> frame(new EncodedAudioFrame
);
523 const size_t bufSize
= dataSize
+ paddingBytes
;
525 boost::uint8_t* data
= new boost::uint8_t[bufSize
];
526 const size_t bytesRead
= _stream
->read(data
, dataSize
);
528 std::fill(data
+ bytesRead
, data
+ bufSize
, 0);
530 if (bytesRead
< dataSize
) {
531 log_error("FLVParser::readAudioFrame: could only read %d/%d bytes",
532 bytesRead
, dataSize
);
535 frame
->dataSize
= bytesRead
;
536 frame
->timestamp
= timestamp
;
537 frame
->data
.reset(data
);
542 // would be called by parser thread
544 std::auto_ptr
<EncodedVideoFrame
>
545 FLVParser::readVideoFrame(boost::uint32_t dataSize
, boost::uint32_t timestamp
)
547 std::auto_ptr
<EncodedVideoFrame
> frame
;
549 const size_t bufSize
= dataSize
+ paddingBytes
;
551 boost::uint8_t* data
= new boost::uint8_t[bufSize
];
552 const size_t bytesRead
= _stream
->read(data
, dataSize
);
554 std::fill(data
+ bytesRead
, data
+ bufSize
, 0);
556 // We won't need frameNum, so will set to zero...
558 // NOTE: ownership of 'data' is transferred here
560 frame
.reset(new EncodedVideoFrame(data
, bytesRead
, 0, timestamp
));
566 FLVParser::fetchMetaTags(OrderedMetaTags
& tags
, boost::uint64_t ts
)
568 boost::mutex::scoped_lock
lock(_metaTagsMutex
);
569 if (!_metaTags
.empty()) {
570 MetaTags::iterator it
= _metaTags
.upper_bound(ts
);
572 // Copy the first value into the return container.
573 std::transform(_metaTags
.begin(), it
, std::back_inserter(tags
),
574 boost::bind(&MetaTags::value_type::second
, _1
));
576 _metaTags
.erase(_metaTags
.begin(), it
);
580 } // end of gnash::media namespace
581 } // end of gnash namespace