#ifndef DEBUG #define DEBUG 1 #endif #include "video_decode.h" #include #include #include #include #include struct video_frame_priv { struct video_frame base; AVFrame *frame; }; struct video_decode_priv { struct video_decode base; AVFormatContext *fmt_ctx; AVCodecContext *codec_ctx; AVCodec *codec; int stream; }; int video_decode_init(struct video_decode **vd_out, char *file, int64_t ts) { struct video_decode_priv *vd; vd = calloc(1, sizeof *vd); if (vd == NULL) return -1; av_register_all(); #if DEBUG == 0 av_log_set_level(AV_LOG_QUIET); #endif 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->base.duration = vd->fmt_ctx->streams[vd->stream]->duration; *vd_out = &vd->base; return 0; } void video_decode_uninit(struct video_decode **_vd) { struct video_decode_priv *vd = (struct video_decode_priv *) *_vd; *_vd = NULL; avcodec_close(vd->codec_ctx); #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0) avformat_close_input(&vd->fmt_ctx); #else av_close_input_file(vd->fmt_ctx); #endif free(vd); } int video_decode_seek(struct video_decode *_vd, int64_t timestamp) { struct video_decode_priv *vd = (struct video_decode_priv *) _vd; if (vd->fmt_ctx->start_time != AV_NOPTS_VALUE) timestamp += vd->fmt_ctx->start_time; return avformat_seek_file(vd->fmt_ctx, vd->stream, INT64_MIN, timestamp, INT64_MAX,0); } int video_decode_get_frame(struct video_decode *_vd, struct video_frame **frame) { struct video_decode_priv *vd = (struct video_decode_priv *) _vd; struct video_frame_priv *f; AVPacket packet; int decoded, got_picture = 0; int ret; if (*frame != NULL) { f = (struct video_frame_priv *) *frame; } else { f = calloc(1, sizeof *f); if (f == NULL) return -1; f->frame = avcodec_alloc_frame(); } packet.data = NULL; do { ret = av_read_frame(vd->fmt_ctx, &packet); if (ret < 0) { if (ret == AVERROR_EOF) { return 0; } #if DEBUG else { char buf[BUFSIZ]; av_strerror(ret, buf, BUFSIZ); fprintf(stderr, "Failed to read frame: %s\n", buf); } #endif return -1; } if (packet.stream_index == vd->stream) { #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"); av_free_packet(&packet); return -2; } #if DEBUG printf("bytes_decoded: %d, got_picture: %d\n", decoded, got_picture); #endif } av_free_packet(&packet); } while (got_picture == 0); 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]; f->base.dts = vd->fmt_ctx->streams[vd->stream]->cur_dts; if (vd->fmt_ctx->start_time != AV_NOPTS_VALUE) f->base.dts -= vd->fmt_ctx->start_time; if (*frame == NULL) *frame = &f->base; return 1; } 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); }