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 CDVDFileInfo::ExtractThumb(const std::string &strPath, CTextureDetails &details, CStreamDetails *pStreamDetails, int pos) { std::string redactPath = CURL::GetRedacted(strPath); unsigned int nTime = XbmcThreads::SystemClockMillis(); CFileItem item(strPath, false); item.SetMimeTypeForInternetFile(); CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, item); if (!pInputStream) { CLog::Log(LOGERROR, "InputStream: Error creating stream for %s", redactPath.c_str()); return false; } if (!pInputStream->Open()) { CLog::Log(LOGERROR, "InputStream: Error opening, %s", redactPath.c_str()); if (pInputStream) delete pInputStream; return false; } CDVDDemux *pDemuxer = NULL; try { pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true); 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); //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; int64_t demuxerId = -1; for (CDemuxStream* pStream : pDemuxer->GetStreams()) { 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 = pStream->uniqueId; demuxerId = pStream->demuxerId; } else pDemuxer->EnableStream(pStream->demuxerId, pStream->uniqueId, false); } } bool bOk = false; int packetsTried = 0; if (nVideoStream != -1) { CDVDVideoCodec *pVideoCodec; std::unique_ptr<CProcessInfo> pProcessInfo(CProcessInfo::CreateInstance()); CDVDStreamInfo hint(*pDemuxer->GetStream(demuxerId, nVideoStream), true); hint.software = true; pVideoCodec = CDVDFactoryCodec::CreateVideoCodec(hint, *pProcessInfo); 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.m_imageRes; 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.m_imageRes / aspect); uint8_t *pOutBuf = (uint8_t*)av_malloc(nWidth * nHeight * 4); struct SwsContext *context = sws_getContext(picture.iWidth, picture.iHeight, AV_PIX_FMT_YUV420P, nWidth, nHeight, AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR, 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; } av_free(pOutBuf); } } else { CLog::Log(LOGDEBUG,"%s - decode failed in %s after %d packets.", __FUNCTION__, redactPath.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, redactPath.c_str(), packetsTried); return bOk; }