- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# MpegSplitterFile.cpp

    00001 /*
    00002  *      Copyright (C) 2003-2005 Gabest
    00003  *      http://www.gabest.org
    00004  *
    00005  *  This Program is free software; you can redistribute it and/or modify
    00006  *  it under the terms of the GNU General Public License as published by
    00007  *  the Free Software Foundation; either version 2, or (at your option)
    00008  *  any later version.
    00009  *
    00010  *  This Program is distributed in the hope that it will be useful,
    00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    00013  *  GNU General Public License for more details.
    00014  *
    00015  *  You should have received a copy of the GNU General Public License
    00016  *  along with GNU Make; see the file COPYING.  If not, write to
    00017  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
    00018  *  http://www.gnu.org/copyleft/gpl.html
    00019  *
    00020  */
    00021
    00022 #include "StdAfx.h"
    00023 #include <mmreg.h>
    00024 #include "MpegSplitterFile.h"
    00025
    00026 #include <initguid.h>
    00027 #include "..\..\..\..\include\moreuuids.h"
    00028
    00029 #define MEGABYTE 1024*1024
    00030 #define ISVALIDPID(pid) (pid >= 0x10 && pid < 0x1fff)
    00031
    00032 CMpegSplitterFile::CMpegSplitterFile(IAsyncReader* pAsyncReader, HRESULT& hr)
    00033         : CBaseSplitterFileEx(pAsyncReader, hr)
    00034         , m_type(us)
    00035         , m_rate(0)
    00036 {
    00037         if(SUCCEEDED(hr)) hr = Init();
    00038 }
    00039
    00040 HRESULT CMpegSplitterFile::Init()
    00041 {
    00042         HRESULT hr;
    00043
    00044         // get the type first
    00045
    00046         m_type = us;
    00047
    00048         Seek(0);
    00049
    00050         if(m_type == us)
    00051         {
    00052                 int cnt = 0, limit = 4;
    00053                 for(trhdr h; cnt < limit && Read(h); cnt++) Seek(h.next);
    00054                 if(cnt >= limit) m_type = ts;
    00055         }
    00056
    00057         Seek(0);
    00058
    00059         if(m_type == us)
    00060         {
    00061                 int cnt = 0, limit = 4;
    00062                 for(pvahdr h; cnt < limit && Read(h); cnt++) Seek(GetPos() + h.length);
    00063                 if(cnt >= limit) m_type = pva;
    00064         }
    00065
    00066         Seek(0);
    00067
    00068         if(m_type == us)
    00069         {
    00070                 BYTE b;
    00071                 for(int i = 0; (i < 4 || GetPos() < 65536) && m_type == us && NextMpegStartCode(b); i++)
    00072                 {
    00073                         if(b == 0xba)
    00074                         {
    00075                                 pshdr h;
    00076                                 if(Read(h))
    00077                                 {
    00078                                         m_type = ps;
    00079                                         m_rate = h.bitrate/8;
    00080                                         break;
    00081                                 }
    00082                         }
    00083                         else if((b&0xe0) == 0xc0 // audio, 110xxxxx, mpeg1/2/3
    00084                                 || (b&0xf0) == 0xe0 // video, 1110xxxx, mpeg1/2
    00085                                 // || (b&0xbd) == 0xbd) // private stream 1, 0xbd, ac3/dts/lpcm/subpic
    00086                                 || b == 0xbd) // private stream 1, 0xbd, ac3/dts/lpcm/subpic
    00087                         {
    00088                                 peshdr h;
    00089                                 if(Read(h, b))
    00090                                 {
    00091                                         m_type = es;
    00092                                 }
    00093                         }
    00094                 }
    00095         }
    00096
    00097         Seek(0);
    00098
    00099         if(m_type == us)
    00100         {
    00101                 return E_FAIL;
    00102         }
    00103
    00104         //
    00105
    00106         if(IsStreaming())
    00107         {
    00108                 for(int i = 0; i < 50 && GetLength() < 1024*100 || i < 20; i++)
    00109                         Sleep(100);
    00110         }
    00111
    00112         // min/max pts & bitrate
    00113
    00114         m_rtMin = m_posMin = _I64_MAX;
    00115         m_rtMax = m_posMax = 0;
    00116
    00117         CList<__int64> fps;
    00118         for(int i = 0, j = 5; i <= j; i++)
    00119                 fps.AddTail(i*GetLength()/j);
    00120
    00121         for(__int64 pfp = 0; fps.GetCount(); )
    00122         {
    00123                 __int64 fp = fps.RemoveHead();
    00124                 fp = min(GetLength() - MEGABYTE/8, fp);
    00125                 fp = max(pfp, fp);
    00126                 __int64 nfp = fp + (pfp == 0 ? 5*MEGABYTE : MEGABYTE/8);
    00127                 if(FAILED(hr = SearchStreams(fp, nfp)))
    00128                         return hr;
    00129                 pfp = nfp;
    00130         }
    00131
    00132         if(m_posMax - m_posMin <= 0 || m_rtMax - m_rtMin <= 0)
    00133                 return E_FAIL;
    00134
    00135         int indicated_rate = m_rate;
    00136         int detected_rate = 10000000i64 * (m_posMax - m_posMin) / (m_rtMax - m_rtMin);
    00137         // normally "detected" should always be less than "indicated", but sometimes it can be a few percent higher (+10% is allowed here)
    00138         // (update: also allowing +/-50k/s)
    00139         if(indicated_rate == 0 || ((float)detected_rate / indicated_rate) < 1.1
    00140         || abs(detected_rate - indicated_rate) < 50*1024)
    00141                 m_rate = detected_rate;
    00142         else ; // TODO: in this case disable seeking, or try doing something less drastical...
    00143
    00144 #ifndef DEBUG
    00145         if(m_streams[video].GetCount() || m_streams[subpic].GetCount())
    00146         {
    00147                 stream s;
    00148                 s.mt.majortype = MEDIATYPE_Video;
    00149                 s.mt.subtype = MEDIASUBTYPE_DVD_SUBPICTURE;
    00150                 s.mt.formattype = FORMAT_None;
    00151                 m_streams[subpic].Insert(s);
    00152         }
    00153 #endif
    00154
    00155         Seek(0);
    00156
    00157         return S_OK;
    00158 }
    00159
    00160 REFERENCE_TIME CMpegSplitterFile::NextPTS(DWORD TrackNum)
    00161 {
    00162         REFERENCE_TIME rt = -1;
    00163         __int64 rtpos = -1;
    00164
    00165         BYTE b;
    00166
    00167         while(GetPos() < GetLength())
    00168         {
    00169                 if(m_type == ps || m_type == es)
    00170                 {
    00171                         if(!NextMpegStartCode(b)) // continue;
    00172                                 {ASSERT(0); break;}
    00173
    00174                         rtpos = GetPos()-4;
    00175
    00176                         if(b >= 0xbd && b < 0xf0)
    00177                         {
    00178                                 peshdr h;
    00179                                 if(!Read(h, b) || !h.len) continue;
    00180
    00181                                 __int64 pos = GetPos();
    00182
    00183                                 if(h.fpts && AddStream(0, b, h.len) == TrackNum)
    00184                                 {
    00185                                         ASSERT(h.pts >= m_rtMin && h.pts <= m_rtMax);
    00186                                         rt = h.pts;
    00187                                         break;
    00188                                 }
    00189
    00190                                 Seek(pos + h.len);
    00191                         }
    00192                 }
    00193                 else if(m_type == ts)
    00194                 {
    00195                         trhdr h;
    00196                         if(!Read(h)) continue;
    00197
    00198                         rtpos = GetPos()-4;
    00199
    00200                         if(h.payload && h.payloadstart && ISVALIDPID(h.pid))
    00201                         {
    00202                                 peshdr h2;
    00203                                 if(NextMpegStartCode(b, 4) && Read(h2, b)) // pes packet
    00204                                 {
    00205                                         if(h2.fpts && AddStream(h.pid, b, h.bytes - (GetPos() - rtpos)) == TrackNum)
    00206                                         {
    00207                                                 ASSERT(h2.pts >= m_rtMin && h2.pts <= m_rtMax);
    00208                                                 rt = h2.pts;
    00209                                                 break;
    00210                                         }
    00211                                 }
    00212                         }
    00213
    00214                         Seek(h.next);
    00215                 }
    00216                 else if(m_type == pva)
    00217                 {
    00218                         pvahdr h;
    00219                         if(!Read(h)) continue;
    00220
    00221                         if(h.fpts)
    00222                         {
    00223                                 rt = h.pts;
    00224                                 break;
    00225                         }
    00226                 }
    00227         }
    00228
    00229         if(rtpos >= 0) Seek(rtpos);
    00230         if(rt >= 0) rt -= m_rtMin;
    00231
    00232         return rt;
    00233 }
    00234
    00235 HRESULT CMpegSplitterFile::SearchStreams(__int64 start, __int64 stop)
    00236 {
    00237         Seek(start);
    00238         stop = min(stop, GetLength());
    00239
    00240         while(GetPos() < stop)
    00241         {
    00242                 BYTE b;
    00243
    00244                 if(m_type == ps || m_type == es)
    00245                 {
    00246                         if(!NextMpegStartCode(b)) continue;
    00247
    00248                         if(b == 0xba) // program stream header
    00249                         {
    00250                                 pshdr h;
    00251                                 if(!Read(h)) continue;
    00252                         }
    00253                         else if(b == 0xbb) // program stream system header
    00254                         {
    00255                                 pssyshdr h;
    00256                                 if(!Read(h)) continue;
    00257                         }
    00258                         else if(b >= 0xbd && b < 0xf0) // pes packet
    00259                         {
    00260                                 peshdr h;
    00261                                 if(!Read(h, b)) continue;
    00262
    00263                                 if(h.type == mpeg2 && h.scrambling) {ASSERT(0); return E_FAIL;}
    00264
    00265                                 if(h.fpts)
    00266                                 {
    00267                                         if(m_rtMin == _I64_MAX) {m_rtMin = h.pts; m_posMin = GetPos();}
    00268                                         if(m_rtMin < h.pts && m_rtMax < h.pts) {m_rtMax = h.pts; m_posMax = GetPos();}
    00269 /*
    00270 int rate = 10000000i64 * (m_posMax - m_posMin) / (m_rtMax - m_rtMin);
    00271 if(m_rate == 0) m_rate = rate;
    00272 TRACE(_T("rate = %d (%d), (h.pts = %I64d)\n"), rate, rate - m_rate, h.pts);
    00273 m_rate = rate;
    00274 */
    00275                                 }
    00276
    00277                                 __int64 pos = GetPos();
    00278                                 AddStream(0, b, h.len);
    00279                                 if(h.len) Seek(pos + h.len);
    00280                         }
    00281                 }
    00282                 else if(m_type == ts)
    00283                 {
    00284                         trhdr h;
    00285                         if(!Read(h)) continue;
    00286
    00287                         // if(h.scrambling) {ASSERT(0); return E_FAIL;}
    00288
    00289                         __int64 pos = GetPos();
    00290
    00291                         if(h.payload && ISVALIDPID(h.pid))
    00292                         {
    00293                                 peshdr h2;
    00294                                 if(h.payloadstart && NextMpegStartCode(b, 4) && Read(h2, b)) // pes packet
    00295                                 {
    00296                                         if(h2.type == mpeg2 && h2.scrambling) {ASSERT(0); return E_FAIL;}
    00297
    00298                                         if(h2.fpts)
    00299                                         {
    00300                                                 if(m_rtMin == _I64_MAX) {m_rtMin = h2.pts; m_posMin = GetPos();}
    00301                                                 if(m_rtMin < h2.pts && m_rtMax < h2.pts) {m_rtMax = h2.pts; m_posMax = GetPos();}
    00302                                         }
    00303                                 }
    00304                                 else
    00305                                 {
    00306                                         b = 0;
    00307                                 }
    00308
    00309                                 AddStream(h.pid, b, h.bytes - (GetPos() - pos));
    00310                         }
    00311
    00312                         Seek(h.next);
    00313                 }
    00314                 else if(m_type == pva)
    00315                 {
    00316                         pvahdr h;
    00317                         if(!Read(h)) continue;
    00318
    00319                         if(h.fpts)
    00320                         {
    00321                                 if(m_rtMin == _I64_MAX) {m_rtMin = h.pts; m_posMin = GetPos();}
    00322                                 if(m_rtMin < h.pts && m_rtMax < h.pts) {m_rtMax = h.pts; m_posMax = GetPos();}
    00323                         }
    00324
    00325                         __int64 pos = GetPos();
    00326                         if(h.streamid == 1) AddStream(h.streamid, 0xe0, h.length);
    00327                         else if(h.streamid == 2) AddStream(h.streamid, 0xc0, h.length);
    00328                         if(h.length) Seek(pos + h.length);
    00329                 }
    00330         }
    00331
    00332         return S_OK;
    00333 }
    00334
    00335 DWORD CMpegSplitterFile::AddStream(WORD pid, BYTE pesid, DWORD len)
    00336 {
    00337         if(pid)
    00338         {
    00339                 if(pesid) m_pid2pes[pid] = pesid;
    00340                 else m_pid2pes.Lookup(pid, pesid);
    00341         }
    00342
    00343         stream s;
    00344         s.pid = pid;
    00345         s.pesid = pesid;
    00346
    00347         int type = unknown;
    00348
    00349         if(pesid >= 0xe0 && pesid < 0xf0) // mpeg video
    00350         {
    00351                 __int64 pos = GetPos();
    00352
    00353                 if(type == unknown)
    00354                 {
    00355                         CMpegSplitterFile::seqhdr h;
    00356                         if(!m_streams[video].Find(s) && Read(h, len, &s.mt))
    00357                                 type = video;
    00358                 }
    00359
    00360                 Seek(pos);
    00361
    00362                 if(type == unknown)
    00363                 {
    00364                         CMpegSplitterFile::avchdr h;
    00365                         if(!m_streams[video].Find(s) && Read(h, len, &s.mt))
    00366                                 type = video;
    00367                 }
    00368         }
    00369         else if(pesid >= 0xc0 && pesid < 0xe0) // mpeg audio
    00370         {
    00371                 __int64 pos = GetPos();
    00372
    00373                 if(type == unknown)
    00374                 {
    00375                         CMpegSplitterFile::mpahdr h;
    00376                         if(!m_streams[audio].Find(s) && Read(h, len, false, &s.mt))
    00377                                 type = audio;
    00378                 }
    00379
    00380                 Seek(pos);
    00381
    00382                 if(type == unknown)
    00383                 {
    00384                         CMpegSplitterFile::aachdr h;
    00385                         if(!m_streams[audio].Find(s) && Read(h, len, &s.mt))
    00386                                 type = audio;
    00387                 }
    00388         }
    00389         else if(pesid == 0xbd) // private stream 1
    00390         {
    00391                 if(s.pid)
    00392                 {
    00393                         if(!m_streams[audio].Find(s))
    00394                         {
    00395                                 __int64 pos = GetPos();
    00396
    00397                                 if(type == unknown)
    00398                                 {
    00399                                         CMpegSplitterFile::ac3hdr h;
    00400                                         if(Read(h, len, &s.mt))
    00401                                                 type = audio;
    00402                                 }
    00403
    00404                                 Seek(pos);
    00405
    00406                                 if(type == unknown)
    00407                                 {
    00408                                         CMpegSplitterFile::dtshdr h;
    00409                                         if(Read(h, len, &s.mt))
    00410                                                 type = audio;
    00411                                 }
    00412                         }
    00413                 }
    00414                 else
    00415                 {
    00416                         BYTE b = (BYTE)BitRead(8, true);
    00417                         WORD w = (WORD)BitRead(16, true);
    00418                         DWORD dw = (DWORD)BitRead(32, true);
    00419
    00420                         if(b >= 0x80 && b < 0x88 || w == 0x0b77) // ac3
    00421                         {
    00422                                 s.ps1id = (b >= 0x80 && b < 0x88) ? (BYTE)(BitRead(32) >> 24) : 0x80;
    00423
    00424                                 CMpegSplitterFile::ac3hdr h;
    00425                                 if(!m_streams[audio].Find(s) && Read(h, len, &s.mt))
    00426                                         type = audio;
    00427                         }
    00428                         else if(b >= 0x88 && b < 0x90 || dw == 0x7ffe8001) // dts
    00429                         {
    00430                                 s.ps1id = (b >= 0x88 && b < 0x90) ? (BYTE)(BitRead(32) >> 24) : 0x88;
    00431
    00432                                 CMpegSplitterFile::dtshdr h;
    00433                                 if(!m_streams[audio].Find(s) && Read(h, len, &s.mt))
    00434                                         type = audio;
    00435                         }
    00436                         else if(b >= 0xa0 && b < 0xa8) // lpcm
    00437                         {
    00438                                 s.ps1id = (b >= 0xa0 && b < 0xa8) ? (BYTE)(BitRead(32) >> 24) : 0xa0;
    00439
    00440                                 CMpegSplitterFile::lpcmhdr h;
    00441                                 if(Read(h, &s.mt) && !m_streams[audio].Find(s)) // note the reversed order, the header should be stripped always even if it's not a new stream
    00442                                         type = audio;
    00443                         }
    00444                         else if(b >= 0x20 && b < 0x40) // DVD subpic
    00445                         {
    00446                                 s.ps1id = (BYTE)BitRead(8);
    00447
    00448                                 CMpegSplitterFile::dvdspuhdr h;
    00449                                 if(!m_streams[subpic].Find(s) && Read(h, &s.mt))
    00450                                         type = subpic;
    00451                         }
    00452                         else if(b >= 0x70 && b < 0x80) // SVCD subpic
    00453                         {
    00454                                 s.ps1id = (BYTE)BitRead(8);
    00455
    00456                                 CMpegSplitterFile::svcdspuhdr h;
    00457                                 if(!m_streams[subpic].Find(s) && Read(h, &s.mt))
    00458                                         type = subpic;
    00459                         }
    00460                         else if(b >= 0x00 && b < 0x10) // CVD subpic
    00461                         {
    00462                                 s.ps1id = (BYTE)BitRead(8);
    00463
    00464                                 CMpegSplitterFile::cvdspuhdr h;
    00465                                 if(!m_streams[subpic].Find(s) && Read(h, &s.mt))
    00466                                         type = subpic;
    00467                         }
    00468                         else if(w == 0xffa0 || w == 0xffa1) // ps2-mpg audio
    00469                         {
    00470                                 s.ps1id = (BYTE)BitRead(8);
    00471                                 s.pid = (WORD)((BitRead(8) << 8) | BitRead(16)); // pid = 0xa000 | track id
    00472
    00473                                 CMpegSplitterFile::ps2audhdr h;
    00474                                 if(!m_streams[audio].Find(s) && Read(h, &s.mt))
    00475                                         type = audio;
    00476                         }
    00477                         else if(w == 0xff90) // ps2-mpg ac3 or subtitles
    00478                         {
    00479                                 s.ps1id = (BYTE)BitRead(8);
    00480                                 s.pid = (WORD)((BitRead(8) << 8) | BitRead(16)); // pid = 0x9000 | track id
    00481
    00482                                 w = BitRead(16, true);
    00483
    00484                                 if(w == 0x0b77)
    00485                                 {
    00486                                         CMpegSplitterFile::ac3hdr h;
    00487                                         if(!m_streams[audio].Find(s) && Read(h, len, &s.mt))
    00488                                                 type = audio;
    00489                                 }
    00490                                 else if(w == 0x0000) // usually zero...
    00491                                 {
    00492                                         CMpegSplitterFile::ps2subhdr h;
    00493                                         if(!m_streams[subpic].Find(s) && Read(h, &s.mt))
    00494                                                 type = subpic;
    00495                                 }
    00496                         }
    00497                 }
    00498         }
    00499         else if(pesid == 0xbe) // padding
    00500         {
    00501         }
    00502         else if(pesid == 0xbf) // private stream 2
    00503         {
    00504         }
    00505
    00506         if(type != unknown && !m_streams[type].Find(s))
    00507         {
    00508                 if(s.pid)
    00509                 {
    00510                         for(int i = 0; i < unknown; i++)
    00511                         {
    00512                                 if(m_streams[i].Find(s)) {/*ASSERT(0);*/ return s;}
    00513                         }
    00514                 }
    00515
    00516                 m_streams[type].Insert(s);
    00517         }
    00518
    00519         return s;
    00520 }