#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;
}