美文网首页
添加视频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到队列中

    创建C++类-JfVideo,保存Video相关参数:JfVideo.h JfVideo.cpp 找到文件中的视频...

  • 第十八节、关于硬解与软解

    硬件解码视频(MediaCodec)、软件解码视频(FFMpeg) 硬件解码视频: 我们知道AVPacket中存放...

  • 第十五节 播放ape音乐的bug

    我们添加一个变量做标记,记录当前AVPacket中是否还有AVFramebool readPacketFinish...

  • 二项队列添加元素

    二项队列添加元素 from my csdn blog二项队列,添加一个元素到队列中,不可用使用二项树合并函数。

  • 十八:AVPacket,AVFrame

    AVPacket AVPacket定义在avcodec.h中 FFMPEG使用AVPacket来暂存解复用之后、解...

  • AVPacket 与 AVFrame (十一)

    AVPacket AVPacket定义在avcodec.h中 FFMPEG使用AVPacket来暂存解复用之后、解...

  • ffmpeg视频播放器相关

    视频播放思路 和播放音频一样,采用生产者消费者模型。AvPacket入队,然后AvPacket出队伍解码。 视频解...

  • 多线程之GCD

    GCD的队列分为串行队列和并行队列两类: 串行队列:只有一个线程,加入到队列中的操作按添加顺序依次执行。并发队列:...

  • iOS 多线程之GCD

    GCD核心概念 任务 :执行的操作 队列 :执行任务的等待队列 任务 同步执行 :同步添加任务到指定的队列中,如果...

  • iOS 多线程:GCD

    同步执行(sync): 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之...

网友评论

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

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