MediaVideoItem.java revision f1d8f2a140bf5c03c8e3b0a4baab8a3a68fb9f1d
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media.videoeditor;
18
19import java.io.IOException;
20import java.lang.ref.SoftReference;
21
22import android.graphics.Bitmap;
23import android.view.SurfaceHolder;
24
25/**
26 * This class represents a video clip item on the storyboard
27 * {@hide}
28 */
29public class MediaVideoItem extends MediaItem {
30    // Instance variables
31    private final int mWidth;
32    private final int mHeight;
33    private final int mAspectRatio;
34    private final int mFileType;
35    private final int mVideoType;
36    private final int mVideoProfile;
37    private final int mVideoBitrate;
38    private final long mDurationMs;
39    private final int mAudioBitrate;
40    private final int mFps;
41    private final int mAudioType;
42    private final int mAudioChannels;
43    private final int mAudioSamplingFrequency;
44
45    private long mBeginBoundaryTimeMs;
46    private long mEndBoundaryTimeMs;
47    private int mVolumePercentage;
48    private boolean mMuted;
49    private String mAudioWaveformFilename;
50    // The audio waveform data
51    private SoftReference<WaveformData> mWaveformData;
52
53    /**
54     * An object of this type cannot be instantiated with a default constructor
55     */
56    @SuppressWarnings("unused")
57    private MediaVideoItem() throws IOException {
58        this(null, null, null, RENDERING_MODE_BLACK_BORDER);
59    }
60
61    /**
62     * Constructor
63     *
64     * @param editor The video editor reference
65     * @param mediaItemId The MediaItem id
66     * @param filename The image file name
67     * @param renderingMode The rendering mode
68     *
69     * @throws IOException if the file cannot be opened for reading
70     */
71    public MediaVideoItem(VideoEditor editor, String mediaItemId, String filename,
72            int renderingMode)
73        throws IOException {
74        this(editor, mediaItemId, filename, renderingMode, 0, END_OF_FILE, 100, false, null);
75    }
76
77    /**
78     * Constructor
79     *
80     * @param editor The video editor reference
81     * @param mediaItemId The MediaItem id
82     * @param filename The image file name
83     * @param renderingMode The rendering mode
84     * @param beginMs Start time in milliseconds. Set to 0 to extract from the
85     *           beginning
86     * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to
87     *           extract until the end
88     * @param volumePercent in %/. 100% means no change; 50% means half value, 200%
89     *            means double, 0% means silent.
90     * @param muted true if the audio is muted
91     * @param audioWaveformFilename The name of the audio waveform file
92     *
93     * @throws IOException if the file cannot be opened for reading
94     */
95    MediaVideoItem(VideoEditor editor, String mediaItemId, String filename, int renderingMode,
96            long beginMs, long endMs, int volumePercent, boolean muted,
97            String audioWaveformFilename)  throws IOException {
98        super(editor, mediaItemId, filename, renderingMode);
99        // TODO: Set these variables correctly
100        mWidth = 1080;
101        mHeight = 720;
102        mAspectRatio = MediaProperties.ASPECT_RATIO_3_2;
103        mFileType = MediaProperties.FILE_MP4;
104        mVideoType = MediaProperties.VCODEC_H264BP;
105        // Do we have predefined values for this variable?
106        mVideoProfile = 0;
107        // Can video and audio duration be different?
108        mDurationMs = 10000;
109        mVideoBitrate = 800000;
110        mAudioBitrate = 30000;
111        mFps = 30;
112        mAudioType = MediaProperties.ACODEC_AAC_LC;
113        mAudioChannels = 2;
114        mAudioSamplingFrequency = 16000;
115
116        mBeginBoundaryTimeMs = beginMs;
117        mEndBoundaryTimeMs = endMs == END_OF_FILE ? mDurationMs : endMs;
118        mVolumePercentage = volumePercent;
119        mMuted = muted;
120        mAudioWaveformFilename = audioWaveformFilename;
121        if (audioWaveformFilename != null) {
122            mWaveformData =
123                new SoftReference<WaveformData>(new WaveformData(audioWaveformFilename));
124        } else {
125            mWaveformData = null;
126        }
127    }
128
129    /**
130     * Sets the start and end marks for trimming a video media item.
131     * This method will adjust the duration of bounding transitions, effects
132     * and overlays if the current duration of the transactions become greater
133     * than the maximum allowable duration.
134     *
135     * @param beginMs Start time in milliseconds. Set to 0 to extract from the
136     *           beginning
137     * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to
138     *           extract until the end
139     *
140     * @throws IllegalArgumentException if the start time is greater or equal than
141     *           end time, the end time is beyond the file duration, the start time
142     *           is negative
143     */
144    public void setExtractBoundaries(long beginMs, long endMs) {
145        if (beginMs > mDurationMs) {
146            throw new IllegalArgumentException("Invalid start time");
147        }
148        if (endMs > mDurationMs) {
149            throw new IllegalArgumentException("Invalid end time");
150        }
151
152        if (beginMs != mBeginBoundaryTimeMs) {
153            if (mBeginTransition != null) {
154                mBeginTransition.invalidate();
155            }
156        }
157
158        if (endMs != mEndBoundaryTimeMs) {
159            if (mEndTransition != null) {
160                mEndTransition.invalidate();
161            }
162        }
163
164        mBeginBoundaryTimeMs = beginMs;
165        mEndBoundaryTimeMs = endMs;
166
167        adjustTransitions();
168
169        // Note that the start and duration of any effects and overlays are
170        // not adjusted nor are they automatically removed if they fall
171        // outside the new boundaries.
172    }
173
174    /**
175     * @return The boundary begin time
176     */
177    public long getBoundaryBeginTime() {
178        return mBeginBoundaryTimeMs;
179    }
180
181    /**
182     * @return The boundary end time
183     */
184    public long getBoundaryEndTime() {
185        return mEndBoundaryTimeMs;
186    }
187
188    /*
189     * {@inheritDoc}
190     */
191    @Override
192    public void addEffect(Effect effect) {
193        if (effect instanceof EffectKenBurns) {
194            throw new IllegalArgumentException("Ken Burns effects cannot be applied to MediaVideoItem");
195        }
196        super.addEffect(effect);
197    }
198
199    /*
200     * {@inheritDoc}
201     */
202    @Override
203    public Bitmap getThumbnail(int width, int height, long timeMs) {
204        return null;
205    }
206
207    /*
208     * {@inheritDoc}
209     */
210    @Override
211    public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,
212            int thumbnailCount) throws IOException {
213        return null;
214    }
215
216    /*
217     * {@inheritDoc}
218     */
219    @Override
220    void invalidateTransitions(long startTimeMs, long durationMs) {
221        // Check if the item overlaps with the beginning and end transitions
222        if (mBeginTransition != null) {
223            if (isOverlapping(startTimeMs, durationMs,
224                    mBeginBoundaryTimeMs, mBeginTransition.getDuration())) {
225                mBeginTransition.invalidate();
226            }
227        }
228
229        if (mEndTransition != null) {
230            final long transitionDurationMs = mEndTransition.getDuration();
231            if (isOverlapping(startTimeMs, durationMs,
232                    mEndBoundaryTimeMs - transitionDurationMs, transitionDurationMs)) {
233                mEndTransition.invalidate();
234            }
235        }
236    }
237
238    /*
239     * {@inheritDoc}
240     */
241    @Override
242    void invalidateTransitions(long oldStartTimeMs, long oldDurationMs, long newStartTimeMs,
243            long newDurationMs) {
244        // Check if the item overlaps with the beginning and end transitions
245        if (mBeginTransition != null) {
246            final long transitionDurationMs = mBeginTransition.getDuration();
247            // If the start time has changed and if the old or the new item
248            // overlaps with the begin transition, invalidate the transition.
249            if (oldStartTimeMs != newStartTimeMs &&
250                    (isOverlapping(oldStartTimeMs, oldDurationMs,
251                            mBeginBoundaryTimeMs, transitionDurationMs) ||
252                    isOverlapping(newStartTimeMs, newDurationMs,
253                            mBeginBoundaryTimeMs, transitionDurationMs))) {
254                mBeginTransition.invalidate();
255            }
256        }
257
258        if (mEndTransition != null) {
259            final long transitionDurationMs = mEndTransition.getDuration();
260            // If the start time + duration has changed and if the old or the new
261            // item overlaps the end transition, invalidate the transition/
262            if (oldStartTimeMs + oldDurationMs != newStartTimeMs + newDurationMs &&
263                    (isOverlapping(oldStartTimeMs, oldDurationMs,
264                            mEndBoundaryTimeMs - transitionDurationMs, transitionDurationMs) ||
265                    isOverlapping(newStartTimeMs, newDurationMs,
266                            mEndBoundaryTimeMs - transitionDurationMs, transitionDurationMs))) {
267                mEndTransition.invalidate();
268            }
269        }
270    }
271
272    /*
273     * {@inheritDoc}
274     */
275    @Override
276    public int getAspectRatio() {
277        return mAspectRatio;
278    }
279
280    /*
281     * {@inheritDoc}
282     */
283    @Override
284    public int getFileType() {
285        return mFileType;
286    }
287
288    /*
289     * {@inheritDoc}
290     */
291    @Override
292    public int getWidth() {
293        return mWidth;
294    }
295
296    /*
297     * {@inheritDoc}
298     */
299    @Override
300    public int getHeight() {
301        return mHeight;
302    }
303
304    /*
305     * {@inheritDoc}
306     */
307    @Override
308    public long getDuration() {
309        return mDurationMs;
310    }
311
312    /*
313     * {@inheritDoc}
314     */
315    @Override
316    public long getTimelineDuration() {
317        return mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
318    }
319
320    /**
321     * Render a frame according to the playback (in the native aspect ratio) for
322     * the specified media item. All effects and overlays applied to the media
323     * item are ignored. The extract boundaries are also ignored. This method
324     * can be used to playback frames when implementing trimming functionality.
325     *
326     * @param surfaceHolder SurfaceHolder used by the application
327     * @param timeMs time corresponding to the frame to display (relative to the
328     *            the beginning of the media item).
329     * @return The accurate time stamp of the frame that is rendered .
330     * @throws IllegalStateException if a playback, preview or an export is
331     *             already in progress
332     * @throws IllegalArgumentException if time is negative or greater than the
333     *             media item duration
334     */
335    public long renderFrame(SurfaceHolder surfaceHolder, long timeMs) {
336        return timeMs;
337    }
338
339    /**
340     * This API allows to generate a file containing the sample volume levels of
341     * the Audio track of this media item. This function may take significant
342     * time and is blocking. The file can be retrieved using
343     * getAudioWaveformFilename().
344     *
345     * @param listener The progress listener
346     *
347     * @throws IOException if the output file cannot be created
348     * @throws IllegalArgumentException if the mediaItem does not have a valid
349     *             Audio track
350     */
351    public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)
352            throws IOException {
353        // TODO: Set mAudioWaveformFilename at the end once the export is complete
354        mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename));
355    }
356
357    /**
358     * Get the audio waveform file name if {@link #extractAudioWaveform()} was
359     * successful. The file format is as following:
360     * <ul>
361     *  <li>first 4 bytes provide the number of samples for each value, as big-endian signed</li>
362     *  <li>4 following bytes is the total number of values in the file, as big-endian signed</li>
363     *  <li>all values follow as bytes Name is unique.</li>
364     *</ul>
365     * @return the name of the file, null if the file has not been computed or
366     *         if there is no Audio track in the mediaItem
367     */
368    String getAudioWaveformFilename() {
369        return mAudioWaveformFilename;
370    }
371
372    /**
373     * @return The waveform data
374     */
375    public WaveformData getWaveformData() {
376        if (mWaveformData == null) {
377            return null;
378        }
379
380        WaveformData waveformData = mWaveformData.get();
381        if (waveformData != null) {
382            return waveformData;
383        } else if (mAudioWaveformFilename != null) {
384            waveformData = new WaveformData(mAudioWaveformFilename);
385            mWaveformData = new SoftReference<WaveformData>(waveformData);
386            return waveformData;
387        } else {
388            return null;
389        }
390    }
391
392    /**
393     * Set volume of the Audio track of this mediaItem
394     *
395     * @param volumePercent in %/. 100% means no change; 50% means half value, 200%
396     *            means double, 0% means silent.
397     * @throws UsupportedOperationException if volume value is not supported
398     */
399    public void setVolume(int volumePercent) {
400        mVolumePercentage = volumePercent;
401    }
402
403    /**
404     * Get the volume value of the audio track as percentage. Call of this
405     * method before calling setVolume will always return 100%
406     *
407     * @return the volume in percentage
408     */
409    public int getVolume() {
410        return mVolumePercentage;
411    }
412
413    /**
414     * @param muted true to mute the media item
415     */
416    public void setMute(boolean muted) {
417        mMuted = muted;
418    }
419
420    /**
421     * @return true if the media item is muted
422     */
423    public boolean isMuted() {
424        return mMuted;
425    }
426
427    /**
428     * @return The video type
429     */
430    public int getVideoType() {
431        return mVideoType;
432    }
433
434    /**
435     * @return The video profile
436     */
437    public int getVideoProfile() {
438        return mVideoProfile;
439    }
440
441    /**
442     * @return The video bitrate
443     */
444    public int getVideoBitrate() {
445        return mVideoBitrate;
446    }
447
448    /**
449     * @return The audio bitrate
450     */
451    public int getAudioBitrate() {
452        return mAudioBitrate;
453    }
454
455    /**
456     * @return The number of frames per second
457     */
458    public int getFps() {
459        return mFps;
460    }
461
462    /**
463     * @return The audio codec
464     */
465    public int getAudioType() {
466        return mAudioType;
467    }
468
469    /**
470     * @return The number of audio channels
471     */
472    public int getAudioChannels() {
473        return mAudioChannels;
474    }
475
476    /**
477     * @return The audio sample frequency
478     */
479    public int getAudioSamplingFrequency() {
480        return mAudioSamplingFrequency;
481    }
482}
483