C++解析TS流
2020-5-16
TS流格式见:https://blog.csdn.net/dxpqxb/article/details/79654004
协议文件见:iso13818-1.pdf
/********************************PAT PMT PES相关解析代码及结构体*******************************/
typedef unsigned char BYTE;
#define TS_PACKET_LENGTH 188
#define TS_SYNC_BYTE 0x47
#define TS_HEADER_SIZE 4
typedef enum _eStreamType
{
TS_STREAM_TYPE_NULL = 0,
TS_STREAM_TYPE_ADTS = 0xF, //具有ADTS音频
TS_STREAM_TYPE_H264 = 0x1B //H.264格式
} TsStreamtype;
typedef struct _TsPtsDtsA
{
BYTE marker1 : 1;
BYTE pts30 : 3;
BYTE res1 : 4; //1
BYTE pts22; //2
BYTE marker2 : 1;
BYTE pts15 : 7; //3
BYTE pts7; //4
BYTE marker3 : 1;
BYTE pts0 : 7; //5
} TsPtsDtsA;
typedef struct _TsPtsDtsB
{
BYTE marker1 : 1;
BYTE pts30 : 3;
BYTE res1 : 4; //1
BYTE pts22; //2
BYTE marker2 : 1;
BYTE pts15 : 7; //3
BYTE pts7; //4
BYTE marker3 : 1;
BYTE pts0 : 7; //5
BYTE marker4 : 1;
BYTE dts30 : 3;
BYTE res2 : 4; //6
BYTE dts22; //7
BYTE marker5 : 1;
BYTE dts15 : 7; //8
BYTE dts7; //9
BYTE marker6 : 1;
BYTE dts0 : 7; //10
} TsPtsDtsB;
typedef struct _TsPes
{
BYTE startCode[3]; //1 起始码 00 00 01
BYTE streamId; //4 0xe0
BYTE pesLengthH; //5
BYTE pesLengthL; //6
BYTE originalOrCopy : 1;
BYTE copyRight : 1;
BYTE dataAlignmentIndicator : 1;
BYTE pesPriority : 1;
BYTE pesScramblingControl : 2;
BYTE res1 : 2; //7
BYTE pesExtensionFlag : 1;
BYTE pesCrcFlag : 1;
BYTE additionalCopyInfoFlag : 1;
BYTE dsmTrickModeFlag : 1;
BYTE esRateFlag : 1;
BYTE escrFlag : 1;
BYTE ptsDtsFlags : 2; //8
BYTE pesHeaderDataLength; //9
} TsPes;
typedef struct _TsPatProgram
{
BYTE programNumberH; //节目号
BYTE programNumberL; //节目号
BYTE programMapPidH : 5; //节目PID
BYTE res1 : 3; //预留 默认111
BYTE programMapPidL; //节目PID
} TsPatProgram;
typedef struct _TsPat
{
BYTE tsHeaderRes; //预留错误码,固定为00
BYTE tableId; //固定为0x00 ,标志是该表是PAT
BYTE sectionLengthH : 4; //段长度
BYTE reserved_1 : 2; //保留
BYTE zero : 1; //无用
BYTE sectionSyntaxIndicator : 1; //段语法标志位,固定为1
BYTE sectionLengthL; //段长度
BYTE transportStreamIdH; //该传输流的ID,区别于一个网络中其它多路复用的流
BYTE transportStreamIdL; //该传输流的ID,区别于一个网络中其它多路复用的流
BYTE currentNextIndicator : 1; //发送的PAT是当前有效还是下一个PAT有效
BYTE versionNumber : 5; //范围0-31,表示PAT的版本号
BYTE reserved_2 : 2; // 保留位11
BYTE sectionNumber; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
BYTE lastSectionNumber; //最后一个分段的号码
TsPatProgram program;
BYTE crc32[4]; //CRC32校验码
} TsPat;
typedef struct _TsNit
{
BYTE tsHeaderRes; //预留错误码,固定为00
BYTE tableId; //固定为0x00 ,标志是该表是PAT
BYTE sectionLengthH : 4; //段长度
BYTE reserved_1 : 2; //保留
BYTE zero : 1; //无用
BYTE sectionSyntaxIndicator : 1; //段语法标志位,固定为1
BYTE sectionLengthL; //段长度
BYTE transportStreamIdH; //该传输流的ID,区别于一个网络中其它多路复用的流
BYTE transportStreamIdL; //该传输流的ID,区别于一个网络中其它多路复用的流
BYTE currentNextIndicator : 1; //发送的PAT是当前有效还是下一个PAT有效
BYTE versionNumber : 5; //范围0-31,表示PAT的版本号
BYTE reserved_2 : 2; // 保留位11
BYTE sectionNumber; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
BYTE lastSectionNumber; //最后一个分段的号码
TsPatProgram program;
BYTE crc32[4]; //CRC32校验码
} TsNit;
typedef struct _TsPmtProgram
{
BYTE streamType; //1 //类型 音频或视频
BYTE elemPidH : 5; //2 //PID
BYTE reseved1 : 3;
BYTE elemPidL; //3
BYTE esLengthH : 4; //内容长度
BYTE reseved2 : 4; //4
BYTE esLengthL; //5
} TsPmtProgram;
typedef struct _TsPmt
{
BYTE tsHeaderRes; //预留错误码,固定为00
BYTE tableId; //1 //不能是0xFF 固定为0x02表示是PMT
BYTE sectionLengthH : 4; //段长度
BYTE reserved_1 : 2; //保留
BYTE zero : 1; //无用
BYTE sectionSyntaxIndicator : 1; //2 //段语法标志位,固定为1
BYTE sectionLengthL; //3 //段长度
BYTE programNumberH; //4 //节目
BYTE programNumberL; //5 //节目
BYTE currentNextIndicator : 1;
BYTE versionNumber : 5; //范围0-31,表示PMT的版本号
BYTE reserved_2 : 2; //6 // 保留位11
BYTE sectionNumber; //7 //分段号码
BYTE lastSectionNumber; //8
BYTE pcrPidH : 5;
BYTE reverved_2 : 3; //9
BYTE pcrPidL; //10
BYTE programInfoLengthH : 4;
BYTE REVERVED_3 : 4; //11
BYTE programInfoLengthL; //12
//descriptor
TsPmtProgram pmtProgram;
BYTE crc32[4]; //CRC32校验码
} TsPmt;
typedef struct _TsHeader
{
BYTE syncByte; //同步码0x47
BYTE pidH : 5; //Pid
BYTE transportPriority : 1; //传输优先
BYTE payloadUnitStartIndicator : 1; //有效负载 PUSI
BYTE transportErrorIndicator : 1; //传输误码 TEI
BYTE pidL; //pid
BYTE continuityCounter : 4; //连续计数
BYTE adaptionFieldControl : 2;
//BYTE adaptionFieldControl:2; //自适应控制 00 01 10 11
BYTE transportScramblingControl : 2; //传输加扰 TSC
BYTE adaptionLength; //自适应部分长度 当adaptionFieldExist 为1
// Adaptation field when 10 11
//payload data whe 01 11
} TsHeader;
static unsigned short MakeShort(unsigned char dataH, unsigned char dataL)
{
unsigned short num = 0;
num = dataH;
num <<= 8;
num |= dataL;
return num;
}
int TsDemuxPacket(const unsigned char *pInputData, int tInputDataLen)
{
if (pInputData == NULL || tInputDataLen != TS_PACKET_LENGTH)
{
printf("XYQ ==> ts data error !\n");
return -1;
}
TsHeader *tsHeader = (TsHeader *)pInputData;
if (tsHeader->syncByte != TS_SYNC_BYTE) // invalid packet sync byte
{
printf("XYQ ==> ts packet head error !\n");
return -2;
}
//解析Pat
static int TsDemuxPat(const unsigned char *pTempData, int tInputDataLen, bool bTempPayloadUnitStartIndicator)
{
if (NULL == pTempData || tInputDataLen < sizeof(TsPat))
{
printf("XYQ ==> pat input data Error !\n");
return -1;
}
TsPat *tsPat = (TsPat *)pTempData;
if (bTempPayloadUnitStartIndicator != true)
{
printf("XYQ ==> pat payload indicator !\n");
return -6;
}
if (tsPat->tableId != 0) //PAT tableId 固定为0
{
printf("XYQ ==> Is Not Pat !\n");
return -7;
}
if (tsPat->sectionSyntaxIndicator != 0x01 && tsPat->reserved_1 != 0x03)
{
printf("XYQ ==> Pat section Syntax Error!\n");
return -8;
}
unsigned short tTempLen = MakeShort(tsPat->sectionLengthH, tsPat->sectionLengthL);
tInputDataLen -= 4;
if (tTempLen > tInputDataLen) //剩余字节大小不小于pat内容长度
{
printf("XYQ ==> Pat table length Error! TempLen[%d] inputDataLen[%d]\n", tTempLen, tInputDataLen);
return -9;
}
if (tsPat->currentNextIndicator != 1) //当前包是否有效
{
printf("XYQ ==> next valid!\n");
return -10;
}
tTempLen -= (5 + 4); //标识符5个字节(暂无用)+4个字节校验码
if ((tTempLen % 4) != 0)
{
printf("XYQ ==> Wrong Pat Data!\n");
return -11;
}
int tTempNum = tTempLen / 4;
pTempData = (unsigned char *)&tsPat->program;
int i = 0;
for (i = 0; i < tTempNum; ++i)
{
TsPatProgram *program = (TsPatProgram *)pTempData;
if (program->res1 != 0x07)
{
printf("XYQ ==> Wrong PmtProgram!\n");
return -12;
}
XTsStream *pTempStream = pXTsStreams + dXStream_PmtIdx; //假定只要一个PMT,多个PMT的后续处理
pTempStream->mPid = MakeShort(program->programMapPidH, program->programMapPidL);
pTempStream->mProgram = MakeShort(program->programNumberH, program->programNumberL);
pTempStream->mType = 0xff;
pTempData += 4;
}
return 0;
}
if (tsHeader->transportErrorIndicator == 1)
{
printf("XYQ ==> transport error !\n");
return -3;
}
if (tsHeader->transportScramblingControl != 0)
{
printf("XYQ ==> scrambled faield !\n");
return -4;
}
bool bTempPayloadUnitStartIndicator = tsHeader->payloadUnitStartIndicator == 1 ? true : false;
bool bTempAdaptationFieldExist = (tsHeader->adaptionFieldControl & 0x02) == 0x02 ? true : false;
bool bTempPayloadDataExist = (tsHeader->adaptionFieldControl & 0x01) == 0x01 ? true : false;
unsigned short tTempPid = MakeShort(tsHeader->pidH, tsHeader->pidL);
//printf("XYQ ==> pid[%d] !\n", tTempPid);
if (tTempPid != 0x1FFF && bTempPayloadDataExist == true)
{
unsigned char *pTempData = (unsigned char *)pInputData;
pTempData += TS_HEADER_SIZE;
tInputDataLen -= TS_HEADER_SIZE;
if (bTempAdaptationFieldExist == true) //存在自适应部分则跳过
{
if (tsHeader->adaptionLength > (TS_PACKET_LENGTH - TS_HEADER_SIZE - 1))
{
printf("XYQ ==> adaption lenth faield !\n");
return -5;
}
unsigned short adLeng = tsHeader->adaptionLength;
adLeng += 1;
pTempData += adLeng; //包括自身长度
tInputDataLen -= adLeng; //包括自身长度
/* printf("adaption length 0x%03d 0x%04d %02x %02x %02x %02x %02x %02x %02x %02x\n", adLeng, tInputDataLen,
pInputData[0], pInputData[1], pInputData[2], pInputData[3],
pInputData[4], pInputData[5], pInputData[6], pInputData[7]); */
}
if (tTempPid == 0) //PAT
{
return XTsDemuxPat(pTempData, tInputDataLen, bTempPayloadUnitStartIndicator);
}
else
{
XTsStream *pTempPmtStream = pXTsStreams + dXStream_PmtIdx;
XTsStream *pTempVideoStream = pXTsStreams + dXStream_VideoIdx;
XTsStream *pTempAudioStream = pXTsStreams + dXStream_AudioIdx;
if (tTempPid == pTempPmtStream->mPid)
{
return XTsDemuxPmt(pTempData, tInputDataLen, bTempPayloadUnitStartIndicator);
}
else if (tTempPid == pTempVideoStream->mPid)
{
return XTsDemuxPes(pTempData, tInputDataLen, bTempPayloadUnitStartIndicator, pTempVideoStream);
}
else if (tTempPid == pTempAudioStream->mPid)
{
return XTsDemuxPes(pTempData, tInputDataLen, bTempPayloadUnitStartIndicator, pTempAudioStream);
}
else
{
printf("XYQ ==> not found Pid !\n");
return -100;
}
}
}
return 0;
}
//解析Pmt
static int XTsDemuxPmt(const unsigned char *pTempData, int tInputDataLen, bool bTempPayloadUnitStartIndicator)
{
if (NULL == pTempData || tInputDataLen < sizeof(TsPmt))
{
printf("XYQ ==> pmt input data Error !\n");
return -1;
}
XTsStream *pTempPmtStream = &pXTsStreams[dXStream_PmtIdx];
TsPmt *tsPmt = (TsPmt *)pTempData;
if (pTempPmtStream->mType == 0xff) //当mType为0xFF赋值
{
if (bTempPayloadUnitStartIndicator)
{
if (tsPmt->tableId != 0x02) //pmt表id固定为0x02
{
printf("XYQ ==> not pmt table !\n");
return -13;
}
if (tsPmt->reserved_1 != 0x03)
{
printf("XYQ ==> pmt data error!\n");
return -14;
}
unsigned short tTempLen = tsPmt->sectionLengthH << 8 | tsPmt->sectionLengthL;
tTempLen += 4; //PMT内容长度+PMT长度+PMT tableId + 防错码
if (tTempLen > dXMaxTableLen)
{
printf("XYQ ==> pmt data error !\n ");
return -141;
}
XTsTableReset(&tXTsTable, tTempLen);
unsigned short tTempLL = (tInputDataLen > tTempLen) ? tTempLen : tInputDataLen;
memcpy(tXTsTable.pData, &(tsPmt->tsHeaderRes), tTempLL);
tXTsTable.mOffset += tTempLL;
if (tXTsTable.mOffset < tXTsTable.mLen)
{
printf("XYQ ==> pmt wait next part !\n ");
return 0; // wait next part
}
}
else //PMT table分成多包处理
{
if (0 == tXTsTable.mOffset)
{
printf("XYQ ==> pmt table not find first !\n ");
return -142;
}
unsigned short tTempL = tXTsTable.mLen - tXTsTable.mOffset;
unsigned short tTempLL = (tInputDataLen > tTempL) ? tTempL : tInputDataLen;
memcpy(tXTsTable.pData + tXTsTable.mOffset, pTempData, tTempLL);
tXTsTable.mOffset += tTempLL;
if (tXTsTable.mOffset < tXTsTable.mLen)
{
printf("XYQ ==> pmt wait next part !\n ");
return 0; // wait next part
}
}
//处理整包PMT
tsPmt = (TsPmt *)tXTsTable.pData;
unsigned short tTempN = MakeShort(tsPmt->programInfoLengthH, tsPmt->programInfoLengthL);
unsigned short tTempL = tXTsTable.mLen - (13 + 4); //13字节头 + 4字节crc
if (tTempN > tTempL)
{
printf("XYQ ==> pmt pcr Info length Error %d %d!\n %02x %02x %02x %02x %02x %02x %02x %02x", tTempN, tTempL,
tXTsTable.pData[0], tXTsTable.pData[1], tXTsTable.pData[2], tXTsTable.pData[3],
tXTsTable.pData[4], tXTsTable.pData[5], tXTsTable.pData[6], tXTsTable.pData[7]);
return -15;
}
//pcr不处理?
//unsigned short sPcr = MakeShort(tsPmt->pcrPidH, tsPmt->pcrPidL);
pTempData = (const unsigned char *)(&tsPmt->pmtProgram);
pTempData += tTempN; //跳过ProgramInfo长度
tTempL -= tTempN;
while (tTempL > 0)
{
if (tTempL < 5)
{
printf("XYQ ==> pmt program length Error, %d!\n ", tTempL);
return -15;
}
TsPmtProgram *program = (TsPmtProgram *)pTempData; //获取一个节目号
if (program->reseved1 != 0x07)
{
printf("XYQ ==> pmt program reseved is not 111 , leftLen[%d]!\n %02x %02x %02x %02x %02x %02x %02x %02x\n",
tTempL,
pTempData[0], pTempData[1], pTempData[2], pTempData[3],
pTempData[4], pTempData[5], pTempData[6], pTempData[7]);
return -17;
}
unsigned short tTempPid = MakeShort(program->elemPidH, program->elemPidL);
unsigned short tTempLL = MakeShort(program->esLengthH, program->esLengthL) + 5;
if (tTempLL > tTempL)
{
printf("XYQ ==> pmt program esLength Error !\n ");
return -18;
}
pTempData += tTempLL;
tTempL -= tTempLL;
// printf("XYQ ==> stream pid : 0x%x 0x%x \n", tTempPid, tTempType);
XTsStream *pTempStream = NULL;
int tTempStreamType = XTsGetStreamType(program->streamType);
if (tTempStreamType == content_type_video)
{
pTempStream = &pXTsStreams[dXStream_VideoIdx];
pTempStream->mContentType = content_type_video;
}
else if (tTempStreamType == content_type_audio)
{
pTempStream = &pXTsStreams[dXStream_AudioIdx];
pTempStream->mContentType = content_type_audio;
}
else
{
continue;
}
if (pTempStream && pTempStream->mType != program->streamType)
{
pTempStream->mPid = tTempPid;
pTempStream->mType = program->streamType;
printf("XYQ ==> stream change type = 0x%02x pid = 0x%04x\n",
pTempStream->mType, pTempStream->mPid);
// set es player type
int tTempCodecType = XTSGetStreamCodecType(pTempStream->mType);
if (pTempStream->mContentType == content_type_audio) //设置音频类型
{
int tTempAudioType = XTSGetStreamAudioType(tTempCodecType);
if (XXX_EsPlayerSetAcodecType(tTempAudioType) != 0)
{
pTempStream->mType = 0xff;
}
}
else if (pTempStream->mContentType == content_type_video) //设置视频类型
{
int tTempVideoType = XTSGetStreamVideoType(tTempCodecType);
if (XXXX_EsPlayerSetVcodecType(tTempVideoType) != 0)
{
pTempStream->mType = 0xff;
}
}
printf("XYQ ==> change codec type end !\n");
}
else if (pTempStream)
{
if (pTempStream->mPid != tTempPid)
{
printf("XYQ ==> chamge stream id : 0x%04x 0x%04x \n", pTempStream->mPid, tTempPid);
}
pTempStream->mPid = tTempPid;
}
}
}
else
{
printf("XYQ ==> not check pmt table !\n");
}
return 0;
}
//解析Pes
static int TsDemuxPes(const unsigned char *pTempData, int tInputDataLen,
bool bTempPayloadUnitStartIndicator, XTsStream *pTempStream)
{
if (NULL == pTempData || NULL == pTempStream)
{
printf("XYQ ==> pes input data Error!\n");
return -1;
}
if (bTempPayloadUnitStartIndicator)
{
if (tInputDataLen < sizeof(TsPes))
{
const unsigned char *pTempData1 = pTempData - 7;
printf("XYQ ==> pes input data Error, %d %02x %02x %02x %02x %02x %02x %02x %02x !\n",
tInputDataLen, pTempData1[0], pTempData1[1], pTempData1[2], pTempData1[3],
pTempData1[4], pTempData1[5], pTempData1[6], pTempData1[7]);
}
TsPes *tsPes = (TsPes *)pTempData;
static const unsigned char tTempStartCodePrefix[] = {0x00, 0x00, 0x01};
if (memcmp(tsPes->startCode, tTempStartCodePrefix, sizeof(tTempStartCodePrefix)) != 0)
{
printf("XYQ ==> pes start Code Error !\n");
return -21;
}
u_int8_t tTempStreamId = tsPes->streamId;
//unsigned short tTempL = tsPes->pesLengthH << 8 | tsPes->pesLengthL;
// printf("XYQ ==> stream id = 0x%x \n", tTempStreamId);
if ((tTempStreamId >= 0xbd && tTempStreamId <= 0xbf)
|| (tTempStreamId >= 0xc0 && tTempStreamId <= 0xdf)
|| (tTempStreamId >= 0xe0 && tTempStreamId <= 0xef)
|| (tTempStreamId >= 0xfa && tTempStreamId <= 0xfe))
{
// PES header extension
unsigned char tTempHLen = tsPes->pesHeaderDataLength + 9;
if (tInputDataLen < tTempHLen)
{
printf("XYQ ==> pes data length Error !\n");
return -23;
}
switch (tsPes->ptsDtsFlags)
{
case 0x02: // PTS only
{
if (tsPes->pesHeaderDataLength >= 5)
{
unsigned char *pPts = (unsigned char *)&tsPes->pesHeaderDataLength;
u_int64_t tTempPts = XTsDecodePts(pPts + 1);
//printf("XYQ ==> ptsdts 02 %02d [%lld] %02x %02x %02x %02x %02x !\n", pTempStream->mType,
// tTempPts, pPts[1], pPts[2], pPts[3], pPts[4], pPts[5]);
if (pTempStream->mDts > 0 && tTempPts > pTempStream->mDts)
{
pTempStream->mFrameRate = tTempPts - pTempStream->mDts;
}
pTempStream->mDts = tTempPts;
if (tTempPts > pTempStream->mLastPts)
{
pTempStream->mLastPts = tTempPts;
}
else
{
//printf("XYQ ==> ptsdts 02 %lld %lld !\n", pTempStream->mLastPts, tTempPts);
}
if (0 == pTempStream->mFirstPts && pTempStream->mFrameNum == (pTempStream->mContentType == content_type_video ? 1 : 0))
{
pTempStream->mFirstPts = tTempPts;
}
pTempStream->mCurPts = tTempPts;
}
else
{
printf("XYQ ==> pes Header data length Error %d !\n", tsPes->pesHeaderDataLength);
}
}
break;
case 0x03: // PTS,DTS
{
if (tsPes->pesHeaderDataLength >= 10)
{
unsigned char *pPts = (unsigned char *)&tsPes->pesHeaderDataLength;
u_int64_t tTempPts = XTsDecodePts(pPts + 1);
u_int64_t tTempDts = XTsDecodePts(pPts + 6);
#if 0
printf("XYQ ==> ptsdts 03 %02d [%lld] [%lld] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x !\n", pTempStream->mType,
tTempPts, tTempDts, pPts[1], pPts[2], pPts[3], pPts[4], pPts[5],
pPts[6], pPts[7], pPts[8], pPts[9], pPts[10]);
#endif
if (pTempStream->mDts > 0 && tTempDts > pTempStream->mDts)
{
pTempStream->mFrameRate = tTempDts - pTempStream->mDts;
}
pTempStream->mDts = tTempDts;
if (tTempPts > pTempStream->mLastPts)
{
pTempStream->mLastPts = tTempPts;
}
else
{
//printf("XYQ ==> ptsdts 03 %lld %lld !\n", pTempStream->mLastPts, tTempPts);
}
if (!pTempStream->mFirstPts && pTempStream->mFrameNum == (pTempStream->mContentType == content_type_video ? 1 : 0))
{
pTempStream->mFirstPts = tTempDts;
}
pTempStream->mCurPts = tTempPts;
}
else
{
printf("XYQ ==> pes Header data length Error %d !\n", tsPes->pesHeaderDataLength);
}
}
break;
default:
//printf("XYQ ==> bitmap:%02x\n", tsPes->ptsDtsFlags);
break;
}
// printf("XYQ ==> type : %d, cur pts : %lld %d \n", pTempStream->mType, (int64_t)pTempVideoStream->mCurPts, (int)(pTempVideoStream->mCurPts/90));
pTempStream->mStreamId = tTempStreamId;
pTempStream->mFrameNum++;
tInputDataLen -= (sizeof(TsPes) + tsPes->pesHeaderDataLength);
pTempData += (sizeof(TsPes) + tsPes->pesHeaderDataLength);
}
else
{
printf("XYQ ==> error stream Id:0x%02x\n", tTempStreamId);
pTempStream->mStreamId = 0;
}
}
if (pTempStream->mStreamId > 0)
{
if (tInputDataLen > 0) // play es data
{
// printf("XYQ ==> data len : %d \n", tTempDataLen);
if (bTempPayloadUnitStartIndicator || (pTempStream->mOffset + tInputDataLen) > dXFrameBufLen)
{
//printf("XYQ ==> put es data : %d %d \n", bTempPayloadUnitStartIndicator == true ? 1 : 0, pTempStream->mOffset);
if (pTempStream->mOffset > 0)
{
char *pTempTsData = NULL;
int tTempTsDataLen = 0;
int tTempRet = 0;
if (pTempStream->mContentType == content_type_video) //视频数据
{
if (1) //pTempStream->mLastPts == pTempStream->mCurPts)
{
int bShow = 0;
do
{
tTempRet = XXX_EsPlayerGetBuffer(true, &pTempTsData, &tTempTsDataLen, pTempStream->mOffset);
if (tTempTsDataLen != pTempStream->mOffset)
{
if (bShow == 0)
{
printf("XYQ ==> video stream get buf failed : %d %d \n", tTempTsDataLen, pTempStream->mOffset);
bShow = 1;
}
usleep(10);
}
else
{
break;
}
} while (1);
if (tTempRet == 0)
{
memcpy(pTempTsData, pTempStream->pData, tTempTsDataLen);
unsigned int tTempVideoPts = -1;
if (pTempStream->mLastPts == pTempStream->mCurPts)
{
tTempVideoPts = (unsigned int)(pTempStream->mLastPts / 90);
}
tTempVideoPts = 0;
//printf("XYQ ==> video pts : %llu, %u \n", pTempStream->mCurPts, tTempVideoPts);
tTempRet = XXX_EsPlayerPutBuffer(true, tTempTsDataLen, tTempVideoPts);
if (tTempRet != 0)
{
printf("XYQ ==> es player put video buf failed !\n");
}
}
else
{
printf("XYQ ==> es player get video buf failed !\n");
usleep(1000 * 30);
}
}
}
else if (pTempStream->mContentType == content_type_audio)
{
int bShow = 0;
do
{
tTempRet = XXX_EsPlayerGetBuffer(false, &pTempTsData, &tTempTsDataLen, pTempStream->mOffset);
if (tTempTsDataLen != pTempStream->mOffset)
{
if (bShow == 1)
{
printf("XYQ ==> audio stream get buf failed : %d %d \n",
tTempTsDataLen, pTempStream->mOffset);
bShow = 1;
}
usleep(10);
}
else
{
break;
}
} while (1);
if (tTempRet == 0)
{
memcpy(pTempTsData, pTempStream->pData, tTempTsDataLen);
unsigned int tTempAudioPts = (unsigned int)(pTempStream->mCurPts / 90);
tTempAudioPts = 0;
tTempRet = XXXX_EsPlayerPutBuffer(false, tTempTsDataLen, tTempAudioPts);
if (tTempRet != 0)
{
printf("XYQ ==> es player put audio buf failed !\n");
}
}
else
{
printf("XYQ ==> es player get audio buf failed !\n");
usleep(1000 * 30);
}
}
}
pTempStream->mOffset = 0;
}
//拷贝完后把数据写入缓存
if (pTempStream->mOffset + tInputDataLen < dXFrameBufLen)
{
memcpy(pTempStream->pData + pTempStream->mOffset, pTempData, tInputDataLen);
pTempStream->mOffset += tInputDataLen;
}
}
/*
int tTempFd = pTempStream->mFile;
if (tTempFd < 0)
{
if (pTempStream->mContentType == content_type_audio)
{
tTempFd = open("./audio.es", O_CREAT | O_RDWR | O_TRUNC, 0777);
}
else if (pTempStream->mContentType == content_type_video)
{
tTempFd = open("./video.es", O_CREAT | O_RDWR | O_TRUNC, 0777);
}
pTempStream->mFile = tTempFd;
}
if (tTempFd > 0)
{
int tTempWriteLen = write(tTempFd, pTempData, tInputDataLen);
printf("XYQ ==> write len : %d %d \n", tTempWriteLen, tInputDataLen);
}
*/
}
else
{
printf("XYQ ==> es streamId is 0!\n");
}
return 0;
}
标签: c++ TS PES iso13818-1
windows 与 USB设备进行数据交换实现
2019-12-5 C++
需求:上位机通过USB与下位机进行数据交互
实现:
打开设备:
static LONG HidOpen(DWORD HidUsbMian, DWORD HidUsbSub)
{
//hid设备GUID
CString strHidPath(_T(""));
HDEVINFO deviceInfoSet = INVALID_HANDLE_VALUE;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetailData = NULL;
SP_DEVINFO_DATA devinfoData;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
ZeroMemory(&deviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
ZeroMemory(&devinfoData, sizeof(SP_DEVINFO_DATA));
devinfoData.cbSize = sizeof(SP_DEVINFO_DATA);
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
GUID interfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
deviceInfoSet = SetupDiGetClassDevsA(&interfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
int deviceIndex = 0;
BOOL bExit = FALSE;
for ( ; bExit == FALSE; )
{
do
{
HANDLE writeHandle = INVALID_HANDLE_VALUE;
DWORD dwRequiredSize = 0;
BOOL res = SetupDiEnumDeviceInterfaces(deviceInfoSet,
NULL,
&interfaceClassGuid,
deviceIndex,
&deviceInterfaceData);
if (res == FALSE)
{
bExit = TRUE;
break;
}
BOOL bFind = FALSE;
for (int i = 0; ; ++i)
{
TCHAR szDriverName[MAX_PATH] = { 0 };
res = SetupDiEnumDeviceInfo(deviceInfoSet, i, &devinfoData);
if (res == FALSE)
{
break;
}
res = SetupDiGetDeviceRegistryProperty(deviceInfoSet, &devinfoData,
SPDRP_CLASS, NULL, reinterpret_cast<PBYTE>(szDriverName), sizeof(szDriverName), NULL);
if (res == FALSE)
{
break;
}
if (_tcscmp(szDriverName, _T("HIDClass")) == 0)
{
res = SetupDiGetDeviceRegistryProperty(deviceInfoSet, &devinfoData,
SPDRP_DRIVER, NULL, reinterpret_cast<PBYTE>(szDriverName), sizeof(szDriverName), NULL);
if (res == TRUE)
{
bFind = TRUE;
break;
}
}
}
//不是有效的设备的继续
if (FALSE == bFind)
{
break;
}
res = SetupDiGetDeviceInterfaceDetail(deviceInfoSet,
&deviceInterfaceData,
NULL,
0,
&dwRequiredSize,
NULL);
pDeviceInterfaceDetailData = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(malloc(dwRequiredSize));
pDeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
res = SetupDiGetDeviceInterfaceDetail(deviceInfoSet,
&deviceInterfaceData,
pDeviceInterfaceDetailData,
dwRequiredSize,
NULL,
NULL);
writeHandle = CreateFile(pDeviceInterfaceDetailData->DevicePath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
/*FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
if (writeHandle == INVALID_HANDLE_VALUE)
{
break;
}
HIDD_ATTRIBUTES attrib;
attrib.Size = sizeof(HIDD_ATTRIBUTES);
HidD_GetAttributes(writeHandle, &attrib);
/*
if ((HidUsbMian == 0x0 || attrib.VendorID == HidUsbMian)
&& (HidUsbSub == 0x0 || attrib.ProductID == HidUsbSub))
*/
if (attrib.VendorID == HidUsbMian && attrib.ProductID == HidUsbSub)
{
strHidPath = pDeviceInterfaceDetailData->DevicePath;
}
CloseHandle(writeHandle);
} while (0);
if (pDeviceInterfaceDetailData != NULL)
{
free(pDeviceInterfaceDetailData);
pDeviceInterfaceDetailData = NULL;
}
if (strHidPath != _T(""))
{
break;
}
++deviceIndex;
}
if (strHidPath == _T(""))
{
return 0;
}
HANDLE openHandle = CreateFile(strHidPath,
GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, 0);
HANDLE readHandle = CreateFile(strHidPath,
GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
0, 0);
/*FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
if (openHandle == INVALID_HANDLE_VALUE)
{
return NULL;
}
do
{
PHIDP_PREPARSED_DATA ppData = NULL;
HIDP_CAPS caps;
if (0)//0 == HidD_SetNumInputBuffers(openHandle, 512))
{
::OutputDebugString(_T("-------------HidD_SetNumInputBuffers Failed-------\r\n"));
break;
}
if (0 == HidD_GetPreparsedData(openHandle, &ppData))
{
::OutputDebugString(_T("-------------HidD_GetPreparsedData Failed-------\r\n"));
break;
}
NTSTATUS ntStatus = HidP_GetCaps(ppData, &caps);
if (ntStatus != HIDP_STATUS_SUCCESS)
{
::OutputDebugString(_T("-------------HidP_GetCaps Failed-------\r\n"));
break;
}
{
CString strMsg(_T(""));
strMsg.Format(_T("--Input Length[%d]---Output Length[%d]----\r\n"), caps.InputReportByteLength, caps.OutputReportByteLength);
::OutputDebugString(strMsg);
}
HidD_FreePreparsedData(ppData);
PHidDevice dev = NewHidDevice();
if (dev != NULL)
{
dev->deviceHandle = openHandle;
dev->deviceReadHandle = readHandle;
int nBufLen = (strHidPath.GetLength() + 1);
dev->devicePath = new TCHAR[nBufLen];
if (dev->devicePath != NULL)
{
memset(dev->devicePath, 0, nBufLen * sizeof(TCHAR));
_tcscpy_s(dev->devicePath, nBufLen, strHidPath.GetString());
}
}
return reinterpret_cast<long>(dev);
} while (0);
if (openHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(openHandle);
openHandle = INVALID_HANDLE_VALUE;
}
if (readHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(readHandle);
readHandle = INVALID_HANDLE_VALUE;
}
return 0;
}
读数据:
ReadFile(handle, buf, 18, &dwReaded, NULL) ;
写数据:
OVERLAPPED ol;
memset(&ol, 0, sizeof(OVERLAPPED));
ol.hEvent = ::CreateEventA(NULL, TRUE, FALSE, NULL);
do
{
int nCurLen = nBufSize >= 8 ? 8 : nBufSize;
memset(buf, 0, 11);
buf[0] = 0x00;
buf[1] = LOBYTE(nCurLen);
buf[2] = 0x00;
memcpy(buf + 3, pTmp, nCurLen);
ResetEvent(ol.hEvent);
BOOL bRet = WriteFile(dev->deviceHandle, reinterpret_cast<LPVOID>(buf), 11, &dwWrited, &ol);
if (bRet == FALSE)
{
DWORD dwTmp = 0;
GetOverlappedResult(dev->deviceHandle, &ol, &dwTmp, TRUE);
}
pTmp += nCurLen;
nBufSize -= nCurLen;
} while (nBufSize > 0);
if (ol.hEvent != NULL)
{
CloseHandle(ol.hEvent);
}
坑1:
传输大数据时,在有的电脑,会出现卡死情况
解决方式:
读跟写句柄分开创建,具体原因不明
MFC 函数注入
2019-11-22 C++
问题:
USB 通信DLL代码找不到,无法知道通信协议
解决思路:
注入windows相关接口,查看通信过程中发送的协议
实现代码:比如注入CreateFile
typedef HANDLE(WINAPI * Real_CreateFile) (
LPCWSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
1. 保存函数调用地址
BYTE g_oldJmp[6] = { 0 };
BYTE g_jmp[6] = { 0 };
2. 实现注入函数 执行函数前先恢复原接口函数地址,执行完再设置为更改后的地址
HANDLE WINAPI Routed_CreateFile(
LPCWSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
CStringW strTmp(lpFileName);
HANDLE handle = NULL;
VirtualProtect((LPVOID)g_pOrigCreateFile, 6, g_myProtect, NULL); //ReadWrite again
memcpy(g_pOrigCreateFile, g_oldJmp, 6); //Unhook API
handle = CreateFileW(strTmp.GetString(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
memcpy(g_pOrigCreateFile, g_jmp, 6); //Rehook API
VirtualProtect((LPVOID)g_pOrigCreateFile, 6, g_oldProtect, NULL); //Normal setts
CStringW strMsg(_T(""));
strMsg.Format(L"%s %x %x %x %x %x\r\n", strTmp.GetString(), dwDesiredAccess, dwShareMode, handle, dwCreationDisposition, dwFlagsAndAttributes);
::OutputDebugStringW(strMsg);
return handle;
}
//注入接口
void BeginRedirect(LPVOID newFunction, LPVOID oldFunction, BYTE *jmp, BYTE *oldJmp)
{
BYTE tempJMP[6] = {0xE9, 0x90, 0x90, 0x90, 0x90, 0xC3};
memcpy(jmp, tempJMP, 6);
DWORD dwJMPSize = (reinterpret_cast<DWORD>(newFunction) - reinterpret_cast<DWORD>(oldFunction) - 5);
VirtualProtect(oldFunction, 6, PAGE_EXECUTE_READWRITE, &g_oldProtect);
//Change memory settings to make sure we can write the JMP in
memcpy(oldJmp, oldFunction, 6); //Copy old bytes before writing JMP
memcpy(&jmp[1], &dwJMPSize, 4); //Write the address to JMP to
memcpy(oldFunction, jmp, 6); //Write it in process memory
VirtualProtect((LPVOID)oldFunction, 6, g_oldProtect, NULL); //Change
}
//初始化
g_pOrigCreateFile = reinterpret_cast<Real_CreateFile>(GetProcAddress(GetModuleHandle("Kernel32.dll"), "CreateFileW"));
if (g_pOrigCreateFile != NULL)
{
BeginRedirect(Routed_CreateFile, g_pOrigCreateFile, g_jmp, g_oldJmp);
}
海康威视GB28181视频流接收解析显示
2019-3-1
海康威视国标GB28181获取视频流并显示
环境:
MFC,jRtp,ffmpeg,sdl2.0,eXosip,
海康威视摄像头:DS-2CD3T27EDWD-L
思路:
利用eXosip实现sip服务端及客户端,实现GB28181协议交互
利用jRtp接收摄像头rtp视频流
利用ffmpeg解码视频流(由于海康威视ps视频流里面含有一些私有协议会导致花屏,因此这边需要先把ps流解析成H264视频流)
利用sdl显示视频
利用dll生成lib
2018-4-29 C++
业务场景:
使用libvlc3.02 SDK提供的lib及dll编译运行程序,提示找不到XXX于动态链接库libvlc.dll上
解决办法:
直接利用libvlc.dll生成libvlc.lib
步骤:
dumpbin.exe:dll导出函数
C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin>dumpbin libvlc.dll /EXPORTS /OUT:libvlc.def
编辑libvlc.def只留下函数所在的行
1 0 00002B30 libvlc_add_intf
2 1 0000BB60 libvlc_audio_equalizer_get_amp_at_index
3 2 0000B980 libvlc_audio_equalizer_get_band_count
python:格式化def
file = open(r"D:\Beihai\python\libvlco.def")
files = open(r"D:\Beihai\python\libvlc.def", "w")
fc = 1
while 1 :
line = file.readline()
if not line :
break
sl = line.split()
#print sl[-1]
nl = sl[-1] + " @ " + str(fc) + "\n"
fc = fc + 1
files.write(nl)
file.close()
files.close()
lib:利用def生成lib
c++面试
2017-9-6 C++
基础:
进程与线程的区别
写一个宏返回最小值
双向链表删除某个节点,插入节点
单链表反转
字符串反转
String构造类、操作符=重载
字符串循环右移
exe的文件头结构
虚构函数可以直接调用吗
构造函数初始化列表异常捕获
Makefile怎么编译一个文件
dll信息在exe头文件哪里
gdb调试bt有什么作用
算法:
牛牛判断牛几算法
int grad(int *arr, int len)
{
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len; j++)
{
for (int k = 0; k < len; k++)
{
if (i !=j && i !=k && k != j)
{
if ((arr[i] + arr[j] + arr[k]) % 10 == 0)
{
int sum = 0;
for (int l = 0; l < len; l++)
{
sum += arr[l];
}
return (sum - arr[i] - arr[j] - arr[k]) % 10;
}
}
}
}
}
return -1;
}
k个任务,每个任务有开始时间和结束时间,求24小时内最大执行任务数
斗地主随机洗牌算法
Fisher–Yates shuffle 洗牌算法
void shuffle(int *arr, int len)
{
for (int i = len - 1; i >= 0; i--)
{
int randomIndex = rand() % (i + 1);
int itemIndex = arr[randomIndex];
arr[randomIndex] = arr[i];
arr[i] = itemIndex;
}
}
使用:
多线程无锁编程:
http://blog.csdn.net/zzulp/article/details/6259866
send与write区别:
在功能上,read/write是recv/send的子集。read/wirte是更通用的文件描述符操作,而recv/send在socket领域则更“专业”一些。
当recv/send的flag参数设置为0时,则和read/write是一样的。
如果有如下几种需求,则read/write无法满足,必须使用recv/send:
- 为接收和发送进行一些选项设置
- 从多个客户端中接收报文
- 发送带外数据(out-of-band data)
标签: c++