1、最纯粹的直播技术实战02Camera的处理以及推流最纯粹的直播技术实战02-Camera的处理以及推流我们在MainActivity里面添加两个Button,一个是直播的,一个是看直播的android:id=+id/activity_main android:layout_width=match_parent android:layout_height=match_parent android:paddingBottom=dimen/activity_vertical_margin android:paddingLeft=dimen/activity_horizontal_margin and
2、roid:paddingRight=dimen/activity_horizontal_margin android:paddingTop=dimen/activity_vertical_margin android:orientation=vertical tools:context=com.xiaoxiao.live.MainActivity 我们把上一次的测试TextView注释掉了,然后新建了一个LiveActivity来处理Camera以及推流,主要就是展示直播。 后续还会有看直播的处理,就要就是拉流以及视频的播放了 接下来就要在LiveActivity里面处理一下Camera的东西
3、了,首先要在LiveActivity的layout里面添加一个SurfaceViewandroid:orientation=vertical android:layout_width=match_parent android:layout_height=match_parent 因为不是拍照,所以Camera的处理就会显得比较的简单了,package com.xiaoxiao.live;import android.graphics.ImageFormat;import android.hardware.Camera;import android.os.Bundle;import androi
4、d.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.SurfaceHolder;import android.view.SurfaceView;/* * Created by Administrator on 2017/2/20. */public class LiveActivity extends AppCompatActivity implements SurfaceHolde
5、r.Callback, Camera.PreviewCallback private Camera mCamera; private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private int mCameraId = 0; private int width = 720; private int height = 480; Override public void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState)
6、; setContentView(R.layout.activity_live); mSurfaceView = (SurfaceView) findViewById(R.id.live_sv_live); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.setFixedSize(width, height); mSurfaceHolder.addCallback(this); Override public boolean onCreateOptionsMenu(Menu menu) getMenuInflater().in
7、flate(R.menu.menu_live, menu); return true; Override public boolean onOptionsItemSelected(MenuItem item) if(item.getItemId() = R.id.checkable_menu) boolean isChecked = item.isChecked(); Log.e(LiveActivity, checked: + isChecked); item.setChecked(!isChecked); mCameraId = 1 - mCameraId; destroyCamera()
8、; initCamera(); return true; return super.onOptionsItemSelected(item); Override public void onPreviewFrame(byte data, Camera camera) Override public void surfaceCreated(SurfaceHolder holder) initCamera(); Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) Ov
9、erride public void surfaceDestroyed(SurfaceHolder holder) destroyCamera(); private void initCamera() try mCamera = Camera.open(mCameraId); mCamera.setPreviewDisplay(mSurfaceHolder); Camera.Parameters params = mCamera.getParameters(); /设置预览大小 params.setPreviewSize(width, height); /设置生成的照片大小 params.se
10、tPictureSize(width, height); params.setPreviewFormat(ImageFormat.NV21); mCamera.setDisplayOrientation(90); /params.setRotation(90); /*List sizes = params.getSupportedPreviewSizes(); for(Camera.Size s : sizes) Log.e(LiveActivity, s.width + X + s.height); */ mCamera.setParameters(params); mCamera.setP
11、reviewCallback(this); mCamera.startPreview(); catch (Exception e) e.printStackTrace(); private void destroyCamera() if(mCamera = null) return; mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; 我们通过menu来做了一个摄像头的切换功能,这样子就可以前摄像头直播或者后摄像头直播了。 到时会在onPreviewFrame里面
12、获取到数据,然后交给jni进行一个编码的处理,然后就推流那么这里就会有一个非常重要的知识点了: 我们通过setPreviewFormat方法把预览的数据(onPreviewFrame方法参数里面的data)的格式设置成了ImageFormat.NV21,一般来说,常用的格式是NV21或者YV12,因为这两种格式被所有的摄像头支持的,Android默认是会设置NV21的。那么什么是NV21或YV12呢,其实这也是一种yuv格式的数据来的上一篇文章我们已经说过了,就是通过把yuv通过编码,然后再封装就可以得到一个视频文件了,但我们还需要对这种yuv进行一定的处理,因为yuv也是有不同的各类的。yu
13、v通常分成两大格式,一种是planar:把所有像素点的Y值全部存放在数组的最前面,然后再存放所有像素点的U值,最后再存放所有像素点的V值 还有一种就是packed:它是依次存放每一个像素点的YUV值的同时yuv还有不同的采样方式,一般主流的有三种:YUV4:4:4 每一个Y对应一组UV分量YUV4:2:2 每两个Y共用一组UV分量YUV4:2:0 每四个Y共用一组UV分量假设一张720 X 480的图片存储成yuv格式:YUV4:4:4 Y = 720 * 480 U = V = 720 * 480 所以整个数组的大小就是720 * 480 * 3YUV4:2:2 Y = 720 * 480
14、U = V = 720 * 480 / 2 所以整个数组的大小就是720 * 480 * 2YUV4:2:0 Y = 720 * 480 U = V = 720 * 480 / 4 所以整个数组的大小就是720 * 480 * 1.5NV21和YV12就是YUV4:2:0这种采样格式的,而且我们到时用FFmpeg编码采用的格式一般是AV_PIX_FMT_YUV420P,都是YUV4:2:0这种采样格式的但还是有一些差别的AV_PIX_FMT_YUV420P 格式是planar,就是先存全部的Y再存全部的U再存全部的V,采样格式4:2:0。存储格式类似 yyyyyyyy uu vv 这样NV21
15、 格式也是planar,采样格式也是4:2:0。存储格式类似 yyyyyyyy vu vuYV12 格式也是planar,采样格式也是4:2:0。存储格式类似 yyyyyyyy vv uu从上面可以看到,我们需要用的格式和预览的格式还是有些差别的,所以我们到时要处理一下。那么现在我们可以先把我们的Camera的功能给测试一下先的,看看能不能预览成功,但在运行前,还需要去AndroidManifest里面配置一下如果Camera模块测试没有问题的话,我们就可以来写native方法了,首先在LiveActivity里面定义好几个native方法 /* * 初始化编码的一些东西,比如编码器等 * p
16、aram width 编码视频的宽 * param height 编码视频的高 * return 0 成功 小于0失败 */ private native int streamerInit(int width, int height); /* * 对每一次预览的数据进行编码推流 * param data NV21格式的数据 * return 0成功,小于0失败 */ private native int streamerHandle(byte data); /* * 把缓冲帧的数据清空 * return 0成功,小于0失败 */ private native int streamerFlush
17、(); /* * 释放资源,比如编码器这些 * return 0成功,小于0失败 */private native int streamerRelease();定义完成native方法后,我们先把LiveActivity里面的逻辑给处理一下先。为了不影响UI线程(以后可能数据处理会有点多),我就使用了HandlerThread这个类来进行异步操作,先把类初始化 mHandlerThread = new HandlerThread(liveHandlerThread); mHandlerThread.start(); mHandler = new LiveHandler(this, mHandl
18、erThread.getLooper();LiveHandler是我定义在LiveActivity的静态内部类,用来进行异步操作的 private static class LiveHandler extends Handler private WeakReference mActivity; public LiveHandler(LiveActivity activity, Looper looper) super(looper); mActivity = new WeakReference(activity); Override public void handleMessage(Mess
19、age msg) super.handleMessage(msg); LiveActivity activity = mActivity.get(); if(activity = null) return; switch (msg.what) case STREAMER_INIT: break; case STREAMER_HANDLE: Bundle bundle = msg.getData(); if(bundle != null) byte data = bundle.getByteArray(frame_data); if(data != null & data.length 0) a
20、ctivity.streamerHandle(data); else Log.e(LiveActivity, byte data null); else Log.e(LiveActivity, bundle null); break; case STREAMER_FLUSH: activity.streamerFlush(); break; case STREAMER_RELEASE: activity.streamerRelease(); break; LiveActivity里面的逻辑主要是一些细节的处理,完整的代码就下面那样:package com.xiaoxiao.live;impor
21、t android.graphics.ImageFormat;import android.hardware.Camera;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.Menu;import
22、android.view.MenuItem;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.lang.ref.WeakReference;/* * Created by Administrator on 2017/2/20. */public class LiveActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PreviewCallback private static final
23、int STREAMER_INIT = 0; private static final int STREAMER_HANDLE = 1; private static final int STREAMER_RELEASE = 2; private static final int STREAMER_FLUSH = 3; private Camera mCamera; private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private int mCameraId = 0; private int widt
24、h = 720; private int height = 480; /* * 判断有没有初始化成功,不成功不不进行后续的编码处理 */ private int liveInitResult = -1; /* * 异步操作 */ private HandlerThread mHandlerThread; private LiveHandler mHandler; Override public void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.
25、activity_live); mSurfaceView = (SurfaceView) findViewById(R.id.live_sv_live); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.setFixedSize(width, height); mSurfaceHolder.addCallback(this); mHandlerThread = new HandlerThread(liveHandlerThread); mHandlerThread.start(); mHandler = new LiveHan
26、dler(this, mHandlerThread.getLooper(); Override public boolean onCreateOptionsMenu(Menu menu) getMenuInflater().inflate(R.menu.menu_live, menu); return true; Override public boolean onOptionsItemSelected(MenuItem item) if(item.getItemId() = R.id.checkable_menu) boolean isChecked = item.isChecked();
27、Log.e(LiveActivity, checked: + isChecked); item.setChecked(!isChecked); mCameraId = 1 - mCameraId; destroyCamera(); initCamera(); return true; return super.onOptionsItemSelected(item); Override public void onPreviewFrame(byte data, Camera camera) /* * 如果初始化成功,那就把数据发送到Handler,然后再调用native方法 */ if(live
28、InitResult = 0 & data != null & data.length 0) Message msg = Message.obtain(); Bundle bundle = new Bundle(); bundle.putByteArray(frame_data, data); msg.what = STREAMER_HANDLE; msg.setData(bundle); mHandler.sendMessage(msg); Override public void surfaceCreated(SurfaceHolder holder) /* * 在surface创建的时候进行初始化,如果失败了,也是需要释放已经开辟了
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1