[Python] Aiortc properly close camera (led still on) after p2p closed

Discussão em 'Python' iniciado por Stack, Dezembro 19, 2024.

  1. Stack

    Stack Membro Participativo

    # video from server to client
    if self.server_video_stream_offer is None:
    self.server_video_stream_offer = self.create_local_tracks()
    self.pcs[call_number]["pc"].addTrack(self.server_video_stream_offer)

    # Attach video from server to QLabel
    if self.server_video_track is None:
    self.server_video_track = WebCamera(self.server_video_stream_offer,self.to_emitter)
    if self.server_video_blackholde is None:
    self.server_video_blackholde = MediaBlackhole()
    self.server_video_blackholde.addTrack(self.server_video_track)
    await self.server_video_blackholde.start()

    @self.pcs[call_number]["pc"].on("track")
    async def on_track(track):
    if track.kind == "audio":
    self.pcs[call_number]["audio_track"] = track
    # audio from client (server use)
    if call_number == 1:
    correct_queue = self.ip_call_1_packet_queue
    elif call_number == 2:
    correct_queue = self.ip_call_2_packet_queue
    else:
    correct_queue = self.ip_call_3_packet_queue
    self.put_to_q = True
    self.pcs[call_number]["audio_track_for_local_use"] = ClientTrack(track, self, self.to_emitter,call_number,self.put_to_q,correct_queue)
    self.pcs[call_number]["audio_blackhole"] = MediaBlackhole()
    self.pcs[call_number]["audio_blackhole"].addTrack(
    self.pcs[call_number]["audio_track_for_local_use"])
    await self.pcs[call_number]["audio_blackhole"].start()
    else:
    self.pcs[call_number]["video_track"] = track
    # video from client (server use)
    self.pcs[call_number]["video_track_for_local_use"] = ClientWebCamera(track, self.to_emitter,
    call_number, self)
    self.pcs[call_number]["video_blackhole"] = MediaBlackhole()
    self.pcs[call_number]["video_blackhole"].addTrack(
    self.pcs[call_number]["video_track_for_local_use"])
    await self.pcs[call_number]["video_blackhole"].start()



    def get_available_cameras(self):
    try:
    devices = FilterGraph().get_input_devices()
    available_cameras = {}
    for device_index, device_name in enumerate(devices):
    available_cameras[device_index] = device_name
    return available_cameras
    except:
    error = traceback.format_exc()
    self.to_emitter.send({"type": "error", "error_message": error})

    def create_local_tracks(self):
    try:
    options = {"framerate": "30", "video_size": "640x480"} # Reduced frame rate and resolution
    camera_name = "video=" + self.get_available_cameras()[0]
    self.webcam = MediaPlayer(camera_name, format='dshow', options=options)
    self.relay = MediaRelay()
    video_track = self.relay.subscribe(self.webcam.video) # Relay the video stream
    return video_track
    except Exception as e:
    error = traceback.format_exc()
    self.to_emitter.send({"type": "error", "error_message": error})


    async def stop_peer_connection(self, call_number, uid):
    try:
    if call_number not in self.pcs:
    # Peer connection does not exist
    return None

    if self.pcs[call_number]["stop_in_progress"]:
    # Stop process is already in progress
    return None

    self.pcs[call_number]["stop_in_progress"] = True

    # 1. Notify PyQt5 about the stop
    self.to_emitter.send({"type": "stop-peer-connection", "call_number": call_number})

    # 2. Empty the correct queue
    queue = self.call_queues[call_number - 1]
    while not queue.empty():
    queue.get()

    # 3. Close peer connection
    try:
    await self.pcs[call_number]["pc"].close()
    except Exception:
    pass

    # 4. Close data channel
    try:
    self.pcs[call_number]["dc"].close()
    except Exception:
    pass

    # 5. Stop client audio track
    try:
    await self.pcs[call_number]["audio_blackhole"].stop()
    except Exception:
    pass

    # 6. Notify PyQt5 about call status
    self.to_emitter.send({"type": f"call-{call_number}-status", "status": "closed-by-client"})

    # 7. Stop client video track
    try:
    self.pcs[call_number]["video_track_for_local_use"].stop()
    await self.pcs[call_number]["video_blackhole"].stop()
    except Exception:
    pass

    # 8. Cleanup
    try:
    del self.pcs[call_number]
    except Exception:
    pass

    # 9. Release server resources if there are no remaining connections
    if not self.pcs: # No active peer connections
    # Stop video relay and related blackholes
    try:
    if self.server_video_blackholde:
    await self.server_video_blackholde.stop()
    self.server_video_blackholde = None
    if self.server_video_track:
    await self.server_video_track.stop()
    self.server_video_track = None
    except Exception:
    pass

    # Stop server audio stream
    try:
    if self.server_audio_stream_offer:
    self.server_audio_stream_offer.stop()
    self.server_audio_stream_offer = None
    except Exception:
    pass

    # Stop and release the webcam
    try:
    self.relay._stop(self.webcam.video)
    if self.webcam:
    self.to_emitter.send({"type": "log", "message": "Stopping webcam."})
    if self.webcam.audio:
    self.webcam.audio.stop()
    if self.webcam.video:
    self.webcam._stop(self.webcam.video)
    self.webcam.video.stop()
    await asyncio.sleep(0.1) # Give time for cleanup
    self.webcam = None
    except Exception as e:
    self.to_emitter.send({"type": "error", "error_message": str(e)})

    # Attempt final resource cleanup
    gc.collect() # Force garbage collection to release resources
    self.to_emitter.send({"type": "log", "message": "Garbage collection triggered for cleanup."})

    except Exception:
    error = traceback.format_exc()
    self.to_emitter.send({"type": "error", "error_message": error})


    class WebCamera(MediaStreamTrack):
    kind = "video"

    def __init__(self, track, to_emitter):
    super().__init__() # don't forget this!
    self.track = track
    self.to_emitter = to_emitter

    async def recv(self):
    try:
    frame = await self.track.recv()
    pil_image = frame.to_image()
    self.to_emitter.send({"type": "server-web-camera-frame", "pil_image": [pil_image]})
    return frame.reformat(format="yuv420p")
    except:
    error_message = traceback.format_exc()


    I can't close the camera led after aiortc p2p offer close.

    The problem is that:

    # 3. Close peer connection
    try:
    await self.pcs[call_number]["pc"].close()
    except Exception:
    pass


    never run (with return)...

    Edit: There is no error, but this close method pause at:

    for transceiver in self.__transceivers:
    await transceiver.stop()


    -->

    await self.__sender.stop()


    -->

    await asyncio.gather(self.__rtp_started.wait(), self.__rtcp_started.wait())


    and that's because the sender is already stoped (client stop the connection in this case).

    Any thoughts?

    Continue reading...

Compartilhe esta Página