#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#define VideoWidth 640
#define VideoHeight 480
void Decode_Mjpeg(AVCodecContext *decodec_ctx, AVPacket *packet, AVFrame *frame, AVFrame *frame_en, FILE *outfile)
{
int ret = 0;
int i = 0;
int j = 0;
//向解码器输入数据
ret = avcodec_send_packet(decodec_ctx, packet);
if(ret < 0)
{
av_log(NULL, AV_LOG_INFO, "Failed to send a packet to decoder!\n");
exit(1);
}
while(ret >= 0)
{//解码器输出数据
ret = avcodec_receive_frame(decodec_ctx, frame);
//若数据不足以解码或者数据用完了
if( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
return;
}else if (ret <0){
printf("Error, Failed to decode!\n");
exit(1);
}
if(outfile)
{
//将Frame中存储的 YUV422 转换为 YUV420
// U / V存储格式一样
// frame->data[1]举例 数据包宽高分别为VideoWidth/2 、VideoHeight/2
//UUUUUUUUUUUUU UUUUUUUUUUUUUU
//UUUUUUUUUUUUU - >
//UUUUUUUUUUUUU - >UUUUUUUUUUUUUU
//UUUUUUUUUUUUU
for(i = 0; i < VideoHeight/2; i ++)
{
for(j = 0; j < VideoWidth/2; j ++)
{
frame->data[1][(VideoWidth/2) * i + j] = frame->data[1][(VideoWidth/2) * i * 2+ j];
frame->data[2][(VideoWidth/2) * i + j] = frame->data[2][(VideoWidth/2) * i * 2+ j];
}
}
//YUV422->YUV420多余数据置0
for(i = 0; i < 307200/4; i ++)
{
frame->data[1][307200/4 + i] = 0;
frame->data[2][307200/4 + i] = 0;
}
//先后向文件输出Y U V 数据
fwrite(frame->data[0], 1, 307200, outfile);
fwrite(frame->data[1], 1, 307200/4, outfile);
fwrite(frame->data[2], 1, 307200/4, outfile);
fflush(outfile);
//把数据拷贝到输入H264编码器的Frame中
memcpy(frame_en->data[0], frame->data[0], 307200);
memcpy(frame_en->data[1], frame->data[1], 307200/4);
memcpy(frame_en->data[2], frame->data[2], 307200/4);
}
av_frame_unref(frame);
}
}
/*
* @brief 打开设备
* @param: void
*@return: AVFormatContext* fmt_ctx 设备格式上下文
*/
AVFormatContext* OpenDevice(void)
{
//打开视频设备相关定义
AVFormatContext* fmt_ctx = NULL; //格式上下文
char *deviceName = "/dev/video1"; //视频设备
AVDictionary *options = NULL;
int device_open_ret=0; //打开成功与否标识
char errorbuf[1024] = {0};
//set log level
av_log_set_level(AV_LOG_DEBUG);
//注册设备all
avdevice_register_all();
//设置采集输入方式Format
const AVInputFormat* avformat = av_find_input_format("video4linux2");
//视频设备添入 options参数 分辨率/帧率
av_dict_set(&options, "video_size", "640x480", 0);
av_dict_set(&options, "framerate", "30", 0);
// av_dict_set(&options, "pixel_format", "mjpeg", 0);
av_dict_set(&options, "input_format", "mjpeg", 0);
//avformat_open_input(输出地址,设备名, 输入方式, 解码端)
if((device_open_ret = avformat_open_input(&fmt_ctx, deviceName, avformat, &options))< 0) //如果打开设备出错
{
//返回错误码
av_strerror(device_open_ret, errorbuf,1024);
//打印错误信息
av_log(NULL, AV_LOG_INFO, "Failed to open device, [%d]%s\n", device_open_ret, errorbuf);
return NULL;
}
return fmt_ctx;
}
/*
* @brief 打开编码器
* @param: int width, int height 分辨率的宽高
*@return: AVCodecContext* codec_ctx 编码器上下文
*/
AVCodecContext* OpenEncoder(int width, int height)
{
const AVCodec *codec = NULL;
AVCodecContext *codec_ctx = NULL;
int ret = 0;
//选择libx264编码器
codec = avcodec_find_encoder_by_name("libx264");
if(!codec)
{
av_log(NULL, AV_LOG_INFO, "ERROR, this codec not found!\n");
exit(1);
}
//编码器上下文
codec_ctx = avcodec_alloc_context3(codec);
if(!codec_ctx)
{
av_log(NULL, AV_LOG_INFO, "Failed to create Codec Ctx!\n");
exit(1);
}
//设置H264编码参数
//SPS / PPS
codec_ctx->profile = FF_PROFILE_H264_HIGH_444; //H264 Profile
codec_ctx->level = 50; //H264 Level = 5.0
//分辨率
codec_ctx->width = width; //640
codec_ctx->height = height;//480
//GOP
codec_ctx->gop_size = 250; //设的大一点
codec_ctx->keyint_min = 25; //最小值
//B帧
codec_ctx->max_b_frames = 3;//b帧数量
codec_ctx->has_b_frames = 1;// 需要b帧
//参考帧
codec_ctx->refs = 3; //参考帧数量
//设置输入的YUV格式
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; //libx264需要 YUV420 的输入
//设置码率
codec_ctx->bit_rate = 600000; // 分辨率 x 帧率 x 8位深 x 1.5(yuv420p)
//设置帧率
codec_ctx->time_base = (AVRational){1, 30};
codec_ctx->framerate = (AVRational){30, 1};
ret = avcodec_open2(codec_ctx, codec, NULL);
if(ret < 0)
{
av_log(NULL, AV_LOG_INFO, "Failed to open the codec, %s\n!", av_err2str(ret));
exit(1);
}
return codec_ctx;
}
/*
* @brief 打开解码器
* @param: int width, int height 分辨率的宽高
*@return: AVCodecContext* codec_ctx 编码器上下文
*/
AVCodecContext* OpenDecoder(int width, int height)
{
const AVCodec *decodec = NULL;
AVCodecContext *decodec_ctx = NULL;
int ret = 0;
//选择编码器
decodec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
if(!decodec)
{
av_log(NULL, AV_LOG_INFO, "Failed to create MJPEG Decoder!\n");
exit(1);
}
//编码器上下文初始化
decodec_ctx = avcodec_alloc_context3(decodec);
if(!decodec_ctx)
{
av_log(NULL, AV_LOG_INFO, "Failed to create MJPEG Decoder Ctx!\n");
exit(1);
}
//分辨率
decodec_ctx->width = width; //640
decodec_ctx->height = height;//480
ret = avcodec_open2(decodec_ctx, decodec, NULL);
if(ret < 0)
{
av_log(NULL, AV_LOG_INFO, "Failed to open the codec, %s\n!", av_err2str(ret));
exit(1);
}
return decodec_ctx;
}
/*
* @brief 创建编码器输入数据包Frame'
* @param: Frame的参数:int width, int height 分辨率的宽高; 数据pix_fmt格式 enum AVPixelFormat pixfmt
*@return: AVFrame* frame 编码器输入数据包
*/
AVFrame* CreateFrame(int width, int height, enum AVPixelFormat pixfmt)
{
AVFrame *frame = NULL;
int ret = 0;
//frame初始化
frame = av_frame_alloc();
if(!frame)
{
av_log(NULL, AV_LOG_INFO, "Failed to create frame!\n");
goto __ERROR;
}
//设置编码器输入数据包参数
frame->width = width;
frame->height = height;
frame->format = pixfmt;
//frame获取数据空间
ret = av_frame_get_buffer(frame, 32);
if(ret < 0)
{
av_log(NULL, AV_LOG_INFO, "Failed to get Frame Buffer!\n");
goto __ERROR;
}
return frame;
__ERROR:
if(frame)
av_frame_free(&frame);
return NULL;
}
/*
* @brief 编码器H264
* @param: AVCodecContext *decodec_ctx, 编码器上下文
* AVPacket *packet, 编码器输入数据包packet
* AVFrame *frame, 编码器输出数据包frame
* FILE *outfile 文件名 若不用可设为NULL
*@return: NULL
*/
void Encode_H264(AVCodecContext *codec_ctx, AVFrame *frame, AVPacket *packet, FILE *outfile)
{
int ret = 0;
//打印PTS
if(frame)
{
av_log(NULL, AV_LOG_INFO, "send frame to encoder, pts=%ld\n", frame->pts);
}
//向编码器送数据
ret = avcodec_send_frame(codec_ctx, frame);
if(ret < 0)
{
av_log(NULL, AV_LOG_INFO, "Failed to send Frame to encodec,ret = %d!\n", ret);
exit(1);
}
while(ret >= 0)
{
//接收编码完成的数据
ret = avcodec_receive_packet(codec_ctx, packet);
//如果数据不足或者数据用完了
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
av_log(NULL, AV_LOG_INFO, "Failed to encodec!\n");
exit(1);
}
if(outfile)
{
fwrite(packet->data, 1, packet->size, outfile);
av_packet_unref(packet);
}
}
}
int main()
{
//set log level
av_log_set_level(AV_LOG_DEBUG);
//上下文
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
AVCodecContext *decodec_ctx = NULL;
AVFrame *frame_decodec= NULL;
AVFrame *frame_encodec= NULL;
AVPacket *packet_de= NULL;
AVPacket *packet_en= NULL;
AVPacket pkt;
int count = 0;
int base = 0;
char *outfileName = "/home/pi/mjpeg/11.yuv";
FILE *outFile = fopen(outfileName, "wb+");
char *h264fileName = "/home/pi/mjpeg/1.h264";
FILE *h264File = fopen(h264fileName, "wb+");
if(!outFile)
{
av_log(NULL, AV_LOG_INFO, "Failed to create outFile!");
goto __ERROR;
}
fmt_ctx = OpenDevice();
decodec_ctx = OpenDecoder(VideoWidth, VideoHeight);
codec_ctx = OpenEncoder(VideoWidth, VideoHeight);
frame_decodec = CreateFrame(VideoWidth, VideoHeight, AV_PIX_FMT_YUV420P);
frame_encodec = CreateFrame(VideoWidth, VideoHeight, AV_PIX_FMT_YUV420P);
packet_de= av_packet_alloc();
packet_en= av_packet_alloc();
if(!packet_de || !packet_en)
{
av_log(NULL, AV_LOG_INFO, "Failed to create Packet!\n");
goto __ERROR;
}
if( av_new_packet(packet_de, 150000)< 0)
{
av_log(NULL, AV_LOG_INFO, "Failed to get Packet Buffer!\n");
goto __ERROR;
}
while((av_read_frame(fmt_ctx, &pkt) == 0))
{
av_log(NULL, AV_LOG_INFO, "Pac size = %d(%p), Count = %d \n",
pkt.size,
pkt.data,
count );
memcpy(packet_de->data, pkt.data, pkt.size);
Decode_Mjpeg(decodec_ctx, packet_de, frame_decodec, frame_encodec, outFile);
frame_encodec->pts = base++;
Encode_H264(codec_ctx, frame_encodec, packet_en, h264File);
count ++;
av_packet_unref(&pkt); //释放pkt
}
//将剩余数据进行处理
Decode_Mjpeg(decodec_ctx, NULL, frame_decodec, NULL, outFile);
Encode_H264(codec_ctx, NULL, packet_en, h264File);
av_log(NULL, AV_LOG_INFO, "Finished");
__ERROR:
//关闭文件
if(outFile)
fclose(outFile);
//释放
if(fmt_ctx)
avformat_close_input(&fmt_ctx);
if(codec_ctx)
avcodec_close(codec_ctx);
if(frame_decodec)
av_frame_free(&frame_decodec);
if(frame_encodec)
av_frame_free(&frame_encodec);
if(packet_de)
av_packet_free(&packet_de);
if(packet_en)
av_packet_free(&packet_en);
return;
}