android 视频开发2分之2(仿美拍,糗百)
上一篇写了分段录制和合并视频。这一篇则是选择视频,然后截断视频。
1、从sdcard中找到视频资源;
2、分析视频,拿到你须要的帧数当你的图片。(我的是依据參数来算的多少秒1帧的图片,通常是1秒1帧来显示图片)
3、给个 可拉动 控件来选择截断区域 。
(我的是基本參数是最少5秒。最大15秒。如需改动。仅仅需改(MIN_TIME,MAX_TIME)这2个參数则以)
先上图:
咱们来看代码。首先是选择视频的:
package com.example.shipin;
import java.util.ArrayList;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
public class VideoNewSelectActivity extends BaseActivity {
/** 图片展示器 */
private GridView gridview;
/** 图片适配器 */
private ViewNewSelectAdapter adapter;
/** 数据集 */
private ArrayList<ViewNewSelectBean> list;
private ImageView img_back;
/** 显示图片的宽 */
private int width;
@Override
protected int getContentViewId() {
return R.layout.activity_video_new_select;
}
@SuppressWarnings("deprecation")
@Override
protected void findViews() {
img_back = (ImageView) findViewById(R.id.video_new_img_back);
gridview = (GridView) findViewById(R.id.video_new_select_gridview);
width = (getWindowManager().getDefaultDisplay().getWidth() - DisplayUtil.dip2px(VideoNewSelectActivity.this, 60)) / 3;
}
@Override
protected void init() {
list = new ArrayList<ViewNewSelectBean>();
adapter = new ViewNewSelectAdapter(list, width, VideoNewSelectActivity.this);
gridview.setAdapter(adapter);
getList();
}
@Override
protected void widgetListener() {
img_back.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
gridview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Bundle bundle = new Bundle();
bundle.putSerializable("serializable", list.get(position));
Intent intent = new Intent(VideoNewSelectActivity.this, VideoNewCutActivity.class);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
adapter.notifyDataSetChanged();
}
};
};
/**
* 获取数据
*
* @version 1.0
* @createTime 2015年6月16日,下午6:14:09
* @updateTime 2015年6月16日,下午6:14:09
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
*/
private void getList() {
new Thread(new Runnable() {
@Override
public void run() {
// 若为图片则为MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri originalUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(originalUri, null, null, null, null);
if (cursor == null) {
return;
}
while (cursor.moveToNext()) {
ViewNewSelectBean bean = new ViewNewSelectBean();
bean.set_id(cursor.getLong(cursor.getColumnIndex("_ID")));
bean.setName(cursor.getString(cursor.getColumnIndex("_display_name")));// 视频名字
bean.setPath(cursor.getString(cursor.getColumnIndex("_data")));// 路径
bean.setWidth(cursor.getInt(cursor.getColumnIndex("width")));// 视频宽
bean.setHeight(cursor.getInt(cursor.getColumnIndex("height")));// 视频高
bean.setDuration(cursor.getLong(cursor.getColumnIndex("duration")));// 时长
list.add(bean);
}
Message message = handler.obtainMessage();
message.what = 1;
handler.sendMessage(message);
// /data/data/com.android.providers.media/databases/external.db
// {5dd10730} 数据库位置
}
}).start();
}
private class ViewNewSelectAdapter extends BaseAdapter {
private Context context;
/** 数据集 */
private ArrayList<ViewNewSelectBean> list;
/** 图片宽 */
private int width;
public ViewNewSelectAdapter(ArrayList<ViewNewSelectBean> list, int width, Context context) {
this.list = list;
this.width = width;
this.context = context;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@SuppressLint("InflateParams")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
final ViewNewSelectBean bean = list.get(position);
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.item_video_new_select_gridview, null);
viewHolder.txt = (TextView) convertView.findViewById(R.id.item_video_new_select_txt_time);
viewHolder.img = (ImageView) convertView.findViewById(R.id.item_video_new_select_img);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
LayoutParams layoutParams = (LayoutParams) viewHolder.img.getLayoutParams();
layoutParams.width = width;
layoutParams.height = width;
viewHolder.img.setLayoutParams(layoutParams);
// 获取图片
Bitmap bitmap = getVideoThumbnail(bean.getPath(), width, width, MediaStore.Images.Thumbnails.MICRO_KIND);
if (bitmap != null) {
// 设置图片
viewHolder.img.setImageBitmap(bitmap);
}
// 设置时长
viewHolder.txt.setText(String.format("时长:%1$s s", bean.getDuration() / 1000));
return convertView;
}
/**
* 获取视频的缩略图
* 先通过ThumbnailUtils来创建一个视频的缩略图,然后再利用ThumbnailUtils来生成指定大小的缩略图。
* 假设想要的缩略图的宽和高都小于MICRO_KIND,则类型要使用MICRO_KIND作为kind的值,这样会节省内存。
*
* @param videoPath
* 视频的路径
* @param width
* 指定输出视频缩略图的宽度
* @param height
* 指定输出视频缩略图的高度度
* @param kind
* 參照MediaStore.Images.
* Thumbnails类中的常量MINI_KIND和MICRO_KIND。
* 当中,MINI_KIND: 512 x 384,MICRO_KIND: 96 x 96
* @return 指定大小的视频缩略图
*/
private Bitmap getVideoThumbnail(String videoPath, int width, int height, int kind) {
Bitmap bitmap = null;
// 获取视频的缩略图
bitmap = ThumbnailUtils.createVideoThumbnail(videoPath, kind);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
return bitmap;
}
private class ViewHolder {
private ImageView img;
private TextView txt;
}
}
}
看到选择之后就到重点部分了,视频截断。首先得从上个activity拿到视频的数据bean,然后通过视频路径。分析视频,通过自己设置的须要多少帧一张的图片来进行取图片。取完图片则是通过android的新控件RecyclerView来进行显示,听说RecyclerView是Listview的升级版(支持横向和竖向)。我还须要他滚动的距离来进行计算切断的位置。RecyclerView貌似拿不到滚动距离,所以咱们得自己计算了(通过拿到当前滑到的position*view的宽度-view距左边的距离就得到滚动距离了)。还有个矩形框来进行拉动选择截断大小哦。好了咱们来看代码吧(代码凝视量还是比較大的,应该都懂,不懂留言给我一一解答):
package com.example.shipin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.widget.LinearLayoutManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.VideoView;
import com.example.shipin.MyRecyclerView.OnItemScrollChangeListener;
import com.example.shipin.VideoNewCutAdapter.MyItemClickListener;
public class VideoNewCutActivity extends BaseActivity {
/**返回*/
private ImageView img_back;
/**确认*/
private TextView txt_enter;
/** 视频bean */
private ViewNewSelectBean bean;
/** 横向listview */
private MyRecyclerView recyclerView;
/** 封面图button */
private ImageView img_bg;
/** 阴影色块left */
private ImageView img_left;
/** 阴影色块right */
private ImageView img_right;
/** 显示时间 */
private TextView txt_time;
/** 封面容器 */
private RelativeLayout relative;
/** 进度条 */
private RelativeLayout relative1;
/** 视频播放 */
private VideoView videoView;
/** 数据集 */
private ArrayList<String> list;
/** 列表适配器 */
private VideoNewCutAdapter adapter;
/** 屏幕宽度 */
private int width;
/** 暂时保存文件路径 */
private String savePath;
/** 最少多少秒 */
public static final int MIN_TIME = 5000;
/** 最大多少秒 */
public static final int MAX_TIME = 15000;
/** 屏幕中1像素占有多少毫秒 */
private float picture = 0;
/** 多少秒一帧 */
private float second_Z;
/** 是否中断线性 */
private boolean isThread = false;
/** 左边拖动button */
private Button txt_left;
/** 右边拖动button */
private Button txt_right;
/** 按下时X抽坐标 */
private float DownX;
/** 拖动条容器 */
private LayoutParams layoutParams_progress;
/** 阴影背景容器 */
private LayoutParams layoutParams_yin;
/** 拖动条的宽度 */
private int width_progress = 0;
/** 拖动条的间距 */
private int Margin_progress = 0;
/** 阴影框的宽度 */
private int width1_progress = 0;
/** 不能超过右边多少 */
private int right_margin = 0;
/** 全部图片长度 */
private int img_widthAll = 0;
/** 最少保留的多少秒长度 */
private int last_length = 0;
/** 左边啦了多少 */
private int left_lenth = 0;
/** 滚动的长度 */
private int Scroll_lenth = 0;
/** 路径 */
private String Ppath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/videoTest/Image/";
@Override
protected int getContentViewId() {
return R.layout.activity_video_new_cut;
}
@SuppressWarnings("deprecation")
@Override
protected void findViews() {
img_back = (ImageView) findViewById(R.id.video_new_img_back);
txt_enter = (TextView) findViewById(R.id.video_new_txt_enter);
recyclerView = (MyRecyclerView) findViewById(R.id.recyclerview_horizontal);
videoView = (VideoView) findViewById(R.id.video_new_cut_videoview);
img_bg = (ImageView) findViewById(R.id.video_new_cut_img_bg);
img_left = (ImageView) findViewById(R.id.video_new_cut_img_left);
img_right = (ImageView) findViewById(R.id.video_new_cut_img_right);
relative = (RelativeLayout) findViewById(R.id.video_new_cut_relative);
txt_time = (TextView) findViewById(R.id.video_new_cut_txt_time);
relative1 = (RelativeLayout) findViewById(R.id.video_new_cut_relative1);
txt_left = (Button) findViewById(R.id.video_new_cut_txt_left);
txt_right = (Button) findViewById(R.id.video_new_cut_txt_right);
width = getWindowManager().getDefaultDisplay().getWidth();
LayoutParams layoutParams = (LayoutParams) relative.getLayoutParams();
layoutParams.width = width;
layoutParams.height = width;
relative.setLayoutParams(layoutParams);
// 创建一个线性布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
// 设置布局管理器
recyclerView.setLayoutManager(layoutManager);
list = new ArrayList<String>();
adapter = new VideoNewCutAdapter(list);
}
@Override
protected void initGetData() {
super.initGetData();
if (getIntent().getExtras() != null) {
bean = (ViewNewSelectBean) getIntent().getExtras().getSerializable("serializable");
}
}
@Override
protected void init() {
// 创建目录
File file = new File(Ppath);
if (!file.exists()) {
file.mkdir();
}
recyclerView.setAdapter(adapter);
videoView.setVideoPath(bean.getPath());
videoView.requestFocus();
/** 一个屏幕1像素是多少毫秒 13.88888 */
picture = (float) MAX_TIME / (float) width;
/** 1.66666 */
second_Z = (float) MAX_TIME / 1000f / ((float) width / (float) DisplayUtil.dip2px(VideoNewCutActivity.this, 60));
getBitmapsFromVideo(bean.getPath(), (int) bean.getDuration());
}
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
adapter.notifyItemInserted(msg.arg1);
if (msg.arg1 == 0) {
sendVideo(DisplayUtil.dip2px(VideoNewCutActivity.this, 60));
}
} else if (msg.what == 2) {
img_widthAll = (int) (msg.arg1 * 1000 / picture);
last_length = (int) (MIN_TIME / picture);
if (img_widthAll < width) {
right_margin = width - img_widthAll;
LayoutParams layoutParams_right = (LayoutParams) img_right.getLayoutParams();
layoutParams_right.width = width - img_widthAll;
img_right.setLayoutParams(layoutParams_right);
layoutParams_progress = (LayoutParams) relative1.getLayoutParams();
layoutParams_progress.width = img_widthAll;
layoutParams_progress.rightMargin = width - img_widthAll;
relative1.setLayoutParams(layoutParams_progress);
txt_time.setText(msg.arg1 + ".0 s");
} else {
img_widthAll = width;
layoutParams_progress = (LayoutParams) relative1.getLayoutParams();
layoutParams_progress.width = width;
relative1.setLayoutParams(layoutParams_progress);
txt_time.setText((MAX_TIME / 1000) + ".0 s");
}
}
};
};
/**
* 获取视频帧图片
*
* @version 1.0
* @createTime 2015年6月17日,上午11:49:54
* @updateTime 2015年6月17日,上午11:49:54
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param dataPath
* @param lenth
*/
public void getBitmapsFromVideo(final String dataPath, final int lenth) {
new Thread(new Runnable() {
@SuppressLint("NewApi")
@Override
public void run() {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(dataPath);
// 取得视频的长度(单位为秒)
int seconds = lenth / 1000;
Message message = handler.obtainMessage();
message.what = 2;
message.arg1 = seconds;
handler.sendMessage(message);
Bitmap bitmap;
// 得到每一秒时刻的bitmap比方第一秒,第二秒
int index = 0;
for (float f = second_Z; f <= (float) seconds; f += second_Z) {
if (isThread) {
return;
}
bitmap = retriever.getFrameAtTime((long) (f * 1000 * 1000), MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
String path = Ppath + System.currentTimeMillis() + ".jpg";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(path);
bitmap.compress(CompressFormat.JPEG, 80, fos);
fos.close();
list.add(path);
Message message1 = handler.obtainMessage();
message1.what = 1;
message1.arg1 = index;
handler.sendMessage(message1);
index++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
protected void widgetListener() {
/** 列表点击事件 */
adapter.setOnClickListener(new MyItemClickListener() {
@Override
public void onItemClick(View view, int position) {
sendVideo((position + 1) * view.getWidth());
}
});
/** 返回 */
img_back.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
/** 完毕 */
txt_enter.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handler.post(runnable3);
}
});
/** 播放 */
relative.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (videoView.isPlaying()) {
img_bg.setVisibility(View.VISIBLE);
videoView.pause();
handler.removeCallbacks(runnable);
} else {
videoView.setVisibility(View.VISIBLE);
img_bg.setVisibility(View.GONE);
videoView.start();
layoutParams_progress = (LayoutParams) relative1.getLayoutParams();
// 会误差 200-800毫秒
handler.postDelayed(runnable, (long) (layoutParams_progress.width * picture) + 500);
}
}
});
/** 左边拖动button */
txt_left.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
DownX = event.getRawX();
layoutParams_progress = (LayoutParams) relative1.getLayoutParams();
layoutParams_yin = (LayoutParams) img_left.getLayoutParams();
width_progress = layoutParams_progress.width;
Margin_progress = layoutParams_progress.leftMargin;
width1_progress = layoutParams_yin.width;
break;
case MotionEvent.ACTION_MOVE:
LeftMoveLayout(event.getRawX() - DownX, event.getRawX());
break;
case MotionEvent.ACTION_UP:
sendVideo();
layoutParams_progress = null;
layoutParams_yin = null;
break;
default:
break;
}
return false;
}
});
/** 右边拖动button */
txt_right.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
DownX = event.getRawX();
layoutParams_progress = (LayoutParams) relative1.getLayoutParams();
layoutParams_yin = (LayoutParams) img_right.getLayoutParams();
width_progress = layoutParams_progress.width;
Margin_progress = layoutParams_progress.rightMargin;
width1_progress = layoutParams_yin.width;
break;
case MotionEvent.ACTION_MOVE:
RightMoveLayout(DownX - event.getRawX());
break;
case MotionEvent.ACTION_UP:
layoutParams_progress = null;
layoutParams_yin = null;
break;
default:
break;
}
return false;
}
});
/** 视频播放完回调 */
videoView.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
img_bg.setVisibility(View.VISIBLE);
handler.removeCallbacks(runnable);
}
});
/** 滚动监听 */
recyclerView.setOnItemScrollChangeListener(new OnItemScrollChangeListener() {
@Override
public void onChange(View view, int position) {
Scroll_lenth = position * view.getWidth() - view.getLeft();
if (Scroll_lenth <= 0) {
Scroll_lenth = 0;
}
// sendVideo();//打开凝视就是边滑动变更新视图
}
@Override
public void onChangeState(int state) {
if (state == 0) {// 精巧情况时候才调用
sendVideo();
}
}
});
}
private Runnable runnable3 = new Runnable() {
@Override
public void run() {
try {
layoutParams_progress = (LayoutParams) relative1.getLayoutParams();
savePath = Ppath + System.currentTimeMillis() + ".mp4";
FUckTest.startTrim(new File(bean.getPath()), new File(savePath), (long) ((Scroll_lenth + left_lenth) * picture), (long) ((Scroll_lenth
+ left_lenth + layoutParams_progress.width) * picture));
Intent it = new Intent(VideoNewCutActivity.this,VideoActivity.class);
it.putExtra("path", savePath);
startActivity(it);
} catch (IOException e) {
e.printStackTrace();
}
}
};
/**
* 向右边啦
*
* @version 1.0
* @createTime 2015年6月18日,上午9:44:32
* @updateTime 2015年6月18日,上午9:44:32
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param MoveX
*/
private void LeftMoveLayout(float MoveX, float X) {
if (layoutParams_progress != null && layoutParams_yin != null) {
if (Margin_progress + (int) MoveX > 0 && width_progress - (int) MoveX > last_length) {
layoutParams_progress.width = width_progress - (int) MoveX;
layoutParams_progress.leftMargin = Margin_progress + (int) MoveX;
layoutParams_yin.width = width1_progress + (int) MoveX;
relative1.setLayoutParams(layoutParams_progress);
img_left.setLayoutParams(layoutParams_yin);
txt_time.setText((float) (Math.round((layoutParams_progress.width * picture / 1000) * 10)) / 10 + " s");
left_lenth = layoutParams_yin.width;
}
}
}
/**
* 向左边拉
*
* @version 1.0
* @createTime 2015年6月18日,上午9:45:16
* @updateTime 2015年6月18日,上午9:45:16
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param MoveX
*/
private void RightMoveLayout(float MoveX) {
if (layoutParams_progress != null && layoutParams_yin != null) {
if (Margin_progress + (int) MoveX > right_margin && width_progress - (int) MoveX > last_length) {
layoutParams_progress.width = width_progress - (int) MoveX;
layoutParams_progress.rightMargin = Margin_progress + (int) MoveX;
layoutParams_yin.width = width1_progress + (int) MoveX;
txt_time.setText((float) (Math.round((layoutParams_progress.width * picture / 1000) * 10)) / 10 + " s");
relative1.setLayoutParams(layoutParams_progress);
img_right.setLayoutParams(layoutParams_yin);
}
}
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
if (!img_bg.isShown()) {
img_bg.setVisibility(View.VISIBLE);
}
if (videoView.isPlaying()) {
videoView.pause();
}
}
};
/**
* 移动起始播放位置
*
* @version 1.0
* @createTime 2015年6月18日,下午2:34:31
* @updateTime 2015年6月18日,下午2:34:31
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param proSlide
*/
private void sendVideo() {
if (!videoView.isShown()) {
videoView.setVisibility(View.VISIBLE);
}
if (videoView.isPlaying()) {
videoView.pause();
}
if (!img_bg.isShown()) {
img_bg.setVisibility(View.VISIBLE);
}
handler.removeCallbacks(runnable);
videoView.seekTo((int) ((Scroll_lenth + left_lenth) * picture));
}
/**
* 移动起始播放位置
*
* @version 1.0
* @createTime 2015年6月18日,下午6:09:50
* @updateTime 2015年6月18日,下午6:09:50
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param lenth
* 长度
*/
private void sendVideo(int lenth) {
if (!videoView.isShown()) {
videoView.setVisibility(View.VISIBLE);
}
if (videoView.isPlaying()) {
videoView.pause();
}
if (!img_bg.isShown()) {
img_bg.setVisibility(View.VISIBLE);
}
handler.removeCallbacks(runnable);
videoView.seekTo((int) (lenth * picture));
}
@Override
protected void onDestroy() {
super.onDestroy();
isThread = true;
deleteFile();
}
/**
* 退出时删除暂时文件
*
* @version 1.0
* @createTime 2015年6月17日,下午1:58:52
* @updateTime 2015年6月17日,下午1:58:52
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
*/
private void deleteFile() {
for (int i = 0; i < list.size(); i++) {
File file = new File(list.get(i));
if (file.exists()) {
file.delete();
}
}
}
}
如今是自己定义RecyclerView:
package com.utoow.diver.view;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.util.AttributeSet;
import android.view.View;
public class MyRecyclerView extends RecyclerView implements OnScrollListener {
/**记录当前第一个View*/
private View mCurrentView;
private OnItemScrollChangeListener mItemScrollChangeListener;
public void setOnItemScrollChangeListener(OnItemScrollChangeListener mItemScrollChangeListener) {
this.mItemScrollChangeListener = mItemScrollChangeListener;
}
public interface OnItemScrollChangeListener {
void onChange(View view, int position);
void onChangeState(int state);
}
public MyRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnScrollListener(this);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
@Override
public void onScrollStateChanged(int arg0) {
if (mItemScrollChangeListener != null) {
mItemScrollChangeListener.onChangeState(arg0);
}
}
/**
* 滚动时,推断当前第一个View是否发生变化。发生才回调
*
* @version 1.0
* @createTime 2015年6月20日,下午4:14:30
* @updateTime 2015年6月20日,下午4:14:30
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param arg0
* @param arg1
*/
@Override
public void onScrolled(int arg0, int arg1) {
mCurrentView = getChildAt(0);
if (mItemScrollChangeListener != null && mCurrentView != null) {
mItemScrollChangeListener.onChange(mCurrentView, getChildPosition(mCurrentView));
}
}
}
最后再把视频截断和视频合并的工具类贴出来:
package com.example.shipin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import com.coremedia.iso.boxes.Container;
import com.coremedia.iso.boxes.TimeToSampleBox;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;
public class FUckTest {
public static void startTrim(File src, File dst, long start, long end) throws IOException {
Movie movie = MovieCreator.build(src.getAbsolutePath());
// 删除全部跟踪我们将创建新的跟踪从旧
List<Track> tracks = movie.getTracks();
movie.setTracks(new LinkedList<Track>());
double startTime = start / 1000;
double endTime = end / 1000;
boolean timeCorrected = false;
// 我们试图找到一个样品同步跟踪。由于我们仅仅能開始解码在这样一个样品我们应该确保新的片段的開始就是这种一个框架
for (Track track : tracks) {
if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
if (timeCorrected) {
// 这个异常可能是假阳性,以防我们有多个与同步跟踪样品在同样的位置。
比方一部电影包括多个品质同样的视频(微软平滑流媒体文件)
throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");
}
startTime = correctTimeToSyncSample(track, startTime, false);// true
endTime = correctTimeToSyncSample(track, endTime, true);// false
timeCorrected = true;
}
}
for (Track track : tracks) {
long currentSample = 0;
double currentTime = 0;
long startSample = -1;
long endSample = -1;
for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
for (int j = 0; j < entry.getCount(); j++) {
// entry.getDelta()的数量当前样本覆盖。
if (currentTime <= startTime) {
// 眼下的样品仍然在新的開始时间之前
startSample = currentSample;
}
if (currentTime <= endTime) {
// 当前样本后,新的開始时间和仍在新endtime前
endSample = currentSample;
} else {
// 眼下样品结束后出现的视频
break;
}
currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
}
movie.addTrack(new CroppedTrack(track, startSample, endSample));
// break;//取消凝视。仅仅截视频不截音频
}
Container container = new DefaultMp4Builder().build(movie);
if (!dst.exists()) {
dst.createNewFile();
}
FileOutputStream fos = new FileOutputStream(dst);
FileChannel fc = fos.getChannel();
container.writeContainer(fc);
fc.close();
fos.close();
}
/**
* 视频拼接,
*
* @version 1.0
* @createTime 2015年6月10日,下午5:12:15
* @updateTime 2015年6月10日,下午5:12:15
* @createAuthor WangYuWen
* @updateAuthor WangYuWen
* @updateInfo (此处输入改动内容,若无改动可不写.)
*
* @param videos
* 视频文件数据。
《路径》
* @param desName
* 合并之后的文件名称
* @param pro
* @throws IOException
*/
public static void appendVideo(String[] videos, String desName) throws IOException {
Movie[] inMovies = new Movie[videos.length];
int index = 0;
for (String video : videos) {
inMovies[index] = MovieCreator.build(video);
index++;
}
List<Track> videoTracks = new LinkedList<Track>();
List<Track> audioTracks = new LinkedList<Track>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
Container out = new DefaultMp4Builder().build(result);
FileChannel fc = new RandomAccessFile(String.format(desName), "rw").getChannel();
out.writeContainer(fc);
fc.close();
}
protected static long getDuration(Track track) {
long duration = 0;
for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
duration += entry.getCount() * entry.getDelta();
}
return duration;
}
private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
long currentSample = 0;
double currentTime = 0;
for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
for (int j = 0; j < entry.getCount(); j++) {
if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
// 样品总是从1開始,但我们从零因此+ 1開始
timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
}
currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
}
double previous = 0;
for (double timeOfSyncSample : timeOfSyncSamples) {
if (timeOfSyncSample > cutHere) {
if (next) {
return timeOfSyncSample;
} else {
return previous;
}
}
previous = timeOfSyncSample;
}
return timeOfSyncSamples[timeOfSyncSamples.length - 1];
}
}
好了,代码比較长。假设有不懂能够留言。(还有就是有的视频文件比較大就算切断了之后也有30多M,假设说这用到项目中肯定须要上传到server的肯定是不行的,须要的你们能够自己去研究下压缩视频,一般须要用到ffmgep这个c库来进行压缩,顺便说下ffmpeg这个c库真的非常不错非常成熟值得研究)。
最后就是:
希望大家多多关注我的博客,多多支持我。
如有好意见或更好的方式欢迎留言谈论。
尊重原创转载请注明:(http://blog.csdn.net/u013895206) !
以下是地址传送门:
http://download.csdn.net/detail/u013895206/8850751
android 视频开发2分之2(仿美拍,糗百)的更多相关文章
- android 视频开发2分之1(仿美拍,糗百)
近期比較忙,非常久没更新博客,今天我们仿一个美拍或者糗事百科的录像功能. 首先确认步奏: 1.打开摄像头: 2.開始录制: 3.支持分段录制,并支持分段删除: 4.把分段录制的视频进行合并: 不说废话 ...
- Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)
本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...
- Android IOS WebRTC 音视频开发总结(八十三)-- 使用WebRTC广播网络摄像头视频(上)
本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...
- 本人讲课时录制的Android应用开发技术教学视频
网盘地址:http://yun.baidu.com/pcloud/album/info?query_uk=1963923831&album_id=3523786484935252365 本人讲 ...
- 移动开发 android 入门开发 阶段视频
一直想把 android 的开发学习录制成视频,这里录制了一部分供大家学习. http://www.chuanke.com/s5402069.html 到这里,文档,源码,视频基本就全了,祝愿大家能够 ...
- Android IOS WebRTC 音视频开发总结(四六)-- 从另一个角度看国内首届WebRTC大会
文章主要从开发者角度谈国内首届WebRTC大会,支持原创,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,更多详见www.rtc.help. -------------------- ...
- Android IOS WebRTC 音视频开发总结(六)-- iOS开发之含泪经验
前段时间在搞webrtc iOS开发,所以将标题改为了Android IOS WebRTC 音视频开发总结, 下面都是开发过程中的经验总结,转载请说明出处(博客园RTC.Blacker): 1. IO ...
- Android 音视频开发学习思路
Android 音视频开发这块目前的确没有比较系统的教程或者书籍,网上的博客文章也都是比较零散的.只能通过一点点的学习和积累把这块的知识串联积累起来. 初级入门篇: Android 音视频开发(一) ...
- Android 音视频开发(一) : 通过三种方式绘制图片
版权声明:转载请说明出处:http://www.cnblogs.com/renhui/p/7456956.html 在 Android 音视频开发学习思路 里面,我们写到了,想要逐步入门音视频开发,就 ...
随机推荐
- 【转载·】Linux yum 安装 gcc 、gcc-c++
2017年09月29日 22:45:54 上善若水 阅读数:6653更多 个人分类: Linux学习 所属专栏: Linux学习杂技 版权声明:本文为博主原创文章,未经博主允许不得转载. ht ...
- 做一个可复用的 echarts-vue 组件(延迟动画加载)
在 vue 项目使用 echarts 的场景中,以下三点不容忽视:1. 可视化的数据往往是异步加载的:2. 若一个页面存在大量的图表( 尤其当存在关系图和地图时 ),往往会导致该页面的渲染速度很慢并可 ...
- 紫书 习题 8-16 UVa 1618 (中途相遇法)
暴力n的四次方, 然而可以用中途相遇法的思想, 分左边两个数和右边两个数来判断, 最后合起来判断. 一边是n平方logn, 合起来是n平方logn(枚举n平方, 二分logn) (1)两种比较方式是相 ...
- js 函数基础(方便复习使用)
// 函数声明: function bbq(){ // ..... } // 函数表达式: // 1.命名函数表达式 var test = function abc(){ document.write ...
- 2015 Multi-University Training Contest 3 hdu 5316 Magician
Magician Time Limit: 18000/9000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total S ...
- Redis介绍以及安装具体解释
redis是一个key-value存储系统. 和Memcached类似.它支持存储的value类型相对很多其它,包含string(字符串).list(链表).set(集合).zset(sorted s ...
- 新手做2D手游该用哪些工具?
全球手游行业规模将突破250亿美元,越来越多的开发者开始进入手游研发领域,而作为一名菜鸟,很多时候,如果没有其他开发者的建议,会走很多弯路.一开始进入游戏研发领域的时候,你很难知道该选择什么工具.什么 ...
- IDEA无法启动:Failed to create JVM:error code -4
发生该错误的原因是由于IDEA须要使用的连续内存空间没有得到满足,解决方式: 1.减小-Xmx和-XX:PermSize的值 切换到IDE_HOME\bin\文件夹下,找到<produc ...
- Delphi的时间 x87 fpu control word 精度设置的不够
在win7 64位系统下, 一个DELPHI写的DLL注入一个C语言程序后. 出现非常奇怪的浮点数相加出错的情况. (注: 在XP系统下是正常的).比如: 40725.0001597563 + 0.7 ...
- MFC中CFileDialog使用方法
用CFileDialog选择了一个文件后,使用FILE::fopen打开文件错误,使用 的是相对地址.和王工调试了半天,怎么跟踪也没发现错误,原来如此. .... .. . . CFileDialog ...