1 // MediaParserFfmpeg.cpp: FFMPEG media parsers, 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
20 #include "ffmpegHeaders.h"
21 #include "MediaParserFfmpeg.h"
22 #include "GnashException.h"
24 #include "IOChannel.h"
26 //#define GNASH_ALLOW_VCODEC_ENV 1
27 // Set this to enable a special GNASH_DEFAULT_VCODEC environment variable, which
28 // is used as a default when the video codec can't be detected. This is a quick
29 // hack to make MJPEG HTTP videos work (which can't be detected as their MIME
30 // type is just "mixed/multipart"). Perhaps the codec will be configurable via
31 // ActionScript sometime. - Udo
39 // Used to calculate a decimal value from a ffmpeg fraction
40 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
, int buf_size
)
51 MediaParserFfmpeg
* p
= static_cast<MediaParserFfmpeg
*>(opaque
);
52 return p
->readPacket(buf
, buf_size
);
56 MediaParserFfmpeg::seekMediaWrapper(void *opaque
, boost::int64_t offset
, int whence
)
58 MediaParserFfmpeg
* p
= static_cast<MediaParserFfmpeg
*>(opaque
);
59 return p
->seekMedia(offset
, whence
);
63 MediaParserFfmpeg::probeStream()
65 const size_t probeSize
= 2048;
66 const size_t bufSize
= probeSize
+ FF_INPUT_BUFFER_PADDING_SIZE
;
68 boost::scoped_array
<boost::uint8_t> buffer(new boost::uint8_t[bufSize
]);
70 assert(_stream
->tell() == static_cast<std::streampos
>(0));
71 size_t actuallyRead
= _stream
->read(buffer
.get(), probeSize
);
73 // Fill any padding with 0s.
74 std::fill(buffer
.get() + actuallyRead
, buffer
.get() + bufSize
, 0);
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 LOG_ONCE(log_unimpl("MediaParserFfmpeg::seek()"));
100 log_debug("MediaParserFfmpeg::seek(%d) TESTING", pos
);
102 AVStream
* videostream
= _formatCtx
->streams
[_videoStreamIndex
];
103 double timebase
= static_cast<double>(videostream
->time_base
.num
/ videostream
->time_base
.den
);
104 long newpos
= static_cast<long>(pos
/ timebase
);
106 if (av_seek_frame(_formatCtx
, _videoStreamIndex
, newpos
, 0) < 0)
108 log_error(_("%s: seeking failed"), __FUNCTION__
);
113 av_init_packet(&Packet
);
117 if (av_read_frame(_formatCtx
, &Packet
) < 0)
119 log_error("Error in av_read_frame (while seeking)");
120 av_seek_frame(_formatCtx
, -1, 0, AVSEEK_FLAG_BACKWARD
);
121 //av_free_packet( &Packet );
125 newtime
= timebase
* static_cast<double>(_formatCtx
->streams
[_videoStreamIndex
]->cur_dts
);
128 //av_free_packet( &Packet );
129 av_seek_frame(_formatCtx
, _videoStreamIndex
, newpos
, 0);
131 newtime
= static_cast<boost::int32_t>(newtime
/ 1000.0);
132 log_debug("Seek requested to time %d triggered seek to key frame at "
133 "time %d", pos
, newtime
);
140 MediaParserFfmpeg::parseVideoFrame(AVPacket
& packet
)
142 assert(packet
.stream_index
== _videoStreamIndex
);
143 assert(_videoStream
);
145 // packet.dts is "decompression" timestamp
146 // packet.pts is "presentation" timestamp
147 // Dunno why we use dts, and don't understand the magic formula either...
150 // pkt->pts can be AV_NOPTS_VALUE if the video format has B frames,
151 // so it is better to rely on pkt->dts if you do not decompress the payload.
153 boost::uint64_t timestamp
= static_cast<boost::uint64_t>(packet
.dts
* as_double(_videoStream
->time_base
) * 1000.0);
156 LOG_ONCE( log_unimpl("%s", __PRETTY_FUNCTION__
) );
160 // flags, for keyframe
161 //bool isKeyFrame = packet.flags&PKT_FLAG_KEY;
163 // TODO: FIXME: *2 is an hack to avoid libavcodec reading past end of allocated space
164 // we might do proper padding or (better) avoid the copy as a whole by making
165 // EncodedVideoFrame virtual.
166 size_t allocSize
= packet
.size
*2;
167 boost::uint8_t* data
= new boost::uint8_t[allocSize
];
168 std::copy(packet
.data
, packet
.data
+packet
.size
, data
);
169 std::auto_ptr
<EncodedVideoFrame
> frame(new EncodedVideoFrame(data
, packet
.size
, 0, timestamp
));
171 pushEncodedVideoFrame(frame
);
178 MediaParserFfmpeg::parseAudioFrame(AVPacket
& packet
)
180 assert(packet
.stream_index
== _audioStreamIndex
);
181 assert(_audioStream
);
183 // packet.dts is "decompression" timestamp
184 // packet.pts is "presentation" timestamp
185 // Dunno why we use dts, and don't understand the magic formula either...
188 // pkt->pts can be AV_NOPTS_VALUE if the video format has B frames,
189 // so it is better to rely on pkt->dts if you do not decompress the payload.
192 boost::uint64_t dts
= packet
.dts
;
193 if ( dts
== static_cast<boost::uint64_t>(AV_NOPTS_VALUE
) ) {
194 // We'll take 'nopts' value as zero.
195 // Would likely be better to make it use timestamp
196 // of previous frame, if any.
198 // For now, this handling fixes warnings like:
199 // mdb:93, lastbuf:0 skiping granule 0
200 // mdb:93, lastbuf:0 skiping granule 0
201 // When playing: http://downloads.bbc.co.uk/news/nol/shared/spl/hi/audio_slideshow/kenadamptw/slideshow_629.swf
203 LOG_ONCE(log_error("FIXME: FFmpeg packet decompression "
204 "timestamp has no value, taking as zero"));
207 boost::uint64_t timestamp
= static_cast<boost::uint64_t>(dts
* as_double(_audioStream
->time_base
) * 1000.0);
208 //log_debug("On getting audio frame with timestamp %d, duration is %d", timestamp, _audioStream->duration);
210 std::auto_ptr
<EncodedAudioFrame
> frame ( new EncodedAudioFrame
);
212 // TODO: FIXME: *2 is an hack to avoid libavcodec reading past end of allocated space
213 // we might do proper padding or (better) avoid the copy as a whole by making
214 // EncodedVideoFrame virtual.
215 size_t allocSize
= packet
.size
*2;
216 boost::uint8_t* data
= new boost::uint8_t[allocSize
];
217 std::copy(packet
.data
, packet
.data
+packet
.size
, data
);
219 frame
->data
.reset(data
);
220 frame
->dataSize
= packet
.size
;
221 frame
->timestamp
= timestamp
;
223 pushEncodedAudioFrame(frame
);
229 MediaParserFfmpeg::parseNextFrame()
231 // lock the stream while reading from it, so actionscript
232 // won't mess with the parser on seek or on getBytesLoaded
233 boost::mutex::scoped_lock
streamLock(_streamMutex
);
235 if ( _parsingComplete
)
237 //log_debug("MediaParserFfmpeg::parseNextFrame: parsing "
238 //"complete, nothing to do");
242 // position the stream where we left parsing as
243 // it could be somewhere else for reading a specific
245 //_stream->seek(_lastParsedPosition);
251 //log_debug("av_read_frame call");
252 int rc
= av_read_frame(_formatCtx
, &packet
);
254 // Update _lastParsedPosition, even in case of error..
255 boost::uint64_t curPos
= _stream
->tell();
256 if ( curPos
> _lastParsedPosition
)
258 _lastParsedPosition
= curPos
;
261 //log_debug("av_read_frame returned %d", rc);
264 log_error(_("MediaParserFfmpeg::parseNextFrame: "
265 "Problems parsing next frame "
266 "(av_read_frame returned %d)."
267 " We'll consider the stream fully parsed."), rc
);
268 _parsingComplete
=true; // No point in parsing over
274 if ( packet
.stream_index
== _videoStreamIndex
)
276 ret
= parseVideoFrame(packet
);
278 else if ( packet
.stream_index
== _audioStreamIndex
)
280 ret
= parseAudioFrame(packet
);
284 ret
= false; // redundant..
285 log_debug("MediaParserFfmpeg::parseNextFrame: unknown stream index %d",
286 packet
.stream_index
);
289 av_free_packet(&packet
);
291 // Check if EOF was reached
292 if ( _stream
->eof() )
294 log_debug("MediaParserFfmpeg::parseNextFrame: at eof after "
296 _parsingComplete
=true;
303 MediaParserFfmpeg::parseNextChunk()
305 if ( ! parseNextFrame() ) return false;
310 MediaParserFfmpeg::getBytesLoaded() const
312 //log_unimpl("%s", __PRETTY_FUNCTION__);
313 return _lastParsedPosition
;
316 MediaParserFfmpeg::MediaParserFfmpeg(std::auto_ptr
<IOChannel
> stream
)
323 _videoStreamIndex(-1),
325 _audioStreamIndex(-1),
327 _lastParsedPosition(0)
336 MediaParserFfmpeg::initializeParser()
338 av_register_all(); // TODO: needs to be invoked only once ?
340 _byteIOCxt
.buffer
= NULL
;
342 _inputFmt
= probeStream();
343 #ifdef GNASH_ALLOW_VCODEC_ENV
345 char* defcodec
= getenv("GNASH_DEFAULT_VCODEC");
346 if (defcodec
&& strlen(defcodec
))
347 _inputFmt
= av_find_input_format(defcodec
);
352 throw MediaException("MediaParserFfmpeg couldn't figure out input "
356 // av_alloc_format_context was deprecated on
357 // 2009-02-08 (r17047) in favor of avformat_alloc_context()
358 #if !defined (LIBAVCODEC_VERSION_MAJOR) || LIBAVCODEC_VERSION_MAJOR < 52
359 _formatCtx
= av_alloc_format_context();
361 _formatCtx
= avformat_alloc_context();
366 // Setup the filereader/seeker mechanism.
367 // 7th argument (NULL) is the writer function,
368 // which isn't needed.
369 _byteIOBuffer
.reset( new unsigned char[byteIOBufferSize
] );
370 init_put_byte(&_byteIOCxt
,
371 _byteIOBuffer
.get(), // buffer
372 byteIOBufferSize
, // buffer size
374 this, // opaque pointer to pass to the callbacks
375 MediaParserFfmpeg::readPacketWrapper
, // packet reader callback
376 NULL
, // packet writer callback
377 MediaParserFfmpeg::seekMediaWrapper
// seeker callback
380 _byteIOCxt
.is_streamed
= 1;
382 // Open the stream. the 4th argument is the filename, which we ignore.
383 if(av_open_input_stream(&_formatCtx
, &_byteIOCxt
, "", _inputFmt
, NULL
) < 0)
385 throw IOException("MediaParserFfmpeg couldn't open input stream");
388 log_debug("Parsing FFMPEG media file: format:%s; nstreams:%d",
389 _inputFmt
->name
, _formatCtx
->nb_streams
);
391 if ( _formatCtx
->title
[0] )
392 log_debug(_(" Title:'%s'"), _formatCtx
->title
);
393 if ( _formatCtx
->author
[0] )
394 log_debug(_(" Author:'%s'"), _formatCtx
->author
);
395 if ( _formatCtx
->copyright
[0] )
396 log_debug(_(" Copyright:'%s'"), _formatCtx
->copyright
);
397 if ( _formatCtx
->comment
[0] )
398 log_debug(_(" Comment:'%s'"), _formatCtx
->comment
);
399 if ( _formatCtx
->album
[0] )
400 log_debug(_(" Album:'%s'"), _formatCtx
->album
);
402 // Find first audio and video stream
403 for (unsigned int i
= 0; i
< static_cast<unsigned int>(_formatCtx
->nb_streams
); i
++)
405 AVStream
* stream
= _formatCtx
->streams
[i
];
407 log_debug("Stream %d of FFMPEG media file is null ?", i
);
411 AVCodecContext
* enc
= stream
->codec
;
413 log_debug("Stream %d of FFMPEG media file has no codec info", i
);
417 switch (enc
->codec_type
) {
418 case CODEC_TYPE_AUDIO
:
419 if (_audioStreamIndex
< 0) {
420 _audioStreamIndex
= i
;
421 _audioStream
= _formatCtx
->streams
[i
];
422 log_debug(_(" Using stream %d for audio: codec id %d"),
423 i
, _audioStream
->codec
->codec_id
);
424 // codec_name will only be filled by avcodec_find_decoder (later);
428 case CODEC_TYPE_VIDEO
:
429 if (_videoStreamIndex
< 0) {
430 _videoStreamIndex
= i
;
431 _videoStream
= _formatCtx
->streams
[i
];
432 log_debug(_(" Using stream %d for video: codec id %d"),
433 i
, _videoStream
->codec
->codec_id
);
434 // codec_name will only be filled by avcodec_find_decoder (later);
444 const int codec
= static_cast<int>(_videoStream
->codec
->codec_id
);
445 boost::uint16_t width
= _videoStream
->codec
->width
;
446 boost::uint16_t height
= _videoStream
->codec
->height
;
447 boost::uint16_t frameRate
= static_cast<boost::uint16_t>(as_double(_videoStream
->r_frame_rate
));
448 #if !defined(HAVE_LIBAVFORMAT_AVFORMAT_H) && !defined(HAVE_FFMPEG_AVCODEC_H)
449 boost::uint64_t duration
= _videoStream
->codec_info_duration
;
451 boost::uint64_t duration
= _videoStream
->duration
;
453 if (duration
== AV_NOPTS_VALUE
) {
454 log_error("Duration of video stream unknown");
455 duration
=0; // TODO: guess!
457 duration
= duration
/ as_double(_videoStream
->time_base
); // TODO: check this
460 _videoInfo
.reset(new VideoInfo(codec
, width
, height
, frameRate
,
461 duration
, CODEC_TYPE_CUSTOM
/*codec type*/));
463 // NOTE: AVCodecContext.extradata : void* for 51.11.0, uint8_t* for 51.38.0
464 _videoInfo
->extra
.reset(new ExtraVideoInfoFfmpeg(
465 (uint8_t*)_videoStream
->codec
->extradata
,
466 _videoStream
->codec
->extradata_size
));
472 const int codec
= static_cast<int>(_audioStream
->codec
->codec_id
);
473 boost::uint16_t sampleRate
= _audioStream
->codec
->sample_rate
;
474 boost::uint16_t sampleSize
= SampleFormatToSampleSize(_audioStream
->codec
->sample_fmt
);
475 bool stereo
= (_audioStream
->codec
->channels
== 2);
476 #if !defined(HAVE_LIBAVFORMAT_AVFORMAT_H) && !defined(HAVE_FFMPEG_AVCODEC_H)
477 boost::uint64_t duration
= _audioStream
->codec_info_duration
;
479 boost::uint64_t duration
= _audioStream
->duration
;
481 if (duration
== AV_NOPTS_VALUE
) {
482 log_error("Duration of audio stream unknown to ffmpeg");
483 duration
=0; // TODO: guess!
486 duration
= duration
/ as_double(_audioStream
->time_base
); // TODO: check this
489 _audioInfo
.reset(new AudioInfo(codec
, sampleRate
, sampleSize
, stereo
,
490 duration
, CODEC_TYPE_CUSTOM
/*codec type*/));
492 // NOTE: AVCodecContext.extradata : void* for 51.11.0, uint8_t* for 51.38.0
493 _audioInfo
->extra
.reset(new ExtraAudioInfoFfmpeg(
494 (uint8_t*)_audioStream
->codec
->extradata
,
495 _audioStream
->codec
->extradata_size
));
502 MediaParserFfmpeg::~MediaParserFfmpeg()
508 // TODO: check if this is correct (should we create RIIA classes for ffmpeg stuff?)
509 //av_close_input_file(_formatCtx); // NOTE: this one triggers a mismatched free/delete on _byteIOBuffer with libavformat.so.52 !
515 // TODO: check if this is correct (should we create RIIA classes for ffmpeg stuff?)
516 //av_free(_inputFmt); // it seems this one blows up, could be due to av_free(_formatCtx) above
522 MediaParserFfmpeg::readPacket(boost::uint8_t* buf
, int buf_size
)
524 //GNASH_REPORT_FUNCTION;
525 //log_debug("readPacket(%d)", buf_size);
527 size_t ret
= _stream
->read(static_cast<void*>(buf
), buf_size
);
534 MediaParserFfmpeg::seekMedia(boost::int64_t offset
, int whence
)
536 //GNASH_REPORT_FUNCTION;
537 //log_debug("::seekMedia(%1%, %2%)", offset, whence);
539 assert(_stream
.get());
541 if (whence
== SEEK_SET
)
543 // Offset is absolute new position in the file
545 boost::format fmt
= boost::format(
546 _("MediaParserFfmpeg couldn't parse input format: "
547 "tried to seek at negative offset %1%."))
549 throw MediaException(fmt
.str());
551 _stream
->seek(offset
);
553 else if (whence
== SEEK_CUR
)
555 // New position is offset + old position
556 _stream
->seek(_stream
->tell() + static_cast<std::streamoff
>(offset
));
558 else if (whence
== SEEK_END
)
560 // New position is offset + end of file
561 log_unimpl("MediaParserFfmpeg seek from end of file");
562 // This is (most likely) a streamed file, so we can't seek to the end!
563 // Instead we seek to byteIOBufferSize bytes... seems to work fine...
564 _stream
->seek(byteIOBufferSize
);
569 // ffmpeg uses whence=AVSEEK_SIZE and offset=0 to request
571 log_unimpl("MediaParserFfmpeg: unsupported whence value %d", whence
);
575 return _stream
->tell();
579 MediaParserFfmpeg::SampleFormatToSampleSize(SampleFormat fmt
)
583 case SAMPLE_FMT_U8
: // unsigned 8 bits
586 case SAMPLE_FMT_S16
: // signed 16 bits
587 case SAMPLE_FMT_FLT
: // float
590 #if !defined (LIBAVCODEC_VERSION_MAJOR) || LIBAVCODEC_VERSION_MAJOR < 52
591 // Was dropped for version 52.0.0
592 case SAMPLE_FMT_S24
: // signed 24 bits
596 case SAMPLE_FMT_S32
: // signed 32 bits
599 case SAMPLE_FMT_NONE
:
601 return 8; // arbitrary value
606 } // gnash.media.ffmpeg namespace
607 } // end of gnash::media namespace
608 } // end of gnash namespace