一個純粹的視頻解碼程序(基於FFMPEG 4.0,在Ubuntu 14.04下驗證)

程序功能:
將指定的視頻文件,解碼爲原始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) *~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章