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

Commit 9bcedf7c authored by Santosh Madhava's avatar Santosh Madhava Committed by Dharmaray Kundargi
Browse files

Video editor API update - rework for Patch Set 3

Change-Id: I75bfa17fe98bec13f672ff9a4edf623bb85020c8
parent 981df1d9
Loading
Loading
Loading
Loading
+647 −481
Original line number Diff line number Diff line
@@ -14,18 +14,27 @@
 * limitations under the License.
 */


package android.media.videoeditor;

import java.io.File;
import java.io.IOException;
import java.lang.ref.SoftReference;

import android.media.videoeditor.MediaArtistNativeHelper.Properties;

/**
 * This class allows to handle an audio track. This audio file is mixed with the
 * audio samples of the media items.
 * {@hide}
 */
public class AudioTrack {
    // Instance variables

    /**
     *  Instance variables
     *  Private object for calling native methods via MediaArtistNativeHelper
     */
    private final MediaArtistNativeHelper mMANativeHelper;
    private final String mUniqueId;
    private final String mFilename;
    private long mStartTimeMs;
@@ -35,21 +44,27 @@ public class AudioTrack {
    private long mEndBoundaryTimeMs;
    private boolean mLoop;
    private boolean mMuted;

    private final long mDurationMs;
    private final int mAudioChannels;
    private final int mAudioType;
    private final int mAudioBitrate;
    private final int mAudioSamplingFrequency;

    // Ducking variables
    /**
     *  Ducking variables
     */
    private int mDuckingThreshold;
    private int mDuckedTrackVolume;
    private boolean mIsDuckingEnabled;

    // The audio waveform filename
    /**
     *  The audio waveform filename
     */
    private String mAudioWaveformFilename;
    // The audio waveform data

    /**
     *  The audio waveform data
     */
    private SoftReference<WaveformData> mWaveformData;

    /**
@@ -70,43 +85,12 @@ public class AudioTrack {
     *
     * @throws IOException if file is not found
     * @throws IllegalArgumentException if file format is not supported or if
     *             the codec is not supported
     *         the codec is not supported or if editor is not of type
     *         VideoEditorImpl.
     */
    public AudioTrack(VideoEditor editor, String audioTrackId, String filename)
            throws IOException {
        mUniqueId = audioTrackId;
        mFilename = filename;
        mStartTimeMs = 0;
        // TODO: This value represents to the duration of the audio file
        mDurationMs = 300000;
        // TODO: This value needs to be read from the audio track of the source
        // file
        mAudioChannels = 2;
        mAudioType = MediaProperties.ACODEC_AAC_LC;
        mAudioBitrate = 128000;
        mAudioSamplingFrequency = 44100;

        mTimelineDurationMs = mDurationMs;
        mVolumePercent = 100;

        // Play the entire audio track
        mBeginBoundaryTimeMs = 0;
        mEndBoundaryTimeMs = mDurationMs;

        // By default loop is disabled
        mLoop = false;

        // By default the audio track is not muted
        mMuted = false;

        // Ducking is enabled by default
        mDuckingThreshold = 0;
        mDuckedTrackVolume = 0;
        mIsDuckingEnabled = false;

        // The audio waveform file is generated later
        mAudioWaveformFilename = null;
        mWaveformData = null;
    public AudioTrack(VideoEditor editor, String audioTrackId, String filename) throws IOException {
        this(editor, audioTrackId, filename, 0, 0, MediaItem.END_OF_FILE, false, 100, false, false,
                0, 0, null);
    }

    /**
@@ -114,7 +98,8 @@ public class AudioTrack {
     *
     * @param editor The video editor reference
     * @param audioTrackId The audio track id
     * @param filename The audio filename
     * @param filename The audio filename. In case file contains Audio and Video,
     *         only the Audio stream will be used as Audio Track.
     * @param startTimeMs the start time in milliseconds (relative to the
     *         timeline)
     * @param beginMs start time in the audio track in milliseconds (relative to
@@ -126,31 +111,69 @@ public class AudioTrack {
     * @param muted true if the audio track is muted
     * @param threshold Ducking will be activated when the relative energy in
     *         the media items audio signal goes above this value. The valid
     *      range of values is 0 to 100.
     * @param duckedTrackVolume The relative volume of the audio track when ducking
     *      is active. The valid range of values is 0 to 100.
     *         range of values is 0 to 90.
     * @param duckedTrackVolume The relative volume of the audio track when
     *         ducking is active. The valid range of values is 0 to 100.
     * @param audioWaveformFilename The name of the waveform file
     *
     * @throws IOException if file is not found
     * @throws IllegalArgumentException if file format is not supported or if
     *             the codec is not supported or if editor is not of type
     *             VideoEditorImpl.
     */
    AudioTrack(VideoEditor editor, String audioTrackId, String filename, long startTimeMs,
            long beginMs, long endMs, boolean loop, int volume, boolean muted,
            boolean duckingEnabled, int duckThreshold, int duckedTrackVolume,
    AudioTrack(VideoEditor editor, String audioTrackId, String filename,
               long startTimeMs,long beginMs, long endMs, boolean loop,
               int volume, boolean muted,boolean duckingEnabled,
               int duckThreshold, int duckedTrackVolume,
            String audioWaveformFilename) throws IOException {
        mUniqueId = audioTrackId;
        mFilename = filename;
        mStartTimeMs = startTimeMs;
        Properties properties = null;
        File file = new File(filename);
        if (!file.exists()) {
            throw new IOException(filename + " not found ! ");
        }

        // TODO: This value represents to the duration of the audio file
        mDurationMs = 300000;
        if (editor instanceof VideoEditorImpl) {
            mMANativeHelper = ((VideoEditorImpl)editor).getNativeContext();
        } else {
            throw new IllegalArgumentException("editor is not of type VideoEditorImpl");
        }
        try {
          properties = mMANativeHelper.getMediaProperties(filename);
        } catch (Exception e) {
            throw new IllegalArgumentException("Unsupported file or file not found");
        }
        switch (mMANativeHelper.getFileType(properties.fileType)) {
            case MediaProperties.FILE_3GP:
            case MediaProperties.FILE_MP4:
            case MediaProperties.FILE_MP3:
                break;

        // TODO: This value needs to be read from the audio track of the source
        // file
        mAudioChannels = 2;
        mAudioType = MediaProperties.ACODEC_AAC_LC;
        mAudioBitrate = 128000;
        mAudioSamplingFrequency = 44100;
            default: {
                throw new IllegalArgumentException("Unsupported input file type");
            }
        }
        switch (mMANativeHelper.getAudioCodecType(properties.audioFormat)) {
            case MediaProperties.ACODEC_AMRNB:
            case MediaProperties.ACODEC_AMRWB:
            case MediaProperties.ACODEC_AAC_LC:
            case MediaProperties.ACODEC_MP3:
                break;
            default:
                throw new IllegalArgumentException("Unsupported Audio Codec Format in Input File");
        }

        if (endMs == MediaItem.END_OF_FILE) {
            endMs = properties.audioDuration;
        }

        mUniqueId = audioTrackId;
        mFilename = filename;
        mStartTimeMs = startTimeMs;
        mDurationMs = properties.audioDuration;
        mAudioChannels = properties.audioChannels;
        mAudioBitrate = properties.audioBitrate;
        mAudioSamplingFrequency = properties.audioSamplingFrequency;
        mAudioType = properties.audioFormat;
        mTimelineDurationMs = endMs - beginMs;
        mVolumePercent = volume;

@@ -159,7 +182,6 @@ public class AudioTrack {

        mLoop = loop;
        mMuted = muted;

        mIsDuckingEnabled = duckingEnabled;
        mDuckingThreshold = duckThreshold;
        mDuckedTrackVolume = duckedTrackVolume;
@@ -174,6 +196,8 @@ public class AudioTrack {
    }

    /**
     * Get the id of the audio track
     *
     * @return The id of the audio track
     */
    public String getId() {
@@ -181,7 +205,7 @@ public class AudioTrack {
    }

    /**
     * Get the filename source for this audio track.
     * Get the filename for this audio track source.
     *
     * @return The filename as an absolute file name
     */
@@ -190,6 +214,8 @@ public class AudioTrack {
    }

    /**
     * Get the number of audio channels in the source of this audio track
     *
     * @return The number of audio channels in the source of this audio track
     */
    public int getAudioChannels() {
@@ -197,13 +223,18 @@ public class AudioTrack {
    }

    /**
     * Get the audio codec of the source of this audio track
     *
     * @return The audio codec of the source of this audio track
     * {@link android.media.videoeditor.MediaProperties}
     */
    public int getAudioType() {
        return mAudioType;
    }

    /**
     * Get the audio sample frequency of the audio track
     *
     * @return The audio sample frequency of the audio track
     */
    public int getAudioSamplingFrequency() {
@@ -211,6 +242,8 @@ public class AudioTrack {
    }

    /**
     * Get the audio bitrate of the audio track
     *
     * @return The audio bitrate of the audio track
     */
    public int getAudioBitrate() {
@@ -230,7 +263,18 @@ public class AudioTrack {
     *         requested and is not supported.
     */
    public void setVolume(int volumePercent) {
        if (volumePercent > MediaProperties.AUDIO_MAX_VOLUME_PERCENT) {
            throw new IllegalArgumentException("Volume set exceeds maximum allowed value");
        }

         if (volumePercent < 0) {
            throw new IllegalArgumentException("Invalid Volume ");
        }
        mVolumePercent = volumePercent;
        /**
         *  Force update of preview settings
         */
        mMANativeHelper.setGeneratePreview(true);
    }

    /**
@@ -244,27 +288,26 @@ public class AudioTrack {
    }

    /**
     * @param muted true to mute the audio track
     * Mute/Unmute the audio track
     *
     * @param muted true to mute the audio track. SetMute(true) will make
     *         the volume of this Audio Track to 0.
     */
    public void setMute(boolean muted) {
        mMuted = muted;
    }

        /**
     * @return true if the audio track is muted
         *  Force update of preview settings
         */
    public boolean isMuted() {
        return mMuted;
        mMANativeHelper.setGeneratePreview(true);
    }

    /**
     * Set the start time of this audio track relative to the storyboard
     * timeline. Default value is 0.
     * Check if the audio track is muted
     *
     * @param startTimeMs the start time in milliseconds
     * @return true if the audio track is muted
     */
    public void setStartTime(long startTimeMs) {
        mStartTimeMs = startTimeMs;
    public boolean isMuted() {
        return mMuted;
    }

    /**
@@ -273,19 +316,25 @@ public class AudioTrack {
     *
     * @return The start time in milliseconds
     */

    public long getStartTime() {
        return mStartTimeMs;
    }

    /**
     * @return The duration in milliseconds. This value represents the audio
     *         track duration (not looped)
     * Get the audio track duration
     *
     * @return The duration in milliseconds. This value represents actual audio
     *         track duration. This value is not effected by 'enableLoop' or
     *         'setExtractBoundaries'.
     */
    public long getDuration() {
        return mDurationMs;
    }

    /**
     * Get the audio track timeline duration
     *
     * @return The timeline duration as defined by the begin and end boundaries
     */
    public long getTimelineDuration() {
@@ -307,14 +356,26 @@ public class AudioTrack {
        if (endMs > mDurationMs) {
            throw new IllegalArgumentException("Invalid end time");
        }
        if (beginMs < 0) {
            throw new IllegalArgumentException("Invalid start time; is < 0");
        }
        if (endMs < 0) {
            throw new IllegalArgumentException("Invalid end time; is < 0");
        }

        mBeginBoundaryTimeMs = beginMs;
        mEndBoundaryTimeMs = endMs;

        mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
        /**
         *  Force update of preview settings
         */
        mMANativeHelper.setGeneratePreview(true);
    }

    /**
     * Get the boundary begin time
     *
     * @return The boundary begin time
     */
    public long getBoundaryBeginTime() {
@@ -322,6 +383,8 @@ public class AudioTrack {
    }

    /**
     * Get the boundary end time
     *
     * @return The boundary end time
     */
    public long getBoundaryEndTime() {
@@ -335,17 +398,31 @@ public class AudioTrack {
     * mEndBoundaryTimeMs are looped.
     */
    public void enableLoop() {
        if (!mLoop) {
            mLoop = true;
            /**
             *  Force update of preview settings
             */
            mMANativeHelper.setGeneratePreview(true);
        }
    }

    /**
     * Disable the loop mode
     */
    public void disableLoop() {
        if (mLoop) {
            mLoop = false;
            /**
             *  Force update of preview settings
             */
            mMANativeHelper.setGeneratePreview(true);
        }
    }

    /**
     * Check if looping is enabled
     *
     * @return true if looping is enabled
     */
    public boolean isLooping() {
@@ -356,7 +433,13 @@ public class AudioTrack {
     * Disable the audio duck effect
     */
    public void disableDucking() {
        if (mIsDuckingEnabled) {
            mIsDuckingEnabled = false;
            /**
             *  Force update of preview settings
             */
            mMANativeHelper.setGeneratePreview(true);
        }
    }

    /**
@@ -382,9 +465,15 @@ public class AudioTrack {
        mDuckingThreshold = threshold;
        mDuckedTrackVolume = duckedTrackVolume;
        mIsDuckingEnabled = true;
        /**
         *  Force update of preview settings
         */
        mMANativeHelper.setGeneratePreview(true);
    }

    /**
     * Check if ducking is enabled
     *
     * @return true if ducking is enabled
     */
    public boolean isDuckingEnabled() {
@@ -392,6 +481,8 @@ public class AudioTrack {
    }

    /**
     * Get the ducking threshold.
     *
     * @return The ducking threshold
     */
    public int getDuckingThreshhold() {
@@ -399,6 +490,8 @@ public class AudioTrack {
    }

    /**
     * Get the ducked track volume.
     *
     * @return The ducked track volume
     */
    public int getDuckedTrackVolume() {
@@ -415,24 +508,78 @@ public class AudioTrack {
     * @throws IOException if the output file cannot be created
     * @throws IllegalArgumentException if the audio file does not have a valid
     *         audio track
     * @throws IllegalStateException if the codec type is unsupported
     */
    public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)
    throws IOException {
        // TODO: Set mAudioWaveformFilename at the end once the extract is
        // complete
        if (mAudioWaveformFilename == null) {
            /**
             *  AudioWaveformFilename is generated
             */
            final String projectPath = mMANativeHelper.getProjectPath();
            final String audioWaveFilename = String.format(projectPath + "/audioWaveformFile-"
                    + getId() + ".dat");

            /**
             * Logic to get frame duration = (no. of frames per sample * 1000)/
             * sampling frequency
             */
            final int frameDuration;
            final int sampleCount;
            final int codecType = mMANativeHelper.getAudioCodecType(mAudioType);
            switch (codecType) {
                case MediaProperties.ACODEC_AMRNB: {
                    frameDuration = (MediaProperties.SAMPLES_PER_FRAME_AMRNB * 1000)
                    / MediaProperties.DEFAULT_SAMPLING_FREQUENCY;
                    sampleCount = MediaProperties.SAMPLES_PER_FRAME_AMRNB;
                    break;
                }

                case MediaProperties.ACODEC_AMRWB: {
                    frameDuration = (MediaProperties.SAMPLES_PER_FRAME_AMRWB * 1000)
                    / MediaProperties.DEFAULT_SAMPLING_FREQUENCY;
                    sampleCount = MediaProperties.SAMPLES_PER_FRAME_AMRWB;
                    break;
                }

                case MediaProperties.ACODEC_AAC_LC: {
                    frameDuration = (MediaProperties.SAMPLES_PER_FRAME_AAC * 1000)
                    / MediaProperties.DEFAULT_SAMPLING_FREQUENCY;
                    sampleCount = MediaProperties.SAMPLES_PER_FRAME_AAC;
                    break;
                }

                case MediaProperties.ACODEC_MP3: {
                    frameDuration = (MediaProperties.SAMPLES_PER_FRAME_MP3 * 1000)
                    / MediaProperties.DEFAULT_SAMPLING_FREQUENCY;
                    sampleCount = MediaProperties.SAMPLES_PER_FRAME_MP3;
                    break;
                }

                default: {
                    throw new IllegalStateException("Unsupported codec type: "
                                                                   + codecType);
                }
            }

            mMANativeHelper.generateAudioGraph( mUniqueId,
                    mFilename,
                    audioWaveFilename,
                    frameDuration,
                    MediaProperties.DEFAULT_CHANNEL_COUNT,
                    sampleCount,
                    listener,
                    false);
            /**
             *  Record the generated file name
             */
            mAudioWaveformFilename = audioWaveFilename;
        }
        mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename));
    }

    /**
     * Get the audio waveform file name if extractAudioWaveform was successful.
     * The file format is as following:
     * <ul>
     * <li>first 4 bytes provide the number of samples for each value, as
     * big-endian signed</li>
     * <li>4 following bytes is the total number of values in the file, as
     * big-endian signed</li>
     * <li>then, all values follow as bytes</li>
     * </ul>
     *
     * @return the name of the file, null if the file does not exist
     */
@@ -441,7 +588,22 @@ public class AudioTrack {
    }

    /**
     * Delete the waveform file
     */
    void invalidate() {
        if (mAudioWaveformFilename != null) {
            new File(mAudioWaveformFilename).delete();
            mAudioWaveformFilename = null;
            mWaveformData = null;
        }
    }

    /**
     * Get the audio waveform data.
     *
     * @return The waveform data
     *
     * @throws IOException if the waveform file cannot be found
     */
    public WaveformData getWaveformData() throws IOException {
        if (mWaveformData == null) {
@@ -452,7 +614,11 @@ public class AudioTrack {
        if (waveformData != null) {
            return waveformData;
        } else if (mAudioWaveformFilename != null) {
            try {
                waveformData = new WaveformData(mAudioWaveformFilename);
            } catch (IOException e) {
                throw e;
            }
            mWaveformData = new SoftReference<WaveformData>(waveformData);
            return waveformData;
        } else {
+197 −173
Original line number Diff line number Diff line
@@ -14,22 +14,29 @@
 * limitations under the License.
 */


package android.media.videoeditor;

/**
 * This is the super class for all effects. An effect can only be applied to a
 * single media item. If one wants to apply the same effect to multiple media
 * items, multiple @{MediaItem.addEffect(Effect)} call must be invoked on each
 * of the MediaItem objects.
 * single media item.
 * {@hide}
 */
public abstract class Effect {
    // Instance variables
    /**
     *  Instance variables
     */
    private final String mUniqueId;
    // The effect owner
    /**
     *  The effect owner
     */
    private final MediaItem mMediaItem;

    protected long mDurationMs;
    // The start time of the effect relative to the media item timeline
    /**
     *  The start time of the effect relative to the beginning
     *  of the media item
     */
    protected long mStartTimeMs;

    /**
@@ -52,11 +59,15 @@ public abstract class Effect {
     *            is applied
     * @param durationMs The effect duration in milliseconds
     */
    public Effect(MediaItem mediaItem, String effectId, long startTimeMs, long durationMs) {
    public Effect(MediaItem mediaItem, String effectId, long startTimeMs,
                  long durationMs) {
        if (mediaItem == null) {
            throw new IllegalArgumentException("Media item cannot be null");
        }

        if ((startTimeMs < 0) || (durationMs < 0)) {
             throw new IllegalArgumentException("Invalid start time Or/And Duration");
        }
        if (startTimeMs + durationMs > mediaItem.getDuration()) {
            throw new IllegalArgumentException("Invalid start time and duration");
        }
@@ -68,6 +79,8 @@ public abstract class Effect {
    }

    /**
     * Get the id of the effect.
     *
     * @return The id of the effect
     */
    public String getId() {
@@ -81,6 +94,10 @@ public abstract class Effect {
     * @param durationMs of the effect in milliseconds
     */
    public void setDuration(long durationMs) {
        if (durationMs <0) {
            throw new IllegalArgumentException("Invalid duration");
        }

        if (mStartTimeMs + durationMs > mMediaItem.getDuration()) {
            throw new IllegalArgumentException("Duration is too large");
        }
@@ -88,7 +105,8 @@ public abstract class Effect {
        final long oldDurationMs = mDurationMs;
        mDurationMs = durationMs;

        mMediaItem.invalidateTransitions(mStartTimeMs, oldDurationMs, mStartTimeMs, mDurationMs);
        mMediaItem.invalidateTransitions(mStartTimeMs, oldDurationMs,
                                         mStartTimeMs, mDurationMs);
    }

    /**
@@ -115,10 +133,13 @@ public abstract class Effect {
        final long oldStartTimeMs = mStartTimeMs;
        mStartTimeMs = startTimeMs;

        mMediaItem.invalidateTransitions(oldStartTimeMs, mDurationMs, mStartTimeMs, mDurationMs);
        mMediaItem.invalidateTransitions(oldStartTimeMs, mDurationMs,
                                         mStartTimeMs, mDurationMs);
    }

    /**
     * Get the start time of the effect
     *
     * @return The start time in milliseconds
     */
    public long getStartTime() {
@@ -142,10 +163,13 @@ public abstract class Effect {
        mStartTimeMs = startTimeMs;
        mDurationMs = durationMs;

        mMediaItem.invalidateTransitions(oldStartTimeMs, oldDurationMs, mStartTimeMs, mDurationMs);
        mMediaItem.invalidateTransitions(oldStartTimeMs, oldDurationMs,
                                         mStartTimeMs, mDurationMs);
    }

    /**
     * Get the media item owner.
     *
     * @return The media item owner
     */
    public MediaItem getMediaItem() {
+142 −119
Original line number Diff line number Diff line
@@ -14,10 +14,11 @@
 * limitations under the License.
 */


package android.media.videoeditor;

/**
 * This class allows to apply color on a media item.
 * This class allows to apply color effect on a media item.
 * {@hide}
 */
public class EffectColor extends Effect {
@@ -43,16 +44,27 @@ public class EffectColor extends Effect {
     * Make the video look like as if it was recorded in 50's
     */
    public static final int TYPE_FIFTIES = 5;

    // Predefined colors
    /**
     * Change the video frame color to the RGB color value GREEN
     */
    public static final int GREEN = 0x0000ff00;
    /**
     * Change the video frame color to the RGB color value PINK
     */
    public static final int PINK = 0x00ff66cc;
    /**
     * Change the video frame color to the RGB color value GRAY
     */
    public static final int GRAY = 0x007f7f7f;

    // The effect type
    /**
     *  The effect type
     */
    private final int mType;

    // The effect color
    /**
     *  The effect color
     */
    private final int mColor;

    /**
@@ -78,16 +90,24 @@ public class EffectColor extends Effect {
     *              If type is TYPE_GRADIENT, color is the RGB color at the
     *              top of the frame. Otherwise, color is ignored
     */
    public EffectColor(MediaItem mediaItem, String effectId, long startTimeMs, long durationMs,
            int type, int color) {
    public EffectColor(MediaItem mediaItem, String effectId, long startTimeMs,
                      long durationMs, int type, int color) {
        super(mediaItem, effectId, startTimeMs, durationMs);
        switch (type) {
            case TYPE_COLOR:
            case TYPE_GRADIENT: {
                switch (color) {
                    case GREEN:
                    case PINK:
                    case GRAY:
                        mColor = color;
                        break;
            }

                    default:
                        throw new IllegalArgumentException("Invalid Color: " + color);
                    }
                    break;
            }
            case TYPE_SEPIA:
            case TYPE_NEGATIVE:
            case TYPE_FIFTIES: {
@@ -99,11 +119,12 @@ public class EffectColor extends Effect {
                throw new IllegalArgumentException("Invalid type: " + type);
            }
        }

        mType = type;
    }

    /**
     * Get the effect type.
     *
     * @return The effect type
     */
    public int getType() {
@@ -111,6 +132,8 @@ public class EffectColor extends Effect {
    }

    /**
     * Get the color if effect type is TYPE_COLOR or TYPE_GRADIENT.
     *
     * @return the color as RGB 888 if type is TYPE_COLOR or TYPE_GRADIENT.
     */
    public int getColor() {
+128 −88

File changed.

Preview size limit exceeded, changes collapsed.

+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */


package android.media.videoeditor;

/**
Loading