DVDVideoPicture* CDVDCodecUtils::ConvertToYUV422PackedPicture(DVDVideoPicture *pSrc, ERenderFormat format) { // Clone a YV12 picture to new YUY2 or UYVY picture. DVDVideoPicture* pPicture = new DVDVideoPicture; if (pPicture) { *pPicture = *pSrc; int totalsize = pPicture->iWidth * pPicture->iHeight * 2; uint8_t* data = new uint8_t[totalsize]; if (data) { pPicture->data[0] = data; pPicture->data[1] = NULL; pPicture->data[2] = NULL; pPicture->data[3] = NULL; pPicture->iLineSize[0] = pPicture->iWidth * 2; pPicture->iLineSize[1] = 0; pPicture->iLineSize[2] = 0; pPicture->iLineSize[3] = 0; pPicture->format = format; //if this is going to be used for anything else than testing the renderer //the library should not be loaded on every function call DllSwScale dllSwScale; if (!dllSwScale.Load()) { CLog::Log(LOGERROR,"CDVDCodecUtils::ConvertToYUY2Picture - failed to load rescale libraries!"); } else { // Perform the scaling. uint8_t* src[] = { pSrc->data[0], pSrc->data[1], pSrc->data[2], NULL }; int srcStride[] = { pSrc->iLineSize[0], pSrc->iLineSize[1], pSrc->iLineSize[2], 0 }; uint8_t* dst[] = { pPicture->data[0], NULL, NULL, NULL }; int dstStride[] = { pPicture->iLineSize[0], 0, 0, 0 }; int dstformat; if (format == RENDER_FMT_UYVY422) dstformat = PIX_FMT_UYVY422; else dstformat = PIX_FMT_YUYV422; struct SwsContext *ctx = dllSwScale.sws_getContext(pSrc->iWidth, pSrc->iHeight, PIX_FMT_YUV420P, pPicture->iWidth, pPicture->iHeight, dstformat, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL); dllSwScale.sws_scale(ctx, src, srcStride, 0, pSrc->iHeight, dst, dstStride); dllSwScale.sws_freeContext(ctx); } } else { CLog::Log(LOGFATAL, "CDVDCodecUtils::ConvertToYUY2Picture, unable to allocate new video picture, out of memory."); delete pPicture; pPicture = NULL; } } return pPicture; }
void CDVDVideoCodecVDA::BGRA_to_YUV420P(uint8_t *bgra_ptr, int bgra_stride, DVDVideoPicture *picture) { // convert PIX_FMT_BGRA to PIX_FMT_YUV420P. struct SwsContext *swcontext = sws_getContext( m_videobuffer.iWidth, m_videobuffer.iHeight, PIX_FMT_BGRA, m_videobuffer.iWidth, m_videobuffer.iHeight, PIX_FMT_YUV420P, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL); if (swcontext) { uint8_t *src[] = { bgra_ptr, 0, 0, 0 }; int srcStride[] = { bgra_stride, 0, 0, 0 }; uint8_t *dst[] = { picture->data[0], picture->data[1], picture->data[2], 0 }; int dstStride[] = { picture->iLineSize[0], picture->iLineSize[1], picture->iLineSize[2], 0 }; sws_scale(swcontext, src, srcStride, 0, picture->iHeight, dst, dstStride); sws_freeContext(swcontext); } }
bool CDVDFileInfo::ExtractThumb(const std::string &strPath, CTextureDetails &details, CStreamDetails *pStreamDetails, int pos) { std::string redactPath = CURL::GetRedacted(strPath); unsigned int nTime = XbmcThreads::SystemClockMillis(); CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strPath, ""); if (!pInputStream) { CLog::Log(LOGERROR, "InputStream: Error creating stream for %s", redactPath.c_str()); return false; } if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)) { CLog::Log(LOGDEBUG, "%s: disc streams not supported for thumb extraction, file: %s", __FUNCTION__, redactPath.c_str()); delete pInputStream; return false; } if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) { SAFE_DELETE(pInputStream); return false; } if (!pInputStream->Open(strPath.c_str(), "", true)) { CLog::Log(LOGERROR, "InputStream: Error opening, %s", redactPath.c_str()); if (pInputStream) SAFE_DELETE(pInputStream); return false; } CDVDDemux *pDemuxer = NULL; try { pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true); if(!pDemuxer) { SAFE_DELETE(pInputStream); CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__); return false; } } catch(...) { CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__); if (pDemuxer) SAFE_DELETE(pDemuxer); SAFE_DELETE(pInputStream); return false; } if (pStreamDetails) { DemuxerToStreamDetails(pInputStream, pDemuxer, *pStreamDetails, strPath); //extern subtitles std::vector<std::string> filenames; std::string video_path; if (strPath.empty()) video_path = pInputStream->GetFileName(); else video_path = strPath; CUtil::ScanForExternalSubtitles(video_path, filenames); for(unsigned int i=0;i<filenames.size();i++) { // if vobsub subtitle: if (URIUtils::GetExtension(filenames[i]) == ".idx") { std::string strSubFile; if ( CUtil::FindVobSubPair(filenames, filenames[i], strSubFile) ) AddExternalSubtitleToDetails(video_path, *pStreamDetails, filenames[i], strSubFile); } else { if ( !CUtil::IsVobSub(filenames, filenames[i]) ) { AddExternalSubtitleToDetails(video_path, *pStreamDetails, filenames[i]); } } } } int nVideoStream = -1; for (int i = 0; i < pDemuxer->GetNrOfStreams(); i++) { CDemuxStream* pStream = pDemuxer->GetStream(i); if (pStream) { // ignore if it's a picture attachment (e.g. jpeg artwork) if(pStream->type == STREAM_VIDEO && !(pStream->flags & AV_DISPOSITION_ATTACHED_PIC)) nVideoStream = i; else pStream->SetDiscard(AVDISCARD_ALL); } } bool bOk = false; int packetsTried = 0; if (nVideoStream != -1) { CDVDVideoCodec *pVideoCodec; CDVDStreamInfo hint(*pDemuxer->GetStream(nVideoStream), true); hint.software = true; if (hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_MPEG1VIDEO) { // libmpeg2 is not thread safe so use ffmepg for mpeg2/mpeg1 thumb extraction CDVDCodecOptions dvdOptions; pVideoCodec = CDVDFactoryCodec::OpenCodec(new CDVDVideoCodecFFmpeg(), hint, dvdOptions); } else { pVideoCodec = CDVDFactoryCodec::CreateVideoCodec( hint ); } if (pVideoCodec) { int nTotalLen = pDemuxer->GetStreamLength(); int nSeekTo = (pos==-1?nTotalLen / 3:pos); CLog::Log(LOGDEBUG,"%s - seeking to pos %dms (total: %dms) in %s", __FUNCTION__, nSeekTo, nTotalLen, redactPath.c_str()); if (pDemuxer->SeekTime(nSeekTo, true)) { int iDecoderState = VC_ERROR; DVDVideoPicture picture; memset(&picture, 0, sizeof(picture)); // num streams * 160 frames, should get a valid frame, if not abort. int abort_index = pDemuxer->GetNrOfStreams() * 160; do { DemuxPacket* pPacket = pDemuxer->Read(); packetsTried++; if (!pPacket) break; if (pPacket->iStreamId != nVideoStream) { CDVDDemuxUtils::FreeDemuxPacket(pPacket); continue; } iDecoderState = pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts); CDVDDemuxUtils::FreeDemuxPacket(pPacket); if (iDecoderState & VC_ERROR) break; if (iDecoderState & VC_PICTURE) { memset(&picture, 0, sizeof(DVDVideoPicture)); if (pVideoCodec->GetPicture(&picture)) { if(!(picture.iFlags & DVP_FLAG_DROPPED)) break; } } } while (abort_index--); if (iDecoderState & VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED)) { { unsigned int nWidth = g_advancedSettings.GetThumbSize(); double aspect = (double)picture.iDisplayWidth / (double)picture.iDisplayHeight; if(hint.forced_aspect && hint.aspect != 0) aspect = hint.aspect; unsigned int nHeight = (unsigned int)((double)g_advancedSettings.GetThumbSize() / aspect); uint8_t *pOutBuf = new uint8_t[nWidth * nHeight * 4]; struct SwsContext *context = sws_getContext(picture.iWidth, picture.iHeight, PIX_FMT_YUV420P, nWidth, nHeight, PIX_FMT_BGRA, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL); if (context) { uint8_t *src[] = { picture.data[0], picture.data[1], picture.data[2], 0 }; int srcStride[] = { picture.iLineSize[0], picture.iLineSize[1], picture.iLineSize[2], 0 }; uint8_t *dst[] = { pOutBuf, 0, 0, 0 }; int dstStride[] = { (int)nWidth*4, 0, 0, 0 }; int orientation = DegreeToOrientation(hint.orientation); sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride); sws_freeContext(context); details.width = nWidth; details.height = nHeight; CPicture::CacheTexture(pOutBuf, nWidth, nHeight, nWidth * 4, orientation, nWidth, nHeight, CTextureCache::GetCachedPath(details.file)); bOk = true; } SAFE_DELETE_ARRAY(pOutBuf); } } else { CLog::Log(LOGDEBUG,"%s - decode failed in %s after %d packets.", __FUNCTION__, redactPath.c_str(), packetsTried); } } SAFE_DELETE(pVideoCodec); } } if (pDemuxer) SAFE_DELETE(pDemuxer); SAFE_DELETE(pInputStream); if(!bOk) { XFILE::CFile file; if(file.OpenForWrite(CTextureCache::GetCachedPath(details.file))) file.Close(); } unsigned int nTotalTime = XbmcThreads::SystemClockMillis() - nTime; CLog::Log(LOGDEBUG,"%s - measured %u ms to extract thumb from file <%s> in %d packets. ", __FUNCTION__, nTotalTime, redactPath.c_str(), packetsTried); return bOk; }
bool CRetroPlayerVideo::CheckConfiguration(const DVDVideoPicture &picture) { const double framerate = 1 / picture.iDuration; if (g_renderManager.IsConfigured() && m_outputWidth == picture.iWidth && m_outputHeight == picture.iHeight && m_outputFramerate == framerate) { // Already configured properly return true; } // Determine RenderManager flags unsigned int flags = 0; if (picture.color_range == 1) flags |= CONF_FLAGS_YUV_FULLRANGE; flags |= CONF_FLAGS_YUVCOEF_BT601; // picture.color_matrix = 4 if (m_bAllowFullscreen) { flags |= CONF_FLAGS_FULLSCREEN; m_bAllowFullscreen = false; // only allow on first configure } CLog::Log(LOGDEBUG, "RetroPlayerVideo: Change configuration: %dx%d, %4.2f fps", picture.iWidth, picture.iHeight, framerate); int orientation = 0; // (90 = 5, 180 = 2, 270 = 7), if we ever want to use RETRO_ENVIRONMENT_SET_ROTATION if (!g_renderManager.Configure(picture.iWidth, picture.iHeight, picture.iDisplayWidth, picture.iDisplayHeight, (float)framerate, flags, picture.format, picture.extended_format, orientation)) { CLog::Log(LOGERROR, "RetroPlayerVideo: Failed to configure renderer"); return false; } m_outputWidth = picture.iWidth; m_outputHeight = picture.iHeight; m_outputFramerate = framerate; PixelFormat format; switch (m_pixelFormat) { case GAME_PIXEL_FORMAT_XRGB8888: CLog::Log(LOGINFO, "RetroPlayerVideo: Pixel Format: XRGB8888, using PIX_FMT_0RGB32"); format = PIX_FMT_0RGB32; break; case GAME_PIXEL_FORMAT_RGB565: CLog::Log(LOGINFO, "RetroPlayerVideo: Pixel Format: RGB565, using PIX_FMT_RGB565"); format = PIX_FMT_RGB565; break; case GAME_PIXEL_FORMAT_0RGB1555: default: CLog::Log(LOGINFO, "RetroPlayerVideo: Pixel Format: 0RGB1555, using PIX_FMT_RGB555"); format = PIX_FMT_RGB555; break; } if (m_swsContext) sws_freeContext(m_swsContext); m_swsContext = sws_getContext( picture.iWidth, picture.iHeight, format, picture.iWidth, picture.iHeight, PIX_FMT_YUV420P, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL ); return true; }
bool CDVDFileInfo::ExtractThumb(const CStdString &strPath, CTextureDetails &details, CStreamDetails *pStreamDetails) { unsigned int nTime = XbmcThreads::SystemClockMillis(); CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strPath, ""); if (!pInputStream) { CLog::Log(LOGERROR, "InputStream: Error creating stream for %s", strPath.c_str()); return false; } if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) { CLog::Log(LOGERROR, "InputStream: dvd streams not supported for thumb extraction, file: %s", strPath.c_str()); delete pInputStream; return false; } if (!pInputStream->Open(strPath.c_str(), "")) { CLog::Log(LOGERROR, "InputStream: Error opening, %s", strPath.c_str()); if (pInputStream) delete pInputStream; return false; } CDVDDemux *pDemuxer = NULL; try { pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream); if(!pDemuxer) { delete pInputStream; CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__); return false; } } catch(...) { CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__); if (pDemuxer) delete pDemuxer; delete pInputStream; return false; } if (pStreamDetails) DemuxerToStreamDetails(pInputStream, pDemuxer, *pStreamDetails, strPath); CDemuxStream* pStream = NULL; int nVideoStream = -1; for (int i = 0; i < pDemuxer->GetNrOfStreams(); i++) { pStream = pDemuxer->GetStream(i); if (pStream) { if(pStream->type == STREAM_VIDEO) nVideoStream = i; else pStream->SetDiscard(AVDISCARD_ALL); } } bool bOk = false; int packetsTried = 0; if (nVideoStream != -1) { CDVDVideoCodec *pVideoCodec; CDVDStreamInfo hint(*pDemuxer->GetStream(nVideoStream), true); hint.software = true; if (hint.codec == CODEC_ID_MPEG2VIDEO || hint.codec == CODEC_ID_MPEG1VIDEO) { // libmpeg2 is not thread safe so use ffmepg for mpeg2/mpeg1 thumb extraction CDVDCodecOptions dvdOptions; pVideoCodec = CDVDFactoryCodec::OpenCodec(new CDVDVideoCodecFFmpeg(), hint, dvdOptions); } else { pVideoCodec = CDVDFactoryCodec::CreateVideoCodec( hint ); } if (pVideoCodec) { int nTotalLen = pDemuxer->GetStreamLength(); int nSeekTo = nTotalLen / 3; CLog::Log(LOGDEBUG,"%s - seeking to pos %dms (total: %dms) in %s", __FUNCTION__, nSeekTo, nTotalLen, strPath.c_str()); if (pDemuxer->SeekTime(nSeekTo, true)) { DemuxPacket* pPacket = NULL; int iDecoderState = VC_ERROR; DVDVideoPicture picture; memset(&picture, 0, sizeof(picture)); // num streams * 80 frames, should get a valid frame, if not abort. int abort_index = pDemuxer->GetNrOfStreams() * 80; do { pPacket = pDemuxer->Read(); packetsTried++; if (!pPacket) break; if (pPacket->iStreamId != nVideoStream) { CDVDDemuxUtils::FreeDemuxPacket(pPacket); continue; } iDecoderState = pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts); CDVDDemuxUtils::FreeDemuxPacket(pPacket); if (iDecoderState & VC_ERROR) break; if (iDecoderState & VC_PICTURE) { memset(&picture, 0, sizeof(DVDVideoPicture)); if (pVideoCodec->GetPicture(&picture)) { if(!(picture.iFlags & DVP_FLAG_DROPPED)) break; } } } while (abort_index--); if (iDecoderState & VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED)) { { unsigned int nWidth = g_advancedSettings.GetThumbSize(); double aspect = (double)picture.iDisplayWidth / (double)picture.iDisplayHeight; if(hint.forced_aspect && hint.aspect != 0) aspect = hint.aspect; unsigned int nHeight = (unsigned int)((double)g_advancedSettings.GetThumbSize() / aspect); DllSwScale dllSwScale; dllSwScale.Load(); BYTE *pOutBuf = new BYTE[nWidth * nHeight * 4]; struct SwsContext *context = dllSwScale.sws_getContext(picture.iWidth, picture.iHeight, PIX_FMT_YUV420P, nWidth, nHeight, PIX_FMT_BGRA, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL); uint8_t *src[] = { picture.data[0], picture.data[1], picture.data[2], 0 }; int srcStride[] = { picture.iLineSize[0], picture.iLineSize[1], picture.iLineSize[2], 0 }; uint8_t *dst[] = { pOutBuf, 0, 0, 0 }; int dstStride[] = { (int)nWidth*4, 0, 0, 0 }; if (context) { int orientation = DegreeToOrientation(hint.orientation); dllSwScale.sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride); dllSwScale.sws_freeContext(context); details.width = nWidth; details.height = nHeight; CPicture::CacheTexture(pOutBuf, nWidth, nHeight, nWidth * 4, orientation, nWidth, nHeight, CTextureCache::GetCachedPath(details.file)); bOk = true; } dllSwScale.Unload(); delete [] pOutBuf; } } else { CLog::Log(LOGDEBUG,"%s - decode failed in %s after %d packets.", __FUNCTION__, strPath.c_str(), packetsTried); } } delete pVideoCodec; } } if (pDemuxer) delete pDemuxer; delete pInputStream; if(!bOk) { XFILE::CFile file; if(file.OpenForWrite(CTextureCache::GetCachedPath(details.file))) file.Close(); } unsigned int nTotalTime = XbmcThreads::SystemClockMillis() - nTime; CLog::Log(LOGDEBUG,"%s - measured %u ms to extract thumb from file <%s> in %d packets. ", __FUNCTION__, nTotalTime, strPath.c_str(), packetsTried); return bOk; }