sr99622 / libonvif

Onvif library with GUI implementation and built in YOLOX
GNU Lesser General Public License v2.1
175 stars 45 forks source link

[issue] getVideoEncoderConfiguration provide no data #84

Closed mseidler1 closed 8 months ago

mseidler1 commented 8 months ago

Hi, when I call the function getVideoEncoderConfiguration it provides no data. The last error messgage just contains "getVideoEncoderConfiguration". getStreamUri and other functions work fine.

Tested on Ubuntu 22.04 with libonvif 2.0.1

sr99622 commented 8 months ago

Hello,

Thank you for reaching out. The most likely explanation for this issue is that the camera has not implemented the function for ONVIF. It is very common for cameras to skip implementation for parts of the spec, or to implement them incorrectly. Most do implement getVideoEncoderConfiguration, so it is a little bit unusual to see that particular error. Perhaps the camera only has one available configuration so they figured it was not worth the effort to implement the function.

The library has been verified with a large variety of cameras, so there is confidence that it has implemented the spec correctly. Because there are so many different varieties of cameras and manufacturers, it's not practical to test every one, so the library relies on basic error messages which may not be all that descriptive.

It is unfortunate that the quality of camera software varies so much. Some cameras have pretty good implementations, but there are a lot that do not work very well at all. A great deal of effort has gone into the library to prevent buggy cameras from crashing the app, as they cannot be relied upon for proper operation.

If you have access to other cameras, I would recommend testing those, as you may have better results with a different camera. Examples of cameras that have good implementations are Hikvision, Dahua, Amcrest and Speco.

Best Regards,

Stephen

mseidler1 commented 8 months ago

Camera is tapo C212. With the onvif-util I get the video encoder configuration with get video.

get video Profile set to profile_1

Resolution: 2304 x 1296 Frame Rate: 30 Gov Length: 25 Bit Rate: 2048

If now use my C++ code. The onvif_data is just containing zeros.

OnvifCam::OnvifCam(string ip, string user, string pass) { onvif_session = (struct OnvifSession)calloc(sizeof(struct OnvifSession), 1); onvif_data = (struct OnvifData)calloc(sizeof(struct OnvifData), 1); initializeSession(onvif_session); int n = broadcast(onvif_session); int j = 1; int cam = 0; int index = 0; bool looking = true; numToken = 0;

for (int i = 0; i < n; i++)
{
    prepareOnvifData(i, onvif_session, onvif_data);
    char host[128];
    extractHost(onvif_data->xaddrs, host);
    getHostname(onvif_data);
    if (!strcmp(host, ip.c_str()))
    {
        strcpy(onvif_data->username, user.c_str());
        strcpy(onvif_data->password, pass.c_str());
        //std::cout << "ONVIF: " << host << endl;
        //std::cout << "ONVIF: " << ip << ": User: " << onvif_data->username << endl;
        //std::cout << "ONVIF: " << ip << ": Password: " << onvif_data->password << endl;
        if (getDeviceInformation(onvif_data) == 0)
        {
            //std::cout << "ONVIF: " << ip << ": " << onvif_data->last_error << endl;
            //std::cout << "ONVIF: " << ip << ": Serial: " << onvif_data->serial_number << endl;
            index = 0;
            looking = true;
            while (looking)
            {
                memset(onvif_data->profileToken, 0, 128);
                if (getProfileToken(onvif_data, index)) 
                    std::cout << "ONVIF: " << ip << ": Can't get token" << endl;
                if (strlen(onvif_data->profileToken) == 0)
                    looking = false;
                else
                {
                    //std::cout << "ONVIF: " << ip << ": Token " << index << ": " << onvif_data->profileToken << endl;
                }
                index ++;
            }
        }
        else 
            std::cout << "ONVIF: " << ip << ": Login falsch" << endl;
        numToken = index -1;
        token[numToken];
        tokenstring[numToken];
        rtsp[numToken];
        ptzAvailable[numToken];
        for (int y = 0; y < numToken; y++)
        {
            if (getProfileToken(onvif_data, y)) 
                std::cout << "ONVIF: " << ip << ": Can't get token" << endl;
            token[y] = y;
            tokenstring[y] = onvif_data->profileToken;

            //std::cout << "ONVIF: " << ip << ": Token eigen: " << token[y] << " String: " << tokenstring[y] << endl;
        }

        for (int y = 0; y < numToken; y++)
        {
            getProfileToken(onvif_data, y);
            if (getVideoEncoderConfiguration(onvif_data)) std::cout << "ONVIF: " << ip << ": " << onvif_data->last_error << endl;
            std::cout << "\nONVIF: " << ip << ": Configuration of token: " << tokenstring[y] << endl;
            std::cout << "ONVIF: " << ip << ": Resolution: " << onvif_data->width << " x " << onvif_data->height << endl;
            std::cout << "ONVIF: " << ip << ": Frame Rate: " << onvif_data->frame_rate << endl;
            std::cout << "ONVIF: " << ip << ": Gov Length: " << onvif_data->gov_length << endl;
            std::cout << "ONVIF: " << ip << ": Bitrate:    " << onvif_data->bitrate << endl;
            getStreamUri(onvif_data);
            std::cout << "ONVIF: " << ip << ": RTSP URI:   " << uri_with_pass(onvif_data) << endl;
            rtsp[y] = uri_with_pass(onvif_data);
            ptzAvailable[y] = hasPTZ(onvif_data);
            std::cout << "ONVIF: " << ip << ": PTZ:        " << ptzAvailable[y]  << " " << hasPTZ(onvif_data) << endl;
        }

    }
}   

//closeSession(onvif_session);
//free(onvif_session);
//free(onvif_data);

}

OnvifCam::~OnvifCam() {

}

sr99622 commented 8 months ago

It looks as though the camera is actually working, as well as onvif-util. This would lead me to believe that the error is in the C++ code somewhere. I would probably look at the input and output of the getProfileToken command, and make sure that the input is a valid profile index and that the onvif_data->profileToken is set to the correct string value. You can capture the return value of getProfileToken, which should be 0 if the function was successful, or a negative number if there was a failure.

I think you need to call getProfile as well to get the videoEncoderConfigurationToken, before calling getVideoEncoderConfiguration.

mseidler1 commented 8 months ago

Thanks for the quick reply. In version 1.4.5 the function getprofile was used to get the bitrate, etc. but since 2.0.1 this doesn't work anymore.

mseidler1 commented 8 months ago

Thanks thats the solution.

  1. getProfileToken()
  2. getProfile()
  3. getVideoEncoderConfiguration

Now the data is available! THX!