Investigations are Failing to export, even though camera id's exist

I properly need a pair of new eyes on my code.

(It’s unpolish, i know.)

The code was working earlier today. I could get it to download a zip file with a .mkv file in it.

However, then i started to make it more complete as a webapi soulution. And now for some reason it’s giving me an “UnknownCameraID”. All the time.

Even though the camera id is something that i lookup in the same code, from the camera name. And the camera is found and the Guid of the camera is correct.

What am i doing wrong here? When i log onto the XProtect Management Client i can see the investigations i am creating. It’s also listing the camera that i want to export from. but nothing gets exported.

using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.IO.Compression;
using VideoOS.Mobile.Portable.MetaChannel;
using VideoOS.Mobile.Portable.MetaChannel.CommEntries;
using VideoOS.Mobile.Portable.Utilities;
using VideoOS.Mobile.Portable.VideoChannel.Params;
using VideoOS.Mobile.Portable.ViewGroupItem;
using VideoOS.Mobile.SDK.Portable.Server.Base.CommandResults;
using VideoOS.Mobile.SDK.Portable.Server.Base.Connection;
using VideoOS.Mobile.SDK.Portable.Server.ViewGroups;
namespace api.Controllers
{
[ApiController]
[Route(“[controller]”)]
public class VideoExportController : ControllerBase
{

    private static bool Initialized = false;
    private static Connection Connection { get; set; }
    private static string Username { get; set; }
    private static string Password { get; set; }
    private static UserType UserType { get; set; } = UserType.Unknown;

    private void OnConnectSuccess(ConnectResponse responseParams)
    {
        Connection.LogIn(Username, Password, ClientTypes.MobileClient, UserType, null,
            OnLoginSuccess, OnFail);
    }

    private void OnFail(BaseCommandResponse responseParams)
    {
        Connection = null;
        Initialized = true;
    }

    private static void OnLoginSuccess(BaseCommandResponse responseParams)
    {
        Connection.RunHeartBeat = true;
        Initialized = true;
    }

    private async Task Connect()
    {

        string httpaddress = "surv-mob-01.thansen.dk";
        uint httpport = 8082;

        Username = "username";
        Password = "password";

        UserType = UserType.ActiveDirectory;


        Connection = new Connection(ChannelTypes.HTTPSecure, httpaddress, httpport)
        {
            CommandsQueueing = CommandsQueueing.PerCommandTask
        };

        var result = await Connection.ConnectAsync(null, new TimeSpan(0, 0, 10));

        var loginSettings = new LoginSettingsParams()
        {
            UserName = Username,
            Password = Password,
            LoginType = UserType.ActiveDirectory,
            ClientType = ClientTypes.MobileClient
        };

        var loginResponse = await Connection.LogInAsync(loginSettings, null, new TimeSpan(0, 0, 10));
    }

    private async Task<string> PerformExport(string cameraname, DateTime startTime, DateTime endTime)
    {
        await Connect();


        var cameraNames = new[] { cameraname };
        var commandTimeout = TimeSpan.FromSeconds(120);

        await Connection.Views.GetViewsAsync(new ViewParams(), commandTimeout);

        var cameraIds = ViewGroupsHelper.GetAllCamerasView(Connection.Views, new ViewParams(), commandTimeout)
            .Descendants(ViewItemType.Camera)
            .Where(cam => cameraNames.Contains(cam.ItemName))
            .Select(cam => cam.ItemId);

        // Test if at least one camera with that name was found.
        if (!cameraIds.Any())
        {
            return "Camera with Milestone Name '" + (cameraname ?? "null") + "' not found";
        }

        // Create investigation
        var options = new InvestigationParams
        {
            Name = "WebAPI video Export",
            StartTime = startTime,
            EndTime = endTime,
            State = InvestigationState.InProgress,
        };
        options.SetCameras(cameraIds);
        var createInvestigationResponse = Connection.Investigations.CreateInvestigation(options, commandTimeout);

        // always check any request's return code. If it's not OK, add a proper handling. For brevity, I'll only show this once
        if (createInvestigationResponse.ErrorCode != ErrorCodes.Ok)
        {
            return createInvestigationResponse.ErrorMessage;
        }

        var investigationId = createInvestigationResponse.InvestigationId;
        var sw = Stopwatch.StartNew();
        while (sw.Elapsed < TimeSpan.FromMinutes(1) // longer investigations may take quite a lot of time to prepare
            && Connection.Investigations.GetInvestigation(investigationId, commandTimeout).Investigations.Single().Status != InvestigationSatus.Saved)
        {
            Thread.Sleep(1000);
        }

        if (investigationId == Guid.Empty)
            return "Empty Investigation ID - Export not possible";


        Connection.Investigations.UpdateInvestigation(
new InvestigationParams
{
    InvestigationId = investigationId,
    State = InvestigationState.Completed
}, commandTimeout);

        var exportParams = new ExportParams
        {
            InvestigationId = investigationId,
            CameraId = cameraIds.FirstOrDefault(),
            Type = ExportType.Mkv,
        };
        var a = Connection.InvestigationExports.StartInvestigationExport(exportParams, commandTimeout);
        var exportId = a.ExportId;

        sw.Restart();
        while (sw.Elapsed < TimeSpan.FromMinutes(1) // longer exports may take quite a lot of time to prepare
            && Connection.Exports.GetExport(exportId, commandTimeout).Exports.FirstOrDefault().State != (int)ExportState.Ready)
        {
            Thread.Sleep(1000);
        }
        if (exportId == Guid.Empty)
        {
          //  return a.ErrorCode.ToString();
        }
        exportParams.ExportId = exportId;
        var downloadLink = "https://surv-mob-01.thansen.dk:8082/" + Connection.Exports.CreateExportDownloadLink(exportParams, commandTimeout).ExportLink;
        return downloadLink;
    }

    [HttpGet("export")]
    public async Task<IActionResult> Export([FromQuery] DateTime FromDateTime, [FromQuery] DateTime ToDateTime, string CameraName)
    {
        var url = await PerformExport(CameraName, FromDateTime, ToDateTime);
        if (url == null || !url.Contains("/XProtectMobile/Export/"))
            return BadRequest("Ikke muligt at eksportere Video - " + url);

        var handler = new HttpClientHandler
        {
            UseDefaultCredentials = true
        };

        var client = new HttpClient(handler);
        var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);

        if (!response.IsSuccessStatusCode)
            return StatusCode((int)response.StatusCode);

        var zipStream = await response.Content.ReadAsStreamAsync();
        using var archive = new ZipArchive(zipStream);

        var mkvEntry = archive.Entries.First(x => x.Name.EndsWith(".mkv"));
        var mkvStream = mkvEntry.Open();

        return File(mkvStream, "video/x-matroska", "video.mkv");
    }

}

}

Ahhhh…. I found the issue.

The Error message is not very precise.

Explanation, if others have the same issue.

  1. I could logon to the mobile server webinterface, and i could see the investigations on that interface. So the investigations were created correctly.
  2. The Export however failed with “Unknown Camera ID” error message. It did not matter if i just set the investigation or also added the camera id. Same error message.

It turns out that my issue was the Timestamp. The Milestone Server recordings must be looked up by UTC timestamps. So i was providing timestamps that had an offset of +1 hour.

And if no recordings are made within that period, you also get the UnknownCameraID error message. You do not get an ‘No Recordings Found’ errror.

That is quite misleading.

1 Like