diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/cores/VideoPlayer/DVDFileInfo.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/cores/VideoPlayer/DVDFileInfo.cpp')
-rw-r--r-- | xbmc/cores/VideoPlayer/DVDFileInfo.cpp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp new file mode 100644 index 0000000..0860b40 --- /dev/null +++ b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DVDFileInfo.h" +#include "ServiceBroker.h" +#include "FileItem.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "pictures/Picture.h" +#include "video/VideoInfoTag.h" +#include "filesystem/StackDirectory.h" +#include "utils/log.h" +#include "utils/URIUtils.h" + +#include "DVDStreamInfo.h" +#include "DVDInputStreams/DVDInputStream.h" +#ifdef HAVE_LIBBLURAY +#include "DVDInputStreams/DVDInputStreamBluray.h" +#endif +#include "DVDInputStreams/DVDFactoryInputStream.h" +#include "DVDDemuxers/DVDDemux.h" +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "DVDDemuxers/DVDFactoryDemuxer.h" +#include "DVDCodecs/DVDFactoryCodec.h" +#include "DVDCodecs/Video/DVDVideoCodec.h" +#include "DVDCodecs/Video/DVDVideoCodecFFmpeg.h" +#include "DVDDemuxers/DVDDemuxVobsub.h" +#include "Process/ProcessInfo.h" + +#include <libavcodec/avcodec.h> +#include <libswscale/swscale.h> +#include "filesystem/File.h" +#include "cores/FFmpeg.h" +#include "TextureCache.h" +#include "Util.h" +#include "utils/LangCodeExpander.h" + +#include <cstdlib> +#include <memory> + +extern "C" { +#include <libavformat/avformat.h> +} + +bool CDVDFileInfo::GetFileDuration(const std::string &path, int& duration) +{ + std::unique_ptr<CDVDDemux> demux; + + CFileItem item(path, false); + auto input = CDVDFactoryInputStream::CreateInputStream(NULL, item); + if (!input) + return false; + + if (!input->Open()) + return false; + + demux.reset(CDVDFactoryDemuxer::CreateDemuxer(input, true)); + if (!demux) + return false; + + duration = demux->GetStreamLength(); + if (duration > 0) + return true; + else + return false; +} + +int DegreeToOrientation(int degrees) +{ + switch(degrees) + { + case 90: + return 5; + case 180: + return 2; + case 270: + return 7; + default: + return 0; + } +} + +bool CDVDFileInfo::ExtractThumb(const CFileItem& fileItem, + CTextureDetails &details, + CStreamDetails *pStreamDetails, + int64_t pos) +{ + const std::string redactPath = CURL::GetRedacted(fileItem.GetPath()); + auto start = std::chrono::steady_clock::now(); + + CFileItem item(fileItem); + item.SetMimeTypeForInternetFile(); + auto pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, item); + if (!pInputStream) + { + CLog::Log(LOGERROR, "InputStream: Error creating stream for {}", redactPath); + return false; + } + + if (!pInputStream->Open()) + { + CLog::Log(LOGERROR, "InputStream: Error opening, {}", redactPath); + return false; + } + + CDVDDemux *pDemuxer = NULL; + + try + { + pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true); + if(!pDemuxer) + { + CLog::Log(LOGERROR, "{} - Error creating demuxer", __FUNCTION__); + return false; + } + } + catch(...) + { + CLog::Log(LOGERROR, "{} - Exception thrown when opening demuxer", __FUNCTION__); + if (pDemuxer) + delete pDemuxer; + + return false; + } + + if (pStreamDetails) + { + + const std::string& strPath = item.GetPath(); + 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) + { + std::unique_ptr<CProcessInfo> pProcessInfo(CProcessInfo::CreateInstance()); + std::vector<AVPixelFormat> pixFmts; + pixFmts.push_back(AV_PIX_FMT_YUV420P); + pProcessInfo->SetPixFormats(pixFmts); + + CDVDStreamInfo hint(*pDemuxer->GetStream(demuxerId, nVideoStream), true); + hint.codecOptions = CODEC_FORCE_SOFTWARE; + + std::unique_ptr<CDVDVideoCodec> pVideoCodec = + CDVDFactoryCodec::CreateVideoCodec(hint, *pProcessInfo); + + if (pVideoCodec) + { + int nTotalLen = pDemuxer->GetStreamLength(); + int64_t nSeekTo = (pos == -1) ? nTotalLen / 3 : pos; + + CLog::Log(LOGDEBUG, "{} - seeking to pos {}ms (total: {}ms) in {}", __FUNCTION__, nSeekTo, + nTotalLen, redactPath); + + if (pDemuxer->SeekTime(static_cast<double>(nSeekTo), true)) + { + CDVDVideoCodec::VCReturn iDecoderState = CDVDVideoCodec::VC_NONE; + VideoPicture 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; + } + + pVideoCodec->AddData(*pPacket); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + + iDecoderState = CDVDVideoCodec::VC_NONE; + while (iDecoderState == CDVDVideoCodec::VC_NONE) + { + iDecoderState = pVideoCodec->GetPicture(&picture); + } + + if (iDecoderState == CDVDVideoCodec::VC_PICTURE) + { + if(!(picture.iFlags & DVP_FLAG_DROPPED)) + break; + } + + } while (abort_index--); + + if (iDecoderState == CDVDVideoCodec::VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED)) + { + { + unsigned int nWidth = std::min(picture.iDisplayWidth, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->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)nWidth / aspect); + + // We pass the buffers to sws_scale uses 16 aligned widths when using intrinsics + int sizeNeeded = FFALIGN(nWidth, 16) * nHeight * 4; + uint8_t *pOutBuf = static_cast<uint8_t*>(av_malloc(sizeNeeded)); + 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 *planes[YuvImage::MAX_PLANES]; + int stride[YuvImage::MAX_PLANES]; + picture.videoBuffer->GetPlanes(planes); + picture.videoBuffer->GetStrides(stride); + uint8_t *src[4]= { planes[0], planes[1], planes[2], 0 }; + int srcStride[] = { stride[0], stride[1], stride[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, "{} - decode failed in {} after {} packets.", __FUNCTION__, + redactPath, packetsTried); + } + } + } + } + + if (pDemuxer) + delete pDemuxer; + + if(!bOk) + { + XFILE::CFile file; + if(file.OpenForWrite(CTextureCache::GetCachedPath(details.file))) + file.Close(); + } + + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); + CLog::Log(LOGDEBUG, "{} - measured {} ms to extract thumb from file <{}> in {} packets. ", + __FUNCTION__, duration.count(), redactPath, packetsTried); + + return bOk; +} + +/** + * \brief Open the item pointed to by pItem and extract streamdetails + * \return true if the stream details have changed + */ +bool CDVDFileInfo::GetFileStreamDetails(CFileItem *pItem) +{ + if (!pItem) + return false; + + std::string strFileNameAndPath; + if (pItem->HasVideoInfoTag()) + strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath; + + if (strFileNameAndPath.empty()) + strFileNameAndPath = pItem->GetDynPath(); + + std::string playablePath = strFileNameAndPath; + if (URIUtils::IsStack(playablePath)) + playablePath = XFILE::CStackDirectory::GetFirstStackedFile(playablePath); + + CFileItem item(playablePath, false); + item.SetMimeTypeForInternetFile(); + auto pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, item); + if (!pInputStream) + return false; + + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + return false; + } + + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || !pInputStream->Open()) + { + return false; + } + + CDVDDemux *pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true); + if (pDemuxer) + { + bool retVal = DemuxerToStreamDetails(pInputStream, pDemuxer, pItem->GetVideoInfoTag()->m_streamDetails, strFileNameAndPath); + delete pDemuxer; + return retVal; + } + else + { + return false; + } +} + +bool CDVDFileInfo::DemuxerToStreamDetails(const std::shared_ptr<CDVDInputStream>& pInputStream, + CDVDDemux* pDemuxer, + const std::vector<CStreamDetailSubtitle>& subs, + CStreamDetails& details) +{ + bool result = DemuxerToStreamDetails(pInputStream, pDemuxer, details); + for (unsigned int i = 0; i < subs.size(); i++) + { + CStreamDetailSubtitle* sub = new CStreamDetailSubtitle(); + sub->m_strLanguage = subs[i].m_strLanguage; + details.AddStream(sub); + result = true; + } + return result; +} + +/* returns true if details have been added */ +bool CDVDFileInfo::DemuxerToStreamDetails(const std::shared_ptr<CDVDInputStream>& pInputStream, + CDVDDemux* pDemux, + CStreamDetails& details, + const std::string& path) +{ + bool retVal = false; + details.Reset(); + + const CURL pathToUrl(path); + for (CDemuxStream* stream : pDemux->GetStreams()) + { + if (stream->type == STREAM_VIDEO && !(stream->flags & AV_DISPOSITION_ATTACHED_PIC)) + { + CStreamDetailVideo *p = new CStreamDetailVideo(); + CDemuxStreamVideo* vstream = static_cast<CDemuxStreamVideo*>(stream); + p->m_iWidth = vstream->iWidth; + p->m_iHeight = vstream->iHeight; + p->m_fAspect = static_cast<float>(vstream->fAspect); + if (p->m_fAspect == 0.0f && p->m_iHeight > 0) + p->m_fAspect = (float)p->m_iWidth / p->m_iHeight; + p->m_strCodec = pDemux->GetStreamCodecName(stream->demuxerId, stream->uniqueId); + p->m_iDuration = pDemux->GetStreamLength(); + p->m_strStereoMode = vstream->stereo_mode; + p->m_strLanguage = vstream->language; + p->m_strHdrType = CStreamDetails::HdrTypeToString(vstream->hdr_type); + + // stack handling + if (URIUtils::IsStack(path)) + { + CFileItemList files; + XFILE::CStackDirectory stack; + stack.GetDirectory(pathToUrl, files); + + // skip first path as we already know the duration + for (int i = 1; i < files.Size(); i++) + { + int duration = 0; + if (CDVDFileInfo::GetFileDuration(files[i]->GetDynPath(), duration)) + p->m_iDuration = p->m_iDuration + duration; + } + } + + // finally, calculate seconds + if (p->m_iDuration > 0) + p->m_iDuration = p->m_iDuration / 1000; + + details.AddStream(p); + retVal = true; + } + + else if (stream->type == STREAM_AUDIO) + { + CStreamDetailAudio *p = new CStreamDetailAudio(); + p->m_iChannels = static_cast<CDemuxStreamAudio*>(stream)->iChannels; + p->m_strLanguage = stream->language; + p->m_strCodec = pDemux->GetStreamCodecName(stream->demuxerId, stream->uniqueId); + details.AddStream(p); + retVal = true; + } + + else if (stream->type == STREAM_SUBTITLE) + { + CStreamDetailSubtitle *p = new CStreamDetailSubtitle(); + p->m_strLanguage = stream->language; + details.AddStream(p); + retVal = true; + } + } /* for iStream */ + + details.DetermineBestStreams(); +#ifdef HAVE_LIBBLURAY + // correct bluray runtime. we need the duration from the input stream, not the demuxer. + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)) + { + if (std::static_pointer_cast<CDVDInputStreamBluray>(pInputStream)->GetTotalTime() > 0) + { + const CStreamDetailVideo* dVideo = static_cast<const CStreamDetailVideo*>(details.GetNthStream(CStreamDetail::VIDEO, 0)); + CStreamDetailVideo* detailVideo = const_cast<CStreamDetailVideo*>(dVideo); + if (detailVideo) + detailVideo->m_iDuration = std::static_pointer_cast<CDVDInputStreamBluray>(pInputStream)->GetTotalTime() / 1000; + } + } +#endif + return retVal; +} + +bool CDVDFileInfo::AddExternalSubtitleToDetails(const std::string &path, CStreamDetails &details, const std::string& filename, const std::string& subfilename) +{ + std::string ext = URIUtils::GetExtension(filename); + std::string vobsubfile = subfilename; + if(ext == ".idx") + { + if (vobsubfile.empty()) + vobsubfile = URIUtils::ReplaceExtension(filename, ".sub"); + + CDVDDemuxVobsub v; + if (!v.Open(filename, STREAM_SOURCE_NONE, vobsubfile)) + return false; + + for(CDemuxStream* stream : v.GetStreams()) + { + CStreamDetailSubtitle *dsub = new CStreamDetailSubtitle(); + std::string lang = stream->language; + dsub->m_strLanguage = g_LangCodeExpander.ConvertToISO6392B(lang); + details.AddStream(dsub); + } + return true; + } + if(ext == ".sub") + { + std::string strReplace(URIUtils::ReplaceExtension(filename,".idx")); + if (XFILE::CFile::Exists(strReplace)) + return false; + } + + CStreamDetailSubtitle *dsub = new CStreamDetailSubtitle(); + ExternalStreamInfo info = CUtil::GetExternalStreamDetailsFromFilename(path, filename); + dsub->m_strLanguage = g_LangCodeExpander.ConvertToISO6392B(info.language); + details.AddStream(dsub); + + return true; +} + |