您当前的位置: 首页 >  音视频

命运之手

暂无认证

  • 1浏览

    0关注

    747博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Android音视频开发】【011】摄像头YUV数据转H264,并进行播放

命运之手 发布时间:2019-12-04 20:13:25 ,浏览量:1

本篇博客主要介绍以下内容

  • 摄像头打开和帧回调
  • 采集YUV数据,编码为H264
  • H264数据解码播放
  • H264数据写入文件

其实摄像头的数据,是可以直接通过SurfaceView进行预览的

这里绕了一个弯,纯粹是为了给大家演示YUV编码,H264解码,H264播放的所有知识点

毕竟,在实际应用中,提供数据的可能不是摄像头,有很多种可能性,所以我们有必要掌握每种方法

如果仅仅是为了显示摄像头视频,请看这个专栏的前几篇博客

实现代码


	//摄像头管理类
	@SuppressWarnings("all")
	public class Cameras {
	
	    //打卡正面相机
	    public static Camera openFrontCamera() {
	        try {
	            int cameraCount = Camera.getNumberOfCameras();
	            for (int i = 0; i = 0) {
	            ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
	            byte[] outData = new byte[bufferInfo.size];
	            outputBuffer.get(outData);
	
	            if (spNalu != null) {
	                System.arraycopy(outData, 0, output, pos, outData.length);
	                pos += outData.length;
	            } else {
	                //保存SPS和PPS
	                ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData);
	                if (spsPpsBuffer.getInt() == 0x00000001) {
	                    spNalu = new byte[outData.length];
	                    System.arraycopy(outData, 0, spNalu, 0, outData.length);
	                } else return -1;
	            }
	
	            mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
	            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
	        }
	
	        //判断NALU类型是否为IDR
	        if (output[4] == 0x65) {
	            //给IDR帧添加SPS和PPS
	            System.arraycopy(output, 0, yuv, 0, pos);
	            System.arraycopy(spNalu, 0, output, 0, spNalu.length);
	            System.arraycopy(yuv, 0, output, spNalu.length, pos);
	            pos += spNalu.length;
	        }
	
	        return pos;
	    }
	
	    //判断是否存在指定OUTPUT_MEDIA_FORMAT的编码器
	    private static MediaCodecInfo selectMediaCodec() {
	        int numCodecs = MediaCodecList.getCodecCount();
	        for (int i = 0; i = 0) {
	            mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
	            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
	        }
	        return true;
	    }
	
	    private long computePresentationTime(long frameIndex) {
	        return 132 + frameIndex * 1000000 / frameRate;
	    }
	
	}


	//处理编码出的H264数据
	//在work方法中定义自己的业务代码,比如拿来播放,写文件,推流等
	public abstract class AvcWorker {
	
	    Queue queue = new LinkedList();
	
	    Lock lock = new ReentrantLock();
	    boolean working = false;
	
	    public AvcWorker() {
	        Threads.post(() -> {
	            onInit();
	            while (true) {
	                lock.lock();
	                if (working) work();
	                else Threads.sleep(500);
	                lock.unlock();
	            }
	        });
	    }
	
	    //添加NALU到队列
	    public void enqueue(byte[] nalu, int len) {
	        ByteBuffer buffer = ByteBuffer.allocate(len);
	        buffer.put(nalu, 0, len);
	        queue.offer(buffer);
	    }
	
	    //NALU出列
	    public ByteBuffer dequeue() {
	        return queue.poll();
	    }
	
	    public AvcWorker start() {
	        lock.lock();
	        onStart();
	        working = true;
	        lock.unlock();
	        return this;
	    }
	
	    public AvcWorker stop() {
	        lock.lock();
	        queue.clear();
	        onStop();
	        working = false;
	        lock.unlock();
	        return this;
	    }
	
	    protected abstract void work();
	
	    protected abstract void onInit();
	
	    protected abstract void onStart();
	
	    protected abstract void onStop();
	}


	@SuppressWarnings("all")
	public class LoginActivity extends CommonActivity {
	
	    @BindView(R.id.v_surface_view)
	    SurfaceView previewSurfaceView;
	    @BindView(R.id.v_surface_view_2)
	    SurfaceView avcDecodeSurfaceView;
	
	    Camera camera;
	
	    int w = 640;
	    int h = 480;
	
	    AvcEncoder avcEncoder;
	    AvcDecoder avcDecoder;
	    AvcWorker avcWorker;
	
	    RandomAccessFile raf;
	
	    byte[] h264 = new byte[1024 * 1024];
	
	    protected void create() {
	        setContentView(R.layout.activity_main);
	        ButterKnife.bind(this, ctx);
	        requestAllPermissionWithCallback();
	    }
	
	    @Override
	    protected void onPermissionOk() {
	        //等待Surface创建完毕,再打开摄像头
	        postLater(this::openCamera, 2000);
	    }
	
	    @SneakyThrows
	    private void openCamera() {
	        //初始化编码器解码器
	        avcEncoder = AvcEncoder.create(w, h, 30, 2500000).start();
	        avcDecoder = AvcDecoder.create(w, h, 30, avcDecodeSurfaceView.getHolder().getSurface()).start();
	
	        //创建H264工作线程
	        avcWorker = new AvcWorker() {
	            @Override
	            @SneakyThrows
	            protected void work() {
	                ByteBuffer byteBuffer = dequeue();
	                if (byteBuffer == null) {
	                    Threads.sleep(100);
	                    return;
	                }
	
	                //ByteBuffer转byte[]
	                int length = byteBuffer.capacity();
	                byte[] buffer = new byte[length];
	                System.arraycopy(byteBuffer.array(), 0, buffer, 0, length);
	
	                //解码播放
	                avcDecoder.decodeNalu(buffer);
	
	                //写入H264文件
	                raf.seek(raf.length());
	                raf.write(buffer);
	            }
	
	            @Override
	            @SneakyThrows
	            protected void onInit() {
	                File file = new File("/sdcard/cam.h264");
	                if (file.exists()) file.delete();
	                raf = new RandomAccessFile(file, "rw");
	            }
	
	            @Override
	            protected void onStart() {
	
	            }
	
	            @Override
	            @SneakyThrows
	            protected void onStop() {
	
	            }
	        }.start();
	
	        //打卡摄像头
	        camera = Cameras.openBackCamera();
	        Camera.Parameters parameters = camera.getParameters();
	        parameters.setPreviewSize(w, h);
	        parameters.setPreviewFrameRate(30);
	        parameters.setPreviewFormat(ImageFormat.NV21);
	        camera.setParameters(parameters);
	        camera.setPreviewDisplay(previewSurfaceView.getHolder());
	        camera.setPreviewCallback((nv21, camera) -> {
	            //NV21转成H264,保存至队列,等待线程处理
	            int len = avcEncoder.encodeFrame(nv21, h264);
	            if (len > 0) avcWorker.enqueue(h264, len);
	        });
	        camera.startPreview();
	    }
	
	
	}


	package com.easing.commons.android.MediaCodec;
	
	import android.graphics.Bitmap;
	
	//YUV各种子格式之间进行转换
	//未经严格测试,部分方法可能有问题
	@SuppressWarnings("all")
	public class YUVHandler {
	
	    //计算YUV的buffer长度
	    public static int yuvBufferLength(int width, int height) {
	        int stride = (int) Math.ceil(width / 16.0) * 16;
	        int y_size = stride * height;
	        int c_stride = (int) Math.ceil(width / 32.0) * 16;
	        int c_size = c_stride * height / 2;
	        return y_size + c_size * 2;
	    }
	
	    //镜像化YUV帧
	    public static void yuvMirror(byte[] yuv, int w, int h) {
	        int a, b;
	        byte temp;
	
	        //镜像化Y
	        for (int i = 0; i             
关注
打赏
1654938663
查看更多评论
0.1007s