CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HW/MpegDemux.cpp
Views: 1401
1
#include "Common/Serialize/SerializeFuncs.h"
2
#include "Core/HW/MpegDemux.h"
3
#include "Core/Reporting.h"
4
5
const int PACKET_START_CODE_MASK = 0xffffff00;
6
const int PACKET_START_CODE_PREFIX = 0x00000100;
7
8
// http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
9
10
const int USER_DATA_START_CODE = 0x000001b2;
11
const int SEQUENCE_START_CODE = 0x000001b3;
12
const int EXT_START_CODE = 0x000001b5;
13
const int SEQUENCE_END_CODE = 0x000001b7;
14
const int GOP_START_CODE = 0x000001b8;
15
const int ISO_11172_END_CODE = 0x000001b9;
16
const int PACK_START_CODE = 0x000001ba;
17
const int SYSTEM_HEADER_START_CODE = 0x000001bb;
18
const int PROGRAM_STREAM_MAP = 0x000001bc;
19
const int PRIVATE_STREAM_1 = 0x000001bd;
20
const int PADDING_STREAM = 0x000001be;
21
const int PRIVATE_STREAM_2 = 0x000001bf;
22
23
MpegDemux::MpegDemux(int size, int offset) : m_audioStream(size) {
24
m_buf = new u8[size];
25
26
m_len = size;
27
m_index = offset;
28
m_audioChannel = -1;
29
m_readSize = 0;
30
}
31
32
MpegDemux::~MpegDemux() {
33
delete [] m_buf;
34
}
35
36
void MpegDemux::DoState(PointerWrap &p) {
37
auto s = p.Section("MpegDemux", 1);
38
if (!s)
39
return;
40
41
Do(p, m_index);
42
Do(p, m_len);
43
Do(p, m_audioChannel);
44
Do(p, m_readSize);
45
if (m_buf)
46
DoArray(p, m_buf, m_len);
47
DoClass(p, m_audioStream);
48
}
49
50
bool MpegDemux::addStreamData(const u8 *buf, int addSize) {
51
if (m_readSize + addSize > m_len)
52
return false;
53
memcpy(m_buf + m_readSize, buf, addSize);
54
m_readSize += addSize;
55
return true;
56
}
57
58
int MpegDemux::readPesHeader(PesHeader &pesHeader, int length, int startCode) {
59
int c = 0;
60
while (length > 0) {
61
c = read8();
62
length--;
63
if (c != 0xFF) {
64
break;
65
}
66
}
67
if ((c & 0xC0) == 0x40) {
68
read8();
69
c = read8();
70
length -= 2;
71
}
72
pesHeader.pts = 0;
73
pesHeader.dts = 0;
74
if ((c & 0xE0) == 0x20) {
75
pesHeader.dts = pesHeader.pts = readPts(c);
76
length -= 4;
77
if ((c & 0x10) != 0) {
78
pesHeader.dts = readPts();
79
length -= 5;
80
}
81
} else if ((c & 0xC0) == 0x80) {
82
int flags = read8();
83
int headerLength = read8();
84
length -= 2;
85
length -= headerLength;
86
if ((flags & 0x80) != 0) {
87
pesHeader.dts = pesHeader.pts = readPts();
88
headerLength -= 5;
89
if ((flags & 0x40) != 0) {
90
pesHeader.dts = readPts();
91
headerLength -= 5;
92
}
93
}
94
if ((flags & 0x3F) != 0 && headerLength == 0) {
95
flags &= 0xC0;
96
}
97
if ((flags & 0x01) != 0) {
98
int pesExt = read8();
99
headerLength--;
100
int skip = (pesExt >> 4) & 0x0B;
101
skip += skip & 0x09;
102
if ((pesExt & 0x40) != 0 || skip > headerLength) {
103
pesExt = skip = 0;
104
}
105
this->skip(skip);
106
headerLength -= skip;
107
if ((pesExt & 0x01) != 0) {
108
int ext2Length = read8();
109
headerLength--;
110
if ((ext2Length & 0x7F) != 0) {
111
int idExt = read8();
112
headerLength--;
113
if ((idExt & 0x80) == 0) {
114
startCode = ((startCode & 0xFF) << 8) | idExt;
115
}
116
}
117
}
118
}
119
skip(headerLength);
120
}
121
if (startCode == PRIVATE_STREAM_1) {
122
int channel = read8();
123
pesHeader.channel = channel;
124
length--;
125
if (channel >= 0x80 && channel <= 0xCF) {
126
// Skip audio header
127
skip(3);
128
length -= 3;
129
if (channel >= 0xB0 && channel <= 0xBF) {
130
skip(1);
131
length--;
132
}
133
} else {
134
// PSP audio has additional 3 bytes in header
135
skip(3);
136
length -= 3;
137
}
138
}
139
return length;
140
}
141
142
int MpegDemux::demuxStream(bool bdemux, int startCode, int length, int channel)
143
{
144
if (bdemux) {
145
PesHeader pesHeader(channel);
146
length = readPesHeader(pesHeader, length, startCode);
147
if (pesHeader.channel == channel || channel < 0) {
148
channel = pesHeader.channel;
149
m_audioStream.push(m_buf + m_index, length, pesHeader.pts);
150
}
151
skip(length);
152
} else {
153
skip(length);
154
}
155
return channel;
156
}
157
158
bool MpegDemux::skipPackHeader() {
159
// MPEG version / SCR
160
if ((read8() & 0xC4) != 0x44) {
161
return false;
162
}
163
skip(1);
164
if ((read8() & 0x04) != 0x04) {
165
return false;
166
}
167
skip(1);
168
if ((read8() & 0x04) != 0x04) {
169
return false;
170
}
171
// SCR_ext
172
if ((read8() & 0x01) != 0x01) {
173
return false;
174
}
175
176
int muxrate = read24();
177
if ((muxrate & 3) != 3) {
178
return false;
179
}
180
int stuffing = read8() & 7;
181
while (stuffing > 0) {
182
if (read8() != 0xFF) {
183
return false;
184
}
185
--stuffing;
186
}
187
return true;
188
}
189
190
bool MpegDemux::demux(int audioChannel)
191
{
192
if (audioChannel >= 0)
193
m_audioChannel = audioChannel;
194
195
bool looksValid = false;
196
bool needMore = false;
197
while (m_index < m_readSize && !needMore)
198
{
199
// Search for start code
200
u32 startCode = 0xFF;
201
while ((startCode & PACKET_START_CODE_MASK) != PACKET_START_CODE_PREFIX && !isEOF()) {
202
startCode = (startCode << 8) | read8();
203
}
204
// Not enough data available yet.
205
if (m_readSize - m_index < 16) {
206
m_index -= 4;
207
break;
208
}
209
210
switch (startCode) {
211
case PACK_START_CODE:
212
if (skipPackHeader()) {
213
looksValid = true;
214
}
215
break;
216
case SYSTEM_HEADER_START_CODE: {
217
looksValid = true;
218
int length = read16();
219
if (m_readSize - m_index < length) {
220
m_index -= 4 + 2;
221
needMore = true;
222
break;
223
}
224
skip(length);
225
break;
226
}
227
case PADDING_STREAM:
228
case PRIVATE_STREAM_2: {
229
looksValid = true;
230
int length = read16();
231
if (m_readSize - m_index < length) {
232
m_index -= 4 + 2;
233
needMore = true;
234
break;
235
}
236
skip(length);
237
break;
238
}
239
case PRIVATE_STREAM_1: {
240
// AUDIO stream
241
int length = read16();
242
// Check for PES header marker.
243
looksValid = (m_buf[m_index] & 0xC0) == 0x80;
244
if (m_readSize - m_index < length) {
245
m_index -= 4 + 2;
246
needMore = true;
247
break;
248
}
249
m_audioChannel = demuxStream(true, startCode, length, m_audioChannel);
250
looksValid = true;
251
break;
252
}
253
case 0x1E0: case 0x1E1: case 0x1E2: case 0x1E3:
254
case 0x1E4: case 0x1E5: case 0x1E6: case 0x1E7:
255
case 0x1E8: case 0x1E9: case 0x1EA: case 0x1EB:
256
case 0x1EC: case 0x1ED: case 0x1EE: case 0x1EF: {
257
// Video Stream
258
int length = read16();
259
// Check for PES header marker.
260
looksValid = (m_buf[m_index] & 0xC0) == 0x80;
261
if (m_readSize - m_index < length) {
262
m_index -= 4 + 2;
263
needMore = true;
264
break;
265
}
266
demuxStream(false, startCode, length, -1);
267
break;
268
}
269
case USER_DATA_START_CODE:
270
// User data, probably same as queried by sceMpegGetUserdataAu.
271
// Not sure what exactly to do or how much to read.
272
// TODO: implement properly.
273
WARN_LOG_REPORT_ONCE(mpeguserdata, Log::ME, "MPEG user data found");
274
looksValid = true;
275
break;
276
}
277
}
278
if (m_index < m_readSize) {
279
int size = m_readSize - m_index;
280
memmove(m_buf, m_buf + m_index, size);
281
m_index = 0;
282
m_readSize = size;
283
} else {
284
m_index = 0;
285
m_readSize = 0;
286
}
287
288
return looksValid;
289
}
290
291
static bool isHeader(const u8 *audioStream, int offset)
292
{
293
const u8 header1 = (u8)0x0F;
294
const u8 header2 = (u8)0xD0;
295
return (audioStream[offset] == header1) && (audioStream[offset+1] == header2);
296
}
297
298
static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int frameSize)
299
{
300
int endScan = limit - 1;
301
302
// Most common case: the header can be found at each frameSize
303
int offset = curpos + frameSize - 8;
304
if (offset < endScan && isHeader(audioStream, offset))
305
return offset;
306
for (int scan = curpos; scan < endScan; scan++) {
307
if (isHeader(audioStream, scan))
308
return scan;
309
}
310
311
return -1;
312
}
313
314
int MpegDemux::getNextAudioFrame(u8 **buf, int *headerCode1, int *headerCode2, s64 *pts)
315
{
316
int gotsize;
317
int frameSize;
318
if (!hasNextAudioFrame(&gotsize, &frameSize, headerCode1, headerCode2))
319
return 0;
320
int audioPos = 8;
321
int nextHeader = getNextHeaderPosition(m_audioFrame, audioPos, gotsize, frameSize);
322
if (nextHeader >= 0) {
323
audioPos = nextHeader;
324
} else {
325
audioPos = gotsize;
326
}
327
m_audioStream.pop_front(0, audioPos, pts);
328
if (buf) {
329
*buf = m_audioFrame + 8;
330
}
331
return frameSize - 8;
332
}
333
334
bool MpegDemux::hasNextAudioFrame(int *gotsizeOut, int *frameSizeOut, int *headerCode1, int *headerCode2)
335
{
336
int gotsize = m_audioStream.get_front(m_audioFrame, 0x2000);
337
if (gotsize < 4 || !isHeader(m_audioFrame, 0))
338
return false;
339
u8 code1 = m_audioFrame[2];
340
u8 code2 = m_audioFrame[3];
341
int frameSize = (((code1 & 0x03) << 8) | (code2 * 8)) + 0x10;
342
if (frameSize > gotsize)
343
return false;
344
345
if (gotsizeOut)
346
*gotsizeOut = gotsize;
347
if (frameSizeOut)
348
*frameSizeOut = frameSize;
349
if (headerCode1)
350
*headerCode1 = code1;
351
if (headerCode2)
352
*headerCode2 = code2;
353
354
return true;
355
}
356
357