2 * Copyright (C) 2005-2008 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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #if (defined HAVE_CONFIG_H) && (!defined WIN32)
26 #include "DVDVideoCodecFFmpeg.h"
27 #include "DVDDemuxers/DVDDemux.h"
28 #include "DVDStreamInfo.h"
30 #include "DVDCodecs/DVDCodecs.h"
31 #include "../../../../utils/Win32Exception.h"
32 #if defined(_LINUX) || defined(_WIN32)
33 #include "utils/CPUInfo.h"
35 #include "AdvancedSettings.h"
36 #include "GUISettings.h"
37 #include "utils/log.h"
38 #include "boost/shared_ptr.hpp"
41 #define RINT(x) ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) - 0.5)))
47 #include "cores/VideoRenderers/RenderManager.h"
59 using namespace boost;
61 enum PixelFormat CDVDVideoCodecFFmpeg::GetFormat( struct AVCodecContext * avctx
62 , const PixelFormat * fmt )
64 CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque;
66 if(!ctx->IsHardwareAllowed())
67 return ctx->m_dllAvCodec.avcodec_default_get_format(avctx, fmt);
69 const PixelFormat * cur = fmt;
70 while(*cur != PIX_FMT_NONE)
73 if(CVDPAU::IsVDPAUFormat(*cur) && g_guiSettings.GetBool("videoplayer.usevdpau"))
75 if(ctx->GetHardware())
78 CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::GetFormat - Creating VDPAU(%ix%i)", avctx->width, avctx->height);
79 CVDPAU* vdp = new CVDPAU();
80 if(vdp->Open(avctx, *cur))
82 ctx->SetHardware(vdp);
90 if(DXVA::CDecoder::Supports(*cur) && g_guiSettings.GetBool("videoplayer.usedxva2"))
92 DXVA::CDecoder* dec = new DXVA::CDecoder();
93 if(dec->Open(avctx, *cur))
95 ctx->SetHardware(dec);
103 if(*cur == PIX_FMT_VAAPI_VLD && g_guiSettings.GetBool("videoplayer.usevaapi"))
105 VAAPI::CDecoder* dec = new VAAPI::CDecoder();
106 if(dec->Open(avctx, *cur))
108 ctx->SetHardware(dec);
117 return ctx->m_dllAvCodec.avcodec_default_get_format(avctx, fmt);
121 CDVDVideoCodecFFmpeg::IHardwareDecoder* CDVDVideoCodecFFmpeg::IHardwareDecoder::Acquire()
123 InterlockedIncrement(&m_references);
127 long CDVDVideoCodecFFmpeg::IHardwareDecoder::Release()
129 long count = InterlockedDecrement(&m_references);
131 if (count == 0) delete this;
135 CDVDVideoCodecFFmpeg::CDVDVideoCodecFFmpeg() : CDVDVideoCodec()
137 m_pCodecContext = NULL;
138 m_pConvertFrame = NULL;
142 m_iPictureHeight = 0;
149 m_dts = DVD_NOPTS_VALUE;
153 CDVDVideoCodecFFmpeg::~CDVDVideoCodecFFmpeg()
158 bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
162 if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllSwScale.Load()) return false;
164 m_dllAvCodec.avcodec_register_all();
166 #if (! defined USE_EXTERNAL_FFMPEG)
167 m_dllSwScale.sws_rgb2rgb_init(SWS_CPU_CAPS_MMX2);
168 #elif (defined HAVE_LIBSWSCALE_RGB2RGB_H) || (defined HAVE_FFMPEG_RGB2RGB_H)
169 m_dllSwScale.sws_rgb2rgb_init(SWS_CPU_CAPS_MMX2);
172 m_bSoftware = hints.software;
173 m_pCodecContext = m_dllAvCodec.avcodec_alloc_context();
178 if(g_guiSettings.GetBool("videoplayer.usevdpau") && !m_bSoftware)
180 while((pCodec = m_dllAvCodec.av_codec_next(pCodec)))
182 if(pCodec->id == hints.codec
183 && pCodec->capabilities & CODEC_CAP_HWACCEL_VDPAU)
185 if ((pCodec->id == CODEC_ID_MPEG4 || pCodec->id == CODEC_ID_XVID) && !g_advancedSettings.m_videoAllowMpeg4VDPAU)
188 CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Creating VDPAU(%ix%i, %d)",hints.width, hints.height, hints.codec);
189 CVDPAU* vdp = new CVDPAU();
190 m_pCodecContext->codec_id = hints.codec;
191 m_pCodecContext->width = hints.width;
192 m_pCodecContext->height = hints.height;
193 if(vdp->Open(m_pCodecContext, pCodec->pix_fmts ? pCodec->pix_fmts[0] : PIX_FMT_NONE))
196 m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set
199 m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set
200 CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Failed to get VDPAU device");
208 pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
212 CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to find codec %d", hints.codec);
216 CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Using codec: %s",pCodec->long_name ? pCodec->long_name : pCodec->name);
218 m_pCodecContext->opaque = (void*)this;
219 m_pCodecContext->debug_mv = 0;
220 m_pCodecContext->debug = 0;
221 m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT;
222 m_pCodecContext->get_format = GetFormat;
223 m_pCodecContext->codec_tag = hints.codec_tag;
225 if (pCodec->id != CODEC_ID_H264 && pCodec->capabilities & CODEC_CAP_DR1
226 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,69,0)
227 && pCodec->id != CODEC_ID_VP8
230 m_pCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
232 // if we don't do this, then some codecs seem to fail.
233 m_pCodecContext->coded_height = hints.height;
234 m_pCodecContext->coded_width = hints.width;
236 if( hints.extradata && hints.extrasize > 0 )
238 m_pCodecContext->extradata_size = hints.extrasize;
239 m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE);
240 memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize);
244 m_pCodecContext->dsp_mask = 0;//FF_MM_FORCE | FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE;
246 // advanced setting override for skip loop filter (see avcodec.h for valid options)
247 // TODO: allow per video setting?
248 if (g_advancedSettings.m_iSkipLoopFilter != 0)
250 m_pCodecContext->skip_loop_filter = (AVDiscard)g_advancedSettings.m_iSkipLoopFilter;
253 // set any special options
254 for(CDVDCodecOptions::iterator it = options.begin(); it != options.end(); it++)
256 m_dllAvCodec.av_set_string(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str());
259 #if defined(_LINUX) || defined(_WIN32)
260 int num_threads = std::min(8 /*MAX_THREADS*/, g_cpuInfo.getCPUCount());
261 if( num_threads > 1 && !hints.software && m_pHardware == NULL // thumbnail extraction fails when run threaded
262 && ( pCodec->id == CODEC_ID_H264
263 || pCodec->id == CODEC_ID_MPEG4 ))
264 m_dllAvCodec.avcodec_thread_init(m_pCodecContext, num_threads);
267 if (m_dllAvCodec.avcodec_open(m_pCodecContext, pCodec) < 0)
269 CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
273 m_pFrame = m_dllAvCodec.avcodec_alloc_frame();
274 if (!m_pFrame) return false;
277 m_name = CStdString("ff-") + pCodec->name;
282 m_name += "-" + m_pHardware->Name();
287 void CDVDVideoCodecFFmpeg::Dispose()
289 if (m_pFrame) m_dllAvUtil.av_free(m_pFrame);
294 m_dllAvCodec.avpicture_free(m_pConvertFrame);
295 m_dllAvUtil.av_free(m_pConvertFrame);
297 m_pConvertFrame = NULL;
301 if (m_pCodecContext->codec) m_dllAvCodec.avcodec_close(m_pCodecContext);
302 if (m_pCodecContext->extradata)
304 m_dllAvUtil.av_free(m_pCodecContext->extradata);
305 m_pCodecContext->extradata = NULL;
306 m_pCodecContext->extradata_size = 0;
308 m_dllAvUtil.av_free(m_pCodecContext);
309 m_pCodecContext = NULL;
311 SAFE_RELEASE(m_pHardware);
313 m_dllAvCodec.Unload();
314 m_dllAvUtil.Unload();
317 void CDVDVideoCodecFFmpeg::SetDropState(bool bDrop)
319 if( m_pCodecContext )
321 // i don't know exactly how high this should be set
322 // couldn't find any good docs on it. think it varies
323 // from codec to codec on what it does
325 // 2 seem to be to high.. it causes video to be ruined on following images
328 // TODO: 'hurry_up' has been deprecated in favor of the skip_* variables
329 // Use those instead.
331 m_pCodecContext->hurry_up = 1;
332 //m_pCodecContext->skip_frame = AVDISCARD_NONREF;
333 //m_pCodecContext->skip_idct = AVDISCARD_NONREF;
334 //m_pCodecContext->skip_loop_filter = AVDISCARD_NONREF;
338 m_pCodecContext->hurry_up = 0;
339 //m_pCodecContext->skip_frame = AVDISCARD_DEFAULT;
340 //m_pCodecContext->skip_idct = AVDISCARD_DEFAULT;
341 //m_pCodecContext->skip_loop_filter = AVDISCARD_DEFAULT;
352 static int64_t pts_dtoi(double pts)
359 static double pts_itod(int64_t pts)
366 int CDVDVideoCodecFFmpeg::Decode(BYTE* pData, int iSize, double dts, double pts)
368 int iGotPicture = 0, len = 0;
370 if (!m_pCodecContext)
376 shared_ptr<CSingleLock> lock;
379 CCriticalSection* section = m_pHardware->Section();
381 lock = shared_ptr<CSingleLock>(new CSingleLock(*section));
385 result = m_pHardware->Check(m_pCodecContext);
387 result = m_pHardware->Decode(m_pCodecContext, NULL);
394 m_pCodecContext->reordered_opaque = pts_dtoi(pts);
396 len = m_dllAvCodec.avcodec_decode_video(m_pCodecContext, m_pFrame, &iGotPicture, pData, iSize);
398 if(m_iLastKeyframe < m_pCodecContext->has_b_frames + 1)
399 m_iLastKeyframe = m_pCodecContext->has_b_frames + 1;
403 CLog::Log(LOGERROR, "%s - avcodec_decode_video returned failure", __FUNCTION__);
407 if (len != iSize && !m_pCodecContext->hurry_up)
408 CLog::Log(LOGWARNING, "%s - avcodec_decode_video didn't consume the full packet. size: %d, consumed: %d", __FUNCTION__, iSize, len);
413 if(m_pFrame->key_frame)
416 m_iLastKeyframe = m_pCodecContext->has_b_frames + 1;
419 if(m_pCodecContext->pix_fmt != PIX_FMT_YUV420P
420 && m_pCodecContext->pix_fmt != PIX_FMT_YUVJ420P
421 && m_pHardware == NULL)
423 if (!m_dllSwScale.IsLoaded())
425 if(!m_dllSwScale.Load())
428 #if (! defined USE_EXTERNAL_FFMPEG)
429 m_dllSwScale.sws_rgb2rgb_init(SWS_CPU_CAPS_MMX2);
430 #elif (defined HAVE_LIBSWSCALE_RGB2RGB_H) || (defined HAVE_FFMPEG_RGB2RGB_H)
431 m_dllSwScale.sws_rgb2rgb_init(SWS_CPU_CAPS_MMX2);
435 if (!m_pConvertFrame)
437 // Allocate an AVFrame structure
438 m_pConvertFrame = (AVPicture*)m_dllAvUtil.av_mallocz(sizeof(AVPicture));
439 // Due to a bug in swsscale we need to allocate one extra line of data
440 if(m_dllAvCodec.avpicture_alloc( m_pConvertFrame
442 , m_pCodecContext->width
443 , m_pCodecContext->height+1) < 0)
445 m_dllAvUtil.av_free(m_pConvertFrame);
446 m_pConvertFrame = NULL;
451 // convert the picture
452 struct SwsContext *context = m_dllSwScale.sws_getContext(m_pCodecContext->width, m_pCodecContext->height,
453 m_pCodecContext->pix_fmt, m_pCodecContext->width, m_pCodecContext->height,
454 PIX_FMT_YUV420P, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL);
458 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::Decode - unable to obtain sws context for w:%i, h:%i, pixfmt: %i", m_pCodecContext->width, m_pCodecContext->height, m_pCodecContext->pix_fmt);
462 m_dllSwScale.sws_scale(context
466 , m_pCodecContext->height
467 , m_pConvertFrame->data
468 , m_pConvertFrame->linesize);
470 m_dllSwScale.sws_freeContext(context);
474 // no need to convert, just free any existing convert buffers
477 m_dllAvCodec.avpicture_free(m_pConvertFrame);
478 m_dllAvUtil.av_free(m_pConvertFrame);
479 m_pConvertFrame = NULL;
485 result = m_pHardware->Decode(m_pCodecContext, m_pFrame);
487 result = VC_PICTURE | VC_BUFFER;
489 if(result & VC_FLUSHED)
495 void CDVDVideoCodecFFmpeg::Reset()
498 m_iLastKeyframe = m_pCodecContext->has_b_frames;
499 m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);
503 delete[] m_pConvertFrame->data[0];
504 m_dllAvUtil.av_free(m_pConvertFrame);
505 m_pConvertFrame = NULL;
509 bool CDVDVideoCodecFFmpeg::GetPicture(DVDVideoPicture* pDvdVideoPicture)
511 GetVideoAspect(m_pCodecContext, pDvdVideoPicture->iDisplayWidth, pDvdVideoPicture->iDisplayHeight);
513 if(m_pCodecContext->coded_width && m_pCodecContext->coded_width < m_pCodecContext->width
514 && m_pCodecContext->coded_width > m_pCodecContext->width - 10)
515 pDvdVideoPicture->iWidth = m_pCodecContext->coded_width;
517 pDvdVideoPicture->iWidth = m_pCodecContext->width;
519 if(m_pCodecContext->coded_height && m_pCodecContext->coded_height < m_pCodecContext->height
520 && m_pCodecContext->coded_height > m_pCodecContext->height - 10)
521 pDvdVideoPicture->iHeight = m_pCodecContext->coded_height;
523 pDvdVideoPicture->iHeight = m_pCodecContext->height;
525 pDvdVideoPicture->pts = DVD_NOPTS_VALUE;
527 // if we have a converted frame, use that
528 AVFrame *frame = m_pFrame;
533 pDvdVideoPicture->iRepeatPicture = 0.5 * frame->repeat_pict;
534 pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED;
535 pDvdVideoPicture->iFlags |= frame->interlaced_frame ? DVP_FLAG_INTERLACED : 0;
536 pDvdVideoPicture->iFlags |= frame->top_field_first ? DVP_FLAG_TOP_FIELD_FIRST: 0;
537 if(m_pCodecContext->pix_fmt == PIX_FMT_YUVJ420P)
538 pDvdVideoPicture->color_range = 1;
540 pDvdVideoPicture->qscale_table = frame->qscale_table;
541 pDvdVideoPicture->qscale_stride = frame->qstride;
543 switch (frame->qscale_type) {
544 case FF_QSCALE_TYPE_MPEG1:
545 pDvdVideoPicture->qscale_type = DVP_QSCALE_MPEG1;
547 case FF_QSCALE_TYPE_MPEG2:
548 pDvdVideoPicture->qscale_type = DVP_QSCALE_MPEG2;
550 case FF_QSCALE_TYPE_H264:
551 pDvdVideoPicture->qscale_type = DVP_QSCALE_H264;
554 pDvdVideoPicture->qscale_type = DVP_QSCALE_UNKNOWN;
557 pDvdVideoPicture->dts = m_dts;
558 m_dts = DVD_NOPTS_VALUE;
559 if (frame->reordered_opaque)
560 pDvdVideoPicture->pts = pts_itod(frame->reordered_opaque);
562 pDvdVideoPicture->pts = DVD_NOPTS_VALUE;
565 pDvdVideoPicture->iFlags |= DVP_FLAG_DROPPED;
568 return m_pHardware->GetPicture(m_pCodecContext, m_pFrame, pDvdVideoPicture);
572 for (int i = 0; i < 4; i++)
573 pDvdVideoPicture->data[i] = m_pConvertFrame->data[i];
574 for (int i = 0; i < 4; i++)
575 pDvdVideoPicture->iLineSize[i] = m_pConvertFrame->linesize[i];
579 for (int i = 0; i < 4; i++)
580 pDvdVideoPicture->data[i] = frame->data[i];
581 for (int i = 0; i < 4; i++)
582 pDvdVideoPicture->iLineSize[i] = frame->linesize[i];
585 pDvdVideoPicture->iFlags |= pDvdVideoPicture->data[0] ? 0 : DVP_FLAG_DROPPED;
586 pDvdVideoPicture->format = DVDVideoPicture::FMT_YUV420P;
592 * Calculate the height and width this video should be displayed in
594 void CDVDVideoCodecFFmpeg::GetVideoAspect(AVCodecContext* pCodecContext, unsigned int& iWidth, unsigned int& iHeight)
598 /* XXX: use variable in the frame */
599 if (pCodecContext->sample_aspect_ratio.num == 0) aspect_ratio = 0;
600 else aspect_ratio = av_q2d(pCodecContext->sample_aspect_ratio) * pCodecContext->width / pCodecContext->height;
602 if (aspect_ratio <= 0.0) aspect_ratio = (float)pCodecContext->width / (float)pCodecContext->height;
604 /* XXX: we suppose the screen has a 1.0 pixel ratio */ // CDVDVideo will compensate it.
605 iHeight = pCodecContext->height;
606 iWidth = ((int)RINT(pCodecContext->height * aspect_ratio)) & -3;
607 if (iWidth > (unsigned int)pCodecContext->width)
609 iWidth = pCodecContext->width;
610 iHeight = ((int)RINT(pCodecContext->width / aspect_ratio)) & -3;
614 unsigned CDVDVideoCodecFFmpeg::GetConvergeCount()
617 return m_iLastKeyframe;