前言

1,软解码只有两种方式:x264只能做编码视频,功能单一;ffmpeg功能齐全。
2,faac用来做音频编码。
3,软解码不能用mediacodec。

架构解析

1,队列要放到native层。硬编的队列放到Java层,这是不一样的地方
2,队列一定要放压缩数据,而不能放原始数据。
3,今天用摄像头数据。
4,camerax不需要旋转,camera1需要自己写旋转算法
5,x264需要自己编译。
6,与MediaCodec(从cpu到dsp在到CPU)最大不同是都在cpu。要有数据存放的地方,x264_picture_t存储原始容器(如yuv)。
7,yuv如何计算?

y是width*height
9,编码在Native层,队列也只能在native层
10,.

代码

添加camerax依赖

 implementation "androidx.camera:camera-core:1.0.0-alpha05"implementation "androidx.camera:camera-camera2:1.0.0-alpha05"

package com.maniu.x264rtmpmaniu.camerax;import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.TextureView;
import android.view.ViewGroup;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageAnalysisConfig;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
import androidx.lifecycle.LifecycleOwner;import com.maniu.x264rtmpmaniu.LivePusher;
import com.maniu.x264rtmpmaniu.utils.ImageUtil;
import java.util.concurrent.locks.ReentrantLock;
//camerax简单
public class VideoChanel implements Preview.OnPreviewOutputUpdateListener, ImageAnalysis.Analyzer {private CameraX.LensFacing currentFacing = CameraX.LensFacing.BACK;//CameraX 宽高我们不需要适配,camerax会自动适配int width = 480;int height = 640;private TextureView textureView;private HandlerThread handlerThread;LivePusher livePusher;private MediaCodec mediaCodec;private boolean isLiving;//    lifecycleOwner activity,livePusher做两件事,一件是初始化native层VideoChannel,一件是推流public VideoChanel(LifecycleOwner lifecycleOwner, TextureView textureView, LivePusher livePusher) {this.livePusher = livePusher;
//        初始化this.textureView = textureView;handlerThread = new HandlerThread("Analyze-thread");handlerThread.start();//ImageAnalysisConfig imageAnalysisConfig =  new ImageAnalysisConfig.Builder() .//图片分析回调在哪个线程,一定不能是主线程。setCallbackHandler(new Handler(handlerThread.getLooper())).setLensFacing(currentFacing)//渲染的模式,渲染最新的图片.setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE).setTargetResolution(new Size(width, height)).build();ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);//imageAnalysis.setAnalyzer(this);//width, height宽高PreviewConfig previewConfig = new PreviewConfig.Builder()  .setTargetResolution(new Size(width, height)).setLensFacing(currentFacing) //前置或者后置摄像头.build();Preview preview = new Preview(previewConfig);//如果预览需要添加监听preview.setOnPreviewOutputUpdateListener(this);//绑定Activity,Usecase是一个不定的数组。//camerax 封装了 数据,拍照,预览(数据cpu传到gpu渲染),视频分析//imageAnalysis 直播,获取到的是一帧帧的画面//preview  预览画面CameraX.bindToLifecycle(lifecycleOwner,preview,imageAnalysis);}private ReentrantLock lock = new ReentrantLock();private byte[] y;private byte[] u;private byte[] v;// 图像帧数据,全局变量避免反复创建,降低gc频率private byte[] nv21;byte[] nv21_rotated;byte[] nv12;//类似camera1 previewCallback@Overridepublic void analyze(ImageProxy image, int rotationDegrees) {//摄像头一旦打开,就会回调analyze(),数据就在image中if (!isLiving) {return;}Log.i("david", "analyze: ");// 开启直播并且已经成功连接服务器才获取i420数据//planes[0] planes[1] planes[2]分别是yuvImageProxy.PlaneProxy[] planes = image.getPlanes();lock.lock();//子线程调用,需要锁锁住。// 重复使用同一批byte数组,减少gc频率//camera1会直接把yuv编成Nv21了,结果确实吃力不讨好,camerax会把Yuv分别放到bytebuffer数组中if (y == null) {//y每一帧的大小都可以算出来,width X height,但不建议这么写,建议使用limit()//limit()获取整个容器长度y = new byte[planes[0].getBuffer().limit() - planes[0].getBuffer().position()];u = new byte[planes[1].getBuffer().limit() - planes[1].getBuffer().position()];v = new byte[planes[2].getBuffer().limit() - planes[2].getBuffer().position()];
//             初始化native层 编码,宽高要反着传this.livePusher.native_setVideoEncInfo(image.getHeight(),image.getWidth(), 10, 640_000);}if (image.getPlanes()[0].getBuffer().remaining() == y.length) {planes[0].getBuffer().get(y);//转移到y的数组planes[1].getBuffer().get(u);planes[2].getBuffer().get(v);int stride = planes[0].getRowStride();//与我们设置的宽高是不一样的。这里是经过适配的Size size = new Size(image.getWidth(), image.getHeight());int width = size.getHeight();int heigth = planes[0].getRowStride();if (nv21 == null) {nv21 = new byte[heigth * width * 3 / 2];nv21_rotated = new byte[heigth * width * 3 / 2];}ImageUtil.yuvToNv21(y, u, v, nv21, heigth, width);ImageUtil.nv21_rotate_to_90(nv21, nv21_rotated, heigth, width);
//        nv21_rotated是一帧画面,是数据的起点,是旋转之后的。推流livePusher.native_pushVideo(nv21_rotated);}lock.unlock();}//用来做预览的,需要textureView@Overridepublic void onUpdated(Preview.PreviewOutput output) {SurfaceTexture surfaceTexture = output.getSurfaceTexture();//我从摄像头拿到的textureView和自己保存的是不是同一个,竖屏变横屏的时候textureView就不是同一个了if (textureView.getSurfaceTexture() != surfaceTexture) {if (textureView.isAvailable()) {// 当切换摄像头时,如果不处理就会报错ViewGroup parent = (ViewGroup) textureView.getParent();parent.removeView(textureView);
//                竖屏变横屏的时候textureView就不是同一个了,重新添加parent.addView(textureView, 0);parent.requestLayout();}//绑定textureView.setSurfaceTexture(surfaceTexture);}}public void startLive() {isLiving = true;}
}
package com.maniu.x264rtmpmaniu;import android.app.Activity;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;import com.maniu.x264rtmpmaniu.utils.ImageUtil;import java.util.Iterator;
import java.util.List;public class CameraHelper implements SurfaceHolder.Callback, Camera.PreviewCallback {private static final String TAG = "CameraHelper";private Activity mActivity;private int mHeight;private int mWidth;private int mCameraId;private Camera mCamera;private byte[] buffer;private byte[] yuv;private SurfaceHolder mSurfaceHolder;private Camera.PreviewCallback mPreviewCallback;private int mRotation;private OnChangedSizeListener mOnChangedSizeListener;public CameraHelper(Activity activity, int cameraId, int width, int height) {mActivity = activity;mCameraId = cameraId;mWidth = width;mHeight = height;}public void switchCamera() {if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;} else {mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;}stopPreview();startPreview();}private void stopPreview() {if (mCamera != null) {//预览数据回调接口mCamera.setPreviewCallback(null);//停止预览mCamera.stopPreview();//释放摄像头mCamera.release();mCamera = null;}}private void startPreview() {try {//获得camera对象mCamera = Camera.open(mCameraId);//配置camera的属性Camera.Parameters parameters = mCamera.getParameters();//设置预览数据格式为nv21parameters.setPreviewFormat(ImageFormat.NV21);//这是摄像头宽、高setPreviewSize(parameters);// 设置摄像头 图像传感器的角度、方向setPreviewOrientation(parameters);mCamera.setParameters(parameters);buffer = new byte[mWidth * mHeight * 3 / 2];yuv= new byte[mWidth * mHeight * 3 / 2];//数据缓存区mCamera.addCallbackBuffer(buffer);mCamera.setPreviewCallbackWithBuffer(this);//设置预览画面mCamera.setPreviewDisplay(mSurfaceHolder);mOnChangedSizeListener.onChanged(mWidth, mHeight);mCamera.startPreview();} catch (Exception ex) {ex.printStackTrace();}}private void setPreviewOrientation(Camera.Parameters parameters) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraId, info);mRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (mRotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90: // 横屏 左边是头部(home键在右边)degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:// 横屏 头部在右边degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360; // compensate the mirror} else { // back-facingresult = (info.orientation - degrees + 360) % 360;}//设置角度mCamera.setDisplayOrientation(result);}private void setPreviewSize(Camera.Parameters parameters) {//获取摄像头支持的宽、高List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();Camera.Size size = supportedPreviewSizes.get(0);Log.d(TAG, "支持 " + size.width + "x" + size.height);//选择一个与设置的差距最小的支持分辨率// 10x10 20x20 30x30// 12x12int m = Math.abs(size.height * size.width - mWidth * mHeight);supportedPreviewSizes.remove(0);Iterator<Camera.Size> iterator = supportedPreviewSizes.iterator();//遍历while (iterator.hasNext()) {Camera.Size next = iterator.next();Log.d(TAG, "支持 " + next.width + "x" + next.height);int n = Math.abs(next.height * next.width - mWidth * mHeight);if (n < m) {m = n;size = next;}}mWidth = size.width;mHeight = size.height;parameters.setPreviewSize(mWidth, mHeight);Log.d(TAG, "设置预览分辨率 width:" + size.width + " height:" + size.height);}public void setPreviewDisplay(SurfaceHolder surfaceHolder) {mSurfaceHolder = surfaceHolder;mSurfaceHolder.addCallback(this);}public void setPreviewCallback(Camera.PreviewCallback previewCallback) {mPreviewCallback = previewCallback;}@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//释放摄像头stopPreview();//开启摄像头startPreview();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {stopPreview();}@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {// data数据依然是倒的ImageUtil.nv21_rotate_to_90(data, yuv, mWidth, mHeight);
//        caerma 1  camera 2   cameraX 拼装yuv
//        camera1  camerax  你只y u vmPreviewCallback.onPreviewFrame(yuv, camera);camera.addCallbackBuffer(buffer);}public void setOnChangedSizeListener(OnChangedSizeListener listener) {mOnChangedSizeListener = listener;}public interface OnChangedSizeListener {void onChanged(int w, int h);}
}

王学岗————直播推流(软编)03x264集成与camera推流相关推荐

  1. 王学岗视频编码————视频编解码基础与MediaCodec编解码(对应1234节)

    为什么要学习音视频 核心竞争力,高端人才相当缺乏,技术迭代慢, 为什么音视频学不好 资料比较少,音视频最难的地方在于编码,没有形成完整的体系 关于音视频编码 上 1,视频文件:MP4,RMVB, AV ...

  2. 王学岗————FFmpeg软解之视频播放(对应第39、第41、第42)

    前言 1,软编(FFmpeg)可以播放任何类型的视频,而硬编(Mediacodec)播放的视频有限. 2,IJKPlayer,哔哩哔哩基于FFmpeg展开,是对FFmpeg的封装. 3,FFmpeg, ...

  3. 音视频7——安卓软编音视频数据推送到rtmp服务器

    音视频开发路线: Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门 demo地址: videoPath/Demo8Activity.j ...

  4. 【建议背诵】2022下半年软考「集成」100题(1)

    为了帮助大家提高备考效率,为大家分享[建议背诵]2022下半年软考「集成」100题,快来跟着小编一起打卡学习吧~ 1.项目的特点有哪些? (1)临时性:指每个项目有明确的开始和结束日期. (2)独特性 ...

  5. OBS配置项:编码器--软编--硬编+码率控制

    一.OBS编码器 1. 软编:x264 使用CPU进行编码,占用CPU资源多,但编码出来视频质量更好 2. 硬编:NVENC H.264 基于GPU编码,编码的计算负载由NV显卡的GPU承担,从而大大 ...

  6. android播放器和视频拍摄中的硬解和软解以及硬编和软编的区别

    转载 原文地址:https://blog.csdn.net/ltym2014/article/details/82354606 https://blog.csdn.net/lipengshiwo/ar ...

  7. 国产首款脑机编解码集成芯片发布

    来源:科学网 5月17日,由中电云脑(天津)科技有限公司(简称"中电云脑")联合天津大学共同研发的国产首款脑机编解码集成芯片--"脑语者"在天津第三届世界智能大 ...

  8. 微信小游戏直播在Android端的跨进程渲染推流实践

    本文由微信开发团队工程师"virwu"分享. 1.引言 近期,微信小游戏支持了视频号一键开播,将微信升级到最新版本,打开腾讯系小游戏(如跳一跳.欢乐斗地主等),在右上角菜单就可以看 ...

  9. 去抖音面试被问到硬编码与软编码区别,如何选取硬编与软编?

    原文链接:https://zhuanlan.zhihu.com/p/82130600 Android的视频相关的开发,大概一直是整个Android生态,以及Android API中,最为分裂以及兼容性 ...

最新文章

  1. 计算机语言编程能力有哪些,除了编程语言,程序员还需要具备哪些能力
  2. 【SQL】IS NULL and = NULL 在 sql server 中的区别
  3. 为什么hive需要mysql作为数据库_Hive安装(本地独立模式,MySql为元数据库)
  4. OAF_OAF增删改-新增的实现(案例)
  5. java23中设计模式——结构模式——Flyweight(享元)
  6. 开篇词:如何轻松获得 Offer
  7. 计蒜客挑战难题:移除数组中的重复元素
  8. Java后端知识---数据结构(1)
  9. easy-excel导入导出excel(待完善)
  10. Akamai:三季度DDoS攻击总数同比上涨138% 规模超100 Gbps
  11. 在线协作编辑算法简介- OT算法
  12. slxrom+v.21+原生android+4.2,小米4移动联通版 魔趣OS 安卓10 MagiskV21版 完美ROOT 纯净完美 原生极简 纯净推荐...
  13. 酷柚易汛进销存开源版对外接口
  14. 读取FBX文件踩坑清单
  15. android扫雷代码解释,android的扫雷程序.doc
  16. win10任务栏全透明
  17. 实现一个病毒扫描app——python爬取病毒样本
  18. 【USACO10HOL】 Cow Politics
  19. 【云和恩墨大讲堂】彭文元 - 中间件BES连接池的配置和问题诊断方法
  20. vue项目在ie浏览器中不兼容问题的处理

热门文章

  1. 工业相机的分辨率是如何定义的?
  2. 黑群晖DSM7.X的Synology Photos套件人脸识别补丁
  3. 游泳防水耳机推荐,推荐四款高质量游泳防水耳机
  4. 不一样的动图-APNG
  5. 计算机二级字处理在哪保存,计算机二级ms-office字处理操作步骤(37页)-原创力文档...
  6. LTS分布式任务调度文档
  7. 计算机毕业设计-超市库存预警系统 库存报警系统【附远程调试+讲解+文档】
  8. android 添加异常,android – 坏标记异常 – 无法添加窗口(Marshmallow – 浮动工具栏)...
  9. 家庭安全月 康巴什消防千份宣传画报贴满新区
  10. Perl 数组和列表