我正在尝试从示例中学习FFmpeg,因为时间很紧。任务是将原始YUV图像编码为给定宽度和高度的JPEG格式。我在FFmpeg官方网站上找到了示例,结果证明非常简单。然而,AVCodecContext中有一些字段我认为只有在编码视频时才有意义(例如bitrate,framerate,timebase,gopsize,max_b_frames等)。
对于视频,我很清楚这些值是什么,但是当我只想要一张图像时,我需要关心这些值吗?目前为了测试,我只是将它们设置为虚拟值,它似乎有效。但我想确保我没有做出从长远来看会失败的可怕假设。
编辑:
这是我得到的代码。大多数都是从示例中复制和粘贴的,其中一些更改是用新的API替换旧的API。
#include "thumbnail.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
void print_averror(int error_code) {
char err_msg[100] = {0};
av_strerror(error_code, err_msg, 100);
printf("Reason: %s\n", err_msg);
}
ffmpeg_status_t save_yuv_as_jpeg(uint8_t* source_buffer, char* output_thumbnail_filename, int thumbnail_width, int thumbnail_height) {
const AVCodec* mjpeg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
if (!mjpeg_codec) {
printf("Codec for mjpeg cannot be found.\n");
return FFMPEG_THUMBNAIL_CODEC_NOT_FOUND;
}
AVCodecContext* codec_ctx = avcodec_alloc_context3(mjpeg_codec);
if (!codec_ctx) {
printf("Codec context cannot be allocated for the given mjpeg codec.\n");
return FFMPEG_THUMBNAIL_ALLOC_CONTEXT_FAILED;
}
AVPacket* pkt = av_packet_alloc();
if (!pkt) {
printf("Thumbnail packet cannot be allocated.\n");
return FFMPEG_THUMBNAIL_ALLOC_PACKET_FAILED;
}
AVFrame* frame = av_frame_alloc();
if (!frame) {
printf("Thumbnail frame cannot be allocated.\n");
return FFMPEG_THUMBNAIL_ALLOC_FRAME_FAILED;
}
// The part that I don't understand
codec_ctx->bit_rate = 400000;
codec_ctx->width = thumbnail_width;
codec_ctx->height = thumbnail_height;
codec_ctx->time_base = (AVRational){1, 25};
codec_ctx->framerate = (AVRational){1, 25};
codec_ctx->gop_size = 10;
codec_ctx->max_b_frames = 1;
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
int ret = av_image_fill_arrays(frame->data, frame->linesize, source_buffer, AV_PIX_FMT_YUV420P, thumbnail_width, thumbnail_height, 32);
if (ret < 0) {
print_averror(ret);
printf("Pixel format: yuv420p, width: %d, height: %d\n", thumbnail_width, thumbnail_height);
return FFMPEG_THUMBNAIL_FILL_FRAME_DATA_FAILED;
}
ret = avcodec_send_frame(codec_ctx, frame);
if (ret < 0) {
print_averror(ret);
printf("Failed to send frame to encoder.\n");
return FFMPEG_THUMBNAIL_FILL_SEND_FRAME_FAILED;
}
ret = avcodec_receive_packet(codec_ctx, pkt);
if (ret < 0) {
print_averror(ret);
printf("Failed to receive packet from encoder.\n");
return FFMPEG_THUMBNAIL_FILL_SEND_FRAME_FAILED;
}
// store the thumbnail in output
int fd = open(output_thumbnail_filename, O_CREAT | O_RDWR);
write(fd, pkt->data, pkt->size);
close(fd);
// freeing allocated structs
avcodec_free_context(&codec_ctx);
av_frame_free(&frame);
av_packet_free(&pkt);
return FFMPEG_SUCCESS;
}
1条答案
按热度按时间e0bqpujr1#
正如你已经知道的,比特率,帧率,时基,gopsize和max_b_frames参数在编码单个JPEG图像时没有影响。
正如我们所看到的,FFmpeg使用MJPEG编解码器对JPEG图像进行编码。
“Motion JPEG”编码器支持图像编码和视频编码,因此编码器具有视频相关参数。
bitrate
无效,因为MJPEG编码器使用qcompress
参数设置质量级别。在支持
bitrate
的编解码器中,更高的视频比特率允许更高的视频质量。MJPEG编码器不支持
bitrate
(即使用于编码视频)。qcompress
是范围[0,1]中的值,其中0是最差质量,1是最佳质量。framerate
在编码图像时无效(framerate
参数仅与视频相关)。Sine MJPEG编解码器可用于图像和视频,编码器支持
framerate
参数。(framerate
可能仅在编码Motion JPEG视频时相关,例如使用视频容器作为AVI)。gopsize
是视频相关参数。在MJPEG格式中,无论
gopsize
参数的值如何,所有帧都是关键帧,并且gopsize=1。当对图像进行编码时,GOP是无意义的(GOP在视频中应用“图片组”)。
max_b_frames
必须是0
(否则会出现错误)。由于MJPEG中的所有帧都是关键帧,因此它们不能是B帧(所有关键帧都是I帧)。
测试:
为了制作可再现的示例,我们可以使用FFmpeg CLI以
yuv420p
像素格式创建原始帧/图像:ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1:duration=1 -vf scale=out_color_matrix=bt601:out_range=pc -f rawvideo -pix_fmt yuv420p thumbnail.yuv
由于我使用的是Windows,并且使用的是FFmpeg版本5.1.2,所以我不得不对您的代码示例进行一些修改。
下面的代码示例,还包括一个
main
函数,它读取thumbnail.yuv
,并执行save_yuv_as_jpeg
:输出图像(
thumbnail.jpg
):