1

I'm using FFmpeg to make a video from image scequences. My purpose is to create a video has a transparent background in mov/mp4 format.

Either using "-c:v qtrle" or "-c:v png" can achieve my purpose, however "-c:v png" is always giving me a video with the super big size.

Normally is around 500kb, using "-c:v png" will increase to 30~40MB. The data rate is unusually high, how can I fix it?

Here is my command ffmpeg -r 30 -i testImage_%03d.png -vcodec png test.mov
I have tried adding bitrate maximum to the command, it didn't work.

BTW "-c:v qtrle" works fine, but quicktime has some problems in Windows, so I tend not to use it.

4
  • 1
    How do you plan to use the output? – Gyan Nov 28 '17 at 6:42
  • I want to edit my video in after effect. – Ives Nov 28 '17 at 6:53
  • 1
    If you have an image sequence, why don't you simply import that in AE? Also, most intermediate-codecs (and most codecs that allow for alpha-channels are intended to be intermediate ones) are not designed for compression, but for losslessness, such as PNG (though it isn't the best example). – flolilo Nov 29 '17 at 4:10
  • @flolilolilo Oh! I think you're right, I've been looking the solution for days, maybe I should leave the size problem. – Ives Nov 29 '17 at 5:17
0

Png is a format for compressing and storing individual images, not for video. So you just copy your image sequence to an image sequence in the test.mov container. In that container, each image needs just as much bytes as it would when stored directly to your hard disk. Because, unlike most of the common video formats, an image format like png does and can not exploit similarities between successive images to obtain higher compression ratios.

Now to obtain a high compression ratio, video formats have to relate image information from 2 or more images. Most of them also filter out quite a lot of pixel colour information, assuming you will not notice that in moving images. Both techniques for reducing the video size make it quite difficult to keep track of which pixel has what transparency.

Therefor most video formats that do show transparency rely on single image format like png. And even then, maybe the only format that is widely supported is Shockwave. There seems to be no format that combines high compression with transparency.

Conclusion is, you'll have to leave out at least 1 of 3 items: small file size, wide support or transparency.

2
  • Thank you for the detailed explanation. 3 listed items, each of them is important for me, size probably is the least important one, but now it's too big to accept. – Ives Nov 29 '17 at 0:58
  • Shockwave is still around!? Last time I heard of it RealPlayer was still considered "good". ;-) Also, I don't see how a well-compressing, alpha-including codec would be less supported than, say, a bloated, alpha-including codec - alpha-including codecs are usually lossless and therefore not used outside of post-production. But stating that e.g. openEXR isn't widely supported is plainly false. – flolilo Nov 29 '17 at 4:03
0

You can try UT video. It is a free lossless compressed format that supports an alpha channel (as RGBA), decoding and encoding is natively supported by ffmpeg, and it is easy to install to allow integration into After Effects, Adobe Media Encoder, etc.

ffmpeg -framerate 30 -i testImage_%03d.png -c:v utvideo test.avi

I don't have access to AE at the moment, so I did not test this. Make sure to restart AE if you installed it while it was open.

3
  • thanks, but this gives me bigger size than -c:v png – Ives Nov 29 '17 at 5:12
  • @Ives It was worth a try. I was assuming the difference wouldn't be much. You can try adding -pred median output option for a small decrease in file size. But flolilolilo raises a good point so perhaps moot. – llogan Nov 29 '17 at 5:23
  • Adding -pred median doesn't change a lot, around 5% decreased, but thanks. – Ives Nov 29 '17 at 5:27
0

Here is my solution for creating an ffmpeg file that is not too large.

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class ConsoleAppManager
{
    private readonly string appName;
    private readonly Process process = new Process();
    private readonly object theLock = new object();
    private SynchronizationContext context;
    private string pendingWriteData;

    public ConsoleAppManager(string appName)
    {
        this.appName = appName;

        this.process.StartInfo.FileName = this.appName;
        this.process.StartInfo.RedirectStandardError = true;
        this.process.StartInfo.StandardErrorEncoding = Encoding.UTF8;

        this.process.StartInfo.RedirectStandardInput = true;
        this.process.StartInfo.RedirectStandardOutput = true;
        this.process.EnableRaisingEvents = true;
        this.process.StartInfo.CreateNoWindow = true;

        this.process.StartInfo.UseShellExecute = false;

        this.process.StartInfo.StandardOutputEncoding = Encoding.UTF8;

        this.process.Exited += this.ProcessOnExited;
    }

    public event EventHandler<string> ErrorTextReceived;
    public event EventHandler ProcessExited;
    public event EventHandler<string> StandartTextReceived;

    public int ExitCode
    {
        get { return this.process.ExitCode; }
    }

    public bool Running
    {
        get; private set;
    }

    public void ExecuteAsync(params string[] args)
    {
        if (this.Running)
        {
            throw new InvalidOperationException(
                "Process is still Running. Please wait for the process to complete.");
        }

        string arguments = string.Join(" ", args);

        this.process.StartInfo.Arguments = arguments;

        this.context = SynchronizationContext.Current;

        this.process.Start();
        this.Running = true;

        new Task(this.ReadOutputAsync).Start();
        new Task(this.WriteInputTask).Start();
        new Task(this.ReadOutputErrorAsync).Start();
    }

    public void Write(string data)
    {
        if (data == null)
        {
            return;
        }

        lock (this.theLock)
        {
            this.pendingWriteData = data;
        }
    }

    public void WriteLine(string data)
    {
        this.Write(data + Environment.NewLine);
    }

    protected virtual void OnErrorTextReceived(string e)
    {
        EventHandler<string> handler = this.ErrorTextReceived;

        if (handler != null)
        {
            if (this.context != null)
            {
                this.context.Post(delegate { handler(this, e); }, null);
            }
            else
            {
                handler(this, e);
            }
        }
    }

    protected virtual void OnProcessExited()
    {
        EventHandler handler = this.ProcessExited;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }

    protected virtual void OnStandartTextReceived(string e)
    {
        EventHandler<string> handler = this.StandartTextReceived;

        if (handler != null)
        {
            if (this.context != null)
            {
                this.context.Post(delegate { handler(this, e); }, null);
            }
            else
            {
                handler(this, e);
            }
        }
    }

    private void ProcessOnExited(object sender, EventArgs eventArgs)
    {
        this.OnProcessExited();
    }

    private async void ReadOutputAsync()
    {
        var standart = new StringBuilder();
        var buff = new char[1024];
        int length;

        while (this.process.HasExited == false)
        {
            standart.Clear();

            length = await this.process.StandardOutput.ReadAsync(buff, 0, buff.Length);
            standart.Append(buff.SubArray(0, length));
            this.OnStandartTextReceived(standart.ToString());
            Thread.Sleep(1);
        }

        this.Running = false;
    }

    private async void ReadOutputErrorAsync()
    {
        var sb = new StringBuilder();

        do
        {
            sb.Clear();
            var buff = new char[1024];
            int length = await this.process.StandardError.ReadAsync(buff, 0, buff.Length);
            sb.Append(buff.SubArray(0, length));
            this.OnErrorTextReceived(sb.ToString());
            Thread.Sleep(1);
        }
        while (this.process.HasExited == false);
    }

    private async void WriteInputTask()
    {
        while (this.process.HasExited == false)
        {
            Thread.Sleep(1);

            if (this.pendingWriteData != null)
            {
                await this.process.StandardInput.WriteLineAsync(this.pendingWriteData);
                await this.process.StandardInput.FlushAsync();

                lock (this.theLock)
                {
                    this.pendingWriteData = null;
                }
            }
        }
    }
}

Then, in actually running the process and sending the CTRL-C in my main app:

            DateTime maxStartDateTime = //... some date time;
            DateTime maxEndDateTime = //... some later date time
            var duration = maxEndDateTime.Subtract(maxStartDateTime);
            appManager = new ConsoleAppManager("ffmpeg.exe");
            string[] args = new string[] { "-rtbufsize 100M -f dshow -i video=\"screen-capture-recorder\":audio=\"virtual-audio-capturer\" -r 20 -timelimit " +
                Convert.ToString(duration.TotalSeconds) +
                " -vcodec libx264 -qp 0 -x264opts keyint=100:min_keyint=80 -acodec libmp3lame -ab 128k  -ac 1 -ar 44100 -async 30 C:\\Users\\Psalm3_3\\GDrive\\Workspace\\TutorApplication\\Videos\\out_vid.mp4" };

            appManager.ExecuteAsync(args);
            await Task.Delay(Convert.ToInt32(duration.TotalSeconds * 1000) + 20000);

            if (appManager.Running)
            {
                // If stilll running, send CTRL-C
                appManager.Write("\x3");
            }

For details, please see https://stackoverflow.com/questions/21848271/redirecting-standard-input-of-console-application?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa and https://stackoverflow.com/questions/30249101/windows-how-to-get-the-process-group-of-a-process-that-is-already-running/50311226#50311226 and https://www.youtube.com/watch?v=JEVlRqajKNI

FYI, previously I was ending up with mp4 files that were 7 or 8 GB, but now with the above code for recording a session that is over 2 hours long the file size is only about 500 MB.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.