c++ - 如何使用 ffmpeg 将 h264 帧保存为 jpeg 图像?
问题描述
我想从 h264 流中保存缩略图,我将其转换为 ffmpeg avpackets 作为 jpeg。
我从 h264 AVPacket (iframe) 开始,并使用 avcodec_send_packet/avcodec_receive_frame 将其解码为 AVFrame。现在尝试使用 avcodec_send_frame/avcodec_receive_packet 从 AVFrame 转换为 AVPacket
我可以转换为 png 而不是 jpg,尽管我确实得到了一个视频,它看起来像是在输出三个单独的帧并排挤成一个。想知道它是不是一帧是R,下一个是G,最后是B。我不确定,显然我在那里做错了什么。我认为它可能是 png 编码器,我不需要它,所以让我们先让 jpg 工作。但是 jpg 正在输出无法打开的文件。
有什么建议吗?
这是我的代码:
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: #include "VideoThumbnailGenerator.h"
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;
}
解决方案
推荐阅读
- javascript - Height not increasing using setInterval (creating border around screen)
- java - 解释模拟匹配(str)与相同(str)的这种行为的原因
- angular - Angular6从HTML调用组件
- r - 从 R 中是和否值的两列导出列联真值表
- python - 使用 lambda 的字典理解将内存地址作为值返回
- python - python - 在混合类型列表中查找最后两个数字的索引
- javascript - 如何设置元素的索引
- c++ - 如何用强盗转换这个类型列表?
- python - 如何将在 Visual Studio 中创建的 Python 项目部署到 Heroku?
- c# - Xamarin 乐天动画