patterncsharpMinor
Bare-bones C# MJPEG stream decoder implementation
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:
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,
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
I have to read the type from a cast on the RHS when an implicit cast exists. Just specify the type:
You allocate a lot more than just twice... There's the obvious ones like:
(You should also remove the namespace and add a using)
But there's also the less obvious:
I wouldn't worry about it though - unless you know the GC is causing you trouble.
Your code is really hard to follow, the
Magic numbers make code harder to follow. Why are
As far as I can see, all of your casts to
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.