2 * (C) 2006-2012 see Authors.txt
4 * This file is part of MPC-HC.
6 * MPC-HC is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * MPC-HC is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "HdmvClipInfo.h"
25 extern LCID
ISO6392ToLcid(LPCSTR code
);
27 CHdmvClipInfo::CHdmvClipInfo(void) :
28 SequenceInfo_start_address(0),
29 ProgramInfo_start_address(0)
31 m_hFile
= INVALID_HANDLE_VALUE
;
35 CHdmvClipInfo::~CHdmvClipInfo()
40 HRESULT
CHdmvClipInfo::CloseFile(HRESULT hr
)
42 if (m_hFile
!= INVALID_HANDLE_VALUE
) {
44 m_hFile
= INVALID_HANDLE_VALUE
;
49 DWORD
CHdmvClipInfo::ReadDword()
51 return ReadByte() << 24 | ReadByte() << 16 | ReadByte() << 8 | ReadByte();
54 short CHdmvClipInfo::ReadShort()
56 return ReadByte() << 8 | ReadByte();
59 BYTE
CHdmvClipInfo::ReadByte()
63 ReadFile(m_hFile
, &bVal
, sizeof(bVal
), &dwRead
, NULL
);
68 void CHdmvClipInfo::ReadBuffer(BYTE
* pBuff
, DWORD nLen
)
71 ReadFile(m_hFile
, pBuff
, nLen
, &dwRead
, NULL
);
74 HRESULT
CHdmvClipInfo::ReadProgramInfo()
76 BYTE number_of_program_sequences
;
77 BYTE number_of_streams_in_ps
;
80 m_Streams
.RemoveAll();
81 Pos
.QuadPart
= ProgramInfo_start_address
;
82 SetFilePointerEx(m_hFile
, Pos
, NULL
, FILE_BEGIN
);
85 ReadByte(); //reserved_for_word_align
86 number_of_program_sequences
= (BYTE
)ReadByte();
88 for (size_t i
= 0; i
< number_of_program_sequences
; i
++) {
89 ReadDword(); //SPN_program_sequence_start
90 ReadShort(); //program_map_PID
91 number_of_streams_in_ps
= (BYTE
)ReadByte(); //number_of_streams_in_ps
92 ReadByte(); //reserved_for_future_use
94 for (size_t stream_index
= 0; stream_index
< number_of_streams_in_ps
; stream_index
++) {
95 m_Streams
.SetCount(iStream
+ 1);
96 m_Streams
[iStream
].m_PID
= ReadShort(); // stream_PID
98 // == StreamCodingInfo
100 SetFilePointerEx(m_hFile
, Pos
, &Pos
, FILE_CURRENT
);
101 Pos
.QuadPart
+= ReadByte() + 1; // length
102 m_Streams
[iStream
].m_Type
= (PES_STREAM_TYPE
)ReadByte();
104 switch (m_Streams
[iStream
].m_Type
) {
105 case VIDEO_STREAM_MPEG1
:
106 case VIDEO_STREAM_MPEG2
:
107 case VIDEO_STREAM_H264
:
108 case VIDEO_STREAM_VC1
: {
109 UINT8 Temp
= ReadByte();
110 BDVM_VideoFormat VideoFormat
= (BDVM_VideoFormat
)(Temp
>> 4);
111 BDVM_FrameRate FrameRate
= (BDVM_FrameRate
)(Temp
& 0xf);
113 BDVM_AspectRatio AspectRatio
= (BDVM_AspectRatio
)(Temp
>> 4);
115 m_Streams
[iStream
].m_VideoFormat
= VideoFormat
;
116 m_Streams
[iStream
].m_FrameRate
= FrameRate
;
117 m_Streams
[iStream
].m_AspectRatio
= AspectRatio
;
120 case AUDIO_STREAM_MPEG1
:
121 case AUDIO_STREAM_MPEG2
:
122 case AUDIO_STREAM_LPCM
:
123 case AUDIO_STREAM_AC3
:
124 case AUDIO_STREAM_DTS
:
125 case AUDIO_STREAM_AC3_TRUE_HD
:
126 case AUDIO_STREAM_AC3_PLUS
:
127 case AUDIO_STREAM_DTS_HD
:
128 case AUDIO_STREAM_DTS_HD_MASTER_AUDIO
:
129 case SECONDARY_AUDIO_AC3_PLUS
:
130 case SECONDARY_AUDIO_DTS_HD
: {
131 UINT8 Temp
= ReadByte();
132 BDVM_ChannelLayout ChannelLayout
= (BDVM_ChannelLayout
)(Temp
>> 4);
133 BDVM_SampleRate SampleRate
= (BDVM_SampleRate
)(Temp
& 0xF);
135 ReadBuffer((BYTE
*)m_Streams
[iStream
].m_LanguageCode
, 3);
136 m_Streams
[iStream
].m_LCID
= ISO6392ToLcid(m_Streams
[iStream
].m_LanguageCode
);
137 m_Streams
[iStream
].m_ChannelLayout
= ChannelLayout
;
138 m_Streams
[iStream
].m_SampleRate
= SampleRate
;
141 case PRESENTATION_GRAPHICS_STREAM
:
142 case INTERACTIVE_GRAPHICS_STREAM
: {
143 ReadBuffer((BYTE
*)m_Streams
[iStream
].m_LanguageCode
, 3);
144 m_Streams
[iStream
].m_LCID
= ISO6392ToLcid(m_Streams
[iStream
].m_LanguageCode
);
147 case SUBTITLE_STREAM
: {
148 ReadByte(); // Should this really be here?
149 ReadBuffer((BYTE
*)m_Streams
[iStream
].m_LanguageCode
, 3);
150 m_Streams
[iStream
].m_LCID
= ISO6392ToLcid(m_Streams
[iStream
].m_LanguageCode
);
158 SetFilePointerEx(m_hFile
, Pos
, NULL
, FILE_BEGIN
);
164 HRESULT
CHdmvClipInfo::ReadInfo(LPCTSTR strFile
)
167 m_hFile
= CreateFile(strFile
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
168 OPEN_EXISTING
, FILE_ATTRIBUTE_READONLY
| FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
170 if (m_hFile
!= INVALID_HANDLE_VALUE
) {
173 if (memcmp(Buff
, "HDMV", 4)) {
174 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
178 if ((memcmp(Buff
, "0200", 4) != 0) && (memcmp(Buff
, "0100", 4) != 0)) {
179 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
182 SequenceInfo_start_address
= ReadDword();
183 ProgramInfo_start_address
= ReadDword();
189 return CloseFile(S_OK
);
192 return AmHresultFromWin32(GetLastError());
195 CHdmvClipInfo::Stream
* CHdmvClipInfo::FindStream(short wPID
)
197 size_t nStreams
= m_Streams
.GetCount();
198 for (size_t i
= 0; i
< nStreams
; i
++) {
199 if (m_Streams
[i
].m_PID
== wPID
) {
200 return &m_Streams
[i
];
207 LPCTSTR
CHdmvClipInfo::Stream::Format()
210 case VIDEO_STREAM_MPEG1
:
212 case VIDEO_STREAM_MPEG2
:
214 case VIDEO_STREAM_H264
:
216 case VIDEO_STREAM_VC1
:
218 case AUDIO_STREAM_MPEG1
:
220 case AUDIO_STREAM_MPEG2
:
222 case AUDIO_STREAM_LPCM
:
224 case AUDIO_STREAM_AC3
:
226 case AUDIO_STREAM_DTS
:
228 case AUDIO_STREAM_AC3_TRUE_HD
:
230 case AUDIO_STREAM_AC3_PLUS
:
232 case AUDIO_STREAM_DTS_HD
:
234 case AUDIO_STREAM_DTS_HD_MASTER_AUDIO
:
235 return _T("DTS-HD XLL");
236 case SECONDARY_AUDIO_AC3_PLUS
:
237 return _T("Sec DD+");
238 case SECONDARY_AUDIO_DTS_HD
:
239 return _T("Sec DTS-HD");
240 case PRESENTATION_GRAPHICS_STREAM
:
242 case INTERACTIVE_GRAPHICS_STREAM
:
244 case SUBTITLE_STREAM
:
247 return _T("Unknown");
251 HRESULT
CHdmvClipInfo::ReadPlaylist(CString strPlaylistFile
, REFERENCE_TIME
& rtDuration
, CAtlList
<PlaylistItem
>& Playlist
)
253 CPath
Path(strPlaylistFile
);
257 Path
.RemoveFileSpec();
258 Path
.RemoveFileSpec();
260 m_hFile
= CreateFile(strPlaylistFile
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
261 OPEN_EXISTING
, FILE_ATTRIBUTE_READONLY
| FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
263 if (m_hFile
!= INVALID_HANDLE_VALUE
) {
265 bool bDuplicate
= false;
267 if (memcmp(Buff
, "MPLS", 4)) {
268 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
272 if ((memcmp(Buff
, "0200", 4) != 0) && (memcmp(Buff
, "0100", 4) != 0)) {
273 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
278 unsigned short nPlaylistItems
;
280 Pos
.QuadPart
= ReadDword(); // PlayList_start_address
281 ReadDword(); // PlayListMark_start_address
284 SetFilePointerEx(m_hFile
, Pos
, NULL
, FILE_BEGIN
);
285 ReadDword(); // length
286 ReadShort(); // reserved_for_future_use
287 nPlaylistItems
= ReadShort(); // number_of_PlayItems
288 ReadShort(); // number_of_SubPaths
291 for (size_t i
= 0; i
< nPlaylistItems
; i
++) {
293 SetFilePointerEx(m_hFile
, Pos
, NULL
, FILE_BEGIN
);
294 Pos
.QuadPart
+= ReadShort() + 2;
296 Item
.m_strFileName
.Format(_T("%s\\STREAM\\%c%c%c%c%c.M2TS"), Path
, Buff
[0], Buff
[1], Buff
[2], Buff
[3], Buff
[4]);
299 if (memcmp(Buff
, "M2TS", 4)) {
300 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
304 dwTemp
= ReadDword();
305 Item
.m_rtIn
= 20000i64
* dwTemp
/ 90; // Carefull : 32->33 bits!
307 dwTemp
= ReadDword();
308 Item
.m_rtOut
= 20000i64
* dwTemp
/ 90; // Carefull : 32->33 bits!
310 rtDuration
+= (Item
.m_rtOut
- Item
.m_rtIn
);
312 if (Playlist
.Find(Item
) != NULL
) {
315 Playlist
.AddTail(Item
);
317 //TRACE(_T("File : %S, Duration : %S, Total duration : %S\n"), strTemp, ReftimeToString (rtOut - rtIn), ReftimeToString (rtDuration));
321 return bDuplicate
? S_FALSE
: S_OK
;
324 return AmHresultFromWin32(GetLastError());
327 HRESULT
CHdmvClipInfo::ReadChapters(CString strPlaylistFile
, CAtlList
<CHdmvClipInfo::PlaylistItem
>& PlaylistItems
, CAtlList
<PlaylistChapter
>& Chapters
)
329 CPath
Path(strPlaylistFile
);
332 Path
.RemoveFileSpec();
333 Path
.RemoveFileSpec();
335 m_hFile
= CreateFile(strPlaylistFile
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
336 OPEN_EXISTING
, FILE_ATTRIBUTE_READONLY
| FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
338 if (m_hFile
!= INVALID_HANDLE_VALUE
) {
339 REFERENCE_TIME
* rtOffset
= DNew REFERENCE_TIME
[PlaylistItems
.GetCount()];
340 REFERENCE_TIME rtSum
= 0;
343 bool bDuplicate
= false;
345 POSITION pos
= PlaylistItems
.GetHeadPosition();
347 CHdmvClipInfo::PlaylistItem
& PI
= PlaylistItems
.GetNext(pos
);
349 rtOffset
[nIndex
] = rtSum
- PI
.m_rtIn
;
350 rtSum
= rtSum
+ PI
.Duration();
355 if (memcmp(Buff
, "MPLS", 4)) {
356 SAFE_DELETE_ARRAY(rtOffset
);
357 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
361 if ((memcmp(Buff
, "0200", 4) != 0) && (memcmp(Buff
, "0100", 4) != 0)) {
362 SAFE_DELETE_ARRAY(rtOffset
);
363 return CloseFile(VFW_E_INVALID_FILE_FORMAT
);
367 unsigned short nMarkCount
;
369 ReadDword(); // PlayList_start_address
370 Pos
.QuadPart
= ReadDword(); // PlayListMark_start_address
373 SetFilePointerEx(m_hFile
, Pos
, NULL
, FILE_BEGIN
);
374 ReadDword(); // length
375 nMarkCount
= ReadShort(); // number_of_PlayList_marks
376 for (size_t i
= 0; i
< nMarkCount
; i
++) {
377 PlaylistChapter Chapter
;
379 ReadByte(); // reserved_for_future_use
380 Chapter
.m_nMarkType
= (PlaylistMarkType
)ReadByte(); // mark_type
381 Chapter
.m_nPlayItemId
= ReadShort(); // ref_to_PlayItem_id
382 Chapter
.m_rtTimestamp
= 20000i64
* ReadDword() / 90 + rtOffset
[Chapter
.m_nPlayItemId
]; // mark_time_stamp
383 Chapter
.m_nEntryPID
= ReadShort(); // entry_ES_PID
384 Chapter
.m_rtDuration
= 20000i64
* ReadDword() / 90; // duration
386 Chapters
.AddTail(Chapter
);
388 //TRACE(_T("Chapter %d : %S\n"), i, ReftimeToString(Chapter.m_rtTimestamp));
392 SAFE_DELETE_ARRAY(rtOffset
);
393 return bDuplicate
? S_FALSE
: S_OK
;
396 return AmHresultFromWin32(GetLastError());
401 HRESULT
CHdmvClipInfo::FindMainMovie(LPCTSTR strFolder
, CString
& strPlaylistFile
, CAtlList
<PlaylistItem
>& MainPlaylist
, CAtlList
<PlaylistItem
>& MPLSPlaylists
)
405 CString
strPath(strFolder
);
408 MPLSPlaylists
.RemoveAll();
410 CAtlList
<PlaylistItem
> Playlist
;
411 WIN32_FIND_DATA fd
= {0};
413 strPath
.Replace(_T("\\PLAYLIST\\"), _T("\\"));
414 strPath
.Replace(_T("\\STREAM\\"), _T("\\"));
415 strPath
+= _T("\\BDMV\\");
416 strFilter
.Format(_T("%sPLAYLIST\\*.mpls"), strPath
);
418 HANDLE hFind
= FindFirstFile(strFilter
, &fd
);
419 if (hFind
!= INVALID_HANDLE_VALUE
) {
420 REFERENCE_TIME rtMax
= 0;
421 REFERENCE_TIME rtCurrent
;
422 CString strCurrentPlaylist
;
424 strCurrentPlaylist
.Format(_T("%sPLAYLIST\\%s"), strPath
, fd
.cFileName
);
425 Playlist
.RemoveAll();
427 // Main movie shouldn't have duplicate M2TS filename...
428 if (ReadPlaylist(strCurrentPlaylist
, rtCurrent
, Playlist
) == S_OK
) {
429 if (rtCurrent
> rtMax
) {
431 strPlaylistFile
= strCurrentPlaylist
;
432 MainPlaylist
.RemoveAll();
433 POSITION pos
= Playlist
.GetHeadPosition();
435 MainPlaylist
.AddTail(Playlist
.GetNext(pos
));
439 if (rtCurrent
>= (REFERENCE_TIME
)MIN_LIMIT
* 600000000) {
441 Item
.m_strFileName
= strCurrentPlaylist
;
443 Item
.m_rtOut
= rtCurrent
;
444 MPLSPlaylists
.AddTail(Item
);
448 } while (FindNextFile(hFind
, &fd
));
453 if (MPLSPlaylists
.GetCount() > 1) {
455 for (size_t j
= 0; j
< MPLSPlaylists
.GetCount(); j
++) {
456 for (size_t i
= 0; i
< MPLSPlaylists
.GetCount() - 1; i
++) {
457 if (MPLSPlaylists
.GetAt(MPLSPlaylists
.FindIndex(i
)).Duration() < MPLSPlaylists
.GetAt(MPLSPlaylists
.FindIndex(i
+ 1)).Duration()) {
458 MPLSPlaylists
.SwapElements(MPLSPlaylists
.FindIndex(i
), MPLSPlaylists
.FindIndex(i
+ 1));