- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# MpaSplitterFile.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 "MpaSplitterFile.h"
    00025
    00026 #include <initguid.h>
    00027 #include "..\..\..\..\include\moreuuids.h"
    00028
    00029 //
    00030
    00031 static const LPCTSTR s_genre[] =
    00032 {
    00033         _T("Blues"), _T("Classic Rock"), _T("Country"), _T("Dance"),
    00034         _T("Disco"), _T("Funk"), _T("Grunge"), _T("Hip-Hop"),
    00035         _T("Jazz"), _T("Metal"), _T("New Age"), _T("Oldies"),
    00036         _T("Other"), _T("Pop"), _T("R&B"), _T("Rap"),
    00037         _T("Reggae"), _T("Rock"), _T("Techno"), _T("Industrial"),
    00038         _T("Alternative"), _T("Ska"), _T("Death Metal"), _T("Pranks"),
    00039         _T("Soundtrack"), _T("Euro-Techno"), _T("Ambient"), _T("Trip-Hop"),
    00040         _T("Vocal"), _T("Jazz+Funk"), _T("Fusion"), _T("Trance"),
    00041         _T("Classical"), _T("Instrumental"), _T("Acid"), _T("House"),
    00042         _T("Game"), _T("Sound Clip"), _T("Gospel"), _T("Noise"),
    00043         _T("Alternative Rock"), _T("Bass"), _T("Soul"), _T("Punk"),
    00044         _T("Space"), _T("Meditative"), _T("Instrumental Pop"), _T("Instrumental Rock"),
    00045         _T("Ethnic"), _T("Gothic"), _T("Darkwave"), _T("Techno-Industrial"),
    00046         _T("Electronic"), _T("Pop-Folk"), _T("Eurodance"), _T("Dream"),
    00047         _T("Southern Rock"), _T("Comedy"), _T("Cult"), _T("Gangsta"),
    00048         _T("Top 40"), _T("Christian Rap"), _T("Pop/Funk"), _T("Jungle"),
    00049         _T("Native US"), _T("Cabaret"), _T("New Wave"), _T("Psychadelic"),
    00050         _T("Rave"), _T("Showtunes"), _T("Trailer"), _T("Lo-Fi"),
    00051         _T("Tribal"), _T("Acid Punk"), _T("Acid Jazz"), _T("Polka"),
    00052         _T("Retro"), _T("Musical"), _T("Rock & Roll"), _T("Hard Rock"),
    00053         _T("Folk"), _T("Folk-Rock"), _T("National Folk"), _T("Swing"),
    00054         _T("Fast Fusion"), _T("Bebob"), _T("Latin"), _T("Revival"),
    00055         _T("Celtic"), _T("Bluegrass"), _T("Avantgarde"), _T("Gothic Rock"),
    00056         _T("Progressive Rock"), _T("Psychedelic Rock"), _T("Symphonic Rock"), _T("Slow Rock"),
    00057         _T("Big Band"), _T("Chorus"), _T("Easy Listening"), _T("Acoustic"),
    00058         _T("Humour"), _T("Speech"), _T("Chanson"), _T("Opera"),
    00059         _T("Chamber Music"), _T("Sonata"), _T("Symphony"), _T("Booty Bass"),
    00060         _T("Primus"), _T("Porn Groove"), _T("Satire"), _T("Slow Jam"),
    00061         _T("Club"), _T("Tango"), _T("Samba"), _T("Folklore"),
    00062         _T("Ballad"), _T("Power Ballad"), _T("Rhytmic Soul"), _T("Freestyle"),
    00063         _T("Duet"), _T("Punk Rock"), _T("Drum Solo"), _T("Acapella"),
    00064         _T("Euro-House"), _T("Dance Hall"), _T("Goa"), _T("Drum & Bass"),
    00065         _T("Club-House"), _T("Hardcore"), _T("Terror"), _T("Indie"),
    00066         _T("BritPop"), _T("Negerpunk"), _T("Polsk Punk"), _T("Beat"),
    00067         _T("Christian Gangsta"), _T("Heavy Metal"), _T("Black Metal"),
    00068         _T("Crossover"), _T("Contemporary C"), _T("Christian Rock"), _T("Merengue"), _T("Salsa"),
    00069         _T("Thrash Metal"), _T("Anime"), _T("JPop"), _T("SynthPop"),
    00070 };
    00071
    00072 //
    00073
    00074 CMpaSplitterFile::CMpaSplitterFile(IAsyncReader* pAsyncReader, HRESULT& hr)
    00075         : CBaseSplitterFileEx(pAsyncReader, hr)
    00076         , m_mode(none)
    00077         , m_rtDuration(0)
    00078         , m_startpos(0)
    00079         , m_endpos(0)
    00080         , m_totalbps(0)
    00081 {
    00082         if(SUCCEEDED(hr)) hr = Init();
    00083 }
    00084
    00085 HRESULT CMpaSplitterFile::Init()
    00086 {
    00087         m_startpos = 0;
    00088         m_endpos = GetLength();
    00089
    00090         if(m_endpos > 128)
    00091         {
    00092                 Seek(m_endpos - 128);
    00093
    00094                 if(BitRead(24) == 'TAG')
    00095                 {
    00096                         m_endpos -= 128;
    00097
    00098                         CStringA str;
    00099
    00100                         // title
    00101                         Read((BYTE*)str.GetBufferSetLength(30), 30);
    00102                         m_tags['TIT2'] = CString(str).Trim();
    00103
    00104                         // artist
    00105                         Read((BYTE*)str.GetBufferSetLength(30), 30);
    00106                         m_tags['TPE1'] = CString(str).Trim();
    00107
    00108                         // album
    00109                         Read((BYTE*)str.GetBufferSetLength(30), 30);
    00110                         m_tags['TALB'] = CString(str).Trim();
    00111
    00112                         // year
    00113                         Read((BYTE*)str.GetBufferSetLength(4), 4);
    00114                         m_tags['TYER'] = CString(str).Trim();
    00115
    00116                         // comment
    00117                         Read((BYTE*)str.GetBufferSetLength(30), 30);
    00118                         m_tags['COMM'] = CString(str).Trim();
    00119
    00120                         // track
    00121                         LPCSTR s = str;
    00122                         if(s[28] == 0 && s[29] != 0)
    00123                                 m_tags['TRCK'].Format(_T("%d"), s[29]);
    00124
    00125                         // genre
    00126                         BYTE genre = (BYTE)BitRead(8);
    00127                         if(genre < countof(s_genre))
    00128                                 m_tags['TCON'] = s_genre[genre];
    00129                 }
    00130         }
    00131
    00132         Seek(0);
    00133
    00134         if(BitRead(24, true) == 'ID3')
    00135         {
    00136                 Seek(3);
    00137
    00138                 BYTE major = (BYTE)BitRead(8);
    00139                 BYTE revision = (BYTE)BitRead(8);
    00140                 BYTE flags = (BYTE)BitRead(8);
    00141                 DWORD size = 0;
    00142                 if(BitRead(1) != 0) return E_FAIL;
    00143                 size |= BitRead(7) << 21;
    00144                 if(BitRead(1) != 0) return E_FAIL;
    00145                 size |= BitRead(7) << 14;
    00146                 if(BitRead(1) != 0) return E_FAIL;
    00147                 size |= BitRead(7) << 7;
    00148                 if(BitRead(1) != 0) return E_FAIL;
    00149                 size |= BitRead(7);
    00150
    00151                 m_startpos = GetPos() + size;
    00152
    00153                 // TODO: read tags
    00154         }
    00155
    00156         __int64 startpos;
    00157         int nBytesPerSec = 0;
    00158
    00159         Seek(m_startpos);
    00160
    00161         if(m_mode == none && Read(m_mpahdr, min(m_endpos - GetPos(), 0x100), true, &m_mt))
    00162         {
    00163                 m_mode = mpa;
    00164
    00165                 startpos = GetPos() - 4;
    00166                 nBytesPerSec = m_mpahdr.nBytesPerSec;
    00167
    00168                 // make sure the first frame is followed by another of the same kind (validates m_mpahdr basically)
    00169                 Seek(startpos + m_mpahdr.FrameSize);
    00170                 if(!Sync(4)) m_mode = none;
    00171         }
    00172
    00173         Seek(m_startpos);
    00174
    00175         if(m_mode == none && Read(m_aachdr, min(m_endpos - GetPos(), 0x100), &m_mt))
    00176         {
    00177                 m_mode = mp4a;
    00178
    00179                 startpos = GetPos() - (m_aachdr.fcrc?7:9);
    00180                 nBytesPerSec = ((WAVEFORMATEX*)m_mt.Format())->nAvgBytesPerSec;
    00181
    00182                 // make sure the first frame is followed by another of the same kind (validates m_aachdr basically)
    00183                 Seek(startpos + m_aachdr.aac_frame_length);
    00184                 if(!Sync(9)) m_mode = none;
    00185         }
    00186
    00187         if(m_mode == none)
    00188                 return E_FAIL;
    00189
    00190         m_startpos = startpos;
    00191
    00192         // initial duration, may not be correct (VBR files...)
    00193         m_rtDuration = 10000000i64 * (m_endpos - m_startpos) / nBytesPerSec;
    00194
    00195         return S_OK;
    00196 }
    00197
    00198 bool CMpaSplitterFile::Sync(int limit)
    00199 {
    00200         int FrameSize;
    00201         REFERENCE_TIME rtDuration;
    00202         return Sync(FrameSize, rtDuration, limit);
    00203 }
    00204
    00205 bool CMpaSplitterFile::Sync(int& FrameSize, REFERENCE_TIME& rtDuration, int limit)
    00206 {
    00207         __int64 endpos = min(m_endpos, GetPos() + limit);
    00208
    00209         if(m_mode == mpa)
    00210         {
    00211                 while(GetPos() <= endpos - 4)
    00212                 {
    00213                         mpahdr h;
    00214
    00215                         if(Read(h, endpos - GetPos(), true)
    00216                         && m_mpahdr.version == h.version
    00217                         && m_mpahdr.layer == h.layer
    00218                         && m_mpahdr.channels == h.channels)
    00219                         {
    00220                                 Seek(GetPos() - 4);
    00221                                 AdjustDuration(h.nBytesPerSec);
    00222
    00223                                 FrameSize = h.FrameSize;
    00224                                 rtDuration = h.rtDuration;
    00225
    00226                                 return true;
    00227                         }
    00228                 }
    00229         }
    00230         else if(m_mode == mp4a)
    00231         {
    00232                 while(GetPos() <= endpos - 9)
    00233                 {
    00234                         aachdr h;
    00235
    00236                         if(Read(h, endpos - GetPos())
    00237                         && m_aachdr.version == h.version
    00238                         && m_aachdr.layer == h.layer
    00239                         && m_aachdr.channels == h.channels)
    00240                         {
    00241                                 Seek(GetPos() - (h.fcrc?7:9));
    00242                                 AdjustDuration(h.nBytesPerSec);
    00243                                 Seek(GetPos() + (h.fcrc?7:9));
    00244
    00245                                 FrameSize = h.FrameSize;
    00246                                 rtDuration = h.rtDuration;
    00247
    00248                                 return true;
    00249                         }
    00250                 }
    00251         }
    00252
    00253         return false;
    00254 }
    00255
    00256 void CMpaSplitterFile::AdjustDuration(int nBytesPerSec)
    00257 {
    00258         ASSERT(nBytesPerSec);
    00259
    00260         int rValue;
    00261         if(!m_pos2bps.Lookup(GetPos(), rValue))
    00262         {
    00263                 m_totalbps += nBytesPerSec;
    00264                 if(!m_totalbps) return;
    00265                 m_pos2bps.SetAt(GetPos(), nBytesPerSec);
    00266                 __int64 avgbps = m_totalbps / m_pos2bps.GetCount();
    00267                 m_rtDuration = 10000000i64 * (m_endpos - m_startpos) / avgbps;
    00268         }
    00269 }