In use libx264 do h264 When compressed , We can order ffmpeg -h encoder=libx264 To see the input formats it supports
Encoder libx264 [libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10]:
General capabilities: delay threads
Threading capabilities: auto
Supported pixel formats: yuv420p yuvj420p yuv422p yuvj422p yuv444p yuvj444p nv12 nv16 nv21
yuv422p No YUY2, You can see me from USB The data that the camera takes out directly YUY2 It can't be used as input format directly .( collection YUY2 Data check the last article https://my.oschina.net/noevilme/blog/4462358)
There is often 3 To do the conversion :
- Do the conversion by pixel
- Use libyuv
- Use ffmpeg swscale
I recommend No 2 Species and 3 Kind of , The big guys' algorithms are optimized , Unless you feel better X. According to the legend of the Jianghu libyuv yes swscale A few times faster , I didn't test it .
Let's know before we switch YUY2 and I420 data format , Words YUV The names are a bit confusing , Reference resources http://fourcc.org/yuv.php
YUY2, YUYV, YUV422 All three are YUY2 The nickname ,ffmpeg Definition AV_PIX_FMT_YUYV422.
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
I420, IYUV, YUV420P, YU12, The front ones are all I420 Name , among YUV420P It is also a general term for several formats , Under certain circumstances, it is I420,ffmpeg Definition AV_PIX_FMT_YUV420P.
Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y
U U U U U U U U
V V V V V V V V
One 、 Use libyuv::YUY2ToI420
libyuv You need to compile and install it yourself https://github.com/lemenkov/libyuv
// Convert YUY2 to I420.
LIBYUV_API
int YUY2ToI420(const uint8_t* src_yuy2,
int src_stride_yuy2,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height);
Above is the interface definition ,stride Parameter refers to the image format line spacing .
int libyuv_convert(const char *input_file, const char *output_file, int width,
int height) {
FILE *in_fp = fopen(input_file, "rb");
if (!in_fp) {
std::cout << "open input failure" << std::endl;
return 1;
}
FILE *out_fp = fopen(output_file, "wb");
if (!out_fp) {
std::cout << "open output failure" << std::endl;
return 1;
}
uint8_t *yuy2_image = new uint8_t[width * height * 2];
uint8_t *i420_image = new uint8_t[width * height * 3 / 2];
while (fread(yuy2_image, 1, width * height * 2, in_fp) ==
(size_t)width * height * 2) {
uint8_t *i420_y = i420_image;
uint8_t *i420_u = i420_y + width * height;
uint8_t *i420_v = i420_u + width * height / 4;
libyuv::YUY2ToI420(yuy2_image, width * 2, i420_y, width, i420_u,
width / 2, i420_v, width / 2, width, height);
fwrite(i420_image, 1, width * height * 3 / 2, out_fp);
}
delete[] i420_image;
delete[] yuy2_image;
fclose(in_fp);
fclose(out_fp);
return 0;
}
Two 、 Use sws_scale
swscale Kuo is ffmpeg Part of , So in the operation, use AVFrame The structure is more convenient , Or you have to define a two-dimensional array .
int ffmpeg_convert2(const char *input_file, const char *output_file, int width,
int height) {
SwsContext *context;
FILE *in_fp, *out_fp;
in_fp = fopen(input_file, "rb");
if (!in_fp) {
std::cout << "open input failure" << std::endl;
return 1;
}
out_fp = fopen(output_file, "wb");
if (!out_fp) {
std::cout << "open out file failure" << std::endl;
fclose(in_fp);
return 1;
}
context = sws_getContext(width, height, AV_PIX_FMT_YUYV422, width, height,
AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, nullptr,
nullptr, nullptr);
AVFrame *av_frame_in = av_frame_alloc();
auto yuy2_image_size =
av_image_alloc(av_frame_in->data, av_frame_in->linesize, width, height,
AV_PIX_FMT_YUYV422, 1);
AVFrame *av_frame_out = av_frame_alloc();
auto i420_image_size =
av_image_alloc(av_frame_out->data, av_frame_out->linesize, width,
height, AV_PIX_FMT_YUV420P, 1);
while (fread(av_frame_in->data[0], 1, yuy2_image_size, in_fp) ==
(size_t)yuy2_image_size) {
sws_scale(context, av_frame_in->data, av_frame_in->linesize, 0, height,
av_frame_out->data, av_frame_out->linesize);
fwrite(av_frame_out->data[0], 1, i420_image_size, out_fp);
}
sws_freeContext(context);
av_freep(&av_frame_in->data[0]);
av_freep(&av_frame_out->data[0]);
av_frame_free(&av_frame_in);
av_frame_free(&av_frame_out);
fclose(in_fp);
fclose(out_fp);
return 0;
}
Call function
#include <cstdio>
#include <iostream>
#include <libyuv/convert_from.h>
#include <libyuv/cpu_id.h>
#include <libyuv/planar_functions.h>
#include <libyuv/row.h>
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libyuv.h>
#include <libyuv/convert.h>
};
int main(int argc, char **argv) {
const char *input_file =
"1280x720_yuy2.yuv";
const char *output_file = "yuv_1280x720_i420.yuv";
libyuv_convert(input_file, output_file, 1280, 720);
ffmpeg_convert2(input_file, "ff_1280x720_i420.yuv", 1280, 720);
return 0;
}