Files
onvif-go/media.go
T
2025-10-30 00:50:27 +00:00

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
}