uploads: disallow more video formats not supported by all browsers.
Disallow uploading videos with 10-bit color or 4:4:4 chroma subsampling. Neither of these features are supported by Firefox. Only 8 such videos have been uploaded to Danbooru: * https://danbooru.donmai.us/media_assets/3070695 (4:4:4) * https://danbooru.donmai.us/media_assets/3070697 (4:4:4) * https://danbooru.donmai.us/media_assets/3292518 (4:4:4) * https://danbooru.donmai.us/media_assets/3358659 (10-bit) * https://danbooru.donmai.us/media_assets/3358660 (10-bit) * https://danbooru.donmai.us/media_assets/3730866 (10-bit) * https://danbooru.donmai.us/media_assets/5056665 (10-bit) * https://danbooru.donmai.us/media_assets/5479605 (4:4:4) Note that Exiftool doesn't output this information, so it's not in the EXIF metadata. We have to reply on ffprobe at upload time instead. Followup to #3615.
This commit is contained in:
@@ -83,6 +83,10 @@ class FFmpeg
|
|||||||
frame_count / duration
|
frame_count / duration
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pix_fmt
|
||||||
|
video_stream[:pix_fmt]
|
||||||
|
end
|
||||||
|
|
||||||
def video_codec
|
def video_codec
|
||||||
video_stream[:codec_name]
|
video_stream[:codec_name]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
# @see https://github.com/streamio/streamio-ffmpeg
|
# @see https://github.com/streamio/streamio-ffmpeg
|
||||||
class MediaFile::Video < MediaFile
|
class MediaFile::Video < MediaFile
|
||||||
delegate :duration, :frame_count, :frame_rate, :has_audio?, :video_codec, :video_stream, :video_streams, :audio_streams, to: :video
|
delegate :duration, :frame_count, :frame_rate, :has_audio?, :pix_fmt, :video_codec, :video_stream, :video_streams, :audio_streams, to: :video
|
||||||
|
|
||||||
def dimensions
|
def dimensions
|
||||||
[video.width, video.height]
|
[video.width, video.height]
|
||||||
@@ -21,6 +21,17 @@ class MediaFile::Video < MediaFile
|
|||||||
return false if is_webm? && metadata["Matroska:DocType"] != "webm"
|
return false if is_webm? && metadata["Matroska:DocType"] != "webm"
|
||||||
return false if is_mp4? && !video_codec.in?(["h264", "vp9"])
|
return false if is_mp4? && !video_codec.in?(["h264", "vp9"])
|
||||||
|
|
||||||
|
# Only allow pixel formats supported by most browsers. Don't allow 10-bit video or 4:4:4 subsampling (neither are supported by Firefox).
|
||||||
|
#
|
||||||
|
# yuv420p: 8-bit YUV, 4:2:0 subsampling. The vast majority of videos use this format.
|
||||||
|
# yuvj420p: 8-bit YUV, 4:2:0 subsampling, color range restricted to 16-235. Uncommon, but widely supported.
|
||||||
|
# yuv444p: 8-bit YUV, 4:4:4 subsampling (i.e. no subsampling). Uncommon, not supported by Firefox.
|
||||||
|
# yuv420p10le: 10-bit YUV, 4:2:0 subsampling (i.e. 10-bit video). Uncommon, not supported by Firefox.
|
||||||
|
# gbrp: 8-bit RGB (used by VP9). Uncommon, but widely supported.
|
||||||
|
#
|
||||||
|
# https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/pixfmt.h
|
||||||
|
return false if !pix_fmt.in?(%w[yuv420p yuvj420p gbrp])
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
6
test/files/mp4/README.md
Normal file
6
test/files/mp4/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Test file sources:
|
||||||
|
|
||||||
|
* https://danbooru.donmai.us/posts/2878908 (https://twitter.com/chan_co/status/913025965604749314)
|
||||||
|
* https://danbooru.donmai.us/posts/4270949 (https://twitter.com/chrone_co/status/1342709028401643520)
|
||||||
|
* https://danbooru.donmai.us/posts/5152189 (https://twitter.com/001_31_/status/1491405055563792386)
|
||||||
|
* https://github.com/jursonovicst/gradient/blob/master/player_validation_sequences/test_yuv420p10le_x265.mp4
|
||||||
BIN
test/files/mp4/test-300x300-av1.mp4
Normal file
BIN
test/files/mp4/test-300x300-av1.mp4
Normal file
Binary file not shown.
BIN
test/files/mp4/test-300x300-vp9.mp4
Normal file
BIN
test/files/mp4/test-300x300-vp9.mp4
Normal file
Binary file not shown.
BIN
test/files/mp4/test-300x300-yuv444p-h264.mp4
Normal file
BIN
test/files/mp4/test-300x300-yuv444p-h264.mp4
Normal file
Binary file not shown.
BIN
test/files/mp4/test-300x300-yuvj420p-h264.mp4
Normal file
BIN
test/files/mp4/test-300x300-yuvj420p-h264.mp4
Normal file
Binary file not shown.
BIN
test/files/mp4/test-yuv420p10le-av1.mp4
Normal file
BIN
test/files/mp4/test-yuv420p10le-av1.mp4
Normal file
Binary file not shown.
BIN
test/files/mp4/test-yuv420p10le-h264.mp4
Normal file
BIN
test/files/mp4/test-yuv420p10le-h264.mp4
Normal file
Binary file not shown.
BIN
test/files/mp4/test-yuv420p10le-vp9.mp4
Normal file
BIN
test/files/mp4/test-yuv420p10le-vp9.mp4
Normal file
Binary file not shown.
BIN
test/files/webm/test-gbrp-vp9.webm
Normal file
BIN
test/files/webm/test-gbrp-vp9.webm
Normal file
Binary file not shown.
BIN
test/files/webm/test-yuv420p10le-vp9.webm
Normal file
BIN
test/files/webm/test-yuv420p10le-vp9.webm
Normal file
Binary file not shown.
@@ -243,12 +243,37 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "fail for a .mp4 file encoded with h265" do
|
should "fail for a .mp4 file encoded with h265" do
|
||||||
create_upload!("test/files/mp4/test-300x300.h265.mp4", user: @user)
|
create_upload!("test/files/mp4/test-300x300-h265.mp4", user: @user)
|
||||||
assert_match("File type is not supported", Upload.last.error)
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "fail for a .mp4 file encoded with av1" do
|
should "fail for a .mp4 file encoded with av1" do
|
||||||
create_upload!("test/files/mp4/test-300x300.av1.mp4", user: @user)
|
create_upload!("test/files/mp4/test-300x300-av1.mp4", user: @user)
|
||||||
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "fail for a 10-bit color .mp4 file encoded with av1" do
|
||||||
|
create_upload!("test/files/mp4/test-yuv420p10le-av1.mp4", user: @user)
|
||||||
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "fail for a 10-bit color .mp4 file encoded with h264" do
|
||||||
|
create_upload!("test/files/mp4/test-yuv420p10le-h264.mp4", user: @user)
|
||||||
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "fail for a 10-bit color .mp4 file encoded with vp9" do
|
||||||
|
create_upload!("test/files/mp4/test-yuv420p10le-vp9.mp4", user: @user)
|
||||||
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "fail for a 4:4:4 subsampled .mp4 file" do
|
||||||
|
create_upload!("test/files/mp4/test-300x300-yuv444p-h264.mp4", user: @user)
|
||||||
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "fail for a 10-bit color .webm file encoded with vp9" do
|
||||||
|
create_upload!("test/files/webm/test-yuv420p10le-vp9.webm", user: @user)
|
||||||
assert_match("File type is not supported", Upload.last.error)
|
assert_match("File type is not supported", Upload.last.error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -335,7 +360,7 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
assert_equal([550, 368], full_variant.dimensions)
|
assert_equal([550, 368], full_variant.dimensions)
|
||||||
assert_equal(:jpg, full_variant.file_ext)
|
assert_equal(:jpg, full_variant.file_ext)
|
||||||
|
|
||||||
assert_equal(nil, media_asset.variant(:sample))
|
assert_nil(media_asset.variant(:sample))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -345,10 +370,12 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
should_upload_successfully("test/files/test-static-32x32.gif")
|
should_upload_successfully("test/files/test-static-32x32.gif")
|
||||||
should_upload_successfully("test/files/test-animated-86x52.gif")
|
should_upload_successfully("test/files/test-animated-86x52.gif")
|
||||||
should_upload_successfully("test/files/mp4/test-300x300.mp4")
|
should_upload_successfully("test/files/mp4/test-300x300.mp4")
|
||||||
should_upload_successfully("test/files/mp4/test-300x300.vp9.mp4")
|
should_upload_successfully("test/files/mp4/test-300x300-vp9.mp4")
|
||||||
|
should_upload_successfully("test/files/mp4/test-300x300-yuvj420p-h264.mp4")
|
||||||
should_upload_successfully("test/files/mp4/test-audio.mp4")
|
should_upload_successfully("test/files/mp4/test-audio.mp4")
|
||||||
should_upload_successfully("test/files/mp4/test-audio.m4v")
|
should_upload_successfully("test/files/mp4/test-audio.m4v")
|
||||||
should_upload_successfully("test/files/webm/test-512x512.webm")
|
should_upload_successfully("test/files/webm/test-512x512.webm")
|
||||||
|
should_upload_successfully("test/files/webm/test-gbrp-vp9.webm")
|
||||||
# should_upload_successfully("test/files/compressed.swf")
|
# should_upload_successfully("test/files/compressed.swf")
|
||||||
|
|
||||||
should_upload_successfully("test/files/avif/fox.profile0.8bpc.yuv420.monochrome.avif")
|
should_upload_successfully("test/files/avif/fox.profile0.8bpc.yuv420.monochrome.avif")
|
||||||
|
|||||||
@@ -214,16 +214,36 @@ class MediaFileTest < ActiveSupport::TestCase
|
|||||||
assert_equal(10, file.frame_count)
|
assert_equal(10, file.frame_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "determine the pixel format of the video" do
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-300x300-av1.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-300x300-h265.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-300x300-vp9.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-300x300.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-audio.m4v").pix_fmt)
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-audio.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p", MediaFile.open("test/files/mp4/test-iso5.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv444p", MediaFile.open("test/files/mp4/test-300x300-yuv444p-h264.mp4").pix_fmt)
|
||||||
|
assert_equal("yuvj420p", MediaFile.open("test/files/mp4/test-300x300-yuvj420p-h264.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p10le", MediaFile.open("test/files/mp4/test-yuv420p10le-av1.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p10le", MediaFile.open("test/files/mp4/test-yuv420p10le-h264.mp4").pix_fmt)
|
||||||
|
assert_equal("yuv420p10le", MediaFile.open("test/files/mp4/test-yuv420p10le-vp9.mp4").pix_fmt)
|
||||||
|
end
|
||||||
|
|
||||||
should "detect corrupt videos" do
|
should "detect corrupt videos" do
|
||||||
assert_equal(true, MediaFile.open("test/files/mp4/test-corrupt.mp4").is_corrupt?)
|
assert_equal(true, MediaFile.open("test/files/mp4/test-corrupt.mp4").is_corrupt?)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "detect supported files" do
|
should "detect supported files" do
|
||||||
assert_equal(true, MediaFile.open("test/files/mp4/test-300x300.mp4").is_supported?)
|
assert_equal(true, MediaFile.open("test/files/mp4/test-300x300.mp4").is_supported?)
|
||||||
assert_equal(true, MediaFile.open("test/files/mp4/test-300x300.vp9.mp4").is_supported?)
|
assert_equal(true, MediaFile.open("test/files/mp4/test-300x300-vp9.mp4").is_supported?)
|
||||||
|
assert_equal(true, MediaFile.open("test/files/mp4/test-300x300-yuvj420p-h264.mp4").is_supported?)
|
||||||
|
|
||||||
assert_equal(false, MediaFile.open("test/files/mp4/test-300x300.h265.mp4").is_supported?)
|
assert_equal(false, MediaFile.open("test/files/mp4/test-300x300-h265.mp4").is_supported?)
|
||||||
assert_equal(false, MediaFile.open("test/files/mp4/test-300x300.av1.mp4").is_supported?)
|
assert_equal(false, MediaFile.open("test/files/mp4/test-300x300-av1.mp4").is_supported?)
|
||||||
|
assert_equal(false, MediaFile.open("test/files/mp4/test-300x300-yuv444p-h264.mp4").is_supported?)
|
||||||
|
assert_equal(false, MediaFile.open("test/files/mp4/test-yuv420p10le-av1.mp4").is_supported?)
|
||||||
|
assert_equal(false, MediaFile.open("test/files/mp4/test-yuv420p10le-h264.mp4").is_supported?)
|
||||||
|
assert_equal(false, MediaFile.open("test/files/mp4/test-yuv420p10le-vp9.mp4").is_supported?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -235,10 +255,12 @@ class MediaFileTest < ActiveSupport::TestCase
|
|||||||
assert_equal(10, file.frame_count)
|
assert_equal(10, file.frame_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "not detect .mkv files as .webm" do
|
should "detect supported files" do
|
||||||
file = MediaFile.open("test/files/webm/test-512x512.mkv")
|
assert_equal(true, MediaFile.open("test/files/webm/test-512x512.webm").is_supported?)
|
||||||
|
assert_equal(true, MediaFile.open("test/files/webm/test-gbrp-vp9.webm").is_supported?)
|
||||||
|
|
||||||
assert_equal(false, file.is_supported?)
|
assert_equal(false, MediaFile.open("test/files/webm/test-512x512.mkv").is_supported?)
|
||||||
|
assert_equal(false, MediaFile.open("test/files/webm/test-yuv420p10le-vp9.webm").is_supported?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user