1
Vote

ResumingFileStreamResult is throwing an error when trying to get length of the stream

description

any ideas why I would be getting this error when using ResumingFileStreamResult? I am trying to stream an MP4 from a wcf service. the type of object being passed into your method is of Stream.

[NotSupportedException: Specified method is not supported.]
System.ServiceModel.Dispatcher.MessageBodyStream.get_Length() +78
VikingErik.Mvc.ResumingActionResults.ResumingActionResultBase.ExecuteResult(ControllerContext context) +306

file attachments

comments

VikingErik wrote Jul 21, 2014 at 10:21 PM

The error suggests the stream you're providing doesn't support getting the length. That would suggest you can't seek on the stream either, probably. Is there a different type of stream you can get? If not, you could try reading the stream into a memory stream first, rewinding it and handing that over but that could be prohibitive for large streams.

I think this library won't be suitable for you if you can't provide a stream that supports getting the length and seeking (since seeking is necessary for responses to byte range requests).

VikingErik wrote Jul 21, 2014 at 10:27 PM

It's been a long time since I've looked at this code but I believe there's a way to find out what ranges were requested if that would help you. You could just request the parts you need from your service and you'd already know then length in that case.

I'm sure we can figure something out if I know more about your specific problem.

wrote Jul 23, 2014 at 4:47 PM

kquinbar wrote Jul 23, 2014 at 4:47 PM

yeah i figured out that wcf never transfers filestreams directly. it always converts to a networkstream which is not seekable and would explain my original error.

so i got your class to work by copying the stream returned from wcf to a memorystream and passing that as the parameter in the constructor for your class. the problem is that IOS (or possibly my front end, videojs) actually make multiple requests, some of them with a byte range of 0-1. in all cases, i am transferring the entire stream from wcf which is obviously inefficient. I was trying to figure out if maybe its possible to only return the requested part of the stream from my wcf service, but i wanted to know if your class expects that it is always dealing with the entire stream and does a seek based on the range requested itself. if it does, do you think it might be possible that i could change that so it always just passes on the entire stream i set as the parameter, and then i could do the seek in my wcf service when i read the filestream on that end and just return a partial stream?

i have attached some example code i was playing around with. in this case i am not using a wcf service, but just accessing a local video file the same way i would inside my wcf service service. maybe take a look and if you have any thoughts on whether this is possible. its interesting though because from what i can tell in my scenerio of IOS and use of videojs, it seems like other than that initial request of range 0-1, the other requests that are being made seem to pass in a request size of the entire stream. this made me question whether i could even pass back a partial stream. or maybe a change has to be made to your class to support this notion?

any ideas you have would be appreciated.

VikingErik wrote Jul 23, 2014 at 5:35 PM

iOS does a partial request (bytes 0-1) to check that the streaming service supports byte requests and partial responses. That's why sometimes you'll see a video come up with an icon saying it's not playable by iOS - it knows because the server isn't responding to a byte request with an HTTP Partial Response matching the request.

It's been awhile but the first version of this library would pass in the parsed parameters to your action so you could go query your resource and return just what you need. I tried to make that process easier in this version. I'll have to play with this some to see what might be workable.

Just off the top of my head, assuming there's a way for you, in your action, to see what bytes were requested. Would it be too much trouble to instantiate a "partial response" type object where you supply the necessary part(s) and return that?

Something like:

public ActionResult ServeVideoStream(<specific parameters you need>, RequestDetails streamRequest) {
foreach(var requestRange in streamRequest.ByteRanges)
{
    streamRequest.Response[requestRange] = GetStreamSegment(requestRange.StartByte, requestRange.EndByte);
}

return streamRequest;
}

That's just a complete off-the-top-of-my-head idea and I would have to do some proof of concept to see if I can do it but I'm curious if the syntax isn't too heavy for your needs. I was hoping to abstract this all away but it seems in your case this isn't helpful because you can't return just the range you want.