1 // MediaParserFfmpeg.cpp: FFMPEG media parsers, for Gnash
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc.
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "ffmpegHeaders.h"
22 #include "MediaParserFfmpeg.h"
23 #include "GnashException.h"
25 #include "IOChannel.h"
27 //#define GNASH_ALLOW_VCODEC_ENV 1
28 // Set this to enable a special GNASH_DEFAULT_VCODEC environment variable, which
29 // is used as a default when the video codec can't be detected. This is a quick
30 // hack to make MJPEG HTTP videos work (which can't be detected as their MIME
31 // type is just "mixed/multipart"). Perhaps the codec will be configurable via
32 // ActionScript sometime. - Udo
40 // Used to calculate a decimal value from a ffmpeg fraction
41 inline double as_double(AVRational time
) {
42 return time
.num
/ static_cast<double>(time
.den
);
45 } // anonymous namespace
49 MediaParserFfmpeg::readPacketWrapper(void* opaque
, boost::uint8_t* buf
,
52 MediaParserFfmpeg
* p
= static_cast<MediaParserFfmpeg
*>(opaque
);
53 return p
->readPacket(buf
, buf_size
);
57 MediaParserFfmpeg::seekMediaWrapper(void *opaque
, boost::int64_t offset
, int whence
)
59 MediaParserFfmpeg
* p
= static_cast<MediaParserFfmpeg
*>(opaque
);
60 return p
->seekMedia(offset
, whence
);
64 MediaParserFfmpeg::probeStream()
66 const size_t probeSize
= 4096;
67 const size_t bufSize
= probeSize
+ FF_INPUT_BUFFER_PADDING_SIZE
;
69 boost::scoped_array
<boost::uint8_t> buffer(new boost::uint8_t[bufSize
]);
71 assert(_stream
->tell() == static_cast<std::streampos
>(0));
72 size_t actuallyRead
= _stream
->read(buffer
.get(), probeSize
);
74 // Fill any padding with 0s.
75 std::fill(buffer
.get() + actuallyRead
, buffer
.get() + bufSize
, 0);
79 if (actuallyRead
< 1) {
80 throw IOException(_("MediaParserFfmpeg could not read probe data "
84 // Probe the file to detect the format
85 AVProbeData probe_data
;
86 probe_data
.filename
= "";
87 probe_data
.buf
= buffer
.get();
88 probe_data
.buf_size
= actuallyRead
;
90 AVInputFormat
* ret
= av_probe_input_format(&probe_data
, 1);
95 MediaParserFfmpeg::seek(boost::uint32_t& pos
)
97 // lock the stream while reading from it, so actionscript
98 // won't mess with the parser on seek or on getBytesLoaded
99 boost::mutex::scoped_lock
streamLock(_streamMutex
);
101 // NOTE: seeking when timestamps are unknown is a pain
102 // See https://savannah.gnu.org/bugs/index.php?33085
103 // TODO: newer ffmpeg versions seem to have an
104 // av_seek_frame_generic() function
105 // which may help us. May be worth taking a look
108 // Handle 0 by seeking to byte 0
109 // Doing this saves lots of headakes in absence
110 // of correct timestamps (which is the case for mp3)
111 log_debug("Seeking MediaParserFfmpeg input to byte offset zero");
112 if (av_seek_frame(_formatCtx
, -1, pos
, AVSEEK_FLAG_BYTE
) < 0) {
113 log_error(_("%s: seeking failed"), __FUNCTION__
);
118 // This is most likely wrong
119 log_debug("MediaParserFfmpeg::seek(%d) TESTING", pos
);
120 long newpos
= static_cast<long>(pos
/ AV_TIME_BASE
);
121 if (av_seek_frame(_formatCtx
, -1, newpos
, 0) < 0) {
122 log_error(_("%s: seeking failed"), __FUNCTION__
);
127 // We'll restart parsing
128 _parsingComplete
= false;
130 // Finally, clear the buffers.
131 // The call will also wake the parse up if it was sleeping.
132 // WARNING: a race condition might be pending here:
133 // If we handled to do all the seek work in the *small*
134 // time that the parser runs w/out mutex locked (ie:
135 // after it unlocked the stream mutex and before it locked
136 // the queue mutex), it will still push an old encoded frame
137 // to the queue; if the pushed frame alone makes it block
138 // again (bufferFull) we'll have a problem.
139 // Note though, that a single frame can't reach a bufferFull
140 // condition, as it takes at least two for anything != 0.
148 MediaParserFfmpeg::parseVideoFrame(AVPacket
& packet
)
150 assert(packet
.stream_index
== _videoStreamIndex
);
151 assert(_videoStream
);
153 // packet.dts is "decompression" timestamp
154 // packet.pts is "presentation" timestamp
155 // Dunno why we use dts, and don't understand the magic formula either...
158 // pkt->pts can be AV_NOPTS_VALUE if the video format has B frames,
159 // so it is better to rely on pkt->dts if you do not decompress the payload.
161 boost::uint64_t timestamp
= static_cast<boost::uint64_t>(packet
.dts
* as_double(_videoStream
->time_base
) * 1000.0);
164 LOG_ONCE( log_unimpl("%s", __PRETTY_FUNCTION__
) );
168 // flags, for keyframe
169 //bool isKeyFrame = packet.flags&PKT_FLAG_KEY;
171 // TODO: FIXME: *2 is an hack to avoid libavcodec reading past end of allocated space
172 // we might do proper padding or (better) avoid the copy as a whole by making
173 // EncodedVideoFrame virtual.
174 size_t allocSize
= packet
.size
*2;
175 boost::uint8_t* data
= new boost::uint8_t[allocSize
];
176 std::copy(packet
.data
, packet
.data
+packet
.size
, data
);
177 std::auto_ptr
<EncodedVideoFrame
> frame(new EncodedVideoFrame(data
, packet
.size
, 0, timestamp
));
179 pushEncodedVideoFrame(frame
);
186 MediaParserFfmpeg::parseAudioFrame(AVPacket
& packet
)
188 assert(packet
.stream_index
== _audioStreamIndex
);
189 assert(_audioStream
);
191 // packet.dts is "decompression" timestamp
192 // packet.pts is "presentation" timestamp
193 // Dunno why we use dts, and don't understand the magic formula either...
196 // pkt->pts can be AV_NOPTS_VALUE if the video format has B frames,
197 // so it is better to rely on pkt->dts if you do not decompress the payload.
200 boost::uint64_t dts
= packet
.dts
;
201 if ( dts
== static_cast<boost::uint64_t>(AV_NOPTS_VALUE
) ) {
202 // We'll take 'nopts' value as zero.
203 // Would likely be better to make it use timestamp
204 // of previous frame, if any.
206 // For now, this handling fixes warnings like:
207 // mdb:93, lastbuf:0 skiping granule 0
208 // mdb:93, lastbuf:0 skiping granule 0
209 // When playing: http://downloads.bbc.co.uk/news/nol/shared/spl/hi/audio_slideshow/kenadamptw/slideshow_629.swf
211 LOG_ONCE(log_error(_("FIXME: FFmpeg packet decompression "
212 "timestamp has no value, taking as zero")));
215 boost::uint64_t timestamp
= static_cast<boost::uint64_t>(dts
* as_double(_audioStream
->time_base
) * 1000.0);
216 //log_debug("On getting audio frame with timestamp %d, duration is %d", timestamp, _audioStream->duration);
218 std::auto_ptr
<EncodedAudioFrame
> frame ( new EncodedAudioFrame
);
220 // TODO: FIXME: *2 is an hack to avoid libavcodec reading past end of allocated space
221 // we might do proper padding or (better) avoid the copy as a whole by making
222 // EncodedVideoFrame virtual.
223 size_t allocSize
= packet
.size
*2;
224 boost::uint8_t* data
= new boost::uint8_t[allocSize
];
225 std::copy(packet
.data
, packet
.data
+packet
.size
, data
);
227 frame
->data
.reset(data
);
228 frame
->dataSize
= packet
.size
;
229 frame
->timestamp
= timestamp
;
231 pushEncodedAudioFrame(frame
);
237 MediaParserFfmpeg::parseNextFrame()
239 // lock the stream while reading from it, so actionscript
240 // won't mess with the parser on seek or on getBytesLoaded
241 boost::mutex::scoped_lock
streamLock(_streamMutex
);
243 if ( _parsingComplete
)
245 //log_debug("MediaParserFfmpeg::parseNextFrame: parsing "
246 //"complete, nothing to do");
250 // position the stream where we left parsing as
251 // it could be somewhere else for reading a specific
253 //_stream->seek(_lastParsedPosition);
259 //log_debug("av_read_frame call");
260 int rc
= av_read_frame(_formatCtx
, &packet
);
262 // Update _lastParsedPosition, even in case of error..
263 boost::uint64_t curPos
= _stream
->tell();
264 if ( curPos
> _lastParsedPosition
)
266 _lastParsedPosition
= curPos
;
269 //log_debug("av_read_frame returned %d", rc);
272 log_error(_("MediaParserFfmpeg::parseNextFrame: "
273 "Problems parsing next frame "
274 "(av_read_frame returned %d)."
275 " We'll consider the stream fully parsed."), rc
);
276 _parsingComplete
=true; // No point in parsing over
282 if ( packet
.stream_index
== _videoStreamIndex
)
284 ret
= parseVideoFrame(packet
);
286 else if ( packet
.stream_index
== _audioStreamIndex
)
288 ret
= parseAudioFrame(packet
);
292 ret
= false; // redundant..
293 log_debug("MediaParserFfmpeg::parseNextFrame: unknown stream index %d",
294 packet
.stream_index
);
297 av_free_packet(&packet
);
299 // Check if EOF was reached
300 if ( _stream
->eof() )
302 log_debug("MediaParserFfmpeg::parseNextFrame: at eof after "
304 _parsingComplete
=true;
311 MediaParserFfmpeg::parseNextChunk()
313 if (!parseNextFrame()) return false;
318 MediaParserFfmpeg::getBytesLoaded() const
320 return _lastParsedPosition
;
323 MediaParserFfmpeg::MediaParserFfmpeg(std::auto_ptr
<IOChannel
> stream
)
330 _videoStreamIndex(-1),
332 _audioStreamIndex(-1),
334 _lastParsedPosition(0)
343 MediaParserFfmpeg::initializeParser()
345 av_register_all(); // TODO: needs to be invoked only once ?
347 _byteIOCxt
.buffer
= NULL
;
349 _inputFmt
= probeStream();
351 #ifdef GNASH_ALLOW_VCODEC_ENV
353 char* defcodec
= getenv("GNASH_DEFAULT_VCODEC");
354 if (defcodec
&& strlen(defcodec
)) {
355 _inputFmt
= av_find_input_format(defcodec
);
360 throw MediaException("MediaParserFfmpeg couldn't figure out input "
364 // Setup the filereader/seeker mechanism.
365 // 7th argument (NULL) is the writer function,
366 // which isn't needed.
367 _byteIOBuffer
.reset(new unsigned char[byteIOBufferSize
]);
369 init_put_byte(&_byteIOCxt
,
370 _byteIOBuffer
.get(), // buffer
371 byteIOBufferSize
, // buffer size
373 this, // opaque pointer to pass to the callbacks
374 MediaParserFfmpeg::readPacketWrapper
, // packet reader callback
375 NULL
, // packet writer callback
376 MediaParserFfmpeg::seekMediaWrapper
// seeker callback
379 _byteIOCxt
.is_streamed
= 1;
381 #if !defined(LIBAVCODEC_VERSION_MAJOR) || LIBAVCODEC_VERSION_MAJOR < 52
383 _formatCtx
= av_alloc_format_context();
385 _formatCtx
= avformat_alloc_context();
390 // Otherwise av_open_input_stream will reallocate the context.
391 AVFormatParameters ap
;
392 std::memset(&ap
, 0, sizeof ap
);
393 ap
.prealloced_context
= 1;
395 if (av_open_input_stream(&_formatCtx
, &_byteIOCxt
, "", _inputFmt
, &ap
) < 0)
397 throw IOException("MediaParserFfmpeg couldn't open input stream");
400 #if defined(LIBAVCODEC_VERSION_MAJOR) && LIBAVCODEC_VERSION_MAJOR >= 52
401 // Note: in at least some versions of ffmpeg, av_open_input_stream does
402 // not parse metadata; not sure why.
403 AVMetadata
* md
= _formatCtx
->metadata
;
405 AVMetadataTag
* tag
= av_metadata_get(md
, "album", 0,
406 AV_METADATA_MATCH_CASE
);
407 if (tag
&& tag
->value
) {
408 setId3Info(&Id3Info::album
, std::string(tag
->value
),
414 log_debug("Parsing FFMPEG media file: format:%s; nstreams:%d",
415 _inputFmt
->name
, _formatCtx
->nb_streams
);
417 // Find first audio and video stream
418 for (size_t i
= 0; i
< static_cast<size_t>(_formatCtx
->nb_streams
); ++i
) {
420 AVStream
* stream
= _formatCtx
->streams
[i
];
422 log_debug("Stream %d of FFMPEG media file is null ?", i
);
426 AVCodecContext
* enc
= stream
->codec
;
428 log_debug("Stream %d of FFMPEG media file has no codec info", i
);
432 switch (enc
->codec_type
) {
433 #if LIBAVCODEC_VERSION_MAJOR >= 53
434 case AVMEDIA_TYPE_AUDIO
:
436 case CODEC_TYPE_AUDIO
:
438 if (_audioStreamIndex
< 0) {
439 _audioStreamIndex
= i
;
440 _audioStream
= _formatCtx
->streams
[i
];
441 // codec_name will only be filled by avcodec_find_decoder
443 log_debug(_(" Using stream %d for audio: codec id %d"),
444 i
, _audioStream
->codec
->codec_id
);
448 #if LIBAVCODEC_VERSION_MAJOR >= 53
449 case AVMEDIA_TYPE_VIDEO
:
451 case CODEC_TYPE_VIDEO
:
453 if (_videoStreamIndex
< 0) {
454 _videoStreamIndex
= i
;
455 _videoStream
= _formatCtx
->streams
[i
];
456 log_debug(_(" Using stream %d for video: codec id %d"),
457 i
, _videoStream
->codec
->codec_id
);
467 const int codec
= static_cast<int>(_videoStream
->codec
->codec_id
);
468 boost::uint16_t width
= _videoStream
->codec
->width
;
469 boost::uint16_t height
= _videoStream
->codec
->height
;
470 boost::uint16_t frameRate
= static_cast<boost::uint16_t>(
471 as_double(_videoStream
->r_frame_rate
));
472 #if !defined(HAVE_LIBAVFORMAT_AVFORMAT_H) && !defined(HAVE_FFMPEG_AVCODEC_H)
473 boost::uint64_t duration
= _videoStream
->codec_info_duration
;
475 boost::uint64_t duration
= _videoStream
->duration
;
477 if (duration
== AV_NOPTS_VALUE
) {
478 log_error(_("Duration of video stream unknown"));
479 duration
=0; // TODO: guess!
481 duration
= duration
/ as_double(_videoStream
->time_base
); // TODO: check this
484 _videoInfo
.reset(new VideoInfo(codec
, width
, height
, frameRate
,
485 duration
, CODEC_TYPE_CUSTOM
/*codec type*/));
487 // NOTE: AVCodecContext.extradata : void* for 51.11.0, uint8_t* for 51.38.0
488 _videoInfo
->extra
.reset(new ExtraVideoInfoFfmpeg(
489 (uint8_t*)_videoStream
->codec
->extradata
,
490 _videoStream
->codec
->extradata_size
));
497 const int codec
= static_cast<int>(_audioStream
->codec
->codec_id
);
498 boost::uint16_t sampleRate
= _audioStream
->codec
->sample_rate
;
499 boost::uint16_t sampleSize
= SampleFormatToSampleSize(_audioStream
->codec
->sample_fmt
);
500 bool stereo
= (_audioStream
->codec
->channels
== 2);
501 #if !defined(HAVE_LIBAVFORMAT_AVFORMAT_H) && !defined(HAVE_FFMPEG_AVCODEC_H)
502 boost::uint64_t duration
= _audioStream
->codec_info_duration
;
504 boost::uint64_t duration
= _audioStream
->duration
;
506 if (duration
== AV_NOPTS_VALUE
) {
507 log_error(_("Duration of audio stream unknown to ffmpeg"));
508 duration
=0; // TODO: guess!
511 duration
= duration
/ as_double(_audioStream
->time_base
); // TODO: check this
514 _audioInfo
.reset(new AudioInfo(codec
, sampleRate
, sampleSize
, stereo
,
515 duration
, CODEC_TYPE_CUSTOM
/*codec type*/));
517 // NOTE: AVCodecContext.extradata : void* for 51.11.0, uint8_t* for 51.38.0
518 _audioInfo
->extra
.reset(new ExtraAudioInfoFfmpeg(
519 (uint8_t*)_audioStream
->codec
->extradata
,
520 _audioStream
->codec
->extradata_size
));
527 MediaParserFfmpeg::~MediaParserFfmpeg()
533 // TODO: check if this is correct (should we create RIIA classes for ffmpeg stuff?)
534 //av_close_input_file(_formatCtx); // NOTE: this one triggers a mismatched free/delete on _byteIOBuffer with libavformat.so.52 !
540 // TODO: check if this is correct (should we create RIIA classes for ffmpeg stuff?)
541 //av_free(_inputFmt); // it seems this one blows up, could be due to av_free(_formatCtx) above
546 // NOTE: as this function is used as a callback from FFMPEG, it should not
547 // throw any exceptions, because:
548 // a) The behaviour of C++ exceptions passed into C code is undefined.
549 // b) Even if we don't crash and burn, the FFMPEG parser is left in an
552 MediaParserFfmpeg::readPacket(boost::uint8_t* buf
, int buf_size
)
554 //GNASH_REPORT_FUNCTION;
555 //log_debug("readPacket(%d)", buf_size);
557 size_t ret
= _stream
->read(static_cast<void*>(buf
), buf_size
);
563 boost::optional
<Id3Info
>
564 MediaParserFfmpeg::getId3Info() const
569 // NOTE: as this function is used as a callback from FFMPEG, it should not
570 // throw any exceptions, because:
571 // a) The behaviour of C++ exceptions passed into C code is undefined.
572 // b) Even if we don't crash and burn, the FFMPEG parser is left in an
575 MediaParserFfmpeg::seekMedia(boost::int64_t offset
, int whence
)
577 //GNASH_REPORT_FUNCTION;
578 //log_debug("::seekMedia(%1%, %2%)", offset, whence);
580 assert(_stream
.get());
582 if (whence
== SEEK_SET
)
584 // Offset is absolute new position in the file
586 boost::format fmt
= boost::format(
587 _("MediaParserFfmpeg couldn't parse input format: "
588 "tried to seek at negative offset %1%."))
593 _stream
->seek(offset
);
596 else if (whence
== SEEK_CUR
)
598 // New position is offset + old position
599 _stream
->seek(_stream
->tell() + static_cast<std::streamoff
>(offset
));
601 else if (whence
== SEEK_END
)
603 // New position is offset + end of file
604 log_unimpl("MediaParserFfmpeg seek from end of file");
605 // This is (most likely) a streamed file, so we can't seek to the end!
606 // Instead we seek to byteIOBufferSize bytes... seems to work fine...
607 _stream
->seek(byteIOBufferSize
);
612 // ffmpeg uses whence=AVSEEK_SIZE and offset=0 to request
614 log_unimpl("MediaParserFfmpeg: unsupported whence value %d", whence
);
619 return _stream
->tell();
623 MediaParserFfmpeg::SampleFormatToSampleSize(SampleFormat fmt
)
627 case SAMPLE_FMT_U8
: // unsigned 8 bits
630 case SAMPLE_FMT_S16
: // signed 16 bits
631 case SAMPLE_FMT_FLT
: // float
634 #if !defined (LIBAVCODEC_VERSION_MAJOR) || LIBAVCODEC_VERSION_MAJOR < 52
635 // Was dropped for version 52.0.0
636 case SAMPLE_FMT_S24
: // signed 24 bits
640 case SAMPLE_FMT_S32
: // signed 32 bits
643 case SAMPLE_FMT_NONE
:
645 return 8; // arbitrary value
650 } // gnash.media.ffmpeg namespace
651 } // end of gnash::media namespace
652 } // end of gnash namespace