• 在android中进行视频的分割


       最近项目有个需求要对录制的视频进行分割,查了很多资料,看到ffmpeg可以对视频进行分割。上网找到别人基于android的开源ffmpeg,终于编译成功ffmpeg.so。但是要使用的话还要查ffmpeg的api,并且写jni的调用接口,非常麻烦。偶然情况下发现了开源软件mp4parser: http://code.google.com/p/mp4parser/ 一款非常棒的开源软件,可以对视频进行分割、组合等操作,而且使用起来非常简单。通过svn对其下载后可以看到里面带着视频分割的例子,但是是用java实现,将其稍微修改一下就可以用在Android上了。

          首先将例子中的代码修改为一个工具类,通过接口传进视频文件的路径和截取视频的开始、结束时间。需要注意的是,如果传的开始时间是10s,视频一般不会刚好是从10s开始的,要根据视频的关键帧做一下调整。截取出来的视频会放到存储卡的Clip目录下。代码如下:

    点击(此处)折叠或打开

    1. package com.example.mp4clip;
    2. import java.io.File;
    3. import java.io.FileOutputStream;
    4. import java.io.IOException;
    5. import java.nio.channels.FileChannel;
    6. import java.util.Arrays;
    7. import java.util.LinkedList;
    8. import java.util.List;
    9. import android.os.Environment;
    10. import android.util.Log;
    11. import com.coremedia.iso.boxes.Container;
    12. import com.googlecode.mp4parser.authoring.Movie;
    13. import com.googlecode.mp4parser.authoring.Track;
    14. import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
    15. import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
    16. import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;
    17. public class ClipUtil {
    18.     private static final String TAG = "ClipUtil";
    19.     /**
    20.      * 截取指定时间段的视频
    21.      * @param path 视频的路径
    22.      * @param begin 需要截取的开始时间
    23.      * @param end 截取的结束时间
    24.      * @throws IOException
    25.      */
    26.     public static void clipVideo(String path, double begin, double end)
    27.             throws IOException {
    28.         File mSdCardDir = Environment.getExternalStorageDirectory();
    29.         File f = new File(mSdCardDir.getAbsolutePath() + File.separator
    30.                 + Util.SAVE_PATH);
    31.         if (!f.exists()) {
    32.             f.mkdir();
    33.         }
    34.         // Movie movie = new MovieCreator().build(new
    35.         // RandomAccessFile("/home/sannies/suckerpunch-distantplanet_h1080p/suckerpunch-distantplanet_h1080p.mov",
    36.         // "r").getChannel());
    37.         Movie movie = MovieCreator.build(path);
    38.         List<Track> tracks = movie.getTracks();
    39.         movie.setTracks(new LinkedList<Track>());
    40.         // remove all tracks we will create new tracks from the old
    41.         double startTime1 = begin;
    42.         double endTime1 = end;
    43.         // double startTime2 = 30;
    44.         // double endTime2 = 40;
    45.         boolean timeCorrected = false;
    46.         // Here we try to find a track that has sync samples. Since we can only
    47.         // start decoding
    48.         // at such a sample we SHOULD make sure that the start of the new
    49.         // fragment is exactly
    50.         // such a frame
    51.         for (Track track : tracks) {
    52.             if (track.getSyncSamples() != null
    53.                     && track.getSyncSamples().length > 0) {
    54.                 if (timeCorrected) {
    55.                     // This exception here could be a false positive in case we
    56.                     // have multiple tracks
    57.                     // with sync samples at exactly the same positions. E.g. a
    58.                     // single movie containing
    59.                     // multiple qualities of the same video (Microsoft Smooth
    60.                     // Streaming file)
    61.                     Log.e(TAG,
    62.                             "The startTime has already been corrected by another track with SyncSample. Not Supported.");
    63.                     throw new RuntimeException(
    64.                             "The startTime has already been corrected by another track with SyncSample. Not Supported.");
    65.                 }
    66.                 startTime1 = correctTimeToSyncSample(track, startTime1, false);
    67.                 endTime1 = correctTimeToSyncSample(track, endTime1, true);
    68.                 // startTime2 = correctTimeToSyncSample(track, startTime2,
    69.                 // false);
    70.                 // endTime2 = correctTimeToSyncSample(track, endTime2, true);
    71.                 timeCorrected = true;
    72.             }
    73.         }
    74.         for (Track track : tracks) {
    75.             long currentSample = 0;
    76.             double currentTime = 0;
    77.             double lastTime = 0;
    78.             long startSample1 = -1;
    79.             long endSample1 = -1;
    80.             // long startSample2 = -1;
    81.             // long endSample2 = -1;
    82.             for (int i = 0; i < track.getSampleDurations().length; i++) {
    83.                 long delta = track.getSampleDurations()[i];
    84.                 if (currentTime > lastTime && currentTime <= startTime1) {
    85.                     // current sample is still before the new starttime
    86.                     startSample1 = currentSample;
    87.                 }
    88.                 if (currentTime > lastTime && currentTime <= endTime1) {
    89.                     // current sample is after the new start time and still
    90.                     // before the new endtime
    91.                     endSample1 = currentSample;
    92.                 }
    93.                 // if (currentTime > lastTime && currentTime <= startTime2) {
    94.                 // // current sample is still before the new starttime
    95.                 // startSample2 = currentSample;
    96.                 // }
    97.                 // if (currentTime > lastTime && currentTime <= endTime2) {
    98.                 // // current sample is after the new start time and still
    99.                 // before the new endtime
    100.                 // endSample2 = currentSample;
    101.                 // }
    102.                 lastTime = currentTime;
    103.                 currentTime += (double) delta
    104.                         / (double) track.getTrackMetaData().getTimescale();
    105.                 currentSample++;
    106.             }
    107.             movie.addTrack(new CroppedTrack(track, startSample1, endSample1));// new
    108.                                                                                 // AppendTrack(new
    109.                                                                                 // CroppedTrack(track,
    110.                                                                                 // startSample1,
    111.                                                                                 // endSample1),
    112.                                                                                 // new
    113.                                                                                 // CroppedTrack(track,
    114.                                                                                 // startSample2,
    115.                                                                                 // endSample2)));
    116.         }
    117.         long start1 = System.currentTimeMillis();
    118.         Container out = new DefaultMp4Builder().build(movie);
    119.         long start2 = System.currentTimeMillis();
    120.         FileOutputStream fos = new FileOutputStream(f.getAbsolutePath()
    121.                 + File.separator
    122.                 + String.format("output-%f-%f.mp4", startTime1, endTime1));
    123.         FileChannel fc = fos.getChannel();
    124.         out.writeContainer(fc);
    125.         fc.close();
    126.         fos.close();
    127.         long start3 = System.currentTimeMillis();
    128.         Log.e(TAG, "Building IsoFile took : " + (start2 - start1) + "ms");
    129.         Log.e(TAG, "Writing IsoFile took : " + (start3 - start2) + "ms");
    130.         Log.e(TAG,
    131.                 "Writing IsoFile speed : "
    132.                         + (new File(String.format("output-%f-%f.mp4",
    133.                                 startTime1, endTime1)).length()
    134.                                 / (start3 - start2) / 1000) + "MB/s");
    135.     }
    136.     private static double correctTimeToSyncSample(Track track, double cutHere,
    137.             boolean next) {
    138.         double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
    139.         long currentSample = 0;
    140.         double currentTime = 0;
    141.         for (int i = 0; i < track.getSampleDurations().length; i++) {
    142.             long delta = track.getSampleDurations()[i];
    143.             if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
    144.                 // samples always start with 1 but we start with zero therefore
    145.                 // +1
    146.                 timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(),
    147.                         currentSample + 1)] = currentTime;
    148.             }
    149.             currentTime += (double) delta
    150.                     / (double) track.getTrackMetaData().getTimescale();
    151.             currentSample++;
    152.         }
    153.         double previous = 0;
    154.         for (double timeOfSyncSample : timeOfSyncSamples) {
    155.             if (timeOfSyncSample > cutHere) {
    156.                 if (next) {
    157.                     return timeOfSyncSample;
    158.                 } else {
    159.                     return previous;
    160.                 }
    161.             }
    162.             previous = timeOfSyncSample;
    163.         }
    164.         return timeOfSyncSamples[timeOfSyncSamples.length - 1];
    165.     }
    166. }

         

             有了工具类,下面就是增加一个操作界面了。我用一个列表列出所有的视频,点击视频后就会在后台截取出5s~15s总共10s的视频。当然也可以根据需要加上自己想要的开始结束时间,代码如下:


    点击(此处)折叠或打开

    1. package com.example.mp4clip;
    2. import java.io.IOException;
    3. import java.lang.ref.SoftReference;
    4. import android.app.Activity;
    5. import android.content.Context;
    6. import android.content.Intent;
    7. import android.database.Cursor;
    8. import android.graphics.drawable.Drawable;
    9. import android.net.Uri;
    10. import android.os.Bundle;
    11. import android.os.Environment;
    12. import android.provider.MediaStore;
    13. import android.util.Log;
    14. import android.util.SparseArray;
    15. import android.view.Menu;
    16. import android.view.MenuItem;
    17. import android.view.View;
    18. import android.view.ViewGroup;
    19. import android.widget.AdapterView;
    20. import android.widget.AdapterView.OnItemClickListener;
    21. import android.widget.ImageView;
    22. import android.widget.ListView;
    23. import android.widget.SimpleCursorAdapter;
    24. import android.widget.TextView;
    25. import edu.mit.mobile.android.imagecache.ImageCache;
    26. import edu.mit.mobile.android.imagecache.ImageCache.OnImageLoadListener;
    27. public class MainActivity extends Activity implements OnItemClickListener,
    28.         OnImageLoadListener {
    29.     private static final String TAG = "MainActivity";
    30.     ListView mList;
    31.     private Cursor mCursor;
    32.     private final SparseArray<SoftReference<ImageView>> mImageViewsToLoad = new SparseArray<SoftReference<ImageView>>();
    33.     private ImageCache mCache;
    34.     @Override
    35.     protected void onCreate(Bundle savedInstanceState) {
    36.         super.onCreate(savedInstanceState);
    37.         setContentView(R.layout.activity_main);
    38.         mCache = ImageCache.getInstance(this);
    39.         mCache.registerOnImageLoadListener(this);
    40.         mList = (ListView) findViewById(R.id.list);
    41.         mList.setOnItemClickListener(this);
    42.         mCursor = getContentResolver().query(
    43.                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, null, null,
    44.                 MediaStore.Video.Media.DATE_MODIFIED + " desc");
    45.         SimpleCursorAdapter adapter = new videoListAdapter(this,
    46.                 R.layout.video_listitem, mCursor,
    47.                 new String[] { MediaStore.Video.Media.TITLE },
    48.                 new int[] { R.id.video_title });
    49.         mList.setAdapter(adapter);
    50.     }
    51.     @Override
    52.     public boolean onCreateOptionsMenu(Menu menu) {
    53.         getMenuInflater().inflate(R.menu.main, menu);
    54.         return true;
    55.     }
    56.     public boolean onOptionsItemSelected(MenuItem item) {
    57.         // 扫描新多媒体文件,添加到数据库中
    58.         sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
    59.                 Uri.parse("file://"
    60.                         + Environment.getExternalStorageDirectory()
    61.                                 .getAbsolutePath())));
    62.         return false;
    63.     }
    64.     @Override
    65.     public void onItemClick(AdapterView<?> parent, View view, int position,
    66.             long id) {
    67.         if (mCursor.moveToPosition(position)) {
    68.             int index = -1;
    69.             index = mCursor.getColumnIndex(MediaStore.Video.Media.DATA);
    70.             String path = null;
    71.             if (index >= 0) {
    72.                 path = mCursor.getString(index);
    73.                 try {
    74.                     ClipUtil.clipVideo(path, 5, 15);
    75.                 } catch (IOException e) {
    76.                     // TODO Auto-generated catch block
    77.                     e.printStackTrace();
    78.                 }
    79.             }
    80.         }
    81.     }
    82.     private static final class ViewHolder {
    83.         /** 视频名称 */
    84.         TextView titleView;
    85.         /** 视频时长 */
    86.         TextView durationView;
    87.         /** 文件大小 */
    88.         TextView sizeView;
    89.     }
    90.     private class videoListAdapter extends SimpleCursorAdapter {
    91.         /*
    92.          * constructor.
    93.          */
    94.         public videoListAdapter(Context context, int layout, Cursor c,
    95.                 String[] from, int[] to) {
    96.             super(context, layout, c, from, to);
    97.         }
    98.         @Override
    99.         public int getCount() {
    100.             return super.getCount();
    101.         }
    102.         @Override
    103.         public Object getItem(int position) {
    104.             return super.getItem(position);
    105.         }
    106.         @Override
    107.         public long getItemId(int position) {
    108.             return super.getItemId(position);
    109.         }
    110.         @Override
    111.         public View getView(int position, View convertView, ViewGroup parent) {
    112.             View view = super.getView(position, convertView, parent);
    113.             Cursor cursor = getCursor();
    114.             cursor.moveToPosition(position);
    115.             ViewHolder holder = (ViewHolder) view.getTag();
    116.             if (holder == null) {
    117.                 holder = new ViewHolder();
    118.                 holder.titleView = (TextView) view
    119.                         .findViewById(R.id.video_title);
    120.                 holder.durationView = (TextView) view
    121.                         .findViewById(R.id.video_duration);
    122.                 holder.sizeView = (TextView) view.findViewById(R.id.video_size);
    123.             }
    124.             view.setTag(holder);
    125.             final ImageView iv = (ImageView) view.findViewById(R.id.thumbnail);
    126.             int index = -1;
    127.             index = mCursor.getColumnIndex(MediaStore.Video.Media.DATA);
    128.             String path = null;
    129.             if (index >= 0) {
    130.                 path = mCursor.getString(index);
    131.                 try {
    132.                     Drawable draw = mCache.loadImage(position, Uri.parse(path),
    133.                             120, 120);
    134.                     if (draw != null) {
    135.                         iv.setBackground(draw);
    136.                     } else {
    137.                         mImageViewsToLoad.put(position,
    138.                                 new SoftReference<ImageView>(iv));
    139.                     }
    140.                 } catch (IOException e) {
    141.                     e.printStackTrace();
    142.                 }
    143.             }
    144.             index = -1;
    145.             index = cursor.getColumnIndex(MediaStore.Video.Media.TITLE);
    146.             String title = null;
    147.             if (index >= 0) {
    148.                 title = cursor.getString(index);
    149.                 holder.titleView.setText(title);
    150.             }
    151.             index = -1;
    152.             index = cursor.getColumnIndex(MediaStore.Video.Media.DURATION);
    153.             int duration;
    154.             if (index >= 0) {
    155.                 duration = cursor.getInt(index);
    156.                 holder.durationView.setText(Util.durationFormat(duration));
    157.             }
    158.             index = -1;
    159.             index = cursor.getColumnIndex(MediaStore.Video.Media.SIZE);
    160.             long size;
    161.             if (index >= 0) {
    162.                 size = cursor.getLong(index);
    163.                 holder.sizeView.setText(Util.sizeFormat(size));
    164.             }
    165.             return view;
    166.         }
    167.     }
    168.     @Override
    169.     public void onImageLoaded(int id, Uri imageUri, Drawable image) {
    170.         Log.d(TAG, "onImageLoaded:" + id);
    171.         final SoftReference<ImageView> ivRef = mImageViewsToLoad.get(id);
    172.         if (ivRef == null) {
    173.             Log.d(TAG, "ivRef=null");
    174.             return;
    175.         }
    176.         final ImageView iv = ivRef.get();
    177.         if (iv == null) {
    178.             Log.d(TAG, "ivRef=null");
    179.             mImageViewsToLoad.remove(id);
    180.             return;
    181.         }
    182.         iv.setBackground(image);
    183.     }
    184. }


  • 相关阅读:
    十:audio 音频
    八: 操作提示(wxml 即将废弃)
    九: 操作提示(js版本)
    七: 表单标签(3)
    四: 基本标签
    五: 表单标签(1)
    六: 表单标签(2)
    webservice和wcf和web.api简单介绍
    Qt 中事件与处理
    Qt 事件过滤器
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5667637.html
Copyright © 2020-2023  润新知