bytedeco / javacv

Java interface to OpenCV, FFmpeg, and more
Other
7.39k stars 1.56k forks source link

How to get evenly distributed frames in android? #2239

Closed ankhafizov closed 2 weeks ago

ankhafizov commented 2 weeks ago

I have a problem to get frames from my video .mp4 h264 to List in android. I successfully solved it in python and transferred code to kotlin.

val cap = VideoCapture(videoPath)
var timestamps = DoubleArray(nframes) { i -> (i * (duration - 0.25) / (nframes - 1)) }
val duration = getMp4DurationInSec(videoPath)
timestamps.forEach { ts ->
            cap.set(CAP_PROP_POS_MSEC, ts * 1000.0)
            Log.i("signal", "CAP_PROP_POS_MSEC ${cap.get(CAP_PROP_POS_MSEC)}") // always give zeros
            val frame = JavaCVMat()
            cap.read(frame)
            Log.i("signal", "tsUS $ts, ${frame.type()}")
            rotate(frame, frame, Core.ROTATE_90_COUNTERCLOCKWISE)
            matList.add(frame)
}

I can't uderstand, why cap.set(CAP_PROP_POS_MSEC) does not work. Some articles in medium says that traditionally it comes with org.opencv becausde it does not have ffmpeg backend. But I installed ffmpeg and still get errors. This is my graddle

implementation group: 'org.bytedeco', name: 'javacv', version: '1.5.10' javacpp group: 'org.bytedeco', name: 'openblas-platform', version: '0.3.26-1.5.10' javacpp group: 'org.bytedeco', name: 'opencv-platform', version: '4.9.0-1.5.10' javacpp group: 'org.bytedeco', name: 'ffmpeg-platform', version: '6.1.1-1.5.10'

Even if I use .avi format with mjpeg the code does not work correctly. Alternativelly I can use MediaMetadataRetriever, but it is too slow, nevertheless it works well. How to solve the problem? can anyone suggest?

saudet commented 2 weeks ago

Try to use FFmpegFrameGrabber instead

ankhafizov commented 2 weeks ago

Frame grabber does not work as well. Finally solved the problem using mjpeg avi format and by index-grabbing:

private fun getMatsFromVideo(): List<JavaCVMat> {
        val matList = mutableListOf<JavaCVMat>()

        val cap = VideoCapture(videoPath)
        val frameCount = cap.get(CAP_PROP_FRAME_COUNT)
        val fps = cap.get(CAP_PROP_FPS)
        val duration = frameCount / fps
        val nframes = (duration * extractFps + 1).toInt()
        Log.i("AcceptPanoramaActivity", "frameCount $frameCount fps $fps duration $duration")
        var indices = DoubleArray(nframes) { i -> (i * frameCount / (nframes - 1)) }

        indices.forEach { idx ->
            cap.set(CAP_PROP_POS_FRAMES, idx)
            val frame = JavaCVMat()
            cap.read(frame)
            Log.i("AcceptPanoramaActivity", "idx $idx, ${frame.type()}")
            rotate(frame, frame, Core.ROTATE_90_COUNTERCLOCKWISE)
            matList.add(frame)
        }

        return matList
    }