44import logging
55import threading
66import time
7- from typing import Optional , Union
7+ from typing import Any , Optional , Union , cast
88
99import av
10+ import av .container
1011from av import AudioFrame , VideoFrame
1112from av .audio import AudioStream
1213from av .frame import Frame
3940 "x11grab" ,
4041]
4142
43+ _AudioOrVideoStream = Union [AudioStream , VideoStream ]
44+
4245
4346async def blackhole_consume (track : MediaStreamTrack ) -> None :
4447 while True :
@@ -54,7 +57,7 @@ class MediaBlackhole:
5457 """
5558
5659 def __init__ (self ) -> None :
57- self .__tracks : dict [MediaStreamTrack , asyncio .Future ] = {}
60+ self .__tracks : dict [MediaStreamTrack , Optional [ asyncio .Future ] ] = {}
5861
5962 def addTrack (self , track : MediaStreamTrack ) -> None :
6063 """
@@ -84,15 +87,15 @@ async def stop(self) -> None:
8487
8588
8689def player_worker_decode (
87- loop ,
88- container ,
89- streams ,
90- audio_track ,
91- video_track ,
92- quit_event ,
93- throttle_playback ,
94- loop_playback ,
95- ):
90+ loop : asyncio . AbstractEventLoop ,
91+ container : av . container . InputContainer ,
92+ streams : list [ _AudioOrVideoStream ] ,
93+ audio_track : "PlayerStreamTrack" ,
94+ video_track : "PlayerStreamTrack" ,
95+ quit_event : threading . Event ,
96+ throttle_playback : bool ,
97+ loop_playback : bool ,
98+ ) -> None :
9699 audio_sample_rate = 48000
97100 audio_samples = 0
98101 audio_time_base = fractions .Fraction (1 , audio_sample_rate )
@@ -156,15 +159,15 @@ def player_worker_decode(
156159
157160
158161def player_worker_demux (
159- loop ,
160- container ,
161- streams ,
162- audio_track ,
163- video_track ,
164- quit_event ,
165- throttle_playback ,
166- loop_playback ,
167- ):
162+ loop : asyncio . AbstractEventLoop ,
163+ container : av . container . InputContainer ,
164+ streams : list [ _AudioOrVideoStream ] ,
165+ audio_track : "PlayerStreamTrack" ,
166+ video_track : "PlayerStreamTrack" ,
167+ quit_event : threading . Event ,
168+ throttle_playback : bool ,
169+ loop_playback : bool ,
170+ ) -> None :
168171 video_first_pts = None
169172 frame_time = None
170173 start_time = time .time ()
@@ -222,7 +225,7 @@ class PlayerStreamTrack(MediaStreamTrack):
222225 def __init__ (self , player : "MediaPlayer" , kind : str ) -> None :
223226 super ().__init__ ()
224227 self .kind = kind
225- self ._player = player
228+ self ._player : Optional [ MediaPlayer ] = player
226229 self ._queue : asyncio .Queue [Union [Frame , Packet ]] = asyncio .Queue ()
227230 self ._start : Optional [float ] = None
228231
@@ -300,7 +303,13 @@ class MediaPlayer:
300303 """
301304
302305 def __init__ (
303- self , file , format = None , options = None , timeout = None , loop = False , decode = True
306+ self ,
307+ file : Any ,
308+ format : Optional [str ] = None ,
309+ options : Optional [dict [str , str ]] = None ,
310+ timeout : Optional [int ] = None ,
311+ loop : bool = False ,
312+ decode : bool = True ,
304313 ) -> None :
305314 self .__container = av .open (
306315 file = file , format = format , mode = "r" , options = options , timeout = timeout
@@ -310,7 +319,7 @@ def __init__(
310319
311320 # examine streams
312321 self .__started : set [PlayerStreamTrack ] = set ()
313- self .__streams = []
322+ self .__streams : list [ _AudioOrVideoStream ] = []
314323 self .__decode = decode
315324 self .__audio : Optional [PlayerStreamTrack ] = None
316325 self .__video : Optional [PlayerStreamTrack ] = None
@@ -341,14 +350,14 @@ def __init__(
341350 self ._loop_playback = loop
342351
343352 @property
344- def audio (self ) -> MediaStreamTrack :
353+ def audio (self ) -> Optional [ MediaStreamTrack ] :
345354 """
346355 A :class:`aiortc.MediaStreamTrack` instance if the file contains audio.
347356 """
348357 return self .__audio
349358
350359 @property
351- def video (self ) -> MediaStreamTrack :
360+ def video (self ) -> Optional [ MediaStreamTrack ] :
352361 """
353362 A :class:`aiortc.MediaStreamTrack` instance if the file contains video.
354363 """
@@ -388,12 +397,12 @@ def _stop(self, track: PlayerStreamTrack) -> None:
388397 self .__container .close ()
389398 self .__container = None
390399
391- def __log_debug (self , msg : str , * args ) -> None :
400+ def __log_debug (self , msg : str , * args : object ) -> None :
392401 logger .debug (f"MediaPlayer(%s) { msg } " , self .__container .name , * args )
393402
394403
395404class MediaRecorderContext :
396- def __init__ (self , stream ) -> None :
405+ def __init__ (self , stream : _AudioOrVideoStream ) -> None :
397406 self .started = False
398407 self .stream = stream
399408 self .task : Optional [asyncio .Task [None ]] = None
@@ -418,8 +427,15 @@ class MediaRecorder:
418427 :param options: Additional options to pass to FFmpeg.
419428 """
420429
421- def __init__ (self , file , format = None , options = None ) -> None :
422- self .__container = av .open (file = file , format = format , mode = "w" , options = options )
430+ def __init__ (
431+ self ,
432+ file : Any ,
433+ format : Optional [str ] = None ,
434+ options : Optional [dict [str , str ]] = None ,
435+ ) -> None :
436+ self .__container : Optional [av .container .OutputContainer ] = av .open (
437+ file = file , format = format , mode = "w" , options = options
438+ )
423439 self .__tracks : dict [MediaStreamTrack , MediaRecorderContext ] = {}
424440
425441 def addTrack (self , track : MediaStreamTrack ) -> None :
@@ -428,6 +444,7 @@ def addTrack(self, track: MediaStreamTrack) -> None:
428444
429445 :param track: A :class:`aiortc.MediaStreamTrack`.
430446 """
447+ stream : Union [AudioStream , VideoStream ]
431448 if track .kind == "audio" :
432449 if self .__container .format .name in ("wav" , "alsa" , "pulse" ):
433450 codec_name = "pcm_s16le"
@@ -437,7 +454,7 @@ def addTrack(self, track: MediaStreamTrack) -> None:
437454 codec_name = "libopus"
438455 else :
439456 codec_name = "aac"
440- stream = self .__container .add_stream (codec_name )
457+ stream = cast ( AudioStream , self .__container .add_stream (codec_name ) )
441458 else :
442459 if self .__container .format .name == "image2" :
443460 stream = self .__container .add_stream ("png" , rate = 30 )
@@ -459,7 +476,7 @@ async def stop(self) -> None:
459476 """
460477 Stop recording.
461478 """
462- if self .__container :
479+ if self .__container is not None :
463480 for track , context in self .__tracks .items ():
464481 if context .task is not None :
465482 context .task .cancel ()
@@ -468,9 +485,8 @@ async def stop(self) -> None:
468485 self .__container .mux (packet )
469486 self .__tracks = {}
470487
471- if self .__container :
472- self .__container .close ()
473- self .__container = None
488+ self .__container .close ()
489+ self .__container = None
474490
475491 async def __run_track (
476492 self , track : MediaStreamTrack , context : MediaRecorderContext
@@ -480,20 +496,30 @@ async def __run_track(
480496 frame = await track .recv ()
481497 except MediaStreamError :
482498 return
499+ assert isinstance (frame , (AudioFrame , VideoFrame )), (
500+ "Only audio or video frames can be recorded"
501+ )
483502
484503 if not context .started :
485- # adjust the output size to match the first frame
486- if isinstance (frame , VideoFrame ):
504+ # Adjust the output size to match the first frame.
505+ if isinstance (context .stream , VideoStream ) and isinstance (
506+ frame , VideoFrame
507+ ):
487508 context .stream .width = frame .width
488509 context .stream .height = frame .height
489510 context .started = True
490511
491- for packet in context .stream .encode (frame ):
512+ for packet in context .stream .encode (frame ): # type: ignore
492513 self .__container .mux (packet )
493514
494515
495516class RelayStreamTrack (MediaStreamTrack ):
496- def __init__ (self , relay , source : MediaStreamTrack , buffered : bool ) -> None :
517+ def __init__ (
518+ self ,
519+ relay : "MediaRelay" ,
520+ source : MediaStreamTrack ,
521+ buffered : bool ,
522+ ) -> None :
497523 super ().__init__ ()
498524 self .kind = source .kind
499525 self ._relay = relay
@@ -583,7 +609,7 @@ def _stop(self, proxy: RelayStreamTrack) -> None:
583609 self .__log_debug ("Stop proxy %s" , id (proxy ))
584610 self .__proxies [track ].discard (proxy )
585611
586- def __log_debug (self , msg : str , * args ) -> None :
612+ def __log_debug (self , msg : str , * args : object ) -> None :
587613 logger .debug (f"MediaRelay(%s) { msg } " , id (self ), * args )
588614
589615 async def __run_track (self , track : MediaStreamTrack ) -> None :
0 commit comments