Author: wencoo
Blog:https://wencoo.blog.csdn.net/
Date: 24/04/2023
Details:
文章目录
- 实现功能
- 查看转换结果
- 参考
图片的解码方式和视频解码是一样的,因为视频是由一副一副的图片组成的,只不过视频的帧会前后参考,而图片是单独的一帧的格式封装。
这其中有一个功能点需要注意,不同的图片格式封装算法不同,所以解码使用的解码器也不同,我现在不清楚什么格式对应什么解码器,所以采用通过id的方式来查找解码器,是一种靠谱的行为。
实现功能
将png图片转换为yuv数据,示例代码如下:
#include <iostream>
#include <fstream>
#include <sstream>
#include <jsoncpp/json/json.h>
#include <tuple>
extern "C"
{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/audio_fifo.h>
#include <libavutil/avassert.h>
#include <libavutil/avstring.h>
#include <libavutil/dict.h>
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/samplefmt.h>
#include <libavutil/time.h>
#include <libavutil/timestamp.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/avfilter.h>
#include <libswresample/swresample.h>
}
int main()
{
std::cout << "Hello World!" << std::endl;
printf("ffmpeg version:%s\n", av_version_info());
int ret = 0;
// input yuv
//打开png图片
// png转换为yuv指令为:ffmpeg -i %4d.png -pix_fmt yuv420p -s 1984x1344 out.yuv
FILE *inFile = NULL;
const char *inFileName = "bg.png";
inFile = fopen(inFileName, "rb+");
if (!inFile)
{
printf("Fail to open file\n");
return -1;
}
int in_width = 48;
int in_height = 48;
// output yuv
FILE *outFile = NULL;
const char *outFileName = "output.yuv";
outFile = fopen( outFileName, "wb");
if (!outFile)
{
printf("Fail to create file for output\n");
return -1;
}
AVFormatContext *pFormatCtx = NULL;
if (avformat_open_input(&pFormatCtx, inFileName, NULL, NULL) != 0)
{
fprintf(stderr, "Couldn't open input filen");
return -1;
}
if (avformat_find_stream_info(pFormatCtx,0) < 0){
fprintf(stderr ,"av_find_stream_info ERRORn");
return -1;
}
int videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
fprintf(stderr ,"the first video stream index: videoStream = %d\n", videoStream);
break;
}
}
if (videoStream == -1)
return -1; // Didn't find a video stream
AVCodecContext *codeCtx = pFormatCtx->streams[videoStream]->codec;
if (!codeCtx)
{
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
std::cout << "pix_fmt:" << codeCtx->pix_fmt << std::endl;
//打开mp4文件
const AVCodec *codec = avcodec_find_decoder(codeCtx->codec_id);
if (!codec)
{
fprintf(stderr, "Codec not found\n");
exit(1);
}
if (avcodec_open2(codeCtx, codec, NULL) < 0)
{
fprintf(stderr, "Could not open codec\n");
exit(1);
}
AVFrame *frame = av_frame_alloc();
if (!frame)
{
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
AVPacket *pkt = av_packet_alloc();
if (!pkt){
fprintf(stderr, "Could not allocate video packet\n");
exit(1);
}
uint8_t *video_dst_data[4] = {NULL};
int video_dst_linesize[4] = {0};
ret = av_image_alloc(video_dst_data, video_dst_linesize, in_width, in_height, codeCtx->pix_fmt,1);
if (ret < 0){
}
int video_dst_bufsize = ret;
int indexNum = 1;
while (av_read_frame(pFormatCtx, pkt) >= 0)
{
if (pkt->stream_index == videoStream){
ret = avcodec_send_packet(codeCtx,pkt);
while(ret >= 0){
ret = avcodec_receive_frame(codeCtx,frame);
if (ret < 0 || ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
{
break;
}
if (codeCtx->codec->type == AVMEDIA_TYPE_VIDEO){
//获取到了yuv视频帧
av_image_copy(video_dst_data, video_dst_linesize,
(const uint8_t **)(frame->data), frame->linesize,
codeCtx->pix_fmt, in_width, in_height);
char tmpstr[100] = {};
sprintf(tmpstr, "yuv-%d.yuv", indexNum);
FILE *f1;
f1 = fopen(tmpstr, "wb");
fwrite(video_dst_data[0], 1, video_dst_bufsize, f1);
fclose(f1);
indexNum++;
}
}
}
av_packet_unref(pkt);
if (ret < 0)
break;
}
fclose(inFile);
fclose(outFile);
return 0;
}
查看转换结果
使用ffplay进行播放查看,需指定格式
ffplay.exe -f rawvideo -video_size 48x48 -pix_fmt rgba -i .\yuv-1.yuv
参考
- FFmpeg开发实战(六):jpeg转换为yuv格式图像