/* * ffcheck — verifies the vendored FFmpeg build (scripts/ffmpeg/build.sh). * * ffcheck Inventory: versions, licenses, configure line and * every enabled decoder / encoder / demuxer / muxer / * parser / bitstream filter / protocol. Exits 1 if * any library is not "LGPL version 2.1 or later" or * if any encoder or muxer is compiled in. * ffcheck --decode F... Smoke test: demux each file, decode up to 90 video * frames (and audio packets) per stream, and print an * MD5 per video frame (planes packed, align 1 — the * same bytes `ffmpeg -f framemd5` hashes for * rawvideo) so output can be compared bit-for-bit * against a reference decoder. Streams with no * decoder are named via the codec descriptor table * (always compiled in), which is how the app can * tell the user exactly what is unsupported. * * Never plays anything: decoding only, no audio or video output device. */ #include #include #include #include #include #include #include #include #include #include #include #include static const char *kExpectedLicense = "LGPL version 2.1 or later"; static int check_license(const char *lib, const char *license) { int ok = strcmp(license, kExpectedLicense) == 0; printf("license %-10s %s%s\n", lib, license, ok ? "" : " <-- NOT LGPL 2.1+"); return ok; } static int inventory(void) { int ok = 1; printf("ffmpeg %s\n", FFMPEG_VERSION); printf("libavutil %u.%u.%u\n", AV_VERSION_MAJOR(avutil_version()), AV_VERSION_MINOR(avutil_version()), AV_VERSION_MICRO(avutil_version())); printf("libavcodec %u.%u.%u\n", AV_VERSION_MAJOR(avcodec_version()), AV_VERSION_MINOR(avcodec_version()), AV_VERSION_MICRO(avcodec_version())); printf("libavformat %u.%u.%u\n", AV_VERSION_MAJOR(avformat_version()), AV_VERSION_MINOR(avformat_version()), AV_VERSION_MICRO(avformat_version())); ok &= check_license("avutil", avutil_license()); ok &= check_license("avcodec", avcodec_license()); ok &= check_license("avformat", avformat_license()); printf("configuration %s\n", avcodec_configuration()); if (strcmp(avcodec_configuration(), avformat_configuration()) || strcmp(avcodec_configuration(), avutil_configuration())) { printf("ERROR: libraries report different configurations\n"); ok = 0; } void *it = NULL; const AVCodec *c; int nenc = 0; printf("decoders:"); while ((c = av_codec_iterate(&it))) if (av_codec_is_decoder(c)) printf(" %s", c->name); printf("\nencoders:"); it = NULL; while ((c = av_codec_iterate(&it))) if (av_codec_is_encoder(c)) { printf(" %s", c->name); nenc++; } printf("%s\n", nenc ? "" : " (none)"); const AVInputFormat *ifmt; printf("demuxers:"); it = NULL; while ((ifmt = av_demuxer_iterate(&it))) printf(" %s", ifmt->name); const AVOutputFormat *ofmt; int nmux = 0; printf("\nmuxers:"); it = NULL; while ((ofmt = av_muxer_iterate(&it))) { printf(" %s", ofmt->name); nmux++; } printf("%s\n", nmux ? "" : " (none)"); const AVCodecParser *p; printf("parsers:"); it = NULL; while ((p = av_parser_iterate(&it))) for (int i = 0; i < 7 && p->codec_ids[i]; i++) printf(" %s", avcodec_get_name(p->codec_ids[i])); const AVBitStreamFilter *b; printf("\nbsfs:"); it = NULL; while ((b = av_bsf_iterate(&it))) printf(" %s", b->name); const char *proto; printf("\nprotocols (input):"); it = NULL; while ((proto = avio_enum_protocols(&it, 0))) printf(" %s", proto); printf("\nprotocols (output):"); it = NULL; while ((proto = avio_enum_protocols(&it, 1))) printf(" %s", proto); printf("\n"); if (nenc || nmux) { printf("ERROR: build is supposed to be decoder/demuxer-only\n"); ok = 0; } printf("%s\n", ok ? "OK" : "FAILED"); return ok ? 0 : 1; } static void print_frame_md5(int idx, const AVFrame *f) { int size = av_image_get_buffer_size(f->format, f->width, f->height, 1); uint8_t *buf = av_malloc(size); uint8_t digest[16]; if (!buf) return; av_image_copy_to_buffer(buf, size, (const uint8_t *const *)f->data, f->linesize, f->format, f->width, f->height, 1); av_md5_sum(digest, buf, size); printf(" frame %3d pts %8lld %c ", idx, (long long)f->pts, av_get_picture_type_char(f->pict_type)); for (int i = 0; i < 16; i++) printf("%02x", digest[i]); printf("\n"); av_free(buf); } static int decode_file(const char *path, int print_md5) { AVFormatContext *fmt = NULL; int ret = avformat_open_input(&fmt, path, NULL, NULL); printf("%s\n", path); if (ret < 0) { printf(" open failed: %s (no demuxer for this container)\n", av_err2str(ret)); return 1; } if ((ret = avformat_find_stream_info(fmt, NULL)) < 0) printf(" find_stream_info: %s\n", av_err2str(ret)); printf(" container %s (%s)\n", fmt->iformat->name, fmt->iformat->long_name); unsigned n = fmt->nb_streams; AVCodecContext **dec = calloc(n, sizeof(*dec)); int *frames = calloc(n, sizeof(int)), *errors = calloc(n, sizeof(int)); long *samples = calloc(n, sizeof(long)); for (unsigned i = 0; i < n; i++) { AVCodecParameters *par = fmt->streams[i]->codecpar; const AVCodecDescriptor *d = avcodec_descriptor_get(par->codec_id); const AVCodec *codec = avcodec_find_decoder(par->codec_id); char tag[AV_FOURCC_MAX_STRING_SIZE]; av_fourcc_make_string(tag, par->codec_tag); printf(" stream %u %s %s [%s] \"%s\" -> %s\n", i, av_get_media_type_string(par->codec_type), avcodec_get_name(par->codec_id), tag, d ? d->long_name : "?", codec ? "decoder available" : "NO DECODER (unsupported)"); if (!codec || (par->codec_type != AVMEDIA_TYPE_VIDEO && par->codec_type != AVMEDIA_TYPE_AUDIO)) continue; dec[i] = avcodec_alloc_context3(codec); avcodec_parameters_to_context(dec[i], par); dec[i]->thread_count = 0; /* auto: frame/slice threads */ if ((ret = avcodec_open2(dec[i], codec, NULL)) < 0) { printf(" open decoder failed: %s\n", av_err2str(ret)); avcodec_free_context(&dec[i]); } } AVPacket *pkt = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); const int kMaxVideoFrames = 90; int done = 0; while (!done) { ret = av_read_frame(fmt, pkt); int si = ret < 0 ? -1 : pkt->stream_index; for (unsigned i = 0; i < n; i++) { if (!dec[i] || (si >= 0 && (unsigned)si != i)) continue; if (avcodec_send_packet(dec[i], ret < 0 ? NULL : pkt) < 0) errors[i]++; while (avcodec_receive_frame(dec[i], frame) >= 0) { if (dec[i]->codec_type == AVMEDIA_TYPE_VIDEO) { if (frames[i] == 0) printf(" stream %u decodes to %dx%d %s\n", i, frame->width, frame->height, av_get_pix_fmt_name(frame->format)); if (print_md5 && frames[i] < kMaxVideoFrames) print_frame_md5(frames[i], frame); } else { if (frames[i] == 0) printf(" stream %u decodes to %d Hz %d ch %s\n", i, frame->sample_rate, frame->ch_layout.nb_channels, av_get_sample_fmt_name(frame->format)); samples[i] += frame->nb_samples; } frames[i]++; av_frame_unref(frame); } } av_packet_unref(pkt); if (ret < 0) break; done = 1; for (unsigned i = 0; i < n; i++) if (dec[i] && dec[i]->codec_type == AVMEDIA_TYPE_VIDEO && frames[i] < kMaxVideoFrames) done = 0; int any_video = 0; for (unsigned i = 0; i < n; i++) if (dec[i] && dec[i]->codec_type == AVMEDIA_TYPE_VIDEO) any_video = 1; if (!any_video) done = 0; /* audio-only: decode to EOF */ } for (unsigned i = 0; i < n; i++) { if (!dec[i]) continue; if (dec[i]->codec_type == AVMEDIA_TYPE_VIDEO) printf(" stream %u: %d video frames decoded, %d send errors\n", i, frames[i], errors[i]); else printf(" stream %u: %d audio frames / %ld samples decoded, %d send errors\n", i, frames[i], samples[i], errors[i]); avcodec_free_context(&dec[i]); } av_frame_free(&frame); av_packet_free(&pkt); free(dec); free(frames); free(errors); free(samples); avformat_close_input(&fmt); return 0; } int main(int argc, char **argv) { if (argc > 1 && (!strcmp(argv[1], "--decode") || !strcmp(argv[1], "--decode-md5"))) { int md5 = !strcmp(argv[1], "--decode-md5"); av_log_set_level(AV_LOG_ERROR); int rc = 0; for (int i = 2; i < argc; i++) rc |= decode_file(argv[i], md5); return rc; } return inventory(); }