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 }

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