Skip to content

Bug Report: Remote Video Connections Write Malformed Entries to TAKServer #1347

@Humble-Helper-96

Description

@Humble-Helper-96

Bug Report: Remote Video Connections Write Malformed Entries to TAKServer Video Feed Manager

Image

Summary

When a user creates a Remote Video Connection ("Publish Video Stream") in CloudTAK, the entry written to TAKServer's Video Feed Manager via the Marti API contains malformed field values — notably port -1 and null in fields where TAKServer expects populated data. These malformed entries cause two problems:

  1. iTAK fails to download the video feed list from TAKServer, throwing an "unknown error" when any malformed entry is present.
  2. TAKServer cannot edit the malformed entry via its own Video Feed Manager web UI (/Marti/VideoUpload.jsp?feedId=N) — it returns "TAK Server resource unavailable or not allowed."

Deleting the CloudTAK-created entry from TAKServer resolves both issues immediately. Creating the same feed directly in TAKServer's Video Feed Manager produces a valid entry that works across all clients.

Environment

  • CloudTAK Version: 12.103.0
  • TAKServer Version: 5.6-RELEASE-22-HEAD
  • CloudTAK Deploy: Docker Compose
  • node-tak SDK Version: ^12.1.0
  • Clients Tested: ATAK (Android), iTAK (iOS), CloudTAK (Web)

Steps to Reproduce

  1. In CloudTAK, navigate to Videos → Streams tab
  2. Click the Publish Video Stream button (camera+ icon)
  3. Create a Remote Video Connection with an HLS feed URL, e.g.:
    • Alias: TestHLSFeed
    • Feed URL: https://my-mediamtx-host.example.com/my-stream/index.m3u8
  4. Save the connection
  5. Observe the entry in TAKServer's Video Feed Manager (/Marti/VideoManager.jsp)

Observed Behavior

Malformed Entry in TAKServer

The entry CloudTAK writes to TAKServer's Video Feed Manager contains invalid field values:

Field Expected Value Actual Value (CloudTAK-created)
Protocol https (derived from URL but not sent)
Address my-mediamtx-host.example.com (derived from URL but not sent)
Port 443 -1
Path /my-stream/index.m3u8 (derived from URL but not sent)
Buffer time 500 (or similar default) null
Network timeout 5000 null
RTSP reliable 0 or 1 null
Type VIDEO null

When the same feed is created directly in TAKServer's Video Feed Manager, all fields are populated with valid defaults.

Impact on iTAK

With the malformed entry present in TAKServer's feed list:

  • iTAK throws "unknown error" when attempting to download video feeds from the server
  • All feeds become inaccessible, not just the malformed one — the entire feed list download fails
  • Deleting the malformed entry immediately restores iTAK's ability to download feeds

Impact on TAKServer Admin

  • Navigating to the edit page for the malformed entry (/Marti/VideoUpload.jsp?feedId=N) returns: "TAK Server resource unavailable or not allowed."
  • The entry can only be deleted from the list view, not edited

CloudTAK Behavior

  • CloudTAK itself can play the feed it created (the HLS stream works)
  • Other CloudTAK users can see and play the feed in their Streams tab
  • The malformed fields do not affect CloudTAK playback

Expected Behavior

When CloudTAK creates a Remote Video Connection via POST /Marti/api/video, it should:

  1. Populate all required fields with valid values (especially protocol, address, port, path, bufferTime, networkTimeout, type)
  2. For https protocol feeds, set port to 443; for rtsp, set port to 8554; etc.
  3. Ensure the entry is editable in TAKServer's Video Feed Manager web UI
  4. Ensure the entry does not break feed list serialization for other TAK clients (iTAK, ATAK)

Root Cause

The bug spans the node-tak SDK and the CloudTAK frontend. There is a fundamental design mismatch between how CloudTAK creates video feeds and what TAKServer's Marti API expects.

The Mismatch

TAKServer's Video Feed Manager stores feeds as decomposed fields: protocol, address, port, path, networkTimeout, bufferTime, rtspReliable, type, etc. CloudTAK's frontend and the node-tak SDK send feeds with a single url field and omit all the decomposed fields. TAKServer accepts the request but stores -1/null for every missing field, producing a record that breaks other clients.

Trace Through Each Layer

Layer 1 — Frontend form (VideosRemoteFeed.vue)

The feed form collects only 3 user-editable fields:

alias   → text input ("Feed Name")
url     → text input ("Feed URL")
active  → toggle

No fields exist for protocol, port, address, path, bufferTime, networkTimeout, rtspReliable, or type.

Layer 2 — Frontend save logic (MenuVideosRemote.vue)

The newFeed() function (line 136) creates a feed with only { uuid, alias, url, active }. The saveConnection() function (line 160) posts this directly as the request body with no field enrichment or URL decomposition.

Layer 3 — SDK (@tak-ps/node-tak, video.js)

The FeedInput schema defines only 4 fields:

export const FeedInput = Type.Object({
    uuid: Type.Optional(Type.String()),
    active: Type.Boolean({ default: true }),
    alias: Type.String(),
    url: Type.String(),
});

The create() method wraps the connection and posts it to TAKServer:

await this.api.fetch(url, {
    method: 'POST',
    body: {
        videoConnections: [{
            uuid,
            ...connection,
            feeds: connection.feeds.map((feed) => {
                return {
                    uuid: randomUUID(),
                    ...feed,       // ← only uuid, alias, url, active
                };
            })
        }]
    }
});

No URL parsing, no default injection, no decomposition into TAKServer's expected fields.

Layer 4 — TAKServer (POST /Marti/api/video)

TAKServer receives a feed with only uuid, alias, url, and active. It accepts the request without validation but stores null/default values for all missing structured fields. The resulting record has port: -1, bufferTime: null, type: null, etc.

What CloudTAK Sends

{
  "videoConnections": [{
    "uuid": "...",
    "active": true,
    "alias": "TestHLSFeed",
    "feeds": [{
      "uuid": "...",
      "active": true,
      "alias": "TestHLSFeed",
      "url": "https://my-mediamtx-host.example.com/my-stream/index.m3u8"
    }]
  }]
}

What TAKServer Expects (Based on Working Manual Entry)

{
  "videoConnections": [{
    "uuid": "...",
    "active": true,
    "alias": "TestHLSFeed",
    "feeds": [{
      "uuid": "...",
      "active": true,
      "alias": "TestHLSFeed",
      "protocol": "https",
      "address": "my-mediamtx-host.example.com",
      "port": 443,
      "path": "/my-stream/index.m3u8",
      "networkTimeout": 5000,
      "bufferTime": 500,
      "rtspReliable": "0",
      "type": "VIDEO"
    }]
  }]
}

Suggested Fix

The fix could be applied at the SDK level, the frontend level, or both:

Option A — SDK-level URL decomposition (@tak-ps/node-tak)

In FeedInput, accept the url field but decompose it in create() before posting to TAKServer:

// Pseudocode for create() in video.js
const parsed = new URL(feed.url);
return {
    uuid: randomUUID(),
    active: feed.active,
    alias: feed.alias,
    protocol: parsed.protocol.replace(':', ''),  // "https"
    address: parsed.hostname,                     // "host.example.com"
    port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
    path: parsed.pathname + parsed.search,        // "/stream/index.m3u8"
    networkTimeout: feed.networkTimeout || 5000,
    bufferTime: feed.bufferTime || 500,
    rtspReliable: feed.rtspReliable || "0",
    type: "VIDEO",
    ...feed,  // allow explicit overrides
};

Option B — Frontend field expansion (CloudTAK)

Add optional fields to VideosRemoteFeed.vue for port, bufferTime, networkTimeout, etc., with sensible defaults. This gives advanced users control while keeping the simple URL workflow.

Ideally both — the SDK should handle URL decomposition as a safety net, and the UI should expose the additional fields for users who need them.

Workaround

Create HLS (or any protocol) video feeds directly in TAKServer's Video Feed Manager (/Marti/VideoUpload.jsp) instead of through CloudTAK's Remote Video Connection interface. Example working configuration:

Field Value
Active ✓ (checked)
Alias TestHLSFeed
Protocol https
Address my-mediamtx-host.example.com
Port 443
Path /my-stream/index.m3u8
Network timeout (ms) 5000
Buffer time (ms) 500

Feeds created this way are visible and playable across all clients: CloudTAK, iTAK, and ATAK.

Affected Repos

  • dfpc-coe/node-takFeedInput schema is missing TAKServer's required fields; create() does not decompose URL or inject defaults
  • dfpc-coe/CloudTAKVideosRemoteFeed.vue only collects alias, url, and active; MenuVideosRemote.vue posts incomplete feed objects

Related Files

File Repo Role
dist/lib/api/video.js @tak-ps/node-tak SDK — FeedInput type, create() method
api/routes/marti-video.ts (lines 61–78) dfpc-coe/CloudTAK Backend route proxying POST /api/marti/video to TAKServer
api/web/src/components/CloudTAK/Menu/MenuVideosRemote.vue dfpc-coe/CloudTAK Frontend — connection save logic
api/web/src/components/CloudTAK/Menu/Videos/VideosRemoteFeed.vue dfpc-coe/CloudTAK Frontend — feed form (alias, url, active only)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions