Esempio n. 1
0
bool CDVDVideoCodecVDA::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
{
  if (CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOPLAYER_USEVDA) && !hints.software)
  {
    CCocoaAutoPool pool;

    //
    int width  = hints.width;
    int height = hints.height;
    int level  = hints.level;
    int profile = hints.profile;
    
    switch(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:
        CLog::Log(LOGNOTICE, "%s - unsupported h264 profile(%d)", __FUNCTION__, hints.profile);
        return false;
        break;
    }

    if (width <= 0 || height <= 0)
    {
      CLog::Log(LOGNOTICE, "%s - bailing with bogus hints, width(%d), height(%d)",
        __FUNCTION__, width, height);
      return false;
    }
    
    if (Cocoa_GPUForDisplayIsNvidiaPureVideo3() && !CDVDCodecUtils::IsVP3CompatibleWidth(width))
    {
      CLog::Log(LOGNOTICE, "%s - Nvidia 9400 GPU hardware limitation, cannot decode a width of %d", __FUNCTION__, width);
      return false;
    }

    CFDataRef avcCData;
    switch (hints.codec)
    {
      case AV_CODEC_ID_H264:
        m_bitstream = new CBitstreamConverter;
        if (!m_bitstream->Open(hints.codec, (uint8_t*)hints.extradata, hints.extrasize, false))
          return false;

        avcCData = CFDataCreate(kCFAllocatorDefault,
          (const uint8_t*)m_bitstream->GetExtraData(), m_bitstream->GetExtraSize());

        m_format = 'avc1';
        m_pFormatName = "vda-h264";
      break;
      default:
        return false;
      break;
    }

    // check the avcC atom's sps for number of reference frames and
    // bail if interlaced, VDA does not handle interlaced h264.
    uint32_t avcc_len = CFDataGetLength(avcCData);
    if (avcc_len < 8)
    {
      // avcc atoms with length less than 8 are borked.
      CFRelease(avcCData);
      delete m_bitstream, m_bitstream = NULL;
      return false;
    }
    else
    {
      bool interlaced = true;
      uint8_t *spc = (uint8_t*)CFDataGetBytePtr(avcCData) + 6;
      uint32_t sps_size = BS_RB16(spc);
      if (sps_size)
        m_bitstream->parseh264_sps(spc+3, sps_size-1, &interlaced, &m_max_ref_frames);
      if (interlaced)
      {
        CLog::Log(LOGNOTICE, "%s - possible interlaced content.", __FUNCTION__);
        CFRelease(avcCData);
        return false;
      }
    }

    if (profile == FF_PROFILE_H264_MAIN && level == 32 && m_max_ref_frames > 4)
    {
      // [email protected], VDA cannot handle greater than 4 reference frames
      CLog::Log(LOGNOTICE, "%s - [email protected] detected, VDA cannot decode.", __FUNCTION__);
      CFRelease(avcCData);
      return false;
    }

    std::string rendervendor = g_Windowing.GetRenderVendor();
    StringUtils::ToLower(rendervendor);
    if (rendervendor.find("nvidia") != std::string::npos)
    {
      // Nvidia gpu's are all powerful and work the way god intended
      m_decode_async = true;
      // The gods are liars, ignore the sirens for now.
      m_use_cvBufferRef = false;
    }
    else if (rendervendor.find("intel") != std::string::npos)
    {
      // Intel gpu are borked when using cvBufferRef
      m_decode_async = true;
      m_use_cvBufferRef = false;
    }
    else
    {
      // ATI gpu's are borked when using async decode
      m_decode_async = false;
      // They lie here too.
      m_use_cvBufferRef = false;
    }

    if (!m_use_cvBufferRef)
    {
      // allocate a YV12 DVDVideoPicture buffer.
      // first make sure all properties are reset.
      memset(&m_videobuffer, 0, sizeof(DVDVideoPicture));
      unsigned int iPixels = width * height;
      unsigned int iChromaPixels = iPixels/4;

      m_videobuffer.dts = DVD_NOPTS_VALUE;
      m_videobuffer.pts = DVD_NOPTS_VALUE;
      m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
      m_videobuffer.format = RENDER_FMT_YUV420P;
      m_videobuffer.color_range  = 0;
      m_videobuffer.color_matrix = 4;
      m_videobuffer.iWidth  = width;
      m_videobuffer.iHeight = height;
      m_videobuffer.iDisplayWidth  = width;
      m_videobuffer.iDisplayHeight = height;

      m_videobuffer.iLineSize[0] = width;   //Y
      m_videobuffer.iLineSize[1] = width/2; //U
      m_videobuffer.iLineSize[2] = width/2; //V
      m_videobuffer.iLineSize[3] = 0;

      m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
      m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
      m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
      m_videobuffer.data[3] = NULL;

      // set all data to 0 for less artifacts.. hmm.. what is black in YUV??
      memset(m_videobuffer.data[0], 0, iPixels);
      memset(m_videobuffer.data[1], 0, iChromaPixels);
      memset(m_videobuffer.data[2], 0, iChromaPixels);
    }

    // setup the decoder configuration dict
    CFMutableDictionaryRef decoderConfiguration = CFDictionaryCreateMutable(
      kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFNumberRef avcWidth  = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width);
    CFNumberRef avcHeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height);
    CFNumberRef avcFormat = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &m_format);

    CFDictionarySetValue(decoderConfiguration, kVDADecoderConfiguration_Height, avcHeight);
    CFDictionarySetValue(decoderConfiguration, kVDADecoderConfiguration_Width,  avcWidth);
    CFDictionarySetValue(decoderConfiguration, kVDADecoderConfiguration_SourceFormat, avcFormat);
    CFDictionarySetValue(decoderConfiguration, kVDADecoderConfiguration_avcCData, avcCData);

    // release the retained object refs, decoderConfiguration owns them now
    CFRelease(avcWidth);
    CFRelease(avcHeight);
    CFRelease(avcFormat);
    CFRelease(avcCData);

    // setup the destination image buffer dict, vda will output this pict format
    CFMutableDictionaryRef destinationImageBufferAttributes = CFDictionaryCreateMutable(
      kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    OSType cvPixelFormatType = kCVPixelFormatType_422YpCbCr8;
    CFNumberRef pixelFormat  = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &cvPixelFormatType);

    // an IOSurface properties dictionary
    CFDictionaryRef iosurfaceDictionary = CFDictionaryCreate(kCFAllocatorDefault,
      NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFDictionarySetValue(destinationImageBufferAttributes,
      kCVPixelBufferPixelFormatTypeKey, pixelFormat);
    CFDictionarySetValue(destinationImageBufferAttributes,
      kCVPixelBufferIOSurfacePropertiesKey, iosurfaceDictionary);
    // release the retained object refs, destinationImageBufferAttributes owns them now
    CFRelease(pixelFormat);
    CFRelease(iosurfaceDictionary);

    // create the VDADecoder object
    OSStatus status;
    try
    {
      status = VDADecoderCreate(decoderConfiguration, destinationImageBufferAttributes,
        (VDADecoderOutputCallback*)VDADecoderCallback, this, (VDADecoder*)&m_vda_decoder);
    }
    catch (...)
    {
      CLog::Log(LOGERROR, "%s - exception",__FUNCTION__);
      status = kVDADecoderDecoderFailedErr;
    }
    CFRelease(decoderConfiguration);
    CFRelease(destinationImageBufferAttributes);
    if (CDarwinUtils::DeviceHasLeakyVDA())
      CFRelease(pixelFormat);
    if (status != kVDADecoderNoErr)
    {
      if (status == kVDADecoderDecoderFailedErr)
          CLog::Log(LOGNOTICE, "%s - VDADecoder Codec failed to open, currently in use by another process",
            __FUNCTION__);
      else
          CLog::Log(LOGNOTICE, "%s - VDADecoder Codec failed to open, status(%d), profile(%d), level(%d)",
            __FUNCTION__, (int)status, profile, level);
      return false;
    }

    m_DropPictures = false;
    m_max_ref_frames = std::max(m_max_ref_frames + 1, 5);
    m_sort_time = 0;
    return true;
  }

  return false;
}
Esempio n. 2
0
bool CDVDVideoCodecVDA::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
{
  if (g_guiSettings.GetBool("videoplayer.usevda") && !hints.software)
  {
    CCocoaAutoPool pool;
    int32_t width, height, profile, level;
    CFDataRef avcCData;
    uint8_t *extradata; // extra data for codec to use
    unsigned int extrasize; // size of extra data

    //
    width  = hints.width;
    height = hints.height;
    level  = hints.level;
    profile = hints.profile;
    extrasize = hints.extrasize;
    extradata = (uint8_t*)hints.extradata;
 
    if (Cocoa_GPUForDisplayIsNvidiaPureVideo3() && !CDVDCodecUtils::IsVP3CompatibleWidth(width))
    {
      CLog::Log(LOGNOTICE, "%s - Nvidia 9400 GPU hardware limitation, cannot decode a width of %d", __FUNCTION__, width);
      return false;
    }

    switch (hints.codec)
    {
      case CODEC_ID_H264:
        // TODO: need to quality h264 encoding (profile, level and number of reference frame)
        // source must be H.264 with valid avcC atom data in extradata
        if (extrasize < 7 || extradata == NULL)
        {
          CLog::Log(LOGNOTICE, "%s - avcC atom too data small or missing", __FUNCTION__);
          return false;
        }
        // valid avcC atom data always starts with the value 1 (version)
        if ( *extradata != 1 )
        {
          if (extradata[0] == 0 && extradata[1] == 0 && extradata[2] == 0 && extradata[3] == 1)
          {
            // video content is from x264 or from bytestream h264 (AnnexB format)
            // NAL reformating to bitstream format needed
            m_dllAvUtil = new DllAvUtil;
            m_dllAvFormat = new DllAvFormat;
            if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load())
            {
              return false;
            }

            ByteIOContext *pb;
            if (m_dllAvFormat->url_open_dyn_buf(&pb) < 0)
            {
              return false;
            }

            m_convert_bytestream = true;
            // create a valid avcC atom data from ffmpeg's extradata
            isom_write_avcc(m_dllAvUtil, m_dllAvFormat, pb, extradata, extrasize);
            // unhook from ffmpeg's extradata
            extradata = NULL;
            // extract the avcC atom data into extradata then write it into avcCData for VDADecoder
            extrasize = m_dllAvFormat->url_close_dyn_buf(pb, &extradata);
            // CFDataCreate makes a copy of extradata contents
            avcCData = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)extradata, extrasize);
            // done with the converted extradata, we MUST free using av_free
            m_dllAvUtil->av_free(extradata);
          }
          else
          {
            CLog::Log(LOGNOTICE, "%s - invalid avcC atom data", __FUNCTION__);
            return false;
          }
        }
        else
        {
          if (extradata[4] == 0xFE)
          {
            // video content is from so silly encoder that think 3 byte NAL sizes
            // are valid, setup to convert 3 byte NAL sizes to 4 byte.
            m_dllAvUtil = new DllAvUtil;
            m_dllAvFormat = new DllAvFormat;
            if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load())
              return false;

            extradata[4] = 0xFF;
            m_convert_3byteTo4byteNALSize = true;
          }
          // CFDataCreate makes a copy of extradata contents
          avcCData = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)extradata, extrasize);
        }

        m_format = 'avc1';
        m_pFormatName = "vda-h264";
      break;
      default:
        return false;
      break;
    }

    // input stream is qualified, now we can load dlls.
    m_dllSwScale = new DllSwScale;
    if (!m_dllSwScale->Load())
    {
      CFRelease(avcCData);
      return false;
    }

    // setup the decoder configuration dict
    CFMutableDictionaryRef decoderConfiguration = CFDictionaryCreateMutable(
      kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFNumberRef avcWidth  = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width);
    CFNumberRef avcHeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height);
    CFNumberRef avcFormat = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &m_format);

    CFDictionarySetValue(decoderConfiguration, m_dll->Get_kVDADecoderConfiguration_Height(), avcHeight);
    CFDictionarySetValue(decoderConfiguration, m_dll->Get_kVDADecoderConfiguration_Width(),  avcWidth);
    CFDictionarySetValue(decoderConfiguration, m_dll->Get_kVDADecoderConfiguration_SourceFormat(), avcFormat);
    CFDictionarySetValue(decoderConfiguration, m_dll->Get_kVDADecoderConfiguration_avcCData(), avcCData);

    // release the retained object refs, decoderConfiguration owns them now
    CFRelease(avcWidth);
    CFRelease(avcHeight);
    CFRelease(avcFormat);
    CFRelease(avcCData);

    // setup the destination image buffer dict, vda will output this pict format
    CFMutableDictionaryRef destinationImageBufferAttributes = CFDictionaryCreateMutable(
      kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    OSType cvPixelFormatType = kCVPixelFormatType_422YpCbCr8;
    CFNumberRef pixelFormat  = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &cvPixelFormatType);
    CFDictionarySetValue(destinationImageBufferAttributes, kCVPixelBufferPixelFormatTypeKey, pixelFormat);

    // create the VDADecoder object
    OSStatus status;
    try
    {
      status = m_dll->VDADecoderCreate(decoderConfiguration, destinationImageBufferAttributes,
        (VDADecoderOutputCallback *)VDADecoderCallback, this, (VDADecoder*)&m_vda_decoder);
    }
    catch (...)
    {
      CLog::Log(LOGERROR, "%s - exception",__FUNCTION__);
      status = kVDADecoderDecoderFailedErr;
    }
    CFRelease(decoderConfiguration);
    CFRelease(destinationImageBufferAttributes);
    if (status != kVDADecoderNoErr)
    {
	  if (status == kVDADecoderDecoderFailedErr)
        CLog::Log(LOGNOTICE, "%s - VDADecoder Codec failed to open, currently in use by another process",
          __FUNCTION__);
	  else
        CLog::Log(LOGNOTICE, "%s - VDADecoder Codec failed to open, status(%d), profile(%d), level(%d)",
          __FUNCTION__, (int)status, profile, level);
      return false;
    }

    // allocate a YV12 DVDVideoPicture buffer.
    // first make sure all properties are reset.
    memset(&m_videobuffer, 0, sizeof(DVDVideoPicture));
    unsigned int iPixels = width * height;
    unsigned int iChromaPixels = iPixels/4;

    m_videobuffer.pts = DVD_NOPTS_VALUE;
    m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
    m_videobuffer.format = DVDVideoPicture::FMT_YUV420P;
    m_videobuffer.color_range  = 0;
    m_videobuffer.color_matrix = 4;
    m_videobuffer.iWidth  = width;
    m_videobuffer.iHeight = height;
    m_videobuffer.iDisplayWidth  = width;
    m_videobuffer.iDisplayHeight = height;

    m_videobuffer.iLineSize[0] = width;   //Y
    m_videobuffer.iLineSize[1] = width/2; //U
    m_videobuffer.iLineSize[2] = width/2; //V
    m_videobuffer.iLineSize[3] = 0;

    m_videobuffer.data[0] = (BYTE*)malloc(iPixels);       //Y
    m_videobuffer.data[1] = (BYTE*)malloc(iChromaPixels); //U
    m_videobuffer.data[2] = (BYTE*)malloc(iChromaPixels); //V
    m_videobuffer.data[3] = NULL;

    // set all data to 0 for less artifacts.. hmm.. what is black in YUV??
    memset(m_videobuffer.data[0], 0, iPixels);
    memset(m_videobuffer.data[1], 0, iChromaPixels);
    memset(m_videobuffer.data[2], 0, iChromaPixels);

    m_DropPictures = false;
    m_sort_time_offset = (CurrentHostCounter() * 1000.0) / CurrentHostFrequency();

    return true;
  }

  return false;
}