Small tweaks (#1043)

* Make Future.consume() inline.

* Small player tweaks.
This commit is contained in:
Nikolay Igotti
2017-11-16 18:24:37 +03:00
committed by GitHub
parent 4d8fddef6e
commit 579e4a0d4c
3 changed files with 34 additions and 26 deletions
@@ -89,10 +89,10 @@ class Future<T> internal constructor(val id: FutureId) {
/**
* Blocks execution until the future is ready.
*/
fun <R> consume(code: (T) -> R) =
inline fun <R> consume(code: (T) -> R) =
when (state) {
FutureState.SCHEDULED, FutureState.COMPUTED -> {
val value = @Suppress("UNCHECKED_CAST") (consumeFuture(id) as T)
val value = @Suppress("UNCHECKED_CAST", "NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") (consumeFuture(id) as T)
code(value)
}
FutureState.INVALID ->
@@ -101,9 +101,7 @@ class Future<T> internal constructor(val id: FutureId) {
throw IllegalStateException("Future is cancelled")
}
fun result(): T {
return consume { it -> it }
}
fun result(): T = consume { it -> it }
val state: FutureState
get() = FutureState.values()[stateOfFuture(id)]
@@ -123,7 +121,7 @@ class Worker(val id: WorkerId) {
* until all scheduled jobs processed, or terminate immediately.
*/
fun requestTermination(processScheduledJobs: Boolean = true) =
Future<Any?>(requestTerminationInternal(id, processScheduledJobs))
Future<FutureId>(requestTerminationInternal(id, processScheduledJobs))
/**
* Schedule a job for further execution in the worker. Schedule is a two-phase operation,
@@ -218,6 +216,7 @@ external internal fun shallowCopyInternal(value: Any?): Any?
external internal fun stateOfFuture(id: FutureId): Int
@SymbolName("Kotlin_Worker_consumeFuture")
@kotlin.internal.InlineExposed
external internal fun consumeFuture(id: FutureId): Any?
@SymbolName("Kotlin_Worker_waitForAnyFuture")
@@ -27,14 +27,13 @@ data class OutputInfo(val width: Int, val height: Int, val pixelFormat: AVPixelF
data class VideoInfo(val width: Int, val height: Int, val fps: Double,
val sampleRate: Int, val channels: Int)
private data class AskNextAudioFrame(val size: Int)
data class VideoFrame(val buffer: CPointer<AVBufferRef>, val lineSize: Int) {
data class VideoFrame(val buffer: CPointer<AVBufferRef>, val lineSize: Int, val timeStamp: Double) {
fun unref() {
av_buffer_unref2(buffer)
}
}
data class AudioFrame(val buffer: CPointer<AVBufferRef>, var position: Int, val size: Int) {
data class AudioFrame(val buffer: CPointer<AVBufferRef>, var position: Int, val size: Int, val timeStamp: Double) {
fun unref() {
av_buffer_unref2(buffer)
}
@@ -68,15 +67,21 @@ class DecodeWorkerState(val formatContext: CPointer<AVFormatContext>,
var windowHeight = 0
var scaledFrameSize = 0
var noMoreFrames = false
val minAudioFrames = 1
val maxAudioFrames = 3
val minAudioFrames = 2
val maxAudioFrames = 5
val minVideoFrames = 5
fun makeVideoFrame(): VideoFrame {
// TODO: reuse buffers!
// Convert the frame from its movie format to window pixel format.
sws_scale(softwareScalingContext, videoFrame!!.pointed.data,
videoFrame!!.pointed.linesize, 0, videoHeight,
scaledVideoFrame!!.pointed.data, scaledVideoFrame!!.pointed.linesize)
val buffer = av_buffer_alloc(scaledFrameSize)!!
val ts = av_frame_get_best_effort_timestamp(videoFrame)* av_q2d(
videoCodecContext!!.pointed.time_base.readValue())
memcpy(buffer.pointed.data, scaledVideoFrame!!.pointed.data[0], scaledFrameSize.signExtend())
return VideoFrame(buffer, scaledVideoFrame!!.pointed.linesize[0])
return VideoFrame(buffer, scaledVideoFrame!!.pointed.linesize[0], ts)
}
fun makeAudioFrame(): AudioFrame {
@@ -87,9 +92,11 @@ class DecodeWorkerState(val formatContext: CPointer<AVFormatContext>,
resampledAudioFrame!!.pointed.nb_samples,
resampledAudioFrame!!.pointed.format,
1)
val ts = av_frame_get_best_effort_timestamp(audioFrame) * av_q2d(
audioCodecContext!!.pointed.time_base.readValue())
val buffer = av_buffer_alloc(audioFrameSize)!!
memcpy(buffer.pointed.data, resampledAudioFrame!!.pointed.data[0], audioFrameSize.signExtend())
return AudioFrame(buffer, 0, audioFrameSize)
return AudioFrame(buffer, 0, audioFrameSize, ts)
}
private fun setResampleOpt(name: String, value: Int) =
@@ -188,7 +195,8 @@ class DecodeWorkerState(val formatContext: CPointer<AVFormatContext>,
}
fun decodeIfNeeded() {
if (!needMoreBuffers()) return
if (!needMoreBuffers() || audioQueue.size() > audioQueue.maxSize - 20 ||
videoQueue.size() > videoQueue.maxSize - 5) return
memScoped {
val packet = alloc<AVPacket>()
@@ -200,10 +208,6 @@ class DecodeWorkerState(val formatContext: CPointer<AVFormatContext>,
avcodec_decode_video2(videoCodecContext, videoFrame, frameFinished.ptr, packet.ptr)
// Did we get a video frame?
if (frameFinished.value != 0) {
// Convert the frame from its movie format to window pixel format.
sws_scale(softwareScalingContext, videoFrame!!.pointed.data,
videoFrame!!.pointed.linesize, 0, videoHeight,
scaledVideoFrame!!.pointed.data, scaledVideoFrame!!.pointed.linesize)
videoQueue.push(makeVideoFrame())
}
}
@@ -233,7 +237,8 @@ class DecodeWorkerState(val formatContext: CPointer<AVFormatContext>,
if (videoQueue.isEmpty()) {
return null
}
return videoQueue.pop()
val frame = videoQueue.pop()!!
return frame
}
fun nextAudioFrame(size: Int): AudioFrame? {
@@ -247,7 +252,7 @@ class DecodeWorkerState(val formatContext: CPointer<AVFormatContext>,
if (frame.position + realSize == frame.size) {
return audioQueue.pop()
} else {
val result = AudioFrame(av_buffer_ref(frame.buffer)!!, frame.position, frame.size)
val result = AudioFrame(av_buffer_ref(frame.buffer)!!, frame.position, frame.size, frame.timeStamp)
frame.position += realSize
return result
}
@@ -271,8 +276,6 @@ class DecodeWorker {
fun workerId() = decodeWorker.id
fun init() {}
fun renderPixelFormat(pixelFormat: PixelFormat) = when (pixelFormat) {
PixelFormat.RGB24 -> AV_PIX_FMT_RGB24
PixelFormat.ARGB32 -> AV_PIX_FMT_RGB32
@@ -351,6 +354,13 @@ class DecodeWorker {
}
}
fun init() {
}
fun deinit() {
decodeWorker.requestTermination().result()
}
fun start(width: Int, height: Int, pixelFormat: PixelFormat) {
decodeWorker.schedule(TransferMode.CHECKED, {
OutputInfo(width, height, renderPixelFormat(pixelFormat))
@@ -361,7 +371,7 @@ class DecodeWorker {
decodeWorker.schedule(
TransferMode.CHECKED,
{ null }) { _ ->
state!!.stop()
state?.stop()
state = null
}.result()
}
@@ -372,9 +382,6 @@ class DecodeWorker {
{ null }) { _ -> (state == null || state!!.done()) as Boolean?
}.consume { it -> it!! }
fun deinit() {
decodeWorker.requestTermination().result()
}
fun requestDecodeChunk() = decodeWorker.schedule(
TransferMode.CHECKED,
@@ -132,6 +132,8 @@ class VideoPlayer(val requestedWidth: Int, val requestedHeight: Int) {
// Interframe pause, may lead to broken A/V sync, think of better approach.
if (state == State.PLAYING) {
// Request decoding.
decoder.requestDecodeChunk()
if (hasVideo) {
if (hasAudio) {
// Use sound for A/V sync.