flathub / flathub/org.videolan.VLC
No Buffering with Decrypted UDP Stream, Causing Playback Issues
- Dominant language
- Shell
- Stars
- 27
- Forks
- 25
- PR merge metrics
- No merged PRs in 30d
Description
this is my class
class VLCPlayerManager(private val context: Context) {
private var libVLC: LibVLC? = null
private var mediaPlayer: MediaPlayer? = null
private var demo: KotlinDemo? = null
private var decryptionProxy: UdpDecryptionProxy? = null
// Interface for callbacks
interface PlayerCallback {
fun onPlayerStateChanged(playing: Boolean, buffering: Boolean, msg: String)
fun onPlayerError(errorMessage: String)
}
fun initializePlayer(
videoLayout: VLCVideoLayout,
multicastGroup: String,
port: String,
bufferingProgressView: View,
callback: PlayerCallback? = null
): MediaPlayer {
try {
releasePlayer() // Ensure previous instance is properly released
} catch (e: Exception) {
e.printStackTrace()
}
initializeDemo()
// Configure VLC options
val options = ArrayList().apply {
// Core VLC options for optimal streaming performance
add("--verbose=4")
add("--no-drop-late-frames")
add("--no-skip-frames")
add("--network-caching=1500")
add("--file-caching=1500")
add("--live-caching=1500")
add("--clock-jitter=0")
add("--clock-synchro=0")
}
// Initialize LibVLC with options
try {
libVLC = LibVLC(context, options)
Log.d("VLCPlayerManager", "LibVLC initialized successfully")
} catch (e: Exception) {
Log.e("VLCPlayerManager", "Failed to initialize LibVLC", e)
}
// Create MediaPlayer instance
mediaPlayer = MediaPlayer(libVLC)
// Attach to the surface
Log.d("VLCPlayerManager", "VideoLayout width: ${videoLayout.width}, height: ${videoLayout.height}")
mediaPlayer?.detachViews()
mediaPlayer?.attachViews(videoLayout, null, false, false)
/* mediaPlayer?.vlcVout?.setVideoView(videoLayout)
mediaPlayer?.vlcVout?.attachViews()*/
// Configure MediaPlayer event listeners
mediaPlayer?.setEventListener { event ->
when (event.type) {
MediaPlayer.Event.Playing -> {
Log.d("VLCPlayerManager", "Player is playing")
val msg ="VLCPlayerManager Player is playing"
/* val width = mediaPlayer?.videowidth
val height = mediaPlayer?.videoHeight*/
// Log.d("VLCPlayerManager", "Video dimensions: ${width}x${height}")
bufferingProgressView.visibility = View.GONE
callback?.onPlayerStateChanged(true, false, msg)
}
MediaPlayer.Event.Paused -> {
Log.e("VLCPlayerManager", "Player is paused")
val msg ="VLCPlayerManager Player is paused"
callback?.onPlayerStateChanged(false, false, msg)
}
MediaPlayer.Event.Buffering -> {
val buffering = event.buffering > 0
val msg = ""
// Log.d("VLCPlayerManager", "Player is buffering: ${event.buffering}%")
bufferingProgressView.visibility = if (buffering) View.VISIBLE else View.GONE
callback?.onPlayerStateChanged(true, buffering,msg)
}
MediaPlayer.Event.EncounteredError -> {
val error = "VLC Playback error"
Log.e("VLCPlayerManager", error)
callback?.onPlayerError(error)
}
MediaPlayer.Event.EndReached -> {
val msg ="VLCPlayerManager Player reached end of stream"
Log.d("VLCPlayerManager", "Player reached end of stream")
callback?.onPlayerStateChanged(false, false, msg)
mediaPlayer?.play() // Auto-restart for live streams
}
}
}
// Start playback
val portNumber = port.toIntOrNull() ?: 1234
//startPlayback(multicastGroup, portNumber)
// startPlayback("239.100.100.2", 5001)
return mediaPlayer!!
}
private var decryptedStreamServer: DecryptedStreamServer? = null
fun startPlayback(multicastGroup: String, port: Int) {
// Initialize and start the decryption proxy
decryptionProxy = UdpDecryptionProxy(context, demo!!)
decryptionProxy?.start(multicastGroup, port)
// Find an available port for the HTTP server
val serverPort = findAvailablePort()
// Start the local HTTP server
decryptedStreamServer = DecryptedStreamServer(decryptionProxy!!, serverPort)
try {
decryptedStreamServer?.start()
} catch (e: IOException) {
Log.e("VLCPlayerManager", "Failed to start HTTP server", e)
// Toast.makeText(context, "Failed to start http server", Toast.LENGTH_SHORT).show()
return
}
// Play the stream from the local server
val streamUrl = "http://127.0.0.1:$serverPort"
Log.e("VLCPlayerManager", "streamUrl $streamUrl")
val media = Media(libVLC, Uri.parse(streamUrl))
configureMedia(media)
mediaPlayer?.media = media
media.release()
mediaPlayer?.play()
Handler(Looper.getMainLooper()).postDelayed({
if (!mediaPlayer?.isPlaying!!) {
Toast.makeText(context, "Player is still paused after delay", Toast.LENGTH_SHORT).show()
}
}, 5000) // 5 seconds
}
fun findAvailablePort(): Int {
return try {
val socket = ServerSocket(0) // 0 means the OS will assign an available port
val port = socket.localPort
socket.close()
port
} catch (e: Exception) {
Log.e("VLCPlayerManager", "Error finding available port", e)
8080 // Fallback to a default port
}
}
// here is my decryption logic
class UdpDecryptionProxy(
private val context: Context,
private val kotlinDemo: KotlinDemo
) {
private val isRunning = AtomicBoolean(false)
private var proxyThread: Thread? = null
private val decryptedDataQueue = LinkedBlockingQueue()
private var leftover: ByteArray? = null
// TS packet size constant
private val TS_PACKET_SIZE = 188
private val bufferMultiplier = PreferenceUtils.getBufferInt(PreferenceKeys.BUFFER_MULTIPLIER)
private val bufferSize = TS_PACKET_SIZE * bufferMultiplier
fun start(multicastGroup: String, port: Int) {
if (isRunning.get()) {
stop()
}
//Toast.makeText(context, "Starting...", Toast.LENGTH_SHORT).show()
isRunning.set(true)
proxyThread = Thread {
try {
// Toast.makeText(context, "IPAdd::$multicastGroup$port", Toast.LENGTH_SHORT).show()
runDecryptionProxy(multicastGroup, port)
} catch (e: Exception) {
Log.e("UdpDecryptionProxy", "Proxy error", e)
}
}
proxyThread?.start()
}
private fun runDecryptionProxy(multicastGroup: String, port: Int) {
val socket = MulticastSocket(port)
val group = InetAddress.getByName(multicastGroup)
socket.joinGroup(group)
val receiveBuffer = ByteArray(bufferSize)
val packet = DatagramPacket(receiveBuffer, receiveBuffer.size)
try {
while (isRunning.get() && !Thread.interrupted()) {
// Receive UDP packet
socket.receive(packet)
// Process the packet
val packetData = packet.data.copyOfRange(0, packet.length)
val combinedData = if (leftover != null && leftover!!.isNotEmpty()) {
leftover!! + packetData
} else {
packetData
}
// Find TS sync byte
val firstSyncIndex = combinedData.indexOfFirst { it == 0x47.toByte() }
if (firstSyncIndex < 0) {
Log.w("UdpDecryptionProxy", "No TS sync byte found")
leftover = null
continue
}
// Process aligned data
val alignedData = combinedData.copyOfRange(firstSyncIndex, combinedData.size)
val completePacketsCount = alignedData.size / TS_PACKET_SIZE
val completeBytesLength = completePacketsCount * TS_PACKET_SIZE
if (completeBytesLength == 0) {
leftover = alignedData
continue
}
val completeData = alignedData.copyOfRange(0, completeBytesLength)
leftover = if (completeBytesLength < alignedData.size) {
alignedData.copyOfRange(completeBytesLength, alignedData.size)
} else {
null
}
try {
// Decrypt the data
val decryptedData = kotlinDemo.decodeBytes(completeData, completeData.size)
// Add decrypted data to the queue
decryptedDataQueue.put(decryptedData)
} catch (e: Exception) {
Log.e("UdpDecryptionProxy", "Decryption error", e)
Toast.makeText(context, "Decryption Error ${e.message}", Toast.LENGTH_SHORT).show()
}
}
} catch (e: IOException) {
Log.e("UdpDecryptionProxy", "Socket error", e)
} finally {
try {
socket.leaveGroup(group)
socket.close()
} catch (e: IOException) {
Log.e("UdpDecryptionProxy", "Error closing socket", e)
}
}
}
fun getDecryptedData(size: Int): ByteArray? {
return try {
val decryptedData = decryptedDataQueue.take() // Blocking operation
// Log.d("UdpDecryptionProxy", "Decrypted data size: ${decryptedData.size}")
decryptedData
} catch (e: InterruptedException) {
Log.e("UdpDecryptionProxy", "Thread interrupted while waiting for data", e)
Thread.currentThread().interrupt() // Re-interrupt the thread
null
}
}
fun stop() {
// Toast.makeText(context, "Stopping...", Toast.LENGTH_SHORT).show()
isRunning.set(false)
proxyThread?.interrupt() // Interrupt the thread
proxyThread = null
decryptedDataQueue.clear()
}
}
// here is my server
class DecryptedStreamServer(
private val decryptionProxy: UdpDecryptionProxy,
port: Int
) : NanoHTTPD(port) {
override fun serve(session: IHTTPSession): Response {
Log.d("DecryptedStreamServer", "Request received: ${session.uri}")
return try {
// Create a chunked response
val response = newChunkedResponse(
Response.Status.OK,
"video/mp2t", // MIME type for MPEG-TS
object : InputStream() {
override fun read(): Int {
// Get decrypted data from the proxy
val decryptedData = decryptionProxy.getDecryptedData(2048 * 1024) // Adjust buffer size as needed
return if (decryptedData != null) {
// Log.d("DecryptedStreamServer", "Serving ${decryptedData.size} bytes")
decryptedData.inputStream().read()
} else {
-1 // End of stream
}
}
}
)
response
} catch (e: Exception) {
Log.e("DecryptedStreamServer", "Error serving request", e)
newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", "Error: ${e.message}")
}
}
override fun start() {
try {
super.start()
Log.d("DecryptedStreamServer", "Server started on port $listeningPort")
} catch (e: IOException) {
Log.e("DecryptedStreamServer", "Failed to start server", e)
throw e
}
}
}
i am always getting buffering 0.0% but in all logs i am getting proper buffer
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.