FFmpeg视频解码器

FFmpeg视频解码器_ffmpeg解码器-CSDN博客

1.FFmpeg库简介

2.FFmpeg解码的函数

2.1 FFmpeg解码的流程图

av_read_frame():循环读取一帧一帧的数据,实际上是h264码流

2.2 FFmpeg解码函数简介

3. FFmpeg解码的数据结构

显示时间戳:该桢该几分几秒显示,基于AVStream的成员变量time_base,该流的时间基
AVPacket:装h.264数据(解码前的数据,编码数据)

AVFrame:装YUV数据(解码后的数据,解码数据)

4.补充小知识:解码后的数据为什么要经过sws_scale()函数处理?

5.课后练习

/**
 * 最简单的基于FFmpeg的解码器
 * Simplest FFmpeg Decoder
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程序实现了视频文件的解码(支持HEVC,H.264,MPEG2等)。
 * 是最简单的FFmpeg视频解码方面的教程。
 * 通过学习本例子可以了解FFmpeg的解码流程。
 * This software is a simplest video decoder based on FFmpeg.
 * Suitable for beginner of FFmpeg.
 *
 */

#include <stdio.h>

#define __STDC_CONSTANT_MACROS

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
};

int main(int argc, char* argv[])
{
    AVFormatContext* pFormatCtx;  // 存储的所有关于视频的信息
    int				i, videoindex; // videoindex:视频流所在的序号值
    AVCodecContext* pCodecCtx;
    AVCodec* pCodec;
    AVFrame* pFrame, * pFrameYUV;
    uint8_t* out_buffer;
    AVPacket* packet;
    int y_size;
    int ret, got_picture;
    struct SwsContext* img_convert_ctx;


    // 要解码的音视频文件路径
    char filepath[] = "./test.avi";

    // 视频桢的数量
    int frame_cnt;

    // 1.初始化,注册所有组件
    //av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();
     
    // 2.打开视频文件
    if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {
        printf("Couldn't open input stream.\n");
        return -1;
    }

    // 3.获取视频码流信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Couldn't find stream information.\n");
        return -1;
    }

    // 获取流信息,进行解码
    videoindex = -1;
    
    // 寻找视频流所在的序号值
    // nb_streams:记录有几个stream,一般stream[0]是视频,stream[1]是音频
    for (i = 0; i < pFormatCtx->nb_streams; i++)
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            // 枚举值AVMEDIA_TYPE_VIDEO 为0,表示视频

            videoindex = i;
            break;
        }

    if (videoindex == -1) {
        printf("Didn't find a video stream.\n");
        return -1;
    }

    // AVCodecParameters:用于保存音视频流的基本参数信息
    AVCodecParameters* codecParameters = pFormatCtx->streams[videoindex]->codecpar;
    pCodecCtx = avcodec_alloc_context3(nullptr);
    avcodec_parameters_to_context(pCodecCtx, codecParameters);

    // 4.查找解码器,eg:H.264编码器
    // pCodecCtx=pFormatCtx->streams[videoindex]->codec;
    pCodec = (AVCodec*)avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return -1;
    }

    // 5.打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return -1;
    }

    // 打开文件
    FILE * fp, * fp_264, * fp_yuv;
    fopen_s(&fp, "info.txt", "wb+");
    fopen_s(&fp_264, "output264.h264", "wb+");
    fopen_s(&fp_yuv, "outputyuv.yuv", "wb+");

    /*
     * 此处终端输出视频信息的代码
     * 取自于pFormatCtx,使用fprintf()
     */

    // 写入到文件info.txt中
    fprintf(fp, "时长: %d μs\n", pFormatCtx->duration); // 微秒,转换成秒除以1e6
    fprintf(fp, "封装格式:%s\n", pFormatCtx->iformat->long_name);
    fprintf(fp, "分辨率: %d*%d\n", pCodecCtx->width, pCodecCtx->height);


    
    packet = av_packet_alloc();
    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    out_buffer = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);


    // 输出视频文件信息
    printf("--------------- File Information ----------------\n");
    
    /*
    * av_dump_format()函数:
      Print detailed information about the input or output format, such as
    * duration, bitrate, streams, container, programs, metadata, side data,
    * codec and time base.    
    */
    av_dump_format(pFormatCtx, 0, filepath, 0);
    printf("-------------------------------------------------\n");
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

    // 视频桢数量
    frame_cnt = 0;

    // 输出H264码流
    // 6. av_read_frame():从输入文件读取一帧压缩数据。
    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == videoindex) {
            // videoindex:判断是视频


            /*
             * 此处输出H264码流的代码
             * 取自于packet,使用fwrite(),把内存中的数据写入文件output264.h264
            */
            fwrite(packet->data, 1, packet->size, fp_264);

            if (avcodec_send_packet(pCodecCtx, packet) < 0) {
                printf("avcodec_send_packet failed!.\n");
                continue;
            }

            while (1)
            {
                ret = avcodec_receive_frame(pCodecCtx, pFrame);
                if (ret != 0)break;

                // sws_scale函数处理生成的图,生成数据存储在pFrameYUV中
                sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
                
                // 输出处理的桢的序号
                printf("Decoded frame index: %d\n", frame_cnt);


                /*
                 * 此处添加输出YUV的代码
                 * 取自于pFrameYUV,使用fwrite()
                 * 
                 * 
                 * data[0]:Y数据,屏幕所有亮的点,黑白数据
                 * data[1]:U数据,数据为Y的四分之一
                 * “U”和“V”表示的则是色度,
                   作用是描述影像色彩及饱和度,用于指定像素的颜色。
                 * data[2]:V数据,数据为Y的四分之一
                 */
                fwrite(pFrameYUV->data[0], 1, pCodecCtx->width * pCodecCtx->height, fp_yuv);
                fwrite(pFrameYUV->data[1], 1, pCodecCtx->width * pCodecCtx->height / 4, fp_yuv);
                fwrite(pFrameYUV->data[2], 1, pCodecCtx->width * pCodecCtx->height / 4, fp_yuv);
                frame_cnt++;
            }
        }
        av_packet_unref(packet);
    }

    sws_freeContext(img_convert_ctx);

    fclose(fp);
    fclose(fp_264);
    fclose(fp_yuv);
    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值