diff options
-rw-r--r-- | src/Makefile | 7 | ||||
-rw-r--r-- | src/frame_to_pgm.c | 44 | ||||
-rw-r--r-- | src/video_decode.c | 151 | ||||
-rw-r--r-- | src/video_decode.h | 19 |
4 files changed, 219 insertions, 2 deletions
diff --git a/src/Makefile b/src/Makefile index 29ffd29..fbdb424 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,11 +1,14 @@ -CFLAGS=-std=c99 -Wall +CFLAGS=-std=c99 -Wall -Wstrict-prototypes -Wmissing-prototypes -Wno-unused-parameter LibAV=`pkg-config --cflags --libs libavcodec` LIBAV_DECODE_FLAGS=$(shell pkg-config --libs --cflags libavformat libavcodec libavutil libswscale) -all: advtime decode_frame +all: advtime decode_frame frame_to_pgm advtime: advtime.c gcc ${LibAV} advtime.c -o $@ decode_frame: decode_frame.c gcc $(CFLAGS) $(LIBAV_DECODE_FLAGS) decode_frame.c -o $@ + +frame_to_pgm: frame_to_pgm.c video_decode.c + gcc $(CFLAGS) $(LIBAV_DECODE_FLAGS) frame_to_pgm.c video_decode.c -o $@ diff --git a/src/frame_to_pgm.c b/src/frame_to_pgm.c new file mode 100644 index 0000000..f018099 --- /dev/null +++ b/src/frame_to_pgm.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "video_decode.h" + +static void +pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, char *filename) +{ + FILE *f; + int i; + + f = fopen(filename, "w"); + fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); + for(i=0; i < ysize; i++) + fwrite(buf + i * wrap, 1, xsize, f); + fclose(f); +} + +int +main(int argc, char **argv) +{ + struct video_decode *vd; + struct video_frame *frame; + int ret; + int64_t timestamp = 0; + + if (argc < 3) + return 1; + + if (argc == 4 && strtol(argv[3], NULL, 10) > 0) + timestamp = strtol(argv[3], NULL, 10); + + ret = video_decode_init(&vd, argv[1], timestamp); + if (ret < 0) + return -ret; + + ret = video_decode_get_frame(vd, &frame); + if (ret < 0) + return -ret; + + pgm_save(frame->data, frame->stride, frame->width, frame->height, + argv[2]); +} 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); +} diff --git a/src/video_decode.h b/src/video_decode.h new file mode 100644 index 0000000..64127f3 --- /dev/null +++ b/src/video_decode.h @@ -0,0 +1,19 @@ +#include <stdint.h> + +struct video_decode; +struct video_frame { + uint8_t *data; + int width, height, stride; +}; + +int +video_decode_init(struct video_decode **vd, char *file, int64_t timestamp); + +void +video_decode_uninit(struct video_decode **vd); + +int +video_decode_get_frame(struct video_decode *vd, struct video_frame **frame); + +void +video_decode_free_frame(struct video_frame **frame); |