Merge pull request #4797 from Karlson2k/Gotham-fix_tag_read
[vuplus_xbmc] / xbmc / video / FFmpegVideoDecoder.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "system.h"
22 #include "DllAvFormat.h"
23 #include "DllAvCodec.h"
24 #include "DllAvUtil.h"
25 #include "DllSwScale.h"
26 #include "guilib/Texture.h"
27
28 #include "FFmpegVideoDecoder.h"
29
30
31 FFmpegVideoDecoder::FFmpegVideoDecoder()
32 {
33   m_pFormatCtx = 0;
34   m_pCodecCtx = 0;
35   m_pCodec = 0;
36   m_pFrame = 0;
37   m_pFrameRGB = 0;
38   
39   m_dllAvFormat = new DllAvFormat();
40   m_dllAvCodec = new DllAvCodec();
41   m_dllAvUtil = new DllAvUtil();
42   m_dllSwScale = new DllSwScale();
43 }
44
45 FFmpegVideoDecoder::~FFmpegVideoDecoder()
46 {
47   close();
48   
49   delete m_dllAvFormat;
50   delete m_dllAvCodec;
51   delete m_dllAvUtil;
52   delete m_dllSwScale;
53 }
54
55 void FFmpegVideoDecoder::close()
56 {
57   // Free the YUV frame
58   if ( m_pFrame )
59         m_dllAvUtil->av_free( m_pFrame );
60
61   // Free the RGB frame
62   if ( m_pFrameRGB )
63   {
64         m_dllAvCodec->avpicture_free( m_pFrameRGB );
65         m_dllAvUtil->av_free( m_pFrameRGB );
66   }
67
68   // Close the codec
69   if ( m_pCodecCtx )
70         m_dllAvCodec->avcodec_close( m_pCodecCtx );
71
72   // Close the video file
73   if ( m_pFormatCtx )
74         m_dllAvFormat->avformat_close_input( &m_pFormatCtx );
75
76   m_pFormatCtx = 0;
77   m_pCodecCtx = 0;
78   m_pCodec = 0;
79   m_pFrame = 0;
80   m_pFrameRGB = 0;
81   
82   if ( m_dllAvCodec->IsLoaded() )
83     m_dllAvCodec->Unload();
84   
85   if ( m_dllAvUtil->IsLoaded() )
86     m_dllAvUtil->Unload();
87   
88   if ( m_dllSwScale->IsLoaded() )
89     m_dllSwScale->Unload();
90   
91   if ( m_dllAvFormat->IsLoaded() )
92     m_dllAvFormat->Unload();
93 }
94
95 bool FFmpegVideoDecoder::isOpened() const
96 {
97   return m_pFrame ? true : false;
98 }
99
100 double FFmpegVideoDecoder::getDuration() const
101 {
102   if ( m_pFormatCtx && m_pFormatCtx->duration / AV_TIME_BASE > 0.0 )
103         return m_pFormatCtx->duration / AV_TIME_BASE;
104
105   return 0.0;
106 }
107   
108 double FFmpegVideoDecoder::getFramesPerSecond() const
109 {
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;
112 #else
113   return m_pFormatCtx ? av_q2d( m_pFormatCtx->streams[ m_videoStream ]->r_frame_rate ) : 0.0;
114 #endif
115 }
116   
117 unsigned int FFmpegVideoDecoder::getWidth() const
118 {
119   if ( !m_pCodecCtx )
120     return 0;
121
122   return m_pCodecCtx->width;
123 }
124
125 unsigned int FFmpegVideoDecoder::getHeight() const
126 {
127   if ( !m_pCodecCtx )
128     return 0;
129
130   return m_pCodecCtx->height;
131 }
132
133 const AVFormatContext * FFmpegVideoDecoder::getAVFormatContext() const
134 {
135   return m_pFormatCtx;
136 }
137
138 const AVCodecContext * FFmpegVideoDecoder::getAVCodecContext() const
139 {
140   return m_pCodecCtx;
141 }
142
143 const AVCodec * FFmpegVideoDecoder::getAVCodec() const
144 {
145   return m_pCodec;
146 }
147
148 CStdString FFmpegVideoDecoder::getErrorMsg() const
149 {
150   return m_errorMsg;
151 }
152
153 double FFmpegVideoDecoder::getLastFrameTime() const
154 {
155   return m_lastFrameTime;
156 }
157
158
159 bool FFmpegVideoDecoder::open( const CStdString& filename )
160 {
161   // See http://dranger.com/ffmpeg/tutorial01.html
162   close();
163   
164   if ( !m_dllAvUtil->Load() || !m_dllAvCodec->Load() || !m_dllSwScale->Load() || !m_dllAvFormat->Load() )
165   {
166     m_errorMsg = "Failed to load FFMpeg libraries";
167     return false;
168   }
169
170   m_dllAvCodec->avcodec_register_all();
171   m_dllAvFormat->av_register_all();
172
173   // Open the video file
174   if ( m_dllAvFormat->avformat_open_input( &m_pFormatCtx, filename.c_str(), NULL, NULL ) < 0 )
175   {
176     m_errorMsg = "Could not open the video file";
177    close();
178     return false;
179   }
180
181   // Retrieve the stream information
182   if ( m_dllAvFormat->avformat_find_stream_info( m_pFormatCtx, 0 ) < 0 )
183   {
184     m_errorMsg = "Could not find the stream information";
185     close();
186     return false;
187   }
188
189   // Find the first video stream
190   m_videoStream = -1;
191
192   for ( unsigned i = 0; i < m_pFormatCtx->nb_streams; i++ )
193   {
194     if ( m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
195     {
196       m_videoStream = i;
197       break;
198     }
199   }
200
201   if ( m_videoStream == -1 )
202   {
203     m_errorMsg = "Could not find a playable video stream";
204     close();
205     return false;
206   }
207
208   // Get a pointer to the codec context for the video stream
209   m_pCodecCtx = m_pFormatCtx->streams[ m_videoStream ]->codec;
210
211   // Find the decoder for the video stream
212   m_pCodec = m_dllAvCodec->avcodec_find_decoder( m_pCodecCtx->codec_id );
213
214   if ( m_pCodec == NULL )
215   {
216     m_errorMsg = "Could not find a video decoder";
217     close();
218     return false;
219   }
220     
221   // Open the codec
222   if ( m_dllAvCodec->avcodec_open2( m_pCodecCtx, m_pCodec, 0 ) < 0 )
223   {
224     m_errorMsg = "Could not open the video decoder";
225     close();
226     return false;
227   }
228   
229   // Allocate video frames
230   m_pFrame = m_dllAvCodec->avcodec_alloc_frame();
231
232   if ( !m_pFrame )
233   {
234     m_errorMsg = "Could not allocate memory for a frame";
235     close();
236     return false;
237   }
238   
239   return true;
240 }
241
242
243 bool FFmpegVideoDecoder::seek( double time )
244 {
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 ));
247
248   if ( m_dllAvFormat->av_seek_frame( m_pFormatCtx, m_videoStream, timestamp, AVSEEK_FLAG_ANY ) < 0 )
249         return false;
250
251   m_dllAvCodec->avcodec_flush_buffers( m_pCodecCtx );
252   return true;
253 }
254
255 bool FFmpegVideoDecoder::nextFrame( CBaseTexture * texture )
256 {
257   // Just in case
258   if ( !m_pCodecCtx )
259         return false;
260
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 )
263   {
264     if ( m_pFrameRGB )
265     {
266       m_dllAvCodec->avpicture_free( m_pFrameRGB );
267       m_dllAvUtil->av_free( m_pFrameRGB );
268     }
269
270     m_frameRGBwidth = texture->GetWidth();
271     m_frameRGBheight = texture->GetHeight();
272
273     // Allocate the conversion frame and relevant picture
274     m_pFrameRGB = (AVPicture*)m_dllAvUtil->av_mallocz(sizeof(AVPicture));
275
276     if ( !m_pFrameRGB )
277       return false;
278
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 )
281       return false;
282   }
283
284   AVPacket packet;
285   int frameFinished;
286
287   while ( true )
288   {
289     // Read a frame
290     if ( m_dllAvFormat->av_read_frame( m_pFormatCtx, &packet ) < 0 )
291       return false;  // Frame read failed (e.g. end of stream)
292
293     if ( packet.stream_index == m_videoStream )
294     {
295       // Is this a packet from the video stream -> decode video frame
296       m_dllAvCodec->avcodec_decode_video2( m_pCodecCtx, m_pFrame, &frameFinished, &packet );
297
298       // Did we get a video frame?
299       if ( frameFinished )
300       {
301         if ( packet.dts != (int64_t)AV_NOPTS_VALUE )
302           m_lastFrameTime = packet.dts * av_q2d( m_pFormatCtx->streams[ m_videoStream ]->time_base );
303         else
304            m_lastFrameTime = 0.0;
305
306         break;
307       }
308     }
309
310     m_dllAvCodec->av_free_packet( &packet );
311   }
312
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 );
316
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 );
321
322   // And into the texture
323   texture->Update( m_frameRGBwidth, m_frameRGBheight, m_frameRGBwidth * 4, XB_FMT_A8R8G8B8, m_pFrameRGB->data[0], false );
324
325   return true;
326 }
327