1
1
from __future__ import annotations
2
2
3
+ from dataclasses import dataclass
3
4
import warnings
4
5
from copy import deepcopy
5
6
from itertools import count
12
13
'cut_array_on_ranges'
13
14
]
14
15
16
+ @dataclass
17
+ class Field :
18
+ n : int
19
+ is_tf : bool
20
+ is_repeat : bool
21
+ prog : bool
22
+ user_data : object
15
23
16
- def apply_rff_array ( old_array : Sequence [ T ], rff : Sequence [int ], tff : Sequence [int ], prog_seq : Sequence [int ]) -> list [T ]:
17
- array_double_rate = list [T ]()
24
+ def rff_frames_to_fields ( rff : list [ int ], tff : list [int ], prog : list [int ], prog_seq : list [int ], user_data : list [T ]) :
25
+ fields = list [Field ]()
18
26
19
- for prog , arr , rffv , tffv in zip (prog_seq , old_array , rff , tff ):
20
- repeat_amount = (3 if rffv else 2 ) if prog == 0 else ((6 if tffv else 4 ) if rffv else 2 )
21
-
22
- array_double_rate .extend ([arr ] * repeat_amount )
23
-
24
- # assert (len(array_double_rate) % 2) == 0
25
- if (len (array_double_rate ) % 2 ) != 0 :
26
- warnings .warn ('uneven amount of fields removing last\n ' )
27
- array_double_rate = array_double_rate [:- 1 ]
28
-
29
- # It seems really weird thats its allowed to have rff stuff across
30
- # vob boundries even for multi angle stuff i have seen this so often though it is ok to remove the warnings
31
- # for i, f1, f2 in zip(count(), array_double_rate[::2], array_double_rate[1::2]):
32
- # if f1 != f2:
33
- # warnings.warn(
34
- # f'Ambiguous pattern due to rff {f1}!={f2} on index {i}\n'
35
- # 'This probably just means telecine happened across chapters boundary.'
36
- # )
37
-
38
- return array_double_rate [::2 ]
39
-
40
-
41
- def apply_rff_video (
42
- node : vs .VideoNode , rff : list [int ], tff : list [int ], prog : list [int ], prog_seq : list [int ]
43
- ) -> vs .VideoNode :
44
- assert len (node ) == len (rff ) == len (tff ) == len (prog ) == len (prog_seq )
45
-
46
- fields = list [dict [str , int ]]()
47
- tfffs = node .std .RemoveFrameProps (['_FieldBased' , '_Field' ]).std .SeparateFields (True )
48
-
49
- for i , current_prg_seq , current_prg , current_rff , current_tff in zip (count (), prog_seq , prog , rff , tff ):
27
+ for i , current_prg_seq , current_prg , current_rff , current_tff ,current_ud in zip (count (), prog_seq , prog , rff , tff , user_data ):
50
28
if not current_prg_seq :
51
- if current_tff :
52
- first_field = 2 * i
53
- second_field = 2 * i + 1
54
- else :
55
- first_field = 2 * i + 1
56
- second_field = 2 * i
29
+ first_field = [2 * i + 1 , 2 * i + 0 ][current_tff ]
30
+ second_field = [2 * i + 0 , 2 * i + 1 ][current_tff ]
57
31
58
32
fields += [
59
- { 'n' : first_field , 'tf' : current_tff , 'prg' : False , 'repeat' : False } ,
60
- { 'n' : second_field , 'tf' : not current_tff , 'prg' : False , 'repeat' : False }
33
+ Field ( first_field , current_tff , False ,False , current_ud ) ,
34
+ Field ( second_field , not current_tff , False ,False , current_ud )
61
35
]
62
36
63
37
if current_rff :
64
38
assert current_prg
65
39
repeat_field = deepcopy (fields [- 2 ])
66
- repeat_field [ 'repeat' ] = True
40
+ repeat_field . is_repeat = True
67
41
fields .append (repeat_field )
68
42
else :
69
43
assert current_prg
@@ -72,37 +46,63 @@ def apply_rff_video(
72
46
if current_rff :
73
47
field_count += 1 + int (current_tff )
74
48
49
+ #maybe set is_repeat even for progressive repeats ?
75
50
fields += [
76
- { 'n' : 2 * i , 'tf' : 1 , 'prg' : True , 'repeat' : False } ,
77
- { 'n' : 2 * i + 1 , 'tf' : 0 , 'prg' : True , 'repeat' : False }
51
+ Field ( 2 * i , True , False , True , current_ud ) ,
52
+ Field ( 2 * i + 1 , False , False , True ,current_ud ),
78
53
] * field_count
79
54
80
- # TODO: mark known progressive frames as progressive
55
+ #There might be a need to make this adjustable
56
+ fixmode_invalid_tff_parity : int = 1
57
+
58
+
59
+ a = 0
60
+ while a < (len (fields ) // 2 ) * 2 :
61
+ tf = fields [a ]
62
+ bf = fields [a + 1 ]
63
+ if tf .is_tf == bf .is_tf :
64
+ warnings .warn (f'Invalid field transition at { a / 2 } { tf } { bf } ' )
65
+
66
+ if fixmode_invalid_tff_parity == 0 :
67
+ bf .is_tf = not bf .is_tf
68
+ else :
69
+ fc = deepcopy (tf )
70
+ fc .is_tf = not fc .is_tf
71
+ fields .insert (a + 1 ,fc )
72
+ a += 2
81
73
82
- # assert (len(fields) % 2) == 0
83
74
if (len (fields ) % 2 ) != 0 :
84
75
warnings .warn ('uneven amount of fields removing last\n ' )
85
76
fields = fields [:- 1 ]
86
77
87
- for a , tf , bf in zip (count (), fields [::2 ], fields [1 ::2 ]):
88
- if tf ['tf' ] == bf ['tf' ]:
89
- bf ['tf' ] = not bf ['tf' ]
78
+ return fields
79
+
80
+
81
+
82
+ def apply_rff_array (old_array : list [T ], rff : list [int ], tff : list [int ], prog : list [int ], prog_seq : list [int ]) -> list [T ]:
83
+ return list ([f .user_data for f in rff_frames_to_fields (rff ,tff ,prog ,prog_seq ,old_array )][1 ::2 ])
90
84
91
- warnings .warn (f'Invalid field transition at { a } ' )
85
+ def apply_rff_video (
86
+ node : vs .VideoNode , rff : list [int ], tff : list [int ], prog : list [int ], prog_seq : list [int ]
87
+ ) -> vs .VideoNode :
88
+ assert len (node ) == len (rff ) == len (tff ) == len (prog ) == len (prog_seq )
89
+
90
+ tfffs = node .std .RemoveFrameProps (['_FieldBased' , '_Field' ]).std .SeparateFields (True )
91
+ fields = rff_frames_to_fields (rff ,tff ,prog ,prog_seq ,list (range (len (tff ))))
92
92
93
93
for fcurr , fnext in zip (fields [::2 ], fields [1 ::2 ]):
94
- if fcurr [ 'tf' ] == fnext [ 'tf' ] :
94
+ if fcurr . is_tf == fnext . is_tf :
95
95
raise CustomRuntimeError (
96
- f'Found invalid stream with two consecutive { "top" if fcurr [ "tf" ] else "bottom" } fields!'
96
+ f'Found invalid stream with two consecutive { "top" if fcurr . is_tf else "bottom" } fields!'
97
97
)
98
98
99
- final = remap_frames (tfffs , [x [ 'n' ] for x in fields ])
99
+ final = remap_frames (tfffs , [x . n for x in fields ])
100
100
101
101
def _set_field (n : int , f : vs .VideoFrame ) -> vs .VideoFrame :
102
102
f = f .copy ()
103
103
104
104
f .props .pop ('_FieldBased' , None )
105
- f .props ._Field = fields [n ][ 'tf' ]
105
+ f .props ._Field = fields [n ]. is_tf
106
106
107
107
return f
108
108
@@ -112,9 +112,9 @@ def _set_field(n: int, f: vs.VideoFrame) -> vs.VideoFrame:
112
112
113
113
def _set_repeat (n : int , f : vs .VideoFrame ) -> vs .VideoFrame :
114
114
f = f .copy ()
115
- if fields [n * 2 ][ 'repeat' ] :
115
+ if fields [n * 2 ]. is_repeat :
116
116
f .props ['RepeatedField' ] = 1
117
- elif fields [n * 2 + 1 ][ 'repeat' ] :
117
+ elif fields [n * 2 + 1 ]. is_repeat :
118
118
f .props ['RepeatedField' ] = 0
119
119
else :
120
120
f .props ['RepeatedField' ] = - 1
@@ -129,7 +129,7 @@ def _update_progressive(n: int, f: vs.VideoFrame) -> vs.VideoFrame:
129
129
tf = fields [n * 2 ]
130
130
bf = fields [n * 2 + 1 ]
131
131
132
- if tf [ 'prg' ] and bf [ 'prg' ] :
132
+ if tf . prog and bf . prog :
133
133
fout .props ['_FieldBased' ] = 0
134
134
135
135
return fout
0 commit comments