[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / cores / VideoPlayer / DVDFileInfo.cpp
blob33ab24885aaebad5862b02c7c5451a0de8dd856d
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "DVDFileInfo.h"
11 #include "DVDInputStreams/DVDInputStream.h"
12 #include "DVDStreamInfo.h"
13 #include "FileItem.h"
14 #include "FileItemList.h"
15 #include "ServiceBroker.h"
16 #include "filesystem/StackDirectory.h"
17 #include "guilib/Texture.h"
18 #include "network/NetworkFileItemClassify.h"
19 #include "pictures/Picture.h"
20 #include "playlists/PlayListFileItemClassify.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/SettingsComponent.h"
23 #include "utils/MemUtils.h"
24 #include "utils/URIUtils.h"
25 #include "utils/log.h"
26 #include "video/VideoFileItemClassify.h"
27 #include "video/VideoInfoTag.h"
28 #ifdef HAVE_LIBBLURAY
29 #include "DVDInputStreams/DVDInputStreamBluray.h"
30 #endif
31 #include "DVDInputStreams/DVDFactoryInputStream.h"
32 #include "DVDDemuxers/DVDDemux.h"
33 #include "DVDDemuxers/DVDDemuxUtils.h"
34 #include "DVDDemuxers/DVDFactoryDemuxer.h"
35 #include "DVDCodecs/DVDFactoryCodec.h"
36 #include "DVDCodecs/Video/DVDVideoCodec.h"
37 #include "DVDCodecs/Video/DVDVideoCodecFFmpeg.h"
38 #include "DVDDemuxers/DVDDemuxVobsub.h"
39 #include "Process/ProcessInfo.h"
41 #include "filesystem/File.h"
42 #include "cores/FFmpeg.h"
43 #include "TextureCache.h"
44 #include "Util.h"
45 #include "utils/LangCodeExpander.h"
47 #include <cstdlib>
48 #include <memory>
50 extern "C" {
51 #include <libavcodec/avcodec.h>
52 #include <libavformat/avformat.h>
53 #include <libswscale/swscale.h>
56 using namespace KODI;
58 bool CDVDFileInfo::GetFileDuration(const std::string &path, int& duration)
60 std::unique_ptr<CDVDDemux> demux;
62 CFileItem item(path, false);
63 auto input = CDVDFactoryInputStream::CreateInputStream(NULL, item);
64 if (!input)
65 return false;
67 if (!input->Open())
68 return false;
70 demux.reset(CDVDFactoryDemuxer::CreateDemuxer(input, true));
71 if (!demux)
72 return false;
74 duration = demux->GetStreamLength();
75 if (duration > 0)
76 return true;
77 else
78 return false;
81 int DegreeToOrientation(int degrees)
83 switch(degrees)
85 case 90:
86 return 5;
87 case 180:
88 return 2;
89 case 270:
90 return 7;
91 default:
92 return 0;
96 std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& fileItem,
97 int chapterNumber)
99 if (!CanExtract(fileItem))
100 return {};
102 const std::string redactPath = CURL::GetRedacted(fileItem.GetPath());
103 auto start = std::chrono::steady_clock::now();
105 CFileItem item(fileItem);
106 item.SetMimeTypeForInternetFile();
107 auto pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, item);
108 if (!pInputStream)
110 CLog::Log(LOGERROR, "InputStream: Error creating stream for {}", redactPath);
111 return {};
114 if (!pInputStream->Open())
116 CLog::Log(LOGERROR, "InputStream: Error opening, {}", redactPath);
117 return {};
120 std::unique_ptr<CDVDDemux> demuxer{CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true)};
121 if (!demuxer)
123 CLog::LogF(LOGERROR, "Error creating demuxer");
124 return {};
127 int nVideoStream = -1;
128 int64_t demuxerId = -1;
129 for (CDemuxStream* pStream : demuxer->GetStreams())
131 if (pStream)
133 // ignore if it's a picture attachment (e.g. jpeg artwork)
134 if (pStream->type == STREAM_VIDEO && !(pStream->flags & AV_DISPOSITION_ATTACHED_PIC))
136 nVideoStream = pStream->uniqueId;
137 demuxerId = pStream->demuxerId;
139 else
140 demuxer->EnableStream(pStream->demuxerId, pStream->uniqueId, false);
144 int packetsTried = 0;
146 std::unique_ptr<CTexture> result{};
147 if (nVideoStream != -1)
149 std::unique_ptr<CProcessInfo> pProcessInfo(CProcessInfo::CreateInstance());
150 std::vector<AVPixelFormat> pixFmts;
151 pixFmts.push_back(AV_PIX_FMT_YUV420P);
152 pProcessInfo->SetPixFormats(pixFmts);
154 CDVDStreamInfo hint(*demuxer->GetStream(demuxerId, nVideoStream), true);
155 hint.codecOptions = CODEC_FORCE_SOFTWARE;
157 std::unique_ptr<CDVDVideoCodec> pVideoCodec =
158 CDVDFactoryCodec::CreateVideoCodec(hint, *pProcessInfo);
160 if (pVideoCodec)
162 int nTotalLen = demuxer->GetStreamLength();
164 bool seekToChapter = chapterNumber > 0 && demuxer->GetChapterCount() > 0;
165 int64_t nSeekTo =
166 seekToChapter ? demuxer->GetChapterPos(chapterNumber) * 1000 : nTotalLen / 3;
168 CLog::LogF(LOGDEBUG, "seeking to pos {}ms (total: {}ms) in {}", nSeekTo, nTotalLen,
169 redactPath);
171 if (demuxer->SeekTime(static_cast<double>(nSeekTo), true))
173 CDVDVideoCodec::VCReturn iDecoderState = CDVDVideoCodec::VC_NONE;
174 VideoPicture picture = {};
176 // num streams * 160 frames, should get a valid frame, if not abort.
177 int abort_index = demuxer->GetNrOfStreams() * 160;
180 DemuxPacket* pPacket = demuxer->Read();
181 packetsTried++;
183 if (!pPacket)
184 break;
186 if (pPacket->iStreamId != nVideoStream)
188 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
189 continue;
192 pVideoCodec->AddData(*pPacket);
193 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
195 iDecoderState = CDVDVideoCodec::VC_NONE;
196 while (iDecoderState == CDVDVideoCodec::VC_NONE)
198 iDecoderState = pVideoCodec->GetPicture(&picture);
201 if (iDecoderState == CDVDVideoCodec::VC_PICTURE)
203 if (!(picture.iFlags & DVP_FLAG_DROPPED))
204 break;
207 } while (abort_index--);
209 if (iDecoderState == CDVDVideoCodec::VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED))
211 unsigned int nWidth =
212 std::min(picture.iDisplayWidth,
213 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes);
214 double aspect = (double)picture.iDisplayWidth / (double)picture.iDisplayHeight;
215 if (hint.forced_aspect && hint.aspect != 0)
216 aspect = hint.aspect;
217 unsigned int nHeight = (unsigned int)((double)nWidth / aspect);
219 result = CTexture::CreateTexture(nWidth, nHeight);
220 result->SetAlpha(false);
221 struct SwsContext* context =
222 sws_getContext(picture.iWidth, picture.iHeight, AV_PIX_FMT_YUV420P, nWidth, nHeight,
223 AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL);
225 if (context)
227 uint8_t* planes[YuvImage::MAX_PLANES];
228 int stride[YuvImage::MAX_PLANES];
229 picture.videoBuffer->GetPlanes(planes);
230 picture.videoBuffer->GetStrides(stride);
231 uint8_t* src[4] = {planes[0], planes[1], planes[2], 0};
232 int srcStride[] = {stride[0], stride[1], stride[2], 0};
233 uint8_t* dst[] = {result->GetPixels(), 0, 0, 0};
234 int dstStride[] = {static_cast<int>(result->GetPitch()), 0, 0, 0};
235 result->SetOrientation(DegreeToOrientation(hint.orientation));
236 sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride);
237 sws_freeContext(context);
240 else
242 CLog::LogF(LOGDEBUG, "decode failed in {} after {} packets.", redactPath, packetsTried);
248 auto end = std::chrono::steady_clock::now();
249 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
250 CLog::LogF(LOGDEBUG, "measured {} ms to extract thumb from file <{}> in {} packets. ",
251 duration.count(), redactPath, packetsTried);
253 return result;
256 bool CDVDFileInfo::CanExtract(const CFileItem& fileItem)
258 if (fileItem.m_bIsFolder)
259 return false;
261 if (fileItem.IsLiveTV() ||
262 // Due to a pvr addon api design flaw (no support for multiple concurrent streams
263 // per addon instance), pvr recording thumbnail extraction does not work (reliably).
264 URIUtils::IsPVRRecording(fileItem.GetDynPath()) ||
265 // plugin path not fully resolved
266 URIUtils::IsPlugin(fileItem.GetDynPath()) || URIUtils::IsUPnP(fileItem.GetPath()) ||
267 NETWORK::IsInternetStream(fileItem) || VIDEO::IsDiscStub(fileItem) ||
268 PLAYLIST::IsPlayList(fileItem))
269 return false;
271 // mostly can't extract from discs and files from discs.
272 if (URIUtils::IsBluray(fileItem.GetPath()) || VIDEO::IsBDFile(fileItem) || fileItem.IsDVD() ||
273 fileItem.IsDiscImage() || VIDEO::IsDVDFile(fileItem, false, true))
274 return false;
276 // For HTTP/FTP we only allow extraction when on a LAN
277 if (URIUtils::IsRemote(fileItem.GetPath()) && !URIUtils::IsOnLAN(fileItem.GetPath()) &&
278 (URIUtils::IsFTP(fileItem.GetPath()) || URIUtils::IsHTTP(fileItem.GetPath())))
279 return false;
281 return true;
285 * \brief Open the item pointed to by pItem and extract streamdetails
286 * \return true if the stream details have changed
288 bool CDVDFileInfo::GetFileStreamDetails(CFileItem *pItem)
290 if (!pItem)
291 return false;
293 if (!CanExtract(*pItem))
294 return false;
296 std::string strFileNameAndPath;
297 if (pItem->HasVideoInfoTag())
298 strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
300 if (strFileNameAndPath.empty())
301 strFileNameAndPath = pItem->GetDynPath();
303 std::string playablePath = strFileNameAndPath;
304 if (URIUtils::IsStack(playablePath))
305 playablePath = XFILE::CStackDirectory::GetFirstStackedFile(playablePath);
307 CFileItem item(playablePath, false);
308 item.SetMimeTypeForInternetFile();
309 auto pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, item);
310 if (!pInputStream)
311 return false;
313 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
315 return false;
318 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || !pInputStream->Open())
320 return false;
323 CDVDDemux *pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true);
324 if (pDemuxer)
326 bool retVal = DemuxerToStreamDetails(pInputStream, pDemuxer, pItem->GetVideoInfoTag()->m_streamDetails, strFileNameAndPath);
327 ProcessExternalSubtitles(pItem);
328 delete pDemuxer;
329 return retVal;
331 else
333 return false;
337 bool CDVDFileInfo::DemuxerToStreamDetails(const std::shared_ptr<CDVDInputStream>& pInputStream,
338 CDVDDemux* pDemuxer,
339 const std::vector<CStreamDetailSubtitle>& subs,
340 CStreamDetails& details)
342 bool result = DemuxerToStreamDetails(pInputStream, pDemuxer, details);
343 for (unsigned int i = 0; i < subs.size(); i++)
345 CStreamDetailSubtitle* sub = new CStreamDetailSubtitle();
346 sub->m_strLanguage = subs[i].m_strLanguage;
347 details.AddStream(sub);
348 result = true;
350 return result;
353 /* returns true if details have been added */
354 bool CDVDFileInfo::DemuxerToStreamDetails(const std::shared_ptr<CDVDInputStream>& pInputStream,
355 CDVDDemux* pDemux,
356 CStreamDetails& details,
357 const std::string& path)
359 bool retVal = false;
360 details.Reset();
362 const CURL pathToUrl(path);
363 for (CDemuxStream* stream : pDemux->GetStreams())
365 if (stream->type == STREAM_VIDEO && !(stream->flags & AV_DISPOSITION_ATTACHED_PIC))
367 CStreamDetailVideo *p = new CStreamDetailVideo();
368 CDemuxStreamVideo* vstream = static_cast<CDemuxStreamVideo*>(stream);
369 p->m_iWidth = vstream->iWidth;
370 p->m_iHeight = vstream->iHeight;
371 p->m_fAspect = static_cast<float>(vstream->fAspect);
372 if (p->m_fAspect == 0.0f && p->m_iHeight > 0)
373 p->m_fAspect = (float)p->m_iWidth / p->m_iHeight;
374 p->m_strCodec = pDemux->GetStreamCodecName(stream->demuxerId, stream->uniqueId);
375 p->m_iDuration = pDemux->GetStreamLength();
376 p->m_strStereoMode = vstream->stereo_mode;
377 p->m_strLanguage = vstream->language;
378 p->m_strHdrType = CStreamDetails::HdrTypeToString(vstream->hdr_type);
380 // stack handling
381 if (URIUtils::IsStack(path))
383 CFileItemList files;
384 XFILE::CStackDirectory stack;
385 stack.GetDirectory(pathToUrl, files);
387 // skip first path as we already know the duration
388 for (int i = 1; i < files.Size(); i++)
390 int duration = 0;
391 if (CDVDFileInfo::GetFileDuration(files[i]->GetDynPath(), duration))
392 p->m_iDuration = p->m_iDuration + duration;
396 // finally, calculate seconds
397 if (p->m_iDuration > 0)
398 p->m_iDuration = p->m_iDuration / 1000;
400 details.AddStream(p);
401 retVal = true;
404 else if (stream->type == STREAM_AUDIO)
406 CStreamDetailAudio *p = new CStreamDetailAudio();
407 p->m_iChannels = static_cast<CDemuxStreamAudio*>(stream)->iChannels;
408 p->m_strLanguage = stream->language;
409 p->m_strCodec = pDemux->GetStreamCodecName(stream->demuxerId, stream->uniqueId);
410 details.AddStream(p);
411 retVal = true;
414 else if (stream->type == STREAM_SUBTITLE)
416 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
417 p->m_strLanguage = stream->language;
418 details.AddStream(p);
419 retVal = true;
421 } /* for iStream */
423 details.DetermineBestStreams();
424 #ifdef HAVE_LIBBLURAY
425 // correct bluray runtime. we need the duration from the input stream, not the demuxer.
426 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
428 if (std::static_pointer_cast<CDVDInputStreamBluray>(pInputStream)->GetTotalTime() > 0)
430 const CStreamDetailVideo* dVideo = static_cast<const CStreamDetailVideo*>(details.GetNthStream(CStreamDetail::VIDEO, 0));
431 CStreamDetailVideo* detailVideo = const_cast<CStreamDetailVideo*>(dVideo);
432 if (detailVideo)
433 detailVideo->m_iDuration = std::static_pointer_cast<CDVDInputStreamBluray>(pInputStream)->GetTotalTime() / 1000;
436 #endif
437 return retVal;
440 void CDVDFileInfo::ProcessExternalSubtitles(CFileItem* item)
442 std::vector<std::string> externalSubtitles;
443 const std::string videoPath = item->GetDynPath();
445 CUtil::ScanForExternalSubtitles(videoPath, externalSubtitles);
447 for (const auto& externalSubtitle : externalSubtitles)
449 // if vobsub subtitle:
450 if (URIUtils::GetExtension(externalSubtitle) == ".idx")
452 std::string subFile;
453 if (CUtil::FindVobSubPair(externalSubtitles, externalSubtitle, subFile))
454 AddExternalSubtitleToDetails(videoPath, item->GetVideoInfoTag()->m_streamDetails,
455 externalSubtitle, subFile);
457 else
459 if (!CUtil::IsVobSub(externalSubtitles, externalSubtitle))
461 AddExternalSubtitleToDetails(videoPath, item->GetVideoInfoTag()->m_streamDetails,
462 externalSubtitle);
468 bool CDVDFileInfo::AddExternalSubtitleToDetails(const std::string &path, CStreamDetails &details, const std::string& filename, const std::string& subfilename)
470 std::string ext = URIUtils::GetExtension(filename);
471 std::string vobsubfile = subfilename;
472 if(ext == ".idx")
474 if (vobsubfile.empty())
475 vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
477 CDVDDemuxVobsub v;
478 if (!v.Open(filename, STREAM_SOURCE_NONE, vobsubfile))
479 return false;
481 for(CDemuxStream* stream : v.GetStreams())
483 CStreamDetailSubtitle *dsub = new CStreamDetailSubtitle();
484 std::string lang = stream->language;
485 dsub->m_strLanguage = g_LangCodeExpander.ConvertToISO6392B(lang);
486 details.AddStream(dsub);
488 return true;
490 if(ext == ".sub")
492 std::string strReplace(URIUtils::ReplaceExtension(filename,".idx"));
493 if (XFILE::CFile::Exists(strReplace))
494 return false;
497 CStreamDetailSubtitle *dsub = new CStreamDetailSubtitle();
498 ExternalStreamInfo info = CUtil::GetExternalStreamDetailsFromFilename(path, filename);
499 dsub->m_strLanguage = g_LangCodeExpander.ConvertToISO6392B(info.language);
500 details.AddStream(dsub);
502 return true;