创建C++类-JfVideo,保存Video相关参数:
JfVideo.h
class JfVideo {
public:
int streamIndex = -1;
AVCodecContext *avCodecContext = NULL;
AVCodecParameters *codecpar = NULL;
JfQueue *queue = NULL;
JfPlayStatus *playStatus = NULL;
JfCallJava *callJava = NULL;
public:
JfVideo(JfPlayStatus *playStatus,JfCallJava *callJava);
~JfVideo();
};
JfVideo.cpp
JfVideo::JfVideo(JfPlayStatus *playStatus, JfCallJava *callJava) {
this->playStatus = playStatus;
this->callJava = callJava;
queue = new JfQueue(playStatus);
}
JfVideo::~JfVideo() {
}
找到文件中的视频流并初始化AVCodecContext:
for (int i = 0; i < pFmtCtx->nb_streams; i++) {
if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (audio == NULL) {
audio = new JfAudio(playStatus,pFmtCtx->streams[i]->codecpar->sample_rate,callJava);
audio->streamIndex = i;
audio->codecpar = pFmtCtx->streams[i]->codecpar;
audio->duration = pFmtCtx->duration / AV_TIME_BASE;//单位是秒
audio->time_base = pFmtCtx->streams[i]->time_base;
duration = audio->duration;
}
} else if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
if (video == NULL){
video = new JfVideo(playStatus,callJava);
video->streamIndex = i;
video->codecpar = pFmtCtx->streams[i]->codecpar;
video->time_base = pFmtCtx->streams[i]->time_base;
}
}
}
if (audio != NULL){
initCodecContext(audio->codecpar,&audio->pACodecCtx);
}
if (video != NULL){
initCodecContext(video->codecpar,&video->pVCodecCtx);
}
初始化AVCodecContext,无法将一个指针变量本身传递给一个函数,要用二级指针:
int JfFFmpeg::initCodecContext(AVCodecParameters *codecParameters, AVCodecContext **pCodecContext) {
AVCodec *dec = avcodec_find_decoder(codecParameters->codec_id);
if (!dec){
if (LOG_DEBUG){
LOGE("FIND DECODER ERROR");
callJava->onCallError(CHILD_THREAD,403,"FIND DECODER ERROR");
}
exit = true;
pthread_mutex_unlock(&init_mutex);
return -1;
}
*pCodecContext = avcodec_alloc_context3(dec);
if (!(*pCodecContext)){
if (LOG_DEBUG){
LOGE("avcodec_alloc_context3 ERROR");
callJava->onCallError(CHILD_THREAD,404,"avcodec_alloc_context3 ERROR");
}
exit = true;
pthread_mutex_unlock(&init_mutex);
return -1;
}
if (avcodec_parameters_to_context(*pCodecContext,codecParameters)){//将解码器中信息复制到上下文当中
if (LOG_DEBUG){
LOGE("avcodec_parameters_to_context ERROR");
callJava->onCallError(CHILD_THREAD,405,"avcodec_parameters_to_context ERROR");
}
exit = true;
pthread_mutex_unlock(&init_mutex);
return -1;
}
if (avcodec_open2(*pCodecContext,dec,NULL) < 0){
if (LOG_DEBUG){
LOGE("avcodec_open2 ERROR");
callJava->onCallError(CHILD_THREAD,406,"avcodec_open2 ERROR");
}
exit = true;
pthread_mutex_unlock(&init_mutex);
return -1;
}
return 0;
}
将待解码的Frame读取出来,存放到队列中:
pthread_mutex_lock(&seek_mutex);
int ret = av_read_frame(pFmtCtx,avPacket);
pthread_mutex_unlock(&seek_mutex);
if (ret == 0) {
if (avPacket->stream_index == audio->streamIndex){
count++;
/*if (LOG_DEBUG) {
LOGD("解码第%d帧",count);
}*/
audio->queue->putAVPacket(avPacket);
} else if (avPacket->stream_index == video->streamIndex){
video->queue->putAVPacket(avPacket);
LOGD("获取到视频流AVPacket")
}else {
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
}
} else {
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
//队列中的avPacket还没有解码完
while (playStatus != NULL && !playStatus->exit){
if (audio->queue->getQueueSize() > 0){//把缓存中的avPacket也要释放出来
av_usleep(1000 * 100);
continue;
} else {
playStatus->exit = true;
break;
}
}
}
新建一个线程,从队列中取出视频的AVPacket,
void *playVideo(void *data){
JfVideo *video = (JfVideo *)data;
while (video->playStatus != NULL && !video->playStatus->exit){
AVPacket *avPacket = av_packet_alloc();
if (video->queue->getAVPacket(avPacket) == 0){
//解码渲染
LOGD("线程中获取视频AVPacket");
}
av_packet_free(&avPacket);//AVPacket中的第一个参数,就是引用,减到0才真正释放
av_free(avPacket);
avPacket = NULL;
}
pthread_exit(&video->thread_play);
}
void JfVideo::play() {
pthread_create(&thread_play,NULL,playVideo,this);
}
网友评论