Skip to navigation

Manage streams

Preview

List, retrieve, stop, and re-egress existing video streams.

Use the Lattice SDK to inspect active streams, retrieve a specific stream, archive a stream when its source goes offline, and re-publish over RTSP or SRT for downstream consumers.

Before you begin

gRPC authentication

These gRPC examples authenticate with a static environment token attached as request metadata. If you are using OAuth 2.0 client credentials instead, set up the token refresh module before running the examples on this page.

Stream ownership

Your integration owns the lifecycle of the IngressStream records it creates. Streams are not automatically removed when a publisher disconnects, a stream becomes inactive, or your integration stops communicating with Lattice. A stream with status STREAM_STATUS_INACTIVE still exists; when the source or publisher reconnects, reuse the existing stream rather than creating another one.

Persist each stream’s ingress_id with the asset or integration that owns it. On startup or reconnect, call GetIngressStream with the saved ID to check the stream. If you don’t have the ID, use ListIngressStreams and inspect its pages to find the stream. Create a new stream only after checking that an existing one for the source does not already exist.

List active streams

Call ListIngressStreams to retrieve the current page of active streams. Archived streams are not returned. Send an empty request to fetch the first page (default: 50, max: 100), then iterate while next_page_token is non-empty.

Pagination contract

Pass the previous response’s next_page_token as page_token and keep every other request field identical. The server may reject requests that change other fields between pages.

// This Go example uses the Lattice REST SDK (lattice-sdk-go) to list active ingress streams.
package main
import (
"context"
"fmt"
"net/http"
"os"
Lattice "github.com/anduril/lattice-sdk-go/v5"
"github.com/anduril/lattice-sdk-go/v5/client"
"github.com/anduril/lattice-sdk-go/v5/option"
)
func main() {
// Get environment variables
latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
// Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
// Check required environment variables
if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
fmt.Println("Missing required environment variables")
os.Exit(1)
}
// Initialize headers for sandbox authorization
headers := http.Header{}
headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
// Create the client
LatticeClient := client.NewClient(
option.WithToken(environmentToken),
option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
option.WithHTTPHeader(headers),
)
ctx := context.Background()
// Iterate every page of active streams. Keep all request fields identical between
// pages other than pageToken; the server may reject a request that changes them.
pageToken := ""
for {
response, err := LatticeClient.Video.ListIngressStreams(ctx, &Lattice.ListIngressStreamsRequest{
PageToken: &pageToken,
})
if err != nil {
fmt.Printf("Failed to list streams: %v\n", err)
os.Exit(1)
}
for _, stream := range response.GetIngressStreams() {
fmt.Printf("Stream %s status %s\n", *stream.GetIngressID(), *stream.GetStatus())
}
if response.GetNextPageToken() == nil || *response.GetNextPageToken() == "" {
break
}
pageToken = *response.GetNextPageToken()
}
}

Get a single stream

Call GetIngressStream with an ingress_id to retrieve a single IngressStream. The response includes the current status, ingress configuration, and associated egress stream identifiers. For a description of every field, see IngressStream object.

MPEG-TS streams are read-only over the public internet

MPEG-TS ingress is supported only at the edge, in closed networks. If you connect to Lattice over the public internet, you can’t start an MPEG-TS stream, but a stream created at the edge still appears in ListIngressStreams and can be inspected with GetIngressStream.

// This Go example uses the Lattice REST SDK (lattice-sdk-go) to retrieve a single ingress stream.
package main
import (
"context"
"fmt"
"net/http"
"os"
Lattice "github.com/anduril/lattice-sdk-go/v5"
"github.com/anduril/lattice-sdk-go/v5/client"
"github.com/anduril/lattice-sdk-go/v5/option"
)
// Ingress ID of the stream to get. Replace before running.
const ingressID = "<INGRESS-ID>"
func main() {
// Get environment variables
latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
// Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
// Check required environment variables
if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
fmt.Println("Missing required environment variables")
os.Exit(1)
}
// Initialize headers for sandbox authorization
headers := http.Header{}
headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
// Create the client
LatticeClient := client.NewClient(
option.WithToken(environmentToken),
option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
option.WithHTTPHeader(headers),
)
ctx := context.Background()
response, err := LatticeClient.Video.GetIngressStream(ctx, &Lattice.GetIngressStreamRequest{
IngressID: ingressID,
})
if err != nil {
fmt.Printf("Failed to get stream: %v\n", err)
os.Exit(1)
}
stream := response.GetIngressStream()
if stream == nil {
fmt.Printf("No ingress stream found for %s\n", ingressID)
os.Exit(1)
}
fmt.Printf("Ingress ID: %s\n", *stream.GetIngressID())
fmt.Printf("Title: %q\n", *stream.GetTitle())
fmt.Printf("Status: %s\n", *stream.GetStatus())
}

Stop an ingress stream

Stopping a stream archives it. The IngressStream record remains retrievable with GetIngressStream, but its status transitions to STREAM_STATUS_ARCHIVED and ListIngressStreams no longer returns the record.

Archive, not delete

DeleteIngressStream does not delete the underlying record. To remove the stale media reference from the camera entity, follow up with an OverrideEntity call as shown in the example below. The example reads the entity, filters out the stale MediaItem, and writes the surviving items back. A field_path of media replaces the entire Media component while leaving every other entity component untouched.

// This Go example uses the Lattice REST SDK (lattice-sdk-go) to stop (archive) an ingress stream
// and remove its stale media reference from the associated camera entity.
package main
import (
"context"
"fmt"
"net/http"
"os"
"time"
Lattice "github.com/anduril/lattice-sdk-go/v5"
"github.com/anduril/lattice-sdk-go/v5/client"
"github.com/anduril/lattice-sdk-go/v5/option"
)
const (
// Ingress ID of the stream to stop. Replace before running.
ingressID = "<DELETE-INGRESS-ID>"
// Entity the video is attached to. Set to "" to only stop the stream without
// editing any entity's media list.
entityID = "<ENTITY-ID>"
)
func main() {
// Get environment variables
latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
// Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
// Check required environment variables
if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
fmt.Println("Missing required environment variables")
os.Exit(1)
}
// Initialize headers for sandbox authorization
headers := http.Header{}
headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
// Create the client
LatticeClient := client.NewClient(
option.WithToken(environmentToken),
option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
option.WithHTTPHeader(headers),
)
ctx := context.Background()
fmt.Printf("Stopping ingress stream with ID: %s\n", ingressID)
_, err := LatticeClient.Video.DeleteIngressStream(ctx, &Lattice.DeleteIngressStreamRequest{
IngressID: ingressID,
})
if err != nil {
fmt.Printf("Failed to stop ingress stream: %v\n", err)
os.Exit(1)
}
// With no entity to update, stopping the stream is all there is to do.
if entityID == "" {
return
}
// Read the entity, filter the stale MediaItem out, and write the surviving items back.
entity, err := LatticeClient.Entities.GetEntity(ctx, &Lattice.GetEntityRequest{
EntityID: entityID,
})
if err != nil {
fmt.Printf("Failed to fetch entity %s: %v\n", entityID, err)
os.Exit(1)
}
var current []*Lattice.MediaItem
if entity.GetMedia() != nil {
current = entity.GetMedia().GetMedia()
}
remaining := make([]*Lattice.MediaItem, 0, len(current))
for _, item := range current {
if item.GetItemIdentifier() == nil || *item.GetItemIdentifier() != ingressID {
remaining = append(remaining, item)
}
}
if len(remaining) == len(current) {
fmt.Printf("Video %s was not in entity %s media list; nothing to remove\n", ingressID, entityID)
return
}
// A field_path of media replaces the entire Media component with the surviving items.
_, err = LatticeClient.Entities.OverrideEntity(ctx, &Lattice.EntityOverride{
EntityID: entityID,
FieldPath: "media",
Entity: &Lattice.Entity{
EntityID: Lattice.String(entityID),
Media: &Lattice.Media{
Media: remaining,
},
},
Provenance: &Lattice.Provenance{
IntegrationName: Lattice.String("lattice-video-integration"),
DataType: Lattice.String("video-association"),
SourceUpdateTime: Lattice.Time(time.Now().UTC()),
},
})
if err != nil {
fmt.Printf("Failed to override entity media: %v\n", err)
os.Exit(1)
}
fmt.Printf("Removed video %s from entity %s\n", ingressID, entityID)
}

What’s next

  • Overview: Review the API surface and stream lifecycle.
  • Stream a video: Publish an asset and start an ingress stream.