media validator

This commit is contained in:
byte[] 2020-02-04 18:32:59 -05:00
commit 32892e729e
5 changed files with 220 additions and 0 deletions

37
Makefile Normal file
View file

@ -0,0 +1,37 @@
RM := rm -f
CC := gcc
CFLAGS := -O3 -Wall -Isrc -I/usr/include/ffmpeg
LIBS := -lavformat -lavutil -lavcodec
LDFLAGS :=
INSTALL ?= install
PREFIX ?= /usr/local
COMMON_OBJECTS := build/validation.o
MEDIASTAT_OBJECTS := build/stat.o
# Phony rules
.PHONY: all mediastat clean
all: mediastat
install: all
$(INSTALL) build/mediastat $(PREFIX)/bin/mediastat
uninstall:
$(RM) -rf $(PREFIX)/bin/mediastat
mediastat: build/mediastat
clean:
$(RM) $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)
# Build rules
build/mediastat: $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)
$(CC) $^ $(LDFLAGS) $(LIBS) -o $@
build/%.o: src/%.c
$(CC) $(CFLAGS) -MMD -c $< -o $@
# Declare dependencies
-include $(COMMON_OBJECTS:.o=.d)
-include $(MEDIASTAT_OBJECTS:.o=.d)

3
build/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*
*/
!.gitignore

80
src/stat.c Normal file
View file

@ -0,0 +1,80 @@
#include <libavformat/avformat.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "validation.h"
int main(int argc, char *argv[])
{
AVFormatContext *format = NULL;
struct stat statbuf;
AVPacket pkt;
if (argc != 2) {
printf("No input specified\n");
return -1;
}
av_log_set_level(AV_LOG_QUIET);
if (stat(argv[1], &statbuf) != 0) {
printf("Couldn't read file\n");
return -1;
}
if (avformat_open_input(&format, argv[1], NULL, NULL) != 0) {
printf("Couldn't read file\n");
return -1;
}
if (!mediatools_validate_video(format)) {
// Error is printed by validation function
return -1;
}
if (av_seek_frame(format, -1, 0, AVSEEK_FLAG_BACKWARD) != 0) {
printf("Couldn't read file\n");
return -1;
}
int vstream_idx = av_find_best_stream(format, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (vstream_idx < 0) {
printf("Couldn't read file\n");
return -1;
}
uint64_t frames = 0;
int64_t last_pts = 0;
int last_stream = 0;
while (av_read_frame(format, &pkt) >= 0) {
int64_t new_pts = pkt.pts + pkt.duration;
if (last_pts < new_pts) {
last_pts = new_pts;
last_stream = pkt.stream_index;
}
if (pkt.stream_index == vstream_idx)
++frames;
av_packet_unref(&pkt);
}
AVRational tb = format->streams[last_stream]->time_base;
AVRational dur = av_mul_q(av_make_q(last_pts, 1), tb);
if (!mediatools_validate_duration(dur))
return -1;
AVCodecParameters *vpar = format->streams[vstream_idx]->codecpar;
printf("%ld %lu %d %d %d %d\n", statbuf.st_size, frames, vpar->width, vpar->height, dur.num, dur.den);
avformat_close_input(&format);
return 0;
}

91
src/validation.c Normal file
View file

@ -0,0 +1,91 @@
#include <libavformat/avformat.h>
#include <stdbool.h>
#include <string.h>
#include "validation.h"
const AVRational t_1h = { 3600, 1 };
const AVRational t_0h = { 0, 1 };
int mediatools_validate_video(AVFormatContext *format)
{
uint64_t num_vstreams = 0;
uint64_t num_astreams = 0;
int64_t vstream_idx = -1;
int64_t astream_idx = -1;
for (size_t i = 0; i < format->nb_streams; ++i) {
if (format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
num_vstreams++;
vstream_idx = i;
}
if (format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
num_astreams++;
astream_idx = i;
}
}
if (num_vstreams != 1) {
printf("Found %lu video streams (must be 1)\n", num_vstreams);
return false;
}
if (num_astreams > 1) {
printf("Found %lu audio streams (must be 0 or 1)\n", num_astreams);
return false;
}
AVInputFormat *iformat = format->iformat;
AVCodecParameters *vpar = format->streams[vstream_idx]->codecpar;
AVCodecParameters *apar = NULL;
if (astream_idx != -1)
apar = format->streams[astream_idx]->codecpar;
if (strstr(iformat->name, "matroska")) {
switch (vpar->codec_id) {
default:
printf("Bad video codec for WebM container (must be VP8, or VP9)\n");
return false;
case AV_CODEC_ID_VP8:
case AV_CODEC_ID_VP9:
;
}
if (apar) {
switch (apar->codec_id) {
default:
printf("Bad audio codec for WebM container (must be Opus or Vorbis)\n");
return false;
case AV_CODEC_ID_VORBIS:
case AV_CODEC_ID_OPUS:
;
}
}
} else {
printf("Unknown input format\n");
return false;
}
if (vpar->width < 32 || vpar->width > 4096) {
printf("Bad width %d (must be 32..4096)\n", vpar->width);
return false;
}
if (vpar->height < 32 || vpar->height > 4096) {
printf("Bad height %d (must be 32..4096)\n", vpar->height);
return false;
}
return true;
}
int mediatools_validate_duration(AVRational dur)
{
if (av_cmp_q(dur, t_0h) < 0 || av_cmp_q(dur, t_1h) > 0) {
printf("Bad duration (must be 0..1 hour)\n");
return false;
}
return true;
}

9
src/validation.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef _VALIDATION_H_DEFINED
#define _VALIDATION_H_DEFINED
#include <libavformat/avformat.h>
int mediatools_validate_video(AVFormatContext *format);
int mediatools_validate_duration(AVRational dur);
#endif