提供一个可以运行的ffmpeg工程:https://gitee.com/qiuguolu1108/ffmpeg-study
ffmpeg -i Jasmine.flv -vcodec libx264 -an -f h264 Jasmine.h264
进入工程中的player目录,使用上述命令,生成待处理的Jasmine.h264文件。
#include "spdlog/spdlog.h"
extern "C" {
#include "libavcodec/avcodec.h"
}
#define INBUF_SIZE 4096
char error_buf[1024] = {0};
char *av_get_err(int err_num) {
av_strerror(err_num, error_buf, sizeof(error_buf));
return error_buf;
}
void print_video_format(const AVFrame *frame) {
SPDLOG_INFO("width: {}", frame->width);
SPDLOG_INFO("height: {}", frame->height);
SPDLOG_INFO("format: {}", frame->format);
}
void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
FILE *yuv_file) {
int ret = avcodec_send_packet(dec_ctx, pkt);
if (ret == AVERROR(EAGAIN)) {
SPDLOG_WARN(
"Receive_frame and send_packet both returned EAGAIN, which is an API "
"violation.");
} else if (ret < 0) {
SPDLOG_WARN(
"Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",
av_get_err(ret), pkt->size);
return;
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return;
} else if (ret < 0) {
SPDLOG_ERROR("Error during decoding");
}
static int s_print_format = 0;
if (s_print_format == 0) {
s_print_format = 1;
print_video_format(frame);
}
// linesize[]代表每行的字节数量,所以每行的偏移是linesize[]
for (int j = 0; j < frame->height; j++) {
fwrite(frame->data[0] + j * frame->linesize[0], 1, frame->width,
yuv_file);
}
for (int j = 0; j < frame->height / 2; j++) {
fwrite(frame->data[1] + j * frame->linesize[1], 1, frame->width / 2,
yuv_file);
}
for (int j = 0; j < frame->height / 2; j++) {
fwrite(frame->data[2] + j * frame->linesize[2], 1, frame->width / 2,
yuv_file);
}
}
}
int main(int argc, char **argv) {
const char *h264_file_name = "Jasmine.h264";
const char *yuv_file_name = "Jasmine.yuv";
FILE *h264_file = fopen(h264_file_name, "rb");
if (!h264_file) {
SPDLOG_ERROR("Could not open {}", h264_file_name);
}
FILE *yuv_file = fopen(yuv_file_name, "wb");
if (!yuv_file) {
SPDLOG_ERROR("Could not open {}", yuv_file_name);
}
// AV_INPUT_BUFFER_PADDING_SIZE
// 在输入比特流结尾的要求附加分配字节的数量上进行解码
uint8_t h264_buf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
memset(h264_buf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
// find the H264 video decoder
const AVCodec *video_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!video_codec) {
SPDLOG_ERROR("Codec not found");
}
AVCodecParserContext *parser = av_parser_init(video_codec->id);
if (!parser) {
SPDLOG_ERROR("parser not found");
}
AVCodecContext *video_codec_ctx = avcodec_alloc_context3(video_codec);
if (!video_codec_ctx) {
SPDLOG_ERROR("Could not allocate video codec context");
}
/* open it */
if (avcodec_open2(video_codec_ctx, video_codec, NULL) < 0) {
SPDLOG_ERROR("Could not open codec");
}
uint8_t *data = h264_buf;
size_t data_size = 0;
AVPacket *pkt = av_packet_alloc();
if (!pkt) {
SPDLOG_ERROR("Could not allocate video packet");
}
AVFrame *frame = av_frame_alloc();
if (!frame) {
SPDLOG_ERROR("Could not allocate video frame");
}
while (!feof(h264_file)) {
/* read raw data from the input file */
data_size = fread(h264_buf, 1, INBUF_SIZE, h264_file);
if (!data_size) break;
/* use the parser to split the data into frames */
data = h264_buf;
while (data_size > 0) {
int ret =
av_parser_parse2(parser, video_codec_ctx, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
SPDLOG_ERROR("Error while parsing\n");
}
data += ret;
data_size -= ret;
if (pkt->size) decode(video_codec_ctx, frame, pkt, yuv_file);
}
}
/* flush the decoder */
decode(video_codec_ctx, frame, NULL, yuv_file);
fclose(h264_file);
fclose(yuv_file);
av_parser_close(parser);
avcodec_free_context(&video_codec_ctx);
av_frame_free(&frame);
av_packet_free(&pkt);
SPDLOG_INFO("video decode successful");
return 0;
}
[2021-09-14 21:37:28.285] [info] [main.cpp:16] width: 1280
[2021-09-14 21:37:28.287] [info] [main.cpp:17] height: 720
[2021-09-14 21:37:28.287] [info] [main.cpp:18] format: 0
[2021-09-14 21:37:29.256] [info] [main.cpp:152] video decode successful
使用如下命令播放生成的yuv文件:
ffplay -pixel_format yuv420p -video_size 1280*720 -framerate 25 Jasmine.yuv