android mp4 画面裁剪,说说Android的视频裁剪(二)
有关视频裁剪我在前面一篇博客中只是做了简单的说明,主要涉及到的知识准备在这篇博客中拿出来说一下。有兴趣的可以fork我的视频裁剪的项目源码,一起学习进步。
Github:https://github.com/iknow4/Android-Video-Trimmer
视频裁剪页如下图所示:
图上面的视频播放用是VideoView来实现的,比较简单。我主要说说视频帧的读取和视频时间的选取。
视频帧的获取可以通过Android提供的MediaMetadataRetriever类来实现。
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(context, videoUri);
在MediaMetadataRetriever类中会调用到media_jni的这个库中的setDataSource()方法,最终使得我们可以获取视频的meta data,meta data是非常丰富的,足足有26种,足够满足我们的需求了。
/**
* The metadata key to retrieve the numeric string describing the
* order of the audio data source on its original recording.
*/
public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
/**
* The metadata key to retrieve the information about the album title
* of the data source.
*/
public static final int METADATA_KEY_ALBUM = 1;
/**
* The metadata key to retrieve the information about the artist of
* the data source.
*/
public static final int METADATA_KEY_ARTIST = 2;
/**
* The metadata key to retrieve the information about the author of
* the data source.
*/
public static final int METADATA_KEY_AUTHOR = 3;
/**
* The metadata key to retrieve the information about the composer of
* the data source.
*/
public static final int METADATA_KEY_COMPOSER = 4;
/**
* The metadata key to retrieve the date when the data source was created
* or modified.
*/
public static final int METADATA_KEY_DATE = 5;
/**
* The metadata key to retrieve the content type or genre of the data
* source.
*/
public static final int METADATA_KEY_GENRE = 6;
/**
* The metadata key to retrieve the data source title.
*/
public static final int METADATA_KEY_TITLE = 7;
/**
* The metadata key to retrieve the year when the data source was created
* or modified.
*/
public static final int METADATA_KEY_YEAR = 8;
/**
* The metadata key to retrieve the playback duration of the data source.
*/
public static final int METADATA_KEY_DURATION = 9;
/**
* The metadata key to retrieve the number of tracks, such as audio, video,
* text, in the data source, such as a mp4 or 3gpp file.
*/
public static final int METADATA_KEY_NUM_TRACKS = 10;
/**
* The metadata key to retrieve the information of the writer (such as
* lyricist) of the data source.
*/
public static final int METADATA_KEY_WRITER = 11;
/**
* The metadata key to retrieve the mime type of the data source. Some
* example mime types include: "video/mp4", "audio/mp4", "audio/amr-wb",
* etc.
*/
public static final int METADATA_KEY_MIMETYPE = 12;
/**
* The metadata key to retrieve the information about the performers or
* artist associated with the data source.
*/
public static final int METADATA_KEY_ALBUMARTIST = 13;
/**
* The metadata key to retrieve the numberic string that describes which
* part of a set the audio data source comes from.
*/
public static final int METADATA_KEY_DISC_NUMBER = 14;
/**
* The metadata key to retrieve the music album compilation status.
*/
public static final int METADATA_KEY_COMPILATION = 15;
/**
* If this key exists the media contains audio content.
*/
public static final int METADATA_KEY_HAS_AUDIO = 16;
/**
* If this key exists the media contains video content.
*/
public static final int METADATA_KEY_HAS_VIDEO = 17;
/**
* If the media contains video, this key retrieves its width.
*/
public static final int METADATA_KEY_VIDEO_WIDTH = 18;
/**
* If the media contains video, this key retrieves its height.
*/
public static final int METADATA_KEY_VIDEO_HEIGHT = 19;
/**
* This key retrieves the average bitrate (in bits/sec), if available.
*/
public static final int METADATA_KEY_BITRATE = 20;
/**
* This key retrieves the language code of text tracks, if available.
* If multiple text tracks present, the return value will look like:
* "eng:chi"
* @hide
*/
public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES = 21;
/**
* If this key exists the media is drm-protected.
* @hide
*/
public static final int METADATA_KEY_IS_DRM = 22;
/**
* This key retrieves the location information, if available.
* The location should be specified according to ISO-6709 standard, under
* a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude
* of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
*/
public static final int METADATA_KEY_LOCATION = 23;
/**
* This key retrieves the video rotation angle in degrees, if available.
* The video rotation angle may be 0, 90, 180, or 270 degrees.
*/
public static final int METADATA_KEY_VIDEO_ROTATION = 24;
/**
* This key retrieves the original capture framerate, if it's
* available. The capture framerate will be a floating point
* number.
*/
public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25;
// Add more here...
了解这些meta 信息,就可以知道一个视频所能给我们提供的信息有哪些了。
在这里,我们是要获取视频的帧(Frame),帧其实就是图片,一帧可能会有N多张图片组成。通过调用MediaMetadataRetriever类中的getFrameAtTime()获取视频的帧的Bitmap。
/**
* Call this method after setDataSource(). This method finds a
* representative frame close to the given time position by considering
* the given option if possible, and returns it as a bitmap. This is
* useful for generating a thumbnail for an input data source or just
* obtain and display a frame at the given time position.
*
* @param timeUs The time position where the frame will be retrieved.
* When retrieving the frame at the given time position, there is no
* guarantee that the data source has a frame located at the position.
* When this happens, a frame nearby will be returned. If timeUs is
* negative, time position and option will ignored, and any frame
* that the implementation considers as representative may be returned.
*
* @param option a hint on how the frame is found. Use
* {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
* that has a timestamp earlier than or the same as timeUs. Use
* {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
* that has a timestamp later than or the same as timeUs. Use
* {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
* that has a timestamp closest to or the same as timeUs. Use
* {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
* or may not be a sync frame but is closest to or the same as timeUs.
* {@link #OPTION_CLOSEST} often has larger performance overhead compared
* to the other options if there is no sync frame located at timeUs.
*
* @return A Bitmap containing a representative video frame, which
* can be null, if such a frame cannot be retrieved.
*/
public Bitmap getFrameAtTime(long timeUs, int option) {
if (option < OPTION_PREVIOUS_SYNC ||
option > OPTION_CLOSEST) {
throw new IllegalArgumentException("Unsupported option: " + option);
}
return _getFrameAtTime(timeUs, option);
}
这里有两个参数:
timeUs 表示每一帧的开始时间位置,给定的时间的帧不一定存在,但是系统会将临近的一帧返回给我们。
option 参数表示获取帧的方式
以下是我调用的代码块,代码块是放在单独的线程中执行的。
try {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(context, videoUri);
// Retrieve media data use microsecond
long videoLengthInMs = Long.parseLong(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)) * 1000;
long numThumbs = videoLengthInMs < one_frame_time ? 1 : (videoLengthInMs / one_frame_time);
final long interval = videoLengthInMs / numThumbs;
//每次截取到3帧之后上报
for (long i = 0; i < numThumbs; ++i) {
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(i * interval, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
try {
bitmap = Bitmap.createScaledBitmap(bitmap, thumb_Width, thumb_Height, false);
} catch (Exception e) {
e.printStackTrace();
}
thumbnailList.add(bitmap);
if (thumbnailList.size() == 3) {
callback.onSingleCallback((ArrayList) thumbnailList.clone(), (int) interval);
thumbnailList.clear();
}
}
if (thumbnailList.size() > 0) {
callback.onSingleCallback((ArrayList) thumbnailList.clone(), (int) interval);
thumbnailList.clear();
}
mediaMetadataRetriever.release();
} catch (final Throwable e) {
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
子线程会在每截取到三帧之后将数据上报给主线程,主线程拿到数据之后会进行ui的更新操作,将bitmap显示出来。
当视频源时间比较长的时候,我们会截取到很多帧图片,所以需要将帧图片可以左右滑动。项目中用到的控件是:VideoThumbHorizontalListView (可水平滚动的ListView)
videoThumbListView.setOnScrollStateChangedListener(new VideoThumbHorizontalListView.OnScrollStateChangedListener({
@Override
public void onScrollStateChanged(ScrollState scrollState, int scrollDirection) {
if (videoThumbListView.getCurrentX() == 0)
return;
switch (scrollState) {
case SCROLL_STATE_FLING:
case SCROLL_STATE_IDLE:
case SCROLL_STATE_TOUCH_SCROLL:
if (scrolledOffset < 0) {
mScrolledOffset = mScrolledOffset - Math.abs(scrolledOffset);
if (mScrolledOffset <= 0)
mScrolledOffset = 0;
} else {
if(PixToTime(mScrolledOffset + SCREEN_WIDTH) <= mDuration)//根据时间来判断还是否可以向左滚动
mScrolledOffset = mScrolledOffset + scrolledOffset;
}
onVideoReset();
onSeekThumbs(0, mScrolledOffset + leftThumbValue);
onSeekThumbs(1, mScrolledOffset + rightThumbValue);
mRangeSeekBarView.invalidate();
break;
}
} );
我会去监听水平ListView的左右滚动事件,以获取它的滚动距离,从而算出并显示当前截取到的视频时间的长度。这里我们需要将视频总长度与屏幕的宽度做一个计算,形成一个映射关系,这是一个比较麻烦的事情,处理不好,会导致在ListView滚动过程中时间显示不准确的问题,哈哈,我的项目就存在着这样的bug。
这个项目只是一个粗糙的项目,页面上会存在一些小问题,不过可以拿来简单的学习一下。
欢迎有兴趣的同学star和fork。
Github:https://github.com/iknow4/Android-Video-Trimmer
Thanks for reading. To help others please click ❤ to recommend this article if you found it helpful.
android mp4 画面裁剪,说说Android的视频裁剪(二)相关推荐
- android mp4 画面裁剪,Android视频时长裁剪
大家好,我是程序员kenney,今天给大家介绍一下如何在Android里面实现视频时长的裁剪. 首先我们要知道视频是由一帧一帧的数据构成的,每一帧都有一个时间戳,这个时间戳就是我们在做视频编码的时候, ...
- php封装多段mp4,解决ffmpeg将多段视频裁剪拼接后卡顿现象
合并 将下载的ts流名称按照顺序记录在一个txt文件中,然后使用命令 ffmpeg.exe -f concat -i ./your.txt -c copy ./output.mkv ffmpeg.ex ...
- android启动画面白屏,Android app启动时黑屏或者白屏的原因及解决办法
1.产生原因 其实显示黑屏或者白屏实属正常,这是因为还没加载到布局文件,就已经显示了window窗口背景,黑屏白屏就是window窗口背景. 示例: 2.解决办法 通过设置设置Style (1)设置背 ...
- android+系统画面恢复,坚持Android系统恢复?轻松修复它
第3部分.安卓系统恢复?如何一键修复? 有时在系统恢复过程中,该过程可能会出现故障,您将丢失设备上的数据,使其无法使用.但是,解决此问题的另一个解决方案是使用dr.fone修复工具修复您的设备. 修复 ...
- 短视频生产利器!视频裁剪之横屏转竖屏新技术,出自腾讯多媒体实验室
腾讯多媒体技术专栏 伴随手机等智能设备的广泛使用以及短视频平台的兴起,越来越多的"竖屏"视频开始占据人们的视野.目前,许多"竖屏"视频仍是由16:9等宽高比的& ...
- Android 3分钟一个库搞定视频替换音频 视频合成 视频裁剪(高仿剪映)
几种框架的比较: https://www.zhihu.com/question/278431860 方法一(Fail) 利用MediaMux实现音视频的合成. 效果:可以实现音视频的合并,利用Andr ...
- Android 使用 mp4parser 做视频裁剪
做音视频时我们很多时候需要做音视频裁剪,本文介绍使用开源库 mp4parser 做裁剪. 视频合并请见我的另外一篇博客<Android 使用 mp4parser 做视频拼接合并> 使用时先 ...
- android 视频录制尺寸裁剪,galleryfinal 实现Android图片单选/多选、拍照、裁剪、压缩。视频选择和录制。...
RxGalleryFinal是一个android图片/视频文件选择器.其支持多选.单选.拍摄和裁剪,主题可自定义,无强制绑定第三方图片加载器. 1.首先加入权限 2.在module gradle中项目 ...
- android可以剪辑代码的控件,Android 仿抖音视频裁剪范围选择控件,支持本地视频和网络视频...
实现后效果:由于是在模拟器上跑的背面的封面列表加载不出来,实际效果请真机运行 image.png 具体代码如下: 绘制上层滑动控件部分 package com.cj.customwidget.widg ...
- Android 基于MediaCodec开发抖音短视频录制(壹)
前言 当一个Android开发者玩抖音玩疯了之后,就会绞尽脑汁思考自己是否也能开发出一款相同的APP来呢? 滴,滴滴! 本篇文章将介绍自己总结的短视频录制的相关内容,主要分为三个部分: 摄像头内容录制 ...
最新文章
- 关于RotatedRect与getRotationMatrix2D 的角度问题
- IdentityServer4 配置负载均衡
- 设计模式原则之依赖倒置原则
- python3中numpy函数tile的用法
- while(1); 作用
- 牛客练习赛19 E和F(签到就走系列)托米的饮料+托米搭积木
- 1251 括号(递归小练)
- java class load 类加载
- kafka 日志相关配置
- 带有返回值的装饰器_如何使用带有工厂功能的装饰器
- 微课|中学生可以这样学Python(例3.2):今天是今年的第几天
- OC继承以及实例变量修饰符
- Spring-context-ApplicationContext/AbstractApplicationContext
- Selenium 调用IEDriverServer打开IE浏览器
- 关于引用lightbox源码
- java动态编译无法导包_java动态编译整个项目,解决jar包找不到问题.doc
- c++中 . 和 - 的区别
- 【修真院WEB小课堂】 angular js中的依赖注入是什么?
- 安利几个翻译照片的好用软件
- 什么?你还没妹子?教你如何借助Python俘获女孩子芳心
热门文章
- oracle 同义词public,oracle中private同义词跟public同义词
- 下载软件时的X86和X64的区别
- SQL Server各版本
- 头部姿态估计:《Fine-Grained Head Pose Estimation Without Keypoints》
- 辽宁等保测评机构项目测评收费价格标准参考
- 《数据结构》实验二 线性表的实验
- 在火狐浏览器打开xpath_元素定位工具:火狐浏览器Try Xpath插件
- 正则表达式小Tips
- 为什么我怎么也理解不了波粒二象性,是因为智商不够吗?
- android root工具排行榜,可root任何机?史上最强安卓root工具出炉