Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上

这篇具有很好参考价值的文章主要介绍了Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 背景

一个需求 : 要将手机上的画面和音频 投屏 到 车机的Android屏幕上。

车机有一个支持OTG的USB-A口,由于设备有限,我们有一个USB-A转HDMI转接口,一跟HDMI线,一个USB-C的拓展坞 (包括HDMI口,两个USB-A口,一个网口),我们将这几根线接在一起,成功将手机和车机连在了一起。

接着,我们在网上找到了一个 jiangdongguo/AndroidUSBCamera ,我们使用Android Studio打开编译安装到车机,并将车机的Usb modeDevice mode切换为Host Mode,这个时候,AndroidUSBCamera会弹出打开USB摄像头的弹框,我们点击同意,就可以看到手机上的画面显示到车机上了。

使用yorkZJC/UvcCameraDemo这个库也可以成功

和这个相关的所有的项目,基本都是基于 saki4510t/UVCCamera 这个开源项目来改的

那么,我们有了以下两个疑问

  • 是如何读取到手机上的画面,显示到车机上的 ?
  • 为什么只有画面,没有声音 ?

2. 是如何读取到手机上的画面,显示到车机上的 ?

带着这个疑问,看了下AndroidUSBCamera的代码。

原来,现在所有主流操作系统都已提供UVC设备驱动,因此符合UVC规格的硬件设备在不需要安装任何的驱动程序下即可在主机中正常使用。使用UVC技术的包括摄像头、数码相机、类比影像转换器、电视棒及静态影像相机等设备。

UVC全称USB Video Class,即 USB视频类,是一种为USB视频捕获设备定义的协议标准。

是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,已成为USB org标准之一。

而项目中,对USB Camera (UVC设备)的使用和视频数据采集进行了高度封装。

//开始进行预览
private void startPreview() {
    mCameraHelper.startPreview(mUVCCameraView);
}

通过startPreview方法,会调用到handleStartPreview方法

public void handleStartPreview(final Object surface) {
    if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
    if ((mUVCCamera == null) || mIsPreviewing) return;
    try {
        mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor);
        // 获取USB Camera预览数据,使用NV21颜色会失真
        // 无论使用YUV还是MPEG,setFrameCallback的设置效果一致
        mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP);
    } catch (final IllegalArgumentException e) {
        try {
            // fallback to YUV mode
            mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, UVCCamera.DEFAULT_PREVIEW_MODE, mBandwidthFactor);
        } catch (final IllegalArgumentException e1) {
            callOnError(e1);
            return;
        }
    }
    if (surface instanceof SurfaceHolder) {
        mUVCCamera.setPreviewDisplay((SurfaceHolder) surface);
    }
    if (surface instanceof Surface) {
        mUVCCamera.setPreviewDisplay((Surface) surface);
    } else {
        mUVCCamera.setPreviewTexture((SurfaceTexture) surface);
    }
    mUVCCamera.startPreview();
    mUVCCamera.updateCameraParams();
    synchronized (mSync) {
        mIsPreviewing = true;
    }
    callOnStartPreview();
}

最终调用到nativeSetPreviewDisplay方法

static jint nativeSetPreviewDisplay(JNIEnv *env, jobject thiz,
	ID_TYPE id_camera, jobject jSurface) {

	jint result = JNI_ERR;
	ENTER();
	UVCCamera *camera = reinterpret_cast<UVCCamera *>(id_camera);
	if (LIKELY(camera)) {
		ANativeWindow *preview_window = jSurface ? ANativeWindow_fromSurface(env, jSurface) : NULL;
		result = camera->setPreviewDisplay(preview_window);
	}
	RETURN(result, jint);
}

这时候,我们就可以预览到手机上的画面了。

3. 为什么只有画面,没有声音 ?

这个时候,我们可以发现,车机上只显示出了画面,没有声音播放的。

看了下AndroidUSBCamera里的代码,当点击了录像按钮,调用startPusher方法,回调里type==0,表示是aac audio stream的,但实际测试中,永远都只会收到type==1的情况,而收不到type==0的情况。

mCameraHelper.startPusher(params, new AbstractUVCCameraHandler.OnEncodeResultListener() {
    @Override
    public void onEncodeResult(byte[] data, int offset, int length, long timestamp, int type) {
        
        // type = 1,h264 video stream
        if (type == 1) {
            FileUtils.putFileStream(data, offset, length);
        }
        // type = 0,aac audio stream
        if(type == 0) {
            trackplayer.write(data, offset, length);//往track中写数据
        }
    }

    @Override
    public void onRecordResult(String videoPath) {
        if(TextUtils.isEmpty(videoPath)) {
            return;
        }
        new Handler(getMainLooper()).post(() -> Toast.makeText(USBCameraActivity.this, "save videoPath:"+videoPath, Toast.LENGTH_SHORT).show());
    }
});

在Github 的issue上,我也看到了这个问题 : 可以支持USB的音频输入吗?

看上去大家也有同样的问题
Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上

这时候,找到的USB摄像头这个应用市场上的app,却是可以在投屏的同时,播放出声音的。

反编译了这个apk,可以看到它的so里面,有一个libUSBAudio.so,看上去就是用来处理音频的so
Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上

我们在网上查找了一下这个so,得知

libusb是一底层的API,可以跨平台实现。

基于libusb可以获取到usb mac的pcm流数据,从而可以读取到音频。

libusb库使用C语言编写,在Android中使用该库需要用到JNI技术。

github : libusb/libusb:用于访问 USB 设备的跨平台库

然后,我们找到了一个libusb的库 jim0608/android_usbaudio: 基于libusb,实现无驱动获取USBAudio

当然,这个库本身是有点问题的,但大体思路可以参考

由于libusb库使用到了JNI,所以我们需要先配置好NDK,其对应版本为21.0.6113669

Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上
Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上

代码中有一个OnDeviceConnectListener,当把视频线插到车机的USB-A口的时候,

onAttach方法就会被调用,这个时候会调用requestPermission去请求权限。

当连接上的时候,会记录下mCtrlBlockmCtrlBlock

private final USBMonitor.OnDeviceConnectListener mOnDeviceConnectListener = new USBMonitor.OnDeviceConnectListener() {

        @Override
        public void onAttach(UsbDevice device) {
            Log.i(TAG, "onAttach: " + device);
            mUSBMonitor.requestPermission(device);
        }

        @Override
        public void onDettach(UsbDevice device) {
            Log.i(TAG, "onDettach: " + device);
        }
    
    	@Override
        public void onConnect(UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
            Log.i(TAG, "onConnect: " + device);
            for (int interfaceIndex = 0; interfaceIndex < device.getInterfaceCount(); interfaceIndex++) {
                if (device.getInterface(interfaceIndex).getInterfaceClass() == USB_CLASS_AUDIO){
                    mCtrlBlock = ctrlBlock;
                    break;
                }
            }

            mAudioDevice = device;
        }

    	//...省略...
    };

接着,我们就可以去初始化音频了

 mCtrlBlock = mUSBMonitor.getDevice(mDevice);
 mUsbAudio.initAudio(mCtrlBlock);

然后再开始捕获

mUsbAudio.startCapture();

这个时候,我们在手机端播放音乐,车机的音响就会输出声音了。

播放声音其实是用过AudioTrack来播放的

在Android中,播放声音可以用MediaPlayer和AudioTrack

区别如下

MediaPlayer AudioTrack
支持格式 MP3,AAC,WAV,OGG,MIDI等 已经解码的PCM流,或WAV格式的音频文件(大部分是PCM流)
解码器 在framework层创建对应音频解码器 不创建解码器,所以只能播放无需解码的WAV文件
联系 在framework层还是会创建AudioTrack

这里,有个采样率的问题,得找到合适的采样率,否则会有播放声音不清晰、白噪音等情况。

结合在一起使用

AndroidUSBCameraandroid_usbaudio结合起来,就可以实现既播放视频,同时播放出声音的效果了。

4. AudioTrack基础的使用

最后,介绍下AudioTrack基础的使用

AudioTrack 的构造方法
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) { ... }
streamType 音频流类型
AudioManager.STREAM_MUSIC:用于音乐播放的音频流。
AudioManager.STREAM_SYSTEM:用于系统声音的音频流。
AudioManager.STREAM_RING:用于电话铃声的音频流。
AudioManager.STREAM_VOICE_CALL:用于电话通话的音频流。
AudioManager.STREAM_ALARM:用于警报的音频流。
AudioManager.STREAM_NOTIFICATION:用于通知的音频流。
AudioManager.STREAM_BLUETOOTH_SCO:用于连接到蓝牙电话时的手机音频流。
AudioManager.STREAM_SYSTEM_ENFORCED:在某些国家实施的系统声音的音频流。
AudioManager.STREAM_DTMFDTMF音调的音频流。
AudioManager.STREAM_TTS:文本到语音转换(TTS)的音频流。
sampleRateInHz 采样率

播放的音频每秒钟会有多少次采样,一般为44100,最好是通过代码动态获取采样率,其他常见的采样率还有

96000, 
88200, 
64000, 
48000, 
44100, 
32000, 
24000, 
22050, 
16000, 
12000, 
11025, 
8000, 
7350, 

如果发现播放后声音不清晰、白噪音等情况,可以调整这个采样率值

channelConfig 声道数

单声道AudioFormat.CHANNEL_IN_MONO,双声道AudioFormat.CHANNEL_IN_STEREO,建议选择单声道

audioFormat 数据位宽

只支持AudioFormat.ENCODING_PCM_8BIT(8bit)AudioFormat.ENCODING_PCM_16BIT(16bit)两种,后者支持所有Android手机

bufferSizeInBytes 音频缓冲区大小

建议使用AudioTrack.getMinBufferSize()这个方法获取

int bufSize = AudioTrack.getMinBufferSize(SAMPLE_RATE_HZ,channelConfig, AudioFormat.ENCODING_PCM_16BIT);
mode 播放模式

有两种播放模式:

  • MODE_STATIC : 一次性将所有数据都写入播放缓冲区中,简单高效,一般用于铃声,系统提醒音,内存比较小的。
  • MODE_STREAM : 需要按照一定的时间间隔,不断的写入音频数据,理论上它可以应用于任何音频播放的场景。
AudioTrack 播放示例

初始化

int bufSize = AudioTrack.getMinBufferSize(SAMPLE_RATE_HZ,AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);

track = new AudioTrack(AudioManager.STREAM_MUSIC,
                       SAMPLE_RATE_HZ,
                       channelConfig,
                       AudioFormat.ENCODING_PCM_16BIT,
                       bufSize,
                       AudioTrack.MODE_STREAM);
track.play();

写入数据

public void pcmData(byte[] data) {
    track.write(data, 0, data.length);
}

停止播放,销毁资源

if(audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED){
    audioTrack.stop();
    audioTrack.release();
}

5.本文代码下载

本文Demo下载地址 : Android UVC USBCamera投屏Demo

参考 :
一篇文章带你了解Android Usb摄像头
这可能是介绍Android UvcCamera最详细的文章了
Android音频系统AudioTrack使用方法详解
ffmpeg开发之旅(8):Android UVC Camera(USB摄像头)开发核心技术详解
Android音视频录制与播放功能简述
Android UCV 同时打开多路摄像头
Android从USB声卡录制高质量音频-----使用libusb读取USB声卡数据
基于libusb库、uac协议,获取Audio声音数据
Android/linux从usb声卡获取音频(使用libusb库)—监听“纯麦”(五)文章来源地址https://www.toymoban.com/news/detail-482329.html

到了这里,关于Android USBCamera投屏 - 利用UVC协议将手机上的画面有线投屏到Android车机的屏幕上的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Studio 支持安卓手机投屏

    有时当我们在线上做技术分享或者功能演示时,希望共享连接中的手机屏幕,此时我们会求助 ApowerMirror,LetsView,Vysor,Scrcpy 等工具。如果你是一个 Android Developer,那么现在你有了更好的选择。 自 Android Studio Electric Eel (电鳗)起支持了手机投屏,且功能非常强大: 手机屏幕

    2024年01月17日
    浏览(48)
  • Android中相机(Camera)画面旋转角度分析:手机摄像头的“正向”、手机画面自然方向、相机画面的偏转角度

    #.概述:     1.如同人眼看东西分上下一样,摄像头也有其“正向”,正常情况下,Android手机后置、前置摄像头的“正向”朝向为手机的“右侧”(默认如此,除非手机厂商修改设置)。 (这里运行代码做过测试发现,前置摄像头也是以右侧为正向,而不是有些资料上说的

    2024年02月14日
    浏览(81)
  • scrcpy之将Android手机投屏到Linux电脑实践

    参考:https://zhuanlan.zhihu.com/p/366378837 电脑端安装投屏程序 手机端设置 手机端无需安装任何软件,只需开启【开发者选项】-【USB调试】及相关选项,比如我开启了【USB调试、USB调试(安全设置)、无线调试、USB安装】 开启【USB调试】相关选项后,用USB数据线连接电脑与手机,

    2024年02月09日
    浏览(56)
  • 如何通过 nginx 实现远程投屏并控制 android 手机

    操作步骤 本地电脑 Local-A.   远程电脑 Remote-B 和 跟 Remote-B 通过 USB 连接的手机 C 1. 手机 C 连接到 Remote-B 2. Remote-B  通adb tcpip port 将台架的adb设置为以太网模式,从而使台架的adb服务可以被端口绑定。 # enable adb ethernet connection adb tcpip 5555 配置 nginx 5. 修改跳板机nginx配置,将端

    2024年02月12日
    浏览(52)
  • C++版Android实时投屏软件系统源码,安卓手机投屏软件源码,无需root权限

    QtScrcpy 可以通过 USB / 网络连接Android设备,并进行显示和控制。无需root权限。 同时支持 GNU/Linux ,Windows 和 MacOS 三大主流桌面平台。 完整代码下载地址:C++版Android实时投屏软件系统源码 它专注于: 精致 (仅显示设备屏幕) 性能 (30~60fps) 质量 (1920×1080以上) 低延迟 (35~70ms) 快速启

    2024年02月05日
    浏览(50)
  • 使用互传APP实现Android手机投屏到windows电脑

    目前自己实现的条件: 1.自用VIVO品牌的手机(每个,手机品牌的自带互传或者投屏APP不一样,VIVO叫做“互传APP”。) 2.PC(自用win11,应该XP、win7/10都可以。)安装“互传PC端” 3.PC和手机在同一个路由器下。 具体步骤: 1,打开PC端互传软件 2.手机互传APP内扫码下图PC端的二

    2024年02月14日
    浏览(48)
  • Android Studio新功能-设备镜像Device mirroring-在电脑侧显示手机实时画面并可控制

    下载最新的灰测版本-蜥蜴 成功运行到真机后,点击右侧Running Devices选项卡,再点击+号 选中当前设备; 非常丝滑同步,在电脑侧也可以顺畅控制真机 该功能大大方便了我们视线保持在显示器上专注开发,并且便于与UI视觉进行校准与比对。 Device mirroring You can now mirror your p

    2024年02月08日
    浏览(41)
  • 探索Android USBCamera:一款开源的USB摄像头应用

    项目地址:https://gitcode.com/jiangdongguo/AndroidUSBCamera 本文将带您走进Android USBCamera项目,这是一个强大的开源库,允许Android设备通过USB连接物理相机并作为系统默认摄像头使用。无论您是开发者、摄影师还是对移动设备摄影有独特需求的用户,这个项目都值得您的关注。 Android

    2024年04月27日
    浏览(35)
  • UVC1.5协议 2

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 文章目录 前言 一、UVC拓扑结构 二、UVC描述符以及单元   UVC协议是USB下的一个子类 ,UVC就是USB video class ,是 USB视频捕获设备定义的协议标准。USB设备可能具备很多功能比如一个摄像头可能集合了

    2023年04月08日
    浏览(34)
  • 使用JavaCV和Spring Boot搭建HTTP-FLV直播服务:实时播放RTSP、RTMP和桌面投屏画面

    使用JavaCV和Spring Boot搭建HTTP-FLV直播服务:实时播放RTSP、RTMP和桌面投屏画面 摘要: 在本文中,我们将探讨如何使用JavaCV和Spring Boot构建一个简单的HTTP-FLV直播服务,该服务可以在浏览器中通过flv.js进行实时播放RTSP、RTMP和桌面投屏的画面。我们将介绍所需的开发环境和依赖库

    2024年01月20日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包