美文网首页
添加视频AVPacket到队列中

添加视频AVPacket到队列中

作者: 张俊峰0613 | 来源:发表于2019-01-10 16:26 被阅读0次
    视频解码流程

    创建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);
    }
    

    相关文章

      网友评论

          本文标题:添加视频AVPacket到队列中

          本文链接:https://www.haomeiwen.com/subject/mwkarqtx.html