Home | History | Annotate | Download | only in videoeditor
      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 
     17 
     18 package android.media.videoeditor;
     19 
     20 import java.io.File;
     21 import java.io.FileNotFoundException;
     22 import java.io.FileOutputStream;
     23 import java.io.IOException;
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 
     27 import java.io.DataOutputStream;
     28 import java.nio.ByteBuffer;
     29 import java.nio.IntBuffer;
     30 
     31 import android.graphics.Bitmap;
     32 import android.media.videoeditor.MediaArtistNativeHelper.ClipSettings;
     33 import android.media.videoeditor.MediaArtistNativeHelper.FileType;
     34 import android.media.videoeditor.MediaArtistNativeHelper.MediaRendering;
     35 
     36 /**
     37  * This abstract class describes the base class for any MediaItem. Objects are
     38  * defined with a file path as a source data.
     39  * {@hide}
     40  */
     41 public abstract class MediaItem {
     42     /**
     43      *  A constant which can be used to specify the end of the file (instead of
     44      *  providing the actual duration of the media item).
     45      */
     46     public final static int END_OF_FILE = -1;
     47 
     48     /**
     49      *  Rendering modes
     50      */
     51     /**
     52      * When using the RENDERING_MODE_BLACK_BORDER rendering mode video frames
     53      * are resized by preserving the aspect ratio until the movie matches one of
     54      * the dimensions of the output movie. The areas outside the resized video
     55      * clip are rendered black.
     56      */
     57     public static final int RENDERING_MODE_BLACK_BORDER = 0;
     58 
     59     /**
     60      * When using the RENDERING_MODE_STRETCH rendering mode video frames are
     61      * stretched horizontally or vertically to match the current aspect ratio of
     62      * the video editor.
     63      */
     64     public static final int RENDERING_MODE_STRETCH = 1;
     65 
     66     /**
     67      * When using the RENDERING_MODE_CROPPING rendering mode video frames are
     68      * scaled horizontally or vertically by preserving the original aspect ratio
     69      * of the media item.
     70      */
     71     public static final int RENDERING_MODE_CROPPING = 2;
     72 
     73     /**
     74      *  The unique id of the MediaItem
     75      */
     76     private final String mUniqueId;
     77 
     78     /**
     79      *  The name of the file associated with the MediaItem
     80      */
     81     protected final String mFilename;
     82 
     83     /**
     84      *  List of effects
     85      */
     86     private final List<Effect> mEffects;
     87 
     88     /**
     89      *  List of overlays
     90      */
     91     private final List<Overlay> mOverlays;
     92 
     93     /**
     94      *  The rendering mode
     95      */
     96     private int mRenderingMode;
     97 
     98     private final MediaArtistNativeHelper mMANativeHelper;
     99 
    100     private final String mProjectPath;
    101 
    102     /**
    103      *  Beginning and end transitions
    104      */
    105     protected Transition mBeginTransition;
    106 
    107     protected Transition mEndTransition;
    108 
    109     protected String mGeneratedImageClip;
    110 
    111     protected boolean mRegenerateClip;
    112 
    113     private boolean mBlankFrameGenerated = false;
    114 
    115     private String mBlankFrameFilename = null;
    116 
    117     /**
    118      * Constructor
    119      *
    120      * @param editor The video editor reference
    121      * @param mediaItemId The MediaItem id
    122      * @param filename name of the media file.
    123      * @param renderingMode The rendering mode
    124      * @throws IOException if file is not found
    125      * @throws IllegalArgumentException if a capability such as file format is
    126      *             not supported the exception object contains the unsupported
    127      *             capability
    128      */
    129     protected MediaItem(VideoEditor editor, String mediaItemId, String filename,
    130                         int renderingMode) throws IOException {
    131         if (filename == null) {
    132             throw new IllegalArgumentException("MediaItem : filename is null");
    133         }
    134         File file = new File(filename);
    135         if (!file.exists()) {
    136             throw new IOException(filename + " not found ! ");
    137         }
    138 
    139         /*Compare file_size with 2GB*/
    140         if (VideoEditor.MAX_SUPPORTED_FILE_SIZE <= file.length()) {
    141             throw new IllegalArgumentException("File size is more than 2GB");
    142         }
    143         mUniqueId = mediaItemId;
    144         mFilename = filename;
    145         mRenderingMode = renderingMode;
    146         mEffects = new ArrayList<Effect>();
    147         mOverlays = new ArrayList<Overlay>();
    148         mBeginTransition = null;
    149         mEndTransition = null;
    150         mMANativeHelper = ((VideoEditorImpl)editor).getNativeContext();
    151         mProjectPath = editor.getPath();
    152         mRegenerateClip = false;
    153         mGeneratedImageClip = null;
    154     }
    155 
    156     /**
    157      * @return The id of the media item
    158      */
    159     public String getId() {
    160         return mUniqueId;
    161     }
    162 
    163     /**
    164      * @return The media source file name
    165      */
    166     public String getFilename() {
    167         return mFilename;
    168     }
    169 
    170     /**
    171      * If aspect ratio of the MediaItem is different from the aspect ratio of
    172      * the editor then this API controls the rendering mode.
    173      *
    174      * @param renderingMode rendering mode. It is one of:
    175      *            {@link #RENDERING_MODE_BLACK_BORDER},
    176      *            {@link #RENDERING_MODE_STRETCH}
    177      */
    178     public void setRenderingMode(int renderingMode) {
    179         switch (renderingMode) {
    180             case RENDERING_MODE_BLACK_BORDER:
    181             case RENDERING_MODE_STRETCH:
    182             case RENDERING_MODE_CROPPING:
    183                 break;
    184 
    185             default:
    186                 throw new IllegalArgumentException("Invalid Rendering Mode");
    187         }
    188 
    189         mMANativeHelper.setGeneratePreview(true);
    190 
    191         mRenderingMode = renderingMode;
    192         if (mBeginTransition != null) {
    193             mBeginTransition.invalidate();
    194         }
    195 
    196         if (mEndTransition != null) {
    197             mEndTransition.invalidate();
    198         }
    199 
    200         for (Overlay overlay : mOverlays) {
    201             ((OverlayFrame)overlay).invalidateGeneratedFiles();
    202         }
    203     }
    204 
    205     /**
    206      * @return The rendering mode
    207      */
    208     public int getRenderingMode() {
    209         return mRenderingMode;
    210     }
    211 
    212     /**
    213      * @param transition The beginning transition
    214      */
    215     void setBeginTransition(Transition transition) {
    216         mBeginTransition = transition;
    217     }
    218 
    219     /**
    220      * @return The begin transition
    221      */
    222     public Transition getBeginTransition() {
    223         return mBeginTransition;
    224     }
    225 
    226     /**
    227      * @param transition The end transition
    228      */
    229     void setEndTransition(Transition transition) {
    230         mEndTransition = transition;
    231     }
    232 
    233     /**
    234      * @return The end transition
    235      */
    236     public Transition getEndTransition() {
    237         return mEndTransition;
    238     }
    239 
    240     /**
    241      * @return The timeline duration. This is the actual duration in the
    242      *         timeline (trimmed duration)
    243      */
    244     public abstract long getTimelineDuration();
    245 
    246     /**
    247      * @return The is the full duration of the media item (not trimmed)
    248      */
    249     public abstract long getDuration();
    250 
    251     /**
    252      * @return The source file type
    253      */
    254     public abstract int getFileType();
    255 
    256     /**
    257      * @return Get the native width of the media item
    258      */
    259     public abstract int getWidth();
    260 
    261     /**
    262      * @return Get the native height of the media item
    263      */
    264     public abstract int getHeight();
    265 
    266     /**
    267      * Get aspect ratio of the source media item.
    268      *
    269      * @return the aspect ratio as described in MediaProperties.
    270      *         MediaProperties.ASPECT_RATIO_UNDEFINED if aspect ratio is not
    271      *         supported as in MediaProperties
    272      */
    273     public abstract int getAspectRatio();
    274 
    275     /**
    276      * Add the specified effect to this media item.
    277      *
    278      * Note that certain types of effects cannot be applied to video and to
    279      * image media items. For example in certain implementation a Ken Burns
    280      * implementation cannot be applied to video media item.
    281      *
    282      * This method invalidates transition video clips if the
    283      * effect overlaps with the beginning and/or the end transition.
    284      *
    285      * @param effect The effect to apply
    286      * @throws IllegalStateException if a preview or an export is in progress
    287      * @throws IllegalArgumentException if the effect start and/or duration are
    288      *      invalid or if the effect cannot be applied to this type of media
    289      *      item or if the effect id is not unique across all the Effects
    290      *      added.
    291      */
    292     public void addEffect(Effect effect) {
    293 
    294         if (effect == null) {
    295             throw new IllegalArgumentException("NULL effect cannot be applied");
    296         }
    297 
    298         if (effect.getMediaItem() != this) {
    299             throw new IllegalArgumentException("Media item mismatch");
    300         }
    301 
    302         if (mEffects.contains(effect)) {
    303             throw new IllegalArgumentException("Effect already exists: " + effect.getId());
    304         }
    305 
    306         if (effect.getStartTime() + effect.getDuration() > getDuration()) {
    307             throw new IllegalArgumentException(
    308             "Effect start time + effect duration > media clip duration");
    309         }
    310 
    311         mMANativeHelper.setGeneratePreview(true);
    312 
    313         mEffects.add(effect);
    314 
    315         invalidateTransitions(effect.getStartTime(), effect.getDuration());
    316 
    317         if (effect instanceof EffectKenBurns) {
    318             mRegenerateClip = true;
    319         }
    320     }
    321 
    322     /**
    323      * Remove the effect with the specified id.
    324      *
    325      * This method invalidates a transition video clip if the effect overlaps
    326      * with a transition.
    327      *
    328      * @param effectId The id of the effect to be removed
    329      *
    330      * @return The effect that was removed
    331      * @throws IllegalStateException if a preview or an export is in progress
    332      */
    333     public Effect removeEffect(String effectId) {
    334         for (Effect effect : mEffects) {
    335             if (effect.getId().equals(effectId)) {
    336                 mMANativeHelper.setGeneratePreview(true);
    337 
    338                 mEffects.remove(effect);
    339 
    340                 invalidateTransitions(effect.getStartTime(), effect.getDuration());
    341                 if (effect instanceof EffectKenBurns) {
    342                     if (mGeneratedImageClip != null) {
    343                         /**
    344                          *  Delete the file
    345                          */
    346                         new File(mGeneratedImageClip).delete();
    347                         /**
    348                          *  Invalidate the filename
    349                          */
    350                         mGeneratedImageClip = null;
    351                     }
    352                     mRegenerateClip = false;
    353                 }
    354                 return effect;
    355             }
    356         }
    357         return null;
    358     }
    359 
    360     /**
    361      * Set the filepath of the generated image clip when the effect is added.
    362      *
    363      * @param The filepath of the generated image clip.
    364      */
    365     void setGeneratedImageClip(String generatedFilePath) {
    366         mGeneratedImageClip = generatedFilePath;
    367     }
    368 
    369     /**
    370      * Get the filepath of the generated image clip when the effect is added.
    371      *
    372      * @return The filepath of the generated image clip (null if it does not
    373      *         exist)
    374      */
    375     String getGeneratedImageClip() {
    376         return mGeneratedImageClip;
    377     }
    378 
    379     /**
    380      * Find the effect with the specified id
    381      *
    382      * @param effectId The effect id
    383      * @return The effect with the specified id (null if it does not exist)
    384      */
    385     public Effect getEffect(String effectId) {
    386         for (Effect effect : mEffects) {
    387             if (effect.getId().equals(effectId)) {
    388                 return effect;
    389             }
    390         }
    391         return null;
    392     }
    393 
    394     /**
    395      * Get the list of effects.
    396      *
    397      * @return the effects list. If no effects exist an empty list will be
    398      *         returned.
    399      */
    400     public List<Effect> getAllEffects() {
    401         return mEffects;
    402     }
    403 
    404     /**
    405      * Add an overlay to the storyboard. This method invalidates a transition
    406      * video clip if the overlay overlaps with a transition.
    407      *
    408      * @param overlay The overlay to add
    409      * @throws IllegalStateException if a preview or an export is in progress or
    410      *             if the overlay id is not unique across all the overlays added
    411      *             or if the bitmap is not specified or if the dimensions of the
    412      *             bitmap do not match the dimensions of the media item
    413      * @throws FileNotFoundException, IOException if overlay could not be saved
    414      *             to project path
    415      */
    416     public void addOverlay(Overlay overlay) throws FileNotFoundException, IOException {
    417         if (overlay == null) {
    418             throw new IllegalArgumentException("NULL Overlay cannot be applied");
    419         }
    420 
    421         if (overlay.getMediaItem() != this) {
    422             throw new IllegalArgumentException("Media item mismatch");
    423         }
    424 
    425         if (mOverlays.contains(overlay)) {
    426             throw new IllegalArgumentException("Overlay already exists: " + overlay.getId());
    427         }
    428 
    429         if (overlay.getStartTime() + overlay.getDuration() > getDuration()) {
    430             throw new IllegalArgumentException(
    431             "Overlay start time + overlay duration > media clip duration");
    432         }
    433 
    434         if (overlay instanceof OverlayFrame) {
    435             final OverlayFrame frame = (OverlayFrame)overlay;
    436             final Bitmap bitmap = frame.getBitmap();
    437             if (bitmap == null) {
    438                 throw new IllegalArgumentException("Overlay bitmap not specified");
    439             }
    440 
    441             final int scaledWidth, scaledHeight;
    442             if (this instanceof MediaVideoItem) {
    443                 scaledWidth = getWidth();
    444                 scaledHeight = getHeight();
    445             } else {
    446                 scaledWidth = ((MediaImageItem)this).getScaledWidth();
    447                 scaledHeight = ((MediaImageItem)this).getScaledHeight();
    448             }
    449 
    450             /**
    451              * The dimensions of the overlay bitmap must be the same as the
    452              * media item dimensions
    453              */
    454             if (bitmap.getWidth() != scaledWidth || bitmap.getHeight() != scaledHeight) {
    455                 throw new IllegalArgumentException(
    456                 "Bitmap dimensions must match media item dimensions");
    457             }
    458 
    459             mMANativeHelper.setGeneratePreview(true);
    460             ((OverlayFrame)overlay).save(mProjectPath);
    461 
    462             mOverlays.add(overlay);
    463             invalidateTransitions(overlay.getStartTime(), overlay.getDuration());
    464 
    465         } else {
    466             throw new IllegalArgumentException("Overlay not supported");
    467         }
    468     }
    469 
    470     /**
    471      * @param flag The flag to indicate if regeneration of clip is true or
    472      *            false.
    473      */
    474     void setRegenerateClip(boolean flag) {
    475         mRegenerateClip = flag;
    476     }
    477 
    478     /**
    479      * @return flag The flag to indicate if regeneration of clip is true or
    480      *         false.
    481      */
    482     boolean getRegenerateClip() {
    483         return mRegenerateClip;
    484     }
    485 
    486     /**
    487      * Remove the overlay with the specified id.
    488      *
    489      * This method invalidates a transition video clip if the overlay overlaps
    490      * with a transition.
    491      *
    492      * @param overlayId The id of the overlay to be removed
    493      *
    494      * @return The overlay that was removed
    495      * @throws IllegalStateException if a preview or an export is in progress
    496      */
    497     public Overlay removeOverlay(String overlayId) {
    498         for (Overlay overlay : mOverlays) {
    499             if (overlay.getId().equals(overlayId)) {
    500                 mMANativeHelper.setGeneratePreview(true);
    501 
    502                 mOverlays.remove(overlay);
    503                 if (overlay instanceof OverlayFrame) {
    504                     ((OverlayFrame)overlay).invalidate();
    505                 }
    506                 invalidateTransitions(overlay.getStartTime(), overlay.getDuration());
    507                 return overlay;
    508             }
    509         }
    510         return null;
    511     }
    512 
    513     /**
    514      * Find the overlay with the specified id
    515      *
    516      * @param overlayId The overlay id
    517      *
    518      * @return The overlay with the specified id (null if it does not exist)
    519      */
    520     public Overlay getOverlay(String overlayId) {
    521         for (Overlay overlay : mOverlays) {
    522             if (overlay.getId().equals(overlayId)) {
    523                 return overlay;
    524             }
    525         }
    526 
    527         return null;
    528     }
    529 
    530     /**
    531      * Get the list of overlays associated with this media item
    532      *
    533      * Note that if any overlay source files are not accessible anymore,
    534      * this method will still provide the full list of overlays.
    535      *
    536      * @return The list of overlays. If no overlays exist an empty list will
    537      *         be returned.
    538      */
    539     public List<Overlay> getAllOverlays() {
    540         return mOverlays;
    541     }
    542 
    543     /**
    544      * Create a thumbnail at specified time in a video stream in Bitmap format
    545      *
    546      * @param width width of the thumbnail in pixels
    547      * @param height height of the thumbnail in pixels
    548      * @param timeMs The time in the source video file at which the thumbnail is
    549      *            requested (even if trimmed).
    550      *
    551      * @return The thumbnail as a Bitmap.
    552      *
    553      * @throws IOException if a file error occurs
    554      * @throws IllegalArgumentException if time is out of video duration
    555      */
    556     public abstract Bitmap getThumbnail(int width, int height, long timeMs)
    557                                         throws IOException;
    558 
    559     /**
    560      * Get the array of Bitmap thumbnails between start and end.
    561      *
    562      * @param width width of the thumbnail in pixels
    563      * @param height height of the thumbnail in pixels
    564      * @param startMs The start of time range in milliseconds
    565      * @param endMs The end of the time range in milliseconds
    566      * @param thumbnailCount The thumbnail count
    567      * @param indices The indices of the thumbnails wanted
    568      * @param callback The callback used to pass back the bitmaps
    569      *
    570      * @throws IOException if a file error occurs
    571      */
    572     public abstract void getThumbnailList(int width, int height,
    573                                           long startMs, long endMs,
    574                                           int thumbnailCount,
    575                                           int[] indices,
    576                                           GetThumbnailListCallback callback)
    577                                           throws IOException;
    578 
    579     public interface GetThumbnailListCallback {
    580         public void onThumbnail(Bitmap bitmap, int index);
    581     }
    582 
    583     // This is for compatibility, only used in tests.
    584     public Bitmap[] getThumbnailList(int width, int height,
    585                                      long startMs, long endMs,
    586                                      int thumbnailCount)
    587                                      throws IOException {
    588         final Bitmap[] bitmaps = new Bitmap[thumbnailCount];
    589         int[] indices = new int[thumbnailCount];
    590         for (int i = 0; i < thumbnailCount; i++) {
    591             indices[i] = i;
    592         }
    593         getThumbnailList(width, height, startMs, endMs,
    594                 thumbnailCount, indices, new GetThumbnailListCallback() {
    595             public void onThumbnail(Bitmap bitmap, int index) {
    596                 bitmaps[index] = bitmap;
    597             }
    598         });
    599 
    600         return bitmaps;
    601     }
    602 
    603     /*
    604      * {@inheritDoc}
    605      */
    606     @Override
    607     public boolean equals(Object object) {
    608         if (!(object instanceof MediaItem)) {
    609             return false;
    610         }
    611         return mUniqueId.equals(((MediaItem)object).mUniqueId);
    612     }
    613 
    614     /*
    615      * {@inheritDoc}
    616      */
    617     @Override
    618     public int hashCode() {
    619         return mUniqueId.hashCode();
    620     }
    621 
    622     /**
    623      * Invalidate the start and end transitions if necessary
    624      *
    625      * @param startTimeMs The start time of the effect or overlay
    626      * @param durationMs The duration of the effect or overlay
    627      */
    628     abstract void invalidateTransitions(long startTimeMs, long durationMs);
    629 
    630     /**
    631      * Invalidate the start and end transitions if necessary. This method is
    632      * typically called when the start time and/or duration of an overlay or
    633      * effect is changing.
    634      *
    635      * @param oldStartTimeMs The old start time of the effect or overlay
    636      * @param oldDurationMs The old duration of the effect or overlay
    637      * @param newStartTimeMs The new start time of the effect or overlay
    638      * @param newDurationMs The new duration of the effect or overlay
    639      */
    640     abstract void invalidateTransitions(long oldStartTimeMs, long oldDurationMs,
    641             long newStartTimeMs, long newDurationMs);
    642 
    643     /**
    644      * Check if two items overlap in time
    645      *
    646      * @param startTimeMs1 Item 1 start time
    647      * @param durationMs1 Item 1 duration
    648      * @param startTimeMs2 Item 2 start time
    649      * @param durationMs2 Item 2 end time
    650      * @return true if the two items overlap
    651      */
    652     protected boolean isOverlapping(long startTimeMs1, long durationMs1,
    653                                     long startTimeMs2, long durationMs2) {
    654         if (startTimeMs1 + durationMs1 <= startTimeMs2) {
    655             return false;
    656         } else if (startTimeMs1 >= startTimeMs2 + durationMs2) {
    657             return false;
    658         }
    659 
    660         return true;
    661     }
    662 
    663     /**
    664      * Adjust the duration transitions.
    665      */
    666     protected void adjustTransitions() {
    667         /**
    668          *  Check if the duration of transitions need to be adjusted
    669          */
    670         if (mBeginTransition != null) {
    671             final long maxDurationMs = mBeginTransition.getMaximumDuration();
    672             if (mBeginTransition.getDuration() > maxDurationMs) {
    673                 mBeginTransition.setDuration(maxDurationMs);
    674             }
    675         }
    676 
    677         if (mEndTransition != null) {
    678             final long maxDurationMs = mEndTransition.getMaximumDuration();
    679             if (mEndTransition.getDuration() > maxDurationMs) {
    680                 mEndTransition.setDuration(maxDurationMs);
    681             }
    682         }
    683     }
    684 
    685     /**
    686      * @return MediaArtistNativeHleper context
    687      */
    688     MediaArtistNativeHelper getNativeContext() {
    689         return mMANativeHelper;
    690     }
    691 
    692     /**
    693      * Initialises ClipSettings fields to default value
    694      *
    695      * @param ClipSettings object
    696      *{@link android.media.videoeditor.MediaArtistNativeHelper.ClipSettings}
    697      */
    698     void initClipSettings(ClipSettings clipSettings) {
    699         clipSettings.clipPath = null;
    700         clipSettings.clipDecodedPath = null;
    701         clipSettings.clipOriginalPath = null;
    702         clipSettings.fileType = 0;
    703         clipSettings.endCutTime = 0;
    704         clipSettings.beginCutTime = 0;
    705         clipSettings.beginCutPercent = 0;
    706         clipSettings.endCutPercent = 0;
    707         clipSettings.panZoomEnabled = false;
    708         clipSettings.panZoomPercentStart = 0;
    709         clipSettings.panZoomTopLeftXStart = 0;
    710         clipSettings.panZoomTopLeftYStart = 0;
    711         clipSettings.panZoomPercentEnd = 0;
    712         clipSettings.panZoomTopLeftXEnd = 0;
    713         clipSettings.panZoomTopLeftYEnd = 0;
    714         clipSettings.mediaRendering = 0;
    715         clipSettings.rgbWidth = 0;
    716         clipSettings.rgbHeight = 0;
    717     }
    718 
    719     /**
    720      * @return ClipSettings object with populated data
    721      *{@link android.media.videoeditor.MediaArtistNativeHelper.ClipSettings}
    722      */
    723     ClipSettings getClipSettings() {
    724         MediaVideoItem mVI = null;
    725         MediaImageItem mII = null;
    726         ClipSettings clipSettings = new ClipSettings();
    727         initClipSettings(clipSettings);
    728         if (this instanceof MediaVideoItem) {
    729             mVI = (MediaVideoItem)this;
    730             clipSettings.clipPath = mVI.getFilename();
    731             clipSettings.fileType = mMANativeHelper.getMediaItemFileType(mVI.
    732                                                                  getFileType());
    733             clipSettings.beginCutTime = (int)mVI.getBoundaryBeginTime();
    734             clipSettings.endCutTime = (int)mVI.getBoundaryEndTime();
    735             clipSettings.mediaRendering = mMANativeHelper.
    736                                           getMediaItemRenderingMode(mVI
    737                                           .getRenderingMode());
    738         } else if (this instanceof MediaImageItem) {
    739             mII = (MediaImageItem)this;
    740             clipSettings = mII.getImageClipProperties();
    741         }
    742         return clipSettings;
    743     }
    744 
    745     /**
    746      * Generates a black frame to be used for generating
    747      * begin transition at first media item in storyboard
    748      * or end transition at last media item in storyboard
    749      *
    750      * @param ClipSettings object
    751      *{@link android.media.videoeditor.MediaArtistNativeHelper.ClipSettings}
    752      */
    753     void generateBlankFrame(ClipSettings clipSettings) {
    754         if (!mBlankFrameGenerated) {
    755             int mWidth = 64;
    756             int mHeight = 64;
    757             mBlankFrameFilename = String.format(mProjectPath + "/" + "ghost.rgb");
    758             FileOutputStream fl = null;
    759             try {
    760                  fl = new FileOutputStream(mBlankFrameFilename);
    761             } catch (IOException e) {
    762                 /* catch IO exception */
    763             }
    764             final DataOutputStream dos = new DataOutputStream(fl);
    765 
    766             final int [] framingBuffer = new int[mWidth];
    767 
    768             ByteBuffer byteBuffer = ByteBuffer.allocate(framingBuffer.length * 4);
    769             IntBuffer intBuffer;
    770 
    771             byte[] array = byteBuffer.array();
    772             int tmp = 0;
    773             while(tmp < mHeight) {
    774                 intBuffer = byteBuffer.asIntBuffer();
    775                 intBuffer.put(framingBuffer,0,mWidth);
    776                 try {
    777                     dos.write(array);
    778                 } catch (IOException e) {
    779                     /* catch file write error */
    780                 }
    781                 tmp += 1;
    782             }
    783 
    784             try {
    785                 fl.close();
    786             } catch (IOException e) {
    787                 /* file close error */
    788             }
    789             mBlankFrameGenerated = true;
    790         }
    791 
    792         clipSettings.clipPath = mBlankFrameFilename;
    793         clipSettings.fileType = FileType.JPG;
    794         clipSettings.beginCutTime = 0;
    795         clipSettings.endCutTime = 0;
    796         clipSettings.mediaRendering = MediaRendering.RESIZING;
    797 
    798         clipSettings.rgbWidth = 64;
    799         clipSettings.rgbHeight = 64;
    800     }
    801 
    802     /**
    803      * Invalidates the blank frame generated
    804      */
    805     void invalidateBlankFrame() {
    806         if (mBlankFrameFilename != null) {
    807             if (new File(mBlankFrameFilename).exists()) {
    808                 new File(mBlankFrameFilename).delete();
    809                 mBlankFrameFilename = null;
    810             }
    811         }
    812     }
    813 
    814 }
    815