src/3rdparty/phonon/ds9/qaudiocdreader.cpp
changeset 0 1918ee327afb
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5 This library is free software: you can redistribute it and/or modify
       
     6 it under the terms of the GNU Lesser General Public License as published by
       
     7 the Free Software Foundation, either version 2.1 or 3 of the License.
       
     8 
       
     9 This library is distributed in the hope that it will be useful,
       
    10 but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 GNU Lesser General Public License for more details.
       
    13 
       
    14 You should have received a copy of the GNU Lesser General Public License
       
    15 along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 #include "qaudiocdreader.h"
       
    19 #include <dshow.h>
       
    20 #include <initguid.h>
       
    21 
       
    22 #include <winioctl.h> // needed for FILE_DEVICE_CD_ROM etc
       
    23 
       
    24 #define IOCTL_CDROM_READ_TOC    CTL_CODE(FILE_DEVICE_CD_ROM, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
       
    25 #define IOCTL_CDROM_RAW_READ    CTL_CODE(FILE_DEVICE_CD_ROM, 0x000F, METHOD_OUT_DIRECT,  FILE_READ_ACCESS)
       
    26 
       
    27 QT_BEGIN_NAMESPACE
       
    28 
       
    29 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
    30 
       
    31 namespace Phonon
       
    32 {
       
    33     namespace DS9
       
    34     {
       
    35         // {CA46BFE1-D55B-4adf-B803-BC2B9AD57824}
       
    36         DEFINE_GUID(IID_ITitleInterface, 
       
    37             0xca46bfe1, 0xd55b, 0x4adf, 0xb8, 0x3, 0xbc, 0x2b, 0x9a, 0xd5, 0x78, 0x24);
       
    38 
       
    39         struct TRACK_DATA {
       
    40             UCHAR Reserved;
       
    41             UCHAR Control : 4;
       
    42             UCHAR Adr : 4;
       
    43             UCHAR TrackNumber;
       
    44             UCHAR Reserved1;
       
    45             UCHAR Address[4];
       
    46         };
       
    47 
       
    48         struct CDROM_TOC {
       
    49             UCHAR Length[2];
       
    50             UCHAR FirstTrack;
       
    51             UCHAR LastTrack;
       
    52             TRACK_DATA TrackData[100];
       
    53         };
       
    54 
       
    55         struct WaveStructure
       
    56         {
       
    57             WaveStructure();
       
    58 
       
    59             char riff[4];
       
    60             qint32 chunksize;
       
    61             char wave[4];
       
    62             char fmt[4];
       
    63             const qint32 chunksize2;
       
    64             const quint16 formatTag;
       
    65             const quint16 nChannels;
       
    66             const quint32 nSamplesPerSec; 
       
    67             const quint32 nAvgBytesPerSec;
       
    68             const quint16 nBlockAlign;
       
    69             const quint16 bitsPerSample;
       
    70             char data[4];
       
    71             qint32 dataLength;
       
    72         };
       
    73 
       
    74         enum TRACK_MODE_TYPE {
       
    75             YellowMode2,
       
    76             XAForm2,
       
    77             CDDA
       
    78         };
       
    79 
       
    80 
       
    81         struct RAW_READ_INFO {
       
    82             LARGE_INTEGER DiskOffset;
       
    83             ULONG    SectorCount;
       
    84             TRACK_MODE_TYPE TrackMode;
       
    85         };
       
    86 
       
    87         class QAudioCDReader : public QAsyncReader, public ITitleInterface
       
    88         {
       
    89         public:
       
    90             QAudioCDReader(QBaseFilter *parent, QChar drive = QChar());
       
    91             ~QAudioCDReader();
       
    92 
       
    93             //reimplementation from IUnknown
       
    94             STDMETHODIMP_(ULONG) AddRef();
       
    95             STDMETHODIMP_(ULONG) Release();
       
    96 
       
    97             STDMETHODIMP Length(LONGLONG *,LONGLONG *);
       
    98             STDMETHODIMP QueryInterface(REFIID iid, void** out);
       
    99             QList<qint64> titles() const;
       
   100 
       
   101         protected:
       
   102             HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual);
       
   103 
       
   104         private:
       
   105             HANDLE m_cddrive;
       
   106             CDROM_TOC m_toc;
       
   107             WaveStructure m_waveHeader;
       
   108             qint64 m_trackAddress;
       
   109         };
       
   110 
       
   111 
       
   112 #define SECTOR_SIZE 2352
       
   113 #define NB_SECTORS_READ 20
       
   114 
       
   115         static const AM_MEDIA_TYPE audioCDMediaType = { MEDIATYPE_Stream, MEDIASUBTYPE_WAVE, TRUE, FALSE, 1, GUID_NULL, 0, 0, 0};
       
   116  
       
   117         int addressToSectors(UCHAR address[4])
       
   118         {
       
   119             return ((address[0] * 60 + address[1]) * 60 + address[2]) * 75 + address[3] - 150;
       
   120         }
       
   121 
       
   122         WaveStructure::WaveStructure() : chunksize(0), chunksize2(16), 
       
   123             formatTag(WAVE_FORMAT_PCM), nChannels(2), nSamplesPerSec(44100), nAvgBytesPerSec(176400), nBlockAlign(4), bitsPerSample(16),
       
   124             dataLength(0)
       
   125         {
       
   126             qMemCopy(riff, "RIFF", 4);
       
   127             qMemCopy(wave, "WAVE", 4);
       
   128             qMemCopy(fmt,  "fmt ", 4);                    
       
   129             qMemCopy(data, "data", 4);
       
   130         }
       
   131 
       
   132 
       
   133         QAudioCDReader::QAudioCDReader(QBaseFilter *parent, QChar drive) : QAsyncReader(parent, QVector<AM_MEDIA_TYPE>() << audioCDMediaType)
       
   134         {
       
   135             //now open the cd-drive
       
   136             QString path; 
       
   137             if (drive.isNull()) {
       
   138                 path = QString::fromLatin1("\\\\.\\Cdrom0"); 	 
       
   139             } else { 	 
       
   140                 path = QString::fromLatin1("\\\\.\\%1:").arg(drive); 	 
       
   141             }
       
   142 
       
   143             m_cddrive = ::CreateFile((const wchar_t *)path.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
       
   144 
       
   145             qMemSet(&m_toc, 0, sizeof(CDROM_TOC));
       
   146             //read the TOC
       
   147             DWORD bytesRead = 0;
       
   148             bool tocRead = ::DeviceIoControl(m_cddrive, IOCTL_CDROM_READ_TOC, 0, 0, &m_toc, sizeof(CDROM_TOC), &bytesRead, 0);
       
   149 
       
   150             if (!tocRead) {
       
   151                 qWarning("unable to load the TOC from the CD");
       
   152                 return;
       
   153             }
       
   154 
       
   155             m_trackAddress = addressToSectors(m_toc.TrackData[0].Address);
       
   156             const qint32 nbSectorsToRead = (addressToSectors(m_toc.TrackData[m_toc.LastTrack + 1 - m_toc.FirstTrack].Address) 
       
   157                 - m_trackAddress);
       
   158             const qint32 dataLength = nbSectorsToRead * SECTOR_SIZE;
       
   159 
       
   160             m_waveHeader.chunksize = 4 + (8 + m_waveHeader.chunksize2) + (8 + dataLength);
       
   161             m_waveHeader.dataLength = dataLength;
       
   162         }
       
   163 
       
   164         QAudioCDReader::~QAudioCDReader()
       
   165         {
       
   166             ::CloseHandle(m_cddrive);
       
   167         }
       
   168 
       
   169         STDMETHODIMP_(ULONG) QAudioCDReader::AddRef()
       
   170         {
       
   171             return QAsyncReader::AddRef();
       
   172         }
       
   173 
       
   174         STDMETHODIMP_(ULONG) QAudioCDReader::Release()
       
   175         {
       
   176             return QAsyncReader::Release();
       
   177         }
       
   178 
       
   179 
       
   180         STDMETHODIMP QAudioCDReader::Length(LONGLONG *total,LONGLONG *available)
       
   181         {
       
   182             const LONGLONG length = sizeof(WaveStructure) + m_waveHeader.dataLength;
       
   183             if (total) {
       
   184                 *total = length;
       
   185             }
       
   186             if (available) {
       
   187                 *available = length;
       
   188             }
       
   189 
       
   190             return S_OK;
       
   191         }
       
   192 
       
   193         STDMETHODIMP QAudioCDReader::QueryInterface(REFIID iid, void** out)
       
   194         {
       
   195             if (!out) {
       
   196                 return E_POINTER;
       
   197             }
       
   198 
       
   199             if (iid == IID_ITitleInterface) {
       
   200                 //we reroute that to the pin
       
   201                 *out = static_cast<ITitleInterface*>(this);
       
   202                 AddRef();
       
   203                 return S_OK;
       
   204             } else {
       
   205                 return QAsyncReader::QueryInterface(iid, out);
       
   206             }
       
   207         }
       
   208 
       
   209 
       
   210         HRESULT QAudioCDReader::read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual)
       
   211         {
       
   212             LONG nbRead = 0;
       
   213 
       
   214             if (actual) {
       
   215                 *actual = 0;
       
   216             }
       
   217 
       
   218             if (pos < sizeof(WaveStructure)) {
       
   219                 //we first copy the content of the structure
       
   220                 nbRead = qMin(LONG(sizeof(WaveStructure) - pos), length);
       
   221                 qMemCopy(buffer, reinterpret_cast<char*>(&m_waveHeader) + pos, nbRead);
       
   222             }
       
   223 
       
   224             const LONGLONG posInTrack = pos - sizeof(WaveStructure) + nbRead;
       
   225             const int bytesLeft = qMin(m_waveHeader.dataLength - posInTrack, LONGLONG(length - nbRead));
       
   226 
       
   227             if (bytesLeft > 0) {
       
   228 
       
   229                 //we need to read again
       
   230 
       
   231                 const int surplus = posInTrack % SECTOR_SIZE; //how many bytes too much at the beginning
       
   232                 const int firstSector = posInTrack / SECTOR_SIZE, 
       
   233                     lastSector = (posInTrack + length - 1) / SECTOR_SIZE;
       
   234                 const int sectorsNeeded = lastSector - firstSector + 1;
       
   235                 int sectorsRead = 0;
       
   236 
       
   237                 QByteArray ba(sectorsNeeded * SECTOR_SIZE, 0);
       
   238 
       
   239 
       
   240                 RAW_READ_INFO ReadInfo;
       
   241                 ReadInfo.TrackMode = CDDA; // Always use CDDA (numerical: 2)
       
   242                 ReadInfo.DiskOffset.QuadPart = (m_trackAddress + firstSector) * 2048;
       
   243                 ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ);
       
   244                 while (ReadInfo.SectorCount) {
       
   245                     DWORD dummy = 0;
       
   246                     if (::DeviceIoControl( m_cddrive, IOCTL_CDROM_RAW_READ,
       
   247                         &ReadInfo, sizeof(ReadInfo),
       
   248                         ba.data() + sectorsRead * SECTOR_SIZE,
       
   249                         ReadInfo.SectorCount * SECTOR_SIZE,
       
   250                         &dummy, NULL ) )
       
   251                     {
       
   252                         ReadInfo.DiskOffset.QuadPart += ReadInfo.SectorCount * 2048;
       
   253                         sectorsRead += ReadInfo.SectorCount;
       
   254                         ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ);
       
   255                     }else {
       
   256                         qWarning("an error occurred while reading from the media");
       
   257                         return S_FALSE;
       
   258                     }
       
   259 
       
   260                 }
       
   261 
       
   262                 //consume bytes on the buffer
       
   263                 qMemCopy(buffer + nbRead, ba.data() + surplus, bytesLeft);
       
   264 
       
   265                 //at this point we have all we need in the buffer
       
   266                 nbRead += bytesLeft;
       
   267             }
       
   268 
       
   269             if (actual) {
       
   270                 *actual = nbRead;
       
   271             }
       
   272 
       
   273             return nbRead == length ? S_OK : S_FALSE;
       
   274         }
       
   275 
       
   276         QList<qint64> QAudioCDReader::titles() const
       
   277         {
       
   278             QList<qint64> ret;
       
   279             ret << 0;
       
   280             for(int i = m_toc.FirstTrack; i <= m_toc.LastTrack ; ++i) {
       
   281                 const uchar *address = m_toc.TrackData[i].Address;
       
   282                 ret << ((address[0] * 60 + address[1]) * 60 + address[2]) * 1000 + address[3]*1000/75 - 2000;
       
   283 
       
   284             }
       
   285             return ret;
       
   286         }
       
   287 
       
   288 
       
   289         QAudioCDPlayer::QAudioCDPlayer() : QBaseFilter(CLSID_NULL)
       
   290         {
       
   291             new QAudioCDReader(this);
       
   292         }
       
   293 
       
   294         QAudioCDPlayer::~QAudioCDPlayer()
       
   295         {
       
   296         }
       
   297 
       
   298         STDMETHODIMP QAudioCDPlayer::QueryInterface(REFIID iid, void** out)
       
   299         {
       
   300             if (iid == IID_ITitleInterface) {
       
   301                 //we reroute that to the pin
       
   302                 return pins().first()->QueryInterface(iid, out);
       
   303             } else {
       
   304                 return QBaseFilter::QueryInterface(iid, out);
       
   305             }
       
   306         }
       
   307     }
       
   308 }
       
   309 
       
   310 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   311 
       
   312 QT_END_NAMESPACE