2 * Copyright (C) 2005-2008 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #include "AdvancedSettings.h"
25 #include "VideoInfoTag.h"
27 #include "FileSystem/StackDirectory.h"
28 #include "utils/log.h"
29 #include "utils/TimeUtils.h"
32 #include "DVDFileInfo.h"
33 #include "DVDStreamInfo.h"
34 #include "DVDInputStreams/DVDInputStream.h"
35 #include "DVDInputStreams/DVDInputStreamBluray.h"
36 #include "DVDInputStreams/DVDFactoryInputStream.h"
37 #include "DVDDemuxers/DVDDemux.h"
38 #include "DVDDemuxers/DVDDemuxUtils.h"
39 #include "DVDDemuxers/DVDFactoryDemuxer.h"
40 #include "DVDDemuxers/DVDDemuxFFmpeg.h"
41 #include "DVDCodecs/DVDCodecs.h"
42 #include "DVDCodecs/DVDFactoryCodec.h"
43 #include "DVDCodecs/Video/DVDVideoCodec.h"
44 #include "DVDCodecs/Video/DVDVideoCodecFFmpeg.h"
46 #include "Codecs/DllAvFormat.h"
47 #include "Codecs/DllAvCodec.h"
48 #include "Codecs/DllSwScale.h"
49 #include "FileSystem/File.h"
52 bool CDVDFileInfo::GetFileDuration(const CStdString &path, int& duration)
54 std::auto_ptr<CDVDInputStream> input;
55 std::auto_ptr<CDVDDemux> demux;
57 input.reset(CDVDFactoryInputStream::CreateInputStream(NULL, path, ""));
61 if (!input->Open(path, ""))
64 demux.reset(CDVDFactoryDemuxer::CreateDemuxer(input.get()));
68 duration = demux->GetStreamLength();
75 bool CDVDFileInfo::ExtractThumb(const CStdString &strPath, const CStdString &strTarget, CStreamDetails *pStreamDetails)
77 int nTime = CTimeUtils::GetTimeMS();
78 CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strPath, "");
81 CLog::Log(LOGERROR, "InputStream: Error creating stream for %s", strPath.c_str());
85 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
87 CLog::Log(LOGERROR, "InputStream: dvd streams not supported for thumb extraction, file: %s", strPath.c_str());
92 if (!pInputStream->Open(strPath.c_str(), ""))
94 CLog::Log(LOGERROR, "InputStream: Error opening, %s", strPath.c_str());
100 CDVDDemux *pDemuxer = NULL;
104 pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream);
108 CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
114 CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
122 DemuxerToStreamDetails(pInputStream, pDemuxer, *pStreamDetails, strPath);
124 CDemuxStream* pStream = NULL;
125 int nVideoStream = -1;
126 for (int i = 0; i < pDemuxer->GetNrOfStreams(); i++)
128 pStream = pDemuxer->GetStream(i);
131 if(pStream->type == STREAM_VIDEO)
134 pStream->SetDiscard(AVDISCARD_ALL);
139 if (nVideoStream != -1)
141 CDVDVideoCodec *pVideoCodec;
143 CDVDStreamInfo hint(*pDemuxer->GetStream(nVideoStream), true);
144 hint.software = true;
146 if (hint.codec == CODEC_ID_MPEG2VIDEO || hint.codec == CODEC_ID_MPEG1VIDEO)
148 // libmpeg2 is not thread safe so use ffmepg for mpeg2/mpeg1 thumb extraction
149 CDVDCodecOptions dvdOptions;
150 pVideoCodec = CDVDFactoryCodec::OpenCodec(new CDVDVideoCodecFFmpeg(), hint, dvdOptions);
154 pVideoCodec = CDVDFactoryCodec::CreateVideoCodec( hint );
159 int nTotalLen = pDemuxer->GetStreamLength();
160 int nSeekTo = nTotalLen / 3;
162 CLog::Log(LOGDEBUG,"%s - seeking to pos %dms (total: %dms) in %s", __FUNCTION__, nSeekTo, nTotalLen, strPath.c_str());
163 if (pDemuxer->SeekTime(nSeekTo, true))
165 DemuxPacket* pPacket = NULL;
166 int iDecoderState = VC_ERROR;
167 DVDVideoPicture picture;
169 // num streams * 40 frames, should get a valid frame, if not abort.
170 int abort_index = pDemuxer->GetNrOfStreams() * 40;
173 pPacket = pDemuxer->Read();
177 if (pPacket->iStreamId != nVideoStream)
179 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
183 iDecoderState = pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts);
184 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
186 if (iDecoderState & VC_ERROR)
189 if (iDecoderState & VC_PICTURE)
191 memset(&picture, 0, sizeof(DVDVideoPicture));
192 if (pVideoCodec->GetPicture(&picture))
194 if(!(picture.iFlags & DVP_FLAG_DROPPED))
199 } while (abort_index--);
201 if (iDecoderState & VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED))
204 int nWidth = g_advancedSettings.m_thumbSize;
205 double aspect = (double)picture.iWidth / (double)picture.iHeight;
206 int nHeight = (int)((double)g_advancedSettings.m_thumbSize / aspect);
208 DllSwScale dllSwScale;
211 BYTE *pOutBuf = new BYTE[nWidth * nHeight * 4];
212 struct SwsContext *context = dllSwScale.sws_getContext(picture.iWidth, picture.iHeight,
213 PIX_FMT_YUV420P, nWidth, nHeight, PIX_FMT_BGRA, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL);
214 uint8_t *src[] = { picture.data[0], picture.data[1], picture.data[2], 0 };
215 int srcStride[] = { picture.iLineSize[0], picture.iLineSize[1], picture.iLineSize[2], 0 };
216 uint8_t *dst[] = { pOutBuf, 0, 0, 0 };
217 int dstStride[] = { nWidth*4, 0, 0, 0 };
221 dllSwScale.sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride);
222 dllSwScale.sws_freeContext(context);
224 CPicture::CreateThumbnailFromSurface(pOutBuf, nWidth, nHeight, nWidth * 4, strTarget);
234 CLog::Log(LOGDEBUG,"%s - decode failed in %s", __FUNCTION__, strPath.c_str());
249 if(file.OpenForWrite(strTarget))
253 int nTotalTime = CTimeUtils::GetTimeMS() - nTime;
254 CLog::Log(LOGDEBUG,"%s - measured %d ms to extract thumb from file <%s> ", __FUNCTION__, nTotalTime, strPath.c_str());
259 void CDVDFileInfo::GetFileMetaData(const CStdString &strPath, CFileItem *pItem)
264 CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strPath, "");
267 CLog::Log(LOGERROR, "%s - Error creating stream for %s", __FUNCTION__, strPath.c_str());
271 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || !pInputStream->Open(strPath.c_str(), ""))
273 CLog::Log(LOGERROR, "%s - invalid stream in %s", __FUNCTION__, strPath.c_str());
278 CDVDDemuxFFmpeg *pDemuxer = new CDVDDemuxFFmpeg;
282 if (!pDemuxer->Open(pInputStream))
284 CLog::Log(LOGERROR, "%s - Error opening demuxer", __FUNCTION__);
292 CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
299 AVFormatContext *pContext = pDemuxer->m_pFormatContext;
302 int nLenMsec = pDemuxer->GetStreamLength();
303 CStdString strDuration;
304 int nHours = nLenMsec / 1000 / 60 / 60;
305 int nMinutes = ((nLenMsec / 1000) - nHours * 3600) / 60;
306 int nSec = (nLenMsec / 1000) - nHours * 3600 - nMinutes * 60;
307 strDuration.Format("%d", nLenMsec);
308 pItem->SetProperty("duration-msec", strDuration);
309 strDuration.Format("%02d:%02d:%02d", nHours, nMinutes, nSec);
310 pItem->SetProperty("duration-str", strDuration);
311 pItem->SetProperty("title", pContext->title);
312 pItem->SetProperty("author", pContext->author);
313 pItem->SetProperty("copyright", pContext->copyright);
314 pItem->SetProperty("comment", pContext->comment);
315 pItem->SetProperty("album", pContext->album);
316 strDuration.Format("%d", pContext->year);
317 pItem->SetProperty("year", strDuration);
318 strDuration.Format("%d", pContext->track);
319 pItem->SetProperty("track", strDuration);
320 pItem->SetProperty("genre", pContext->genre);
324 pInputStream->Close();
330 * \brief Open the item pointed to by pItem and extact streamdetails
331 * \return true if the stream details have changed
333 bool CDVDFileInfo::GetFileStreamDetails(CFileItem *pItem)
338 CStdString strFileNameAndPath;
339 if (pItem->HasVideoInfoTag())
340 strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
344 CStdString playablePath = strFileNameAndPath;
345 if (CUtil::IsStack(playablePath))
346 playablePath = XFILE::CStackDirectory::GetFirstStackedFile(playablePath);
348 CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, playablePath, "");
352 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || !pInputStream->Open(playablePath.c_str(), ""))
358 CDVDDemux *pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream);
361 bool retVal = DemuxerToStreamDetails(pInputStream, pDemuxer, pItem->GetVideoInfoTag()->m_streamDetails, strFileNameAndPath);
373 /* returns true if details have been added */
374 bool CDVDFileInfo::DemuxerToStreamDetails(CDVDInputStream *pInputStream, CDVDDemux *pDemux, CStreamDetails &details, const CStdString &path)
379 for (int iStream=0; iStream<pDemux->GetNrOfStreams(); iStream++)
381 CDemuxStream *stream = pDemux->GetStream(iStream);
382 if (stream->type == STREAM_VIDEO)
384 CStreamDetailVideo *p = new CStreamDetailVideo();
385 p->m_iWidth = ((CDemuxStreamVideo *)stream)->iWidth;
386 p->m_iHeight = ((CDemuxStreamVideo *)stream)->iHeight;
387 p->m_fAspect = ((CDemuxStreamVideo *)stream)->fAspect;
388 if (p->m_fAspect == 0.0f)
389 p->m_fAspect = (float)p->m_iWidth / p->m_iHeight;
390 pDemux->GetStreamCodecName(iStream, p->m_strCodec);
391 p->m_iDuration = pDemux->GetStreamLength();
394 if (CUtil::IsStack(path))
397 XFILE::CStackDirectory stack;
398 stack.GetDirectory(path, files);
400 // skip first path as we already know the duration
401 for (int i = 1; i < files.Size(); i++)
404 if (CDVDFileInfo::GetFileDuration(files[i]->m_strPath, duration))
405 p->m_iDuration = p->m_iDuration + duration;
409 // finally, calculate seconds
410 if (p->m_iDuration > 0)
411 p->m_iDuration = p->m_iDuration / 1000;
413 details.AddStream(p);
417 else if (stream->type == STREAM_AUDIO)
419 CStreamDetailAudio *p = new CStreamDetailAudio();
420 p->m_iChannels = ((CDemuxStreamAudio *)stream)->iChannels;
421 if (stream->language)
422 p->m_strLanguage = stream->language;
423 pDemux->GetStreamCodecName(iStream, p->m_strCodec);
424 details.AddStream(p);
428 else if (stream->type == STREAM_SUBTITLE)
430 if (stream->language)
432 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
433 p->m_strLanguage = stream->language;
434 details.AddStream(p);
440 details.DetermineBestStreams();
442 // correct bluray runtime. we need the duration from the input stream, not the demuxer.
443 if (pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
445 if(((CDVDInputStreamBluray*)pInputStream)->GetTotalTime() > 0)
447 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = ((CDVDInputStreamBluray*)pInputStream)->GetTotalTime() / 1000;