HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Bare-bones C# MJPEG stream decoder implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
streambaredecoderimplementationbonesmjpeg

Problem

I tried to prove to myself that MJPEG stream decoding can be accomplished using bare-bones .NET.

So I wrote a class that decode MJPEG streams, trying to keep tips in mind:

  • No allocation, except the two arrays during startup



  • Using a delegate called in a separate Task, so the image processing/display by the caller does not cause interruption



  • Trying to benefit from TPL async features during stream initialization and reading.



Right now I must confess it works fine for some streams decoded side by side.

On one hand, I welcome any suggestion that would make this code more efficient.

On the other, I would like to maximize the amount of streams I could decode together.

I tried to use the TPL Dataflow to pipeline the process, unfortunately it requires some workaround that induces the penalties I tried to avoid here (allocation, etc...) and the results are bad.

I also tried unsafe code, which speeds things so insignificantly that I feel it does not worth to keep it.

So my question remains, how can I adapt this code to decode the most MJPEG streams I can?

If you are interested in this problem, I released a project on Github to make it run with some open IP cams.

```
using System;
using System.Drawing;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleMJPEGStreamViewer {
static class SimpleMJPEGDecoder {

///
/// Start a MJPEG on a http stream
///
/// Delegate to run at each frame
/// url of the http stream (only basic auth is implemented)
/// optional login
/// optional password (only basic auth is implemented)
/// cancellation token used to cancel the stream parsing
/// Max chunk byte size when reading stream
/// Maximum frame byte size
///
public async static Task StartAsync(Action action, string url, string login = null, string password = null, CancellationToken? token = null,

Solution

This is going to be a quick review to kick things off...

As this is a public method should should check the arguments and throw suitable exceptions.

I like var but don't use it when it makes code less obvious!

var previous = (byte)0;


I have to read the type from a cast on the RHS when an implicit cast exists. Just specify the type:

byte previous = 0;


You allocate a lot more than just twice... There's the obvious ones like:

cli.DefaultRequestHeaders.Authorization = new System.Net...


(You should also remove the namespace and add a using)

But there's also the less obvious:

Task.Run(() => action(img));


I wouldn't worry about it though - unless you know the GC is causing you trouble.

Your code is really hard to follow, the ParseBuffer method is a bit too long for my liking and you have a goto which IMO should never be used.

Magic numbers make code harder to follow. Why are 0xd8, 0xd9 and 0xFF significant? I have no way of knowing what they represent from your code.

As far as I can see, all of your casts to byte in ParseBuffer are redundant.

Code Snippets

var previous = (byte)0;
byte previous = 0;
cli.DefaultRequestHeaders.Authorization = new System.Net...
Task.Run(() => action(img));

Context

StackExchange Code Review Q#123394, answer score: 2

Revisions (0)

No revisions yet.