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