FFMPEG获取摄像头数据,并编码为H264格式

 

#define __STDC_CONSTANT_MACROS
#include <string.h>

extern "C"
{
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
}


#define WIDTH  640
#define HEIGHT 480


static
AVFormatContext* open_dev(){

    int ret = 0;
    char errors[1024] = { 0, };

    //ctx
    AVFormatContext *fmt_ctx = NULL;
    AVDictionary *options = NULL;
    //[[video device]:[audio device]]
    const char *device_name = "/dev/video0";
    av_dict_set(&options, "video_size", "640x480",0);

    //get format
    AVInputFormat *iformat = av_find_input_format("video4linux2");

    //open device
    if ((ret = avformat_open_input(&fmt_ctx, device_name, iformat, &options)) < 0){
        av_strerror(ret, errors, 1024);
        fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);
        return NULL;
    }

    return fmt_ctx;
}

void open_encodec(int width, int height, AVCodecContext ** enc_ctx)
{
    AVCodec * codec = avcodec_find_encoder_by_name("libx264");
    if(codec == NULL)
    {
        av_log(NULL, AV_LOG_INFO, "Codec libx264 not found!\n");
        return;
    }


    *enc_ctx = avcodec_alloc_context3(codec);
    if(enc_ctx == NULL)
    {
        av_log(NULL, AV_LOG_INFO, "alloc context3 failed!\n");
        return;
    }
    //sps pps
    (*enc_ctx)->profile = FF_PROFILE_H264_HIGH_444;
    (*enc_ctx)->level = 50;//level 5.0 version

    //resolution
    (*enc_ctx)->width = width;
    (*enc_ctx)->height = height;
    //GOP
    (*enc_ctx)->gop_size = 250;
    (*enc_ctx)->keyint_min = 25;

    (*enc_ctx)->max_b_frames = 3;
    (*enc_ctx)->has_b_frames = 1;

    (*enc_ctx)->refs = 3;
    (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;

    (*enc_ctx)->bit_rate = 60000000;// 6000kbps

    (*enc_ctx)->time_base = (AVRational){1,30};
    (*enc_ctx)->framerate = (AVRational){30,1};


    int ret = avcodec_open2((*enc_ctx), codec, NULL);
    if(ret < 0)
    {
        av_log(NULL, AV_LOG_INFO, "avcodec_open2 failed!\n");
        return;
    }
}

AVFrame* create_frame(int width, int height)
{
    AVFrame *frame = av_frame_alloc();
    if(frame == NULL)
    {
        av_log(NULL, AV_LOG_INFO, "av_frame_alloc failed!\n");
        return NULL;
    }
    frame->width = width;
    frame->height = height;
    frame->format = AV_PIX_FMT_YUV420P;

    int ret = av_frame_get_buffer(frame, 32);
    if(ret < 0)
    {
        av_log(NULL, AV_LOG_INFO, "av_frame_get_buffer failed!\n");
        return NULL;
    }
    return frame;
}


static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *newpkt, FILE * outfile)
{
#if 0
    int ret = avcodec_send_frame(ctx, frame);
    if(ret < 0)
    {
        av_log(NULL, AV_LOG_INFO, "avcodec_send_frame failed!\n");
        exit(-1);
    }
    while(ret >= 0)
    {
        ret = avcodec_receive_packet(ctx,newpkt);
        if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            //av_log(NULL, AV_LOG_INFO, "avcodec_receive_packet no more data!\n");
            return;
        }
        else if(ret < 0)
        {
            av_log(NULL, AV_LOG_INFO, "avcodec_receive_packet failed!\n");
            exit(-1);
        }
        fwrite(newpkt->data, 1, newpkt->size, outfile);
        av_packet_unref(newpkt);
    }

#endif
}

void rec_video() {

    //context
    AVFormatContext *fmt_ctx = NULL;
    //set log level
    av_log_set_level(AV_LOG_DEBUG);

    //register audio device
    avdevice_register_all();


    //create file
    const char *out = "/home/lili/Videos/video.yuv";
    FILE *outfile = fopen(out, "wb+");
    if (!outfile)
    {
        printf("Error, Failed to open file!\n");
        return;
    }

    AVCodecContext *codec_ctx = NULL;
    open_encodec(WIDTH, HEIGHT, &codec_ctx);

    //编码的输入数据
    AVFrame*  avframe = create_frame(WIDTH, HEIGHT);
    //创建编码后输出的数据
    AVPacket* newpkt = av_packet_alloc();
    if (!newpkt){
        printf("Error, av_packet_alloc!\n");
        return;
    }
    //打开设备
    fmt_ctx = open_dev();
    if (!fmt_ctx){
        printf("Error, Failed to open device!\n");
        return;
    }
    AVPacket pkt;
    int ret = -1, count = 1;


    const char *encode_file_path = "/home/lili/Videos/video.h264";
    FILE *encode_file = fopen(encode_file_path, "wb+");
    if (!encode_file)
    {
        printf("Error, Failed to open file!\n");
        return;
    }

    int base = 0;int got_output = 0;
    while((ret = av_read_frame(fmt_ctx, &pkt)) == 0 && count++ < 200)
    {
        av_log(NULL, AV_LOG_INFO, "packet size is %d\n", pkt.size);
#define FORMAT_YUYV


#ifdef FORMAT_YUV
        //ffplay -s 640x480 -pixel_format yuyv422 ~/Videos/video.yuv
       fwrite(pkt.data, 1, pkt.size, outfile);//yuyv422 format
       fflush(outfile);
#else
    /*
           Y U Y V Y U Y V
           Y U Y V Y U Y V
           Y U Y V Y U Y V
           Y U Y V Y U Y V
              - yuyv422 -
       Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
       Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
       Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
       Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
       U U U U U U      V V V V V V      U V U V U V      V U V U V U
       V V V V V V      U U U U U U      U V U V U V      V U V U V U
        - I420 -          - YV12 -         - NV12 -         - NV21 -
    */
        //ffplay -video_size 640x480 -pixel_format yuv420p ~/Videos/video.yuv

        int len = WIDTH * HEIGHT;
        for(int i = 0;i < len; i++){
            avframe->data[0][i] = pkt.data[i*2];//Y
        }

        //yuyv 序列为YU YV YU YV,一个yuv422帧的长度 width * height * 2 个字节
        //yuyv --- >yuv420p丢弃偶数行 u v
        int cnt = 0;
        for(int i = 0; i < HEIGHT; i += 2)
        {
            for(int j = 0; j < WIDTH/2; j++)
            {
                avframe->data[1][cnt] = pkt.data[i * WIDTH * 2 + j * 4 + 1];//Cb
                avframe->data[2][cnt++] = pkt.data[i * WIDTH * 2 + j * 4 + 3];//Cr
            }
        }
        fwrite(avframe->data[0], 1, WIDTH * HEIGHT, outfile);
        fwrite(avframe->data[1], 1, WIDTH * HEIGHT/4, outfile);
        fwrite(avframe->data[2], 1, WIDTH * HEIGHT/4, outfile);
        fflush(outfile);
        //important!!!
        avframe->pts = base++;

        //encode(codec_ctx, avframe, newpkt, encode_file);


        ret = avcodec_encode_video2(codec_ctx, newpkt, avframe, &got_output);
        if (ret < 0)
        {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output)
        {
            printf("Write frame (size=%5d)\n", newpkt->size);
            fwrite(newpkt->data, 1, newpkt->size, encode_file);
            fflush(encode_file);
            av_packet_unref(newpkt);
        }

#endif

    }

    /* get the delayed frames */
    for (got_output = 1; got_output;)
    {
        fflush(stdout);
        ret = avcodec_encode_video2(codec_ctx, newpkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output)
        {
            printf("Write frame (size=%5d)\n", newpkt->size);
            fwrite(newpkt->data, 1, newpkt->size, encode_file);
            av_packet_unref(newpkt);
        }
    }


    fclose(outfile);
    fclose(encode_file);
    av_log(NULL, AV_LOG_DEBUG, "finish!\n");

    return;
}


int main()
{
    rec_video();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值