ffmpeg系列-打开文件获取视频帧

简要说明一下视频播放器的原理。

视频播放器播放一个互联网上的视频文件,需要经过以下几个步骤:解协议,解封装,解码视音频,视音频同步。如果播放本地文件则不需要解协议,为以下几个步骤:解封装,解码视音频,视音频同步。他们的过程如图所示。

ffmpeg系列-打开文件获取视频帧_第1张图片

想要了解更多可以参考 雷神 [总结]视音频编解码技术零基础学习方法

不多赘述,直接上代码

//
//  main.cpp
//  OpenFile
//
//  Created by ination on 17/3/22.
//  Copyright © 2017年 ination. All rights reserved.
//

#include 
#ifdef __cplusplus
extern "C"
{
#endif
#include 
#include 
#include 
#include 
#ifdef __cplusplus
};
#endif



int main(int argc, const char * argv[]) {
    // insert code here...
    
    avcodec_register_all();
    av_register_all();
    avformat_network_init();
    
    //输出支持解封装格式
    printf("======  av_input_format  =====\n");
    AVInputFormat *fmt = NULL;
    while ((fmt = av_iformat_next(fmt))) {
        printf("name : %s\n",fmt->name);
        printf("long_name : %s\n",fmt->long_name);
        printf("\n");
    }
    printf("==============================\n");
    
    AVFormatContext *pAVFormatCtx = NULL;
    pAVFormatCtx = avformat_alloc_context();
    char filepath[] = "/Users/apple/Movies/tlbb03.flv";
    
    //打开文件
    char errorBuf[1024];
    int retOpenFile = avformat_open_input(&pAVFormatCtx, filepath, NULL, NULL);
    if (0 != retOpenFile){
        av_strerror(retOpenFile, errorBuf, sizeof(errorBuf));
        printf("Couldn't open file %s: %d(%s)\n", filepath, retOpenFile, errorBuf);
        return -1;
    }
    
    //输出文件信息
    printf("------------- File Information ------------------\n");
    av_dump_format(pAVFormatCtx,0,filepath,0);
    printf("-------------------------------------------------\n");
    
    //音视频分离
    int retFindStream = avformat_find_stream_info(pAVFormatCtx, NULL);
    if (0 != retFindStream){
        av_strerror(retFindStream, errorBuf, sizeof(errorBuf));
        printf("Couldn't find stream %s: %d(%s)\n", filepath, retFindStream, errorBuf);
        return -1;
    }
    
    int videoStreamIndex = -1;
    for (int i = 0; i < pAVFormatCtx->nb_streams;i++){
        AVStream *stream = pAVFormatCtx->streams[i];
        AVCodecParameters *codeParam = stream->codecpar;
        if (AVMEDIA_TYPE_VIDEO == codeParam->codec_type){
            videoStreamIndex = i;
            break;
        }
    }
    if (-1 == videoStreamIndex){
        printf("Didn't find a video stream.\n");
        return -1;
    }
    
    //视频流信息
    AVStream *videoStream = pAVFormatCtx->streams[videoStreamIndex];
    AVCodecParameters *codeParam = videoStream->codecpar;
    AVCodecContext *pAVCodeCtx = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(pAVCodeCtx, codeParam);
    if (0 == pAVCodeCtx){
        printf("Couldn't create AVCodecContext\n");
        return -1;
    }
    
    //查找视频解码器
    AVCodecID videoCodeId = codeParam->codec_id;
    AVCodec *videoDeCode = avcodec_find_decoder(videoCodeId);
    if(videoDeCode == NULL){
        printf("Codec not found.\n");
        return -1;
    }
    
    //打开视频解码器
    int retOpenVideoDecode = avcodec_open2(pAVCodeCtx, videoDeCode, NULL);
    if (retOpenVideoDecode != 0){
        av_strerror(retOpenVideoDecode, errorBuf, sizeof(errorBuf));
        printf("open decode Error. %s\n",errorBuf);
        return -1;
    }
    
    AVPacket *avPacket = av_packet_alloc();
    AVFrame *avVideoFrame = av_frame_alloc();
    
    bool bFirstFrame = false;
    while (1) {
        //从原始数据读取一帧
        av_read_frame(pAVFormatCtx, avPacket);
        if (avPacket->stream_index == videoStreamIndex){
            //送往解码器
            int retPackt = avcodec_send_packet(pAVCodeCtx, avPacket);
            if (retPackt < 0){
                av_strerror(retPackt, errorBuf, sizeof(errorBuf));
                printf("packet Error. %s\n",errorBuf);
                continue;
            }
            //从解码器获取一帧
            int retDcode = avcodec_receive_frame(pAVCodeCtx, avVideoFrame);
            if(retDcode < 0){
                av_strerror(retDcode, errorBuf, sizeof(errorBuf));
                printf("Decode Error. %s\n",errorBuf);
                continue;
            }else{
                bFirstFrame = true;
                break;
            }

        }
    }
    
    if (bFirstFrame){
        //todo 图像处理
    }
    
    //资源释放
    av_frame_free(&avVideoFrame);
    av_packet_free(&avPacket);
    avcodec_close(pAVCodeCtx);
    avformat_close_input(&pAVFormatCtx);
    avformat_network_deinit();
    
    return 0;

}

代码的目的只是获取一帧图像作为缩率图的demo,存在资源为释放的问题。


你可能感兴趣的:(ffmpeg)