2020-02-04 23:32:59 +00:00
|
|
|
#include <libavformat/avformat.h>
|
2020-05-27 01:35:11 +00:00
|
|
|
#include <libavutil/pixdesc.h>
|
2020-10-23 05:14:03 +00:00
|
|
|
#include <libavutil/pixfmt.h>
|
2020-02-04 23:32:59 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "validation.h"
|
|
|
|
|
|
|
|
const AVRational t_1h = { 3600, 1 };
|
|
|
|
const AVRational t_0h = { 0, 1 };
|
|
|
|
|
2020-05-27 05:04:23 +00:00
|
|
|
static int validate_image_pixel_format(enum AVPixelFormat format)
|
2020-05-27 01:35:11 +00:00
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
// Still image formats
|
2020-05-27 05:04:23 +00:00
|
|
|
case AV_PIX_FMT_GBRAP:
|
2020-05-27 04:35:37 +00:00
|
|
|
case AV_PIX_FMT_MONOBLACK:
|
|
|
|
case AV_PIX_FMT_MONOWHITE:
|
2020-05-27 02:42:20 +00:00
|
|
|
case AV_PIX_FMT_YA16LE:
|
|
|
|
case AV_PIX_FMT_YA16BE:
|
2020-05-27 01:53:46 +00:00
|
|
|
case AV_PIX_FMT_GRAY8:
|
2020-05-27 01:59:00 +00:00
|
|
|
case AV_PIX_FMT_GRAY8A:
|
2020-05-27 02:05:48 +00:00
|
|
|
case AV_PIX_FMT_GRAY10LE:
|
|
|
|
case AV_PIX_FMT_GRAY10BE:
|
|
|
|
case AV_PIX_FMT_GRAY12LE:
|
|
|
|
case AV_PIX_FMT_GRAY12BE:
|
|
|
|
case AV_PIX_FMT_GRAY16LE:
|
|
|
|
case AV_PIX_FMT_GRAY16BE:
|
2020-05-27 01:55:16 +00:00
|
|
|
case AV_PIX_FMT_PAL8:
|
2020-05-27 01:35:11 +00:00
|
|
|
case AV_PIX_FMT_RGB8:
|
|
|
|
case AV_PIX_FMT_RGB24:
|
|
|
|
case AV_PIX_FMT_RGB32:
|
|
|
|
case AV_PIX_FMT_RGBA:
|
2020-05-27 02:05:48 +00:00
|
|
|
case AV_PIX_FMT_RGB48LE:
|
|
|
|
case AV_PIX_FMT_RGB48BE:
|
2020-05-27 02:17:47 +00:00
|
|
|
case AV_PIX_FMT_RGBA64LE:
|
|
|
|
case AV_PIX_FMT_RGBA64BE:
|
2020-05-27 01:49:05 +00:00
|
|
|
case AV_PIX_FMT_YUVJ420P:
|
|
|
|
case AV_PIX_FMT_YUVJ422P:
|
|
|
|
case AV_PIX_FMT_YUVJ444P:
|
|
|
|
case AV_PIX_FMT_YUVJ440P:
|
2020-05-27 02:53:39 +00:00
|
|
|
case AV_PIX_FMT_YUVA420P:
|
|
|
|
case AV_PIX_FMT_YUVA422P:
|
|
|
|
case AV_PIX_FMT_YUVA444P:
|
2020-05-27 05:04:23 +00:00
|
|
|
return true;
|
2020-05-27 01:35:11 +00:00
|
|
|
|
2020-05-27 05:04:23 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int validate_video_pixel_format(enum AVPixelFormat format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
2020-05-27 01:35:11 +00:00
|
|
|
// Video frame formats
|
|
|
|
case AV_PIX_FMT_YUV420P:
|
|
|
|
case AV_PIX_FMT_YUV420P10LE:
|
|
|
|
case AV_PIX_FMT_YUV422P:
|
|
|
|
case AV_PIX_FMT_YUV422P10LE:
|
|
|
|
case AV_PIX_FMT_YUV444P:
|
2020-05-27 01:49:05 +00:00
|
|
|
case AV_PIX_FMT_YUV440P:
|
2020-05-27 01:35:11 +00:00
|
|
|
case AV_PIX_FMT_YUV444P10LE:
|
|
|
|
case AV_PIX_FMT_YUV420P12LE:
|
|
|
|
case AV_PIX_FMT_YUV422P12LE:
|
|
|
|
case AV_PIX_FMT_YUV444P12LE:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int validate_audio_sample_format(enum AVSampleFormat format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
case AV_SAMPLE_FMT_FLT:
|
|
|
|
case AV_SAMPLE_FMT_FLTP:
|
|
|
|
case AV_SAMPLE_FMT_S16:
|
|
|
|
case AV_SAMPLE_FMT_S16P:
|
|
|
|
case AV_SAMPLE_FMT_S32:
|
|
|
|
case AV_SAMPLE_FMT_S32P:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 23:32:59 +00:00
|
|
|
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) {
|
2020-05-27 01:35:11 +00:00
|
|
|
AVCodecParameters *codecpar = format->streams[i]->codecpar;
|
|
|
|
|
|
|
|
if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
2020-02-04 23:32:59 +00:00
|
|
|
num_vstreams++;
|
|
|
|
vstream_idx = i;
|
2020-05-27 01:35:11 +00:00
|
|
|
} else if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
2020-02-04 23:32:59 +00:00
|
|
|
num_astreams++;
|
|
|
|
astream_idx = i;
|
2020-05-27 01:35:11 +00:00
|
|
|
} else if (codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
|
|
|
|
// Allow subtitles
|
|
|
|
} else {
|
|
|
|
printf("Unknown codec type %s\n", av_get_media_type_string(codecpar->codec_type));
|
|
|
|
return false;
|
2020-02-04 23:32:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2020-02-04 23:39:46 +00:00
|
|
|
printf("Bad video codec for WebM container (must be VP8 or VP9)\n");
|
2020-02-04 23:32:59 +00:00
|
|
|
return false;
|
|
|
|
case AV_CODEC_ID_VP8:
|
|
|
|
case AV_CODEC_ID_VP9:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2020-05-27 05:04:23 +00:00
|
|
|
if (!validate_video_pixel_format(vpar->format)) {
|
|
|
|
printf("Found unsupported pixel format %s\n", av_get_pix_fmt_name(vpar->format));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-04 23:32:59 +00:00
|
|
|
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:
|
|
|
|
;
|
|
|
|
}
|
2020-05-27 05:04:23 +00:00
|
|
|
|
|
|
|
if (!validate_audio_sample_format(apar->format)) {
|
|
|
|
printf("Found unsupported audio sample format %s\n", av_get_sample_fmt_name(apar->format));
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-04 23:32:59 +00:00
|
|
|
}
|
2020-05-16 15:14:42 +00:00
|
|
|
} else if (strcmp(iformat->name, "gif") == 0) {
|
2020-05-14 20:53:38 +00:00
|
|
|
switch (vpar->codec_id) {
|
|
|
|
default:
|
|
|
|
printf("Bad video codec for GIF container (must be GIF)\n");
|
|
|
|
return false;
|
|
|
|
case AV_CODEC_ID_GIF:
|
|
|
|
;
|
|
|
|
}
|
2020-05-27 05:04:23 +00:00
|
|
|
|
|
|
|
if (!validate_image_pixel_format(vpar->format)) {
|
|
|
|
printf("Found unsupported pixel format %s\n", av_get_pix_fmt_name(vpar->format));
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-16 17:24:32 +00:00
|
|
|
} else if (strcmp(iformat->name, "image2") == 0 || strcmp(iformat->name, "jpeg_pipe") == 0) {
|
2020-05-16 15:14:42 +00:00
|
|
|
switch (vpar->codec_id) {
|
|
|
|
default:
|
2020-05-16 17:24:32 +00:00
|
|
|
printf("Bad video codec for JPEG container (must be JPEG)\n");
|
2020-05-16 15:14:42 +00:00
|
|
|
return false;
|
|
|
|
case AV_CODEC_ID_MJPEG:
|
|
|
|
;
|
|
|
|
}
|
2020-05-27 05:04:23 +00:00
|
|
|
|
|
|
|
if (!validate_image_pixel_format(vpar->format)) {
|
|
|
|
printf("Found unsupported pixel format %s\n", av_get_pix_fmt_name(vpar->format));
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-16 15:14:42 +00:00
|
|
|
} else if (strcmp(iformat->name, "png_pipe") == 0 || strcmp(iformat->name, "apng") == 0) {
|
|
|
|
switch (vpar->codec_id) {
|
|
|
|
default:
|
|
|
|
printf("Bad video codec for PNG container (must be PNG)\n");
|
|
|
|
return false;
|
|
|
|
case AV_CODEC_ID_PNG:
|
|
|
|
case AV_CODEC_ID_APNG:
|
|
|
|
;
|
|
|
|
}
|
2020-05-27 05:04:23 +00:00
|
|
|
|
|
|
|
if (!validate_image_pixel_format(vpar->format)) {
|
|
|
|
printf("Found unsupported pixel format %s\n", av_get_pix_fmt_name(vpar->format));
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-16 15:14:42 +00:00
|
|
|
} else if (strcmp(iformat->name, "svg_pipe") == 0) {
|
|
|
|
switch (vpar->codec_id) {
|
|
|
|
default:
|
|
|
|
printf("Bad video codec for SVG container (must be SVG)\n");
|
|
|
|
return false;
|
|
|
|
case AV_CODEC_ID_SVG:
|
|
|
|
;
|
|
|
|
}
|
2020-02-04 23:32:59 +00:00
|
|
|
} else {
|
|
|
|
printf("Unknown input format\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-16 17:30:57 +00:00
|
|
|
if (vpar->width < 1 || vpar->width > 32767) {
|
|
|
|
printf("Invalid width %d\n", vpar->width);
|
2020-02-04 23:32:59 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-16 17:30:57 +00:00
|
|
|
if (vpar->height < 1 || vpar->height > 32767) {
|
|
|
|
printf("Invalid height %d\n", vpar->height);
|
2020-02-04 23:32:59 +00:00
|
|
|
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) {
|
2020-05-16 17:24:32 +00:00
|
|
|
printf("Invalid duration (must be 0..1 hour)\n");
|
2020-02-04 23:32:59 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|