注意:编译的时候可能会报错,报错之后修改如下:
(1)将CODEC_TYPE_VIDEO改正AVMEDIA_TYPE_VIDEO
(2)在PKT_FLAG_KEY前面加个AV_
test_convert-v4l2.c文件
#include <stdlib.h>
#include <stdio.h>#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
int main(int argc, char **argv)
{
//char *filename = "./test.avi";
char *filename = "/dev/video0";
char *outfilename = "./save.avi";
AVFormatContext *pFormatCtxDec = 0, *pFormatCtxEnc;
AVCodecContext *pCodecCtxDec, *pCodecCtxEnc;
AVCodec *pCodecDec, *pCodecEnc;
AVFrame *pFrameDec, *pFrameEnc;
AVOutputFormat *pOutputFormat;
AVStream *video_st;
AVFormatParameters inputFmtParameter;
int i, videoStream;
int outbuf_size;
uint8_t *outbuf;
//AVPacket packet *pktt = &packet;
//AVPacket packet;
int frameFinished, frames = 0;
int out_size;
avcodec_init();
av_register_all();//注册库中所有可用的文件格式和编码器
avdevice_register_all();//v4l2是属于avdevice的范畴
/*
//打开输入文件
if(av_open_input_file(&pFormatCtxDec, filename, NULL, 0, NULL) != 0)
{
printf("can't open the file %s\n", filename);
exit(1);
}
*/
//获取这个输入设备的处理器,以便进行处理
AVInputFormat *inputFmt = av_find_input_format("video4linux2");
if(inputFmt == NULL)
{
printf("can't find the v4l2\n");
exit(1);
}
pFormatCtxDec = avformat_alloc_context();
if(!pFormatCtxDec)
{
printf("avformat_alloc_contest error\n");
exit(1);
}
memset(&inputFmtParameter, 0, sizeof(inputFmtParameter));
inputFmtParameter.height = 240;
inputFmtParameter.width = 320;
inputFmtParameter.channel = 0;
//inputFmtParameter.standard = "ntsc";
inputFmtParameter.pix_fmt = PIX_FMT_YUYV422;
inputFmtParameter.time_base.num = 1;
inputFmtParameter.time_base.den = 25;
if(av_open_input_file(&pFormatCtxDec, filename, inputFmt,
sizeof(inputFmtParameter), &inputFmtParameter) < 0)
{
printf("can't open video0\n");
exit(1);
}
//取出流信息
if(av_find_stream_info(pFormatCtxDec) < 0)
{
printf("can't find suitable codec parameters\n");
exit(1);
}
//列出输入文件的相关流信息
dump_format(pFormatCtxDec, 0, filename, 0);
//寻找第一个视频流
videoStream = -1;
for(i = 0; i < pFormatCtxDec->nb_streams; i++)
{
// if(pFormatCtxDec->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) //版本差异
if(pFormatCtxDec->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
if(videoStream == -1) //没找到视频流
{
printf("can't find video stream\n");
exit(1);
}
//取得视频流编码上下文指针
pCodecCtxDec = pFormatCtxDec->streams[videoStream]->codec;
pCodecDec = avcodec_find_decoder(pCodecCtxDec->codec_id);
//没找到合适的视频解码器
if(pCodecDec == NULL)
{
printf("can't find suitable video decoder\n");
exit(1);
}
if(pCodecDec->capabilities&CODEC_CAP_TRUNCATED)
{
pCodecCtxDec->flags|= CODEC_FLAG_TRUNCATED;
printf("pCodecCtxDec->flags:CODEC_FLAG_TRUNCATED");
}
//打开视频解码器
if(avcodec_open(pCodecCtxDec, pCodecDec) < 0)
{
printf("can't open the video decoder\n");
exit(1);
}
//----------------------------------------------//
//下面为输出文件处理部分
//为解码帧分配内存
pFrameDec = avcodec_alloc_frame();
if(pFrameDec == NULL)
{
printf("can't alloc frame\n");
exit(1);
}
//根据文件名来获取输出文件格式,default is "mpeg"
pOutputFormat = av_guess_format(NULL, outfilename, NULL);
if(pOutputFormat == NULL)
{
printf("could not deduce ouput format from file extension\n");
pOutputFormat= av_guess_format("mpeg",NULL,NULL); //使用默认的格式
}
if(pOutputFormat == NULL)
{
printf("Could not find suitable output format\n");
exit(1);
}
pFormatCtxEnc = avformat_alloc_context();
if(pFormatCtxEnc == NULL)
{
printf("Memory error\n");
exit(0);
}
pFormatCtxEnc->oformat = pOutputFormat;
sprintf(pFormatCtxEnc->filename, "%s", outfilename);
//增加一个新的流到一个媒体文件
video_st = av_new_stream(pFormatCtxEnc, 0);
if(!video_st)
{
printf("could not alloc video stream\n");
exit(0);
}
//初始化编解码器
pCodecCtxEnc = video_st->codec;
pCodecCtxEnc->codec_id = pOutputFormat->video_codec;
pCodecCtxEnc->codec_type = AVMEDIA_TYPE_VIDEO;
/*比特率 每秒传送的比特数,比特率越高,传送的数据越大.
比特率表示经过编码(压縮)后的音.视频数据每秒钟需要用
多少个比特来表示,.比特率与音视频压缩的关系简单的说
就是比特率越高,音,视频质量就越好,但编码后的文件就越大.*/
pCodecCtxEnc->bit_rate = 400000; //400kbit/s
//设置分辨率,必须是2的倍数
pCodecCtxEnc->width = pCodecCtxDec->width;
pCodecCtxEnc->height = pCodecCtxDec->height;
//pCodecCtxEnc->time_base.den =
//pFormatCtxDec->streams[videoStream]->r_frame_rate.num;
//pCodecCtxEnc->time_base.num =
//pFormatCtxDec->streams[videoStream]->r_frame_rate.den;
pCodecCtxEnc->time_base.den = 25;
pCodecCtxEnc->time_base.num = 1;
//设置像素格式
pCodecCtxEnc->pix_fmt = PIX_FMT_YUV420P;
//设置GOP大小 表示每12针会插入一个I针
pCodecCtxEnc->gop_size = 12;
if(pCodecCtxEnc->codec_id == CODEC_ID_MPEG1VIDEO)
{
/*Needed to avoid using macroblocks in which some coeffs overflow.
This does not happen with normal video, it just happens here as the
motion of the chroma plane does not match the luma plane.*/
pCodecCtxEnc->mb_decision = 2;
}
//some formats want stream headers to be seperate
if( !strcmp(pFormatCtxEnc->oformat->name, "mp4") ||
!strcmp(pFormatCtxEnc->oformat->name, "mov") ||
!strcmp(pFormatCtxEnc->oformat->name, "3gp"))
{
//这样一个标志没注明,才不会导致报错没有使用global headers
pCodecCtxEnc->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
//设置必要的输出参数,即使没有参数也必须调用进行设置
if(av_set_parameters(pFormatCtxEnc, NULL) < 0)
{
printf("Invalid output format parameters\n");
exit(1);
}
//调试
printf("den = %d\n", pFormatCtxDec->streams[videoStream]->r_frame_rate.den);
printf("num = %d\n", pFormatCtxDec->streams[videoStream]->r_frame_rate.num);
// printf("Press any key to continue!");
//getchar();
//-------------------------------------------------------
pCodecEnc = avcodec_find_encoder(pCodecCtxEnc->codec_id);
if(!pCodecEnc)
{
printf("can't find suitable video encoder\n");
exit(0);
}//找到合适的视频编码器
if(avcodec_open(pCodecCtxEnc, pCodecEnc) < 0)
{
printf("can't open the output video codec\n");
exit(0);
}//打开视频编码器
if(!(pFormatCtxEnc->oformat->flags & AVFMT_RAWPICTURE))
{
outbuf_size = 5000000;
outbuf = av_malloc(outbuf_size);
printf("outbuf_size:5000000\n");
}
pFrameEnc = avcodec_alloc_frame();
if(pFrameEnc == NULL)
{
printf("can't alloc pFrameEnc\n");
exit(1);
}
//打开输出文件
if(!(pFormatCtxEnc->flags & AVFMT_NOFILE))
{
//*pb: I/O上下文,通过对该变量赋值可以改变输入源或输出目的
if(url_fopen(&pFormatCtxEnc->pb, outfilename, URL_WRONLY) < 0)
{
fprintf(stderr, "can't open the output file %s\n", outfilename);
exit(0);
}
}
//查看输出文件是否含有流信息
if(!pFormatCtxEnc->nb_streams) //nb_streams; 音视频流数量
{
printf("output file does not contain any stream\n");
exit(0);
}
//写视频流头
av_write_header(pFormatCtxEnc);
//------------YUV422P->YUV420P----------
struct SwsContext *img_convert_ctx;
if(pCodecCtxDec->pix_fmt == PIX_FMT_YUYV422)
{
//pCodecCtxDec->pix_fmt = PIX_FMT_YUV422P
//printf("v4l2 fmt: PIX_FMT_YUV422P\n");
//V4L2和FFMPEG中对YUV422P的定义不同,实际上应该为YUYV422
printf("v4l2 fmt:YVYV422\n");
printf("pCodecCtxDec->width:%d\n",pCodecCtxDec->width);
printf("pCodecCtxDec->height:%d\n",pCodecCtxDec->height);
img_convert_ctx = sws_getContext(pCodecCtxDec->width,
pCodecCtxDec->height,
//pCodecCtxDec->pix_fmt,
PIX_FMT_YUYV422,
pCodecCtxDec->width,
pCodecCtxDec->height,
PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL, NULL, NULL);
if(img_convert_ctx == NULL)
{
printf("Could not initialize the conversation context\n");
exit(1);
}
}
//从输入文件中读取一个包
int j = 0;
int bytesDecoded = 0;
AVPacket packet, *pkt_test = &packet; //初始化pkt_test
//AVPacket *pkt_test ;
// av_init_packet(pkt_test);
/* uint8_t *pic_tmp;
pic_tmp = malloc(320 * 240 * 2);
pkt_test->data = pic_tmp;
memset(pkt_test->data, 0, 153601);
*/
// pkt_test = (AVPacket *)av_malloc(sizeof(AVPacket));
//pkt_test->data = NULL;
//AVPacket *pkt_test;
//av_init_packet(pkt_test);
uint8_t *pic_tmp;
pic_tmp = malloc((320 * 240 * 3)/2);
char *p_temp1;
p_temp1 = pic_tmp;
pFrameEnc->data[0] = pic_tmp;
pFrameEnc->data[1] = pFrameEnc->data[0] + 320*240;
pFrameEnc->data[2] = pFrameEnc->data[1] + 320*240/4;
pFrameEnc->linesize[0] = 320;
pFrameEnc->linesize[1] = 160;
pFrameEnc->linesize[2] = 160;
uint8_t *pic_tmp2;
pic_tmp2 = malloc(320 * 240 * 2);
char *p_temp2;
p_temp2 = pic_tmp2;
pFrameDec->data[0] = pic_tmp2;
memset(pFrameDec->data[0], 0, 320*240*2);
pFrameDec->linesize[0] = 160;
//pkt_test->data = calloc(320*240*2, 1);
//while((av_read_frame(pFormatCtxDec, &packet) >= 0) && (j != 100))
//while((av_read_frame(pFormatCtxDec, pkt_test) >= 0) && (j != 100))
while((av_read_frame(pFormatCtxDec, pkt_test) >= 0) && (j != 1000))
{
j++;
//判断是否为当前视频流中的包
/*
FILE *fp;
fp = fopen("out.yuv","wb");
fwrite(packet.data, 1, packet.size, fp);
fclose(fp);
*/
/*
printf("packet.size=%d\n", packet.size);
printf("packet.stream_index=%d\n", packet.stream_index);
printf("packet.pts=%d\n", packet.pts);
printf("packet.dts=%d\n", packet.dts);*/
//if(packet.stream_index == videoStream)
if(pkt_test->stream_index == videoStream)
{
/*if(avcodec_decode_video(pCodecCtxDec, pFrameDec,
&frameFinished, packet.data, packet.size) < 0)
{
printf("Error while decoding\n");
exit(0);
}*/
//解码 一个视频帧
//pFrameDec->data[0] = malloc(320 * 240 * 2);
bytesDecoded = avcodec_decode_video2(pCodecCtxDec, pFrameDec,
//&frameFinished, &packet);
&frameFinished, pkt_test);
//printf("bytesDecoded=%d\n", bytesDecoded);
printf("loading %d\t", j);
}
//判断视频帧是否读完
if(frameFinished)
{
/*
pFrameEnc->data[0] = pFrameDec->data[0];
pFrameEnc->data[1] = pFrameDec->data[1];
pFrameEnc->data[2] = pFrameDec->data[2];
pFrameEnc->linesize[0] = pFrameDec->linesize[0];
pFrameEnc->linesize[1] = pFrameDec->linesize[1];
pFrameEnc->linesize[2] = pFrameDec->linesize[2];
printf("pFrameEnc->data:%d\n", pFrameEnc->data);
printf("pFrameEnc->linesize:%d\n", pFrameEnc->linesize);
*/
//调试补充数据YUV
/*
int x, y, i;
AVFrame *pict = NULL;
uint8_t *picture_buf;
pict = avcodec_alloc_frame();
int size;
size = avpicture_get_size(PIX_FMT_YUV420P, 320, 240);
picture_buf = av_malloc(size);
avpicture_fill((AVPicture *)pict, picture_buf,
PIX_FMT_YUV420P, 320, 240);
for(y = 0; y < pCodecCtxDec->height; y++)
{
for(x = 0; x <pCodecCtxDec->width; x++)
{
pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
}
}
for(y = 0; y < pCodecCtxDec->height / 2; y++)
{
for(x = 0; x < pCodecCtxDec->width / 2; x++)
{
pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
}
}
sws_scale(img_convert_ctx,
pict->data, pict->linesize,
0, pCodecCtxDec->height,
pFrameEnc->data, pFrameEnc->linesize);
*/
/*
printf("pFrameDec->data:%d\n", pFrameDec->data);
printf("pFrameDec->linesize:%d\n", pFrameDec->linesize);
*/
sws_scale(img_convert_ctx,
pFrameDec->data, pFrameDec->linesize,
0, pCodecCtxDec->height,
pFrameEnc->data, pFrameEnc->linesize);
frames++;
if(pFormatCtxEnc->oformat->flags & AVFMT_RAWPICTURE)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.flags |= AV_PKT_FLAG_KEY;/*前面没有AV_报错没有声明*/
pkt.stream_index = video_st->index;
pkt.data = (uint8_t *)pFrameEnc;
pkt.size = sizeof(AVPicture);
av_write_frame(pFormatCtxEnc, &pkt);
}
else
{
//需要将YUYV422转换成YUV420
//摄像头采集到的格式为YUV420
out_size = avcodec_encode_video(pCodecCtxEnc,
outbuf, outbuf_size, pFrameEnc);
if(out_size > 0)
{
// AVPacket是个很重要的结构,该结构在读媒体源文件和写输出文件时都需要用到
// int64_t pts; 显示时间戳
// int64_t dts; 解码时间戳
// uint8_t *data; 包数据
// int size; 包数据长度
// int stream_index; 包所属流序号
// int duration; 时长
// 以上信息,如果是在读媒体源文件那么avcodec会初始化,如果是输出文件,用户需要对以上信息赋值
AVPacket pkt;
av_init_packet(&pkt);
pkt.pts = pCodecCtxEnc->coded_frame->pts;
if(pCodecCtxEnc->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = video_st->index;
pkt.data = outbuf;
pkt.size = out_size;
av_write_frame(pFormatCtxEnc, &pkt);
}
}
}
av_free_packet(pkt_test);
}
//av_free_packet(&packet);
av_free_packet(pkt_test);
printf("out_size:%d\n", out_size);
for(; out_size; i++)
{
out_size = avcodec_encode_video(pCodecCtxEnc, outbuf,
outbuf_size, NULL);
if(out_size != 0)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.pts = pCodecCtxEnc->coded_frame->pts;
if(pCodecCtxEnc->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = video_st->index;
pkt.data = outbuf;
pkt.size = out_size;
av_write_frame(pFormatCtxEnc, &pkt);
}
}
av_write_trailer(pFormatCtxEnc);
for(i = 0; i < pFormatCtxEnc->nb_streams; i++)
{
av_freep(&pFormatCtxEnc->streams[i]->codec);
av_freep(&pFormatCtxEnc->streams[i]);
}
if(!(pOutputFormat->flags & AVFMT_NOFILE))
{
url_fclose(&pFormatCtxEnc->pb);
}
sws_freeContext(img_convert_ctx);
av_free(pFrameDec);
av_free(pFrameEnc);
av_free(outbuf);
av_free(pFormatCtxEnc);
avcodec_close(pCodecCtxDec);
avcodec_close(pCodecCtxEnc);
av_close_input_file(pFormatCtxDec);
return 0;
}
Makefile文件:
# Project: inputmethod
# Makefile created by Longhui20130331
CC = arm-linux-gcc
INCS += -I /opt/FriendlyARM/mini2440/ffmpeg-0.8.14/
LIBS += -L /opt/FriendlyARM/mini2440/ffmpeg-0.8.14/libavutil -lavutil
LIBS += -L /opt/FriendlyARM/mini2440/ffmpeg-0.8.14/libavformat -lavformat
LIBS += -L /opt/FriendlyARM/mini2440/ffmpeg-0.8.14/libavcodec -lavcodec
LIBS += -L /opt/FriendlyARM/mini2440/ffmpeg-0.8.14/libswscale -lswscale
LIBS += -L /opt/FriendlyARM/mini2440/ffmpeg-0.8.14/libavdevice -lavdevice -lm -lz
RM = rm -f
all = test_convert_v4l2
$(all):
$(CC) -g -o $(all) test_convert_v4l2.c $(LIBS) $(INCS)
.PHONY: clean
clean:
${RM} $(all)