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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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