FFmpeg SDK开发课程笔记(一):调用FFmpeg SDK对YUV视频序列进行编码

课程地址 http://edu.csdn.net/course/detail/2515/39428?auto_start=1

需要包含的头文件

#include <iostream>
#include <stdio.h>
#include "Error.h"
extern "C"
{
    //#include<stdio.h>
    #include <stdint.h>
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
    #include<libavutil/imgutils.h>
    #include <libavutil/opt.h>
}

Error.h
就是定义几个出错类型就可以了

/*
 * Error.h
 *
 *  Created on: Aug 14, 2017
 *      Author: media
 */

#ifndef ERROR_H_
#define ERROR_H_

#define IO_FILE_ERROR_READ_FAILED -1
#define FF_ERROR_INIT_FAILED -10
#define FF_ERROR_ENCODE_FAILED -11


#endif /* ERROR_H_ */

定义相关的变量

注意以下均为全局变量

文件路径

const char *inputFileName = NULL;
const char *outputFileName = NULL;
FILE *pFin = NULL, *pFout = NULL;

视频信息

int frameWidth = 0 , frameHeight = 0;
int bitRate = 0, frameToEncode = 0;

ffmpeg中相关的数据结构

AVCodec *codec = NULL;
AVCodecContext *codecCtx = NULL;
AVFrame *frame = NULL;
AVPacket pkt;

处理命令行参数

argv分别为

  1. 输入文件路径
  2. 输出文件路径
  3. 视频宽度
  4. 视频高度
  5. 码率
  6. 需要编码的帧数
static int parse_input_paramaters(int argc, char **argv)
{
    inputFileName=argv[1];
    outputFileName=argv[2];

    pFin=fopen(inputFileName, "rb+");
    if(!pFin)
    {
        std::cout<<"Error in reading inputfile"<<std::endl;
        return IO_FILE_ERROR_READ_FAILED;
    }

    pFout=fopen(outputFileName, "wb+");
    if(!pFout)
    {
        std::cout<<"Error in reading outputfile"<<std::endl;
        return IO_FILE_ERROR_READ_FAILED;
    }

    frameWidth = atoi(argv[3]);
    frameHeight = atoi(argv[4]);
    bitRate = atoi(argv[5]);
    frameToEncode = atoi(argv[6]);
    return 1;
}

读取YUV数据

static int read_yuv_data(int color)
{
    /* color = 0 : Y
     * color = 1 : U
     * color = 2 : V
     */

    int color_height = color == 0 ? frameHeight : frameHeight/2;
    int color_width = color == 0 ? frameWidth : frameWidth/2;
    //这里是YUV420P的格式
    int color_size=color_height*color_width;
    int color_stride=frame->linesize[color];


    if(color_width == color_stride)
    //这种情况相当于frame->data放满了
    {
        //fread_s(frame->data[color], color_size, 1, color_size, pFin);
        //LINUX下没有fread_s
        fread(frame->data[color], color_size, 1, pFin);
        /*
        fread

函数原型:size_t fread(void* buff,size_t size,size_t count,FILE* stream)

作用:从文件中读入数据到指定的地址中,ffpempeg里面的AVFrame->data[0,1,2]用于存储YUV数据,所以读到这里

参数:第一个参数为接收数据的指针(buff),也即数据存储的地址

第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节

第三个参数为元素个数,即要读取的数据大小为size的元素个素

第四个参数为提供数据的文件指针,该指针指向文件内部数据
        */
    }
    else
    //这种情况还需要对齐
    {
        for(int row_idx=0; row_idx<color_height;row_idx++)
        {
            //fread_s(frame->data[color]+row_idx*color_stride,color_width,1,color_width,pFin);
            fread(frame->data[color]+row_idx*color_stride,color_width,1,pFin);
        }
    }
    return pkt.size;
}

main 函数

int main(int argv,char **argc)
{
    int got_packet = 0;
    if(parse_input_paramaters(argv,argc)>0)
    {
        std::cout<<"Input file:"<<inputFileName<<std::endl;
        std::cout<<"Output file:"<<outputFileName<<std::endl;
        std::cout<<"frameWidth:"<<frameWidth<<std::endl;
        std::cout<<"frameHeight:"<<frameHeight<<std::endl;
        std::cout<<"bitRate:"<<bitRate<<std::endl;
        std::cout<<"frameToEncode:"<<frameToEncode<<std::endl;
    }
    else
    {
        std::cout<<"Error in cmd line!"<<std::endl;
        return -1;
    }
//处理argv完毕
    {//这个大括号似乎是多余的
        avcodec_register_all();
        //寻找编码器
        codec = avcodec_find_encoder(AV_CODEC_ID_H264);
        if(!codec)
        {
            return FF_ERROR_INIT_FAILED;
        }
        //创建AVCodecContext 结构体,这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,参考http://blog.csdn.net/yuan892173701/article/details/8702333
        codecCtx = avcodec_alloc_context3(codec);
        if(!codecCtx)
        {
            return FF_ERROR_INIT_FAILED;
        }
        codecCtx->width = frameWidth;
        codecCtx->height = frameHeight;
        codecCtx->bit_rate = bitRate;
        AVRational r = {1, 25};
        //时间刻度是1/25
        codecCtx->time_base = r;
        codecCtx->gop_size = 12;
        //the number of pictures in a group of pictures, or 0 for intra_only
        codecCtx->max_b_frames = 1;
        codecCtx->pix_fmt=AV_PIX_FMT_YUV420P;
        av_opt_set(codecCtx->priv_data,"preset","slow",0);
        //初始化一个视音频编解码器的AVCodecContext
        if(avcodec_open2(codecCtx, codec, NULL)<0)
        {
            return FF_ERROR_INIT_FAILED;
        }
        //分配AVFrame
        frame = av_frame_alloc();
        if(!frame)
        {
            return FF_ERROR_INIT_FAILED;
        }
        frame->width = codecCtx->width;
        frame->height = codecCtx->height;
        frame->format = codecCtx->pix_fmt;
        if(av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, (AVPixelFormat)frame->format, 32)<0)
        {
            return FF_ERROR_INIT_FAILED;
        }
    }
    for(int frameIdx = 0; frameIdx <frameToEncode; frameIdx++)
    {
        //初始化AVPacket
        av_init_packet(&pkt);
        pkt.data=NULL;
        pkt.size=0;

        //把YUV数据写入frame->data里面
        read_yuv_data(0);
        read_yuv_data(1);
        read_yuv_data(2);

        frame->pts = frameIdx;

        if(avcodec_encode_video2(codecCtx, &pkt, frame, &got_packet)<0)
        //avcodec_encodec_video2: AVFrame to AVPacket
        {
            std::cout<<"Error in encoding"<<std::endl;
            return FF_ERROR_ENCODE_FAILED;
        }
        if(got_packet)
        {
            std::cout<<"Write packet of frame "<<frameIdx<<",size "<<pkt.size<<std::endl;
            fwrite(pkt.data, 1, pkt.size, pFout);
            av_packet_unref(&pkt);
        }
        for(got_packet = 1;got_packet;)
        {
            if(avcodec_encode_video2(codecCtx, &pkt, NULL, &got_packet)<0)
            {
                std::cout<<"Error in encoding"<<std::endl;
                return FF_ERROR_ENCODE_FAILED;
            }
            if(got_packet)
            //有时候帧数据写完了AVPacket里面还有东西
            {
                if(avcodec_encode_video2(codecCtx, &pkt, frame, &got_packet)<0)
                {
                    std::cout<<"Write packet of size "<<pkt.size<<std::endl;
                    fwrite(pkt.data, 1, pkt.size, pFout);
                    av_packet_unref(&pkt);
                }
            }
        }
    }
    //
    fclose(pFin);
    fclose(pFout);
    //释放内存
    avcodec_close(codecCtx);
    av_free(codecCtx);
    av_freep(frame->data[0]);
    av_frame_free(&frame);

    std::cout<<"success!"<<std::endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值