Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / Core / HW / MpegDemux.cpp
blobb42403630fea8e74cf44054e0b8e9f1ce8c12254
1 #include "MpegDemux.h"
3 const int PACKET_START_CODE_MASK = 0xffffff00;
4 const int PACKET_START_CODE_PREFIX = 0x00000100;
6 // http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
8 const int USER_DATA_START_CODE = 0x000001b2;
9 const int SEQUENCE_START_CODE = 0x000001b3;
10 const int EXT_START_CODE = 0x000001b5;
11 const int SEQUENCE_END_CODE = 0x000001b7;
12 const int GOP_START_CODE = 0x000001b8;
13 const int ISO_11172_END_CODE = 0x000001b9;
14 const int PACK_START_CODE = 0x000001ba;
15 const int SYSTEM_HEADER_START_CODE = 0x000001bb;
16 const int PROGRAM_STREAM_MAP = 0x000001bc;
17 const int PRIVATE_STREAM_1 = 0x000001bd;
18 const int PADDING_STREAM = 0x000001be;
19 const int PRIVATE_STREAM_2 = 0x000001bf;
21 MpegDemux::MpegDemux(int size, int offset) : m_audioStream(size) {
22 m_buf = new u8[size];
24 m_len = size;
25 m_index = offset;
26 m_audioChannel = -1;
27 m_readSize = 0;
30 MpegDemux::~MpegDemux() {
31 delete [] m_buf;
34 void MpegDemux::DoState(PointerWrap &p) {
35 auto s = p.Section("MpegDemux", 1);
36 if (!s)
37 return;
39 p.Do(m_index);
40 p.Do(m_len);
41 p.Do(m_audioChannel);
42 p.Do(m_readSize);
43 if (m_buf)
44 p.DoArray(m_buf, m_len);
45 p.DoClass(m_audioStream);
48 bool MpegDemux::addStreamData(const u8 *buf, int addSize) {
49 if (m_readSize + addSize > m_len)
50 return false;
51 memcpy(m_buf + m_readSize, buf, addSize);
52 m_readSize += addSize;
53 return true;
56 int MpegDemux::readPesHeader(PesHeader &pesHeader, int length, int startCode) {
57 int c = 0;
58 while (length > 0) {
59 c = read8();
60 length--;
61 if (c != 0xFF) {
62 break;
65 if ((c & 0xC0) == 0x40) {
66 read8();
67 c = read8();
68 length -= 2;
70 pesHeader.pts = 0;
71 pesHeader.dts = 0;
72 if ((c & 0xE0) == 0x20) {
73 pesHeader.dts = pesHeader.pts = readPts(c);
74 length -= 4;
75 if ((c & 0x10) != 0) {
76 pesHeader.dts = readPts();
77 length -= 5;
79 } else if ((c & 0xC0) == 0x80) {
80 int flags = read8();
81 int headerLength = read8();
82 length -= 2;
83 length -= headerLength;
84 if ((flags & 0x80) != 0) {
85 pesHeader.dts = pesHeader.pts = readPts();
86 headerLength -= 5;
87 if ((flags & 0x40) != 0) {
88 pesHeader.dts = readPts();
89 headerLength -= 5;
92 if ((flags & 0x3F) != 0 && headerLength == 0) {
93 flags &= 0xC0;
95 if ((flags & 0x01) != 0) {
96 int pesExt = read8();
97 headerLength--;
98 int skip = (pesExt >> 4) & 0x0B;
99 skip += skip & 0x09;
100 if ((pesExt & 0x40) != 0 || skip > headerLength) {
101 pesExt = skip = 0;
103 this->skip(skip);
104 headerLength -= skip;
105 if ((pesExt & 0x01) != 0) {
106 int ext2Length = read8();
107 headerLength--;
108 if ((ext2Length & 0x7F) != 0) {
109 int idExt = read8();
110 headerLength--;
111 if ((idExt & 0x80) == 0) {
112 startCode = ((startCode & 0xFF) << 8) | idExt;
117 skip(headerLength);
119 if (startCode == PRIVATE_STREAM_1) {
120 int channel = read8();
121 pesHeader.channel = channel;
122 length--;
123 if (channel >= 0x80 && channel <= 0xCF) {
124 // Skip audio header
125 skip(3);
126 length -= 3;
127 if (channel >= 0xB0 && channel <= 0xBF) {
128 skip(1);
129 length--;
131 } else {
132 // PSP audio has additional 3 bytes in header
133 skip(3);
134 length -= 3;
137 return length;
140 int MpegDemux::demuxStream(bool bdemux, int startCode, int channel)
142 int length = read16();
143 if (bdemux) {
144 PesHeader pesHeader(channel);
145 length = readPesHeader(pesHeader, length, startCode);
146 if (pesHeader.channel == channel || channel < 0) {
147 channel = pesHeader.channel;
148 m_audioStream.push(m_buf + m_index, length, pesHeader.pts);
150 skip(length);
151 } else {
152 skip(length);
154 return channel;
157 void MpegDemux::demux(int audioChannel)
159 if (audioChannel >= 0)
160 m_audioChannel = audioChannel;
161 while (m_index < m_len)
163 if (m_index + 2048 > m_readSize)
164 break;
165 // Search for start code
166 int startCode = 0xFF;
167 while ((startCode & PACKET_START_CODE_MASK) != PACKET_START_CODE_PREFIX && !isEOF()) {
168 startCode = (startCode << 8) | read8();
170 switch (startCode) {
171 case PACK_START_CODE:
172 skip(10);
173 break;
174 case SYSTEM_HEADER_START_CODE:
175 skip(14);
176 break;
177 case PADDING_STREAM:
178 case PRIVATE_STREAM_2:
180 int length = read16();
181 skip(length);
182 break;
184 case PRIVATE_STREAM_1: {
185 // Audio stream
186 m_audioChannel = demuxStream(true, startCode, m_audioChannel);
187 break;
189 case 0x1E0: case 0x1E1: case 0x1E2: case 0x1E3:
190 case 0x1E4: case 0x1E5: case 0x1E6: case 0x1E7:
191 case 0x1E8: case 0x1E9: case 0x1EA: case 0x1EB:
192 case 0x1EC: case 0x1ED: case 0x1EE: case 0x1EF:
193 // Video Stream
194 demuxStream(false, startCode, -1);
195 break;
196 case USER_DATA_START_CODE:
197 // User data, probably same as queried by sceMpegGetUserdataAu.
198 // Not sure what exactly to do or how much to read.
199 // TODO: implement properly.
200 break;
203 if (m_index < m_readSize) {
204 int size = m_readSize - m_index;
205 memcpy(m_buf, m_buf + m_index, size);
206 m_index = 0;
207 m_readSize = size;
208 } else {
209 m_index = 0;
210 m_readSize = 0;
214 static bool isHeader(u8* audioStream, int offset)
216 const u8 header1 = (u8)0x0F;
217 const u8 header2 = (u8)0xD0;
218 return (audioStream[offset] == header1) && (audioStream[offset+1] == header2);
221 static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int frameSize)
223 int endScan = limit - 1;
225 // Most common case: the header can be found at each frameSize
226 int offset = curpos + frameSize - 8;
227 if (offset < endScan && isHeader(audioStream, offset))
228 return offset;
229 for (int scan = curpos; scan < endScan; scan++) {
230 if (isHeader(audioStream, scan))
231 return scan;
234 return -1;
237 int MpegDemux::getNextAudioFrame(u8 **buf, int *headerCode1, int *headerCode2, s64 *pts)
239 int gotsize;
240 int frameSize;
241 if (!hasNextAudioFrame(&gotsize, &frameSize, headerCode1, headerCode2))
242 return 0;
243 int audioPos = 8;
244 int nextHeader = getNextHeaderPosition(m_audioFrame, audioPos, gotsize, frameSize);
245 if (nextHeader >= 0) {
246 audioPos = nextHeader;
247 } else {
248 audioPos = gotsize;
250 m_audioStream.pop_front(0, audioPos, pts);
251 if (buf) {
252 *buf = m_audioFrame + 8;
254 return frameSize - 8;
257 bool MpegDemux::hasNextAudioFrame(int *gotsizeOut, int *frameSizeOut, int *headerCode1, int *headerCode2)
259 int gotsize = m_audioStream.get_front(m_audioFrame, 0x2000);
260 if (gotsize == 0 || !isHeader(m_audioFrame, 0))
261 return false;
262 u8 code1 = m_audioFrame[2];
263 u8 code2 = m_audioFrame[3];
264 int frameSize = (((code1 & 0x03) << 8) | ((code2 & 0xFF) * 8)) + 0x10;
265 if (frameSize > gotsize)
266 return false;
268 if (gotsizeOut)
269 *gotsizeOut = gotsize;
270 if (frameSizeOut)
271 *frameSizeOut = frameSize;
272 if (headerCode1)
273 *headerCode1 = code1;
274 if (headerCode2)
275 *headerCode2 = code2;
277 return true;