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/>.
21 #include "DVDOverlayCodecFFmpeg.h"
22 #include "DVDOverlayText.h"
23 #include "DVDOverlaySpu.h"
24 #include "DVDOverlayImage.h"
25 #include "DVDStreamInfo.h"
27 #include "utils/log.h"
28 #include "utils/EndianSwap.h"
30 CDVDOverlayCodecFFmpeg::CDVDOverlayCodecFFmpeg() : CDVDOverlayCodec("FFmpeg Subtitle Decoder")
32 m_pCodecContext = NULL;
38 memset(&m_Subtitle, 0, sizeof(m_Subtitle));
41 CDVDOverlayCodecFFmpeg::~CDVDOverlayCodecFFmpeg()
46 bool CDVDOverlayCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
48 if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load()) return false;
50 m_dllAvCodec.avcodec_register_all();
52 AVCodec* pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
55 CLog::Log(LOGDEBUG,"%s - Unable to find codec %d", __FUNCTION__, hints.codec);
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;
68 if( hints.extradata && hints.extrasize > 0 )
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);
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';
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
84 if (!strncmp(ptr, "size:", 5))
86 int width = 0, height = 0;
87 sscanf(ptr, "size: %dx%d", &width, &height);
88 if (width > 0 && height > 0)
90 m_pCodecContext->width = width;
91 m_pCodecContext->height = height;
92 CLog::Log(LOGDEBUG,"%s - parsed extradata: size: %d x %d", __FUNCTION__, width, height);
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;
103 // if tried all possibilities, then read newline char and move to next line
104 ptr = strchr(ptr, '\n');
105 if (ptr != NULL) ptr++;
107 while (ptr != NULL && ptr <= parse_extra + parse_extrasize);
109 delete[] parse_extra;
112 if (m_dllAvCodec.avcodec_open2(m_pCodecContext, pCodec, NULL) < 0)
114 CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
121 void CDVDOverlayCodecFFmpeg::Dispose()
125 if (m_pCodecContext->codec) m_dllAvCodec.avcodec_close(m_pCodecContext);
126 m_dllAvUtil.av_free(m_pCodecContext);
127 m_pCodecContext = NULL;
129 FreeSubtitle(m_Subtitle);
131 m_dllAvCodec.Unload();
132 m_dllAvUtil.Unload();
135 void CDVDOverlayCodecFFmpeg::FreeSubtitle(AVSubtitle& sub)
137 for(unsigned i=0;i<sub.num_rects;i++)
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]);
147 m_dllAvUtil.av_freep(&sub.rects);
149 sub.start_display_time = 0;
150 sub.end_display_time = 0;
153 int CDVDOverlayCodecFFmpeg::Decode(DemuxPacket *pPacket)
155 if (!m_pCodecContext || !pPacket)
158 int gotsub = 0, len = 0;
160 FreeSubtitle(m_Subtitle);
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;
169 len = m_dllAvCodec.avcodec_decode_subtitle2(m_pCodecContext, &m_Subtitle, &gotsub, &avpkt);
173 CLog::Log(LOGERROR, "%s - avcodec_decode_subtitle returned failure", __FUNCTION__);
177 if (len != avpkt.size)
178 CLog::Log(LOGWARNING, "%s - avcodec_decode_subtitle didn't consume the full packet", __FUNCTION__);
183 double pts_offset = 0.0;
185 if (m_pCodecContext->codec_id == CODEC_ID_HDMV_PGS_SUBTITLE && m_Subtitle.format == 0)
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)
192 pts_offset = m_Subtitle.pts - pPacket->pts ;
196 m_StartTime = DVD_MSEC_TO_TIME(m_Subtitle.start_display_time);
197 m_StopTime = DVD_MSEC_TO_TIME(m_Subtitle.end_display_time);
199 //adapt start and stop time to our packet pts
201 CDVDOverlayCodec::GetAbsoluteTimes(m_StartTime, m_StopTime, pPacket, dummy, pts_offset);
207 void CDVDOverlayCodecFFmpeg::Reset()
212 void CDVDOverlayCodecFFmpeg::Flush()
214 FreeSubtitle(m_Subtitle);
215 m_SubtitleIndex = -1;
217 m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);
220 CDVDOverlay* CDVDOverlayCodecFFmpeg::GetOverlay()
222 if(m_SubtitleIndex<0)
225 if(m_Subtitle.num_rects == 0 && m_SubtitleIndex == 0)
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;
236 if(m_Subtitle.format == 0)
238 if(m_SubtitleIndex >= (int)m_Subtitle.num_rects)
241 #if LIBAVCODEC_VERSION_INT >= (52<<10)
242 if(m_Subtitle.rects[m_SubtitleIndex] == NULL)
244 AVSubtitleRect& rect = *m_Subtitle.rects[m_SubtitleIndex];
246 AVSubtitleRect& rect = m_Subtitle.rects[m_SubtitleIndex];
249 CDVDOverlayImage* overlay = new CDVDOverlayImage();
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;
260 overlay->width = rect.w;
261 overlay->height = rect.h;
263 #if (!defined USE_EXTERNAL_FFMPEG) || (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,71,100))
264 overlay->bForced = rect.flags != 0;
267 int right = overlay->x + overlay->width;
268 int bottom = overlay->y + overlay->height;
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;
275 if(bottom > m_height)
279 else if(bottom <= 576)
281 else if(bottom <= 720)
283 else if(bottom <= 1080)
292 else if(right <= 1024)
294 else if(right <= 1280)
296 else if(right <= 1920)
302 overlay->source_width = m_width;
303 overlay->source_height = m_height;
305 BYTE* s = rect.pict.data[0];
306 BYTE* t = overlay->data;
307 for(int i=0;i<rect.h;i++)
309 memcpy(t, s, rect.w);
310 s += rect.pict.linesize[0];
311 t += overlay->linesize;
314 for(int i=0;i<rect.nb_colors;i++)
315 overlay->palette[i] = Endian_SwapLE32(((uint32_t *)rect.pict.data[1])[i]);
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]);