Skip to content

Commit 7743b28

Browse files
Documentation tweaks and re-adding dropFrame (#192)
* Add endianness to docs. Closes #181 * Clarification in transform docs. Closes #183 * Add segment note to docs. Closes #185 * Re-add dropFrame as an optional field in timecode. Closes #188 --------- Co-authored-by: [email protected] <[email protected]>
1 parent 49e81b1 commit 7743b28

File tree

12 files changed

+157
-63
lines changed

12 files changed

+157
-63
lines changed

src/main/python/camdkit/clip.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,10 @@ class Clip(CompatibleBaseModel):
128128
"constraints": TRANSFORMS,
129129
"uniqueItems": False})] = None
130130
"""A list of transforms.
131-
Transforms are composed in order with the last in the list representing
132-
the X,Y,Z in meters of camera sensor relative to stage origin.
131+
Transforms are composed in sequential order, starting with the first
132+
transform in the list and concluding with the last transform in the list.
133+
The compound transform contains the position (in meters) and orientation
134+
(in degrees) of the camera sensor relative to stage origin.
133135
The Z axis points upwards and the coordinate system is right-handed.
134136
Y points in the forward camera direction (when pan, tilt and roll are
135137
zero).

src/main/python/camdkit/examples.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ def _get_recommended_dynamic_clip():
152152
clip.tracker_notes = ("Example generated sample.",)
153153
# timing
154154
clip.timing_mode = (TimingMode.EXTERNAL,)
155-
clip.timing_sample_rate = (Fraction(24000, 1001),)
155+
clip.timing_sample_rate = (Fraction(24, 1),)
156156
clip.timing_timecode = (Timecode(hours=1,minutes=2,seconds=3,frames=4,
157-
frame_rate=StrictlyPositiveRational(24000, 1001)),)
157+
frame_rate=StrictlyPositiveRational(24, 1)),)
158158
# transforms
159159
v, r = _example_transform_components()
160160
clip.transforms = ((Transform(translation=v, rotation=r, id="Camera"),),)
@@ -213,6 +213,9 @@ def _get_complete_dynamic_clip():
213213
mean_path_delay=0.000123,
214214
vlan=100)
215215
),)
216+
clip.timing_timecode = (Timecode(hours=1,minutes=2,seconds=3,frames=4,
217+
frame_rate=StrictlyPositiveRational(24000, 1001),
218+
sub_frame=1,dropFrame=True),)
216219
# transforms
217220
clip.global_stage = (GlobalPosition(100.0,200.0,300.0,100.0,200.0,300.0),)
218221
v = Vector3(x=1.0, y=2.0, z=3.0)

src/main/python/camdkit/mosys/f4.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ def to_timecode(self) -> Timecode:
8282
seconds = ((self.data_bits2 << 2) % 64) + ((self.data_bits3 >> 6) % 4)
8383
frames = self.data_bits3 % 64
8484
sub_frame = 0
85+
dropFrame = False
8586
return Timecode(hours=hours,minutes=minutes,seconds=seconds,frames=frames,
86-
frame_rate=frame_rate,sub_frame=sub_frame)
87+
frame_rate=frame_rate,sub_frame=sub_frame,dropFrame=dropFrame)
8788

8889
class F4Packet:
8990
command_byte: int = 0

src/main/python/camdkit/timing_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class Timecode(CompatibleBaseModel):
7878
frames: int = Field(..., ge=0, le=119, strict=True)
7979
frame_rate: Annotated[StrictlyPositiveRational, Field(alias="frameRate")]
8080
sub_frame: Annotated[NonNegativeInt, Field(alias="subFrame", strict=True)] = 0
81+
dropFrame: bool | None = False
8182

8283
# noinspection PyNestedDecorators
8384
@field_validator("frame_rate", mode="before")

src/test/python/test_clip.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,10 @@ def test_timing_regular_parameters(self):
297297
timing_sample_rate = (sample_rate, sample_rate)
298298
timecode0 = Timecode(hours=1, minutes=2, seconds=3,frames=4,
299299
frame_rate=StrictlyPositiveRational(24, 1),
300-
sub_frame=0)
300+
sub_frame=0,dropFrame=False)
301301
timecode1= Timecode(hours=1, minutes=2, seconds=3, frames=5,
302302
frame_rate=StrictlyPositiveRational(24, 1),
303-
sub_frame=1)
303+
sub_frame=1,dropFrame=True)
304304
timing_timecode = (timecode0, timecode1)
305305
ptp = SynchronizationPTP(profile=PTPProfile.SMPTE_2059_2_2021,
306306
domain=1,
@@ -355,10 +355,10 @@ def test_timing_regular_parameters(self):
355355
{"num": 24000, "denom": 1001}))
356356
self.assertTupleEqual(clip_as_json["timing"]["timecode"], (
357357
{"hours":1, "minutes":2, "seconds":3, "frames":4,
358-
# no subFrame serialized because value was that of the default
358+
# no subFrame / dropFrame serialized because values are the defaults
359359
"frameRate": {"num": 24, "denom": 1}},
360360
{"hours": 1,"minutes": 2,"seconds": 3,"frames": 5,
361-
"frameRate": {"num": 24, "denom": 1}, "subFrame": 1}))
361+
"frameRate": {"num": 24, "denom": 1}, "subFrame": 1, "dropFrame": True}))
362362
expected_synchronization_dict = {
363363
"locked": True,
364364
"source": "ptp",

src/test/python/test_model.py

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ def test_serialize(self):
8686
clip.timing_sample_rate = (Fraction(24000, 1001), Fraction(24000, 1001))
8787
clip.timing_timecode = (Timecode(hours=1, minutes=2, seconds=3, frames=4,
8888
frame_rate=StrictlyPositiveRational(24, 1),
89-
sub_frame=0),
89+
sub_frame=0,dropFrame=False),
9090
Timecode(hours=1, minutes=2, seconds=3, frames=5,
9191
frame_rate=StrictlyPositiveRational(24, 1),
92-
sub_frame=0))
92+
sub_frame=0,dropFrame=False))
9393
sync = Synchronization(
9494
frequency=Fraction(24000, 1001),
9595
locked=True,
@@ -778,7 +778,7 @@ def test_timecode(self):
778778
with self.assertRaises(ValueError):
779779
Timecode(hours=0, minutes=0, seconds=0, frames=0,
780780
frame_rate=StrictlyPositiveRational(0,1),
781-
sub_frame=0)
781+
sub_frame=0,dropFrame=False)
782782
self.assertTrue(TimingTimecode.validate(Timecode(hours=0, minutes=0, seconds=0, frames=0,
783783
frame_rate=StrictlyPositiveRational(24, 1))))
784784
self.assertTrue(TimingTimecode.validate(Timecode(hours=1, minutes=2, seconds=3, frames=4,
@@ -787,48 +787,49 @@ def test_timecode(self):
787787
frame_rate=StrictlyPositiveRational(24, 1))))
788788
with self.assertRaises(ValueError):
789789
Timecode(hours=-1, seconds=2, minutes=3, frames=4,
790-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
790+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
791791
with self.assertRaises(ValueError):
792792
Timecode(hours=24, seconds=2, minutes=3, frames=4,
793-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
793+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
794794
with self.assertRaises(ValueError):
795795
Timecode(hours=1, seconds=-1, minutes=3, frames=4,
796-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
796+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
797797
with self.assertRaises(ValueError):
798798
Timecode(hours=1, seconds=60, minutes=3, frames=4,
799-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
799+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
800800
with self.assertRaises(ValueError):
801801
Timecode(hours=1, seconds=2, minutes=-1, frames=4,
802-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
802+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
803803
with self.assertRaises(ValueError):
804804
Timecode(hours=1, seconds=2, minutes=60, frames=4,
805-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
805+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
806806
with self.assertRaises(ValueError):
807807
Timecode(hours=1, seconds=2, minutes=3, frames=-1,
808-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
808+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
809809
with self.assertRaises(ValueError):
810810
Timecode(hours=1, seconds=2, minutes=3, frames=24,
811-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0)
811+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=0, dropFrame=False)
812812
with self.assertRaises(ValueError):
813813
Timecode(hours=1, seconds=2, minutes=3, frames=24,
814-
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=1)
814+
frame_rate=StrictlyPositiveRational(24, 1), sub_frame=1, dropFrame=False)
815815
with self.assertRaises(ValueError):
816816
Timecode(hours=1, seconds=2, minutes=3, frames=25,
817-
frame_rate=StrictlyPositiveRational(25, 1), sub_frame=1)
817+
frame_rate=StrictlyPositiveRational(25, 1), sub_frame=1, dropFrame=False)
818818
with self.assertRaises(ValueError):
819819
Timecode(hours=1, seconds=2, minutes=3, frames=30,
820-
frame_rate=StrictlyPositiveRational(30, 1), sub_frame=1)
820+
frame_rate=StrictlyPositiveRational(30, 1), sub_frame=1, dropFrame=False)
821821
with self.assertRaises(ValueError):
822822
Timecode(hours=1, seconds=2, minutes=3, frames=30,
823-
frame_rate=StrictlyPositiveRational(30, 1), sub_frame=1)
823+
frame_rate=StrictlyPositiveRational(30, 1), sub_frame=1, dropFrame=False)
824824
self.assertTrue(TimingTimecode.validate(Timecode(hours=1, minutes=2, seconds=3, frames=119,
825-
frame_rate=StrictlyPositiveRational(120, 1), sub_frame=0)))
825+
frame_rate=StrictlyPositiveRational(120, 1),
826+
sub_frame=0, dropFrame=False)))
826827
with self.assertRaises(ValueError):
827828
Timecode(hours=1, seconds=2, minutes=3, frames=120,
828-
frame_rate=StrictlyPositiveRational(120, 1), sub_frame=0)
829+
frame_rate=StrictlyPositiveRational(120, 1), sub_frame=0, dropFrame=False)
829830
with self.assertRaises(ValueError):
830831
Timecode(hours=1, seconds=2, minutes=3, frames=120,
831-
frame_rate=StrictlyPositiveRational(121, 1), sub_frame=0)
832+
frame_rate=StrictlyPositiveRational(121, 1), sub_frame=0, dropFrame=False)
832833

833834
def test_timecode_from_dict(self):
834835
r = TimingTimecode.from_json({
@@ -841,16 +842,17 @@ def test_timecode_from_dict(self):
841842
"denom": 1
842843
},
843844
"subFrame": 0,
845+
"dropFrame": False,
844846
})
845847
self.assertEqual(str(r), str(Timecode(hours=1, minutes=2, seconds=3, frames=4,
846848
frame_rate=StrictlyPositiveRational(24, 1),
847-
sub_frame=0)))
849+
sub_frame=0, dropFrame=False)))
848850

849851
def test_timecode_to_dict(self):
852+
# If the explicit sub-frame or drop-frame is the default, it isn't serialized
850853
j0 = TimingTimecode.to_json(Timecode(hours=1, minutes=2, seconds=3, frames=4,
851854
frame_rate=StrictlyPositiveRational(24, 1),
852-
sub_frame=0))
853-
# since the explicit sub_frame of zero matches the default, it isn't serialized
855+
sub_frame=0, dropFrame=False))
854856
self.assertDictEqual(j0, {
855857
"hours": 1,
856858
"minutes": 2,
@@ -862,7 +864,7 @@ def test_timecode_to_dict(self):
862864
}})
863865
j1 = TimingTimecode.to_json(Timecode(hours=1, minutes=2, seconds=3, frames=4,
864866
frame_rate=StrictlyPositiveRational(24, 1),
865-
sub_frame=1))
867+
sub_frame=1, dropFrame=False))
866868
self.assertDictEqual(j1, {
867869
"hours": 1,
868870
"minutes": 2,
@@ -873,6 +875,20 @@ def test_timecode_to_dict(self):
873875
"denom": 1
874876
},
875877
"subFrame": 1})
878+
j2 = TimingTimecode.to_json(Timecode(hours=1, minutes=2, seconds=3, frames=4,
879+
frame_rate=StrictlyPositiveRational(24, 1),
880+
sub_frame=1, dropFrame=True))
881+
self.assertDictEqual(j2, {
882+
"hours": 1,
883+
"minutes": 2,
884+
"seconds": 3,
885+
"frames": 4,
886+
"frameRate": {
887+
"num": 24,
888+
"denom": 1
889+
},
890+
"subFrame": 1,
891+
"dropFrame": True})
876892

877893
def test_lens_encoders_limits(self):
878894
clip = Clip()

src/test/python/test_mosys_reader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def test_reader(self):
3535
self.assertEqual(str(clip.timing_timecode[8]),
3636
str(Timecode(hours=15, minutes=3, seconds=47, frames=10,
3737
frame_rate=StrictlyPositiveRational(25,1),
38-
sub_frame=0)))
38+
sub_frame=0, dropFrame=False)))
3939
self.assertEqual(clip.transforms[9][0].translation, Vector3(x=-8.121, y=-185.368, z=119.806))
4040
self.assertEqual(clip.transforms[10][0].rotation, Rotator3(pan=-2.969, tilt=-28.03, roll=3.1))
4141
self.assertEqual(clip.lens_encoders[11], FizEncoders(focus=0.7643280029296875, zoom=0.0014190673828125))

0 commit comments

Comments
 (0)