最纯粹的直播技术实战02Camera的处理以及推流.docx

上传人:b****5 文档编号:5040426 上传时间:2022-12-12 格式:DOCX 页数:30 大小:815.75KB
下载 相关 举报
最纯粹的直播技术实战02Camera的处理以及推流.docx_第1页
第1页 / 共30页
最纯粹的直播技术实战02Camera的处理以及推流.docx_第2页
第2页 / 共30页
最纯粹的直播技术实战02Camera的处理以及推流.docx_第3页
第3页 / 共30页
最纯粹的直播技术实战02Camera的处理以及推流.docx_第4页
第4页 / 共30页
最纯粹的直播技术实战02Camera的处理以及推流.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

最纯粹的直播技术实战02Camera的处理以及推流.docx

《最纯粹的直播技术实战02Camera的处理以及推流.docx》由会员分享,可在线阅读,更多相关《最纯粹的直播技术实战02Camera的处理以及推流.docx(30页珍藏版)》请在冰豆网上搜索。

最纯粹的直播技术实战02Camera的处理以及推流.docx

最纯粹的直播技术实战02Camera的处理以及推流

最纯粹的直播技术实战02-Camera的处理以及推流

我们在MainActivity里面添加两个Button,一个是直播的,一个是看直播的

xmlversion="1.0"encoding="utf-8"?

>

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"

android:

paddingRight="@dimen/activity_horizontal_margin"

android:

paddingTop="@dimen/activity_vertical_margin"

android:

orientation="vertical"

tools:

context="com.xiaoxiao.live.MainActivity">

android:

id="@+id/main_tv_info"

android:

layout_width="wrap_content"

android:

layout_height="wrap_content"

android:

visibility="gone"

android:

text="HelloWorld!

"/>

android:

id="@+id/main_bt_live"

android:

layout_width="match_parent"

android:

layout_height="wrap_content"

android:

text="我要直播"/>

android:

id="@+id/main_bt_watch"

android:

layout_width="match_parent"

android:

layout_height="wrap_content"

android:

text="看直播"/>

我们把上一次的测试TextView注释掉了,然后新建了一个LiveActivity来处理Camera以及推流,主要就是展示直播。

后续还会有看直播的处理,就要就是拉流以及视频的播放了

接下来就要在LiveActivity里面处理一下Camera的东西了,首先要在LiveActivity的layout里面添加一个SurfaceView

xmlversion="1.0"encoding="utf-8"?

>

android:

orientation="vertical"android:

layout_width="match_parent"

android:

layout_height="match_parent">

android:

id="@+id/live_sv_live"

android:

layout_width="match_parent"

android:

layout_height="match_parent"/>

因为不是拍照,所以Camera的处理就会显得比较的简单了,

packagecom.xiaoxiao.live;

importandroid.graphics.ImageFormat;

importandroid.hardware.Camera;

importandroid.os.Bundle;

importandroid.support.v7.app.AppCompatActivity;

importandroid.util.Log;

importandroid.view.Menu;

importandroid.view.MenuItem;

importandroid.view.SurfaceHolder;

importandroid.view.SurfaceView;

/**

*CreatedbyAdministratoron2017/2/20.

*/

publicclassLiveActivityextendsAppCompatActivityimplementsSurfaceHolder.Callback,Camera.PreviewCallback{

privateCameramCamera;

privateSurfaceViewmSurfaceView;

privateSurfaceHoldermSurfaceHolder;

privateintmCameraId=0;

privateintwidth=720;

privateintheight=480;

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_live);

mSurfaceView=(SurfaceView)findViewById(R.id.live_sv_live);

mSurfaceHolder=mSurfaceView.getHolder();

mSurfaceHolder.setFixedSize(width,height);

mSurfaceHolder.addCallback(this);

}

@Override

publicbooleanonCreateOptionsMenu(Menumenu){

getMenuInflater().inflate(R.menu.menu_live,menu);

returntrue;

}

@Override

publicbooleanonOptionsItemSelected(MenuItemitem){

if(item.getItemId()==R.id.checkable_menu){

booleanisChecked=item.isChecked();

Log.e("LiveActivity","checked:

"+isChecked);

item.setChecked(!

isChecked);

mCameraId=1-mCameraId;

destroyCamera();

initCamera();

returntrue;

}

returnsuper.onOptionsItemSelected(item);

}

@Override

publicvoidonPreviewFrame(byte[]data,Cameracamera){

}

@Override

publicvoidsurfaceCreated(SurfaceHolderholder){

initCamera();

}

@Override

publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intwidth,intheight){

}

@Override

publicvoidsurfaceDestroyed(SurfaceHolderholder){

destroyCamera();

}

privatevoidinitCamera(){

try{

mCamera=Camera.open(mCameraId);

mCamera.setPreviewDisplay(mSurfaceHolder);

Camera.Parametersparams=mCamera.getParameters();

//设置预览大小

params.setPreviewSize(width,height);

//设置生成的照片大小

params.setPictureSize(width,height);

params.setPreviewFormat(ImageFormat.NV21);

mCamera.setDisplayOrientation(90);

//params.setRotation(90);

/*Listsizes=params.getSupportedPreviewSizes();

for(Camera.Sizes:

sizes){

Log.e("LiveActivity",s.width+"X"+s.height);

}*/

mCamera.setParameters(params);

mCamera.setPreviewCallback(this);

mCamera.startPreview();

}catch(Exceptione){

e.printStackTrace();

}

}

privatevoiddestroyCamera(){

if(mCamera==null){

return;

}

mCamera.setPreviewCallback(null);

mCamera.stopPreview();

mCamera.release();

mCamera=null;

}

}

我们通过menu来做了一个摄像头的切换功能,这样子就可以前摄像头直播或者后摄像头直播了。

到时会在onPreviewFrame里面获取到数据,然后交给jni进行一个编码的处理,然后就推流

那么这里就会有一个非常重要的知识点了:

我们通过setPreviewFormat方法把预览的数据(onPreviewFrame方法参数里面的data)的格式设置成了ImageFormat.NV21,一般来说,常用的格式是NV21或者YV12,因为这两种格式被所有的摄像头支持的,Android默认是会设置NV21的。

那么什么是NV21或YV12呢,其实这也是一种yuv格式的数据来的

上一篇文章我们已经说过了,就是通过把yuv通过编码,然后再封装就可以得到一个视频文件了,但我们还需要对这种yuv进行一定的处理,因为yuv也是有不同的各类的。

yuv通常分成两大格式,一种是planar:

把所有像素点的Y值全部存放在数组的最前面,然后再存放所有像素点的U值,最后再存放所有像素点的V值

还有一种就是packed:

它是依次存放每一个像素点的YUV值的

同时yuv还有不同的采样方式,一般主流的有三种:

YUV4:

4:

4每一个Y对应一组UV分量

YUV4:

2:

2每两个Y共用一组UV分量

YUV4:

2:

0每四个Y共用一组UV分量

假设一张720X480的图片存储成yuv格式:

YUV4:

4:

4Y=720*480U=V=720*480所以整个数组的大小就是720*480*3

YUV4:

2:

2Y=720*480U=V=720*480/2所以整个数组的大小就是720*480*2

YUV4:

2:

0Y=720*480U=V=720*480/4所以整个数组的大小就是720*480*1.5

NV21和YV12就是YUV4:

2:

0这种采样格式的,而且我们到时用FFmpeg编码采用的格式一般是AV_PIX_FMT_YUV420P,都是YUV4:

2:

0这种采样格式的

但还是有一些差别的

AV_PIX_FMT_YUV420P格式是planar,就是先存全部的Y再存全部的U再存全部的V,采样格式4:

2:

0。

存储格式类似yyyyyyyyuuvv这样

NV21格式也是planar,采样格式也是4:

2:

0。

存储格式类似yyyyyyyyvuvu

YV12格式也是planar,采样格式也是4:

2:

0。

存储格式类似yyyyyyyyvvuu

从上面可以看到,我们需要用的格式和预览的格式还是有些差别的,所以我们到时要处理一下。

那么现在我们可以先把我们的Camera的功能给测试一下先的,看看能不能预览成功,但在运行前,还需要去AndroidManifest里面配置一下

如果Camera模块测试没有问题的话,我们就可以来写native方法了,首先在LiveActivity里面定义好几个native方法

/**

*初始化编码的一些东西,比如编码器等

*@paramwidth编码视频的宽

*@paramheight编码视频的高

*@return0成功小于0失败

*/

privatenativeintstreamerInit(intwidth,intheight);

/**

*对每一次预览的数据进行编码推流

*@paramdataNV21格式的数据

*@return0成功,小于0失败

*/

privatenativeintstreamerHandle(byte[]data);

/**

*把缓冲帧的数据清空

*@return0成功,小于0失败

*/

privatenativeintstreamerFlush();

/**

*释放资源,比如编码器这些

*@return0成功,小于0失败

*/

privatenativeintstreamerRelease();

定义完成native方法后,我们先把LiveActivity里面的逻辑给处理一下先。

为了不影响UI线程(以后可能数据处理会有点多),我就使用了HandlerThread这个类来进行异步操作,先把类初始化

mHandlerThread=newHandlerThread("liveHandlerThread");

mHandlerThread.start();

mHandler=newLiveHandler(this,mHandlerThread.getLooper());

LiveHandler是我定义在LiveActivity的静态内部类,用来进行异步操作的

privatestaticclassLiveHandlerextendsHandler{

privateWeakReferencemActivity;

publicLiveHandler(LiveActivityactivity,Looperlooper){

super(looper);

mActivity=newWeakReference(activity);

}

@Override

publicvoidhandleMessage(Messagemsg){

super.handleMessage(msg);

LiveActivityactivity=mActivity.get();

if(activity==null){

return;

}

switch(msg.what){

caseSTREAMER_INIT:

break;

caseSTREAMER_HANDLE:

Bundlebundle=msg.getData();

if(bundle!

=null){

byte[]data=bundle.getByteArray("frame_data");

if(data!

=null&&data.length>0){

activity.streamerHandle(data);

}else{

Log.e("LiveActivity","bytedatanull");

}

}else{

Log.e("LiveActivity","bundlenull");

}

break;

caseSTREAMER_FLUSH:

activity.streamerFlush();

break;

caseSTREAMER_RELEASE:

activity.streamerRelease();

break;

}

}

}

LiveActivity里面的逻辑主要是一些细节的处理,完整的代码就下面那样:

packagecom.xiaoxiao.live;

importandroid.graphics.ImageFormat;

importandroid.hardware.Camera;

importandroid.os.Bundle;

importandroid.os.Handler;

importandroid.os.HandlerThread;

importandroid.os.Looper;

importandroid.os.Message;

importandroid.support.v7.app.AppCompatActivity;

importandroid.util.Log;

importandroid.view.Menu;

importandroid.view.MenuItem;

importandroid.view.SurfaceHolder;

importandroid.view.SurfaceView;

importjava.lang.ref.WeakReference;

/**

*CreatedbyAdministratoron2017/2/20.

*/

publicclassLiveActivityextendsAppCompatActivityimplementsSurfaceHolder.Callback,Camera.PreviewCallback{

privatestaticfinalintSTREAMER_INIT=0;

privatestaticfinalintSTREAMER_HANDLE=1;

privatestaticfinalintSTREAMER_RELEASE=2;

privatestaticfinalintSTREAMER_FLUSH=3;

 

privateCameramCamera;

privateSurfaceViewmSurfaceView;

privateSurfaceHoldermSurfaceHolder;

privateintmCameraId=0;

privateintwidth=720;

privateintheight=480;

/**

*判断有没有初始化成功,不成功不不进行后续的编码处理

*/

privateintliveInitResult=-1;

/**

*异步操作

*/

privateHandlerThreadmHandlerThread;

privateLiveHandlermHandler;

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_live);

mSurfaceView=(SurfaceView)findViewById(R.id.live_sv_live);

mSurfaceHolder=mSurfaceView.getHolder();

mSurfaceHolder.setFixedSize(width,height);

mSurfaceHolder.addCallback(this);

mHandlerThread=newHandlerThread("liveHandlerThread");

mHandlerThread.start();

mHandler=newLiveHandler(this,mHandlerThread.getLooper());

}

@Override

publicbooleanonCreateOptionsMenu(Menumenu){

getMenuInflater().inflate(R.menu.menu_live,menu);

returntrue;

}

@Override

publicbooleanonOptionsItemSelected(MenuItemitem){

if(item.getItemId()==R.id.checkable_menu){

booleanisChecked=item.isChecked();

Log.e("LiveActivity","checked:

"+isChecked);

item.setChecked(!

isChecked);

mCameraId=1-mCameraId;

destroyCamera();

initCamera();

returntrue;

}

returnsuper.onOptionsItemSelected(item);

}

@Override

publicvoidonPreviewFrame(byte[]data,Cameracamera){

/**

*如果初始化成功,那就把数据发送到Handler,然后再调用native方法

*/

if(liveInitResult==0&&data!

=null&&data.length>0){

Messagemsg=Message.obtain();

Bundlebundle=newBundle();

bundle.putByteArray("frame_data",data);

msg.what=STREAMER_HANDLE;

msg.setData(bundle);

mHandler.sendMessage(msg);

}

}

@Override

publicvoidsurfaceCreated(SurfaceHolderholder){

/**

*在surface创建的时候进行初始化,如果失败了,也是需要释放已经开辟了

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 广告传媒

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1