安卓视频直播

经过前面的几个步骤,我们基本把视频直播的所有环境配置完成,接下来我们进行真正的开发阶段。
1.把前面编译好的库以及文件复制到androidstudio中cpp文件夹下,如图
这里写图片描述
2.修改cmakelist文件中、

cmake_minimum_required(VERSION 3.4.1)

add_subdirectory(src/main/cpp/include/librtmp)
#添加动态库
file(GLOB my_source src/main/cpp/*.cpp src/main/cpp/*.c)
add_library(pusher SHARED ${my_source})

#设置变量 指向ffmpeg静态库目录
set(my_lib_path ${CMAKE_SOURCE_DIR}/src/main/cpp/lib/${ANDROID_ABI})
#添加静态库 并设置库路径
add_library(faac STATIC IMPORTED)
set_target_properties(faac PROPERTIES IMPORTED_LOCATION ${my_lib_path}/libfaac.a)
add_library(x264 STATIC IMPORTED)
set_target_properties(x264 PROPERTIES IMPORTED_LOCATION ${my_lib_path}/libx264.a)
#设置头文件查找目录
include_directories(src/main/cpp/include)

#编译链接
target_link_libraries(pusher log rtmp faac x264  )

在build.gradle中defaultConfig的添加如下代码:

 sourceSets {
            main {
                jniLibs.srcDirs = ['lib']
            }
        }

3..直播流程图
这里写图片描述
视频编码格式
视频编码格式:
代表软编码器——openh264、x264
这里写图片描述
Profile : baseline main high
Level : 限制了码率上限
Resolution : 分辨率
Bitrate : 码率,与数据大小成正比
Frame Rate : 帧率,每秒多少帧图像,影响流畅度
Frame Interval : 关键帧间隔
4.音频编码格式
aac
这里写图片描述
Profile : lc main le
Sample Rate : 采样率
Channel : 声道数
Bitrate : 码率,与数据大小成正比
RTMP
Real Time Message Protocol(实时信息传输协议)
基于TCP的应用层协议
视频包
1、解码信息包(sps与pps)
2、数据包
音频包
1、解码信息包
2、数据包

5.视频包数据
这里写图片描述
6.音频包数据
这里写图片描述
7.RTMPDump
用于实现视频的录制,从rtmp的中转服务器接受到视频流,并把视频流保存成flv文件
交叉编译,过程如下:
这里写图片描述
8.在进行相机录制视频摄像时候,在手机屏幕上看到图像不是很正确,这就需要相机的旋转
特别再用camera时候注意:

    mCamera.addCallbackBuffer(buffer);

不然后期手机内存会很快的增加,调用camera处理相机的代码:


    private final static String TAG = "VideoPusher";
    private boolean mPreviewRunning;
    private Camera mCamera;
    private SurfaceHolder mHolder;
    private VideoParam mParam;
    private byte[] buffer;
    private byte[] raw;
    private Activity mActivity;
    private int screen;
    private final static int SCREEN_PORTRAIT = 0;
    private final static int SCREEN_LANDSCAPE_LEFT = 90;
    private final static int SCREEN_LANDSCAPE_RIGHT = 270;

    public VideoPusher(Activity activity, SurfaceHolder surfaceHolder, VideoParam param, PusherNative pusherNative) {
        super(pusherNative);
        mActivity = activity;
        mParam = param;
        mHolder = surfaceHolder;
        surfaceHolder.addCallback(this);
    }

    @Override
    public void startPusher() {
        startPreview();
        mPusherRuning = true;
    }

    @Override
    public void stopPusher() {
        mPusherRuning = false;
    }

    @Override
    public void release() {
        mPusherRuning = false;
        mActivity = null;
        stopPreview();
    }

    public void switchCamera() {
        if (mParam.getCameraId() == CameraInfo.CAMERA_FACING_BACK) {
            mParam.setCameraId(CameraInfo.CAMERA_FACING_FRONT);
        } else {
            mParam.setCameraId(CameraInfo.CAMERA_FACING_BACK);
        }
        stopPreview();
        startPreview();
    }

    private void stopPreview() {
        if (mPreviewRunning && mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
            mPreviewRunning = false;
        }
    }

    @SuppressWarnings("deprecation")
    private void startPreview() {
        if (mPreviewRunning) {
            return;
        }
        try {
            mCamera = Camera.open(mParam.getCameraId());
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewFormat(ImageFormat.NV21);
            setPreviewSize(parameters);
            setPreviewFpsRange(parameters);
            setPreviewOrientation(parameters);
            mCamera.setParameters(parameters);
            int bitsPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.NV21);
            buffer = new byte[mParam.getWidth() * mParam.getHeight() * bitsPerPixel / 8];
            raw = new byte[mParam.getWidth() * mParam.getHeight() * bitsPerPixel / 8];
            mCamera.addCallbackBuffer(buffer);
            System.out.println("bitsPerPixel:" + bitsPerPixel);
            mCamera.setPreviewCallbackWithBuffer(this);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
            mPreviewRunning = true;
        } catch (Exception ex) {
            ex.printStackTrace();
            if (null != mListener) {
                mListener.onErrorPusher(-100);
            }
        }
    }

    private void setPreviewSize(Camera.Parameters parameters) {
        List<Integer> supportedPreviewFormats = parameters.getSupportedPreviewFormats();
        for (Integer integer : supportedPreviewFormats) {
            System.out.println("支持:" + integer);
        }
        List<Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
        Size size = supportedPreviewSizes.get(0);
        Log.d(TAG, "支持 " + size.width + "x" + size.height);
        int m = Math.abs(size.height * size.width - mParam.getHeight() * mParam.getWidth());
        supportedPreviewSizes.remove(0);
        Iterator<Size> iterator = supportedPreviewSizes.iterator();
        while (iterator.hasNext()) {
            Size next = iterator.next();
            Log.d(TAG, "支持 " + next.width + "x" + next.height);
            int n = Math.abs(next.height * next.width - mParam.getHeight() * mParam.getWidth());
            if (n < m) {
                m = n;
                size = next;
            }
        }
        mParam.setHeight(size.height);
        mParam.setWidth(size.width);
        parameters.setPreviewSize(mParam.getWidth(), mParam.getHeight());
        Log.d(TAG, "预览分辨率 width:" + size.width + " height:" + size.height);
    }

    private void setPreviewFpsRange(Camera.Parameters parameters) {
        int range[] = new int[2];
        parameters.getPreviewFpsRange(range);
        Log.d(TAG, "预览帧率 fps:" + range[0] + " - " + range[1]);
//      int fps = mParam.getFps()*1000;
//      if (fps >= range[0] && fps <= range[1]) {
//          parameters.setPreviewFpsRange(fps, fps);
//      }
    }

    private void setPreviewOrientation(Camera.Parameters parameters) {
        CameraInfo info = new CameraInfo();
        Camera.getCameraInfo(mParam.getCameraId(), info);
        int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
        screen = 0;
        switch (rotation) {
        case Surface.ROTATION_0:
            screen = SCREEN_PORTRAIT;
            mNative.setVideoOptions(mParam.getHeight(), mParam.getWidth(), mParam.getBitrate(), mParam.getFps());
            break;
        case Surface.ROTATION_90: // 横屏 左边是头部(home键在右边)
            screen = SCREEN_LANDSCAPE_LEFT;
            mNative.setVideoOptions(mParam.getWidth(), mParam.getHeight(), mParam.getBitrate(), mParam.getFps());
            break;
        case Surface.ROTATION_180:
            screen = 180;
            break;
        case Surface.ROTATION_270:// 横屏 头部在右边
            screen = SCREEN_LANDSCAPE_RIGHT;
            mNative.setVideoOptions(mParam.getWidth(), mParam.getHeight(), mParam.getBitrate(), mParam.getFps());
            break;
        }
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + screen) % 360;
            result = (360 - result) % 360; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - screen + 360) % 360;
        }
        mCamera.setDisplayOrientation(result);

        // // 如果是竖屏 设置预览旋转90度,并且由于回调帧数据也需要旋转所以宽高需要交换
        // if (mContext.getResources().getConfiguration().orientation ==
        // Configuration.ORIENTATION_PORTRAIT) {
        // mNative.setVideoOptions(mParam.getHeight(), mParam.getWidth(),
        // mParam.getBitrate(), mParam.getFps());
        // parameters.set("orientation", "portrait");
        // mCamera.setDisplayOrientation(90);
        // } else {
        // mNative.setVideoOptions(mParam.getWidth(), mParam.getHeight(),
        // mParam.getBitrate(), mParam.getFps());
        // parameters.set("orientation", "landscape");
        // mCamera.setDisplayOrientation(0);
        // }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mHolder = holder;
        stopPreview();
        startPreview();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        stopPreview();
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        if (mPusherRuning) {
            switch (screen) {
            case SCREEN_PORTRAIT:
                portraitData2Raw(data);
                break;
            case SCREEN_LANDSCAPE_LEFT:
                raw = data;
                break;
            case SCREEN_LANDSCAPE_RIGHT:
                landscapeData2Raw(data);
                break;
            }
            mNative.fireVideo(raw);
        }
        camera.addCallbackBuffer(buffer);
    }

    private void landscapeData2Raw(byte[] data) {
        int width = mParam.getWidth(), height = mParam.getHeight();
        int y_len = width * height;
        int k = 0;
        // y数据倒叙插入raw中
        for (int i = y_len - 1; i > -1; i--) {
            raw[k] = data[i];
            k++;
        }
        // System.arraycopy(data, y_len, raw, y_len, uv_len);
        // v1 u1 v2 u2
        // v3 u3 v4 u4
        // 需要转换为:
        // v4 u4 v3 u3
        // v2 u2 v1 u1
        int maxpos = data.length - 1;
        int uv_len = y_len >> 2; // 4:1:1
        for (int i = 0; i < uv_len; i++) {
            int pos = i << 1;
            raw[y_len + i * 2] = data[maxpos - pos - 1];
            raw[y_len + i * 2 + 1] = data[maxpos - pos];
        }
    }

    private void portraitData2Raw(byte[] data) {
        // if (mContext.getResources().getConfiguration().orientation !=
        // Configuration.ORIENTATION_PORTRAIT) {
        // raw = data;
        // return;
        // }
        int width = mParam.getWidth(), height = mParam.getHeight();
        int y_len = width * height;
        int uvHeight = height >> 1; // uv数据高为y数据高的一半
        int k = 0;
        if (mParam.getCameraId() == CameraInfo.CAMERA_FACING_BACK) {
            for (int j = 0; j < width; j++) {
                for (int i = height - 1; i >= 0; i--) {
                    raw[k++] = data[width * i + j];
                }
            }
            for (int j = 0; j < width; j += 2) {
                for (int i = uvHeight - 1; i >= 0; i--) {
                    raw[k++] = data[y_len + width * i + j];
                    raw[k++] = data[y_len + width * i + j + 1];
                }
            }
        } else {
            for (int i = 0; i < width; i++) {
                int nPos = width - 1;
                for (int j = 0; j < height; j++) {
                    raw[k] = data[nPos - i];
                    k++;
                    nPos += width;
                }
            }
            for (int i = 0; i < width; i += 2) {
                int nPos = y_len + width - 1;
                for (int j = 0; j < uvHeight; j++) {
                    raw[k] = data[nPos - i - 1];
                    raw[k + 1] = data[nPos - i];
                    k += 2;
                    nPos += width;
                }
            }
        }
    }

在cpp中编写具体的逻辑代码
我已经在github编写好了源码,需要的自行下载
livevideo

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值