diff options
Diffstat (limited to 'src/video_decode.c')
| -rw-r--r-- | src/video_decode.c | 151 | 
1 files changed, 151 insertions, 0 deletions
diff --git a/src/video_decode.c b/src/video_decode.c new file mode 100644 index 0000000..87d3483 --- /dev/null +++ b/src/video_decode.c @@ -0,0 +1,151 @@ +#define DEBUG 1 +#include "video_decode.h" + +#include <libavutil/avutil.h> +#include <libavformat/avformat.h> +#include <libavcodec/avcodec.h> +#include <libswscale/swscale.h> + + +struct video_decode { +	AVFormatContext *fmt_ctx; +	AVCodecContext *codec_ctx; +	AVCodec *codec; +	int stream; +}; + +struct video_frame_priv { +	struct video_frame base; +	AVFrame *frame; +}; + +int +video_decode_init(struct video_decode **vd_out, char *file, int64_t ts) +{ +	struct video_decode *vd; + +	vd = calloc(1, sizeof *vd); +	if (vd == NULL) +		return -1; + +	av_register_all(); + +	vd->fmt_ctx = NULL; +	vd->codec_ctx = NULL; + +	if (avformat_open_input(&vd->fmt_ctx, file, NULL, NULL) != 0) +		return -1; + +	if (avformat_find_stream_info(vd->fmt_ctx, NULL) < 0) +		return -2; +#if DEBUG +	av_dump_format(vd->fmt_ctx, 0, file, 0); +#endif +	vd->stream = av_find_best_stream(vd->fmt_ctx, AVMEDIA_TYPE_VIDEO, +					     -1, -1, NULL, 0); +	if (vd->stream == -1) +		return -3; + +	vd->codec_ctx = vd->fmt_ctx->streams[vd->stream]->codec; + +	if (vd->fmt_ctx->start_time != AV_NOPTS_VALUE) +		ts += vd->fmt_ctx->start_time; +	/* FIXME: Check size */ +	avformat_seek_file(vd->fmt_ctx, vd->stream, INT64_MIN, ts, INT64_MAX,0); + +	vd->codec = avcodec_find_decoder(vd->codec_ctx->codec_id); +	if (vd->codec == NULL) { +		fprintf(stderr, "Failed to find decoder!\n"); +		return -4; +	} +#if DEBUG +	printf("has cap truncated: %d\n", +	       vd->codec->capabilities & CODEC_CAP_TRUNCATED); +#endif +#if 0 +	if (codec->capabilities & CODEC_CAP_TRUNCATED) +		codec_ctx->flags |= CODEC_FLAG_TRUNCATED; +#endif +	if (avcodec_open2(vd->codec_ctx, vd->codec, NULL) < 0) { +		fprintf(stderr, "Failed to open codec!\n"); +		return -5; +	} + +	*vd_out = vd; +	return 0; +} + +void +video_decode_uninit(struct video_decode **_vd) +{ +	struct video_decode *vd = *_vd; + +	*_vd = NULL; +	avcodec_close(vd->codec_ctx); +	avformat_close_input(&vd->fmt_ctx); +	free(vd); +} + +int +video_decode_get_frame(struct video_decode *vd, struct video_frame **frame) +{ +	struct video_frame_priv *f; +	AVPacket packet; +	int decoded, got_picture; + +	f = calloc(1, sizeof *f); +	if (f == NULL) +		return -1; + +	packet.data = NULL; +	do { +		if (packet.data != NULL) +			av_free_packet(&packet); +		if (av_read_frame(vd->fmt_ctx, &packet) < 0) { +			fprintf(stderr, "Found no video stream packet!\n"); +			return -1; +		} +	} while (packet.stream_index != vd->stream); + +	f->frame = avcodec_alloc_frame(); + +	do { +#if DEBUG +		printf("packet.data: %p, packet.siz: %d, packet.strm_idx: %d\n", +		       packet.data, packet.size, packet.stream_index); +#endif +		decoded = avcodec_decode_video2(vd->codec_ctx, f->frame, +						&got_picture, &packet); +		if (decoded < 0) { +			fprintf(stderr, "Error while decoding frame!\n"); +			return -2; +		} +#if DEBUG +		printf("bytes_decoded: %d, got_picture: %d\n", +		       decoded, got_picture); +#endif +		 +	} while (got_picture == 0 && packet.size > 0); +	av_free_packet(&packet); +	if (got_picture == 0) +		return -3; + +	f->base.data = f->frame->data[0]; +	f->base.width = vd->codec_ctx->width; +	f->base.height = vd->codec_ctx->height; +	f->base.stride = f->frame->linesize[0]; + +	*frame = &f->base; + +	return 0; +} + +void +video_decode_free_frame(struct video_frame **frame) +{ +	struct video_frame_priv *f = (struct video_frame_priv *) *frame; + +	*frame = NULL; +	av_free(f->frame); +	free(f); +}  | 
