All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ws.schild.jave.ConversionOutputAnalyzer Maven / Gradle / Ivy

/*
 * Copyright 2018 a.schild.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ws.schild.jave;

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author a.schild
 */
public class ConversionOutputAnalyzer {
    private final static Log LOG = LogFactory.getLog(ConversionOutputAnalyzer.class);

        /**
     * This regexp is used to parse the ffmpeg output about the ongoing encoding
     * process.
     */
    private static final Pattern PROGRESS_INFO_PATTERN = Pattern.compile(
            "\\s*(\\w+)\\s*=\\s*(\\S+)\\s*", Pattern.CASE_INSENSITIVE);

    private final EncoderProgressListener listener;
    
    private final long duration;
    // Step 0 = Before input stuff
    // Step 1 = Input stuff
    // Step 2 = Stream Mapping
    // Step 3 = Output
    // Step 4 = frame=...
    private int step = 0;
    private boolean streamMappingFound= false;
    private boolean outputFound= false;
    private int lineNR = 0;
    private String lastWarning= null;
    
    public ConversionOutputAnalyzer(long duration, EncoderProgressListener listener) {
        this.duration= duration;
        this.listener= listener;
    }
 
    public void analyzeNewLine(String line) throws EncoderException
    {
        lineNR++;
        if (LOG.isDebugEnabled())
        {
            LOG.debug("Input Line (" + lineNR + "): <" + line +">");
        }
        if (line.startsWith("WARNING: "))
        {
            if (listener != null)
            {
                listener.message(line);
            }
        }
        if (line.startsWith("Press [q]"))
        {
            // Abort messages
        }
        else
        {
            switch (step)
            {
                case 0:
                {
                    if (line.startsWith("Input #0"))
                    {
                        step = 1;
                    } else
                    {
                        // wait for Stream mapping:
                    }
                }
                break;
                case 1:
                {
                    if (line.startsWith("Stream mapping:"))
                    {
                        streamMappingFound= true;
                        step = 2;
                    }
                    else if (line.startsWith("Output #0"))
                    {
                        outputFound= true;
                        step = 2;
                    } else if (!line.startsWith("  "))
                    {
                        throw new EncoderException(step, lineNR, line);
                    } else
                    {
                        // wait for Stream mapping:
                    }
                }
                break;
                case 2:
                {
                    if (line.startsWith("Output #0"))
                    {
                        outputFound= true;
                        step = 3;
                    } else if (line.startsWith("Stream mapping:"))
                    {
                        streamMappingFound= true;
                        step = 3;
                    } else if (!line.startsWith("  "))
                    {
                        throw new EncoderException(step, lineNR, line);
                    } else
                    {
                        // wait for Stream mapping:
                    }
                }
                break;
                case 3:
                {
                    if (line.startsWith("  "))
                    {
                        // output details
                    } else if (line.startsWith("video:"))
                    {
                        step = 4;
                    } else if (line.startsWith("frame="))
                    {
                        // Progressnotification video
                    } else if (line.startsWith("size="))
                    {
                        // Progressnotification audio
                    } else if (line.endsWith("Queue input is backward in time")
                            || line.contains("Application provided invalid, non monotonically increasing dts to muxer in stream"))
                    {
                        // Ignore these non-fatal errors, if they are fatal, the next line(s)
                        // will trow the full error
                        if (listener != null)
                        {
                            listener.message(line);
                        }
                    }
                    else
                    {
                        throw new EncoderException(step, lineNR, line);
                    }
                }
            }
            if (line.startsWith("frame=") || line.startsWith("size="))
            {
                try
                {
                    line = line.trim();
                    if (line.length() > 0)
                    {
                        HashMap table = parseProgressInfoLine(line);
                        if (table == null)
                        {
                            if (listener != null)
                            {
                                listener.message(line);
                            }
                            lastWarning = line;
                        } else
                        {
                            if (listener != null)
                            {
                                String time = table.get("time");
                                if (time != null)
                                {
                                    String dParts[] = time.split(":");
                                    // HH:MM:SS.xx

                                    Double seconds = Double.parseDouble(dParts[dParts.length - 1]);
                                    if (dParts.length > 1)
                                    {
                                        seconds += Double.parseDouble(dParts[dParts.length - 2]) * 60;
                                        if (dParts.length > 2)
                                        {
                                            seconds += Double.parseDouble(dParts[dParts.length - 3]) * 60 * 60;
                                        }
                                    }

                                    int perm = (int) Math.round((seconds * 1000L * 1000L)
                                            / (double) duration);
                                    if (perm > 1000)
                                    {
                                        perm = 1000;
                                    }
                                    listener.progress(perm);
                                }
                            }
                            lastWarning = null;
                        }
                    }
                } catch (Exception ex)
                {
                    LOG.warn("Error in progress parsing for line: " + line);
                }
            }
        }
    }
    
    public String getLastWarning()
    {
        return lastWarning;
    }
    
    /**
     * Private utility. Parse a line and try to match its contents against the
     * {@link Encoder#PROGRESS_INFO_PATTERN} pattern. It the line can be parsed,
     * it returns a hashtable with progress informations, otherwise it returns
     * null.
     *
     * @param line The line from the ffmpeg output.
     * @return A hashtable with the value reported in the line, or null if the
     * given line can not be parsed.
     */
    private HashMap parseProgressInfoLine(String line) {
        HashMap table = null;
        Matcher m = PROGRESS_INFO_PATTERN.matcher(line);
        while (m.find())
        {
            if (table == null)
            {
                table = new HashMap<>();
            }
            String key = m.group(1);
            String value = m.group(2);
            table.put(key, value);
        }
        return table;
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy