radek-k / FFMediaToolkit

FFMediaToolkit is a cross-platform video decoder/encoder library for .NET that uses FFmpeg native libraries. It supports video frames extraction, reading stream metadata and creating videos from bitmaps in any format supported by FFmpeg.
MIT License
352 stars 56 forks source link

End of file copying frames - but number of output frames doesn't equal number of input frames #113

Closed voltagex closed 2 years ago

voltagex commented 2 years ago
        public static void Convert(string inputFile)
        {
            FFmpegLoader.FFmpegPath = Path.Join(Directory.GetParent(Application.ExecutablePath).ToString(), "lib");
            string outputPath = Path.GetDirectoryName(inputFile);
            string outputFile = Path.GetFileNameWithoutExtension(inputFile) + ".mp4";
            outputFile = Path.Combine(outputPath, outputFile);
            var inputVideo = MediaFile.Open(inputFile);
            var outputContainer = MediaBuilder.CreateContainer(outputFile).WithVideo(new VideoEncoderSettings(1920, 1080, 30, VideoCodec.H264)).Create();
            int? inputFrameCount = inputVideo.Video.Info.NumberOfFrames;
            int i = 0;
            while (i != inputFrameCount)
            {
                outputContainer.Video.AddFrame(inputVideo.Video.GetNextFrame());
                i++;
            }
        }

I am trying to convert MJPEG in an AVI container (from a "wildlife camera") to H264 in an MP4 container

IMAG0020.zip

I get an exception about hitting the end of the file at frame 272, but inputVideo.Video.Info.NumberOfFrames says it has 330 frames.

Am I doing something wrong here? This does seem like a bit of a "long way round" for something equivalent to `ffmpeg -i video.avi -an video.mp4"

radek-k commented 2 years ago

FFMediaToolkit doesn't use FFmpeg tool itself, but its low-level API. If you just want to convert a video file I recommend using a FFmpeg command-line wrapper library such as Xabe.FFmpeg It would be more reliable and easier to setup.

voltagex commented 2 years ago

Sure, but I still think there's a bug with this library.

hey-red commented 2 years ago

@voltagex Number of frames is provided by container and it's might be inaccurate anyway.

If look to ffprobe report, you got same values:

ffprobe.exe -i .\IMAG0020.AVI -print_format json -loglevel fatal -show_streams -count_frames -select_streams v

Output:

 "nb_frames": "330",
 "nb_read_frames": "272",

You should avoid leverages to nb_frames, because this value might be incorrect. Also, not all containers store their frame count.

voltagex commented 2 years ago

Thanks, this makes more sense - I'd probably need to do two loops through the source video then to make sure I know how many frames to read.

hey-red commented 2 years ago

@voltagex

You can do it in one loop:

while (inputVideo.Video.TryGetNextFrame(out var frame))
{
    outputContainer.Video.AddFrame(frame);
}