bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) { AVCodec* pCodec; if(!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllSwScale.Load() || !m_dllAvFilter.Load() ) return false; m_dllAvCodec.avcodec_register_all(); m_dllAvFilter.avfilter_register_all(); m_bSoftware = hints.software; m_pCodecContext = m_dllAvCodec.avcodec_alloc_context(); pCodec = NULL; if (hints.codec == CODEC_ID_H264) { switch(hints.profile) { case FF_PROFILE_H264_HIGH_10: case FF_PROFILE_H264_HIGH_10_INTRA: case FF_PROFILE_H264_HIGH_422: case FF_PROFILE_H264_HIGH_422_INTRA: case FF_PROFILE_H264_HIGH_444_PREDICTIVE: case FF_PROFILE_H264_HIGH_444_INTRA: case FF_PROFILE_H264_CAVLC_444: m_bSoftware = true; break; } } #ifdef HAVE_LIBVDPAU if(g_guiSettings.GetBool("videoplayer.usevdpau") && !m_bSoftware) { while((pCodec = m_dllAvCodec.av_codec_next(pCodec))) { if(pCodec->id == hints.codec && pCodec->capabilities & CODEC_CAP_HWACCEL_VDPAU) { if ((pCodec->id == CODEC_ID_MPEG4) && !g_advancedSettings.m_videoAllowMpeg4VDPAU) continue; CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Creating VDPAU(%ix%i, %d)",hints.width, hints.height, hints.codec); VDPAU::CDecoder* vdp = new VDPAU::CDecoder(); m_pCodecContext->codec_id = hints.codec; m_pCodecContext->width = hints.width; m_pCodecContext->height = hints.height; m_pCodecContext->coded_width = hints.width; m_pCodecContext->coded_height = hints.height; // check number of surfaces used in renderer unsigned int surfaces = 0; for(CDVDCodecOptions::iterator it = options.begin(); it != options.end(); it++) { if (it->m_name == "surfaces") { surfaces = std::atoi(it->m_value.c_str()); break; } } if(vdp->Open(m_pCodecContext, pCodec->pix_fmts ? pCodec->pix_fmts[0] : PIX_FMT_NONE, surfaces)) { m_pHardware = vdp; m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set break; } m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Failed to get VDPAU device"); vdp->Release(); } } } #endif if(pCodec == NULL) pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec); if(pCodec == NULL) { CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to find codec %d", hints.codec); return false; } CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Using codec: %s",pCodec->long_name ? pCodec->long_name : pCodec->name); m_pCodecContext->opaque = (void*)this; m_pCodecContext->debug_mv = 0; m_pCodecContext->debug = 0; m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT; m_pCodecContext->get_format = GetFormat; m_pCodecContext->codec_tag = hints.codec_tag; #if defined(__APPLE__) && defined(__arm__) // ffmpeg with enabled neon will crash and burn if this is enabled m_pCodecContext->flags &= CODEC_FLAG_EMU_EDGE; #else if (pCodec->id != CODEC_ID_H264 && pCodec->capabilities & CODEC_CAP_DR1 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,69,0) && pCodec->id != CODEC_ID_VP8 #endif ) m_pCodecContext->flags |= CODEC_FLAG_EMU_EDGE; #endif // if we don't do this, then some codecs seem to fail. m_pCodecContext->coded_height = hints.height; m_pCodecContext->coded_width = hints.width; if( hints.extradata && hints.extrasize > 0 ) { m_pCodecContext->extradata_size = hints.extrasize; m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE); memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize); } // set acceleration m_pCodecContext->dsp_mask = 0;//FF_MM_FORCE | FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE; // advanced setting override for skip loop filter (see avcodec.h for valid options) // TODO: allow per video setting? if (g_advancedSettings.m_iSkipLoopFilter != 0) { m_pCodecContext->skip_loop_filter = (AVDiscard)g_advancedSettings.m_iSkipLoopFilter; } // set any special options for(CDVDCodecOptions::iterator it = options.begin(); it != options.end(); it++) { if (it->m_name == "surfaces") m_uSurfacesCount = std::atoi(it->m_value.c_str()); else m_dllAvUtil.av_set_string3(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str(), 0, NULL); } int num_threads = std::min(8 /*MAX_THREADS*/, g_cpuInfo.getCPUCount()); if( num_threads > 1 && !hints.software && m_pHardware == NULL // thumbnail extraction fails when run threaded && ( pCodec->id == CODEC_ID_H264 || pCodec->id == CODEC_ID_MPEG4 )) m_dllAvCodec.avcodec_thread_init(m_pCodecContext, num_threads); if (m_dllAvCodec.avcodec_open(m_pCodecContext, pCodec) < 0) { CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec"); return false; } m_pFrame = m_dllAvCodec.avcodec_alloc_frame(); if (!m_pFrame) return false; if(pCodec->name) m_name = CStdString("ff-") + pCodec->name; else m_name = "ffmpeg"; if(m_pHardware) m_name += "-" + m_pHardware->Name(); return true; }
CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigned int surfaces) { CDVDVideoCodec* pCodec = NULL; CDVDCodecOptions options; //when support for a hardware decoder is not compiled in //only print it if it's actually available on the platform CStdString hwSupport; #if defined(HAVE_LIBVDADECODER) && defined(__APPLE__) hwSupport += "VDADecoder:yes "; #elif defined(__APPLE__) hwSupport += "VDADecoder:no "; #endif #if defined(HAVE_VIDEOTOOLBOXDECODER) && defined(__APPLE__) hwSupport += "VideoToolBoxDecoder:yes "; #elif defined(__APPLE__) hwSupport += "VideoToolBoxDecoder:no "; #endif #ifdef HAVE_LIBCRYSTALHD hwSupport += "CrystalHD:yes "; #else hwSupport += "CrystalHD:no "; #endif #if defined(HAVE_LIBOPENMAX) && defined(_LINUX) hwSupport += "OpenMax:yes "; #elif defined(_LINUX) hwSupport += "OpenMax:no "; #endif #if defined(HAVE_LIBVDPAU) && defined(_LINUX) hwSupport += "VDPAU:yes "; #elif defined(_LINUX) && !defined(__APPLE__) hwSupport += "VDPAU:no "; #endif #if defined(_WIN32) && defined(HAS_DX) hwSupport += "DXVA:yes "; #elif defined(_WIN32) hwSupport += "DXVA:no "; #endif #if defined(HAVE_LIBVA) && defined(_LINUX) hwSupport += "VAAPI:yes "; #elif defined(_LINUX) && !defined(__APPLE__) hwSupport += "VAAPI:no "; #endif #if defined(HAVE_LIBGSTREAMER) hwSupport += "GStreamer:yes "; #else hwSupport += "GStreamer:no "; #endif CLog::Log(LOGDEBUG, "CDVDFactoryCodec: compiled in hardware support: %s", hwSupport.c_str()); #if defined(HAVE_LIBGSTREAMER) if (!hint.software) { CLog::Log(LOGINFO, "Trying GStreamer Video Decoder..."); if ( (pCodec = OpenCodec(new CDVDVideoCodecGStreamer(), hint, options)) ) return pCodec; } #endif // dvd's have weird still-frames in it, which is not fully supported in ffmpeg if(hint.stills && (hint.codec == CODEC_ID_MPEG2VIDEO || hint.codec == CODEC_ID_MPEG1VIDEO)) { if( (pCodec = OpenCodec(new CDVDVideoCodecLibMpeg2(), hint, options)) ) return pCodec; } #if defined(HAVE_LIBVDADECODER) if (!hint.software && g_guiSettings.GetBool("videoplayer.usevda")) { if (g_sysinfo.HasVDADecoder()) { if (hint.codec == CODEC_ID_H264 && !hint.ptsinvalid) { CLog::Log(LOGINFO, "Trying Apple VDA Decoder..."); if ( (pCodec = OpenCodec(new CDVDVideoCodecVDA(), hint, options)) ) return pCodec; } } } #endif #if defined(HAVE_VIDEOTOOLBOXDECODER) if (!hint.software && g_guiSettings.GetBool("videoplayer.usevideotoolbox")) { if (g_sysinfo.HasVideoToolBoxDecoder()) { switch(hint.codec) { case CODEC_ID_H264: if (hint.codec == CODEC_ID_H264 && hint.ptsinvalid) break; CLog::Log(LOGINFO, "Apple VideoToolBox Decoder..."); if ( (pCodec = OpenCodec(new CDVDVideoCodecVideoToolBox(), hint, options)) ) return pCodec; break; default: break; } } } #endif #if defined(HAVE_LIBCRYSTALHD) if (!hint.software && g_guiSettings.GetBool("videoplayer.usechd")) { if (CCrystalHD::GetInstance()->DevicePresent()) { switch(hint.codec) { case CODEC_ID_VC1: case CODEC_ID_WMV3: case CODEC_ID_H264: case CODEC_ID_MPEG2VIDEO: if (hint.codec == CODEC_ID_H264 && hint.ptsinvalid) break; if (hint.codec == CODEC_ID_MPEG2VIDEO && hint.width <= 720) break; CLog::Log(LOGINFO, "Trying Broadcom Crystal HD Decoder..."); if ( (pCodec = OpenCodec(new CDVDVideoCodecCrystalHD(), hint, options)) ) return pCodec; break; default: break; } } } #endif #if defined(HAVE_LIBOPENMAX) if (g_guiSettings.GetBool("videoplayer.useomx") && !hint.software ) { if (hint.codec == CODEC_ID_H264 || hint.codec == CODEC_ID_MPEG2VIDEO || hint.codec == CODEC_ID_VC1) { CLog::Log(LOGINFO, "Trying OpenMax Decoder..."); if ( (pCodec = OpenCodec(new CDVDVideoCodecOpenMax(), hint, options)) ) return pCodec; } } #endif // try to decide if we want to try halfres decoding #if !defined(_LINUX) && !defined(_WIN32) float pixelrate = (float)hint.width*hint.height*hint.fpsrate/hint.fpsscale; if( pixelrate > 1400.0f*720.0f*30.0f ) { CLog::Log(LOGINFO, "CDVDFactoryCodec - High video resolution detected %dx%d, trying half resolution decoding ", hint.width, hint.height); options.push_back(CDVDCodecOption("lowres","1")); } #endif CStdString value; value.Format("%d", surfaces); options.push_back(CDVDCodecOption("surfaces", value)); if( (pCodec = OpenCodec(new CDVDVideoCodecFFmpeg(), hint, options)) ) return pCodec; return NULL; }
bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) { AVCodec* pCodec; if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllSwScale.Load()) return false; m_dllSwScale.sws_rgb2rgb_init(SWS_CPU_CAPS_MMX2); m_pCodecContext = m_dllAvCodec.avcodec_alloc_context(); // avcodec_get_context_defaults(m_pCodecContext); pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec); if (!pCodec) { CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to find codec %d", hints.codec); return false; } m_pCodecContext->opaque = (void*)this; m_pCodecContext->get_buffer = my_get_buffer; m_pCodecContext->release_buffer = my_release_buffer; m_pCodecContext->debug_mv = 0; m_pCodecContext->debug = 0; m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT; /* some decoders (eg. dv) do not know the pix_fmt until they decode the * first frame. setting to -1 avoid enabling DR1 for them. */ m_pCodecContext->pix_fmt = (PixelFormat) - 1; if (pCodec->id != CODEC_ID_H264 && pCodec->capabilities & CODEC_CAP_DR1) m_pCodecContext->flags |= CODEC_FLAG_EMU_EDGE; // Hack to correct wrong frame rates that seem to be generated by some // codecs if (m_pCodecContext->time_base.den > 1000 && m_pCodecContext->time_base.num == 1) m_pCodecContext->time_base.num = 1000; // if we don't do this, then some codecs seem to fail. m_pCodecContext->coded_height = hints.height; m_pCodecContext->coded_width = hints.width; if( hints.extradata && hints.extrasize > 0 ) { m_pCodecContext->extradata_size = hints.extrasize; m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE); memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize); } // set acceleration m_pCodecContext->dsp_mask = FF_MM_FORCE | FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE; // This doesn't seem to help with the H.264 MT patch. "Basically, the code decodes // up to 128 macroblocks in one thread while doing prediction+idct+deblock of the // previously decoded 128 blocks in another thread." This could explain the lack of // much difference. // //m_pCodecContext->skip_loop_filter = AVDISCARD_BIDIR; // set any special options for(CDVDCodecOptions::iterator it = options.begin(); it != options.end(); it++) { m_dllAvCodec.av_set_string(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str()); } #ifdef _LINUX int num_threads = std::min(8 /*MAX_THREADS*/, g_cpuInfo.getCPUCount()); if(num_threads > 1 && (pCodec->id == CODEC_ID_H264 || pCodec->id == CODEC_ID_MPEG4 || pCodec->id == CODEC_ID_MPEG2VIDEO)) m_dllAvCodec.avcodec_thread_init(m_pCodecContext, num_threads); #endif if (m_dllAvCodec.avcodec_open(m_pCodecContext, pCodec) < 0) { CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec"); return false; } m_pFrame = m_dllAvCodec.avcodec_alloc_frame(); if (!m_pFrame) return false; return true; }