MediaMetadataRetriever是Android原生提供的获取音视频文件信息的一个类,我们可以通过这个类的相关方法获取一些基本信息,如视频时长、宽高、帧率、方向、某一帧的等。
我们可以通过MediaMetadataRetriever的extractMetadata(int keyCode)的方法获取一些视频的基本信息,以下列出一些常用的:
可以通过getFrameAtTime(long timeUs)获取某一时刻附近的帧;API>=28,可以准确获取帧,相关方法getFrameAtIndex();获取帧图可以应用在 视频封面图 、 视频裁剪的缩略图 等。
MediaMetadataRetriever还是比较常用的,以上只是简略的列出了MediaMetadataRetriever的一些用法,具体可自行查阅源文件。
Android 用MediaCodec实现视频硬解码
本文向你讲述如何用android标准的API (MediaCodec)实现视频的硬件编解码。例程将从摄像头采集视频开始,然后进行H264编码,再解码,然后显示。我将尽量讲得简短而清晰,不展示那些不相关的代码。但是,我不建议你读这篇文章,也不建议你开发这类应用,而应该转而开发一些戳鱼、打鸟、其乐融融的程序。好吧,下面的内容是写给那些执迷不悟的人的,看完之后也许你会同意我的说法:Android只是一个玩具,很难指望它来做靠谱的应用。
1、从摄像头采集视频
可以通过摄像头Preview的回调,来获取视频数据。
首先创建摄像头,并设置参数:
[java] view plaincopy
cam = Cameraopen();
camsetPreviewDisplay(holder);
CameraParameters parameters = camgetParameters();
parameterssetFlashMode("off"); // 无闪光灯
parameterssetWhiteBalance(CameraParametersWHITE_BALANCE_AUTO);
parameterssetSceneMode(CameraParametersSCENE_MODE_AUTO);
parameterssetFocusMode(CameraParametersFOCUS_MODE_AUTO);
parameterssetPreviewFormat(ImageFormatYV12);
parameterssetPictureSize(camWidth, camHeight);
parameterssetPreviewSize(camWidth, camHeight);
//这两个属性 如果这两个属性设置的和真实手机的不一样时,就会报错
camsetParameters(parameters);
宽度和高度必须是摄像头支持的尺寸,否则会报错。要获得所有支持的尺寸,可用getSupportedPreviewSizes,这里不再累述。据说所有的参数必须设全,漏掉一个就可能报错,不过只是据说,我只设了几个属性也没出错。 然后就开始Preview了:
[java] view plaincopy
buf = new byte[camWidth camHeight 3 / 2];
camaddCallbackBuffer(buf);
camsetPreviewCallbackWithBuffer(this);
camstartPreview();
setPreviewCallbackWithBuffer是很有必要的,不然每次回调系统都重新分配缓冲区,效率会很低。
在onPreviewFrame中就可以获得原始的了(当然,this 肯定要 implements PreviewCallback了)。这里我们是把它传给编码器:
[java] view plaincopy
public void onPreviewFrame(byte[] data, Camera camera) {
if (frameListener != null) {
frameListeneronFrame(data, 0, datalength, 0);
}
camaddCallbackBuffer(buf);
}
2、编码
首先要初始化编码器:
[java] view plaincopy
mediaCodec = MediaCodeccreateEncoderByType("Video/AVC");
MediaFormat mediaFormat = MediaFormatcreateVideoFormat(type, width, height);
mediaFormatsetInteger(MediaFormatKEY_BIT_RATE, 125000);
mediaFormatsetInteger(MediaFormatKEY_FRAME_RATE, 15);
mediaFormatsetInteger(MediaFormatKEY_COLOR_FORMAT, MediaCodecInfoCodecCapabilitiesCOLOR_FormatYUV420Planar);
mediaFormatsetInteger(MediaFormatKEY_I_FRAME_INTERVAL, 5);
mediaCodecconfigure(mediaFormat, null, null, MediaCodecCONFIGURE_FLAG_ENCODE);
mediaCodecstart();
然后就是给他喂数据了,这里的数据是来自摄像头的:
[java] view plaincopy
public void onFrame(byte[] buf, int offset, int length, int flag) {
ByteBuffer[] inputBuffers = mediaCodecgetInputBuffers();
ByteBuffer[] outputBuffers = mediaCodecgetOutputBuffers();
int inputBufferIndex = mediaCodecdequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBufferclear();
inputBufferput(buf, offset, length);
mediaCodecqueueInputBuffer(inputBufferIndex, 0, length, 0, 0);
}
MediaCodecBufferInfo bufferInfo = new MediaCodecBufferInfo();
int outputBufferIndex = mediaCodecdequeueOutputBuffer(bufferInfo,0);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
if (frameListener != null)
frameListeneronFrame(outputBuffer, 0, length, flag);
mediaCodecreleaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodecdequeueOutputBuffer(bufferInfo, 0);
}
先把来自摄像头的数据喂给它,然后从它里面取压缩好的数据喂给解码器。
3、解码和显示
首先初始化解码器:
[java] view plaincopy
mediaCodec = MediaCodeccreateDecoderByType("Video/AVC");
MediaFormat mediaFormat = MediaFormatcreateVideoFormat(mime, width, height);
mediaCodecconfigure(mediaFormat, surface, null, 0);
mediaCodecstart();
这里通过给解码器一个surface,解码器就能直接显示画面。
然后就是处理数据了:
[java] view plaincopy
public void onFrame(byte[] buf, int offset, int length, int flag) {
ByteBuffer[] inputBuffers = mediaCodecgetInputBuffers();
int inputBufferIndex = mediaCodecdequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBufferclear();
inputBufferput(buf, offset, length);
mediaCodecqueueInputBuffer(inputBufferIndex, 0, length, mCount 1000000 / FRAME_RATE, 0);
mCount++;
}
MediaCodecBufferInfo bufferInfo = new MediaCodecBufferInfo();
int outputBufferIndex = mediaCodecdequeueOutputBuffer(bufferInfo,0);
while (outputBufferIndex >= 0) {
mediaCodecreleaseOutputBuffer(outputBufferIndex, true);
outputBufferIndex = mediaCodecdequeueOutputBuffer(bufferInfo, 0);
}
}
queueInputBuffer第三个参数是时间戳,其实怎么写都无所谓,只要是按时间线性增加的就可以,这里就随便弄一个了。后面一段的代码就是把缓冲区给释放掉,因为我们直接让解码器显示,就不需要解码出来的数据了,但是必须要这么释放一下,否则解码器始终给你留着,内存就该不够用了。
好了,到现在,基本上就可以了。如果你运气够好,现在就能看到视频了,比如在我的三星手机上这样就可以了。但是,我试过几个其他平台,多数都不可以,总是有各种各样的问题,如果要开发一个不依赖平台的应用,还有很多的问题要解决。说说我遇到的一些情况:
1、视频尺寸
一般都能支持176X144/352X288这种尺寸,但是大一些的,640X480就有很多机子不行了,至于为什么,我也不知道。当然,这个尺寸必须和摄像头预览的尺寸一致,预览的尺寸可以枚举一下。
2、颜色空间
根据ANdroid SDK文档,确保所有硬件平台都支持的颜色,在摄像头预览输出是YUV12,在编码器输入是COLOR_FormatYUV420Planar,也就是前面代码中设置的那样。 不过,文档终究是文档,否则安卓就不是安卓。
在有的平台上,这两个颜色格式是一样的,摄像头的输出可以直接作为编码器的输入。也有的平台,两个是不一样的,前者就是YUV12,后者等于I420,需要把前者的UV分量颠倒一下。下面的代码效率不高,可供参考。
[java] view plaincopy
byte[] i420bytes = null;
private byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {
if (i420bytes == null)
i420bytes = new byte[yv12byteslength];
for (int i = 0; i < widthheight; i++)
i420bytes[i] = yv12bytes[i];
for (int i = widthheight; i < widthheight + (width/2height/2); i++)
i420bytes[i] = yv12bytes[i + (width/2height/2)];
for (int i = widthheight + (width/2height/2); i < widthheight + 2(width/2height/2); i++)
i420bytes[i] = yv12bytes[i - (width/2height/2)];
return i420bytes;
}
这里的困难是,我不知道怎样去判断是否需要这个转换。据说,Android 43不用再从摄像头的PreView里面取图像,避开了这个问题。这里有个例子,虽然我没读,但看起来挺厉害的样子,应该不会有错吧(觉厉应然)。>
视频号的短视频是无法跟某音、某手一样直接下载的,在视频号后台,完全没有相关的设置选项,默认视频号视频就不支持直接下载,所以我们需要利用微信内置视频播放的缓存文件来保存视频号的视频,该方法同样适用于支持微信内置播放器或者内置浏览器进行播放的视频的下载。
首先,我找了一个我之前视频号发的视频作为例子,
我们直接点击该视频,在手机上进行播放,请务必注意,让他自己播放完,这一步非常重要,播放的时候可以静音后,放旁边自己播放完即可,
等待视频播放完后,我们打开手机的文件管理(我的手机用的MIUI),
然后按照文件夹名称依次打开“内部存储设备AndroiddatacomtencentmmMicroMsg”文件夹,
再打开“data”文件夹时回提示安卓框架访问的提示,不用理会,点击前往查看,(此提示是在2021年国家对涉及隐私的文件读取监管趋严导致的,之前是可以直接打开的)。
在“MicroMsg”文件夹下,我们会看到有个名字很长的文件夹,这就是我们的微信账号,我的微信登录了两个账号,所以会有两个名字非常长的文件夹,默认我们没有其他 *** 作的话,我们刚刚打开视频的微信账号会在第一个,我们点击进去,
我们打开“MicroMsg”文件夹下的“finder>>video”文件夹,在“video”文件夹中就存放着我们刚刚看的视频号视频的缓存,默认第一个就是我们刚刚打开的视频号视频。
我们选中该文件,进行重命名,在文件名称的最后加上“mp4”即可,
然后我们可以在手机上播放此视频。
我们也可以继续使用微信将此视频再发到电脑上,需要注意的是,我尝试过直接拷贝重命名后的视频到电脑上,是无法用某影音直接播放的,需要借助转码工具进行转码才可以,可能某影音没有该视频的解码器,其它播放器我没有尝试过。
以上就是关于【Android音视频】MediaMetadataRetriever使用全部的内容,包括:【Android音视频】MediaMetadataRetriever使用、Android MediaCodec surface模式下如何读取原始视频数据、视频号 | 以安卓手机为例介绍如何下载视频号视频等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)