简体   繁体   English

在 iOS 上用 C#.net7.0 解析 MJPEG stream

[英]Parsing MJPEG stream with C# net7.0 on iOS

I want to parse a MJPEG stream with.net7.0 on iOS. My current solution is working on windows and android, but on iOS I can not receive the raw stream.我想在 iOS 上使用 .net7.0 解析 MJPEG stream。我当前的解决方案是在 windows 和 android 上工作,但在 iOS 上我无法接收原始 stream。

What is MJPEG?什么是 MJPEG? MJPEG is a constant stream of jpegs. MJPEG 是 jpeg 的常量 stream。 The content starts with header informations, after that is an image, and after that there is a header again in the content.内容以header信息开头,后面是一张图片,再后面的内容里还有一个header。 Example of a response:响应示例:

Content-Type: image/jpeg
Content-Length: 50706


{Image}

Content-Type: image/jpeg
Content-Length: 50750


{Image2}

After the header, there are always 2 line breaks.在 header 之后,总是有 2 个换行符。 So I parse the response to find 2 line breaks, parse the Content-Lenght and know, that the following 50706 bytes are the image.所以我解析响应以找到 2 个换行符,解析Content-Lenght并知道以下 50706 字节是图像。 I extract the image from the stream parse for the next 2 line breaks and so on.我从 stream 解析中提取图像用于接下来的 2 个换行符等。

My code is like this:我的代码是这样的:

HttpResponseMessage headerResponse = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
Stream stream = headerResponse.Content.ReadAsStreamAsync();
{Parsing stuff} ...

My problem now is, that iOS is not handing over the raw content.我现在的问题是,iOS 没有交出原始内容。 There are no header informations in the content stream. It is like this:内容stream中没有header信息,是这样的:

{Image}
{Image2}

So there is no way for me to split the stream in single images.所以我无法将 stream 拆分为单个图像。 The headerResponse.Content.GetContentLegth() is not the correct value, also it is not changing while parsing the stream. I already tried different HttpClients ( NSUrlSessionHandler, CFNetworkHandler ) headerResponse.Content.GetContentLegth()不是正确的值,在解析 stream 时它也没有改变。我已经尝试过不同的 HttpClients( NSUrlSessionHandler、CFNetworkHandler

So is there a way, to prevent iOS from removing the Header informations from the content stream?那么有没有办法防止iOS从内容stream中删除Header信息?

It is running now, thanks to 2 posts I found in the inte.net: Swift Mjpeg Streaming Only Showing Single Frame and Download Files in Xamarin iOS in the Background它现在正在运行,感谢我在 inte.net 中找到的 2 个帖子: Swift Mjpeg Streaming Only Showing Single FrameDownload Files in Xamarin iOS in the Background

My solution is a mix of both我的解决方案是两者的结合

public class MyImageReceiver
{
    public async Task Get(string requestUri, Action<IEnumerable<byte>> imageReceived, int timeout, CancellationToken cancellationToken)
    {
        bool isTimeout = false;
        DateTime lastImageReceivedAt = DateTime.Now;

        var config = NSUrlSessionConfiguration.DefaultSessionConfiguration;
        using (NSUrl nsUrl = NSUrl.FromString(requestUri))
        using (NSMutableUrlRequest request = new NSMutableUrlRequest(nsUrl,
                cachePolicy: NSUrlRequestCachePolicy.ReloadIgnoringLocalCacheData,
                timeoutInterval: 2000))
        using (CameraPictureClientSessionDelegate cameraPictureClientSessionDelegate = new CameraPictureClientSessionDelegate())
        using (NSUrlSession session = NSUrlSession.FromConfiguration(config, cameraPictureClientSessionDelegate, null))
        using (NSUrlSessionDataTask streamingTask = session.CreateDataTask(request: request))
        {
            request.HttpMethod = "GET";


            cameraPictureClientSessionDelegate.ImageReceived += (s, e) =>
            {
                lastImageReceivedAt = DateTime.Now;
                _waitHandle.Release();
                imageReceived(e);
            };
            
            streamingTask.Resume();

            do
            {
                await _waitHandle.WaitAsync(timeout, cancellationToken);
                if ((DateTime.Now - lastImageReceivedAt).TotalMilliseconds > timeout)
                {
                    isTimeout = true;
                }
            }
            while ((streamingTask.State == NSUrlSessionTaskState.Running
                    || streamingTask.State == NSUrlSessionTaskState.Suspended)
                    && !cancellationToken.IsCancellationRequested
                    && !isTimeout);
            if (streamingTask.State == NSUrlSessionTaskState.Running
                || streamingTask.State == NSUrlSessionTaskState.Suspended)
            {
                streamingTask.Cancel();
            }
            if (isTimeout)
            {
                throw new TimeoutException();
            }
        }
    }
}

And the delegate和代表

public class CameraPictureClientSessionDelegate :
    NSUrlSessionDataDelegate,
    INSUrlSessionTaskDelegate
{
    private SemaphoreSlim _semaphore = new SemaphoreSlim(1);
    public event EventHandler<IEnumerable<byte>> ImageReceived;
    List<byte> _data = new List<byte>();
    public override void DidReceiveData(NSUrlSession session, NSUrlSessionDataTask dataTask, NSData data)
    {
        try
        {
            _semaphore.Wait();
            _data.AddRange(data.ToArray());
        }
        finally
        {
            _semaphore.Release();
        }
    }

    public override void DidReceiveResponse(NSUrlSession session, NSUrlSessionDataTask dataTask, NSUrlResponse response, Action<NSUrlSessionResponseDisposition> completionHandler)
    {
        try
        {
            _semaphore.Wait();
            if (_data.Count > 0)
            {
                ImageReceived?.Invoke(this, _data);
                _data = new List<byte>();
            }
        }
        finally
        {
            _semaphore.Release();
        }
        completionHandler(NSUrlSessionResponseDisposition.Allow);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM