gopro / OpenGoPro

An open source interface specification to communicate with a GoPro camera with accompanying demos and tutorials.
https://gopro.github.io/OpenGoPro/
MIT License
658 stars 149 forks source link

Better handle / allow configurability in regards to initial connection setup #405

Open tony-gutierrez opened 9 months ago

tony-gutierrez commented 9 months ago

Component What is the bug in?

If you call stop shutter with a WiredGoPro that has already been initiated and is past the get state step, it will stop the shutter correctly.

To Reproduce Steps to reproduce the behavior:

  1. Connect a camera, do something to get the screen off of "usb connected" (run a setting command and then kill your python program)
  2. Press the record button on the camera
  3. Try to stop shutter via python sdk
  4. See repeating calls to get camera state.

Expected behavior The stop shutter command is sent regardless of existing state.

Hardware

Additional context When using multiprocessing in python, it is often necessary for sub-processes to initiate a new wiredGoPro object as they cannot be passed among processes. With this but, a sub process is unable to kill the shutter that was started in another process.

tony-gutierrez commented 9 months ago

Just noticed line 153. I wonder if there is any harm in not waiting for encoding to be false. Or if there could be a way to specify that we don't care.

It could get initial state, and then set the _busy and _encoding flags to the appropriate initial values. However I have noticed that _encoding does not get updated when starting the shutter, and appears to only be updated if the user happens to call is_ready().

Screenshot 2023-09-21 at 10 59 33 PM
tcamise-gpsw commented 9 months ago

I actually think this is behaving as expected. The Wireless / Wired GoPro classes track the encoding and ready statuses so that they know when to allow communication.

If the camera is encoding, it will not accept commands (besides Set Shutter Off).

One option is to always set shutter off when opening the WiredGoPro. However I don't think that is desired behavior, at least not by default. It seems to me that if you started recorded on the camera UI, it is up to you to stop recording on the camera UI. However, if you think otherwise I could entertain the idea of a "force_init" argument to WiredGoPro that would send "Set Shutter Off"

tony-gutierrez commented 9 months ago

We have a commercial use where a robot drives around with 8 cams on it. We have to use multiprocessing in python, which means each process (8 total) briefly talks to one camera to do an action (configure, start and stop). The processes end after each action. I am working around it for now with the stop shutter, but if our main host program crashes or something like that, we currently cannot stop recording via the SDK.

An option to force stop on open would help, but I would prefer an option to bypass the wait for state check. That way we could do more controlled things like check the recorded duration, stop, and then DL media

The wiredGoPro is not currently accurately tracking the encoding status, so I don't think it would be a breaking change to have a wiredGoPro created, bypass the check, and it think it is not recording. It could even be an enhancement so that encoding:true is accurately set on reopen.

tony-gutierrez commented 8 months ago

For others, I solved this for now by forcing an http call to the camera upon detection, before instantiating a WiredGoPro instance. You will need to create the baseUrl using the ip of the camera. I have the IP from my own _find_mult_serial_via_mdns function that detects multiple cameras, but you can also craft it from the camera serial number.

async def forceStopShutter(baseUrl):
    async with aiohttp.ClientSession() as session:
        async with session.get(baseUrl + 'gopro/camera/shutter/stop') as resp:
            return resp.status == 200
tcamise-gpsw commented 7 months ago

I plan at some point to do a major redesign to both:

Perhaps part of this will be to allow configurable strategies for at least initialization as is discussed here.

strouble commented 2 months ago

I came across the same problem and my solution is to start a second method on the same object which stops recording if the camera is busy.

async def _open(self):
    async def stop_shutter():
        while True:
            try:
                self._logger.debug(f"Try to stop recording on open() for {self.__cam.identifier}")
                break
            except open_gopro.exceptions.GoProNotOpened:
                await asyncio.sleep(1)

        state = (await self.__cam.http_command.get_camera_state()).data
        if state.get(constants.StatusId.ENCODING) or state.get(constants.StatusId.SYSTEM_BUSY):
            await self.__cam.http_command.set_shutter(shutter=Params.Toggle.DISABLE)

    await asyncio.gather(self.__cam.open(), stop_shutter())

The method can simply be called with await self._open() instead of the original call await self.__cam.open().

tony-gutierrez commented 2 months ago

I just rewrote in node, python was sluggish controlling multiple cameras, and the SDK is missing many of the available settings.

On Sat, May 4, 2024, 12:59 PM Philipp Strobel @.***> wrote:

I came across the same problem and my solution is to start a second method on the same object which stops recording if the camera is busy.

async def _open(self): async def stop_shutter(): while True: try: self._logger.debug(f"Try to stop recording on open() for {self.__cam.identifier}") break except open_gopro.exceptions.GoProNotOpened: await asyncio.sleep(1)

    state = (await self.__cam.http_command.get_camera_state()).data
    if state.get(constants.StatusId.ENCODING) or state.get(constants.StatusId.SYSTEM_BUSY):
        await self.__cam.http_command.set_shutter(shutter=Params.Toggle.DISABLE)

await asyncio.gather(self.__cam.open(), stop_shutter())

The method can simply be called with await self._open() instead of the original call await self.__cam.open().

— Reply to this email directly, view it on GitHub https://github.com/gopro/OpenGoPro/issues/405#issuecomment-2094301932, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADVCNM22FE5QGYQHZUJYWYTZAUHX5AVCNFSM6AAAAAA5CN2PWCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJUGMYDCOJTGI . You are receiving this because you authored the thread.Message ID: @.***>