将 YUV 数据 编码为 h.264 格式
首先需要确保FFMPEG是否已经安装libx264
。
废话少说,直接贴上代码。
encode_yuv.c
#include <stdio.h>
#include <stdbool.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <time.h>
#include <pthread.h>
void usage(void)
{
printf("./encode_yuv input_file.yuv width height bitrate channel_num\n");
}
#define MAX_CHANNEL_NUM (4)
static struct timeval s_start_time[MAX_CHANNEL_NUM];
static struct timeval s_end_time[MAX_CHANNEL_NUM];
void Timer_SetStartTime(int channel)
{
gettimeofday(&s_start_time[channel]);
}
void Timer_SetEndTime(int channel)
{
gettimeofday(&s_end_time[channel]);
}
void Timer_GetIntervalTime(int channel, unsigned int *sec, unsigned int *usec)
{
unsigned int second;
unsigned int usecond;
second = s_end_time[channel].tv_sec - s_start_time[channel].tv_sec;
if(s_end_time[channel].tv_usec >= s_start_time[channel].tv_usec)
{
usecond = s_end_time[channel].tv_usec - s_start_time[channel].tv_usec;
}
else
{
usecond = 1000000 + s_end_time[channel].tv_usec - s_start_time[channel].tv_usec;
second = second - 1;
}
*sec = second;
*usec = usecond;
}
struct FileInfo
{
int channel;
int in_w;
int in_h;
int bitrate;
char input_file[128];
char output_file[128];
};
void * EncodeTask2(void *arg)
{
struct FileInfo stFileInfo;
int second;
int usecond;
int channel;
memcpy(&stFileInfo, arg, sizeof(stFileInfo));
char cmd[1024];
//ffmpeg -s 720x576 -pix_fmt yuv420p -i vi_chn_4_720_576_p420_39.yuv -r 25 -vcodec mpeg2video out.mpeg
snprintf(cmd, 1024, "ffmpeg -s %dx%d -pix_fmt yuv420p -i %s -vcodec mpeg2video -b:v %dk -r 25 %s", stFileInfo.in_w, stFileInfo.in_h, stFileInfo.input_file, stFileInfo.bitrate, stFileInfo.output_file);
channel = stFileInfo.channel;
Timer_SetStartTime(channel);
system(cmd);
Timer_SetEndTime(channel);
Timer_GetIntervalTime(channel, &second, &usecond);
printf("channel: %d, encode 100 frame use %d s %d us\n", channel, second, usecond);
return NULL;
}
void * EncodeTask(void *arg)
{
struct FileInfo stFileInfo;
memcpy(&stFileInfo, arg, sizeof(stFileInfo));
Encode(stFileInfo.channel, stFileInfo.input_file, stFileInfo.output_file, stFileInfo.in_w, stFileInfo.in_h);
return NULL;
}
int Encode(int channel, char *input_file, char *output_file, int in_w, int in_h)
{
int second;
int usecond;
AVCodec *pCodec;
AVCodecContext *pCodecCtx= NULL;
int i, ret, got_output;
FILE *fp_in;
FILE *fp_out;
AVFrame *pFrame;
AVPacket pkt;
int y_size;
//enum AVCodecID codec_id=AV_CODEC_ID_H264;
enum AVCodecID codec_id=AV_CODEC_ID_MPEG2VIDEO;
//Input raw data
fp_in = fopen(input_file, "rb");
if (!fp_in)
{
printf("Error: Could not open %s\n", input_file);
printf("in-error\n");
return -1;
}
//Output bitstream
fp_out = fopen(output_file, "wb");
if (!fp_out)
{
printf("Error: Could not open %s\n", output_file);
printf("out-error\n");
return -1;
}
avcodec_register_all();
av_register_all();
pCodec = avcodec_find_encoder(codec_id);
if (!pCodec)
{
printf("Error: Codec not found\n");
return -1;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx)
{
printf("Error: Could not allocate video codec context\n");
return -1;
}
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->width=in_w;
pCodecCtx->height=in_h;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
pCodecCtx->bit_rate = 400000;
pCodecCtx->gop_size = 12;
pCodecCtx->codec_id = AV_CODEC_ID_MPEG2VIDEO;
if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
{
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
pCodecCtx->qcompress = 0.6;
}
if (pCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
pCodecCtx->max_b_frames = 2;
if (pCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
pCodecCtx->mb_decision = 2;
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("Error: Could not open codec\n");
return -1;
}
pFrame = av_frame_alloc();
if (!pFrame)
{
printf("Error: Could not allocate video frame\n");
return -1;
}
pFrame->format = pCodecCtx->pix_fmt;
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width,
pCodecCtx->height, pCodecCtx->pix_fmt, 16);
if (ret < 0)
{
printf("Error: Could not allocate raw picture buffer\n");
return -1;
}
int count = 0;
y_size = pCodecCtx->width * pCodecCtx->height;
Timer_SetStartTime(channel);
//Encode
bool isHasFrame = true;
do
{
isHasFrame = (!((fread(pFrame->data[0], 1, y_size, fp_in) <= 0)
||(fread(pFrame->data[1], 1, y_size/4, fp_in) <= 0)
||(fread(pFrame->data[2], 1, y_size/4, fp_in) <= 0)));
if(!isHasFrame)
{
break;
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
i++;
pFrame->pts = i;
got_output = 0;
ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output);
if(ret < 0)
{
printf("Error: encode a frame failed!\n");
return -1;
}
if(got_output)
{
fwrite(pkt.data, 1, pkt.size, fp_out);
av_free_packet(&pkt);
}
}while(isHasFrame);
//printf("3--------------\n");
//Flush Encoder
for(got_output = 1; got_output; i++)
{
ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);
if (ret < 0)
{
printf("Error: encode a frame failed!\n");
return -1;
}
if (got_output)
{
fwrite(pkt.data, 1, pkt.size, fp_out);
av_free_packet(&pkt);
}
}
Timer_SetEndTime(channel);
Timer_GetIntervalTime(channel, &second, &usecond);
printf("channel: %d, encode 100 frame use %d s %d us\n", channel, second, usecond);
//printf("4--------------\n");
fclose(fp_out);
avcodec_close(pCodecCtx);
av_free(pCodecCtx);
av_freep(&pFrame->data[0]);
av_frame_free(&pFrame);
}
int main(int argc, char* argv[])
{
if(argc != 6)
{
usage();
return -1;
}
char *input_file = argv[1];
//char *output_file = argv[4];
int in_w = atoi(argv[2]);
int in_h = atoi(argv[3]);
int bitrate = atoi(argv[4]);
int channel_num = atoi(argv[5]);
//char output_file[128]
char cmd[1024];
pthread_t tid;
struct FileInfo arstFileInfo[MAX_CHANNEL_NUM];
int i = 0;
for(i = 0; i < channel_num; ++i)
{
arstFileInfo[i].channel = i;
arstFileInfo[i].in_w = in_w;
arstFileInfo[i].in_h = in_h;
arstFileInfo[i].bitrate = bitrate;
strcpy(arstFileInfo[i].input_file, input_file);
snprintf(arstFileInfo[i].output_file, 128, "/tmp/test%d.mpeg", i);
snprintf(cmd, 1024, "rm %s", arstFileInfo[i].output_file);
system(cmd);
}
for(i = 0; i < channel_num; ++i)
{
//pthread_create(&tid, NULL, EncodeTask2, (void *)(&arstFileInfo[i]));
pthread_create(&tid, NULL, EncodeTask, (void *)(&arstFileInfo[i]));
}
while(1)
{
sleep(1);
}
return 0;
}
说明:指定的宽高应该需要和yuv的宽高一致。
编译:gcc encode_yuv.c -L./lib -lavutil -lavcodec -lswresample -lavformat -lpthread -I./include -o encode_yuv