程序功能:
将指定的视频文件,解码为原始YUV数据,只包含视频流。
开发环境:
Ubuntu 14.04, GCC 4.8.4, FFMPEG 4.0
编译方法:
将代码copy命名为SimpleDecoder.c,与Makefile放置于同一目录下,执行 make 即可。
运行方法:
执行 ./mydecoder 视频路径,比如 ./mydecoder ~/Videos/xx.mp4,解码后的文件保存在xx.mp4同目录下,文件名为xx.mp4_rawvideo.yuv。
说明:
将指定的视频文件,解码为原始YUV数据,只包含视频流。
开发环境:
Ubuntu 14.04, GCC 4.8.4, FFMPEG 4.0
编译方法:
将代码copy命名为SimpleDecoder.c,与Makefile放置于同一目录下,执行 make 即可。
运行方法:
执行 ./mydecoder 视频路径,比如 ./mydecoder ~/Videos/xx.mp4,解码后的文件保存在xx.mp4同目录下,文件名为xx.mp4_rawvideo.yuv。
说明:
解码后,由于文件保存的是YUV原始数据,一个40MB大小的MP4文件,转换后可能达到2GB。因此,在程序里面做了一个大小限制,当转换后的YUV文件大小达到500M(宏控OUTPUT_VIDEO_SIZE)时,即停止转换。
SimpleDecoder.c:
/*
* A simple decoder with FFMPEG4.0.
* Only support video decoder, not support audio and subtitle.
* Created by LiuWei@20180527
*/
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <sys/stat.h>
#define SUFFIX_VIDEO "_rawvideo.yuv"
#define OUTPUT_VIDEO_SIZE 500*1024*1024 /* 500MB */
static AVFormatContext *fmt_ctx = NULL;
static AVStream *video_stream = NULL;
static AVCodec *dec = NULL;
static AVCodecContext *video_dec_ctx = NULL;
static AVFrame *video_frame = NULL;
static AVPacket pkt;
static FILE *dst_video_file = NULL;
static int video_stream_index = -1;
static uint8_t *video_dst_data[4] = {NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;
static struct stat file_stat;
int main(int argc, char *argv[])
{
int ret = 0;
char *src_file_name = NULL;
char *dst_video_filename = NULL;
if(argc != 2) {
printf("e.g. ./mydecoder ~/Videos/xx.mp4 \n");
return -1;
}
src_file_name = argv[1];
dst_video_filename = (char *)malloc(strlen(src_file_name) + strlen(SUFFIX_VIDEO) + 1);
if(!dst_video_filename) {
printf("malloc for dst_video_filename fail.\n");
return -1;
}
strcpy(dst_video_filename, src_file_name);
strcat(dst_video_filename, SUFFIX_VIDEO);
/* Open an input stream and read the reader. The codecs are not opened.
* The stream must be closed with avformat_close_input().*/
if(avformat_open_input(&fmt_ctx, src_file_name, NULL, NULL) < 0) {
printf("Could not open %s\n", src_file_name);
free(dst_video_filename);
return -1;
}
if(avformat_find_stream_info(fmt_ctx, NULL) < 0) {
printf("Could not find stream information\n");
goto END;
}
/* Open codec for video stream */
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if(ret < 0) {
printf("Could not find video stream in input file %s\n", src_file_name);
goto END;
} else {
video_stream_index = ret;
}
video_stream = fmt_ctx->streams[video_stream_index];
dec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if(!dec) {
printf("Failed to find %s codec\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
goto END;
}
/* Allocate a codec context for the decoder.
* The resulting struct should be freed with avcodec_free_context().*/
video_dec_ctx = avcodec_alloc_context3(dec);
if(!video_dec_ctx) {
printf("Failed to allocate the %s codec context\n",
av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
goto END;
}
/* Copy codec parameters from input stream to output codec context */
ret = avcodec_parameters_to_context(video_dec_ctx, video_stream->codecpar);
if(ret < 0) {
printf("Failed to copy %s codec parameters to decoder context\n",
av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
goto END;
}
/* Init the decoders */
ret = avcodec_open2(video_dec_ctx, dec, NULL);
if(ret < 0) {
printf("Failed to open %s codec\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
goto END;
}
/* Dump information */
printf("----------------------File Info---------------------");
av_dump_format(fmt_ctx, 0, src_file_name, 0);
printf("----------------------------------------------------\n");
/* Create a file to store video frame */
dst_video_file = fopen(dst_video_filename, "wb");
if(!dst_video_file) {
printf("Could not open destination file %s\n", dst_video_filename);
goto END;
}
/* Allocate image where the decoded image will be put.
* The allocated image buffer has to be freed by using av_freep(&pointers[0]). */
ret = av_image_alloc(video_dst_data, video_dst_linesize,
video_dec_ctx->width, video_dec_ctx->height, video_dec_ctx->pix_fmt, 1);
if(ret < 0) {
printf("Could not allocate raw video buffer\n");
goto END;
}
video_dst_bufsize = ret;
video_frame = av_frame_alloc(); /* must be freed using av_frame_free(). */
if(!video_frame) {
printf("Could not allocate frame\n");
goto END;
}
/* Initialize packet, send data to NULL, let the demuxer fill it */
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
/* Read frames from the file */
while(av_read_frame(fmt_ctx, &pkt) >= 0) { /* The packet must be freed with av_packet_unref() */
if(pkt.stream_index != video_stream_index) {
av_packet_unref(&pkt);
continue;
}
ret = avcodec_send_packet(video_dec_ctx, &pkt);
if(ret < 0) {
av_packet_unref(&pkt);
continue;
}
do {
ret = avcodec_receive_frame(video_dec_ctx, video_frame);
if(ret < 0)
break;
else if(ret == 0) { /* Got a frame successfully */
/* copy decoded frame to destination buffer:
* this is required since rawvideo expects non aligned data */
av_image_copy(video_dst_data, video_dst_linesize,
(const uint8_t **)(video_frame->data), video_frame->linesize,
video_dec_ctx->pix_fmt, video_dec_ctx->width, video_dec_ctx->height);
/* write to rawvideo file */
fwrite(video_dst_data[0], 1, video_dst_bufsize, dst_video_file);
} else if(ret == AVERROR_EOF) {
avcodec_flush_buffers(video_dec_ctx);
break;
}
} while(ret != AVERROR(EAGAIN));
av_packet_unref(&pkt);
/* If file size reaches the limitation, stop decoding */
stat(dst_video_filename, &file_stat);
if(file_stat.st_size >= OUTPUT_VIDEO_SIZE)
break;
}
printf("Demuxing succeeded.\n");
printf("Play the output video file with the command:\n"
"ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
av_get_pix_fmt_name(video_dec_ctx->pix_fmt), video_dec_ctx->width, video_dec_ctx->height, (char *)dst_video_filename);
END:
if(dst_video_filename)
free(dst_video_filename);
if(video_dst_data[0])
av_freep(&video_dst_data[0]);
if(video_frame)
av_frame_free(&video_frame);
if(dst_video_file)
fclose(dst_video_file);
if(video_dec_ctx)
avcodec_free_context(&video_dec_ctx);
if(fmt_ctx)
avformat_close_input(&fmt_ctx);
return 0;
}
Makefile:
CC=gcc
CCFLAGS=-I/usr/local/include -O2
LDFLAGS=-L/usr/local/lib -lavformat -lavfilter -lavcodec -lswresample -lavdevice -lavutil -lswscale -lpostproc -lpthread -lm -lx264 -lx265 -lz -lSDL2
TARGET=mydecoder
OBJS=SimpleDecoder.o
RM=rm -f
STRIP=strip
$(TARGET):$(OBJS)
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS)
# $(STRIP) $(TARGET)
$(OBJS):%.o:%.c
$(CC) -c -g $(CCFLAGS) $< -o $@
clean:
$(RM) $(TARGET) $(OBJS) *~