Sunday, June 30, 2024

Program D-Link DCS-5010L - Part 3

 As mentioned in Program D-Link DCS-5010L - Part 2, I wasn't able to successfully open DCS-5010L stream with OpenCV+GStreamer, so I'm going to explore OpenCV+ffmpeg. Ffmpeg is famous OpenSource Multimedia framework which contains a set of utility with almost all codec supported. Here is a Ffmpeg vs. GStreamer comparison.

To going this approach, there is FfmpegCV, which might be served as OpenCV replacement with ffmpeg support, has compatible API as OpenCV. Here I'm going to give it a try. Similar as using GStreamer, would need to install ffmpeg executable separately. Then do: pip install ffmpegcv

This will install the stable version, without cuda acceleration as I don't have nVidia GPU. After trying around, I realized that I need to use this API to open the stream:

cap = ffmpegcv.VideoCaptureStream(stream_url)

However, I don't see how it can help to play the audio.

Then I turned to PyAV. With that, I figured out that the 'video.cgi' would only provide video stream. Need to open 'audio.cgi' for the audio stream which is in wave format.

I added my code to forked dlink-dcs-python-lib github repository, with arrow key support for tile/pan the camera. There is no need to put the OpenCV video into a Tcl/tk gadget, but may create a TK GUI for setting configuration/options such as video file saving path, resolution, motion detecting and so on.

Saturday, June 22, 2024

Program D-Link DCS-5010L - Part 2

OpenCV is mainly focusing on video. It does not support audio directly. To playback audio, would rely on ffmpeg or GStreamer. ffmpeg is mostly a standalone offline multimedia converting tools. OpenCV has GStreamer built-in support, but it is optional. I used pip installed opencv-python on Windows for Python 3.12, and GStreamer isn't enabled. May refer to github opencv-python and https://discuss.bluerobotics.com/t/opencv-python-with-gstreamer-backend/8842. Even install the full package with pip install opencv-contrib-python won't get GStream enabled CV2. Might need to build with source. Either way, would need to install GStreamer first, which is available here: https://gstreamer.freedesktop.org/download. To build GStreamer enabled opencv-python:

git clone --recursive https://github.com/opencv/opencv-python.git
cd .\opencv-python
set CMAKE_ARGS="-DWITH_GSTREAMER=ON"
or $env:CMAKE_ARGS="-DWITH_GSTREAMER=ON" for PowerShell
pip wheel . --verbose

The build gets setuptools 59.2.0, I'm using miniConda and I'm seeing "conda 24.1.2 requires setuptools>=60.0.0, but you have setuptools 59.2.0 which is incompatible". This is not my environment setuptools out-of-date issue, have tried: pip install setuptools --upgrade, python -m pip install pip --upgrade, conda update -n base setuptools, nothing works. And after this, seeing:

  Running command Getting requirements to build wheel
  Traceback (most recent call last):
    File "C:\ProgramData\miniconda3\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 353, in <module>
      main()
...
    File "C:\Users\squ\AppData\Local\Temp\pip-build-env-lekizdpu\overlay\Lib\site-packages\pkg_resources\__init__.py", line 2172, in <module>
      register_finder(pkgutil.ImpImporter, find_on_path)
                      ^^^^^^^^^^^^^^^^^^^
  AttributeError: module 'pkgutil' has no attribute 'ImpImporter'. Did you mean: 'zipimporter'?
  error: subprocess-exited-with-error

Sounds same as opencv-python issues 988, one comment there mentioned:

I encountered the same problem while running pip wheel . --verbose |& tee install_opencv.log. It worked after removing the ==59.2.0" from the "setuptools==59.2.0" https://github.com/opencv/opencv-python/blob/4.x/pyproject.toml#L16.

The pyproject.toml file is under the root, with the setuptools line removed, the original problem is resolved. Build under Windows would need Visual Studio Build Tools (refer to Revisit 坦克大战编程)., need to do the build under the Native build environment. The build will try Ninja, Visual Studio and NMake generator, and will try from v144 to v141. I have v143, and have "Developer Command Prompt for VS 2022" environment launched, all Ninja and NMake will fail as no support of platform, VS v143 progress a bit far,  but still get error as:

 -- Trying 'Visual Studio 17 2022 x64 v143' generator

  -- The C compiler identification is unknown
  CMake Error at CMakeLists.txt:3 (ENABLE_LANGUAGE):
    No CMAKE_C_COMPILER could be found
.

After some research, turns out Windows SDK is needed (I deselected it when installing Visual Studio Build Tools to save some space). It takes a while to build, depends on your computer. But it works. The opencv_python-*.whl file will be generated in the root folder. Just run pip to install it.

At beginning of the build, it will show the configuration, make sure Gstreamer is ON. It will stay OFF if GStreamer develop isn't installed. And with Gstreamer is enabled, I'm getting runtime error as:

ImportError: DLL load failed while importing cv2: The specified module could not be found

which is similar as opencv-python issues 856. Installed Microsoft Visual C++ Redistributable for Visual Studio 2022, but that didn't help. My Windows is not the 'N' one, so not a problem of Windows Media Feature Pack. I have tried the build whl before Gstreamer option enabled and not seeing problem, so I believe the problem is with GStreamer dll, but adding "C:\gstreamer\1.0\msvc_x86_64\lib\gstreamer-1.0" to path didn't help. Have to hook up Sysinternals' Process Monitor, and it shows the run is looking for a bunch of gstreamer's runtime dll which were not in site-packages\cv2 folder. Added "C:\gstreamer\1.0\msvc_x86_64\bin" to environment variable 'PATH' didn't help. Copy dlls over works, but not a clean way. Per https://stackoverflow.com/questions/214852/python-module-dlls, since version python 3.8 they added a mechanism to do this more securely. Read documentation on os.add_dll_directory https://docs.python.org/3/library/os.html#os.add_dll_directory. So doing this in code would work:

import os
gst_root = os.getenv('GSTREAMER_1_0_ROOT_MSVC_X86_64', 'C:/gstreamer/1.0/msvc_x86_64/')
os.add_dll_directory(gst_root+'bin')
import cv2

With Gstreamer enabled, I'm still not able to open the stream with log as:

[ WARN:0@2.620] global cap_gstreamer.cpp:2840 cv::handleMessage OpenCV | GStreamer warning: Embedded video playback halted; module souphttpsrc0 reported: Could not establish connection to server.
[ WARN:0@2.623] global cap_gstreamer.cpp:1698 cv::GStreamerCapture::open OpenCV | GStreamer warning: unable to start pipeline
[ WARN:0@2.623] global cap_gstreamer.cpp:1173 cv::GStreamerCapture::isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
[ WARN:0@2.623] global cap.cpp:206 cv::VideoCapture::open VIDEOIO(GSTREAMER): backend is generally available but can't be used to capture by name

A bit frustrate on this. The http url is all right as I can open it without using GStreamer. Most example I can find are using rtsp protocol but not http. Will leave this aside until I can figure out how to use GStreamer alone to open the stream. I'm going to try OpenCV+ffmpeg next

Friday, June 21, 2024

Program for D-Link DCS-5010L

 D-Link DCS-5010L is a pretty old generation Pan & Tilt WIFI network camera. It is so old, only can be accessed with old IE web browser as it needs ActiveX. For Edge, may work by using IE mode. And almost impossible to use Firefox or Chrome browser. And for some feature and function, would need java installed. And it might not work due to old firmware and interface.

D-Link's Android app even cannot connect to the camera. Give up on that as so frustrate. The TinyCam app can view and control the movement of the camera, but cannot change video resolution, codec type, and motion detection setting.

So come to the idea to use Python script to control and view the camera, more flexibility. Later, may think to make my own Android app for the camera. There is github dlink-dcs-python-lib can be used as lib or reference. This lib has unit test code, but does not have stream video viewer code. Github copilot suggests to use opencv-python for video processing and use requests for handling HTTP requests. Code like below, worked quite well:

import cv2
import requests
import numpy as np
import os

CAM_HOST = os.environ.get('CAM_HOST') or ''
CAM_PORT = os.environ.get('CAM_PORT', 80)
CAM_USER = os.getenv('CAM_USER', 'admin')
CAM_PASS = os.getenv('CAM_PASS', '')

# URL of the video stream
stream_url = f'http://{CAM_HOST}:{CAM_PORT}/video.cgi'

# Start a session
session = requests.Session()
response = session.get(stream_url, stream=True, auth=(CAM_USER, CAM_PASS))

# Check if the connection to the stream is successful
if response.status_code == 200:
    bytes_data = bytes()
    for chunk in response.iter_content(chunk_size=1024):
        bytes_data += chunk
        a = bytes_data.find(b'\xff\xd8')  # JPEG start
        b = bytes_data.find(b'\xff\xd9')  # JPEG end
        if a != -1 and b != -1:
            jpg = bytes_data[a:b+2]  # Extract the JPEG image
            bytes_data = bytes_data[b+2:]  # Remove the processed bytes
            frame = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
            if frame is not None:
                cv2.imshow('Video Stream', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):  # Exit loop if 'q' is pressed
                    break
    cv2.destroyAllWindows()
else:
    print("Failed to connect to the stream.")

Above code is for viewing Motion Jpeg. To view h.264 stream, can use below code:

import cv2
import os

# URL of the H.264 video stream
CAM_HOST = os.environ.get('CAM_HOST') or ''
CAM_PORT = os.environ.get('CAM_PORT', 80)
CAM_USER = os.getenv('CAM_USER', 'admin')
CAM_PASS = os.getenv('CAM_PASS', '')

# URL of the video stream
stream_url = f'http://{CAM_USER}:{CAM_PASS}@{CAM_HOST}:{CAM_PORT}/video.cgi'

# Create a VideoCapture object
cap = cv2.VideoCapture(stream_url)

# Check if camera opened successfully
if not cap.isOpened():
    print("Error: Could not open video stream.")
else:
    # Read until video is completed
    while cap.isOpened():
        # Capture frame-by-frame
        ret, frame = cap.read()
        if ret:
            # Display the resulting frame
            cv2.imshow('Video Stream', frame)

            # Press Q on keyboard to exit
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
        else:
            break

# When everything done, release the video capture object
cap.release()

# Closes all the frames
cv2.destroyAllWindows()

Wednesday, June 19, 2024

Revisit 坦克大战编程

In 2021 I posted two blog regarding building Tank natively with Visual Studio Build tools:

When I trying to redo the build from scratch, I noticed some is missing in my post, for example, there is no 'Build' folder by default. No need of Cygwin. Also, I'm planning to add network support, so player can play the game over internet. So I decide to make another blog to explore this. So here is a complete step by step for creating the build:

  1. Download Visual Studio Build Tools. Go to https://visualstudio.microsoft.com/downloads/?q=build+tools, scroll down and looking for something like Build Tools for Visual Studio 2022. When install, only need select "Desktop Develoment with C++", and for optional modules, the build environment such as MSVC v143 and C++ CMake tools for Windows are needed. All other can be deselected to save some space.
  2. Run "git clone https://github.com/quyq/Tanks.git" in working directory to pull the source code
  3. Install SDL2 header file and lib. SDL2 binary can be download from:
  4. Start a Visual Studio Build Tools environment, and do:
    • create 'build' folder (such as 'mkdir build') under project root
    • change working directory to 'build' (i.e. run 'cd build'), then run:
      • cmake -G "NMake Makefiles" ..
      • Note: the two dots is a must which set the source folder as one level up, and this will create Makefile, out folder and several other files/folder under 'build'
    • Run 'NMake' which should create 'tank.exe' under 'build/out' folder. Resource files would be copied to there too.

For using "NMake Makefiles" generator, update settings.json as:

{
    "terminal.integrated.defaultProfile.windows": "Command Prompt",
    "cmake.generator": "NMake Makefiles",
}

The first line will change default terminal from "Power Shell" to "Command Prompt". The next line select the generator. By default, it will use Visual Studio 16 2019 generator.

Build under WSL might be much easier, just install make/g++ and SDL2 develop package, then run make. If using Win11+WSL2, then no other extra work needed. If running WSL2 on Win10, may need update to latest WSL which has systemd support for GUI. And for audio, may follow instruction from https://x410.dev/cookbook/wsl/enabling-sound-in-wsl-ubuntu-let-it-sing/ which has clear step by step instruction and does not open unnecessary permission for utilizing PulseAudio.