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 }

Generated on Tue Dec 13 14:47:21 2005 for guliverkli by  doxygen 1.4.5