Esempio n. 1
0
    public IEnumerable <BitmapStream> GetImagesFromMedia(string inputPath, int frameCount, CancellationToken cancellationToken, Action <string> log = null, bool autoToneMapHDR = true)
    {
        var mediaInfo = GetMediaInfo(inputPath, cancellationToken, log);

        log?.Invoke("Reading images from FFmpeg...");

        var fps       = frameCount / mediaInfo.Duration.TotalSeconds;
        var fpsFilter = $"fps={fps.ToInvariantString()}";

        // Note: tone mapping algorithms have been tested (*cough* *cough* on the Interstellar movie only =°)
        // and compared to the SDR reference barcode, hable seems to give the closest result.
        // Warning: this is very slow!
        // https://web.archive.org/web/20190722004804/https://stevens.li/guides/video/converting-hdr-to-sdr-with-ffmpeg/
        var hdrToSdrFilter = "zscale=transfer=linear:npl=100,format=gbrpf32le,zscale=primaries=bt709,tonemap=tonemap=hable,zscale=transfer=bt709:matrix=bt709:range=tv,format=yuv420p";

        var vfilters = new List <string> {
            fpsFilter
        };

        if (mediaInfo.IsHDR && autoToneMapHDR)
        {
            log?.Invoke("HDR video detected. Adding tone mapping filter.");
            vfilters.Add(hdrToSdrFilter);
        }

        // Output a raw stream of bitmap images taken at the specified frequency
        var args = $"-i \"{inputPath}\" -vf \"{string.Join(",", vfilters)}\" -c:v bmp -f rawvideo -an -";

        log?.Invoke($"FFmpeg arguments: {args}");

        var process = StartFfmpegInstance(args, redirectError: log != null);

        if (log != null)
        {
            process.ErrorDataReceived += (s, e) => log(e.Data);
            process.BeginErrorReadLine();
        }

        IEnumerable <BitmapStream> GetLazyStream()
        {
            using (cancellationToken.Register(() => TryKill(process)))
                using (var reader = new BinaryReader(process.StandardOutput.BaseStream))
                {
                    while (BitmapStream.TryCreate(reader, out var bitmapStream, cancellationToken))
                    {
                        yield return(bitmapStream);
                    }
                }
        }

        return(GetLazyStream());
    }
    public static bool TryCreate(BinaryReader reader, out BitmapStream bitmapStream, CancellationToken cancellationToken)
    {
        try
        {
            // https://en.wikipedia.org/wiki/BMP_file_format
            var magicNumber = reader.ReadBytes(2);
            if (magicNumber.Length != 2)
            {
                bitmapStream = null;
                return(false);
            }

            if (magicNumber[0] != 0x42 || magicNumber[1] != 0x4D)
            {
                throw new InvalidDataException();
            }

            var bmpSizeBytes = reader.ReadBytes(4);
            var bmpSize      = BitConverter.ToInt32(bmpSizeBytes, 0);

            var remainingDataLength = bmpSize - bmpSizeBytes.Length - magicNumber.Length;
            var remainingData       = reader.ReadBytes(remainingDataLength);

            var ms = new MemoryStream();
            ms.Write(magicNumber, 0, magicNumber.Length);
            ms.Write(bmpSizeBytes, 0, bmpSizeBytes.Length);
            ms.Write(remainingData, 0, remainingData.Length);
            ms.Position = 0;

            bitmapStream = new BitmapStream(ms);
            return(true);
        }
        catch (Exception)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                cancellationToken.ThrowIfCancellationRequested();
                throw new OperationCanceledException();
            }
            else
            {
                throw;
            }
        }
    }
Esempio n. 3
0
 public override void Flush()
 {
     BitmapStream?.Flush();
     DataA?.Flush();
     DataB?.Flush();
 }