我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解Android开发之MP4文件转GIF文件

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解Android开发之MP4文件转GIF文件

一 基本实现原理

在介绍具体实现过程之前,先简单说下基本原理和实现步骤,在解决相对比较复杂的问题,我习惯先理清主要原理步骤,不要一开始就被繁琐细节绊住,待具体实现时再逐个攻破。下面是主要步骤:

     1、视频文件的读取:包括录制和本地文件读取 

     2、将需要转换的视频部分解析为 Bitmap 序列

     3、将解析好的 Bitmap 序列编码生成 GIF 文件

二 视频文件的读取

视频文件的读取比较简单,没什么特别需要说的地方,这里简单贴出视频读取的核心部分代码,详细实现可以Google一下就行了。


private View.OnClickListener clickListener = new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 Intent intent = new Intent();
 intent.setType("video/*");
 intent.setAction(Intent.ACTION_GET_CONTENT);
 startActivityForResult(Intent.createChooser(intent, "Select Video"), SELECT_VIDEO);
 }
};
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 if (requestCode == REQUEST_SELECT_VIDEO) {
 if (resultCode == RESULT_OK) {
  Uri videoUri = data.getData();
  filePath = getRealFilePath(videoUri);
 }
 }
}

三 视频文件的解析

视频文件读取成功后,接下来要做的就是解析视频文件,选取需要转换的视频片段,提取Bitmap序列。下面来看下具体实现,提取 Bitmap 序列就是根据给定的起始时间和结束时间以及帧率从视频文件中获取相应的 Bitmap,本文主要是利用

MediaMetadataRetriever 
提供的 API 来实现的,在看代码前可以先看下
MediaMetadataRetriever 
的 API 文档,该类的核心功能就是获取视频的帧和元数据,下面是核心实现代码:


public List<Bitmap> createBitmaps(String path) {
 MediaMetadataRetriever mmr = new MediaMetadataRetriever();
 mmr.setDataSource(path);
 double inc = 1000 * 1000 / fps;
 for (double i = begin; i < end; i += inc) {
 Bitmap frame = mmr.getFrameAtTime((long) i, MediaMetadataRetriever.OPTION_CLOSEST);
 if (frame != null) {
  bitmaps.add(scale(frame));
 }
 }
 return bitmaps;
}
private Bitmap scale(Bitmap bitmap) {
 return Bitmap.createScaledBitmap(bitmap,
 width > 0 ? width : bitmap.getWidth(),
 height > 0 ? height : bitmap.getHeight(),
 true);
}

四 生成 GIF 文件

拿到要生成 GIF 的 Bitmap 序列,接下来需要做的就是将 Bitmap 序列中的数据按照 GIF 的文件格式编码,生成最终的 GIF 文件。目标很明确,接下来就看具体实现过程了。

1. GIF 格式简介

生成 GIF 文件之前有必要介绍下 GIF 的存储格式,GIF 格式的相关文章比较多,这里也没必要太详细的介绍,只是简单说下后面程序中会用到的方面。

GIF 图象是基于颜色列表的(存储的数据是该点的颜色对应于颜色列表的索引值),最多只支持 8 位(256 色)。GIF 文件内部分成许多存储块,用来存储多幅图象或者是决定图象表现行为的控制块,用以实现动画和交互式应用。GIF 文件还通过 LZW 压缩算法压缩图象数据来减少图象尺寸。

GIF 文件内部是按块划分的,包括控制块和数据块两种。控制块是控制数据块行为的,根据不同的控制块包含一些不同的控制参数;数据块只包含一些 8-bit 的字符流,由它前面的控制块来决定它的功能,每个数据块 0 到 255 个字节,数据块的第一个字节指出这个数据块大小(字节数),计算数据块的大小时不包括这个字节,所以一个空的数据块有一个字节,那就是数据块的大小0x00。

2. GIF 文件写入

刚开始接触 GIF 文件会觉得比较复杂,存储格式、编码格式等都比 Bitmap 要复杂的多,但其实可以把问题简单化理解,生成 GIF 和生成 Bitmap 原理类似,就是按照规定的格式写文件就行了,不用太纠结内部细节,否则就会陷入繁琐的细节(俗称钻牛角尖)而忽略了最终目的只是为了生成 GIF 文件。下面就来看下有哪些文件部分需要写入的:

提取 Bitmap 的像素值

首先需要将上面得到的 Bitmap 的像素值提取出来,方便后面把像素值写入到 GIF 文件中,在提取像素值的同时,生成 GIF 文件所需要的颜色表,生成颜色表过程比较复杂,这里就不贴出源码,感兴趣的可以Google一下颜色量化算法,不感兴趣的直接用现成的就好,下面是提取像素值的具体实现:


protected void getImagePixels() {
 int w = image.getWidth();
 int h = image.getHeight();
 pixels = new byte[w*h*3];
 for (int i = 0; i < h; i++) {
 int stride = w * 3 * i;
 for (int j = 0; j < w; j++) {
  int p = image.getPixel(j, i);
  int step = j * 3;
  int offset = stride + step;
  // blue
  pixels[offset+0] = (byte) ((p & 0x0000FF) >> 0);
  // green
  pixels[offset+1] = (byte) ((p & 0x00FF00) >> 8);
  // red
  pixels[offset+2] = (byte) ((p & 0xFF0000) >> 16); 
 }
 }
}

GIF 文件头(Header)

文件头部分总共 6 个字节,包括:GIF 署名和版本号,GIF 署名由 3 个字符"GIF"组成,共 3 个字节,版本号也是由 3 个字节组成,可以为"87a"或"89a"(分别为 1987 年和 1989 年版本),实现代码如下:


// 写入文件头
protected void writeHeader() throws IOException {
 writeString("GIF89a");
}
protected void writeString(String s) throws IOException {
 for (int i = 0; i < s.length(); i++) {
 out.write((byte) s.charAt(i));
 }
}

逻辑屏幕标识符(Logical Screen Descriptor)

文件头的后面是逻辑屏幕标识符(Logical Screen Descriptor),这一部分由 7 个字节组成,定义了 GIF 图象的大小、颜色深度、背景色以及有无全局颜色列表和颜色列表的索引数。实现代码如下:


// 写入逻辑屏幕标识符
protected void writeLSD() throws IOException {
 writeShort(width); // 写入图像宽度
 writeShort(height); // 写入图像高度
 out.write((0x80 | // 全局颜色列表标志置 1
    0x70 | // 确定图象的颜色深度(7+1=8)
    0x00 | // 全局颜色列表分类排列置为 0
    0x07)); // 颜色列表的索引数(2的7+1次方)
 out.write(0); // 背景颜色(在全局颜色列表中的索引)
 out.write(0); // 像素宽高比默认 1:1
}
protected void writeShort(int value) throws IOException {
 out.write(value & 0xff);
 out.write((value >> 8) & 0xff);
}

逻辑屏幕标识符部分结构稍微复杂些,如果不知道每一位代表什么意思可以参考:GIF图形文件格式文档 中的逻辑屏幕标识符部分。

全局颜色列表(Global Color Table)

全局颜色列表必须紧跟在逻辑屏幕标识符后面,每个颜色列表索引条目由三个字节组成,按R、G、B的顺序排列,具体生成颜色表的实现可以看源码部分,由于生成过程比较复杂,这里就不贴颜色表生成的代码了,下面是写入颜色表的代码:


// 写入颜色表
protected void writePalette() throws IOException {
 out.write(colorTab, 0, colorTab.length);
 int n = (3 * 256) - colorTab.length;
 for (int i = 0; i < n; i++) {
 out.write(0);
 }
}

图形控制扩展(Graphic Control Extension)

这一部分是可选的,89a 版本才支持,可以放在一个图象块(包括图象标识符、局部颜色列表和图象数据)或文本扩展块的前面,用来控制跟在它后面的第一个图象(或文本)的渲染( Render )形式,下面实现代码:


protected void writeGraphicCtrlExt() throws IOException {
 out.write(0x21); // 扩展块标识,固定值 0x21
 out.write(0xf9); // 图形控制扩展标签,固定值 0xf9
 out.write(4); // 块大小,固定值 4
 out.write(0 | // 1:3 保留位
   0 | // 4:6 不使用处置方法
   0 | // 7 用户输入标志置 0
   0); // 8 透明色标志置 0
 writeShort(delay); // 延迟时间
 out.write(0);  // 透明色索引值
 out.write(0);  // 块终结器,固定值 0
}

图象标识符(Image Descriptor)

一个 GIF 文件内可以包含多幅图象,一幅图象结束之后紧接着下是一幅图象的标识符,图象标识符以 0x2C(',')字符开始,定义紧接着它的图象的性质,包括图象相对于逻辑屏幕边界的偏移量、图象大小以及有无局部颜色列表和颜色列表大小,由10个字节组成,下面是实现代码:


protected void writeImageDesc() throws IOException {
 out.write(0x2c); // 图象标识符开始,固定值为 0x2c
 writeShort(0);  // x 方向偏移
 writeShort(0);  // y 方向偏移
 writeShort(width); // 图像宽度
 writeShort(height); // 图像高度
 out.write((
  0x80 |  // 局部颜色列表标志置 1
  0x00 |
  0x00 |
  0x07));  // 局部颜色列表的索引数(2的7+1次方)
}

图象数据(Image Data)

GIF 图象数据使用了 LZW 压缩算法,大大减小了图象数据的大小,具体的 LZW 压缩算法可以Google一下,程序实现部分可以参考文章底部的源码链接。下面是图像数据的写入实现:


protected void writePixels() throws IOException {
 LZWEncoder encoder = new LZWEncoder(
  width, height, indexedPixels, colorDepth);
 encoder.encode(out);
}

文件终结器(Trailer)

这一部分只有一个字节,标识一个GIF文件结束,固定值为 0x3B,实现代码:


public void finish() throws IOException {
 out.write(0x3b);
 out.flush();
 out.close();
}

总结

到目前为止,将 MP4 文件转换为 GIF 文件的实现过程基本完成,如果需要对 GIF 文件进行裁剪、添加水印等处理的话,可以在 Bitmap 序列写入 GIF 之前,对 Bitmap 进行相应的处理即可,如果有什么问题欢迎交流学习。希望本文的内容对大家的学习工作能有所帮助。

您可能感兴趣的文章:Android音频录制MediaRecorder之简易的录音软件实现代码Android简单的利用MediaRecorder进行录音的实例代码Android应用开发:电话监听和录音代码示例Android App调用MediaRecorder实现录音功能的实例Android开发四大组件之实现电话拦截和电话录音Android使用MediaRecorder实现录音及播放Android录音应用实例教程一个html5播放视频的video控件只支持android的默认格式mp4和3gpAndroid 使用VideoView播放MP4的简单实现Android录音并且输出为Mp4文件的方法教程


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解Android开发之MP4文件转GIF文件

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

详解Android开发之MP4文件转GIF文件

一 基本实现原理在介绍具体实现过程之前,先简单说下基本原理和实现步骤,在解决相对比较复杂的问题,我习惯先理清主要原理步骤,不要一开始就被繁琐细节绊住,待具体实现时再逐个攻破。下面是主要步骤: 1、视频文件的读取:包括录制和本地文件
2022-06-06

Android开发之文件操作详解

本文实例讲述了Android开发之文件操作。分享给大家供大家参考,具体如下:目前,几乎所有的设备都会涉及到文件的操作,例如什么电脑,手机等设备。Android的文件操作和电脑是比较类似的,既可以存储在手机内置的存储器里也可以是sd卡。在这篇
2023-05-30

FlutterWidget开发之Focus组件图文详解

这篇文章主要为大家介绍了FlutterWidget开发之Focus组件图文详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-08

Android开发之XML文件解析的使用

前言 本文主要介绍在Android中怎样来解析XML文件。主要采用的是SAX机制,SAX全称为Simple API for XML,它既是一种接口,也是一个软件包。作为接口,SAX是事件驱动型XML解析的一个标准接口。XML文件解析一般有2
2022-06-06

Android开发之DOM解析xml文件的方法

本文实例讲述了Android中DOM解析xml文件的方法。分享给大家供大家参考,具体如下: 一、在assets文件中写xml文件
2022-06-06

Android开发之文件操作模式深入理解

一、基本概念 代码如下: // 上下文对象 private Context context; public FileService(Context context) { super(); this.context = context; }
2022-06-06

详解Android开发数据持久化之文件存储(附源码)

其实我们在社交网络上面所发出的任何信息, 都希望能够保留下来. 那么如何实现呢 数据持久化 数据持久化, 就是将内存中的瞬时数据保存在存储设备中, 保证即便关机之后, 数据仍然存在. 保存在内存中的数据是瞬时数据, 保存在存储设备中的数据就
2022-06-06

Android开发中libs和jinLibs文件夹的作用详解

前言相信各位Android开发中们在Android 开发中经常和这两个文件夹打交道,以前一直迷迷糊糊的使用,没去想过。最近遇到了一些问题,仔细研究了一下,特此记录分享。下面话不多说了,来一起看看详细的介绍吧。libs: librarys用来
2023-05-30

Android开发-之环境的搭建(图文详解)

关于Android开发可以使用的工具有eclipse和Android studio等,这两个工具都各有各的好处和不足。studio是谷歌推出的一款开发工具,而我们都知道Android就是谷歌公司的,所以相对于eclipse来说会好一点推荐开
2022-06-06

Android 中Manifest.xml文件详解

Android 中Manifest.xml文件详解 每一个Android项目都包含一个清单(Manifest)文件--AndroidManifest.xml,它存储在项目层次中的最底层。清单可以定义应用程序及其组件的结构和元数据。它包含了组
2022-06-06

详解Android文件存储

摘要 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理。根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存
2022-06-06

Android学习笔记之AndroidManifest.xml文件解析(详解)

一、关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件。它位于整个项目的根目录,描述了package中暴露的组件(activities, services, 等等),他们
2022-06-06

Android开发之自定义控件用法详解

本文实例讲述了Android开发之自定义控件用法。分享给大家供大家参考,具体如下: 今天和大家分享下组合控件的使用。很多时候android自定义控件并不能满足需求,如何做呢?很多方法,可以自己绘制一个,可以通过继承基础控件来重写某些环节,当
2022-06-06

Android开发之TimePicker控件用法实例详解

本文实例分析了Android开发之TimePicker控件用法。分享给大家供大家参考,具体如下: 新建项目: New Android Project-> Project name:HelloSpinner Build Target:Andr
2022-06-06

Android开发之资源文件用法实例总结

本文实例总结了Android开发之资源文件用法。分享给大家供大家参考,具体如下: 这里记录在Android开发中经常用到的一些用法 arrays.xml定义数组 例:
2022-06-06

Android开发基础实现音频文件的播放详解

这篇文章主要为大家介绍了Android开发基础实现音频文件的播放详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录