How to peek or execute for first item of Sequence (onEach() and first())?

#1

I’ve got a sequence (of images). I’d like to init some stuff (a video recorder) based on info from the sequence (the resolution of the images). Is there any way to say “if this is the first element of the sequence, do this extra stuff, otherwise, do normal things”?

Here is the catch: The video recorder is an AutoClosable resource. So I’d love to “peek” into the first element in the sequence, but I can’t.

fun Sequence<BufferedImage>.framesToVideoFile(
    destinationFileName: String = "out.mp4",
    imageWidth: Int = 1280, // *** I want these to default to the image res
    imageHeight: Int = 720 // *** I want these to default to the image res
) {
    var frameCount = 0
    FFmpegFrameRecorder(destinationFileName, imageWidth, imageHeight, 0).apply {
        frameRate = 30.0
        videoBitrate = 0 // max
        videoQuality = 0.0 // max
        start()
    }.use { ffr ->
        LOG.info { "Starting recording to $destinationFileName (${ffr.imageWidth}, ${ffr.imageHeight})" }

        for (bi in this) {
            val frame = BufferedImage(ffr.imageWidth, ffr.imageHeight, BufferedImage.TYPE_INT_ARGB)
            frame.createGraphics()!!.apply {
                drawImage(bi, 0, 0, null)
                dispose()
            }
            ffr.record(converter.get().convert(frame), avutil.AV_PIX_FMT_ARGB)
            if (frameCount > 0 && frameCount and frameCount - 1 == 0) {
                LOG.debug { "Recorded frame $frameCount" }
            }
            frameCount++
        }
    }
    LOG.info { "All frames recorded: $frameCount" }
}
#2
for((ind, bi) in this.withIndex()) {
    if(ind == 0) ...
}
// or
var startup = true
for(bi in this) {
   if(startup){
       startup = false
       // do initialization based on first element
   }
   ...
}

They aren’t the most elegant solution, but either of those should work perfectly. I guess the second version might be a bit better, as it is save from integer overflows for infinite sequences and there might be a chance that the JIT compiler can optimize away the if statement (but this shouldn’t have any kind of performance impact anyways)

#3

Agree with withIndex approach.
Is there any way to combine that with the try-with-resources use that I had in the existing code? I can’t quite think of how to fit it together…

What I’d really like is something akin to

MyResource(mySequence.peekFirst().width).use { for (bi in this) { ...} }

#4

Kinda hacky but seems to work:

fun <T> Sequence<T>.peekFirst():Pair<Sequence<T>, T> {
    val firstValue = this.first()
    return Pair(sequenceOf(firstValue) + this, firstValue)
}