Android视频压缩
一.业务场景
Android设备使用相机进行视频录像后,生成视频文件;视频文件上传云端服务后,进行业务方面的处理;
如何减少视频文件大小呢?
二.解决方案
考虑到安装包大小,视频压缩速度,采用了VideoProcessor进行压缩。
视频压缩方式
- fps压缩:fps大于25的,控制在20
- 720P压缩:720P以上的视频进行720P压缩
- H264转码H265(HEIF): 在相机视频取景时,配置支持HEIF格式或针对所有视频文件转码为H265编码
三.fps压缩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
void fpsCompressVidio(File file) {
String sourcePath = file.getPath();
String fileName = file.getName();
long fileSize = file.length()/1000;
String resFilePath = fpsFilePath + fileName;
Uri uri = Uri.parse(sourcePath);
long duration = 0;
int width = 0;
int height = 0;
float aveFrameRate = 0.0f;
int frameRate = 0;
try {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(this,uri);
int bitrate = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE));
retriever.release();
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(this,uri,null);
MediaFormat format = extractor.getTrackFormat(VideoUtil.selectTrack(extractor, false));
frameRate = format.containsKey(MediaFormat.KEY_FRAME_RATE) ? format.getInteger(MediaFormat.KEY_FRAME_RATE) : -1;
aveFrameRate = VideoUtil.getAveFrameRate(new VideoProcessor.MediaSource(this,uri));
width = format.getInteger(MediaFormat.KEY_WIDTH);
height = format.getInteger(MediaFormat.KEY_HEIGHT);
int rotation = format.containsKey(MediaFormat.KEY_ROTATION) ? format.getInteger(MediaFormat.KEY_ROTATION) : -1;
duration = format.containsKey(MediaFormat.KEY_DURATION) ? format.getLong(MediaFormat.KEY_DURATION) : -1;
String KEY_MIME= format.containsKey(MediaFormat.KEY_MIME) ? format.getString(MediaFormat.KEY_MIME) : "";
String videoInfo = String.format(Locale.ENGLISH,"size:%dX%d,framerate:%d,aveFrameRate:%f,rotation:%d,bitrate:%d,duration:%.1fs", width, height, frameRate,aveFrameRate, rotation, bitrate,
duration / 1000f / 1000f);
extractor.release();
Log.e("--testVidio",videoInfo);
} catch (Exception e) {
e.printStackTrace();
}
long startTimestamp = System.currentTimeMillis();
if (frameRate > 25) {
frameRate = 20;
}
final int tempFrameRate = frameRate;
sharedExecutor.execute(new Runnable() {
@Override
public void run() {
try {
VideoProcessor.processor(getApplicationContext())
.input(sourcePath)
.output(resFilePath)
.frameRate(tempFrameRate)
.progressListener(new VideoProgressListener() {
@Override
public void onProgress(float progress) {
int intProgress = (int) (progress * 100);
Log.e("-VideoProcessor:", String.valueOf(intProgress));
uiAction(new Runnable() {
@Override
public void run() {
tv_count.setText(fileName + "压缩进度:"+ intProgress + "%" );
}
});
if (intProgress >=100) {
long curTime = System.currentTimeMillis();
long deltaTime = curTime - startTimestamp;
String res = fileName + "压缩耗时为:" + deltaTime + "ms";
Log.e("-VideoProcessor:", res);
}
}
}).process();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
|
四.720P压缩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
void compressVideo(File file) {
String sourcePath = file.getPath();
String fileName = file.getName();
String resFilePath = w_h_FilePath + fileName;
long startTimestamp = System.currentTimeMillis();
sharedExecutor.execute(new Runnable() {
@Override
public void run() {
try {
VideoProcessor.processor(getApplicationContext())
.input(sourcePath)
.output(resFilePath)
.outWidth(1280).outHeight(720)
.progressListener(new VideoProgressListener() {
@Override
public void onProgress(float progress) {
int intProgress = (int) (progress * 100);
Log.e("-VideoProcessor:", String.valueOf(intProgress));
if (intProgress >=100) {
long curTime = System.currentTimeMillis();
long deltaTime = curTime - startTimestamp;
String res = fileName + "压缩耗时为:" + deltaTime + "ms";
Log.e("-VideoProcessor:", res);
}
}
}).process();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
|
五.视频文件转码为H265
1
2
3
4
5
6
7
8
9
10
|
def start_codec_compressed(self):
if len(self.fps_list) == 0:
return
for file_path in self.fps_list:
file_name = os.path.basename(file_path)
dest_file_path = FfmpegManager.CODEC_DEST_FILE_PATH + file_name
subprocess.run(["ffmpeg", "-i", file_path, "-c:v", "libx265", "-preset", "medium", "-x265-params", "crf=28", dest_file_path], check=True)
|
使用ffmpeg进行265转码验证下压缩效果
1
2
3
|
python_fps/high 目录下所有文件大小:4367638719, 共4.0676805367693305G
python_fps/high_codec目录下所有文件大小:1686972766,共1.5711158197373152G
---所有视频文件采用H264转H265压缩方式的压缩率 = 61.38%
|
在Android设备中,在使用相机录制时,检查设备是否支持H.265编码?如果支持,对相机录制进行配置。如果不支持,使用H264编码。
1.检查设备支持
检查设备是否支持 H.265 编码器,通过检查CamcorderProfile类中的QUALITY_HEVC属性来判断,例如:
1
|
boolean isHEVCSupported = CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HEVC);
|
使用 MediaRecorder 对象进行录制时,你需要将视频编码器属性设置为 VideoEncoder.HEVC,例如:
1
|
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.HEVC);
|
3.配置CamcorderProfile类
如果你想使用 CamcorderProfile 类来设置摄像机参数,你可以将 videoCodec 属性设置为 MediaRecorder.VideoEncoder.HEVC,例如:
1
2
|
CamcorderProfile camcorderProfile = CamcorderProfile.get(cameCamcorderProfile.QUALITY_HIGH);
camcorderProfile.videoCodec = MediaRecorder.VideoEncoder.HEVC;
|
4.Android系统关于H.265支持情况
HEVC(H.265)是高级视频编码标准,目前有许多现代 Android 设备都支持该标准。以下是一些支持HEVC(H.265)标准的Android系统版本:
-
Android 5.0 及以上版本
在 Android 5.0(Lollipop)及以上版本中,Google 开始支持 HEVC 解码并播放,该功能可通过 MediaCodec API 访问。
-
Android 7.0 及以上版本
在 Android 7.0(Nougat)中,Google 添加了对 HEVC 编码的支持。该支持使 Android 设备可以使用 HEVC 标准将视频编码成更小的文件,同时保持更高画质。
-
Android 8.0 及以上版本
在 Android 8.0(Oreo)及以上版本中,Google 进一步改进了对 HEVC 的支持,并将其作为系统支持的标准编解码器之一。
需要注意的是,设备支持 HEVC 的主要因素是硬件编解码器的支持。如果设备没有内置硬件加速器来处理 HEVC 编解码,则可能无法支持 HEVC 标准。此外,有些 Android 设备可能通过软件方式支持 HEVC 解码,但可能会消耗更多的 CPU 资源或导致性能下降。因此,在使用 HEVC 执行视频编解码时,需要考虑设备硬件和软件资源,以获得最佳的性能和效果
六.结论
- fps压缩,效果不佳
- 720P压缩:效果有限
- 转码H265:如果设备支持,则相机录制视频时进行H265配置
文章作者
梵梵爸
上次更新
2023-09-05
许可协议
原创文章,如需转载请注明文章作者和出处。谢谢