diff -r 000000000000 -r 1918ee327afb src/3rdparty/phonon/ds9/qaudiocdreader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/3rdparty/phonon/ds9/qaudiocdreader.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,312 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see . +*/ + +#include "qaudiocdreader.h" +#include +#include + +#include // needed for FILE_DEVICE_CD_ROM etc + +#define IOCTL_CDROM_READ_TOC CTL_CODE(FILE_DEVICE_CD_ROM, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CDROM_RAW_READ CTL_CODE(FILE_DEVICE_CD_ROM, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS) + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ + namespace DS9 + { + // {CA46BFE1-D55B-4adf-B803-BC2B9AD57824} + DEFINE_GUID(IID_ITitleInterface, + 0xca46bfe1, 0xd55b, 0x4adf, 0xb8, 0x3, 0xbc, 0x2b, 0x9a, 0xd5, 0x78, 0x24); + + struct TRACK_DATA { + UCHAR Reserved; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TrackNumber; + UCHAR Reserved1; + UCHAR Address[4]; + }; + + struct CDROM_TOC { + UCHAR Length[2]; + UCHAR FirstTrack; + UCHAR LastTrack; + TRACK_DATA TrackData[100]; + }; + + struct WaveStructure + { + WaveStructure(); + + char riff[4]; + qint32 chunksize; + char wave[4]; + char fmt[4]; + const qint32 chunksize2; + const quint16 formatTag; + const quint16 nChannels; + const quint32 nSamplesPerSec; + const quint32 nAvgBytesPerSec; + const quint16 nBlockAlign; + const quint16 bitsPerSample; + char data[4]; + qint32 dataLength; + }; + + enum TRACK_MODE_TYPE { + YellowMode2, + XAForm2, + CDDA + }; + + + struct RAW_READ_INFO { + LARGE_INTEGER DiskOffset; + ULONG SectorCount; + TRACK_MODE_TYPE TrackMode; + }; + + class QAudioCDReader : public QAsyncReader, public ITitleInterface + { + public: + QAudioCDReader(QBaseFilter *parent, QChar drive = QChar()); + ~QAudioCDReader(); + + //reimplementation from IUnknown + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + STDMETHODIMP Length(LONGLONG *,LONGLONG *); + STDMETHODIMP QueryInterface(REFIID iid, void** out); + QList titles() const; + + protected: + HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual); + + private: + HANDLE m_cddrive; + CDROM_TOC m_toc; + WaveStructure m_waveHeader; + qint64 m_trackAddress; + }; + + +#define SECTOR_SIZE 2352 +#define NB_SECTORS_READ 20 + + static const AM_MEDIA_TYPE audioCDMediaType = { MEDIATYPE_Stream, MEDIASUBTYPE_WAVE, TRUE, FALSE, 1, GUID_NULL, 0, 0, 0}; + + int addressToSectors(UCHAR address[4]) + { + return ((address[0] * 60 + address[1]) * 60 + address[2]) * 75 + address[3] - 150; + } + + WaveStructure::WaveStructure() : chunksize(0), chunksize2(16), + formatTag(WAVE_FORMAT_PCM), nChannels(2), nSamplesPerSec(44100), nAvgBytesPerSec(176400), nBlockAlign(4), bitsPerSample(16), + dataLength(0) + { + qMemCopy(riff, "RIFF", 4); + qMemCopy(wave, "WAVE", 4); + qMemCopy(fmt, "fmt ", 4); + qMemCopy(data, "data", 4); + } + + + QAudioCDReader::QAudioCDReader(QBaseFilter *parent, QChar drive) : QAsyncReader(parent, QVector() << audioCDMediaType) + { + //now open the cd-drive + QString path; + if (drive.isNull()) { + path = QString::fromLatin1("\\\\.\\Cdrom0"); + } else { + path = QString::fromLatin1("\\\\.\\%1:").arg(drive); + } + + m_cddrive = ::CreateFile((const wchar_t *)path.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + + qMemSet(&m_toc, 0, sizeof(CDROM_TOC)); + //read the TOC + DWORD bytesRead = 0; + bool tocRead = ::DeviceIoControl(m_cddrive, IOCTL_CDROM_READ_TOC, 0, 0, &m_toc, sizeof(CDROM_TOC), &bytesRead, 0); + + if (!tocRead) { + qWarning("unable to load the TOC from the CD"); + return; + } + + m_trackAddress = addressToSectors(m_toc.TrackData[0].Address); + const qint32 nbSectorsToRead = (addressToSectors(m_toc.TrackData[m_toc.LastTrack + 1 - m_toc.FirstTrack].Address) + - m_trackAddress); + const qint32 dataLength = nbSectorsToRead * SECTOR_SIZE; + + m_waveHeader.chunksize = 4 + (8 + m_waveHeader.chunksize2) + (8 + dataLength); + m_waveHeader.dataLength = dataLength; + } + + QAudioCDReader::~QAudioCDReader() + { + ::CloseHandle(m_cddrive); + } + + STDMETHODIMP_(ULONG) QAudioCDReader::AddRef() + { + return QAsyncReader::AddRef(); + } + + STDMETHODIMP_(ULONG) QAudioCDReader::Release() + { + return QAsyncReader::Release(); + } + + + STDMETHODIMP QAudioCDReader::Length(LONGLONG *total,LONGLONG *available) + { + const LONGLONG length = sizeof(WaveStructure) + m_waveHeader.dataLength; + if (total) { + *total = length; + } + if (available) { + *available = length; + } + + return S_OK; + } + + STDMETHODIMP QAudioCDReader::QueryInterface(REFIID iid, void** out) + { + if (!out) { + return E_POINTER; + } + + if (iid == IID_ITitleInterface) { + //we reroute that to the pin + *out = static_cast(this); + AddRef(); + return S_OK; + } else { + return QAsyncReader::QueryInterface(iid, out); + } + } + + + HRESULT QAudioCDReader::read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual) + { + LONG nbRead = 0; + + if (actual) { + *actual = 0; + } + + if (pos < sizeof(WaveStructure)) { + //we first copy the content of the structure + nbRead = qMin(LONG(sizeof(WaveStructure) - pos), length); + qMemCopy(buffer, reinterpret_cast(&m_waveHeader) + pos, nbRead); + } + + const LONGLONG posInTrack = pos - sizeof(WaveStructure) + nbRead; + const int bytesLeft = qMin(m_waveHeader.dataLength - posInTrack, LONGLONG(length - nbRead)); + + if (bytesLeft > 0) { + + //we need to read again + + const int surplus = posInTrack % SECTOR_SIZE; //how many bytes too much at the beginning + const int firstSector = posInTrack / SECTOR_SIZE, + lastSector = (posInTrack + length - 1) / SECTOR_SIZE; + const int sectorsNeeded = lastSector - firstSector + 1; + int sectorsRead = 0; + + QByteArray ba(sectorsNeeded * SECTOR_SIZE, 0); + + + RAW_READ_INFO ReadInfo; + ReadInfo.TrackMode = CDDA; // Always use CDDA (numerical: 2) + ReadInfo.DiskOffset.QuadPart = (m_trackAddress + firstSector) * 2048; + ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ); + while (ReadInfo.SectorCount) { + DWORD dummy = 0; + if (::DeviceIoControl( m_cddrive, IOCTL_CDROM_RAW_READ, + &ReadInfo, sizeof(ReadInfo), + ba.data() + sectorsRead * SECTOR_SIZE, + ReadInfo.SectorCount * SECTOR_SIZE, + &dummy, NULL ) ) + { + ReadInfo.DiskOffset.QuadPart += ReadInfo.SectorCount * 2048; + sectorsRead += ReadInfo.SectorCount; + ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ); + }else { + qWarning("an error occurred while reading from the media"); + return S_FALSE; + } + + } + + //consume bytes on the buffer + qMemCopy(buffer + nbRead, ba.data() + surplus, bytesLeft); + + //at this point we have all we need in the buffer + nbRead += bytesLeft; + } + + if (actual) { + *actual = nbRead; + } + + return nbRead == length ? S_OK : S_FALSE; + } + + QList QAudioCDReader::titles() const + { + QList ret; + ret << 0; + for(int i = m_toc.FirstTrack; i <= m_toc.LastTrack ; ++i) { + const uchar *address = m_toc.TrackData[i].Address; + ret << ((address[0] * 60 + address[1]) * 60 + address[2]) * 1000 + address[3]*1000/75 - 2000; + + } + return ret; + } + + + QAudioCDPlayer::QAudioCDPlayer() : QBaseFilter(CLSID_NULL) + { + new QAudioCDReader(this); + } + + QAudioCDPlayer::~QAudioCDPlayer() + { + } + + STDMETHODIMP QAudioCDPlayer::QueryInterface(REFIID iid, void** out) + { + if (iid == IID_ITitleInterface) { + //we reroute that to the pin + return pins().first()->QueryInterface(iid, out); + } else { + return QBaseFilter::QueryInterface(iid, out); + } + } + } +} + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE