@Tyhj
2017-04-21T14:40:57.000000Z
字数 6272
阅读 1728
Android
原文:https://www.zybuluo.com/Tyhj/note/730409
直播、视频聊天这种东西遍地都是,但是实现起来真的不是很容易。最近养了一只狗,想试试用一台手机一直拍摄,另一只手机来接收视频。
首先找了找怎么实现,并没有找到完整的教程。但是想法有了,就是先打开手机摄像头,然后获取摄像头的每一帧的数据,然后发送给服务器,服务器再给另一台手机,手机收到视频后再显示到SurfaceView上面。逻辑应该是没有问题的,我试了也可以,就是数据传输有问题,但是我一个搞Android的服务器菜一点就菜一点,先实现了,优化再谈。
首先打开手机摄像头,将摄像头的数据展示到SurfaceView上面。
void initCamera(boolean what){
try {
int position=0;
int rotate=0;
if(what) {//what就是指摄像头的正反
position = 1;//选择打开的摄像头
rotate=180;//这里就是让显示的画面旋转一下
}
destroyCamera();
camera=Camera.open(position);
camera.setPreviewDisplay(mSurfaceHolder);
Camera.Parameters parameter=camera.getParameters();
parameter.setPreviewFormat(ImageFormat.NV21);
List<Camera.Size> sizeList=parameter.getSupportedPreviewSizes();
for(int i=0;i<sizeList.size();i++){
Log.e("支持的尺寸大小","x:"+sizeList.get(i).width+" y"+sizeList.get(i).height);
}
parameter.setPreviewSize(320,240);
//设置预览方向
camera.setDisplayOrientation(90);
//设置拍照之后图片方向
parameter.setRotation(rotate);
camera.setParameters(parameter);
camera.setPreviewCallback(back);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
//这里是处理每一帧的图象然后发送出去,可以看见在这里可以获取到每一帧的bitmap,干很多事情,什么美颜呀,水印呀都可以玩,然后我是拿到数据直接转化为Base64字符串发送出去的,这样应该是很蠢的,但是技术有限,加上我之前有个写好的后台程序就可以发字符串,就将就一下。
//处理每一帧
Camera.PreviewCallback back=new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if(i/30!=0){
return;
}
Camera.Size size=camera.getParameters().getPreviewSize();
YuvImage image=new YuvImage(data,ImageFormat.NV21,size.width,size.height,null);
Bitmap bitmap=null;
String string=null;
if(image!=null){
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0,0,size.width,size.height),20,stream);
//bitmap= BitmapFactory.decodeByteArray(stream.toByteArray(),0,stream.size());
//加水印
//bitmap= BitmapUtils.addLogo(MainActivity.this,bitmap,"小萨专用",R.drawable.watermark);
byte[] bytes = stream.toByteArray();
string = Base64.encodeToString(bytes, Base64.NO_WRAP);
string.replace("\n","小萨");
sendMsg(string);
stream.flush();
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
当切换摄像头的时候要先关闭摄像头
private void destroyCamera(){
if(camera!=null) {
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
还有其他一些问题:
对焦后拍照:
void fab(){
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if(success){
camera.takePicture(null,null,JpgmPicture);
}
}
});
}
//保存拍摄的图片
private Camera.PictureCallback JpgmPicture=new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File file=getOutputMediaFile();
if(file==null)
return;
try {
FileOutputStream fos=new FileOutputStream(file);
fos.write(data);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
}
};
//创建一个文件来保存拍摄的图片
private static File getOutputMediaFile(){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
return mediaFile;
}
添加水印
//添加水印
public static Bitmap addLogo(Context context, Bitmap bitmap, String markText, int markBitmapId){
// 当水印文字与水印图片都没有的时候,返回原图
if (TextUtils.isEmpty(markText) && markBitmapId == 0) {
return bitmap;
}
// 获取图片的宽高
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
// 创建一个和图片一样大的背景图
Bitmap bmp = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
// 画背景图
canvas.drawBitmap(bitmap, 0, 0, null);
//-------------开始绘制文字-------------------------------
// 文字开始的坐标,默认为左上角
float textX = 0;
float textY = 0;
if (!TextUtils.isEmpty(markText)) {
// 创建画笔
Paint mPaint = new Paint();
// 文字矩阵区域
Rect textBounds = new Rect();
// 获取屏幕的密度,用于设置文本大小
//float scale = context.getResources().getDisplayMetrics().density;
// 水印的字体大小
//mPaint.setTextSize((int) (11 * scale));
mPaint.setTextSize(20);
// 文字阴影
mPaint.setShadowLayer(0.5f, 0f, 1f, Color.BLACK);
// 抗锯齿
mPaint.setAntiAlias(true);
// 水印的区域
mPaint.getTextBounds(markText, 0, markText.length(), textBounds);
// 水印的颜色
mPaint.setColor(Color.WHITE);
//当图片大小小于文字水印大小的3倍的时候,不绘制水印
if (textBounds.width() > bitmapWidth / 3 || textBounds.height() > bitmapHeight / 3) {
return bitmap;
}
// 文字开始的坐标
textX = bitmapWidth - textBounds.width() - 10;//这里的-10和下面的+6都是微调的结果
textY = bitmapHeight - textBounds.height() + 6;
// 画文字
canvas.drawText(markText, textX, textY, mPaint);
}
//------------开始绘制图片-------------------------
if (markBitmapId != 0) {
// 载入水印图片
Bitmap markBitmap = BitmapFactory.decodeResource(context.getResources(), markBitmapId);
// 如果图片的大小小于水印的3倍,就不添加水印
if (markBitmap.getWidth() > bitmapWidth / 3 || markBitmap.getHeight() > bitmapHeight / 3) {
return bitmap;
}
int markBitmapWidth = markBitmap.getWidth();
int markBitmapHeight = markBitmap.getHeight();
// 图片开始的坐标
float bitmapX = (float) (bitmapWidth - markBitmapWidth - 10);//这里的-10和下面的-20都是微调的结果
float bitmapY = (float) (textY - markBitmapHeight - 20);
// 画图
canvas.drawBitmap(markBitmap, bitmapX, bitmapY, null);
}
//保存所有元素
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
return bmp;
}
然后那个接收视频信息
//收到信息后展示
public void sendBordcast(String messge) {
Log.e("长度:", messge.length() + "");
Bitmap bitmap = stringtoBitmap(messge);
if (bitmap == null)
return;
int orientation = bitmap.getHeight() < bitmap.getWidth() ? 90 : 0;
Canvas canvas = mSurfaceHolder.lockCanvas();
if (orientation != 0){
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, true);
}
canvas.drawBitmap(bitmap, 0, 0, new Paint());
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
//把我发送的字符串转成bitmap
public Bitmap stringtoBitmap(String string) {
string.replace("小萨", "\n");
// 将字符串转换成Bitmap类型
Bitmap bitmap = null;
try {
byte[] bitmapArray = null;
bitmapArray = Base64.decode(string, Base64.NO_WRAP);
bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0,
bitmapArray.length);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
基本上就是这样子,然后数据传输肯定很菜的,要优化,我也把图象数据压缩了,压缩方法也是最垃圾的,然后获取的图象我也是设置成了最小尺寸,还不是获取每一帧的数据,故意丢了一些数据。这样的话我的那个学生优惠的阿里服务器也可以带的动,也没什么问题,延迟有一点。就是图片实在是太小了。对了,声音也是没有的。有时间再改。
求建议,tyhj5@qq.com