Skip to content

Commit

Permalink
feat: use atomicfu
Browse files Browse the repository at this point in the history
  • Loading branch information
JellyBrick committed Aug 3, 2023
1 parent 446a32f commit 06e8018
Show file tree
Hide file tree
Showing 21 changed files with 95 additions and 96 deletions.
4 changes: 3 additions & 1 deletion lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ plugins {

repositories {
mavenCentral()
jcenter()
maven("https://jitpack.io")
}

dependencies {
testImplementation(group = "org.jetbrains.kotlin", name = "kotlin-test-junit5")
testImplementation(group = "org.jetbrains.kotlin", name = "kotlin-test")
testImplementation(group = "ch.qos.logback", name = "logback-classic", version = "1.4.8")

implementation(group = "org.jetbrains.kotlinx", name = "atomicfu", version = "0.21.0")

implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.3")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class MatroskaAudioTrack(trackInfo: AudioTrackInfo, private val inputStream: See
return try {
val file = MatroskaStreamingFile(inputStream)
file.readFile()
accurateDuration.set(file.duration.toInt().toLong())
accurateDuration.value = file.duration.toLong()
file
} catch (e: IOException) {
throw RuntimeException(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ open class MpegAudioTrack(trackInfo: AudioTrackInfo, private val inputStream: Se
try {
val fileReader = file.loadReader(trackConsumer)
?: throw FriendlyException("Unknown MP4 format.", FriendlyException.Severity.SUSPICIOUS, null)
accurateDuration.set(fileReader.duration)
accurateDuration.value = fileReader.duration
localExecutor.executeProcessingLoop(
{ fileReader.provideFrames() },
{ timecode: Long -> fileReader.seekToTimecode(timecode) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import be.zvz.klover.container.mpeg.reader.MpegVersionedSectionInfo
import be.zvz.klover.container.mpeg.reader.fragmented.MpegFragmentedFileTrackProvider
import be.zvz.klover.container.mpeg.reader.standard.MpegStandardFileTrackProvider
import be.zvz.klover.tools.io.SeekableInputStream
import kotlinx.atomicfu.AtomicBoolean
import kotlinx.atomicfu.atomic
import java.io.IOException
import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean

/**
* Handles processing an MP4 file for the purpose of streaming one specific track from it. Only performs seeks when
Expand Down Expand Up @@ -53,9 +54,9 @@ class MpegFileLoader(inputStream: SeekableInputStream) {
*/
fun parseHeaders() {
try {
val movieBoxSeen = AtomicBoolean()
val movieBoxSeen = atomic(false)
reader.inChain(root).handle("moov") { moov: MpegSectionInfo ->
movieBoxSeen.set(true)
movieBoxSeen.value = true
reader.inChain(moov).handle("trak") { trak: MpegSectionInfo -> parseTrackInfo(trak) }
.handle("mvex") { mvex: MpegSectionInfo -> fragmentedFileReader.parseMovieExtended(mvex) }
.handle("udta") { udta: MpegSectionInfo -> parseMetadata(udta) }.run()
Expand Down Expand Up @@ -118,9 +119,9 @@ class MpegFileLoader(inputStream: SeekableInputStream) {
if (!start && "sidx" == child.type) {
true
} else if (!start && "emsg" == child.type) {
movieBoxSeen.get()
movieBoxSeen.value
} else if (start && ("mdat" == child.type || "free" == child.type)) {
movieBoxSeen.get()
movieBoxSeen.value
} else {
false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import be.zvz.klover.container.mpeg.reader.MpegSectionInfo
import be.zvz.klover.container.mpeg.reader.MpegVersionedSectionInfo
import be.zvz.klover.tools.Units
import be.zvz.klover.tools.io.DetachedByteChannel
import kotlinx.atomicfu.atomic
import java.io.IOException
import java.nio.channels.Channels
import java.nio.channels.ReadableByteChannel
import java.util.concurrent.atomic.AtomicReference

/**
* Track provider for fragmented MP4 file format.
Expand Down Expand Up @@ -43,7 +43,7 @@ class MpegFragmentedFileTrackProvider(private val reader: MpegReader, private va
reader.skip(moof)
continue
}
val fragment = parseTrackMovieFragment(moof, consumer.track.trackId)
val fragment = parseTrackMovieFragment(moof, consumer.track.trackId)!!
val mdat = reader.nextChild(root)
val timecode = fragment.baseTimecode
reader.seek.seek(moof.offset + fragment.dataOffset)
Expand Down Expand Up @@ -133,8 +133,8 @@ class MpegFragmentedFileTrackProvider(private val reader: MpegReader, private va
}

@Throws(IOException::class)
private fun parseTrackMovieFragment(moof: MpegSectionInfo, trackId: Int): MpegTrackFragmentHeader {
val header = AtomicReference<MpegTrackFragmentHeader>()
private fun parseTrackMovieFragment(moof: MpegSectionInfo, trackId: Int): MpegTrackFragmentHeader? {
val header = atomic<MpegTrackFragmentHeader?>(null)
reader.inChain(moof).handle("traf") { traf: MpegSectionInfo ->
val builder = MpegTrackFragmentHeader.Builder()
reader.inChain(traf)
Expand All @@ -154,10 +154,10 @@ class MpegFragmentedFileTrackProvider(private val reader: MpegReader, private va
}
}.run()
if (builder.trackId == trackId) {
header.set(builder.build())
header.value = builder.build()
}
}.run()
return header.get()
return header.value
}

@Throws(IOException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object AudioPipelineFactory {
*/
@JvmStatic
fun isProcessingRequired(context: AudioProcessingContext, inputFormat: AudioDataFormat?): Boolean {
return context.outputFormat != inputFormat || context.playerOptions.volumeLevel.get() != 100 || context.playerOptions.filterFactory.get() != null
return context.outputFormat != inputFormat || context.playerOptions.volumeLevel.value != 100 || context.playerOptions.filterFactory.value != null
}

/**
Expand All @@ -32,7 +32,7 @@ object AudioPipelineFactory {
val end: UniversalPcmAudioFilter = FinalPcmAudioFilter(context, createPostProcessors(context))
val builder = FilterChainBuilder()
builder.addFirst(end)
if (context.filterHotSwapEnabled || context.playerOptions.filterFactory.get() != null) {
if (context.filterHotSwapEnabled || context.playerOptions.filterFactory.value != null) {
val userFilters = UserProvidedAudioFilters(context, end)
builder.addFirst(userFilters)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BufferingPostProcessor(private val context: AudioProcessingContext, privat
outputBuffer.clear()
encoder.encode(buffer, outputBuffer)
offeredFrame.timecode = timecode
offeredFrame.volume = context.playerOptions.volumeLevel.get()
offeredFrame.volume = context.playerOptions.volumeLevel.value
offeredFrame.setBuffer(outputBuffer)
context.frameBuffer.consume(offeredFrame)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class UserProvidedAudioFilters(

@Throws(InterruptedException::class)
private fun checkRebuild() {
if (hotSwapEnabled && context.playerOptions.filterFactory.get() !== chain.context) {
if (hotSwapEnabled && context.playerOptions.filterFactory.value !== chain.context) {
flush()
close()
chain = buildFragment(context, nextFilter)
Expand All @@ -62,7 +62,7 @@ class UserProvidedAudioFilters(
context: AudioProcessingContext,
nextFilter: UniversalPcmAudioFilter,
): AudioFilterChain {
val factory = context.playerOptions.filterFactory.get()
val factory = context.playerOptions.filterFactory.value
return if (factory == null) {
AudioFilterChain(nextFilter, emptyList(), null)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class AudioFrameVolumeChanger private constructor(
val volumeChanger = AudioFrameVolumeChanger(
context.configuration,
context.outputFormat,
context.playerOptions.volumeLevel.get(),
context.playerOptions.volumeLevel.value,
)
try {
volumeChanger.setupLibraries()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import java.nio.ShortBuffer
* Audio chunk post processor to apply selected volume.
*/
class VolumePostProcessor(private val context: AudioProcessingContext) : AudioPostProcessor {
private val volumeProcessor: PcmVolumeProcessor = PcmVolumeProcessor(context.playerOptions.volumeLevel.get())
private val volumeProcessor: PcmVolumeProcessor = PcmVolumeProcessor(context.playerOptions.volumeLevel.value)

@Throws(InterruptedException::class)
override fun process(timecode: Long, buffer: ShortBuffer) {
val currentVolume = context.playerOptions.volumeLevel.get()
val currentVolume = context.playerOptions.volumeLevel.value
if (currentVolume != volumeProcessor.lastVolume) {
AudioFrameVolumeChanger.apply(context)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package be.zvz.klover.natives

import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.atomicfu.atomic

/**
* Abstract instance of a class which holds native resources that must be freed.
*/
abstract class NativeResourceHolder : AutoCloseable {
private val released = AtomicBoolean()
private val released = atomic(false)

/**
* Assert that the native resources have not been freed.
*/
protected fun checkNotReleased() {
check(!released.get()) { "Cannot use the decoder after closing it." }
check(!released.value) { "Cannot use the decoder after closing it." }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import be.zvz.klover.player.event.AudioEvent
import be.zvz.klover.player.event.AudioEventListener
import be.zvz.klover.player.event.TrackEndEvent
import be.zvz.klover.player.event.TrackStartEvent
import kotlinx.atomicfu.AtomicLong
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.AtomicReference

/**
* Triggers cleanup checks on all active audio players at a fixed interval.
Expand All @@ -22,7 +23,7 @@ class AudioPlayerLifecycleManager(private val scheduler: ScheduledExecutorServic
Runnable,
AudioEventListener {
private val activePlayers: ConcurrentMap<AudioPlayer, AudioPlayer> = ConcurrentHashMap()
private val scheduledTask: AtomicReference<ScheduledFuture<*>> = AtomicReference()
private val scheduledTask: AtomicRef<ScheduledFuture<*>?> = atomic(null)

/**
* Initialise the scheduled task.
Expand Down Expand Up @@ -52,7 +53,7 @@ class AudioPlayerLifecycleManager(private val scheduler: ScheduledExecutorServic

override fun run() {
activePlayers.keys.forEach { player ->
player.checkCleanup(cleanupThreshold.get())
player.checkCleanup(cleanupThreshold.value)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package be.zvz.klover.player

import be.zvz.klover.filter.PcmFilterFactory
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import kotlinx.atomicfu.atomic

/**
* Mutable options of an audio player which may be applied in real-time.
Expand All @@ -11,16 +10,16 @@ class AudioPlayerOptions {
/**
* Volume level of the audio, see [AudioPlayer.volume]. Applied in real-time.
*/
val volumeLevel: AtomicInteger = AtomicInteger(100)
val volumeLevel = atomic(100)

/**
* Current PCM filter factory. Applied in real-time.
*/
val filterFactory: AtomicReference<PcmFilterFactory?> = AtomicReference()
val filterFactory = atomic<PcmFilterFactory?>(null)

/**
* Current frame buffer size. If not set, the global default is used. Changing this only affects the next track that
* is started.
*/
val frameBufferDuration: AtomicReference<Int> = AtomicReference()
val frameBufferDuration = atomic(-1)
}
22 changes: 11 additions & 11 deletions lib/src/main/kotlin/be/zvz/klover/player/DefaultAudioPlayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import be.zvz.klover.track.playback.AudioFrame
import be.zvz.klover.track.playback.AudioTrackExecutor
import be.zvz.klover.track.playback.LocalAudioTrackExecutor
import be.zvz.klover.track.playback.MutableAudioFrame
import kotlinx.atomicfu.atomic
import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.Volatile
import kotlin.concurrent.withLock
Expand All @@ -48,7 +48,7 @@ class DefaultAudioPlayer(private val manager: DefaultAudioPlayerManager) : Audio

@Volatile
private var shadowTrack: InternalAudioTrack? = null
private val paused: AtomicBoolean = AtomicBoolean()
private val paused = atomic(false)
private val listeners: MutableList<AudioEventListener> = mutableListOf()
private val trackSwitchLock: ReentrantLock = ReentrantLock()
private val options: AudioPlayerOptions = AudioPlayerOptions()
Expand Down Expand Up @@ -151,7 +151,7 @@ class DefaultAudioPlayer(private val manager: DefaultAudioPlayerManager) : Audio
var track: InternalAudioTrack? = null

lastRequestTime = System.currentTimeMillis()
if (timeout == 0L && paused.get()) {
if (timeout == 0L && paused.value) {
return null
}
while (activeTrack?.also { track = it } != null) {
Expand Down Expand Up @@ -188,7 +188,7 @@ class DefaultAudioPlayer(private val manager: DefaultAudioPlayerManager) : Audio
override fun provide(targetFrame: MutableAudioFrame, timeout: Long, unit: TimeUnit): Boolean {
var track: InternalAudioTrack? = null
lastRequestTime = System.currentTimeMillis()
if (timeout == 0L && paused.get()) {
if (timeout == 0L && paused.value) {
return false
}
while (activeTrack.also { track = it } != null) {
Expand Down Expand Up @@ -245,28 +245,28 @@ class DefaultAudioPlayer(private val manager: DefaultAudioPlayerManager) : Audio
}

override var volume: Int
get() = options.volumeLevel.get()
get() = options.volumeLevel.value
set(volume) {
options.volumeLevel.set(min(1000, max(0, volume)))
options.volumeLevel.value = min(1000, max(0, volume))
}

override var filterFactory: PcmFilterFactory?
get() = options.filterFactory.get()
get() = options.filterFactory.value
set(factory) {
options.filterFactory.set(factory)
options.filterFactory.value = factory
}

override var frameBufferDuration: Int
get() = options.frameBufferDuration.get()
get() = options.frameBufferDuration.value
set(value) {
options.frameBufferDuration.set(max(200, value))
options.frameBufferDuration.value = max(200, value)
}

/**
* @return Whether the player is paused
*/
override var isPaused: Boolean
get() = paused.get()
get() = paused.value
set(value) {
if (paused.compareAndSet(!value, value)) {
if (value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,19 @@ import be.zvz.klover.track.InternalAudioTrack
import be.zvz.klover.track.TrackStateListener
import be.zvz.klover.track.playback.AudioTrackExecutor
import be.zvz.klover.track.playback.LocalAudioTrackExecutor
import kotlinx.atomicfu.atomic
import org.slf4j.LoggerFactory
import java.util.Optional
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong

open class DefaultAudioPlayerManager : AudioPlayerManager {
override var trackStuckThreshold = 10000L
override var playerCleanupThreshold: Long
get() = cleanupThreshold.get()
get() = cleanupThreshold.value
set(value) {
cleanupThreshold.set(value)
cleanupThreshold.value = value
}
private val cleanupThreshold = AtomicLong(TimeUnit.MINUTES.toMillis(1))
private val cleanupThreshold = atomic(TimeUnit.MINUTES.toMillis(1))

val manager = Executors.newScheduledThreadPool(
1,
Expand Down Expand Up @@ -65,8 +64,10 @@ open class DefaultAudioPlayerManager : AudioPlayerManager {
return if (customExecutor != null) {
customExecutor
} else {
val bufferDuration =
Optional.ofNullable(playerOptions.frameBufferDuration.get()).orElse(frameBufferDuration)
val bufferDuration = when (playerOptions.frameBufferDuration.value) {
-1 -> frameBufferDuration
else -> playerOptions.frameBufferDuration.value
}
LocalAudioTrackExecutor(track, configuration, playerOptions, isUsingSeekGhosting, bufferDuration)
}
}
Expand Down
Loading

0 comments on commit 06e8018

Please sign in to comment.