Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07eb6adf authored by Luca Stefani's avatar Luca Stefani Committed by Łukasz Patron
Browse files

Recorder: Use native functions to get H264 info

Change-Id: Ice2339bcb74574817830ceab066e22b08b9dd4c9
parent 0eaeb01e
Loading
Loading
Loading
Loading
+16 −73
Original line number Diff line number Diff line
@@ -19,32 +19,28 @@ package org.lineageos.recorder.screen;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.CamcorderProfile;
import android.media.EncoderCapabilities;
import android.media.EncoderCapabilities.VideoEncoderCap;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;

import org.lineageos.recorder.R;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;

import safesax.Element;
import safesax.ElementListener;
import safesax.Parsers;
import safesax.RootElement;
import java.util.List;

abstract class EncoderDevice {
    final Context context;
    private final String LOGTAG = getClass().getSimpleName();

    private static final List<VideoEncoderCap> videoEncoders =
            EncoderCapabilities.getVideoEncoders();

    // Standard resolution tables, removed values that aren't multiples of 8
    private final int[][] validResolutions = {
            // CEA Resolutions
@@ -149,56 +145,15 @@ abstract class EncoderDevice {
            venc = null;
        }

        int maxWidth;
        int maxHeight;
        int bitrate;

        try {
            File mediaProfiles = new File("/system/etc/media_profiles.xml");
            FileInputStream fin = new FileInputStream(mediaProfiles);
            byte[] bytes = new byte[(int) mediaProfiles.length()];
            //noinspection ResultOfMethodCallIgnored
            fin.read(bytes);
            String xml = new String(bytes);
            RootElement root = new RootElement("MediaSettings");
            Element encoder = root.requireChild("VideoEncoderCap");
            final ArrayList<VideoEncoderCap> encoders = new ArrayList<>();
            encoder.setElementListener(new ElementListener() {
                @Override
                public void end() {
                }

                @Override
                public void start(Attributes attributes) {
                    if (!TextUtils.equals(attributes.getValue("name"), "h264"))
                        return;
                    encoders.add(new VideoEncoderCap(attributes));
                }
            });
            Parsers.parse(new StringReader(xml), root.getContentHandler());
            if (encoders.size() != 1) {
                throw new IOException("derp");
            }

            VideoEncoderCap v = encoders.get(0);
            maxWidth = v.maxFrameWidth;
            maxHeight = v.maxFrameHeight;
            bitrate = v.maxBitRate;
        } catch (IOException | SAXException e) {
            CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);

            if (profile == null) {
                profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
            }
        int maxWidth = 640;
        int maxHeight = 480;
        int bitrate = 2000000;

            if (profile == null) {
                maxWidth = 640;
                maxHeight = 480;
                bitrate = 2000000;
            } else {
                maxWidth = profile.videoFrameWidth;
                maxHeight = profile.videoFrameHeight;
                bitrate = profile.videoBitRate;
        for (VideoEncoderCap cap : videoEncoders) {
            if (cap.mCodec == MediaRecorder.VideoEncoder.H264) {
                maxWidth = cap.mMaxFrameWidth;
                maxHeight = cap.mMaxFrameHeight;
                bitrate = cap.mMaxBitRate;
            }
        }

@@ -286,18 +241,6 @@ abstract class EncoderDevice {
        return surface;
    }

    private static class VideoEncoderCap {
        final int maxFrameWidth;
        final int maxFrameHeight;
        final int maxBitRate;

        VideoEncoderCap(Attributes attributes) {
            maxFrameWidth = Integer.valueOf(attributes.getValue("maxFrameWidth"));
            maxFrameHeight = Integer.valueOf(attributes.getValue("maxFrameHeight"));
            maxBitRate = Integer.valueOf(attributes.getValue("maxBitRate"));
        }
    }

    abstract class EncoderRunnable implements Runnable {
        MediaCodec venc;

+0 −35
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 safesax;

import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;

/**
 * An XML parse exception which includes the line number in the message.
 */
@SuppressWarnings("ALL")
class BadXmlException extends SAXParseException {

    public BadXmlException(String message, Locator locator) {
        super(message, locator);
    }

    public String getMessage() {
        return "Line " + getLineNumber() + ": " + super.getMessage();
    }
}
+0 −97
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 safesax;

/**
 * Contains element children.
 */
@SuppressWarnings("ALL")
class Children {

    final Child[] children = new Child[16];

    /**
     * Looks up a child by name and creates a new one if necessary.
     */
    Element getOrCreate(Element parent, String uri, String localName) {
        int hash = uri.hashCode() * 31 + localName.hashCode();
        int index = hash & 15;

        Child current = children[index];
        if (current == null) {
            // We have no children in this bucket yet.
            current = new Child(parent, uri, localName, parent.depth + 1, hash);
            children[index] = current;
            return current;
        } else {
            // Search this bucket.
            Child previous;
            do {
                if (current.hash == hash
                        && current.uri.compareTo(uri) == 0
                        && current.localName.compareTo(localName) == 0) {
                    // We already have a child with that name.
                    return current;
                }

                previous = current;
                current = current.next;
            } while (current != null);

            // Add a new child to the bucket.
            current = new Child(parent, uri, localName, parent.depth + 1, hash);
            previous.next = current;
            return current;
        }
    }

    /**
     * Looks up a child by name.
     */
    Element get(String uri, String localName) {
        int hash = uri.hashCode() * 31 + localName.hashCode();
        int index = hash & 15;

        Child current = children[index];
        if (current == null) {
            return null;
        } else {
            do {
                if (current.hash == hash
                        && current.uri.compareTo(uri) == 0
                        && current.localName.compareTo(localName) == 0) {
                    return current;
                }
                current = current.next;
            } while (current != null);

            return null;
        }
    }

    static class Child extends Element {

        final int hash;
        Child next;

        Child(Element parent, String uri, String localName, int depth,
              int hash) {
            super(parent, uri, localName, depth);
            this.hash = hash;
        }
    }
}
+0 −202
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 safesax;

import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;

import java.util.ArrayList;

/**
 * An XML element. Provides access to child elements and hooks to listen for
 * events related to this element.
 *
 * @see RootElement
 */
@SuppressWarnings("ALL")
public class Element {

    final String uri;
    final String localName;
    final int depth;
    final Element parent;
    Children children;

    ArrayList<Element> requiredChilden;
    boolean visited;

    StartElementListener startElementListener;
    EndElementListener endElementListener;
    EndTextElementListener endTextElementListener;

    Element(Element parent, String uri, String localName, int depth) {
        this.parent = parent;
        this.uri = uri;
        this.localName = localName;
        this.depth = depth;
    }

    /**
     * Gets the child element with the given name. Uses an empty string as the
     * namespace.
     */
    public Element getChild(String localName) {
        return getChild("", localName);
    }

    /**
     * Gets the child element with the given name.
     */
    public Element getChild(String uri, String localName) {
        if (endTextElementListener != null) {
            throw new IllegalStateException("This element already has an end"
                    + " text element listener. It cannot have children.");
        }

        if (children == null) {
            children = new Children();
        }

        return children.getOrCreate(this, uri, localName);
    }

    /**
     * Gets the child element with the given name. Uses an empty string as the
     * namespace. We will throw a {@link org.xml.sax.SAXException} at parsing time
     * if the specified child is missing. This helps you ensure that your
     * listeners are called.
     */
    public Element requireChild(String localName) {
        return requireChild("", localName);
    }

    /**
     * Gets the child element with the given name. We will throw a {@link
     * org.xml.sax.SAXException} at parsing time if the specified child is
     * missing. This helps you ensure that your listeners are called.
     */
    public Element requireChild(String uri, String localName) {
        Element child = getChild(uri, localName);

        if (requiredChilden == null) {
            requiredChilden = new ArrayList<>();
            requiredChilden.add(child);
        } else {
            if (!requiredChilden.contains(child)) {
                requiredChilden.add(child);
            }
        }

        return child;
    }

    /**
     * Sets start and end element listeners at the same time.
     */
    public void setElementListener(ElementListener elementListener) {
        setStartElementListener(elementListener);
        setEndElementListener(elementListener);
    }

    /**
     * Sets start and end text element listeners at the same time.
     */
    public void setTextElementListener(TextElementListener elementListener) {
        setStartElementListener(elementListener);
        setEndTextElementListener(elementListener);
    }

    /**
     * Sets a listener for the start of this element.
     */
    public void setStartElementListener(
            StartElementListener startElementListener) {
        if (this.startElementListener != null) {
            throw new IllegalStateException(
                    "Start element listener has already been set.");
        }
        this.startElementListener = startElementListener;
    }

    /**
     * Sets a listener for the end of this element.
     */
    public void setEndElementListener(EndElementListener endElementListener) {
        if (this.endElementListener != null) {
            throw new IllegalStateException(
                    "End element listener has already been set.");
        }
        this.endElementListener = endElementListener;
    }

    /**
     * Sets a listener for the end of this text element.
     */
    public void setEndTextElementListener(
            EndTextElementListener endTextElementListener) {
        if (this.endTextElementListener != null) {
            throw new IllegalStateException(
                    "End text element listener has already been set.");
        }

        if (children != null) {
            throw new IllegalStateException("This element already has children."
                    + " It cannot have an end text element listener.");
        }

        this.endTextElementListener = endTextElementListener;
    }

    @Override
    public String toString() {
        return toString(uri, localName);
    }

    static String toString(String uri, String localName) {
        return "'" + (uri.equals("") ? localName : uri + ":" + localName) + "'";
    }

    /**
     * Clears flags on required children.
     */
    void resetRequiredChildren() {
        ArrayList<Element> requiredChildren = this.requiredChilden;
        if (requiredChildren != null) {
            for (int i = requiredChildren.size() - 1; i >= 0; i--) {
                requiredChildren.get(i).visited = false;
            }
        }
    }

    /**
     * Throws an exception if a required child was not present.
     */
    void checkRequiredChildren(Locator locator) throws SAXParseException {
        ArrayList<Element> requiredChildren = this.requiredChilden;
        if (requiredChildren != null) {
            for (int i = requiredChildren.size() - 1; i >= 0; i--) {
                Element child = requiredChildren.get(i);
                if (!child.visited) {
                    throw new BadXmlException(
                            "Element named " + this + " is missing required"
                                    + " child element named "
                                    + child + ".", locator);
                }
            }
        }
    }
}
+0 −26
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 safesax;

/**
 * Listens for the beginning and ending of elements.
 */
@SuppressWarnings("ALL")
public interface ElementListener extends StartElementListener,
        EndElementListener {

}
Loading