Reputation: 999
I am trying to stream a video from websocket url in android. The url looks something like this ws://abc.com:80/api/streaming/download/mp4/ . I am not really sure how to proceed because this thing is new for me. I tried searching on the internet and i found only one solution on stackoverflow which says to connect with websocket using okhttp and then use okhttpdatasource with Exoplayer. So I tried connecting to websocket url using okhttp and i am successfully recieving bytestream. Here is the code:
public void startListen() {
// Request request = new Request.Builder().url(mContext.getResources().getString(R.string.ws_url)).build();
Request request = new Request.Builder().url(getApplicationContext().getResources().getString(R.string.myUrl)).build();
//final ByteBuffer buf = ByteBuffer.allocate(MediaBlockSize);
mWebSocket = mOkHttpClient.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
Log.d("MNMN", String.valueOf(response));
}
@Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
Log.d("MNMN", "text = " + text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
//Log.d("MNMNx", "size = " + bytes.size());
final byte b[] = bytes.toByteArray();
try {
mOutputStream.write(b);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
super.onClosing(webSocket, code, reason);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
Log.d("MNMN", String.valueOf(response));
}
});
}
But i don't really know how to make it work with Exoplayer. Exoplayer has extension for okhttpdatasource but i didn't find any good tutorial of using it. Can someone guide me how can I use stream received from okhttp with exoplayer to play the video?
Upvotes: 2
Views: 2716
Reputation: 3786
I know its late to answer. But in case someone else looking for this.
I had similar situation. I don't think OkHttpDataSource
is meant for wss
.
I came up with my own Exo DataSource that works as expected.
wss
using OkHttp's WebSocketListener
class WssDataStreamCollector @Inject constructor() : WebSocketListener() {
private val wssData = ConcurrentSkipListSet<ByteString>()
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
wssData.add(bytes)
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
super.onClosing(webSocket, code, reason)
wssData.removeAll(wssData)
}
fun canStream(): Boolean {
return wssData.size > 0
}
fun getNextStream(): ByteString {
return wssData.pollFirst()
}
}
class WssStreamDataSource : BaseDataSource(true) {
@Inject
lateinit var httpClient: OkHttpClient
@Inject
lateinit var dataStreamCollector: WssDataStreamCollector
var webSocketClient: WebSocket? = null
private var currentByteStream: ByteArray? = null
private var currentPosition = 0;
private var remainingBytes = 0;
override fun open(dataSpec: DataSpec): Long {
// Form the request and open the socket.
// Provide the listener
// which collects the data for us (Previous class).
webSocketClient = httpClient.newWebSocket(
Request.Builder().apply {
dataSpec.httpRequestHeaders.forEach { entry ->
addHeader(entry.key, entry.value)
}
}.url(dataSpec.uri.toString()).build(),
dataStreamCollector
)
return -1 // Return -1 as the size is unknown (streaming)
}
override fun getUri(): Uri? {
webSocketClient?.request()?.url?.let {
return Uri.parse(it.toString())
}
return null
}
override fun read(target: ByteArray, offset: Int, length: Int): Int {
// return 0 (nothing read) when no data present...
if (currentByteStream == null && !dataStreamCollector.canStream()) {
return 0
}
// parse one (data) ByteString at a time.
// reset the current position and remaining bytes
// for every new data
if (currentByteStream == null) {
currentByteStream = dataStreamCollector.getNextStream().toByteArray()
currentPosition = 0
remainingBytes = currentByteStream?.size ?: 0
}
val readSize = min(length, remainingBytes)
currentByteStream?.copyInto(target, offset, currentPosition, currentPosition + readSize)
currentPosition += readSize
remainingBytes -= readSize
// once the data is read set currentByteStream to null
// so the next data would be collected to process in next
// iteration.
if (remainingBytes == 0) {
currentByteStream = null
}
return readSize
}
override fun close() {
// close the socket and relase the resources
webSocketClient?.cancel()
}
// Factory class for DataSource
class Factory : DataSource.Factory {
override fun createDataSource(): DataSource = WssStreamDataSource()
}
}
That's all, you are good to go.
ProgressiveMediaSource
with the DataSource
we created like below.SimpleExoPlayer(yourBuilder).apply {
setVideoSurfaceView(surfaceView)
val mediaItem = MediaItem.fromUri(uri) // URI with wss://
val factory = ProgressiveMediaSource.Factory(WssStreamDataSource.Factory())
addMediaSource(factory.createMediaSource(mediaItem))
prepare()
playWhenReady = true
play()
}
Upvotes: 2