Merge branch 'codec-prio-from-cookie'

Addresses the desire to prefer codecs from the client.
Some clients perform better with particular codecs put above, for
instance the device has hardware decoding capabilities for it.
Unfortunately there is no reliable way to query this in the frontend.

Now we can set a cookie 'video_codecs_prio' with strings of codecs
we want the order of importance. The cookie can contain codecs
separated by a space. For instance 'vp8 h.264' to get those
codecs first for an old 2014 laptop that has only hw support for
these codecs.

Supported codecs are the distinct ones in short_name in
models.transcoding_types:

vp9
vp8
h.264

After this works well in production, I can deside to build a user-
friendly settings page.
This commit is contained in:
2022-04-07 21:04:35 +02:00
2 changed files with 244 additions and 48 deletions

View File

@@ -245,52 +245,6 @@ class VideoTestCase(UploadMixin, TestCase):
content, content,
) )
def test_video_view_renders_transcoding_types_in_correct_order(self):
video = factories.create(
models.Video,
title='Vid 1',
slug='vid-1',
default_quality='480p',
)
factories.create(
models.Transcoding,
video=video,
quality='480p',
type='video/mp4; codecs="avc1.64001e,mp4a.40.2"',
url='http://480p.mp4',
)
factories.create(
models.Transcoding,
video=video,
quality='480p',
type='video/webm; codecs="vp9, opus"',
url='http://480p.vp9.webm',
)
factories.create(
models.Transcoding,
video=video,
quality='480p',
type='video/webm; codecs="vp8, vorbis"',
url='http://480p.vp8.webm',
)
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
class VideoWithTrackTestCase(UploadMixin, TestCase): class VideoWithTrackTestCase(UploadMixin, TestCase):
def setUp(self): def setUp(self):
@@ -480,3 +434,228 @@ class VideoWithTrackTestCase(UploadMixin, TestCase):
</video>""", </video>""",
content, content,
) )
class VideoWithCodecOrderCookieTestCase(UploadMixin, TestCase):
""" Test the order of the codecs """
def setUp(self):
super().setUp()
self.client = Client()
self.video = factories.create(
models.Video,
title='Vid 1',
slug='vid-1',
default_quality='480p',
)
factories.create(
models.Transcoding,
video=self.video,
quality='480p',
type='video/mp4; codecs="avc1.64001e,mp4a.40.2"',
url='http://480p.mp4',
)
factories.create(
models.Transcoding,
video=self.video,
quality='480p',
type='video/webm; codecs="vp9, opus"',
url='http://480p.vp9.webm',
)
factories.create(
models.Transcoding,
video=self.video,
quality='480p',
type='video/webm; codecs="vp8, vorbis"',
url='http://480p.vp8.webm',
)
def test_video_view_renders_transcoding_types_in_correct_order_without_cookie(self):
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_empty_cookie(self):
self.client.cookies['video_codecs_prio'] = ''
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_only_vp8_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'vp8'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_only_h264_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'h.264'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_vp8_and_h264_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'vp8 h.264'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_h264_and_vp8_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'h.264 vp8'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_h264_and_vp9_and_vp8_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'h.264 vp9 vp8'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_h264_and_vp8_and_vp9_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'h.264 vp8 vp9'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_ignores_garbage_in_cookie(self):
self.client.cookies['video_codecs_prio'] = 'bwarpblergh crap bla'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)
def test_video_view_renders_transcoding_types_in_correct_order_with_h264_and_vp8_and_crap_and_vp9_mentioned(self):
self.client.cookies['video_codecs_prio'] = 'h.264 vp8 nonexistent-thing vp9'
resp:HttpResponse = self.client.get(reverse('video', args=['vid-1']))
self.assertEqual(resp.status_code, 200)
content:str = resp.content.decode(resp.charset)
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.mp4" type='video/mp4; codecs="avc1.64001e,mp4a.40.2"' />
<source src="http://480p.vp8.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://480p.vp9.webm" type='video/webm; codecs="vp9, opus"' />
You need a browser that understands HTML5 video and supports h.264 or vp8 or vp9 codecs.
</video>""",
content,
)

View File

@@ -44,9 +44,12 @@ def video(request: HttpRequest, slug: str) -> HttpResponse:
} }
for transcoding in quality for transcoding in quality
] ]
# sort by transcoding type priority
client_codec_prios = _get_codec_prios_from_client(request.COOKIES.get('video_codecs_prio', ''))
# sort by client desired order, or transcoding type priority
sources.sort( sources.sort(
key=lambda i: models.get_transcoding_type_by_name(i['type']).priority, key=lambda i: client_codec_prios.get(models.get_transcoding_type_by_name(i['type']).short_name) or \
models.get_transcoding_type_by_name(i['type']).priority,
reverse=True reverse=True
) )
template_data['sources'] = sources template_data['sources'] = sources
@@ -92,3 +95,17 @@ def _url_for(transcoding: models.Transcoding) -> str:
return transcoding.url return transcoding.url
elif transcoding.upload: elif transcoding.upload:
return transcoding.upload.file.url return transcoding.upload.file.url
def _get_codec_prios_from_client(prio_string: str) -> Dict[str, int]:
"""
Get prios from prio string.
For they are more important than build-in prios, they have
values higher than the max value of the build-in prios.
"""
max_prio = max(tt.priority for tt in models.transcoding_types) + 1
client_codecs_prio = prio_string.split(' ')
client_codecs_prio.reverse()
return {
v: max_prio + i
for i, v in enumerate(client_codecs_prio)
}