流程:
avformat_open_input : 打开视频文件。
avformat_find_stream_info: 查看是否有流信息。
av_find_best_stream : 找到视频流。
avcodec_find_decoder : 找到这个视频流对应的解码器,我们测试是这个mp4视频的解
码器是 h264 。
avcodec_alloc_context3 : 分配一个解码器上下文。
avcodec_parameters_to_context : 将视频流的参数信息复制给解码器上下文。
avcodec_open2 : 打开解码器。
av_read_frame : 循环读取压缩的数据 。
avcodec_send_packet : 将上一步读取的数据发送给解码器。
avcodec_receive_frame 从解码器拿到解码之后的原始裸数据AVFrame。
然后从 AVFrame中可以知道这个frame的 像素格式,我们测试的这个mp4是 0,
也就是 AV_PIX_FMT_YUV420P,
frame 的data 变量存储了原始数据。
data[0] 是Y数据 , data[1] 是U数据 , data[2] 是V数据 ,
size 依次是 widthheight, widthheight/4, width*height/4。
然后读取保存。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int saveAsYuv420p(AVFrame *pFrame, int index) {
int file = open("output.yuv", O_WRONLY | O_CREAT | O_TRUNC);
// AV_PIX_FMT_YUV420P
printf("pFrame->format = %d\n", pFrame->format);
//save y
size_t t = write(file, pFrame->data[0], pFrame->width * pFrame->height);
if (t <= 0) {
printf("<0\n");
} else {
printf("write ok \n");
}
//save u
t = write(file, pFrame->data[1], pFrame->width * pFrame->height / 4);
if (t <= 0) {
printf("<0\n");
} else {
printf("write ok \n");
}
//save v
t = write(file, pFrame->data[2], pFrame->width * pFrame->height / 4);
if (t <= 0) {
printf("<0\n");
} else {
printf("write ok \n");
}
close(file);
return 0;
}
/**
*
*/
int main(int argc, char **argv) {
int ret;
const char *inFileName = "../missu.mp4";
AVFormatContext *ifmt_ctx = NULL;
AVPacket *packet = NULL;
packet = av_packet_alloc();
AVFrame *pFrame = NULL;
AVCodecContext *decode_ctx;
if (avformat_open_input(&ifmt_ctx, inFileName, NULL, NULL) != 0) {
perror("avformat_open_input");
return -1;
}
if (avformat_find_stream_info(ifmt_ctx, NULL) < 0) {
perror("avformat_find_stream_info");
fprintf(stdout, "Couldn't find stream information \n");
return -1;
}
if (ifmt_ctx->nb_streams == 0) {
printf("nb_streams is 0,exit \n");
return -1;
}
// Find the first video stream
int videoStream = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1,
NULL, 0);
if (videoStream == -1) {
printf("Didn't find a video stream \n");
return -1;
}
AVStream *stream = ifmt_ctx->streams[videoStream];
// Copy contexts
const AVCodec *decoder = avcodec_find_decoder(stream->codecpar->codec_id);
printf("format->name = %d \n", stream->codecpar->format); // h264
printf("decoder->name = %s \n", decoder->name); // h264
printf("decoder->AVCodecID = %d \n", decoder->id); //AV_CODEC_ID_H264
decode_ctx = avcodec_alloc_context3(decoder);
if (!decode_ctx) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
ret = avcodec_parameters_to_context(decode_ctx, stream->codecpar);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Failed to copy decoder parameters to input decoder context "
"for stream\n");
return ret;
}
ret = avcodec_open2(decode_ctx, decoder, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream \n");
return ret;
}
int i = 0;
pFrame = av_frame_alloc();
while (av_read_frame(ifmt_ctx, packet) >= 0) {
if (packet->stream_index == videoStream) {
i++;
again: ret = avcodec_send_packet(decode_ctx, packet);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
av_log(NULL, AV_LOG_ERROR, "AVERROR(EAGAIN) \n");
}
av_log(NULL, AV_LOG_ERROR, "Decoding failed: %d \n", ret);
break;
}
ret = avcodec_receive_frame(decode_ctx, pFrame);
if (ret == AVERROR(EAGAIN)) {
goto again;
}
if (ret != 0) {
av_log(NULL, AV_LOG_ERROR,
"avcodec_receive_frame: failed %d \n", ret);
break;
}
fprintf(stdout, "we get a video frame \n");
saveAsYuv420p(pFrame, i);
if (i == 1) {
break;
}
}
}
end: av_frame_free(&pFrame);
av_packet_free(&packet);
avformat_close_input(&ifmt_ctx);
return 0;
}
如果有任何问题,欢迎评论留言。