2 * Copyright (C) 2005-2013 Team XBMC
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 2, or (at your option)
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 XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "DllAvFormat.h"
23 #include "DllAvCodec.h"
24 #include "DllAvUtil.h"
25 #include "DllSwScale.h"
26 #include "guilib/Texture.h"
28 #include "FFmpegVideoDecoder.h"
31 FFmpegVideoDecoder::FFmpegVideoDecoder()
39 m_dllAvFormat = new DllAvFormat();
40 m_dllAvCodec = new DllAvCodec();
41 m_dllAvUtil = new DllAvUtil();
42 m_dllSwScale = new DllSwScale();
45 FFmpegVideoDecoder::~FFmpegVideoDecoder()
55 void FFmpegVideoDecoder::close()
59 m_dllAvUtil->av_free( m_pFrame );
64 m_dllAvCodec->avpicture_free( m_pFrameRGB );
65 m_dllAvUtil->av_free( m_pFrameRGB );
70 m_dllAvCodec->avcodec_close( m_pCodecCtx );
72 // Close the video file
74 m_dllAvFormat->avformat_close_input( &m_pFormatCtx );
82 if ( m_dllAvCodec->IsLoaded() )
83 m_dllAvCodec->Unload();
85 if ( m_dllAvUtil->IsLoaded() )
86 m_dllAvUtil->Unload();
88 if ( m_dllSwScale->IsLoaded() )
89 m_dllSwScale->Unload();
91 if ( m_dllAvFormat->IsLoaded() )
92 m_dllAvFormat->Unload();
95 bool FFmpegVideoDecoder::isOpened() const
97 return m_pFrame ? true : false;
100 double FFmpegVideoDecoder::getDuration() const
102 if ( m_pFormatCtx && m_pFormatCtx->duration / AV_TIME_BASE > 0.0 )
103 return m_pFormatCtx->duration / AV_TIME_BASE;
108 double FFmpegVideoDecoder::getFramesPerSecond() const
110 #if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE)
111 return m_pFormatCtx ? av_q2d( m_dllAvFormat->av_stream_get_r_frame_rate( m_pFormatCtx->streams[ m_videoStream ] ) ) : 0.0;
113 return m_pFormatCtx ? av_q2d( m_pFormatCtx->streams[ m_videoStream ]->r_frame_rate ) : 0.0;
117 unsigned int FFmpegVideoDecoder::getWidth() const
122 return m_pCodecCtx->width;
125 unsigned int FFmpegVideoDecoder::getHeight() const
130 return m_pCodecCtx->height;
133 const AVFormatContext * FFmpegVideoDecoder::getAVFormatContext() const
138 const AVCodecContext * FFmpegVideoDecoder::getAVCodecContext() const
143 const AVCodec * FFmpegVideoDecoder::getAVCodec() const
148 CStdString FFmpegVideoDecoder::getErrorMsg() const
153 double FFmpegVideoDecoder::getLastFrameTime() const
155 return m_lastFrameTime;
159 bool FFmpegVideoDecoder::open( const CStdString& filename )
161 // See http://dranger.com/ffmpeg/tutorial01.html
164 if ( !m_dllAvUtil->Load() || !m_dllAvCodec->Load() || !m_dllSwScale->Load() || !m_dllAvFormat->Load() )
166 m_errorMsg = "Failed to load FFMpeg libraries";
170 m_dllAvCodec->avcodec_register_all();
171 m_dllAvFormat->av_register_all();
173 // Open the video file
174 if ( m_dllAvFormat->avformat_open_input( &m_pFormatCtx, filename.c_str(), NULL, NULL ) < 0 )
176 m_errorMsg = "Could not open the video file";
181 // Retrieve the stream information
182 if ( m_dllAvFormat->avformat_find_stream_info( m_pFormatCtx, 0 ) < 0 )
184 m_errorMsg = "Could not find the stream information";
189 // Find the first video stream
192 for ( unsigned i = 0; i < m_pFormatCtx->nb_streams; i++ )
194 if ( m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
201 if ( m_videoStream == -1 )
203 m_errorMsg = "Could not find a playable video stream";
208 // Get a pointer to the codec context for the video stream
209 m_pCodecCtx = m_pFormatCtx->streams[ m_videoStream ]->codec;
211 // Find the decoder for the video stream
212 m_pCodec = m_dllAvCodec->avcodec_find_decoder( m_pCodecCtx->codec_id );
214 if ( m_pCodec == NULL )
216 m_errorMsg = "Could not find a video decoder";
222 if ( m_dllAvCodec->avcodec_open2( m_pCodecCtx, m_pCodec, 0 ) < 0 )
224 m_errorMsg = "Could not open the video decoder";
229 // Allocate video frames
230 m_pFrame = m_dllAvCodec->avcodec_alloc_frame();
234 m_errorMsg = "Could not allocate memory for a frame";
243 bool FFmpegVideoDecoder::seek( double time )
245 // Convert the frame number into time stamp
246 int64_t timestamp = (int64_t) (time * AV_TIME_BASE * av_q2d( m_pFormatCtx->streams[ m_videoStream ]->time_base ));
248 if ( m_dllAvFormat->av_seek_frame( m_pFormatCtx, m_videoStream, timestamp, AVSEEK_FLAG_ANY ) < 0 )
251 m_dllAvCodec->avcodec_flush_buffers( m_pCodecCtx );
255 bool FFmpegVideoDecoder::nextFrame( CBaseTexture * texture )
261 // If we did not preallocate the picture or the texture size changed, (re)allocate it
262 if ( !m_pFrameRGB || texture->GetWidth() != m_frameRGBwidth || texture->GetHeight() != m_frameRGBheight )
266 m_dllAvCodec->avpicture_free( m_pFrameRGB );
267 m_dllAvUtil->av_free( m_pFrameRGB );
270 m_frameRGBwidth = texture->GetWidth();
271 m_frameRGBheight = texture->GetHeight();
273 // Allocate the conversion frame and relevant picture
274 m_pFrameRGB = (AVPicture*)m_dllAvUtil->av_mallocz(sizeof(AVPicture));
279 // Due to a bug in swsscale we need to allocate one extra line of data
280 if ( m_dllAvCodec->avpicture_alloc( m_pFrameRGB, PIX_FMT_RGB32, m_frameRGBwidth, m_frameRGBheight + 1 ) < 0 )
290 if ( m_dllAvFormat->av_read_frame( m_pFormatCtx, &packet ) < 0 )
291 return false; // Frame read failed (e.g. end of stream)
293 if ( packet.stream_index == m_videoStream )
295 // Is this a packet from the video stream -> decode video frame
296 m_dllAvCodec->avcodec_decode_video2( m_pCodecCtx, m_pFrame, &frameFinished, &packet );
298 // Did we get a video frame?
301 if ( packet.dts != (int64_t)AV_NOPTS_VALUE )
302 m_lastFrameTime = packet.dts * av_q2d( m_pFormatCtx->streams[ m_videoStream ]->time_base );
304 m_lastFrameTime = 0.0;
310 m_dllAvCodec->av_free_packet( &packet );
313 // We got the video frame, render it into the picture buffer
314 struct SwsContext * context = m_dllSwScale->sws_getContext( m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt,
315 m_frameRGBwidth, m_frameRGBheight, PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL );
317 m_dllSwScale->sws_scale( context, m_pFrame->data, m_pFrame->linesize, 0, m_pCodecCtx->height,
318 m_pFrameRGB->data, m_pFrameRGB->linesize );
319 m_dllSwScale->sws_freeContext( context );
320 m_dllAvCodec->av_free_packet( &packet );
322 // And into the texture
323 texture->Update( m_frameRGBwidth, m_frameRGBheight, m_frameRGBwidth * 4, XB_FMT_A8R8G8B8, m_pFrameRGB->data[0], false );