1
|
|
|
import json |
2
|
|
|
import os |
3
|
|
|
import unittest |
4
|
|
|
|
5
|
|
|
import ffmpeg_streaming |
6
|
|
|
from ffmpeg_streaming import Formats |
7
|
|
|
from ffmpeg_streaming import FFProbe, Representation, Size, Bitrate |
8
|
|
|
from ffmpeg_streaming._media import HLS, DASH |
9
|
|
|
from ffmpeg_streaming.ffprobe import Streams |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
class TestStreaming(unittest.TestCase): |
13
|
|
|
|
14
|
|
|
def setUp(self): |
15
|
|
|
self.src_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files') |
16
|
|
|
self.src_video = os.path.join(self.src_dir, 'test.mp4') |
17
|
|
|
with open(os.path.join(self.src_dir, 'fixture_ffprobe')) as ffprobe: |
18
|
|
|
self.ffprobe = json.loads(ffprobe.read()) |
19
|
|
|
|
20
|
|
|
def test_ffprobe(self): |
21
|
|
|
ffprobe = FFProbe(self.src_video) |
22
|
|
|
|
23
|
|
|
all_media = ffprobe.all() |
24
|
|
|
|
25
|
|
|
streams = ffprobe.streams() |
26
|
|
|
self.assertIsInstance(streams, Streams) |
27
|
|
|
self.assertEqual(streams.all(), all_media['streams']) |
28
|
|
|
self.assertEqual(streams.first_stream(), all_media['streams'][0]) |
29
|
|
|
self.assertEqual(streams.video()['codec_type'], 'video') |
30
|
|
|
self.assertEqual(streams.audio()['codec_type'], 'audio') |
31
|
|
|
|
32
|
|
|
def test_hls(self): |
33
|
|
|
video = ffmpeg_streaming.input(self.src_video) |
34
|
|
|
hls_obj = video.hls(Formats.h264()) |
35
|
|
|
self.assertIsInstance(hls_obj, HLS) |
36
|
|
|
self.assertEqual(hls_obj.media.input, self.src_video) |
37
|
|
|
|
38
|
|
|
hls_obj.representations(Representation(Size(256, 144), Bitrate(102400))) |
39
|
|
|
rep_1 = hls_obj.reps[0] |
40
|
|
|
self.assertIsInstance(rep_1, Representation) |
41
|
|
|
self.assertEqual(str(rep_1.size), '256x144') |
42
|
|
|
self.assertEqual(rep_1.bitrate.video, '100k') |
43
|
|
|
|
44
|
|
|
hls_obj.auto_generate_representations() |
45
|
|
|
reps = list(hls_obj.reps) |
46
|
|
|
self.assertEqual(len(reps), 3) |
47
|
|
|
|
48
|
|
|
for rep_ in reps: |
49
|
|
|
self.assertIsInstance(rep_, Representation) |
50
|
|
|
self.assertEqual(str(reps[0].size), '480x270') |
51
|
|
|
self.assertEqual(reps[0].bitrate.video, '176k') |
52
|
|
|
self.assertEqual(str(reps[1].size), '426x240') |
53
|
|
|
self.assertEqual(reps[1].bitrate.video, '88k') |
54
|
|
|
self.assertEqual(str(reps[2].size), '256x144') |
55
|
|
|
self.assertEqual(reps[2].bitrate.video, '71k') |
56
|
|
|
|
57
|
|
|
hls_obj.output(os.path.join(self.src_dir, 'hls', 'test.m3u8')) |
58
|
|
|
with open(os.path.join(self.src_dir, 'fixture_test.m3u8')) as test_m3u8: |
59
|
|
|
expected_m3u8 = test_m3u8.read() |
60
|
|
|
with open(os.path.join(self.src_dir, 'hls', 'test.m3u8')) as test_m3u8: |
61
|
|
|
actual_m3u8 = test_m3u8.read() |
62
|
|
|
self.assertEqual(actual_m3u8, expected_m3u8) |
63
|
|
|
with open(os.path.join(self.src_dir, 'hls', 'test_270p.m3u8')) as test_m3u8: |
64
|
|
|
actual_270_m3u8 = test_m3u8.readlines() |
65
|
|
|
self.assertEqual(actual_270_m3u8[0].replace('\n', ''), '#EXTM3U') |
66
|
|
|
self.assertEqual(actual_270_m3u8[1].replace('\n', ''), '#EXT-X-VERSION:3') |
67
|
|
|
self.assertEqual(actual_270_m3u8[2].replace('\n', ''), '#EXT-X-ALLOW-CACHE:YES') |
68
|
|
|
|
69
|
|
|
def test_encrypted_hls(self): |
70
|
|
|
video = ffmpeg_streaming.input(self.src_video) |
71
|
|
|
hls_obj = video.hls(Formats.h264()) |
72
|
|
|
hls_obj.encryption(os.path.join(self.src_dir, 'enc.key'), 'https://www.aminyazdanpanah.com/enc.key') |
73
|
|
|
|
74
|
|
|
self.assertIsNotNone(hls_obj.options.get('hls_key_info_file', None)) |
75
|
|
|
|
76
|
|
|
with open(hls_obj.options.get('hls_key_info_file', None)) as key_info: |
77
|
|
|
key_info = key_info.readlines() |
78
|
|
|
self.assertEqual(key_info[0].replace('\n', ''), 'https://www.aminyazdanpanah.com/enc.key') |
79
|
|
|
self.assertEqual(key_info[1].replace('\n', ''), os.path.join(self.src_dir, 'enc.key')) |
80
|
|
|
|
81
|
|
|
hls_obj.auto_generate_representations() |
82
|
|
|
|
83
|
|
|
hls_obj.output(os.path.join(self.src_dir, 'encrypted_hls', 'test.m3u8'), stderr=False) |
84
|
|
|
with open(os.path.join(self.src_dir, 'fixture_test.m3u8')) as test_m3u8: |
85
|
|
|
expected_m3u8 = test_m3u8.read() |
86
|
|
|
with open(os.path.join(self.src_dir, 'encrypted_hls', 'test.m3u8')) as test_m3u8: |
87
|
|
|
actual_encrypted_m3u8 = test_m3u8.read() |
88
|
|
|
self.assertEqual(actual_encrypted_m3u8, expected_m3u8) |
89
|
|
|
|
90
|
|
|
def test_dash(self): |
91
|
|
|
video = ffmpeg_streaming.input(self.src_video) |
92
|
|
|
dash_obj = video.dash(Formats.hevc()) |
93
|
|
|
self.assertIsInstance(dash_obj, DASH) |
94
|
|
|
self.assertEqual(dash_obj.media.input, self.src_video) |
95
|
|
|
|
96
|
|
|
dash_obj.auto_generate_representations() |
97
|
|
|
|
98
|
|
|
dash_obj.output(os.path.join(self.src_dir, 'dash', 'test.mpd'), stderr=False) |
99
|
|
|
with open(os.path.join(self.src_dir, 'dash', 'test.mpd')) as test_mpd: |
100
|
|
|
actual_mpd = test_mpd.readlines() |
101
|
|
|
self.assertEqual(actual_mpd[0].replace('\n', ''), '<?xml version="1.0" encoding="utf-8"?>') |
102
|
|
|
self.assertEqual(actual_mpd[1].replace('\n', ''), '<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"') |
103
|
|
|
|
104
|
|
|
|
105
|
|
|
if __name__ == '__main__': |
106
|
|
|
unittest.main() |
107
|
|
|
|