source: trunk/src/3rdparty/phonon/ds9/qaudiocdreader.cpp

Last change on this file was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 10.1 KB
Line 
1/* This file is part of the KDE project.
2
3Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5This library is free software: you can redistribute it and/or modify
6it under the terms of the GNU Lesser General Public License as published by
7the Free Software Foundation, either version 2.1 or 3 of the License.
8
9This library is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU Lesser General Public License for more details.
13
14You should have received a copy of the GNU Lesser General Public License
15along 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
27QT_BEGIN_NAMESPACE
28
29#ifndef QT_NO_PHONON_MEDIACONTROLLER
30
31namespace 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
312QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.