311 lines
9.3 KiB
Go
311 lines
9.3 KiB
Go
package onvif
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"fmt"
|
|
|
|
"github.com/0x524A/go-onvif/soap"
|
|
)
|
|
|
|
// Media service namespace
|
|
const mediaNamespace = "http://www.onvif.org/ver10/media/wsdl"
|
|
|
|
// GetProfiles retrieves all media profiles
|
|
func (c *Client) GetProfiles(ctx context.Context) ([]*Profile, error) {
|
|
endpoint := c.mediaEndpoint
|
|
if endpoint == "" {
|
|
endpoint = c.endpoint
|
|
}
|
|
|
|
type GetProfiles struct {
|
|
XMLName xml.Name `xml:"trt:GetProfiles"`
|
|
Xmlns string `xml:"xmlns:trt,attr"`
|
|
}
|
|
|
|
type GetProfilesResponse struct {
|
|
XMLName xml.Name `xml:"GetProfilesResponse"`
|
|
Profiles []struct {
|
|
Token string `xml:"token,attr"`
|
|
Name string `xml:"Name"`
|
|
VideoSourceConfiguration *struct {
|
|
Token string `xml:"token,attr"`
|
|
Name string `xml:"Name"`
|
|
UseCount int `xml:"UseCount"`
|
|
SourceToken string `xml:"SourceToken"`
|
|
Bounds *struct {
|
|
X int `xml:"x,attr"`
|
|
Y int `xml:"y,attr"`
|
|
Width int `xml:"width,attr"`
|
|
Height int `xml:"height,attr"`
|
|
} `xml:"Bounds"`
|
|
} `xml:"VideoSourceConfiguration"`
|
|
VideoEncoderConfiguration *struct {
|
|
Token string `xml:"token,attr"`
|
|
Name string `xml:"Name"`
|
|
UseCount int `xml:"UseCount"`
|
|
Encoding string `xml:"Encoding"`
|
|
Resolution *struct {
|
|
Width int `xml:"Width"`
|
|
Height int `xml:"Height"`
|
|
} `xml:"Resolution"`
|
|
Quality float64 `xml:"Quality"`
|
|
RateControl *struct {
|
|
FrameRateLimit int `xml:"FrameRateLimit"`
|
|
EncodingInterval int `xml:"EncodingInterval"`
|
|
BitrateLimit int `xml:"BitrateLimit"`
|
|
} `xml:"RateControl"`
|
|
} `xml:"VideoEncoderConfiguration"`
|
|
PTZConfiguration *struct {
|
|
Token string `xml:"token,attr"`
|
|
Name string `xml:"Name"`
|
|
UseCount int `xml:"UseCount"`
|
|
NodeToken string `xml:"NodeToken"`
|
|
} `xml:"PTZConfiguration"`
|
|
} `xml:"Profiles"`
|
|
}
|
|
|
|
req := GetProfiles{
|
|
Xmlns: mediaNamespace,
|
|
}
|
|
|
|
var resp GetProfilesResponse
|
|
|
|
username, password := c.GetCredentials()
|
|
soapClient := soap.NewClient(c.httpClient, username, password)
|
|
|
|
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
|
|
return nil, fmt.Errorf("GetProfiles failed: %w", err)
|
|
}
|
|
|
|
profiles := make([]*Profile, len(resp.Profiles))
|
|
for i, p := range resp.Profiles {
|
|
profile := &Profile{
|
|
Token: p.Token,
|
|
Name: p.Name,
|
|
}
|
|
|
|
if p.VideoSourceConfiguration != nil {
|
|
profile.VideoSourceConfiguration = &VideoSourceConfiguration{
|
|
Token: p.VideoSourceConfiguration.Token,
|
|
Name: p.VideoSourceConfiguration.Name,
|
|
UseCount: p.VideoSourceConfiguration.UseCount,
|
|
SourceToken: p.VideoSourceConfiguration.SourceToken,
|
|
}
|
|
if p.VideoSourceConfiguration.Bounds != nil {
|
|
profile.VideoSourceConfiguration.Bounds = &IntRectangle{
|
|
X: p.VideoSourceConfiguration.Bounds.X,
|
|
Y: p.VideoSourceConfiguration.Bounds.Y,
|
|
Width: p.VideoSourceConfiguration.Bounds.Width,
|
|
Height: p.VideoSourceConfiguration.Bounds.Height,
|
|
}
|
|
}
|
|
}
|
|
|
|
if p.VideoEncoderConfiguration != nil {
|
|
profile.VideoEncoderConfiguration = &VideoEncoderConfiguration{
|
|
Token: p.VideoEncoderConfiguration.Token,
|
|
Name: p.VideoEncoderConfiguration.Name,
|
|
UseCount: p.VideoEncoderConfiguration.UseCount,
|
|
Encoding: p.VideoEncoderConfiguration.Encoding,
|
|
Quality: p.VideoEncoderConfiguration.Quality,
|
|
}
|
|
if p.VideoEncoderConfiguration.Resolution != nil {
|
|
profile.VideoEncoderConfiguration.Resolution = &VideoResolution{
|
|
Width: p.VideoEncoderConfiguration.Resolution.Width,
|
|
Height: p.VideoEncoderConfiguration.Resolution.Height,
|
|
}
|
|
}
|
|
if p.VideoEncoderConfiguration.RateControl != nil {
|
|
profile.VideoEncoderConfiguration.RateControl = &VideoRateControl{
|
|
FrameRateLimit: p.VideoEncoderConfiguration.RateControl.FrameRateLimit,
|
|
EncodingInterval: p.VideoEncoderConfiguration.RateControl.EncodingInterval,
|
|
BitrateLimit: p.VideoEncoderConfiguration.RateControl.BitrateLimit,
|
|
}
|
|
}
|
|
}
|
|
|
|
if p.PTZConfiguration != nil {
|
|
profile.PTZConfiguration = &PTZConfiguration{
|
|
Token: p.PTZConfiguration.Token,
|
|
Name: p.PTZConfiguration.Name,
|
|
UseCount: p.PTZConfiguration.UseCount,
|
|
NodeToken: p.PTZConfiguration.NodeToken,
|
|
}
|
|
}
|
|
|
|
profiles[i] = profile
|
|
}
|
|
|
|
return profiles, nil
|
|
}
|
|
|
|
// GetStreamURI retrieves the stream URI for a profile
|
|
func (c *Client) GetStreamURI(ctx context.Context, profileToken string) (*MediaURI, error) {
|
|
endpoint := c.mediaEndpoint
|
|
if endpoint == "" {
|
|
endpoint = c.endpoint
|
|
}
|
|
|
|
type GetStreamUri struct {
|
|
XMLName xml.Name `xml:"trt:GetStreamUri"`
|
|
Xmlns string `xml:"xmlns:trt,attr"`
|
|
StreamSetup struct {
|
|
Stream string `xml:"Stream"`
|
|
Transport struct {
|
|
Protocol string `xml:"Protocol"`
|
|
} `xml:"Transport"`
|
|
} `xml:"trt:StreamSetup"`
|
|
ProfileToken string `xml:"trt:ProfileToken"`
|
|
}
|
|
|
|
type GetStreamUriResponse struct {
|
|
XMLName xml.Name `xml:"GetStreamUriResponse"`
|
|
MediaUri struct {
|
|
Uri string `xml:"Uri"`
|
|
InvalidAfterConnect bool `xml:"InvalidAfterConnect"`
|
|
InvalidAfterReboot bool `xml:"InvalidAfterReboot"`
|
|
Timeout string `xml:"Timeout"`
|
|
} `xml:"MediaUri"`
|
|
}
|
|
|
|
req := GetStreamUri{
|
|
Xmlns: mediaNamespace,
|
|
ProfileToken: profileToken,
|
|
}
|
|
req.StreamSetup.Stream = "RTP-Unicast"
|
|
req.StreamSetup.Transport.Protocol = "RTSP"
|
|
|
|
var resp GetStreamUriResponse
|
|
|
|
username, password := c.GetCredentials()
|
|
soapClient := soap.NewClient(c.httpClient, username, password)
|
|
|
|
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
|
|
return nil, fmt.Errorf("GetStreamUri failed: %w", err)
|
|
}
|
|
|
|
return &MediaURI{
|
|
URI: resp.MediaUri.Uri,
|
|
InvalidAfterConnect: resp.MediaUri.InvalidAfterConnect,
|
|
InvalidAfterReboot: resp.MediaUri.InvalidAfterReboot,
|
|
}, nil
|
|
}
|
|
|
|
// GetSnapshotURI retrieves the snapshot URI for a profile
|
|
func (c *Client) GetSnapshotURI(ctx context.Context, profileToken string) (*MediaURI, error) {
|
|
endpoint := c.mediaEndpoint
|
|
if endpoint == "" {
|
|
endpoint = c.endpoint
|
|
}
|
|
|
|
type GetSnapshotUri struct {
|
|
XMLName xml.Name `xml:"trt:GetSnapshotUri"`
|
|
Xmlns string `xml:"xmlns:trt,attr"`
|
|
ProfileToken string `xml:"trt:ProfileToken"`
|
|
}
|
|
|
|
type GetSnapshotUriResponse struct {
|
|
XMLName xml.Name `xml:"GetSnapshotUriResponse"`
|
|
MediaUri struct {
|
|
Uri string `xml:"Uri"`
|
|
InvalidAfterConnect bool `xml:"InvalidAfterConnect"`
|
|
InvalidAfterReboot bool `xml:"InvalidAfterReboot"`
|
|
Timeout string `xml:"Timeout"`
|
|
} `xml:"MediaUri"`
|
|
}
|
|
|
|
req := GetSnapshotUri{
|
|
Xmlns: mediaNamespace,
|
|
ProfileToken: profileToken,
|
|
}
|
|
|
|
var resp GetSnapshotUriResponse
|
|
|
|
username, password := c.GetCredentials()
|
|
soapClient := soap.NewClient(c.httpClient, username, password)
|
|
|
|
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
|
|
return nil, fmt.Errorf("GetSnapshotUri failed: %w", err)
|
|
}
|
|
|
|
return &MediaURI{
|
|
URI: resp.MediaUri.Uri,
|
|
InvalidAfterConnect: resp.MediaUri.InvalidAfterConnect,
|
|
InvalidAfterReboot: resp.MediaUri.InvalidAfterReboot,
|
|
}, nil
|
|
}
|
|
|
|
// GetVideoEncoderConfiguration retrieves video encoder configuration
|
|
func (c *Client) GetVideoEncoderConfiguration(ctx context.Context, configurationToken string) (*VideoEncoderConfiguration, error) {
|
|
endpoint := c.mediaEndpoint
|
|
if endpoint == "" {
|
|
endpoint = c.endpoint
|
|
}
|
|
|
|
type GetVideoEncoderConfiguration struct {
|
|
XMLName xml.Name `xml:"trt:GetVideoEncoderConfiguration"`
|
|
Xmlns string `xml:"xmlns:trt,attr"`
|
|
ConfigurationToken string `xml:"trt:ConfigurationToken"`
|
|
}
|
|
|
|
type GetVideoEncoderConfigurationResponse struct {
|
|
XMLName xml.Name `xml:"GetVideoEncoderConfigurationResponse"`
|
|
Configuration struct {
|
|
Token string `xml:"token,attr"`
|
|
Name string `xml:"Name"`
|
|
UseCount int `xml:"UseCount"`
|
|
Encoding string `xml:"Encoding"`
|
|
Resolution *struct {
|
|
Width int `xml:"Width"`
|
|
Height int `xml:"Height"`
|
|
} `xml:"Resolution"`
|
|
Quality float64 `xml:"Quality"`
|
|
RateControl *struct {
|
|
FrameRateLimit int `xml:"FrameRateLimit"`
|
|
EncodingInterval int `xml:"EncodingInterval"`
|
|
BitrateLimit int `xml:"BitrateLimit"`
|
|
} `xml:"RateControl"`
|
|
} `xml:"Configuration"`
|
|
}
|
|
|
|
req := GetVideoEncoderConfiguration{
|
|
Xmlns: mediaNamespace,
|
|
ConfigurationToken: configurationToken,
|
|
}
|
|
|
|
var resp GetVideoEncoderConfigurationResponse
|
|
|
|
username, password := c.GetCredentials()
|
|
soapClient := soap.NewClient(c.httpClient, username, password)
|
|
|
|
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
|
|
return nil, fmt.Errorf("GetVideoEncoderConfiguration failed: %w", err)
|
|
}
|
|
|
|
config := &VideoEncoderConfiguration{
|
|
Token: resp.Configuration.Token,
|
|
Name: resp.Configuration.Name,
|
|
UseCount: resp.Configuration.UseCount,
|
|
Encoding: resp.Configuration.Encoding,
|
|
Quality: resp.Configuration.Quality,
|
|
}
|
|
|
|
if resp.Configuration.Resolution != nil {
|
|
config.Resolution = &VideoResolution{
|
|
Width: resp.Configuration.Resolution.Width,
|
|
Height: resp.Configuration.Resolution.Height,
|
|
}
|
|
}
|
|
|
|
if resp.Configuration.RateControl != nil {
|
|
config.RateControl = &VideoRateControl{
|
|
FrameRateLimit: resp.Configuration.RateControl.FrameRateLimit,
|
|
EncodingInterval: resp.Configuration.RateControl.EncodingInterval,
|
|
BitrateLimit: resp.Configuration.RateControl.BitrateLimit,
|
|
}
|
|
}
|
|
|
|
return config, nil
|
|
}
|