添加视频AVPacket到队列中

2019-01-10  本文已影响0人  张俊峰0613
视频解码流程

创建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);
}
上一篇下一篇

猜你喜欢

热点阅读