androidnv21
① Android音视频开发-入门(三):使用 Camera API 采集NV21数据
做过Android开发的人一般都知道,有两种方法能够做到这一点:SufaceView、TextureView。
Android 中Google支持的Camera Preview CallBack的YUV常用格式有两种:一种是NV21,一种是YV12,Android一般默认使用的是YCbCR_420_sp(NV21)
② android 21 是什么版本
android每一个系统版本都对应一个编号的,21代表安卓5.0系统,23代表安卓6.0系统。
API等级15:Android 4.0.3 - 4.0.4 Ice Cream Sandwich
API等级16:Android 4.1 Jelly Bean
API等级17:Android 4.2 Jelly Bean
API等级18:Android 4.3 Jelly Bean
API等级19:Android 4.4 KitKat
API等级20:Android 4.4W
API等级21:Android 5.0 Lollipop
API等级22:Android 5.1 Lollipop
API等级23:Android 6.0 Marshmallow
(2)androidnv21扩展阅读:
从2009年5月开始,Android操作系统改用甜点来作为版本代号,这些版本按照从C大写字母开始的顺序来进行命名:纸杯蛋糕(Cupcake)、甜甜圈(Donut)、闪电泡芙(Éclair)、冻酸奶(Froyo)、姜饼(Gingerbread)。
蜂巢(Honeycomb)﹑冰淇淋三明治(Ice Cream Sandwich)、果冻豆(Jelly Bean)、奇巧(KitKat)、棒棒糖(Lollipop)、棉花糖(Marshmallow)、牛轧糖(Nougat)、奥利奥(Oreo )、馅饼(Pie)。
③ 视频数据存储方式YUV
在视频中的数据保存和传输都是YUV数据格式。主要是为了降低数据大小,如果argb格式数据,1px可能需要4个字节,而YUV可能就只需要1.5个字节。
简单的讲YUV是一种图像和视频的编码方式,RGB通过三种颜色来表达现实世界中的各种颜色,YUV通过 亮度 与 色度饱和度 来表示颜色。
RGB很好理解,它更直观。从学生开始就认识绘画的颜料是用三种颜色调配来的,汽车的油漆颜色也是RGB三种颜色调配而来的。
YUV的出现有它的历史意义但也是一种必然。它基于人眼对亮度的敏感度比色彩的敏感度更高的特点。Y表示亮度也可以理解在灰度值,最低的亮度就是黑色最高的亮度就是白色,中间的可呈现出灰色。
在黑白电视机向彩色电视机过渡的年代,黑白电视机只需要YUV中的一个分量Y就可以呈现出黑白画面。UV分量用在彩色电视机上即可呈现出彩色了。YUV可以带来更高的帧内压缩比,由于人眼对黑白更敏感,YUV可以弱化不敏感的信息,减少UV分量的采样。RGB24的每个像素需要3*8个字节,YUV呢?不同的YUV采样方式压缩比有所不同。
电视信息使用的是YUV而数字信息使用的是 YCrCb 命令,以下统称YUV。
YUV简介
与RGB类似,YUV也是一种颜色编码方法,主要用于视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样设计解决了彩色电视机与黑白电视的兼容问题。
YUV,分为三个分量,“Y"表示明亮度(Luminance或Luma),也就是灰度;而”U"和“V”表示是色度(Chrominance或Chroma),作用于指定像素的颜色。
UV即CbCr(C代表颜色color,b代表蓝色blue,r代表红色red)
分类
YUV格式的两大类:平面(plannr)和紧凑(packed)。
对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随是存储所有像素点的V,或者是先v后u
对于packed的YUV格式,每个像素点的Y,U,V是连续交替存储的。比如YUV420P 其中P表示紧凑,YUV420SP其中的SP表示“半紧凑”。
采样
主流的采样方式有三种,YUV4:4:4,YUV4:2:2 ,YVU4:2:0
YUV4:4:4采样,每一个Y对应一组UV分量,一个YUV占8+8+8=24bits 3个字节。
YUV4:2:2采样,每两个Y共用一组UV分量,一个YUV占8+4+4=16bits 2个字节。
YUV4:2:0采样,每四个Y共用一组UV分量,一个YUV占8+2+2 = 12bits 1.5个字节。
最觉的YUV420P和YUV420SP都是基于4:2:0采样的,所以如果图片的宽为width,高为height,在内存中占的空间width*height*3/2,其中前width*height的空间存放Y分量,接着width*height/4存放U分量,最后width*height/4存放V分量。
YUV格式
常见的YUV格式有YUY2、YUYV、YVYU、UYUV、AYUV、Y41P、Y411、Y211、Y211、IF09、IYUV、YV12、YVU9、YUV411、YUV420等,Android中比较常见是YUV420分两种:YUV420P和YUV420SP。以下为YUV420P和YUV420SP。
YUV420P
YUV420P是平面模式,Y、U、V分别在不同平面,也就是有三个平面。它是YUV标准格式4:2:0,为了更方便的看如下表示 :
为了说明存储方式,每一组用不同颜色表示。每一种颜色都是一组,每四个Y共用一组UV分量。
比如:
Y1、Y2、Y7、Y8共用U1,V1;
Y3、Y4、Y9、Y10共用U2,V2;
Y5、Y6、Y11、Y12共用U3,V3;
Y13,Y14,Y19,Y20共用U4,V4
Y17,Y18,Y23,Y24共用U6,V6
那么真实的在字节流中就是按照行从左到右一行一行的拼起来的:
YUV420分为:YU12和YV12
YUV格式
在Android中也叫作I420格式,首先是所有Y值 ,然后是所有U值,最后是所有V值。比如6*6的图片,内存大小就是6*6*3/2 = 54个字节。为了更清晰的查看,我们换行看,真实的是一行byte[]数据流。
1 YYYYYY
2 YYYYYY
3 YYYYYY
4 YYYYYY
5 UUUUUU
6 VVVVVV
YV12格式
YV12格式与YU12格式,首先是所有Y值,然后是所有V值,最后是所有U值。比如6*6图片,内存大小就是6*6*3/2=54字节
1 YYYYYY
2 YYYYYY
3 YYYYYY
4 YYYYYY
5 VVVVVV
6 UUUUUU
YUV420SP
YUV420SP也是平面模式。分为NV21和NV12两种格式。Y是一个平面,UV是一个平面, UV/VU为交替存储,而不是分为三个平面。
在Android Camera中文档强烈推荐使用NV21和YU12,因为这两种格式支持所有的相机设备。
Camera默认输出YUV的数据格式为NV21。但是在Camera2中推荐使用格式则是YUV_420_888。
NV21格式
在Android Carmra中手机从摄像头采集的预览数据默认值是NV21。
NV21存储顺序是先存Y值,再VU交替存储:YYYVUVUVU,比如6*6的图片,内存大小就是6*6*3/2=54个字节。
1 YYYYYY
2 YYYYYY
3 YYYYYY
4 YYYYYY
5 VUVUVU
6 VUVUVU
NV12格式
NV12存储顺序是先存Y值,再UV交替存储:YYYUVUVUV,比如6*6的图片,内存大小就是6*6*3/2=54字节。
1 YYYYYY
2 YYYYYY
3 YYYYYY
4 YYYYYY
5 UVUVUV
6 UVUVUV
这里先熟悉下Android中常见的YUV420P和YUV420SP。一般我们在使用yuv数据的时候,会对yuv数据进行变换,比如:摄像头数据旋转,从一种格式转为另一种数据等。
④ Android Camera提取出来的yuv420源数据怎么提取y,u,v分量
Camera默认的预览格式是 NV21,4:2:0 结构的,每个U、V用于4个Y
参考下下面的代码
**
* Converts YUV420 NV21 to ARGB8888
*
* @param data byte array on YUV420 NV21 format.
* @param width pixels width
* @param height pixels height
* @retur a ARGB8888 pixels int array. Where each int is a pixels ARGB.
*/
public static int[] convertYUV420_NV21toARGB8888(byte [] data, int width, int height) {
int size = width*height;
int offset = size;
int[] pixels = new int[size];
int u, v, y1, y2, y3, y4;
// i along Y and the final pixels
// k along pixels U and V
for(int i=0, k=0; i < size; i+=2, k+=1) {
y1 = data[i ]&0xff;
y2 = data[i+1]&0xff;
y3 = data[width+i ]&0xff;
y4 = data[width+i+1]&0xff;
v = data[offset+k ]&0xff;
u = data[offset+k+1]&0xff;
v = v-128;
u = u-128;
pixels[i ] = convertYUVtoARGB(y1, u, v);
pixels[i+1] = convertYUVtoARGB(y2, u, v);
pixels[width+i ] = convertYUVtoARGB(y3, u, v);
pixels[width+i+1] = convertYUVtoARGB(y4, u, v);
if (i!=0 && (i+2)%width==0)
i+=width;
}
return pixels;
}
private static int convertYUVtoARGB(int y, int u, int v) {
int r,g,b;
r = y + (int)(1.402f*u);
g = y - (int)(0.344f*v + 0.714f*u);
b = y + (int)(1.772f*v);
r = r>255? 255 : r<0 ? 0 : r;
g = g>255? 255 : g<0 ? 0 : g;
b = b>255? 255 : b<0 ? 0 : b;
return 0xff000000 | (r<<16) | (g<<8) | b;
}
⑤ android音视频开发一安卓常用API
Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。如果想简单地做一个录音机,录制成音频文件,则推荐使用 MediaRecorder,而如果需要对音频做进一步的算法处理、或者采用第三方的编码库进行压缩、以及网络传输等应用,则建议使用 AudioRecord,其实 MediaRecorder 底层也是调用了 AudioRecord 与 Android Framework 层的 AudioFlinger 进行交互的。直播中实时采集音频自然是要用AudioRecord了。
2.1 播放声音可以用MediaPlayer和AudioTrack,两者都提供了Java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。
2.2 其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。而AudioTrack只能播放已经解码的PCM流,如果对比支持的文件格式的话则是AudioTrack只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。
2.3 MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包含了AudioTrack。
2.4 在接触Android音频播放API的时候,发现SoundPool也可以用于播放音频。下面是三者的使用场景:MediaPlayer 更加适合在后台长时间播放本地音乐文件或者在线的流式资源; SoundPool 则适合播放比较短的音频片段,比如游戏声音、按键声、铃声片段等等,它可以同时播放多个音频; 而 AudioTrack 则更接近底层,提供了非常强大的控制能力,支持低延迟播放,适合流媒体和VoIP语音电话等场景。
使用 Camera API 采集视频数据并保存到文件,分别使用 SurfaceView、TextureView 来预览 Camera 数据,取到 NV21 的数据回调。
4.1 一个音视频文件是由音频和视频组成的,我们可以通过MediaExtractor、MediaMuxer把音频或视频给单独抽取出来,抽取出来的音频和视频能单独播放;
4.2 MediaMuxer的作用是生成音频或视频文件;还可以把音频与视频混合成一个音视频文件。
文献资料 https://www.cnblogs.com/renhui/p/7452572.html
⑥ android怎么获得nv21
破解准备: 电脑端准备的的软件是: 1、Galaxy Fit 盖世S5670的三星USB电脑联接驱动程序SAMSUNG USB Driver for Mobile Phones,即(SAMSUNG_USB_Driver_for_Mobile_Phones)。可在三星官网下载Kies软件,安装完成后自动安装驱动。或在X:\Kies\USB Driver文件夹里找到,X为盘符。 2、SuperOneClick 一键ROOT工具软件。 破解步骤: 电脑端: 1、安装三星USB电脑联接驱动程序SAMSUNG USB Driver for Mobile Phones。 2、安装SuperOneClick 一键ROOT工具软件。 手机端: 1、拔掉外置的SD卡。 2、点击进设置--应用程序--开发--勾选启动USB调试,然后完全退出回到待机版面。 3、插好数据线,联上电脑。 4、启动SuperOneClick,点Root按钮,它自己会跑程序了,一系列自动操作后,会问你要否测试,点是,再问你要否传输SU文件到手机测试,点是,之后,就会显示手机Root成功了,最后会问你要否Donate捐钱,点否。 注:SuperOneClick会自动安装Superuser.apk(ROOT授权程序)到手机,如未安装Superuser.apk,则没有授权了ROOT的骷髅头标志图标;(Superuser.apk在SuperOneClick里自带的,如SuperOneClick压缩包里没有,则需找一个带Superuser.apk的SuperOneClick。也可在网上下载一个Superuser.apk。)用手机PC助手Kies软件安装或自己拷贝到手机退出联接后在手机上自行安装均可。 5、关闭SuperOneClick, 在电脑中卸载掉手机代表的U盘,就可以拔掉数据线,就真正完成整个破解ROOT权限了。到手机菜单最后一页,看看是否多了一个骷髅头标志图标,“授权管理" 或英文叫“Superuser”, 有的就代表成功破解ROOT权限了。 如果都做到了还不能够成功ROOT的话,请尝试在Recovery模式下ROOT。另外ROOT的时候显示等待没有反应,只需点击进设置--应用程序--开发--勾选启动USB调试,重新勾一下就OK了
⑦ 如何使Android的YUV-NV21相机图像实时libgdxopengl2.0的背景吗
各式各样的
⑧ android 摄像头出来的格式怎么会是yuv420p 而不是yuv420sp(nv21)呢
那是因为你在相机参数初始化的时候未设置,加上这个就可以了
List<Integer> formatsList = parameters.getSupportedPreviewFormats(); //获取设备支持的预览format
if(formatsList.contains(ImageFormat.NV21))
parameters.setPreviewFormat(ImageFormat.NV21); //设置预览格式为NV21,默认为NV21
if(formatsList.contains(ImageFormat.JPEG))
parameters.setPictureFormat(ImageFormat.JPEG); //设置照片储存格式
⑨ Android -- 音视频基础知识
帧,是视频的一个基本概念,表示一张画面,如上面的翻页动画书中的一页,就是一帧。一个视频就是由许许多多帧组成的。
帧率,即单位时间内帧的数量,单位为:帧/秒 或fps(frames per second)。一秒内包含多少张图片,图片越多,画面越顺滑,过渡越自然。 帧率的一般以下几个典型值:
24/25 fps:1秒 24/25 帧,一般的电影帧率。
30/60 fps:1秒 30/60 帧,游戏的帧率,30帧可以接受,60帧会感觉更加流畅逼真。
85 fps以上人眼基本无法察觉出来了,所以更高的帧率在视频里没有太大意义。
这里我们只讲常用到的两种色彩空间。
RGB的颜色模式应该是我们最熟悉的一种,在现在的电子设备中应用广泛。通过R G B三种基础色,可以混合出所有的颜色。
这里着重讲一下YUV,这种色彩空间并不是我们熟悉的。这是一种亮度与色度分离的色彩格式。
早期的电视都是黑白的,即只有亮度值,即Y。有了彩色电视以后,加入了UV两种色度,形成现在的YUV,也叫YCbCr。
Y:亮度,就是灰度值。除了表示亮度信号外,还含有较多的绿色通道量。
U:蓝色通道与亮度的差值。
V:红色通道与亮度的差值。
音频数据的承载方式最常用的是 脉冲编码调制 ,即 PCM 。
在自然界中,声音是连续不断的,是一种模拟信号,那怎样才能把声音保存下来呢?那就是把声音数字化,即转换为数字信号。
我们知道声音是一种波,有自己的振幅和频率,那么要保存声音,就要保存声音在各个时间点上的振幅。
而数字信号并不能连续保存所有时间点的振幅,事实上,并不需要保存连续的信号,就可以还原到人耳可接受的声音。
根据奈奎斯特采样定理:为了不失真地恢复模拟信号,采样频率应该不小于模拟信号频谱中最高频率的2倍。
根据以上分析,PCM的采集步骤分为以下步骤:
采样率,即采样的频率。
上面提到,采样率要大于原声波频率的2倍,人耳能听到的最高频率为20kHz,所以为了满足人耳的听觉要求,采样率至少为40kHz,通常为44.1kHz,更高的通常为48kHz。
采样位数,涉及到上面提到的振幅量化。波形振幅在模拟信号上也是连续的样本值,而在数字信号中,信号一般是不连续的,所以模拟信号量化以后,只能取一个近似的整数值,为了记录这些振幅值,采样器会采用一个固定的位数来记录这些振幅值,通常有8位、16位、32位。
位数越多,记录的值越准确,还原度越高。
最后就是编码了。由于数字信号是由0,1组成的,因此,需要将幅度值转换为一系列0和1进行存储,也就是编码,最后得到的数据就是数字信号:一串0和1组成的数据。
整个过程如下:
声道数,是指支持能不同发声(注意是不同声音)的音响的个数。 单声道:1个声道
双声道:2个声道
立体声道:默认为2个声道
立体声道(4声道):4个声道
码率,是指一个数据流中每秒钟能通过的信息量,单位bps(bit per second)
码率 = 采样率 * 采样位数 * 声道数
这里的编码和上面音频中提到的编码不是同个概念,而是指压缩编码。
我们知道,在计算机的世界中,一切都是0和1组成的,音频和视频数据也不例外。由于音视频的数据量庞大,如果按照裸流数据存储的话,那将需要耗费非常大的存储空间,也不利于传送。而音视频中,其实包含了大量0和1的重复数据,因此可以通过一定的算法来压缩这些0和1的数据。
特别在视频中,由于画面是逐渐过渡的,因此整个视频中,包含了大量画面/像素的重复,这正好提供了非常大的压缩空间。
因此,编码可以大大减小音视频数据的大小,让音视频更容易存储和传送。
视频编码格式有很多,比如H26x系列和MPEG系列的编码,这些编码格式都是为了适应时代发展而出现的。
其中,H26x(1/2/3/4/5)系列由ITU(International Telecommunication Union)国际电传视讯联盟主导
MPEG(1/2/3/4)系列由MPEG(Moving Picture Experts Group, ISO旗下的组织)主导。
当然,他们也有联合制定的编码标准,那就是现在主流的编码格式H264,当然还有下一代更先进的压缩编码标准H265。
H264是目前最主流的视频编码标准,所以我们后续的文章中主要以该编码格式为基准。
H264由ITU和MPEG共同定制,属于MPEG-4第十部分内容。
我们已经知道,视频是由一帧一帧画面构成的,但是在视频的数据中,并不是真正按照一帧一帧原始数据保存下来的(如果这样,压缩编码就没有意义了)。
H264会根据一段时间内,画面的变化情况,选取一帧画面作为完整编码,下一帧只记录与上一帧完整数据的差别,是一个动态压缩的过程。
在H264中,三种类型的帧数据分别为
I帧:帧内编码帧。就是一个完整帧。
P帧:前向预测编码帧。是一个非完整帧,通过参考前面的I帧或P帧生成。
B帧:双向预测内插编码帧。参考前后图像帧编码生成。B帧依赖其前最近的一个I帧或P帧及其后最近的一个P帧。
全称:Group of picture。指一组变化不大的视频帧。
GOP的第一帧成为关键帧:IDR
IDR都是I帧,可以防止一帧解码出错,导致后面所有帧解码出错的问题。当解码器在解码到IDR的时候,会将之前的参考帧清空,重新开始一个新的序列,这样,即便前面一帧解码出现重大错误,也不会蔓延到后面的数据中。
DTS全称:Decoding Time Stamp。标示读入内存中数据流在什么时候开始送入解码器中进行解码。也就是解码顺序的时间戳。
PTS全称:Presentation Time Stamp。用于标示解码后的视频帧什么时候被显示出来。
前面我们介绍了RGB和YUV两种图像色彩空间。H264采用的是YUV。
YUV存储方式分为两大类:planar 和 packed。
planar如下:
packed如下:
上面说过,由于人眼对色度敏感度低,所以可以通过省略一些色度信息,即亮度共用一些色度信息,进而节省存储空间。因此,planar又区分了以下几种格式:YUV444、 YUV422、YUV420。
YUV 4:4:4采样,每一个Y对应一组UV分量。
YUV 4:2:2采样,每两个Y共用一组UV分量。
YUV 4:2:0采样,每四个Y共用一组UV分量。
其中,最常用的就是YUV420。
YUV420属于planar存储方式,但是又分两种类型:
YUV420P:三平面存储。数据组成为YYYYYYYYUUVV(如I420)或YYYYYYYYVVUU(如YV12)。
YUV420SP:两平面存储。分为两种类型YYYYYYYYUVUV(如NV12)或YYYYYYYYVUVU(如NV21)
原始的PCM音频数据也是非常大的数据量,因此也需要对其进行压缩编码。
和视频编码一样,音频也有许多的编码格式,如:WAV、MP3、WMA、APE、FLAC等等,音乐发烧友应该对这些格式非常熟悉,特别是后两种无损压缩格式。
但是,我们今天的主角不是他们,而是另外一个叫AAC的压缩格式。
AAC是新一代的音频有损压缩技术,一种高压缩比的音频压缩算法。在MP4视频中的音频数据,大多数时候都是采用AAC压缩格式。
AAC格式主要分为两种:ADIF、ADTS。
ADIF:Audio Data Interchange Format。音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。这种格式常用在磁盘文件中。
ADTS:Audio Data Transport Stream。音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。
ADIF数据格式:
ADTS 一帧 数据格式(中间部分,左右省略号为前后数据帧):
AAC内部结构也不再赘述,可以参考AAC 文件解析及解码流程
细心的读者可能已经发现,前面我们介绍的各种音视频的编码格式,没有一种是我们平时使用到的视频格式,比如:mp4、rmvb、avi、mkv、mov...
没错,这些我们熟悉的视频格式,其实是包裹了音视频编码数据的容器,用来把以特定编码标准编码的视频流和音频流混在一起,成为一个文件。
例如:mp4支持H264、H265等视频编码和AAC、MP3等音频编码。
我们在一些播放器中会看到,有硬解码和软解码两种播放形式给我们选择,但是我们大部分时候并不能感觉出他们的区别,对于普通用户来说,只要能播放就行了。
那么他们内部究竟有什么区别呢?
在手机或者PC上,都会有CPU、GPU或者解码器等硬件。通常,我们的计算都是在CPU上进行的,也就是我们软件的执行芯片,而GPU主要负责画面的显示(是一种硬件加速)。
所谓软解码,就是指利用CPU的计算能力来解码,通常如果CPU的能力不是很强的时候,一则解码速度会比较慢,二则手机可能出现发热现象。但是,由于使用统一的算法,兼容性会很好。
硬解码,指的是利用手机上专门的解码芯片来加速解码。通常硬解码的解码速度会快很多,但是由于硬解码由各个厂家实现,质量参差不齐,非常容易出现兼容性问题。
MediaCodec 是Android 4.1(api 16)版本引入的编解码接口,是所有想在Android上开发音视频的开发人员绕不开的坑。
由于Android碎片化严重,虽然经过多年的发展,Android硬解已经有了很大改观,但实际上各个厂家实现不同, 还是会有一些意想不到的坑。
相对于FFmpeg,Android原生硬解码还是相对容易入门一些,所以接下来,我将会从MediaCodec入手,讲解如何实现视频的编解码,以及引入OpenGL实现对视频的编辑,最后才引入FFmpeg来实现软解,算是一个比较常规的音视频开发入门流程吧。
⑩ android app 如何与uvc摄像头通讯
来看看是怎么操作UVC摄像头的吧.我们实现了一个专门检测UVC摄像头的服务:UVCCameraService类,主要代码如下:
监听
mUSBMonitor = new USBMonitor(this, new USBMonitor.OnDeviceConnectListener() { @Override
public void onAttach(final UsbDevice device) {
Log.v(TAG, "onAttach:" + device);
mUSBMonitor.requestPermission(device);
} @Override
public void onConnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew) {
releaseCamera(); if (BuildConfig.DEBUG) Log.v(TAG, "onConnect:"); try { final UVCCamera camera = new MyUVCCamera();
camera.open(ctrlBlock);
camera.setStatusCallback(new IStatusCallback() { // ... uvc 摄像头链接成功
Toast.makeText(UVCCameraService.this, "UVCCamera connected!", Toast.LENGTH_SHORT).show(); if (device != null)
cameras.append(device.getDeviceId(), camera);
}catch (Exception ex){
ex.printStackTrace();
}
} @Override
public void onDisconnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock) { // ... uvc 摄像头断开链接
if (device != null) {
UVCCamera camera = cameras.get(device.getDeviceId()); if (mUVCCamera == camera) {
mUVCCamera = null;
Toast.makeText(UVCCameraService.this, "UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
liveData.postValue(null);
}
cameras.remove(device.getDeviceId());
}else {
Toast.makeText(UVCCameraService.this, "UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
mUVCCamera = null;
liveData.postValue(null);
}
} @Override
public void onCancel(UsbDevice usbDevice) {
releaseCamera();
} @Override
public void onDettach(final UsbDevice device) {
Log.v(TAG, "onDettach:");
releaseCamera();// AppContext.getInstance().bus.post(new UVCCameraDisconnect());
}
});
这个类主要实现UVC摄像头的监听\链接\销毁\反监听.当有UVC摄像头链接成功后,会创建一个mUVCCamera对象.
然后在MediaStream里, 我们改造了switchCamera,当参数传2时,表示要切换到UVCCamera(0,1分别表示切换到后置\前置摄像头).
创建
在创建摄像头时,如果是要创建uvc摄像头,那直接从服务里面获取之前创建的mUVCCamera实例:
if (mCameraId == 2) {
UVCCamera value = UVCCameraService.liveData.getValue(); if (value != null) { // uvc camera.
uvcCamera = value;
value.setPreviewSize(width, height,1, 30, UVCCamera.PIXEL_FORMAT_YUV420SP,1.0f); return; // value.startPreview();
}else{
Log.i(TAG, "NO UVCCamera");
uvcError = new Exception("no uvccamera connected!"); return;
} // mCameraId = 0;
}123456789101112131415
预览
在预览时,如果uvc摄像头已经创建了,那执行uvc摄像头的预览操作:
UVCCamera value = uvcCamera;if (value != null) {
SurfaceTexture holder = mSurfaceHolderRef.get(); if (holder != null) {
value.setPreviewTexture(holder);
} try {
value.setFrameCallback(uvcFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP/*UVCCamera.PIXEL_FORMAT_NV21*/);
value.startPreview();
cameraPreviewResolution.postValue(new int[]{width, height});
}catch (Throwable e){
uvcError = e;
}
}1234567891011121314
这里我们选的colorFormat为PIXEL_FORMAT_YUV420SP 相当于标准摄像头的NV21格式.
关闭预览
同理,关闭时,调用的是uvc摄像头的关闭.
UVCCamera value = uvcCamera; if (value != null) {
value.stopPreview();
}1234
销毁
因为我们这里并没有实质性的创建,所以销毁时也仅将实例置为null就可以了.
UVCCamera value = uvcCamera;if (value != null) { // value.destroy();
uvcCamera = null;
}12345
有了这些操作,我们看看上层怎么调用,
首先需要在Manifest里面增加若干代码,具体详见UVCCamera工程说明.如下:
<activity android:name=".UVCActivity" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
然后,的代码在UVCActivity里,这个类可以在library分支的myapplication工程里找到.即这里.
启动或者停止UVC摄像头推送:
public void onPush(View view) { // 异步获取到MediaStream对象.
getMediaStream().subscribe(new Consumer<MediaStream>() { @Override
public void accept(final MediaStream mediaStream) throws Exception { // 判断当前的推送状态.
MediaStream.PushingState state = mediaStream.getPushingState(); if (state != null && state.state > 0) { // 当前正在推送,那终止推送和预览
mediaStream.stopStream();
mediaStream.closeCameraPreview();
}else{ // switch 0表示后置,1表示前置,2表示UVC摄像头
// 异步开启UVC摄像头
RxHelper.single(mediaStream.switchCamera(2), null).subscribe(new Consumer<Object>() { @Override
public void accept(Object o) throws Exception { // 开启成功,进行推送.
// ...
mediaStream.startStream("cloud.easydarwin.org", "554", id);
}
}, new Consumer<Throwable>() { @Override
public void accept(final Throwable t) throws Exception { // ooop...开启失败,提示下...
t.printStackTrace();
runOnUiThread(new Runnable() { @Override
public void run() {
Toast.makeText(UVCActivity.this, "UVC摄像头启动失败.." + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
});
}
}
});
}
这样,整个推送就完成了.如果一切顺利,应当能在VLC播放出来UVC摄像头的视频了~~
我们再看看如何录像.也非常简单…
public void onRecord(View view) { // 开始或结束录像.
final TextView txt = (TextView) view;
getMediaStream().subscribe(new Consumer<MediaStream>() { @Override
public void accept(MediaStream mediaStream) throws Exception { if (mediaStream.isRecording()){ // 如果正在录像,那停止.
mediaStream.stopRecord();
txt.setText("录像");
}else { // 没在录像,开始录像...
// 表示最大录像时长为30秒,30秒后如果没有停止,会生成一个新文件.依次类推...
// 文件格式为test_uvc_0.mp4,test_uvc_1.mp4,test_uvc_2.mp4,test_uvc_3.mp4
String path = getExternalFilesDir(Environment.DIRECTORY_MOVIES) + "/test_uvc.mp4";
mediaStream.startRecord(path, 30000); final TextView pushingStateText = findViewById(R.id.pushing_state);
pushingStateText.append("\n录像地址:" + path);
txt.setText("停止");
}
}
});
}21
UVC摄像头还支持后台推送,即不预览的情况下进行推送,同时再切换到前台继续预览.只需要调用一个接口即可实现,如下:
@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
ms.setSurfaceTexture(surfaceTexture); // 设置预览的surfaceTexture}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
ms.setSurfaceTexture(null); // 设置预览窗口为null,表示关闭预览功能
return true;
}123456789
如果要彻底退出uvc摄像头的预览\推送,那只需要同时退出服务即可.
public void onQuit(View view) { // 退出
finish(); // 终止服务...
Intent intent = new Intent(this, MediaStream.class);
stopService(intent);
}1234567
## 获取更多信息 ##