How to implement IInputStream UWP - c#

I should get an IInputStream to pass to the method: CreateFromStreamAsync, I have to specifically use this method because I use an AudioGraph (the uri that I pass to the method is a uri to listen to the radio)
"HLS" I guessed it from the following link
Code class StreamRadio:
public class StreamRadio : IInputStream
{
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (token, progress) =>
{
await Task.Delay(2000);
var output = buffer.AsStream();
var outputRadio = output.AsInputStream();
IBuffer bufferRadio = new DataReader(outputRadio).ReadBuffer(64);
return bufferRadio;
});
}
public void Dispose()
{
throw new NotImplementedException();
}
}
Code Method:
StreamRadio streamRadio = new StreamRadio();
var adaptiveMediaSourceResult = await AdaptiveMediaSource.CreateFromStreamAsync(streamRadio, new Uri(uriRadio), "HLS");
if (adaptiveMediaSourceResult.Status != AdaptiveMediaSourceCreationStatus.Success)
{
return null;
}
Thanks in advance!

Solution:
var random = RandomAccessStreamReference.CreateFromUri(new Uri(uriRadio));
IRandomAccessStream stream = await random.OpenReadAsync();
var adaptiveMediaSourceResult = await AdaptiveMediaSource.CreateFromStreamAsync(stream, new Uri(uriRadio), "HLS");

Related

How to catch passed parameter and change it's value in Mocked method

I need to unit test really simple method that downloads file from Azure Blob storage. - DownloadFileAsync. Here is whole class.
public class BlobStorageService : IBlobStorageService
{
private readonly BlobServiceClient _blobStorageService;
public BlobStorageService(IBlobStorageConnector blobStorageConnector)
{
var connector = blobStorageConnector ?? throw new ArgumentNullException(nameof(blobStorageConnector));
_blobStorageService = connector.GetBlobStorageClient();
}
public async Task<Stream> DownloadFileAsync(string fileName, string containerName)
{
var container = _blobStorageService.GetBlobContainerClient(containerName);
var blob = container.GetBlobClient(fileName);
if (await blob.ExistsAsync())
{
using (var stream = new MemoryStream())
{
await blob.DownloadToAsync(stream);
stream.Position = 0;
return stream;
}
}
return Stream.Null;
}
}
}
The problem is that it requires quite a lot of mocking. I'm quite new to the idea of testing, so probably it's much better way to do that.
public class BlobStorageServiceTests
{
private string _containerName = "containerTest";
private string _blobName = "blob";
[Fact]
public async Task BlobStorageService_Should_Return_File()
{
// Arrange
Mock<IBlobStorageConnector> connectorMock = new Mock<IBlobStorageConnector>();
Mock<BlobServiceClient> blobServiceClientMock = new Mock<BlobServiceClient>();
Mock<BlobContainerClient> blobContainerClientMock = new Mock<BlobContainerClient>();
Mock<BlobClient> blobClientMock = new Mock<BlobClient>();
Mock<Response<bool>> responseMock = new Mock<Response<bool>>();
//Preparing result stream
string testString = "testString";
byte[] bytes = Encoding.ASCII.GetBytes(testString);
Stream testStream = new MemoryStream(bytes);
testStream.Position = 0;
responseMock.Setup(x => x.Value).Returns(true);
// this doesn't work, passed stream is not changed, does callback work with value not reference?
blobClientMock.Setup(x => x.DownloadToAsync(It.IsAny<Stream>(), CancellationToken.None)).Callback<Stream, CancellationToken>((stm, token) => stm = testStream);
blobClientMock.Setup(x => x.ExistsAsync(CancellationToken.None)).ReturnsAsync(responseMock.Object);
blobContainerClientMock.Setup(x => x.GetBlobClient(_blobName)).Returns(blobClientMock.Object);
blobServiceClientMock.Setup(x => x.GetBlobContainerClient(_containerName)).Returns(blobContainerClientMock.Object);
connectorMock.Setup(x => x.GetBlobStorageClient()).Returns(blobServiceClientMock.Object);
BlobStorageService blobStorageService = new BlobStorageService(connectorMock.Object); ;
// Act
var result = await blobStorageService.DownloadFileAsync(_blobName, _containerName);
StreamReader reader = new StreamReader(result);
string stringResult = reader.ReadToEnd();
// Assert
stringResult.Should().Contain(testString);
}
}
Everything works like a charm and only small part of the test causes problem.
This part to be exact:
// This callback works
blobClientMock.Setup(x => x.ExistsAsync(CancellationToken.None)).ReturnsAsync(responseMock.Object).Callback(() => Trace.Write("inside job"));
// this doesn't work, does callback not fire?
blobClientMock.Setup(x => x.DownloadToAsync(It.IsAny<Stream>(), CancellationToken.None)).ReturnsAsync(dynamicResponseMock.Object).Callback<Stream, CancellationToken>((stm, token) => Trace.Write("inside stream"));
//Part of tested class where callback should fire
if (await blob.ExistsAsync())
{
using (var stream = new MemoryStream())
{
await blob.DownloadToAsync(stream);
stream.Position = 0;
return stream;
}
}
The last part has slightly different code as in the beggining, I'm trying to just write to Trace. "Inside Job" shows well, "Inside stream" not at all. Is the callback not being fired? What can be wrong here?
As mentioned in the comments, you need to write to the captured stream, not replace it, to get the expected behavior
//...
blobClientMock
.Setup(x => x.DownloadToAsync(It.IsAny<Stream>(), CancellationToken.None))
.Returns((Stream stm, CancellationToken token) => testStream.CopyToAsync(stm, token));
//...
I know that it is not 100% the answer to this question, but you can solve the mocking problem by creating a stub like below:
public sealed class StubBlobClient : BlobClient
{
private readonly Response _response;
public StubBlobClient(Response response)
{
_response = response;
}
public override Task<Response> DownloadToAsync(Stream destination)
{
using (var archive = new ZipArchive(destination, ZipArchiveMode.Create, true))
{
var jsonFile = archive.CreateEntry("file.json");
using var entryStream = jsonFile.Open();
using var streamWriter = new StreamWriter(entryStream);
streamWriter.WriteLine(TrunkHealthBlobConsumerTests.TrunkHealthJsonData);
}
return Task.FromResult(_response);
}
}
and one more stub for BlobContainerClient like below:
public sealed class StubBlobContainerClient : BlobContainerClient
{
private readonly BlobClient _blobClient;
public StubBlobContainerClient(BlobClient blobClient)
{
_blobClient = blobClient;
}
public override AsyncPageable<BlobHierarchyItem> GetBlobsByHierarchyAsync(
BlobTraits traits = BlobTraits.None,
BlobStates states = BlobStates.None,
string delimiter = default,
string prefix = default,
CancellationToken cancellationToken = default)
{
var item = BlobsModelFactory.BlobHierarchyItem("some prefix", BlobsModelFactory.BlobItem(name: "trunk-health-regional.json"));
Page<BlobHierarchyItem> page = Page<BlobHierarchyItem>.FromValues(new[] { item }, null, null);
var pages = new[] { page };
return AsyncPageable<BlobHierarchyItem>.FromPages(pages);
}
public override BlobClient GetBlobClient(string prefix)
{
return _blobClient;
}
}
then simply arrange your test like this:
var responseMock = new Mock<Response>();
var blobClientStub = new StubBlobClient(responseMock.Object);
var blobContainerClientStub = new StubBlobContainerClient(blobClientStub);

Azure App Service- GZip Compression in Request

With this code (without the request compression part) I'm able to get gzip compressed content from Azure App Service (Xamarin.Froms App with offline sync). But when i try to gzip the request http-content i get a "Bad Request".
Any ideas? Is it possible to gzip the request content with Azure App Service?
namespace XXX.XXX.XXX.XXX.XXX
{
public class HttpGZipClientHandler : System.Net.Http.HttpClientHandler
{
long time = 0;
private long _downloadedBytesFromServer;
private long _downloadedProcessedBytes;
private long _intendedUploadedBytesToServer;
private long _uploadedBytesToServer;
private long _additionalTimeOverhead = 0;
public override bool SupportsAutomaticDecompression { get { return true; } }
public long DownloadedBytesFromServer { get { return _downloadedBytesFromServer; } }
public long DownloadedProcessedBytes { get { return _downloadedProcessedBytes; } }
public long IntendedUploadedBytesToServer { get { return _intendedUploadedBytesToServer; } }
public long UploadedBytesToServer { get { return _uploadedBytesToServer; } }
public long AdditionalTimeOverhead { get { return _additionalTimeOverhead; } }
public void ResetStatistics()
{
_downloadedBytesFromServer = 0;
_downloadedProcessedBytes = 0;
_intendedUploadedBytesToServer = 0;
_uploadedBytesToServer = 0;
_additionalTimeOverhead = 0;
}
protected override async Task<System.Net.Http.HttpResponseMessage> SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
//Save content headers before compressing
System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>> savedContentHeaders = new Dictionary<string, IEnumerable<string>>();
foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in request.Content.Headers)
{
savedContentHeaders.Add(keyValue.Key, keyValue.Value);
}
//Compress request content
System.Diagnostics.Stopwatch sp1 = new System.Diagnostics.Stopwatch();
sp1.Start();
_intendedUploadedBytesToServer += request.Content.Headers.ContentLength.HasValue ? request.Content.Headers.ContentLength.Value : 0;
await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Compress);
byte[] uploadedBytes = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
_uploadedBytesToServer += uploadedBytes.Length;
sp1.Stop();
_additionalTimeOverhead += sp1.ElapsedMilliseconds;
//Set headers
foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in savedContentHeaders)
{
request.Content.Headers.Add(keyValue.Key, keyValue.Value);
}
request.Headers.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
request.Content.Headers.Add("Content-Encoding", "gzip");
//Execute request
System.Net.Http.HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
_downloadedBytesFromServer += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;
//Decompress response content
if (response.Content.Headers.ContentEncoding.Contains("gzip"))
{
System.Diagnostics.Stopwatch sp2 = new System.Diagnostics.Stopwatch();
sp2.Start();
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
response.Content = new HttpGZipContent(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);
byte[] processedBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
_downloadedProcessedBytes += processedBytes.Length;
sp2.Stop();
_additionalTimeOverhead += sp2.ElapsedMilliseconds;
}
else
_downloadedProcessedBytes += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;
return response;
}
}
internal sealed class HttpGZipContent : System.Net.Http.HttpContent
{
private readonly byte[] _content;
private readonly System.IO.Compression.CompressionMode _compressionMode;
public HttpGZipContent(byte[] content, System.IO.Compression.CompressionMode compressionMode)
{
_compressionMode = compressionMode;
_content = content;
}
protected override async System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
{
if (_compressionMode == System.IO.Compression.CompressionMode.Compress)
{
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content.Length))
{
using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Compress))
{
zipStream.Write(_content, 0, _content.Length);
zipStream.Flush();
}
byte[] compressed = memoryStream.ToArray();
System.IO.MemoryStream copyStream = new System.IO.MemoryStream(compressed);
await copyStream.CopyToAsync(stream).ConfigureAwait(false);
}
}
else
{
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content, 0, _content.Length))
{
using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Decompress))
{
await zipStream.CopyToAsync(stream).ConfigureAwait(false);
}
}
}
}
protected override bool TryComputeLength(out long length)
{
length = _content.Length;
return true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
}
Based on my understanding, you need to implement the request decompression for your mobile app back-end. If you are using the C# backend, you could create your custom ActionFilterAttribute as follows:
public class RequestDeCompressFilter : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var request = actionContext.Request;
if (request.Content.Headers.ContentEncoding.Contains("GZIP"))
{
await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);
}
//TODO: compress the response, you could follow http://www.intstrings.com/ramivemula/articles/jumpstart-47-gzipdeflate-compression-in-asp-net-mvc-application/
await base.OnActionExecutingAsync(actionContext, cancellationToken);
}
public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
//you could also compress the response here
return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
}
}
Then, mark your action as follows:
[RequestDeCompressFilter]
public async Task<IHttpActionResult> PostMessage(Message item)
Also, you could follow HTTP Message Handlers in ASP.NET Web API to implement your HTTP message handler.

Reading an HttpError result from HttpResponseMessage without using exceptions

I'm trying to pull an HttpError out of an HttpResponseMessage message which may or not be there. If the Api throws an exception it will be serialised as an HttpError however errors such as 404's will not be in this format.
I've managed to fix this bug in the code below by catching to exception thrown if we fail to deserialize the HttpError.
The issue is now I'm using exception driven development.
Idealy I want something like this.
var httpError = await response.Content.TryReadAsAsync<HttpError>(formatters);
if (httpError == null)
{
// Definetly not an HttpError and no exception thrown
}
Surely the must be an easy way of telling the type of the content in the HttpContent?
public static async Task<ApiResponseMessage<T>> GetApiResponseAsync<T>(this HttpResponseMessage response, IEnumerable<MediaTypeFormatter> formatters) where T : class
{
if (!response.IsSuccessStatusCode)
{
HttpError httpError;
// Exception driven programming
try
{
// Could use string?
var contentString = response.Content.ReadAsStringAsync();
// This doesn't work. Throws exception if not correct type
var contentObject = await response.Content.ReadAsAsync<object>();
var alwaysNull = contentObject as HttpError;
httpError = await response.Content.ReadAsAsync<HttpError>(formatters);
}
catch (Exception)
{
httpError = null;
}
return new ApiResponseMessage<T>
{
IsSuccess = false,
HttpError = httpError,
Response = response
};
}
return new ApiResponseMessage<T>
{
IsSuccess = true,
Result = await response.Content.ReadAsAsync<T>(formatters),
Response = response
};
}
Cleaned up the code so it at least compiles.
public class ReadAsyncResult<T>
{
public ReadAsyncResult()
{
}
public ReadAsyncResult(T result)
{
Result = result;
IsSuccess = result != null;
}
public T Result { get; set; }
public bool IsSuccess { get; set; }
public static async Task<ReadAsyncResult<T>> TryReadAsAsync<T>(HttpContent content)
{
return await TryReadAsAsync<T>(content, CancellationToken.None);
}
public static async Task<ReadAsyncResult<T>> TryReadAsAsync<T>(HttpContent content,
CancellationToken cancellationToken)
{
if (content == null)
return new ReadAsyncResult<T>();
var type = typeof(T);
var objectContent = content as ObjectContent;
if (objectContent?.Value != null && type.IsInstanceOfType(objectContent.Value))
{
return new ReadAsyncResult<T>((T) objectContent.Value);
}
var mediaType = content.Headers.ContentType;
var reader =
new MediaTypeFormatterCollection(new MediaTypeFormatterCollection()).FindReader(type, mediaType);
if (reader == null) return new ReadAsyncResult<T>();
var value = await ReadAsAsyncCore<T>(content, type, reader, cancellationToken);
return new ReadAsyncResult<T>(value);
}
private static async Task<T> ReadAsAsyncCore<T>(HttpContent content, Type type, MediaTypeFormatter formatter,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var stream = await content.ReadAsStreamAsync();
var result = await formatter.ReadFromStreamAsync(type, stream, content, null, cancellationToken);
return (T) result;
}
}
It is of course, blindingly simple.
var message = new HttpResponseMessage();
HttpError httpError;
message.TryGetContentValue(out httpError);
if (httpError != null)
{
// Do stuff
}
Edit:
This didn't fix my issue as the content type was not of type ObjectResult. I was expecting the TryGetContentValue to work in the same way as HttpContent.ReadAsAsync.
After digging through the source code for ReadAsAsync I have created a working solution.
public class ReadAsyncResult<T>
{
public ReadAsyncResult()
{
}
public ReadAsyncResult(T result)
{
Result = result;
IsSuccess = result != null;
}
public T Result { get; set; }
public bool IsSuccess { get; set; }
}
public static async Task<ReadAsyncResult<T>> TryReadAsAsync<T>(this HttpContent content)
{
return await TryReadAsAsync<T>(content, CancellationToken.None);
}
public static async Task<ReadAsyncResult<T>> TryReadAsAsync<T>(this HttpContent content, CancellationToken cancellationToken)
{
if (content == null)
return new ReadAsyncResult<T>();
var type = typeof(T);
var objectContent = content as ObjectContent;
if (objectContent?.Value != null && type.IsInstanceOfType(objectContent.Value))
{
return new ReadAsyncResult<T>((T)objectContent.Value);
}
var mediaType = content.Headers.ContentType;
var reader = new MediaTypeFormatterCollection(new MediaTypeFormatterCollection()).FindReader(type, mediaType);
if (reader == null) return new ReadAsyncResult<T>();
var value = await ReadAsAsyncCore<T>(content, type, reader, cancellationToken);
return new ReadAsyncResult<T>(value);
}
private static async Task<T> ReadAsAsyncCore<T>(HttpContent content, Type type, MediaTypeFormatter formatter, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var stream = await content.ReadAsStreamAsync();
var result = await formatter.ReadFromStreamAsync(type, stream, content, null, cancellationToken);
return (T) result;
}

How to play a video stream that requires authentication?

I have a Windows Store app (C#/XAML) which communicates with a REST service. At some point, I need to play a video stream provided by this service.
If I just assign the stream URI to the MediaElement.Source property, it doesn't work, because the request needs to be authenticated. I need to customize the request sent by the MediaElement control in order to add cookies, credentials and some other custom headers, but I can't find any method or property to do this.
How can I do it? Is it even possible?
OK, I got it working. Basically, the solution has 2 parts:
make the HTTP request manually (with any required credentials or headers)
wrap the response stream in a custom IRandomAccessStream that implements Seek by making another request to the server, using the Range header to specify which part of the stream I need.
Here's the RandomAccessStream implementation:
delegate Task<Stream> AsyncRangeDownloader(ulong start, ulong? end);
class StreamingRandomAccessStream : IRandomAccessStream
{
private readonly AsyncRangeDownloader _downloader;
private readonly ulong _size;
public StreamingRandomAccessStream(Stream startStream, AsyncRangeDownloader downloader, ulong size)
{
if (startStream != null)
_stream = startStream.AsInputStream();
_downloader = downloader;
_size = size;
}
private IInputStream _stream;
private ulong _requestedPosition;
public void Dispose()
{
if (_stream != null)
_stream.Dispose();
}
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (cancellationToken, progress) =>
{
progress.Report(0);
if (_stream == null)
{
var netStream = await _downloader(_requestedPosition, null);
_stream = netStream.AsInputStream();
}
var result = await _stream.ReadAsync(buffer, count, options).AsTask(cancellationToken, progress);
return result;
});
}
public void Seek(ulong position)
{
if (_stream != null)
_stream.Dispose();
_requestedPosition = position;
_stream = null;
}
public bool CanRead { get { return true; } }
public bool CanWrite { get { return false; } }
public ulong Size { get { return _size; } set { throw new NotSupportedException(); } }
public IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer) { throw new NotSupportedException(); }
public IAsyncOperation<bool> FlushAsync() { throw new NotSupportedException(); }
public IInputStream GetInputStreamAt(ulong position) { throw new NotSupportedException(); }
public IOutputStream GetOutputStreamAt(ulong position) { throw new NotSupportedException(); }
public IRandomAccessStream CloneStream() { throw new NotSupportedException(); }
public ulong Position { get { throw new NotSupportedException(); } }
}
It can be used like this:
private HttpClient _client;
private void InitClient()
{
_client = new HttpClient();
// Configure the client as needed with CookieContainer, Credentials, etc
// ...
}
private async Task StartVideoStreamingAsync(Uri uri)
{
var request = new HttpRequestMessage(HttpMethod.Get, uri);
// Add required headers
// ...
var response = await _client.SendAsync(request);
ulong length = (ulong)response.Content.Headers.ContentLength;
string mimeType = response.Content.Headers.ContentType.MediaType;
Stream responseStream = await response.Content.ReadAsStreamAsync();
// Delegate that will fetch a stream for the specified range
AsyncRangeDownloader downloader = async (start, end) =>
{
var request2 = new HttpRequestMessage();
request2.Headers.Range = new RangeHeaderValue((long?)start, (long?)end);
// Add other required headers
// ...
var response2 = await _client.SendAsync(request2);
return await response2.Content.ReadAsStreamAsync();
};
var videoStream = new StreamingRandomAccessStream(responseStream, downloader, length);
_mediaElement.SetSource(videoStream, mimeType);
}
The user can seek to an arbitrary position in the video, and the stream will issue another request to get the stream at the specified position.
It's still more complex than I think it should be, but it works...
Note that the server must support the Range header in requests, and must issue the Content-Length header in the initial response.

HttpResponseMessage called twice for content type image/jpeg

My machine is hosting a RESTful service on MS WebAPI (.net4) and is also connected to a camera. I can make a request to have the camera take a snapshot (jpg) and show the captured image.
Strange thing is, the request is always called 2x - 2 images are captured but only the last image is returned as output. (using google chrome postman to test)
in my server:
var config = new HttpSelfHostConfiguration(string.Format("http://localhost:{0}/", port));
config.MessageHandlers.Add(new SimpleFileHandler());
config.Formatters.Add(new JpegTypeFormatter());
config.Routes.MapHttpRoute( /* the route map */
in SimpleFileHandler
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken);
}
A jpeg content type formatter called JpegMediaFormatter:
public class JpegTypeFormatter : MediaTypeFormatter
{
private static Type _supportedType = typeof(MemoryStream);
public JpegTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue(MediaTypeNames.Image.Jpeg));
}
public override bool CanReadType(Type type)
{
return type == _supportedType;
}
public override bool CanWriteType(Type type)
{
return type == _supportedType;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
throw new NotImplementedException();
}
private Task GetWriteTask(Stream stream, MemoryStream data)
{
return new Task(() =>
{
var ms = new MemoryStream(data.ToArray());
ms.CopyTo(stream);
});
}
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
if (value == null)
{
//value = new byte[0];
value = new MemoryStream();
}
Task writeTask = GetWriteTask(writeStream, (MemoryStream)value);
writeTask.Start();
return writeTask;
}
}
Camera capture RESTful call:
[HttpGet]
public HttpResponseMessage Capture(int width)
{ // call camera API, capture image and save as img
var memoryStream = new MemoryStream();
img.Save(memoryStream, ImageFormat.Jpeg);
return Request.CreateResponse(HttpStatusCode.OK, memoryStream,
new MediaTypeHeaderValue(MediaTypeNames.Image.Jpeg));
}
Somehow calling new MediaTypeHeaderValue() with JPEG content will cause the GET to be called twice.
If I change to call some other MediaType i.e. application/json, the GET is not called 2x.
Any ideas why this is happening?
Edit: additional details
server code:
public Server : IDisposable
{
HttpSelfHostServer _server;
public Server()
{
var config = new HttpSelfHostConfiguration("http://localhost:30019");
config.Routes.MapHttpRoute("capture", "camera/capture/");
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
...
}
Camera controller:
public class CameraController : ApiController
{
// camera -> variable to camera h/w
var camera = HW.Camera;
var img = camera.Capture();
if(img != null)
{
var memoryStream = new MemoryStream();
img.Save(memoryStream, ImageFormat.Jpeg);
var resp = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK;
Content = new PushStreamContent((respStream, cnt, ctx) =>
{
using(respStream)
{
memoryStream.WriteTo(respStream);
}
});
};
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return resp;
}
}
I am trying to understand your requirement...what type is 'img'? and also i think you can avoid creating a custom formatter..example below:
[HttpGet]
public HttpResponseMessage Capture(int width)
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new PushStreamContent((responseStream, httpContent, transportContext) =>
{
using (responseStream)
{
img.Save(responseStream, ImageFormat.Jpeg);
}//closing this important!
});
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return response;
}
if you have an image already as a file, then you could just do the following to respond back with an image:
[HttpGet]
public HttpResponseMessage Capture(int width)
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StreamContent(File.OpenRead(#"C:\Images\Car.jpg"));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return response;
}

Categories