[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDCodecs / Overlay / DVDOverlayCodecFFmpeg.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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 "DVDOverlayCodecFFmpeg.h"
22 #include "DVDOverlayText.h"
23 #include "DVDOverlaySpu.h"
24 #include "DVDOverlayImage.h"
25 #include "DVDStreamInfo.h"
26 #include "DVDClock.h"
27 #include "utils/log.h"
28 #include "utils/EndianSwap.h"
29
30 CDVDOverlayCodecFFmpeg::CDVDOverlayCodecFFmpeg() : CDVDOverlayCodec("FFmpeg Subtitle Decoder")
31 {
32   m_pCodecContext = NULL;
33   m_SubtitleIndex = -1;
34   m_width         = 0;
35   m_height        = 0;
36   m_StartTime     = 0.0;
37   m_StopTime      = 0.0;
38   memset(&m_Subtitle, 0, sizeof(m_Subtitle));
39 }
40
41 CDVDOverlayCodecFFmpeg::~CDVDOverlayCodecFFmpeg()
42 {
43   Dispose();
44 }
45
46 bool CDVDOverlayCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
47 {
48   if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load()) return false;
49
50   m_dllAvCodec.avcodec_register_all();
51
52   AVCodec* pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
53   if (!pCodec)
54   {
55     CLog::Log(LOGDEBUG,"%s - Unable to find codec %d", __FUNCTION__, hints.codec);
56     return false;
57   }
58
59   m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);
60   m_pCodecContext->debug_mv = 0;
61   m_pCodecContext->debug = 0;
62   m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT;
63   m_pCodecContext->sub_id = hints.identifier;
64   m_pCodecContext->codec_tag = hints.codec_tag;
65   m_pCodecContext->time_base.num = 1;
66   m_pCodecContext->time_base.den = DVD_TIME_BASE;
67
68   if( hints.extradata && hints.extrasize > 0 )
69   {
70     m_pCodecContext->extradata_size = hints.extrasize;
71     m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE);
72     memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize);
73
74     // start parsing of extra data - create a copy to be safe and make it zero-terminating to avoid access violations!
75     unsigned int parse_extrasize = hints.extrasize;
76     char* parse_extra = new char[parse_extrasize + 1];
77     memcpy(parse_extra, hints.extradata, parse_extrasize);
78     parse_extra[parse_extrasize] = '\0';
79
80     // assume that the extra data is formatted as a concatenation of lines ('\n' terminated) 
81     char *ptr = parse_extra;
82     do // read line by line
83     {
84       if (!strncmp(ptr, "size:", 5))
85       {
86         int width = 0, height = 0;
87         sscanf(ptr, "size: %dx%d", &width, &height);
88         if (width > 0 && height > 0)
89         {
90           m_pCodecContext->width = width;
91           m_pCodecContext->height = height;
92           CLog::Log(LOGDEBUG,"%s - parsed extradata: size: %d x %d", __FUNCTION__,  width, height);
93         }
94       }
95       /*        
96       // leaving commented code: these items don't work yet... but they may be meaningful
97       if (!strncmp(ptr, "palette:", 8))
98         if (sscanf(ptr, "palette: %x, %x, %x, %x, %x, %x, %x, %x,"
99                                 " %x, %x, %x, %x, %x, %x, %x, %x", ...        
100       if (!strncasecmp(ptr, "forced subs: on", 15))
101         forced_subs_only = 1;
102       */
103       // if tried all possibilities, then read newline char and move to next line
104       ptr = strchr(ptr, '\n');
105       if (ptr != NULL) ptr++;
106     } 
107     while (ptr != NULL && ptr <= parse_extra + parse_extrasize);
108
109     delete[] parse_extra;
110   }
111
112   if (m_dllAvCodec.avcodec_open2(m_pCodecContext, pCodec, NULL) < 0)
113   {
114     CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
115     return false;
116   }
117
118   return true;
119 }
120
121 void CDVDOverlayCodecFFmpeg::Dispose()
122 {
123   if (m_pCodecContext)
124   {
125     if (m_pCodecContext->codec) m_dllAvCodec.avcodec_close(m_pCodecContext);
126     m_dllAvUtil.av_free(m_pCodecContext);
127     m_pCodecContext = NULL;
128   }
129   FreeSubtitle(m_Subtitle);
130
131   m_dllAvCodec.Unload();
132   m_dllAvUtil.Unload();
133 }
134
135 void CDVDOverlayCodecFFmpeg::FreeSubtitle(AVSubtitle& sub)
136 {
137   for(unsigned i=0;i<sub.num_rects;i++)
138   {
139     if(sub.rects[i])
140     {
141       m_dllAvUtil.av_free(sub.rects[i]->pict.data[0]);
142       m_dllAvUtil.av_free(sub.rects[i]->pict.data[1]);
143       m_dllAvUtil.av_freep(&sub.rects[i]);
144     }
145   }
146   if(sub.rects)
147     m_dllAvUtil.av_freep(&sub.rects);
148   sub.num_rects = 0;
149   sub.start_display_time = 0;
150   sub.end_display_time = 0;
151 }
152
153 int CDVDOverlayCodecFFmpeg::Decode(DemuxPacket *pPacket)
154 {
155   if (!m_pCodecContext || !pPacket)
156     return 1;
157
158   int gotsub = 0, len = 0;
159
160   FreeSubtitle(m_Subtitle);
161
162   AVPacket avpkt;
163   m_dllAvCodec.av_init_packet(&avpkt);
164   avpkt.data = pPacket->pData;
165   avpkt.size = pPacket->iSize;
166   avpkt.pts = pPacket->pts == DVD_NOPTS_VALUE ? AV_NOPTS_VALUE : (int64_t)pPacket->pts;
167   avpkt.dts = pPacket->dts == DVD_NOPTS_VALUE ? AV_NOPTS_VALUE : (int64_t)pPacket->dts;
168
169   len = m_dllAvCodec.avcodec_decode_subtitle2(m_pCodecContext, &m_Subtitle, &gotsub, &avpkt);
170
171   if (len < 0)
172   {
173     CLog::Log(LOGERROR, "%s - avcodec_decode_subtitle returned failure", __FUNCTION__);
174     return OC_ERROR;
175   }
176
177   if (len != avpkt.size)
178     CLog::Log(LOGWARNING, "%s - avcodec_decode_subtitle didn't consume the full packet", __FUNCTION__);
179
180   if (!gotsub)
181     return OC_BUFFER;
182
183   double pts_offset = 0.0;
184  
185   if (m_pCodecContext->codec_id == CODEC_ID_HDMV_PGS_SUBTITLE && m_Subtitle.format == 0) 
186   {
187     // for pgs subtitles the packet pts of the end_segments are wrong
188     // instead use the subtitle pts to calc the offset here
189     // see http://git.videolan.org/?p=ffmpeg.git;a=commit;h=2939e258f9d1fff89b3b68536beb931b54611585
190     if (m_Subtitle.pts != DVD_NOPTS_VALUE)
191     {
192       pts_offset = m_Subtitle.pts - pPacket->pts ;
193     }
194   }
195
196   m_StartTime   = DVD_MSEC_TO_TIME(m_Subtitle.start_display_time);
197   m_StopTime    = DVD_MSEC_TO_TIME(m_Subtitle.end_display_time);
198
199   //adapt start and stop time to our packet pts
200   bool dummy = false;
201   CDVDOverlayCodec::GetAbsoluteTimes(m_StartTime, m_StopTime, pPacket, dummy, pts_offset);
202   m_SubtitleIndex = 0;
203
204   return OC_OVERLAY;
205 }
206
207 void CDVDOverlayCodecFFmpeg::Reset()
208 {
209   Flush();
210 }
211
212 void CDVDOverlayCodecFFmpeg::Flush()
213 {
214   FreeSubtitle(m_Subtitle);
215   m_SubtitleIndex = -1;
216
217   m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);
218 }
219
220 CDVDOverlay* CDVDOverlayCodecFFmpeg::GetOverlay()
221 {
222   if(m_SubtitleIndex<0)
223     return NULL;
224
225   if(m_Subtitle.num_rects == 0 && m_SubtitleIndex == 0)
226   {
227     // we must add an empty overlay to replace the previous one
228     CDVDOverlay* o = new CDVDOverlay(DVDOVERLAY_TYPE_NONE);
229     o->iPTSStartTime = m_StartTime;
230     o->iPTSStopTime  = 0;
231     o->replace  = true;
232     m_SubtitleIndex++;
233     return o;
234   }
235
236   if(m_Subtitle.format == 0)
237   {
238     if(m_SubtitleIndex >= (int)m_Subtitle.num_rects)
239       return NULL;
240
241 #if LIBAVCODEC_VERSION_INT >= (52<<10)
242     if(m_Subtitle.rects[m_SubtitleIndex] == NULL)
243       return NULL;
244     AVSubtitleRect& rect = *m_Subtitle.rects[m_SubtitleIndex];
245 #else
246     AVSubtitleRect& rect = m_Subtitle.rects[m_SubtitleIndex];
247 #endif
248
249     CDVDOverlayImage* overlay = new CDVDOverlayImage();
250
251     overlay->iPTSStartTime = m_StartTime;
252     overlay->iPTSStopTime  = m_StopTime;
253     overlay->replace  = true;
254     overlay->linesize = rect.w;
255     overlay->data     = (BYTE*)malloc(rect.w * rect.h);
256     overlay->palette  = (uint32_t*)malloc(rect.nb_colors*4);
257     overlay->palette_colors = rect.nb_colors;
258     overlay->x        = rect.x;
259     overlay->y        = rect.y;
260     overlay->width    = rect.w;
261     overlay->height   = rect.h;
262
263 #if (!defined USE_EXTERNAL_FFMPEG) || (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,71,100))
264     overlay->bForced  = rect.flags != 0;
265 #endif
266
267     int right  = overlay->x + overlay->width;
268     int bottom = overlay->y + overlay->height;
269
270     if(m_height == 0 && m_pCodecContext->height)
271       m_height = m_pCodecContext->height;
272     if(m_width  == 0 && m_pCodecContext->width)
273       m_width  = m_pCodecContext->width;
274
275     if(bottom > m_height)
276     {
277       if     (bottom <= 480)
278         m_height      = 480;
279       else if(bottom <= 576)
280         m_height      = 576;
281       else if(bottom <= 720)
282         m_height      = 720;
283       else if(bottom <= 1080)
284         m_height      = 1080;
285       else
286         m_height      = bottom;
287     }
288     if(right > m_width)
289     {
290       if     (right <= 720)
291         m_width      = 720;
292       else if(right <= 1024)
293         m_width      = 1024;
294       else if(right <= 1280)
295         m_width      = 1280;
296       else if(right <= 1920)
297         m_width      = 1920;
298       else
299         m_width      = right;
300     }
301
302     overlay->source_width  = m_width;
303     overlay->source_height = m_height;
304
305     BYTE* s = rect.pict.data[0];
306     BYTE* t = overlay->data;
307     for(int i=0;i<rect.h;i++)
308     {
309       memcpy(t, s, rect.w);
310       s += rect.pict.linesize[0];
311       t += overlay->linesize;
312     }
313
314     for(int i=0;i<rect.nb_colors;i++)
315       overlay->palette[i] = Endian_SwapLE32(((uint32_t *)rect.pict.data[1])[i]);
316
317     m_dllAvUtil.av_free(rect.pict.data[0]);
318     m_dllAvUtil.av_free(rect.pict.data[1]);
319     m_dllAvUtil.av_freep(&m_Subtitle.rects[m_SubtitleIndex]);
320     m_SubtitleIndex++;
321
322     return overlay;
323   }
324
325   return NULL;
326 }