C++ 如何使用ffmpeg将h264帧保存为jpeg图像?
我想将h264流中的缩略图保存为JPEG格式的ffmpeg AV数据包 我从一个h264 AVPacket(iframe)开始,使用avcodec\u send\u packet/avcodec\u receive\u frame将其解码为一个AVFrame。现在尝试使用avcodec\u发送\u帧/avcodec\u接收\u数据包从AVFrame转换为AVPacket 我可以转换成png而不是jpg,虽然我确实得到了一个视频,看起来像是将三个单独的帧并排压缩成一个。不知道是不是一帧是R,下一帧是G,最后是B。我不确定,很明显我在那里做错了什么。我想这可能是png编码器,我不需要它,所以让我们先让jpg工作。但是jpg正在输出不可修复的文件 有什么建议吗 这是我的密码:C++ 如何使用ffmpeg将h264帧保存为jpeg图像?,c++,ffmpeg,jpeg,h.264,C++,Ffmpeg,Jpeg,H.264,我想将h264流中的缩略图保存为JPEG格式的ffmpeg AV数据包 我从一个h264 AVPacket(iframe)开始,使用avcodec\u send\u packet/avcodec\u receive\u frame将其解码为一个AVFrame。现在尝试使用avcodec\u发送\u帧/avcodec\u接收\u数据包从AVFrame转换为AVPacket 我可以转换成png而不是jpg,虽然我确实得到了一个视频,看起来像是将三个单独的帧并排压缩成一个。不知道是不是一帧是R,下一帧
int output_thumbnails(AVPacket* video_packet)
{
char png_file_name[max_chars_per_filename];
char thumbnail_id_char[10];
_itoa_s(thumbnail_id, thumbnail_id_char, 10);
strcpy_s(png_file_name, max_chars_per_filename, time_stamped_filepath);
strcat_s(png_file_name, max_chars_per_filename, time_stamped_filename);
strcat_s(png_file_name, max_chars_per_filename, thumbnail_id_char);
strcat_s(png_file_name, max_chars_per_filename, ".jpg");
thumbnail_id++;
int error_code = send_AVPacket_to_videocard(video_packet, av_codec_context_RTSP);
//if (error_code == AVERROR_EOF)
//{
// // error_code = videocard_to_PNG(png_file_name, av_codec_context_RTSP, av_codec_RTSP);
//}
if (error_code == AVERROR(EAGAIN)) //send packets to videocard until function returns EAGAIN
{
error_code = videocard_to_PNG(png_file_name, av_codec_context_RTSP);
//EAGAIN means that the video card buffer is ready to have the png pulled off of it
if (error_code == AVERROR_EOF)
{
// error_code = videocard_to_PNG(png_file_name, av_codec_context_RTSP, av_codec_RTSP);
}
else if (error_code == AVERROR(EAGAIN))
{
}
else
{
deal_with_av_errors(error_code, __LINE__, __FILE__);
}
}
else
{
deal_with_av_errors(error_code, __LINE__, __FILE__);
}
return 0;
}
VideoThumbnailGenerator.h:
#包括“VideoThumbnailGenerator.h”
bool解码器\u上下文\u创建=false;
bool编码器\u上下文\u创建=false;
AVCodecContext*h264_解码器_编解码器_ctx;
AvcodeContext*缩略图\编码器\编解码器\ ctx;
int发送AVPacket到视频卡(AVPacket*数据包,AVCodecContext*编解码器ctx)
{
如果(!解码器\u上下文\u已创建)
{
AVCodec*h264\u codec=AVCodec\u find\u解码器(codec\u ctx->codec\u id);
h264_解码器_编解码器_ctx=avcodec_alloc_上下文3(h264_编解码器);
h264\u解码器\u编解码器\u ctx->宽度=编解码器\u ctx->宽度;
h264\u解码器\u编解码器\u ctx->高度=编解码器\u ctx->高度;
h264_解码器_编解码器_ctx->pix_fmt=AV_pix_fmt_RGB24;
h264\解码器\编解码器\ ctx->编解码器\类型=AVMEDIA \类型\视频;
h264_解码器_编解码器_ctx->skip_frame=AVDISCARD_nontrans;//AVDISCARD_NONREF;//AVDISCARD_nontrans;
h264_解码器_编解码器_ctx->time_base.num=1;
h264_解码器_编解码器_ctx->time_base.den=30;
h264\u解码器\u编解码器\u ctx->extradata=编解码器\u ctx->extradata;
h264_解码器_编解码器_ctx->extradata_size=codec_ctx->extradata_size;
int error\u code=avcodec\u open2(h264\u解码器\u编解码器\u ctx,h264\u编解码器,空);
如果(!h264_编解码器){
返回-1;
}
如果(错误代码<0)
{
返回错误代码;
}
解码器\u上下文\u已创建=真;
}
//使用硬件解码来解码视频帧
int error_code=avcodec_send_数据包(h264_解码器_codec_ctx,数据包);
如果(错误代码==平均错误(EAGAIN))
{
返回平均值(EAGAIN);
}
如果(错误_codewidth=128;
缩略图编码器编解码器高度=(int)((浮点)编解码器高度/(浮点)编解码器宽度)*128);
缩略图\u编码器\u编解码器\u ctx->pix\u fmt=AV\u pix\u fmt\u RGB24;//AV\u pix\u fmt\u YUVJ420P
缩略图\u编码器\u编解码器\u ctx->codec\u type=AVMEDIA\u type\u视频;
缩略图\u编码器\u编解码器\u ctx->time\u base.num=1;
缩略图\u编码器\u编解码器\u ctx->time\u base.den=30;
bool thread\u check=缩略图\u编码器\u编解码器\u ctx->线程类型和FF\u线程\u帧;
bool frame\u threads\u check=缩略图\u编码器\u编解码器\u ctx->codec->功能和AV\u编解码器\u CAP\u frame\u线程;
int error\u code=avcodec\u open2(缩略图\u编码器\u编解码器\u ctx,缩略图\u编解码器,NULL);
如果(!缩略图_编解码器){
返回-1;
}
如果(错误代码<0)
{
返回错误代码;
}
编码器\u上下文\u已创建=真;
}
AVFrame*thumbnail_frame=av_frame_alloc();
AVPacket*缩略图_packet=av_packet_alloc();
//av_初始_包(png_包);
int error_code=avcodec_接收_帧(h264_解码器_codec_ctx,缩略图_帧);
//每次都检查错误
//注意:EAGAIN错误不会出现在这里,因为它们不会在
if(错误代码<0&&error\u代码!=AVERROR(EAGAIN))
{
printf(“错误:无法从视频卡获取帧”);
返回错误代码;
}
//如果还有更多帧要拉,则清空缓冲区(不应该有)
//while(错误代码!=averor(EAGAIN))
//{
////每次都检查错误
////注意:EAGAIN错误不会出现在这里,因为它们不会在
//如果(错误代码<0)
// {
//printf(“错误:无法从视频卡获取帧”);
//返回错误代码;
// }
//
//错误代码=avcodec接收帧(h264解码器ctx,png帧);
//}
//现在我们转换回AVPacket,这次是一个保存PNG信息的包,所以我们可以存储到文件中
错误代码=avcodec发送帧(缩略图编码器ctx,缩略图帧);
如果(错误代码>=0){
错误代码=avcodec接收数据包(缩略图编码器ctx,缩略图数据包);
文件*out\u PNG;
errno_t err=fopen_s(&out_PNG,PNG_file_path,“wb”);
如果(错误==0){
写入(缩略图数据包->数据,缩略图数据包->大小,1,输出PNG);
}
fclose(out_PNG);
}
返回错误代码;
}
<代码> >代码> STRCAT/CODE>,这是一个令人担忧的级别,它被称为C++代码。为什么不<代码> STD::String ?也许是一个好主意,切换到String,只是读一下关于STRCAT性能问题。我倾向于只按需要来坚持C函数和C++,但这看起来是个不错的选择。谢谢。这不是性能问题,而是Eclipse。使用固定大小的缓冲区会带来缓冲区溢出或内存泄漏的风险。使用strcat_会在缓冲区溢出/内存泄漏方面有所帮助。固定大小的缓冲区删除..会很好,因为这个程序的性能是第一位的。谢谢tadman。目前更担心的是如何将这个jpg正确保存到文件中这是一个令人担忧的级别,代码< >代码> >代码>为什么C++不>代码> STD::String ?也许是一个好主意,切换到String,只是读一下关于STRCAT的性能问题。我只需要按C函数和C++来做,但是这看起来是个不错的选择。谢谢。这不是性能问题,它是删除。避免使用固定大小的缓冲区,并冒缓冲区溢出或内存泄漏的风险
bool decoder_context_created = false;
bool encoder_context_created = false;
AVCodecContext* h264_decoder_codec_ctx;
AVCodecContext* thumbnail_encoder_codec_ctx;
int send_AVPacket_to_videocard(AVPacket* packet, AVCodecContext* codec_ctx)
{
if(!decoder_context_created)
{
AVCodec* h264_codec = avcodec_find_decoder(codec_ctx->codec_id);
h264_decoder_codec_ctx = avcodec_alloc_context3(h264_codec);
h264_decoder_codec_ctx->width = codec_ctx->width;
h264_decoder_codec_ctx->height = codec_ctx->height;
h264_decoder_codec_ctx->pix_fmt = AV_PIX_FMT_RGB24;
h264_decoder_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
h264_decoder_codec_ctx->skip_frame = AVDISCARD_NONINTRA;//AVDISCARD_NONREF;//AVDISCARD_NONINTRA;
h264_decoder_codec_ctx->time_base.num = 1;
h264_decoder_codec_ctx->time_base.den = 30;
h264_decoder_codec_ctx->extradata = codec_ctx->extradata;
h264_decoder_codec_ctx->extradata_size = codec_ctx->extradata_size;
int error_code = avcodec_open2(h264_decoder_codec_ctx, h264_codec, NULL);
if (!h264_codec) {
return -1;
}
if (error_code < 0)
{
return error_code;
}
decoder_context_created = true;
}
//use hardware decoding to decode video frame
int error_code = avcodec_send_packet(h264_decoder_codec_ctx, packet);
if(error_code == AVERROR(EAGAIN))
{
return AVERROR(EAGAIN);
}
if(error_code<0)
{
printf("Error: Could not send packet to video card");
return error_code;
}
return 0;
}
int videocard_to_PNG(char *png_file_path, AVCodecContext* codec_ctx)
{
if (!encoder_context_created)
{
//AVCodec* thumbnail_codec = avcodec_find_encoder(AV_CODEC_ID_PNG);
AVCodec* thumbnail_codec = avcodec_find_encoder(AV_CODEC_ID_JPEG2000);
thumbnail_encoder_codec_ctx = avcodec_alloc_context3(thumbnail_codec);
thumbnail_encoder_codec_ctx->width = 128;
thumbnail_encoder_codec_ctx->height = (int)(((float)codec_ctx->height/(float)codec_ctx->width) * 128);
thumbnail_encoder_codec_ctx->pix_fmt = AV_PIX_FMT_RGB24; //AV_PIX_FMT_YUVJ420P
thumbnail_encoder_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
thumbnail_encoder_codec_ctx->time_base.num = 1;
thumbnail_encoder_codec_ctx->time_base.den = 30;
bool thread_check = thumbnail_encoder_codec_ctx->thread_type & FF_THREAD_FRAME;
bool frame_threads_check = thumbnail_encoder_codec_ctx->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS;
int error_code = avcodec_open2(thumbnail_encoder_codec_ctx, thumbnail_codec, NULL);
if (!thumbnail_codec) {
return -1;
}
if (error_code < 0)
{
return error_code;
}
encoder_context_created = true;
}
AVFrame* thumbnail_frame = av_frame_alloc();
AVPacket* thumbnail_packet = av_packet_alloc();
//av_init_packet(png_packet);
int error_code = avcodec_receive_frame(h264_decoder_codec_ctx, thumbnail_frame);
//check for errors everytime
//note EAGAIN errors won't get here since they won't get past while
if (error_code < 0 && error_code != AVERROR(EAGAIN))
{
printf("Error: Could not get frame from video card");
return error_code;
}
//empty buffer if there are any more frames to pull (there shouldn't be)
//while(error_code != AVERROR(EAGAIN))
//{
// //check for errors everytime
// //note EAGAIN errors won't get here since they won't get past while
// if (error_code < 0)
// {
// printf("Error: Could not get frame from video card");
// return error_code;
// }
//
// error_code = avcodec_receive_frame(h264_decoder_codec_ctx, png_frame);
//}
//now we convert back to AVPacket, this time one holding PNG info, so we can store to file
error_code = avcodec_send_frame(thumbnail_encoder_codec_ctx, thumbnail_frame);
if (error_code >= 0) {
error_code = avcodec_receive_packet(thumbnail_encoder_codec_ctx, thumbnail_packet);
FILE* out_PNG;
errno_t err = fopen_s(&out_PNG, png_file_path, "wb");
if (err == 0) {
fwrite(thumbnail_packet->data, thumbnail_packet->size, 1, out_PNG);
}
fclose(out_PNG);
}
return error_code;
}