文章目录
- 一、TS流简介
- 1. 什么是TS流 :
- 2. 在学习TS流是需要了解的部分定义:
- 3. 解析TS流的重点在于理解他的表结构:解析TS流的流程主要是通过对应的PID去分布解析我们需要的信息,从而截取出对应的有效数据
- 4. 解析流程:具体的对应结构在我上面列出的参考文章中都讲解的非常详细,本文主要写一个简单流程引导,做到一个快速集成到项目的目的
- 二、TS流解析代码
- 三、数据处理过程
- 四、总结
为了解析传输到手机的TS流有效视频数据,进行预览播放和录制等功能,在网上看到的部分关于android播放TS流,如UDP预览播放,采取的方式真是够标新立异,如通过保存到本地ts文件然后播放,会有闪屏现象等,当然也有很多的播放器支持播放,如ffmepg-android,vlc-android,vitamio等等。我感觉这些库要么太大,要么有很多都不满足我的需求,所以我自己尝试学习和通过jni调用c++去解析ts流
以下文章是我本文学习并参考的部分感觉非常不错的,希望大家学习时可以看看,方便全面学习:
- MediaRecorder系列之StagefrightRecorder录制TS流flow(一)
- TS流讲解–什么是ts流
- TS流基本概念(以下ts结构图来源于此文章)
- 流媒体基础知识TS流 PS流 ES流区别
- TS码流格式分析
- 以下解析代码由此项目改动优化部分得到:https://github.com/js2854/TSParser
一、TS流简介
1. 什么是TS流 :
TS(Transport Stream,传输流),全称则是MPEG2-TS,主要应用于实时传送的节目,如实时广播的电视节目,机顶盒等。它的主要格式分别为h264/mpeg4,acc/MP3等,MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。
2. 在学习TS流是需要了解的部分定义:
- ES流:基本码流,不分段的音频、视频或其他信息的连续码流。
- PES流:分包的ES流,通过添加PES头进行标记,PES包的长度是可变的
- TS流:传输流,固定长度的封包(188B),便于解析和恢复错包,它包含三个部分:ts header、adaptation field、payload,如下图结构,ts header通过PID去识别数据包的内容,adaptation field为补充内容,payload即我们需要解析的pes数据。
- 需要注意的是,一端TS流里面可能包含多个节目,这些在解析PAT和PMT时可以通过打印信息得到,在我代码里有注释,我的项目里固定只包含了一个,所以适配代码需要自己改动
3. 解析TS流的重点在于理解他的表结构:解析TS流的流程主要是通过对应的PID去分布解析我们需要的信息,从而截取出对应的有效数据
- 节目关联表Program Association Table (PAT) 0x0000,通过PAT我们可以解析对应的PMT表的PID
- 节目映射表Program Map Tables (PMT) 在PMT中解析出对应的视频和音频的PID值
- 条件接收表Conditional Access Table (CAT) 0x0001
- 网络信息表Network Information Table(NIT) 0x0010
- 部分参数或者结构说明我在代码注释中给出
4. 解析流程:具体的对应结构在我上面列出的参考文章中都讲解的非常详细,本文主要写一个简单流程引导,做到一个快速集成到项目的目的
- 遍历TS流,通过同步字节查到ts header,sync byte: 1B,其值固定为0x47(需要考虑差错,buff拼接的情况)
- 获取PAT
- 根据PAT查询的PMT_PID查询对应的PMT的表
- 根据PMT查询对应的VEDIO_PID和AUDIO_PID
- 对应的PID解析视频和音频ParsePES
- 以下为解析流程结构图:
二、TS流解析代码
本文给出的TS解析代码根据项目https://github.com/js2854/TSParser改动得来,该开源项目主要实现对TS文件的解析和各种信息的打印,我这边参考添加的改动:更改为TS流实现相应解析,增加的PES-音视频有效数据的解析,并通过jni输出到java层,添加android jni实现,数据缓存buff等,详细的方法都有部分注释,如有不明白,错误或侵权方面的问题请私信我,谢谢
APP_PROJECT_PATH := $(call my-dir)
APP_BUILD_SCRIPT := $(call my-dir)/Android.mk
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM=android-23
LOCAL_PATH := $(call my-dir)
# Program
include $(CLEAR_VARS)
LOCAL_MODULE := tsparse
LOCAL_SRC_FILES := jni_lib.cpp AACDecoder.cpp MFifo.cpp TSParser.cpp
#LOCAL_C_INCLUDES := \
#$(MY_LOCAL_ANDSRC)/system/core/include \
#$(MY_LOCAL_ANDSRC)/frameworks/native/include \
#$(MY_LOCAL_ANDSRC)/hardware/libhardware/include
#LOCAL_CFLAGS := -DHAVE_PTHREADS
LOCAL_C_INCLUDES += $(LOCAL_PATH)/prebuilt/include
LOCAL_LDLIBS := -llog -lz -lGLESv2 -landroid -lOpenSLES
include $(BUILD_SHARED_LIBRARY)
- jni_lib.cpp
#ifndef UINT64_C
#define UINT64_C(c) (c ## ULL)
#endif
#include "mdebug.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include "TSParser.h"
static JavaVM *g_jvm = NULL;
static TSParser * mpTSParser=NULL;
pthread_mutex_t playMutex = PTHREAD_MUTEX_INITIALIZER;
extern "C" {
JNIEXPORT jint JNI_OnLoad(JavaVM * vm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
mInfo("JNI_OnLoad");
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
return -1;
g_jvm = vm;
mpCamera = new CUCamera();
return JNI_VERSION_1_4;
}
}
///
int OnDestroy() {
if(mpTSParser){
mpTSParser->__stopThread();
if (mpTSParser->TsDoloopThreadHandle)
pthread_join(mpTSParser->TsDoloopThreadHandle, NULL);
mpTSParser->TsDoloopThreadHandle=NULL;
delete mpTSParser;
mpTSParser = NULL;
}
return 0;
}
static void *_tmain(void * cc)
{
if(mpTSParser!=NULL)
mpTSParser->Parse();
}
extern "C" {
JNIEXPORT jint JNICALL Java_包名0_包名1_包名2_init(JNIEnv *env,
jobject obj);
JNIEXPORT jint JNICALL Java_包名0_包名1_包名2_JniLib_PushTsData(JNIEnv * env, jobject obj, jbyteArray jbArr,jint DataLen);
JNIEXPORT void JNICALL Java_包名0_包名1_包名2_JniLib_initTS(JNIEnv *env,jobject obj);
JNIEXPORT void JNICALL Java_包名0_包名1_包名2_JniLib_stopTsParse(JNIEnv *env,jobject obj);
JNIEXPORT void JNICALL Java_包名0_包名1_包名2_JniLib_startTsParse(JNIEnv *env,jobject obj);
}
;
JNIEXPORT jint JNICALL Java_包名0_包名1_包名2_PushTsData(JNIEnv * env, jobject obj, jbyteArray jbArr,jint DataLen)
{
if(!mpTSParser)
return -1;
int ret = 0;
jsize jlen = env->GetArrayLength(jbArr);
jbyte* jbuf = env->GetByteArrayElements(jbArr, JNI_FALSE);
char* buf = (char*)jbuf;
mfxBitstreamTS *pBufTs = NULL;
while(true){
pBufTs = mpTSParser->GetEmptyTsBuf();
if(pBufTs==NULL||pBufTs->Data==NULL)
{
usleep(1);
continue;
}
break;
}
if (pBufTs == NULL ||pBufTs->Data == NULL) {
return -2;
}
// mInfo("-----------------------PushTsFrame %d",DataLen); TS_TIMES
memcpy(pBufTs->Data,(unsigned char *) jbuf, DataLen);
pBufTs->DataLength = DataLen;
mpTSParser->PushTsBuf(pBufTs);
env->ReleaseByteArrayElements(jbArr, jbuf, 0);
return ret;
}
JNIEXPORT void JNICALL Java_包名0_包名1_包名2_initTS(JNIEnv * env,
jobject thiz) {
if(mpTSParser==NULL)
mpTSParser = new TSParser();
mpTSParser->initMemory();
mpTSParser->JavaMethodInit(g_jvm, thiz);
return;
}
JNIEXPORT void JNICALL Java_包名0_包名1_包名2_stopTsParse(JNIEnv * env,
jobject obj) {
if (!mpTSParser)
return ;
mpTSParser->__stopThread();
if (mpTSParser->TsDoloopThreadHandle)
pthread_join(mpTSParser->TsDoloopThreadHandle, NULL);
mpTSParser->TsDoloopThreadHandle=NULL;
delete mpTSParser;
mpTSParser = NULL;
}
JNIEXPORT void JNICALL Java_包名0_包名1_包名2_startTsParse(JNIEnv * env,jobject obj ) {
if (!mpTSParser)
return ;
int ret_t;
struct sched_param param;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
param.sched_priority = 90;
pthread_attr_setschedparam(&attr, ¶m);
ret_t = pthread_create(&mpTSParser->TsDoloopThreadHandle, &attr, _tmain,NULL);
if (ret_t) {
mLogW("pthread_create TsDoloopThreadHandle failed [%d] \n", ret_t);
}
}
JNIEXPORT jint JNICALL Java_包名0_包名1_包名2_CameraLib_init(JNIEnv *env,
jobject obj) {
mpCamera->JavaMethodInit(g_jvm, obj);
return 0;
}
- types.h 建议到参考开源项目中拷贝
- TSParse.h
#ifndef __TS_PARSER_H__
#define __TS_PARSER_H__
struct _HANDLE_
{
unsigned int code;
void *pContext;
};
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include "mdebug.h"
#include <string.h>
#include <fcntl.h>
#include "types.h"
#include <string.h>
#include <stdlib.h>
#include "MFifo.h"
using namespace std;
typedef enum TS_ERR
{
TS_OK = 0,
TS_IN_PARAM_ERR,
TS_SYNC_BYTE_ERR,
TS_FILE_OPEN_FAIL,
TS_FILE_SEEK_FAIL,
}TS_ERR;
// PID种类
typedef enum E_PKT_TYPE
{
E_PAT = 0,
E_PMT = 1,
E_PCR = 2,
E_AUDIO = 3,
E_VIDEO = 4,
E_NIT =5,
E_SI =6,
E_MAX = 7
}E_PKT_TYPE;
class TSPacket
{
public:
// uint8 * bufH264pkt;//=new uint8[1024*2000];
uint32 pktH264Len;//=0;
uint32 pktAccLen;//=0;
uint32 pktindex;//=0;
bool get_PAT_Head=false;
bool get_PMT_Head=false;
mfxBitstreamTS *h264Buf;
mfxBitstreamTS *__accBuf;
TSPacket()
: m_pBuf(NULL)
, m_pHdr(NULL)
, m_u16PID(PID_UNSPEC)
, m_u8CC(0)
, m_u16PMTPID(PID_UNSPEC)
, m_u8StreamId(0)
, m_s64PCR(INVALID_VAL)
, m_s64PTS(INVALID_VAL)
, m_s64DTS(INVALID_VAL)
{
// bufH264pkt=new uint8[1024*2000];
pktH264Len=0;
pktindex=0;
get_PAT_Head=false;
get_PMT_Head=false;
}
~TSPacket() {}
uint16 GetPID() const { return m_u16PID; }
uint8 GetCC() const { return m_u8CC; }
bool IsPAT() { return (PID_PAT == m_u16PID); }
uint16 GetPMTPID() const { return m_u16PMTPID; }
bool IsSIT() { return (PID_DVB_SIT == m_u16PID); }
bool IsNIT() { return (PID_DVB_NIT == m_u16PID); }
bool IsPMT() { return (PID_UNSPEC != m_u16PID &&s_au16PIDs[E_PMT] == m_u16PID); }//
bool IsVideo() { return (s_au16PIDs[E_VIDEO] == m_u16PID); }
bool IsAudio() { return (s_au16PIDs[E_AUDIO] == m_u16PID); }
sint64 GetPCR() const { return m_s64PCR; }
sint64 GetPTS() const { return m_s64PTS; }
sint64 GetDTS() const { return m_s64DTS; }
public:
static uint16 s_au16PIDs[E_MAX]; // 记录所有pid
bool __HasAdaptField();
bool __HasPayload();
AdaptFixedPart* __GetAdaptField();
uint8 __GetAdaptLen();
sint64 __GetPCR();
bool __IsVideoStream(uint8 u8StreamType);
bool __IsAudioStream(uint8 u8StreamType);
uint8 __GetPayloadOffset();
uint8 __GetTableStartPos();
sint64 __GetPTS(const OptionPESHdrFixedPart *pHdr);
sint64 __GetDTS(const OptionPESHdrFixedPart *pHdr);
uint8 m_count_v;
const uint8 *m_pBuf;
TSHdrFixedPart *m_pHdr;
uint16 m_u16PID;
uint8 m_u8CC;
uint16 m_u16PMTPID;
uint8 m_u8StreamId;
sint64 m_s64PCR;
sint64 m_s64PTS;
sint64 m_s64DTS;
};
typedef struct{
//对内使用
uint8 out_videobuff[TS_MAX_OUT_BUFF]; //当前视频帧数据缓存
int video_buflen; //当前视频帧数据缓存长度
uint64_t pts_video; //当前视频帧PTS
uint64_t dts_video; //当前视频帧DTS
int video_cc_ok ; //
int video_last_cc; //上个视频TS包计数值
int video_intactness; //帧内容完整标志 1 : 完整 ; 0 : 不完整
uint8 out_audiobuff[TS_MAX_OUT_BUFF]; //当前音频帧数据缓存
int audio_buflen; //当前音频帧数据缓存长度
uint64_t pts_audio; //当前音频帧PTS
uint64_t dts_audio; //当前音频帧DTS
int audio_cc_ok;
int audio_last_cc;
int audio_intactness;
}ts_outdata;
class TSParser :public TSPacket
{
public:
TSParser();
~TSParser();
unsigned char pcm_buffer[1024 * 20];
long Xferred;
double m_Fps;
double a_Fps;
unsigned int jpg_count;
unsigned int audio_count;
CFifo m_H264BufFifo;
CFifo m_AccBufFifo;
CFifo m_TsBufFifo;
CFifo m_DirtyH264BufFifo;
CFifo m_DirtyAccBufFifo;
CFifo m_DirtyTsBufFifo;
int m_H264BufCount;
int m_AccBufCount;
int m_TsBufCount;
mfxBitstreamTS m_H264Buf[100];
mfxBitstreamTS m_AccBuf[100];
mfxBitstreamTS m_TsBuf[100];
TS_ERR Parse();
HANDLE TsVedioThreadHandle;
HANDLE TsAudioThreadHandle;
HANDLE TsDoloopThreadHandle;
HANDLE PrintThreadHandle;
JavaVM* m_jvm;
jobject _javaAudioObj;
jclass _javaAudioClass;
jobject _javaVedioObj;
jclass _javaVedioClass;
jobject _javaSpeedObj;
jclass _javaSpeedClass;
jmethodID _accCid;
jmethodID _h264Cid;
jmethodID _speedCid;
void InitH264Memory();
mfxBitstreamTS * GetEmptyH264Buf();
void ResetH264Buf();
bool PushDirytH264Buf(mfxBitstreamTS * pbuf);
mfxBitstreamTS * GetH264Buf();
bool PushH264Buf(mfxBitstreamTS * pbuf);
void ReleaseH264Buf();
void InitAccMemory();
mfxBitstreamTS * GetEmptyAccBuf();
void ReleaseAccBuf();
bool PushAccBuf(mfxBitstreamTS * pbuf);
mfxBitstreamTS * GetAccBuf();
void ResetAccBuf();
bool PushDirytAccBuf(mfxBitstreamTS * pbuf);
void InitTsMemory();
mfxBitstreamTS * GetEmptyTsBuf();
void ReleaseTsBuf();
bool PushTsBuf(mfxBitstreamTS * pbuf);
bool PushTsFrame(unsigned char *pData, unsigned int len);
mfxBitstreamTS * GetTsBuf();
void ResetTsBuf();
bool PushDirytTsBuf(mfxBitstreamTS * pbuf);
TS_ERR __stopThread();
TS_ERR initMemory();
TS_ERR initAudioDecoder();
int JavaMethodInit(JavaVM* vm, jobject obj);
private:
static void *videothread(void * cc);
static void *audiothread(void * cc);
static void * print_thread(void * cc) ;
void ShowStat(long t,TSParser * pBc);
bool __SeekToFirstPkt(uint64 u64Offset=0);
void __PrintPacketInfo(TSPacket &tPkt, uint64 u64Offset, uint32 u32PktNo);
const char *__TSTimeToStr(sint64 s64Time);
TS_ERR __ParsePAT();
TS_ERR __ParsePMT();
TS_ERR __ParsePES();
TS_ERR __ParsePESData();
TS_ERR __Parse(const uint8 *pBuf, uint16 u16BufLen);
private:
// const char* m_strFile;
};
#define DELETER_BUFFER(p) if (NULL != p) { delete p; p = NULL;}
class AutoDelCharBuf
{
public:
AutoDelCharBuf(uint8 *pBuf) : m_pBuf(pBuf) {}
~AutoDelCharBuf() { DELETER_BUFFER(m_pBuf); }
uint8 *Ptr() { return m_pBuf; }
private:
uint8 *m_pBuf;
};
#endif //__TS_PARSER_H__
- TSParse.cpp
#include "TSParser.h"
#define MAX_READ_PKT_NUM 20000
#define MAX_CHECK_PKT_NUM 3
#define MAX_TIME_STR_LEN 20
#define MK_WORD(high,low) (((high)<<8)|(low))
#define MK_PCR(b1,b2,b3,b4,b5) (((sint64)(b1)<<25)|((sint64)(b2)<<17)|((sint64)(b3)<<9)|((sint64)(b4)<<1)|(b5))
#define MK_PTS_DTS(b1,b2,b3,b4,b5) (((sint64)(b1)<<30)|((sint64)(b2)<<22)|((sint64)(b3)<<15)|((sint64)(b4)<<7)|(b5))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define RETURN_IF_NOT_OK(ret) if (TS_OK != ret) { return ret; }
// 记录所有pid
uint16 TSPacket::s_au16PIDs[E_MAX] = { PID_UNSPEC, PID_UNSPEC, PID_UNSPEC,
PID_UNSPEC, PID_UNSPEC, PID_UNSPEC, PID_UNSPEC };
bool isDoWritePes = false;
static uint8 avStreamId;
bool m_H264Running = true;
bool m_AccRunning = true;
bool m_TsRunning = true;
int videoIndex = 0;
int last_ts_cc = 0;
int current_ts_cc = 0;
_HANDLE_ *pHandle;
/*判断是否存在适应区域*/
bool TSPacket::__HasAdaptField() {
assert(NULL != m_pHdr);
return (m_pHdr->adaptation_field_control == 0x3); //(0 != (m_pHdr->adaptation_field_control & 0x2));//m_pHdr->adaptation_field_control == 0x2 ||
}
/* 判断是否存在负载
*/
bool TSPacket::__HasPayload() {
assert(NULL != m_pHdr);
return m_pHdr->payload_unit_start_indicator
|| ((m_pHdr->adaptation_field_control & 0x1));
}
/*获取适应区域指针;适应区域不存在时返回NULL
*/
AdaptFixedPart* TSPacket::__GetAdaptField() {
assert(NULL != m_pBuf);
assert(NULL != m_pHdr);
AdaptFixedPart *pAdpt = NULL;
if (__HasAdaptField()) {
pAdpt = (AdaptFixedPart*) (m_pBuf + sizeof(TSHdrFixedPart));
}
return pAdpt;
}
/*获取适应区域的长度
*/
uint8 TSPacket::__GetAdaptLen() {
uint8 u8AdaptLen = 0;
AdaptFixedPart *pAdpt = __GetAdaptField();
if (NULL != pAdpt) {
// "adaptation_field_length" field is 1 byte
u8AdaptLen = pAdpt->adaptation_field_length + 1;
}
return u8AdaptLen;
}
/*存在PCR字段时,获取PCR的值;不存在时返回-1*/
sint64 TSPacket::__GetPCR() {
assert(NULL != m_pBuf);
assert(NULL != m_pHdr);
sint64 s64PCR = INVALID_VAL;
if (__HasAdaptField()) {
AdaptFixedPart *pAdpt = (AdaptFixedPart*) (m_pBuf
+ sizeof(TSHdrFixedPart));
if (pAdpt->adaptation_field_length > 0 && pAdpt->PCR_flag) {
PCR *pcr = (PCR*) ((const char*) pAdpt + sizeof(AdaptFixedPart));
s64PCR = MK_PCR(pcr->pcr_base32_25,
pcr->pcr_base24_17,
pcr->pcr_base16_9,
pcr->pcr_base8_1,
pcr->pcr_base0);
}
}
return s64PCR;
}
/*根据StreamType判断是否视频流*/
bool TSPacket::__IsVideoStream(uint8 u8StreamType) {
return ((ES_TYPE_MPEG1V == u8StreamType) || (ES_TYPE_MPEG2V == u8StreamType)
|| (ES_TYPE_MPEG4V == u8StreamType)
|| (ES_TYPE_H264 == u8StreamType));
}
/*根据StreamType判断是否音频流*/
bool TSPacket::__IsAudioStream(uint8 u8StreamType) {
return ((ES_TYPE_MPEG1A == u8StreamType) || (ES_TYPE_MPEG2A == u8StreamType)
|| (ES_TYPE_AC3 == u8StreamType) || (ES_TYPE_AAC == u8StreamType)
|| (ES_TYPE_DTS == u8StreamType));
}
/*获取负载相对于TS包头的偏移*/
uint8 TSPacket::__GetPayloadOffset() {
uint8 u8Pos = sizeof(TSHdrFixedPart);
if (__HasAdaptField()) {
u8Pos += __GetAdaptLen();
}
return u8Pos;
}
/*获取PAT/PMT表相对于TS包头的偏移*/
uint8 TSPacket::__GetTableStartPos() {
assert(NULL != m_pBuf);
uint8 u8Pos = __GetPayloadOffset();
if (__HasPayload()) {
// "pointer_field" field is 1 byte,
/**
* 当前 的 pointer_field 通过 PSI 包中赋值设置为‘1’的 payload_unit_start_indicator 来标示。(在非 PSI 包中,该指
示符标示传输流包中 PES 包起始)。pointer_field 指向传输流包中第一分段的起始。在传输流包中从不存在
多于一个的 pointer_field
*/
// and whose value is the number of bytes before payload
uint8 u8PtrFieldLen = m_pBuf[u8Pos] + 1;
u8Pos += u8PtrFieldLen;
}
return u8Pos;
}
/*存在PTS字段时,获取PTS的值;不存在时返回-1*/
sint64 TSPacket::__GetPTS(const OptionPESHdrFixedPart *pHdr) {
assert(NULL != pHdr);
sint64 s64PTS = INVALID_VAL;
if (pHdr->PTS_DTS_flags & 0x2) {
PTS_DTS *pPTS =
(PTS_DTS*) ((char*) pHdr + sizeof(OptionPESHdrFixedPart));
s64PTS =
MK_PTS_DTS(pPTS->ts32_30, pPTS->ts29_22, pPTS->ts21_15, pPTS->ts14_7, pPTS->ts6_0);
}
return s64PTS;
}
/*存在DTS字段时,获取DTS的值;不存在时返回-1*/
sint64 TSPacket::__GetDTS(const OptionPESHdrFixedPart *pHdr) {
assert(NULL != pHdr);
sint64 s64DTS = INVALID_VAL;
if (pHdr->PTS_DTS_flags & 0x1) {
PTS_DTS *pDTS = (PTS_DTS*) ((char*) pHdr + sizeof(OptionPESHdrFixedPart)
+ sizeof(PTS_DTS));
s64DTS =
MK_PTS_DTS(pDTS->ts32_30, pDTS->ts29_22, pDTS->ts21_15, pDTS->ts14_7, pDTS->ts6_0);
}
return s64DTS;
}
bool has_finish = false;
int pre_head; //记录截取头字节
int last_head; //记录剩余的字节尾部
int first_index; //记录第一个头文件找到位置
TSParser::TSParser() {
}
TSParser::~TSParser() {
ReleaseH264Buf();
ReleaseAccBuf();
ReleaseTsBuf();
}
TS_ERR TSParser::__stopThread() {
m_H264Running = false;
m_TsRunning = false;
m_AccRunning = false;
ReleaseAccBuf();
ReleaseH264Buf();
ReleaseTsBuf();
aac_decode_close(pHandle->pContext);
delete pHandle;
if (TsVedioThreadHandle)
pthread_join(TsVedioThreadHandle, NULL);
TsVedioThreadHandle = NULL;
if (TsAudioThreadHandle)
pthread_join(TsAudioThreadHandle, NULL);
TsAudioThreadHandle = NULL;
if (PrintThreadHandle)
pthread_join(PrintThreadHandle, NULL);
PrintThreadHandle = NULL;
return TS_OK;
}
TS_ERR TSParser::initMemory() {
m_H264Running = true;
m_TsRunning = true;
m_AccRunning = true;
InitTsMemory();
InitH264Memory();
InitAccMemory();
return TS_OK;
}
TS_ERR TSParser::initAudioDecoder() {
pHandle = new _HANDLE_;
pHandle->code = AV_CODEC_ID_MP3;
pHandle->pContext = 0;
av_register_all();
av_log_set_callback(my_logoutput);
pHandle->pContext = aac_decoder_create(AV_CODEC_ID_MP3, IN_SAMPLE_RATE,
AUDIO_CHANNELS, SAMPLE_BIT);
if (pHandle->pContext != NULL) {
mDebug("initAudioDecoder成功");
}
return TS_OK;
}
TS_ERR TSParser::Parse() {
int ret_t;
initAudioDecoder();
mfxBitstreamTS *tsBuf = NULL;
bool has_cache = false;
bool has_sycn = false;
bool is_head_start = false;
m_H264Running = true;
m_TsRunning = true;
m_AccRunning = true;
mDebug("--------------开始解析");
struct sched_param param;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
param.sched_priority = 90;
pthread_attr_setschedparam(&attr, ¶m);
ret_t = pthread_create(&TsVedioThreadHandle, &attr, videothread, this);
if (ret_t) {}
ret_t = pthread_create(&TsAudioThreadHandle, &attr, audiothread, this);
if (ret_t) {}
ret_t = pthread_create(&PrintThreadHandle, &attr, print_thread, this);
if (ret_t) {}
TS_ERR ret = TS_OK;
unsigned char buffer[TS_PKT_LEN * 2];
uint8 *pCacheBuf = buffer;
uint32 ts_temp_len = 0;
uint32 ts_cache_len = 0;
unsigned char *pCurrentPos = NULL;
long ms = 0;
struct timespec ts;
struct timespec pts;
clock_gettime(CLOCK_MONOTONIC, &pts);
clock_gettime(CLOCK_MONOTONIC, &ts);
pktH264Len = 0;
pktAccLen = 0;
h264Buf = 0;
jpg_count = 0;
audio_count = 0;
for (; m_TsRunning;) {
while (m_TsRunning) {
tsBuf = GetTsBuf();
if (tsBuf != NULL) {
break;
} else {
usleep(2);
}
}
if (tsBuf == NULL) {
break;
}
Xferred += tsBuf->DataLength;
if (tsBuf->Data == NULL || tsBuf->DataLength == 0) {
PushDirytTsBuf(tsBuf);
tsBuf = NULL;
continue;
}
pCurrentPos = tsBuf->Data;
ts_temp_len = tsBuf->DataLength;
is_head_start = true;
if (has_cache) {
has_cache = false;
memcpy(pCacheBuf + ts_cache_len, pCurrentPos,(TS_PKT_LEN - ts_cache_len));
if (TS_SYNC_BYTE == buffer[0]) {
ret = __Parse(pCacheBuf, TS_PKT_LEN);
ts_temp_len = tsBuf->DataLength - (TS_PKT_LEN - ts_cache_len);
pCurrentPos += TS_PKT_LEN - ts_cache_len;
} else {
//缓存帧无头文件 %d ", ts_cache_len;
}
}
while (ts_temp_len > TS_PKT_LEN && m_TsRunning) {
if (TS_SYNC_BYTE == *(pCurrentPos)
&& TS_SYNC_BYTE == *(pCurrentPos + TS_PKT_LEN)) {
is_head_start = false;
ret = __Parse(pCurrentPos, TS_PKT_LEN);
pCurrentPos += TS_PKT_LEN;
ts_temp_len -= TS_PKT_LEN;
} else {
//文件出错,查找同步头
pCurrentPos++;
ts_temp_len--;
}
}
if (TS_SYNC_BYTE == *(pCurrentPos)) {
if (ts_temp_len == TS_PKT_LEN) {
ret = __Parse(pCurrentPos, TS_PKT_LEN);
} else {
ts_cache_len = ts_temp_len;
memcpy(pCacheBuf, pCurrentPos, ts_cache_len);
has_cache = true;
}
} else {
for (int i = 0; i < ts_temp_len; i++) {
if (!m_TsRunning) {
break;
}
if (TS_SYNC_BYTE == *(pCurrentPos + i)) {
memcpy(pCacheBuf, pCurrentPos + i, ts_temp_len - i);
ts_cache_len = ts_temp_len - i;
has_cache = true;
break;
}
}
}
PushDirytTsBuf(tsBuf);
tsBuf = NULL;
clock_gettime(CLOCK_MONOTONIC, &ts);
ms = (ts.tv_sec - pts.tv_sec) * 1000
+ (ts.tv_nsec - pts.tv_nsec) / 1000000;
if (ms >= 1000) {
clock_gettime(CLOCK_MONOTONIC, &pts);
m_Fps = (double) (jpg_count * 1000) / ms;
jpg_count = 0;
a_Fps = (double) (audio_count * 1000) / ms;
audio_count = 0;
}}
return ret;
}
/*解析TS包*/
TS_ERR TSParser::__Parse(const uint8 *pBuf, uint16 u16BufLen) {
//
assert(NULL != pBuf);
TS_ERR ret = TS_OK;
if ((NULL == pBuf) || (TS_PKT_LEN != u16BufLen)) {
return TS_IN_PARAM_ERR;
}
if (TS_SYNC_BYTE != pBuf[0]) {
return TS_SYNC_BYTE_ERR;
}
// mInfo("--------------------__Parse 查找开始");
m_pBuf = pBuf;
m_pHdr = (TSHdrFixedPart*) pBuf;
m_u16PID = MK_WORD(m_pHdr->pid12_8,m_pHdr->pid7_0);
if (m_u16PID == PID_NULL) {
return ret;
}
//s_au16PIDs[E_PMT] = 256; //项目中ts流信息基本固定了
//s_au16PIDs[E_VIDEO] = 4113;
//s_au16PIDs[E_AUDIO] = 4352;
//s_au16PIDs[E_PCR] = 4097;
//s_au16PIDs[E_SI] = 31;
//s_au16PIDs[E_PAT] = 0;
/**continuity_counter 为 4 比特字段,随着具有相同 PID 的每个传输流包而增加。
continuity_counter 在取其最大值之后循环返回到 0 值。当包的 adaptation_field_control 为‘00’或‘10’时,
continuity_counter 不增加*/
m_u8CC = m_pHdr->continuity_counter;
if (IsPAT()) {
ret = __ParsePAT();
return ret;
} else if (IsSIT()) {
} else if (IsNIT()) {
} else if (IsPMT()) {
ret = __ParsePMT();
return ret;
} else if (m_u16PID == s_au16PIDs[E_PCR]) {
//PCR是TS里面的,即TS packet的header里面可能会有,他用来指定所期望的该ts packet到达decoder的时间,他的作用于SCR类似。
/**包含 PID 未标示为 PCR_PID 的基本流数据的、包内连续性计数器不连续性点出现的以及包内 PTS 或
DTS 发生的每个传输流包,应在相关节目的系统时间基不连续性发生之后到达 T-STD 的输入端。在不连续
性状态为真的情况中,若相同 PID 的两个连续的传输流包出现,具有相同的 continuity_counter 值并具有
adaptation_field_control 值设置为‘01’或‘11’,则第二个包可以丢弃。传输流应不通过这样的方式来构造,
因为丢弃此类包它将引起 PES 包有效载荷数据或 PSI 数据的丢失。*/
// m_s64PCR = __GetPCR();
}
// if(m_u16PID!=PID_NULL&&(m_pHdr->adaptation_field_control != 0x2)){
/*当传输流包有效载荷包含 PSI 数据时,payload_unit_start_indicator 具有以下意义:若传输流包承载 PSI
分段的首字节,则 payload_unit_start_indicator 值必为 1,指示此传输流包的有效载荷的首字节承载
pointer_field。若传输流包不承载 PSI 分段的首字节,则 payload_unit_start_indicator 值必为‘0’,指示在此
有效载荷中不存在 pointer_field。参阅 2.4.4.1 和 2.4.4.2。**/
if (IsVideo() ) {//|| IsAudio()
// mDebug("TAV——————————————————————视频数据");
if (m_pHdr->payload_unit_start_indicator == 1) { //‘1’,则一个且仅有一个 PES 包在此传输流包中起始
// mDebug("--------------查询到PES头信息");
ret = __ParsePES();
} else { //‘0’指示在此传输流包中无任何 PES 包将开始
/*空包payload_unit_start_indicator应置为0.
·PID:13b。表示净荷的数据类型。PID=0x0000,表示净荷的数据位节目关联表。*/
if (IsSIT()) {
} else {
ret = __ParsePESData();
}
}
}else if(IsAudio()){
mDebug("TAV——————————————————————音频数据");
}
return ret;
}
TS_ERR TSParser::__ParsePAT() {
assert(NULL != m_pBuf);
const uint8 *pPATBuf = m_pBuf + __GetTableStartPos();
PATHdrFixedPart *pPAT = (PATHdrFixedPart*) pPATBuf;
uint16 u16SectionLen =
MK_WORD(pPAT->section_length11_8, pPAT->section_length7_0);
uint16 u16AllSubSectionLen = u16SectionLen
- (sizeof(PATHdrFixedPart) - HDR_LEN_NOT_INCLUDE) - CRC32_LEN;
uint16 u16SubSectionLen = sizeof(PATSubSection);
const uint8 *ptr = pPATBuf + sizeof(PATHdrFixedPart);
for (uint16 i = 0; i < u16AllSubSectionLen; i += u16SubSectionLen) {
PATSubSection *pDes = (PATSubSection*) (ptr + i);
uint16 u16ProgNum = pDes->program_number;
uint16 u16PID = MK_WORD(pDes->pid12_8, pDes->pid7_0);
if (0x00 == u16ProgNum) {
uint16 u16NetworkPID = u16PID;
} else {
m_u16PMTPID = u16PID; // program_map_PID
break;
}
}
s_au16PIDs[E_PMT] = m_u16PMTPID;
return TS_OK;
}
TS_ERR TSParser::__ParsePMT() {
assert(NULL != m_pBuf);
const uint8 *pPMTBuf = m_pBuf + __GetTableStartPos();
PMTHdrFixedPart *pPMT = (PMTHdrFixedPart*) pPMTBuf;
s_au16PIDs[E_PCR] = MK_WORD(pPMT->PCR_PID12_8, pPMT->PCR_PID7_0);
uint16 u16SectionLen =
MK_WORD(pPMT->section_length11_8, pPMT->section_length7_0);
// n * program_info_descriptor的长度
uint16 u16ProgInfoLen =
MK_WORD(pPMT->program_info_length11_8, pPMT->program_info_length7_0);
uint16 u16AllSubSectionLen = u16SectionLen
- (sizeof(PMTHdrFixedPart) - HDR_LEN_NOT_INCLUDE) - u16ProgInfoLen
- CRC32_LEN;
uint16 u16SubSectionLen = sizeof(PMTSubSectionFixedPart);
const uint8 *ptr = pPMTBuf + sizeof(PMTHdrFixedPart) + u16ProgInfoLen;
for (uint16 i = 0; i < u16AllSubSectionLen; i += u16SubSectionLen) {
PMTSubSectionFixedPart *pSec = (PMTSubSectionFixedPart*) (ptr + i);
uint16 u16ElementaryPID =
MK_WORD(pSec->elementaryPID12_8, pSec->elementaryPID7_0);
uint16 u16ESInfoLen =
MK_WORD(pSec->ES_info_lengh11_8, pSec->ES_info_lengh7_0);
u16SubSectionLen += u16ESInfoLen;
if (__IsVideoStream(pSec->stream_type)) {
s_au16PIDs[E_VIDEO] = u16ElementaryPID;
} else if (__IsAudioStream(pSec->stream_type)) {
s_au16PIDs[E_AUDIO] = u16ElementaryPID;
} else {
}
}
return TS_OK;
}
TS_ERR TSParser::__ParsePES() {
int cc;
int ret = -1;
assert(NULL != m_pBuf);
uint64 total_len = 0;
uint64 es_len = 0;
const uint8 *pPESBuf = m_pBuf + 4; // __GetPayloadOffset(); TODO: 此处4为格式固定值,写死调式降低延迟
const uint8 *pPESData;
PESHdrFixedPart *pPES = (PESHdrFixedPart*) pPESBuf;
if (PES_START_CODE == pPES->packet_start_code_prefix) { //PES_START_CODE == pPES->packet_start_code_prefix
m_u8StreamId = pPES->stream_id;
if ((m_u8StreamId & PES_STREAM_VIDEO)
|| (m_u8StreamId & PES_STREAM_AUDIO)) {
OptionPESHdrFixedPart *pHdr = (OptionPESHdrFixedPart*) (pPESBuf
+ sizeof(PESHdrFixedPart));
avStreamId = m_u8StreamId;
pPESData = m_pBuf + (4 + sizeof(PESHdrFixedPart)
+ sizeof(OptionPESHdrFixedPart)
+ pHdr->PES_Hdr_data_length);
es_len = TS_PKT_LEN
- (4 + sizeof(PESHdrFixedPart)
+ sizeof(OptionPESHdrFixedPart)
+ pHdr->PES_Hdr_data_length);
if (IsVideo()) {
if (pktH264Len != 0 && h264Buf != NULL) {
if (h264Buf->MaxLength > pktH264Len) {
h264Buf->DataLength = pktH264Len;
PushH264Buf(h264Buf);
h264Buf = NULL;
pktH264Len = 0;
jpg_count++;
} else {
mDebug("h264 buf is small than H264 data -%d",
pktH264Len);
PushDirytH264Buf(h264Buf);
h264Buf = NULL;
pktH264Len = 0;
}
}
while (m_H264Running) {
h264Buf = GetEmptyH264Buf();
if (h264Buf == NULL || h264Buf->Data == NULL) {
mDebug("取不到空的H264寄存对象");
usleep(1);
continue;
}
break;
}
if (h264Buf == NULL) {
return TS_IN_PARAM_ERR;
}
memcpy(h264Buf->Data, pPESData, es_len);
pktH264Len = es_len;
} else if (IsAudio()) {
// if (pktAccLen != 0 && __accBuf != NULL) {
// if (__accBuf->MaxLength > pktAccLen) {
// __accBuf->DataLength = pktAccLen;
// PushAccBuf(__accBuf);
// __accBuf = NULL;
// pktAccLen = 0;
// } else {
// mDebug("__accBuf buf is small than H264 data -%d",
// pktAccLen);
// PushDirytAccBuf(__accBuf);
// __accBuf = NULL;
// pktAccLen = 0;
// }
// audio_count++;
// }
// while (m_AccRunning) {
// __accBuf = GetEmptyAccBuf();
// if (__accBuf == NULL || __accBuf->Data == NULL) {
// usleep(10);
// continue;
// }
// break;
// }
// if (__accBuf == NULL) {
// return TS_IN_PARAM_ERR;
// }
// memcpy(__accBuf->Data, pPESData, es_len);
// pktAccLen = es_len;
}
} else {
// mLogW("---PES视频打头非视频帧");
}
} else {
// mLogW("---PES非视频打头");
avStreamId = 0;
}
return TS_OK;
}
TS_ERR TSParser::__ParsePESData() {
int cc;
uint64 total_len = 0;
uint64 es_len = 0;
uint8 es_test = __GetPayloadOffset();
const uint8 *pPESBuf = m_pBuf + __GetPayloadOffset();
es_len = TS_PKT_LEN - __GetPayloadOffset();
if (IsVideo()) //视频
{
if (h264Buf != NULL && h264Buf->Data != NULL) {
memcpy(h264Buf->Data + pktH264Len, pPESBuf, es_len);
pktH264Len = pktH264Len + es_len;
} else {
}
} else if (IsAudio()) { //音频
// if (__accBuf != NULL && __accBuf->Data != NULL) {
// memcpy(__accBuf->Data + pktAccLen, pPESBuf, es_len);
// pktAccLen = pktAccLen + es_len;
// } else {
mDebug("CameraLib ----__ParsePESData fifo存入为空 ");
// }
}
pPESBuf = NULL;
return TS_OK;
}
const char *TSParser::__TSTimeToStr(sint64 s64Time) {
static char s_acTimeStr[MAX_TIME_STR_LEN] = { 0 };
sint64 s64MiliSecond = s64Time / 90;
sint64 s64Second = s64MiliSecond / 1000;
return s_acTimeStr;
}
void TSParser::InitAccMemory() {
int i;
bool ret;
m_AccBufCount = 20;
long len = 25000 * 200;
for (i = 0; i < m_AccBufCount; i++) {
memset(&m_AccBuf[i], 0, sizeof(mfxBitstreamTS));
m_AccBuf[i].Data = new UCHAR[len];
if (m_AccBuf[i].Data) {
memset(m_AccBuf[i].Data, 0xff, len);
} else {
return;
}
m_AccBuf[i].MaxLength = len;
m_AccBuf[i].last_cc = -1;
m_AccBuf[i].intactness = 1;
}
ResetAccBuf();
}
void TSParser::ReleaseAccBuf() {
for (int i = 0; i < m_AccBufCount; i++) {
if (m_AccBuf[i].Data) {
delete[] m_AccBuf[i].Data;
m_AccBuf[i].Data = NULL;
}
}
}
bool TSParser::PushAccBuf(mfxBitstreamTS * pbuf) {
bool ret = m_AccBufFifo.put((void *) pbuf);
if (!ret) {
mfxBitstreamTS * pbuf1 = NULL;
pbuf1 = (mfxBitstreamTS *) m_AccBufFifo.get();
if (pbuf1) {
PushDirytAccBuf(pbuf1);
}
return m_AccBufFifo.put((void *) pbuf);
} else
return ret;
}
mfxBitstreamTS * TSParser::GetAccBuf() {
mfxBitstreamTS * pbuf = NULL;
pbuf = (mfxBitstreamTS *) m_AccBufFifo.get();
return pbuf;
}
bool TSParser::PushDirytAccBuf(mfxBitstreamTS * pbuf) {
if (pbuf == NULL)
return false;
pbuf->DataLength = 0;
pbuf->DataOffset = 0;
return m_DirtyAccBufFifo.put((void *) pbuf);
}
mfxBitstreamTS * TSParser::GetEmptyAccBuf() {
mfxBitstreamTS * pbuf = NULL;
pbuf = (mfxBitstreamTS *) m_DirtyAccBufFifo.get();
if (pbuf) {
pbuf->DataLength = 0;
pbuf->DataOffset = 0;
}
return pbuf;
}
void TSParser::ResetAccBuf() {
int i = 0;
m_DirtyAccBufFifo.flush();
m_DirtyAccBufFifo.Create(m_AccBufCount);
for (i = 0; i < m_AccBufCount; i++) {
int ret = m_DirtyAccBufFifo.put((void *) &m_AccBuf[i]);
if (!ret) {
return;
}
}
m_AccBufFifo.flush();
m_AccBufFifo.Create(m_AccBufCount - 1);
}
void TSParser::InitH264Memory() {
int i;
bool ret;
m_H264BufCount = 20;
long len = 1024 * 3500;
for (i = 0; i < m_H264BufCount; i++) {
memset(&m_H264Buf[i], 0, sizeof(mfxBitstreamTS));
m_H264Buf[i].Data = new UCHAR[len];
if (m_H264Buf[i].Data) {
memset(m_H264Buf[i].Data, 0xff, len);
} else {
return;
}
m_H264Buf[i].MaxLength = len;
m_H264Buf[i].last_cc = -1;
m_H264Buf[i].intactness = 1;
}
ResetH264Buf();
}
void TSParser::ReleaseH264Buf() {
for (int i = 0; i < m_H264BufCount; i++) {
if (m_H264Buf[i].Data) {
delete[] m_H264Buf[i].Data;
m_H264Buf[i].Data = NULL;
}
}
}
bool TSParser::PushH264Buf(mfxBitstreamTS * pbuf) {
if (pbuf->DataLength >= 1024 * 1000 || pbuf->DataLength <= 0) {
mDebug("error H264buf长度异常 %d", pbuf->DataLength);
}
bool ret = m_H264BufFifo.put((void *) pbuf);
if (!ret) { //判断是否存入,没有存入说明已满,去除头丢掉,再存
mfxBitstreamTS * pbuf1 = NULL;
pbuf1 = (mfxBitstreamTS *) m_H264BufFifo.get();
if (pbuf1) {
PushDirytH264Buf(pbuf1);
}
return m_H264BufFifo.put((void *) pbuf);
} else
return ret;
}
mfxBitstreamTS * TSParser::GetH264Buf() {
mfxBitstreamTS * pbuf = NULL;
pbuf = (mfxBitstreamTS *) m_H264BufFifo.get();
return pbuf;
}
void TSParser::ResetH264Buf() {
int i = 0;
m_DirtyH264BufFifo.flush();
m_DirtyH264BufFifo.Create(m_H264BufCount);
for (i = 0; i < m_H264BufCount; i++) {
int ret = m_DirtyH264BufFifo.put((void *) &m_H264Buf[i]);
if (!ret) {
return;
}
}
m_H264BufFifo.flush();
m_H264BufFifo.Create(m_H264BufCount - 1);
}
bool TSParser::PushDirytH264Buf(mfxBitstreamTS * pbuf) {
if (pbuf == NULL)
return false;
pbuf->DataLength = 0;
pbuf->DataOffset = 0;
return m_DirtyH264BufFifo.put((void *) pbuf);
}
mfxBitstreamTS * TSParser::GetEmptyH264Buf() {
mfxBitstreamTS * pbuf = NULL;
pbuf = (mfxBitstreamTS *) m_DirtyH264BufFifo.get();
if (pbuf) {
pbuf->DataLength = 0;
pbuf->DataOffset = 0;
}
return pbuf;
}
void TSParser::InitTsMemory() {
int i;
bool ret;
m_TsBufCount = 30;
long len = 188 * 1024;
for (i = 0; i < m_TsBufCount; i++) {
memset(&m_TsBuf[i], 0, sizeof(mfxBitstreamTS));
m_TsBuf[i].Data = new UCHAR[len];
if (m_TsBuf[i].Data) {
memset(m_TsBuf[i].Data, 0xff, len);
} else {
mDebug("new m_TsBuf[%d] failed:\n", i);
return;
}
m_TsBuf[i].MaxLength = len;
m_TsBuf[i].last_cc = -1;
m_TsBuf[i].intactness = 1;
}
ResetTsBuf();
}
void TSParser::ReleaseTsBuf() {
for (int i = 0; i < m_TsBufCount; i++) {
if (m_TsBuf[i].Data) {
delete[] m_TsBuf[i].Data;
m_TsBuf[i].Data = NULL;
}
}
}
bool TSParser::PushTsBuf(mfxBitstreamTS * pbuf) {
bool ret = m_TsBufFifo.put((void *) pbuf);
if (!ret) { //判断是否存入,没有存入说明已满,去除头丢掉,再存
mfxBitstreamTS * pbuf1 = NULL;
pbuf1 = (mfxBitstreamTS *) m_TsBufFifo.get();
if (pbuf1) {
PushDirytTsBuf(pbuf1);
}
return m_TsBufFifo.put((void *) pbuf);
} else
return ret;
}
bool TSParser::PushTsFrame(unsigned char *pData, unsigned int len) {
mfxBitstreamTS *pBufJpg = NULL;
while (m_TsRunning) {
pBufJpg = GetEmptyTsBuf();
if (pBufJpg == NULL || pBufJpg->Data == NULL) {
usleep(1);
continue;
}
break;
}
if (pBufJpg == NULL || pBufJpg->Data == NULL) {
return -2;
}
memcpy(pBufJpg->Data, pData, len);
pBufJpg->DataLength = len;
PushTsBuf(pBufJpg);
return true;
}
mfxBitstreamTS * TSParser::GetTsBuf() {
mfxBitstreamTS * pbuf = NULL;
pbuf = (mfxBitstreamTS *) m_TsBufFifo.get();
return pbuf;
}
void TSParser::ResetTsBuf() {
int i = 0;
m_DirtyTsBufFifo.flush();
m_DirtyTsBufFifo.Create(m_TsBufCount);
for (i = 0; i < m_TsBufCount; i++) {
int ret = m_DirtyTsBufFifo.put((void *) &m_TsBuf[i]);
if (!ret) {
return;
}
}
m_TsBufFifo.flush();
m_TsBufFifo.Create(m_TsBufCount - 1);
}
bool TSParser::PushDirytTsBuf(mfxBitstreamTS * pbuf) {
if (pbuf == NULL)
return false;
pbuf->DataLength = 0;
pbuf->DataOffset = 0;
return m_DirtyTsBufFifo.put((void *) pbuf);
}
mfxBitstreamTS * TSParser::GetEmptyTsBuf() {
mfxBitstreamTS * pbuf = NULL;
pbuf = (mfxBitstreamTS *) m_DirtyTsBufFifo.get();
if (pbuf) {
pbuf->DataLength = 0;
pbuf->DataOffset = 0;
}
return pbuf;
}
jobject getInstanceTs(JNIEnv* env, jclass obj_class) {
jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
jobject obj = env->NewObject(obj_class, construction_id);
return obj;
}
//获取视频帧
void *TSParser::videothread(void * cc) {
TSParser * pBc = (TSParser *) cc;
mfxBitstreamTS *h264Buf = NULL;
for (; m_H264Running;) {
while (m_H264Running) {
h264Buf = pBc->GetH264Buf();
if (h264Buf != NULL && h264Buf->Data != NULL) { //如果是视频 &&h264Buf->DataLength!=0
if (pBc->m_jvm) {
bool isAttached = false;
JNIEnv* env = NULL;
if (pBc->m_jvm->GetEnv((void**) &env,
JNI_VERSION_1_4) != JNI_OK) {
jint res = pBc->m_jvm->AttachCurrentThread(&env, NULL);
// Get the JNI env for this thread
if ((res < 0) || !env) {
env = NULL;
} else {
isAttached = true;
}
}
if (env && pBc->_h264Cid) {
jbyteArray bytes = env->NewByteArray(
h264Buf->DataLength);
env->SetByteArrayRegion(bytes, 0, h264Buf->DataLength,
(jbyte*) h264Buf->Data);
pBc->_javaVedioObj = getInstanceTs(env,
pBc->_javaVedioClass);
env->CallVoidMethod(pBc->_javaVedioObj, pBc->_h264Cid,
bytes);
env->DeleteLocalRef(bytes);
env->DeleteLocalRef(pBc->_javaVedioObj);
}
if (isAttached) {
if (pBc->m_jvm->DetachCurrentThread() < 0) {
mDebug( "Could not detach thread from JVM");
}
}
}
break;
} else {
usleep(1);
}
}
pBc->PushDirytH264Buf(h264Buf);
h264Buf = NULL;
}
}
void *TSParser::audiothread(void * cc) {
TSParser * pBc = (TSParser *) cc;
uint32 decoderMp3State = 0;
uint32 * pPCMLen = 0;
mfxBitstreamTS *accBuf = NULL;
for (; m_AccRunning;) {
while (m_AccRunning) {
accBuf = pBc->GetAccBuf();
// mDebug("callback DeliverFrame test 0");
if (accBuf != NULL && accBuf->Data != NULL) { //如果是视频 &&h264Buf->DataLength!=0
//void *pParam, unsigned char *pData, int nLen, unsigned char *pPCM, unsigned int *outLen
decoderMp3State = aac_decode_frame(pHandle->pContext,
accBuf->Data, accBuf->DataLength, pBc->pcm_buffer,
pPCMLen);
if (pBc->m_jvm && decoderMp3State > 0) {
bool isAttached = false;
JNIEnv* env = NULL;
if (pBc->m_jvm->GetEnv((void**) &env,
JNI_VERSION_1_4) != JNI_OK) {
// try to attach the thread and get the env
// Attach this thread to JVM
jint res = pBc->m_jvm->AttachCurrentThread(&env, NULL);
// Get the JNI env for this thread
if ((res < 0) || !env) {
mDebug("Could not attach thread to JVM (%d, %p)",
res, env);
env = NULL;
} else {
isAttached = true;
}
}
if (env && pBc->_accCid) {
jbyteArray bytes = env->NewByteArray(decoderMp3State);
env->SetByteArrayRegion(bytes, 0, decoderMp3State,(jbyte*) pBc->pcm_buffer);
pBc->_javaAudioObj = getInstanceTs(env,pBc->_javaAudioClass);
env->CallVoidMethod(pBc->_javaAudioObj, pBc->_accCid,bytes);
env->DeleteLocalRef(bytes);
env->DeleteLocalRef(pBc->_javaAudioObj);
}
if (isAttached) {
if (pBc->m_jvm->DetachCurrentThread() < 0) {
mDebug( "Could not detach thread from JVM");
}
}
}
break;
} else {
usleep(10);
}
}
pBc->PushDirytAccBuf(accBuf);
accBuf = NULL;
}
}
int TSParser::JavaMethodInit(JavaVM* vm, jobject obj) {
if (m_jvm)
return 0;
m_jvm = vm;
if (!m_jvm) {
mDebug( " No JavaVM have been provided.");
return -1;
}
// get the JNI env for this thread
bool isAttached = false;
JNIEnv* env = NULL;
if (m_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
// try to attach the thread and get the env
// Attach this thread to JVM
jint res = m_jvm->AttachCurrentThread(&env, NULL);
// Get the JNI env for this thread
if ((res < 0) || !env) {
mDebug( "Could not attach thread to JVM (%d, %p)", res, env);
return -1;
}
isAttached = true;
}
// get the ViEAndroidGLES20 class
jclass javaRenderClassLocal = reinterpret_cast<jclass>(env->FindClass(
"包名0/包名1/包名2/JniLib"));
//jclass javaRenderClassLocal = reinterpret_cast<jclass> (env->FindClass("包名0/包名1/包名2/JniLib.activity.xxx"));
if (!javaRenderClassLocal) {
mDebug("could not find class 包名0/包名1/包名2/JniLib");
return -1;
}
mDebug("get class 包名0/包名1/包名2/JniLib success");
_javaAudioClass = reinterpret_cast<jclass>(env->NewGlobalRef(
javaRenderClassLocal));
if (!_javaAudioClass) {
mDebug( "could not create Java class reference");
return -1;
}
mDebug("create Java class reference success");
_javaVedioClass = reinterpret_cast<jclass>(env->NewGlobalRef(
javaRenderClassLocal));
if (!_javaVedioClass) {
mDebug( "could not create Java class reference");
return -1;
}
mDebug("create Java class reference success");
_javaSpeedClass = reinterpret_cast<jclass>(env->NewGlobalRef(
javaRenderClassLocal));
if (!_javaSpeedClass) {
mDebug( "could not create Java class reference");
return -1;
}
// Delete local class ref, we only use the global ref
env->DeleteLocalRef(javaRenderClassLocal);
_javaAudioObj = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
if (!_javaAudioObj) {
mDebug("could not create Java object reference");
return -1;
}
// get the method ID for the ReDraw function
_accCid = env->GetMethodID(_javaAudioClass, "aacCallBack", "([B)V");
if (_accCid == NULL) {
mDebug( " could not get dataCallBack ID");
return -1;
}
_javaVedioObj = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
if (!_javaVedioObj) {
mDebug("could not create Java object reference");
return -1;
}
mDebug(" create Global Java object reference success");
// get the method ID for the ReDraw function
_h264Cid = env->GetMethodID(_javaVedioClass, "h264CallBack", "([B)V");
if (_h264Cid == NULL) {
mDebug( " could not get dataCallBack ID");
return -1;
}
_javaSpeedObj = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
if (!_javaSpeedObj) {
mDebug("could not create Java object reference");
return -1;
}
mDebug(" create Global Java object reference success");
// get the method ID for the ReDraw function
_speedCid = env->GetMethodID(_javaSpeedClass, "speedBack", "(JDDI)V");
if (_speedCid == NULL) {
mDebug( " could not get dataCallBack ID");
return -1;
}
mDebug("get dataCallBack ID success");
return 0;
}
- mfxBitstreamTS结构体:
typedef struct{
uint64_t pts; //当前音频帧PTS
uint64_t dts; //当前音频帧DTS
int cc_ok;
int last_cc;
int intactness;
int tream_id;//标识 流
uint8* Data;//当前音频帧数据缓存
mfxU32 DataOffset;
uint32 DataLength;//当前音频帧数据缓存长度
mfxU32 MaxLength;
} mfxBitstreamTS;
三、数据处理过程
以下以H264视频数据处理为例:
- 初始化内存InitH264Memory()–>在fifo中存入空的数组ResetH264Buf()
- 从fifo中取出出GetEmptyH264Buf(),填入数据
–>存入H264数据到fifo : PushYuvBuf(mfxBitstream * pbuf) - 在线程中处理结果时:从H264数据fifo里取出:GetYuvBuf()
—>处理完后数据置空再存入空的fifo里PushDirytH264Buf(mfxBitstream * pbuf) - 处理结果线程结束:清理内存ReleaseH264Buf();
数据处理线程:
- C++线程1: 循环处理TS流查找头文件,解析表结构,裁劫出的音视频数据,填入数据fifo中
- C++线程2: 创建一个Video的线程,从video fifo中取数据并回调到java方法中
- C++线程3: 创建一个Audio的线程,从audio fifo中取数据并回调到java方法中
- java线程1: 创建一个buff缓存组,存入视频组数据,并通过一个while(flag)线程,不断投入到Android Decoder硬解码中。
四、总结
在处理1080P的TS流数据时,测试从解析流到android硬解码预览显示,总延迟约140ms~200ms(晓龙835处理器),发现在硬解码部分耗时较大,有堵塞整个数据通道的嫌疑,具体优化方案在以后给出。