时光倒流策略

时光倒流策略

时光倒流原理

把一个视频分为20帧一段(一个队列),从末尾开始显示(队列满20帧开始播放,具体队列大小可以调整)
1、创建两个队列,一个队列解码,一个队列播放,解码和播放分开两个线程
2、seek末尾开始解码存储最末尾队列达到满20帧(解码队列),满后转换到播放队列(解码队列全部出队),开始末尾帧播放
3、播放时候,如果解码线程判断解码队列是空的,seek到上一段队列开始存储第一个pts前20帧(最好方式提前获取视频gop每帧的pts能更准确seek)
4、按上面2、3方式反复就可以每帧倒流
说明:条件,获取时间总帧数(可以进行分段),获得每20一组的第一个帧时间戳(视频可能存在b帧所以可能无法获取,如果视频gop时间戳肯定是可以)(为了精确seek和保存队列)

环境 android系统

MediaExtractor、MediaCodec、ImageReader实现自己解码后yuv队列,按分辨率来设置队列大小防止崩溃(测试720p的可以20帧)
线程控制3条:MediaExtractor、MediaCodec实现解码视频线程;ImageReader是openGL获取yuv(mOnImageAvailableListener监听获取),videoRender渲染视频把yuv数据渲染SurfaceView的openGL上

        /**
         * 图像数据回调
         * ImageReader这种方式时间有的长
         */
        long csss=0;
        private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
                = new ImageReader.OnImageAvailableListener() {

            @Override
            public void onImageAvailable(ImageReader reader) {

                long cu=0;
                if (csss==0){
                    csss=System.currentTimeMillis();
                }else{
                    cu=System.currentTimeMillis()-csss;
                    csss=System.currentTimeMillis();
                }
                try (Image image = reader.acquireNextImage()) {
                    Image.Plane[] planes = image.getPlanes();
//                for (int i = 0; i < planes.length; i++) {
//                    ByteBuffer iBuffer = planes[i].getBuffer();
//                    int iSize = iBuffer.remaining();
//                    Log.e(TAG, "pixelStride  " + planes[i].getPixelStride());
//                    Log.e(TAG, "rowStride   " + planes[i].getRowStride());
//                    Log.e(TAG, "width  " + image.getWidth());
//                    Log.e(TAG, "height  " + image.getHeight());
//                    Log.e(TAG, "Finished reading data from plane  " + i);
//                }

                    if (planes.length > 0) {
//                    ByteBuffer buffer = planes[0].getBuffer();
                        //数据格式转换 YUV_420 ===》NV21
                        long  ss=System.currentTimeMillis();
                        byte[] data = ImageUtils.getDataFromImage2(image,1);
                        if (cache!=null){
                            synchronized (object) {
                                if (!cache.isNotSleep){
                                    syncPlay=false;
                                }
                                cache.yuvData=data;
                                queue.offer(cache);
                            }
                            ss=System.currentTimeMillis()-ss;
                            Log.e("======",ss+"====="+cu+"=====cu===="+cache.getCurrentSampleTime());
                            cache=null;
                        }
                    }

                }catch (Exception e){

                }
                notifymSync();
            }

        };
        /**
         * 刷新视频渲染
         * @param videoCache  视频数据
         * @param mediaNative  nativewindow添数据
         */
        private  void videoRefresh(VideoCache videoCache,MediaNative mediaNative) {
            if (videoCache != null&&videoCache.yuvData!=null) {
                int outputBufferIndex = videoCache.getOutputBufferIndex();
                long distant = videoCache.getDistant();
                if (distant > 0&&!videoCache.isNotSleep) {
                    if (audioThread != null && mediaEditor.isHasAudio()) {
                        long dif = (audioThread.currentPosition - currentPosition) / AV_TIME_BASE;
                        if (audioThread.currentPosition < mediaEditor.getAudioDuration()) {
                            if (dif >= 20) {
                                distant = 0;
                            } else if (dif > 10) {
                                distant = 5;
                            }
                        }

                    }
                    try {
                        Thread.sleep(distant);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                if (outputBufferIndex >= 0) {
                    if (videoCache.render) {
                        mediaNative.setYUV(videoCache.yuvData, videoCache.strideHeiht,
                                videoCache.strideWidth, videoCache.height, videoCache.width);
                    }
                }
            }
        }

时光倒流实现

时光倒流视频处理逻辑过程部分代码

    class VideoDecodeThread extends Thread{
        private volatile boolean threadRun=true;
        private LinkedList<VideoCache> queue = new LinkedList<>();
        private Queue<Integer> seekQueue = new LinkedList<>();
        private long currentPosition = 0;
        Object object = new Object();
        Object objectQueue = new Object();
        public volatile boolean isSeeking = false;
        private boolean playing = false;
        private MediaExtractor mediaExtractor;
        private int sameSmc = -1;
        private long duration;
        public boolean isReleaseEnd;
        private volatile boolean syncPlay;//视频解码比较慢同步视频播放第一帧同事开始
        private ImageReader mImageReader;
        private HandlerThread mImageReaderThread;
        private Handler mImageReaderHandler;
        protected final Object mSync = new Object();
        private  VideoCache cache;

        public void notifymSync() {
            synchronized (mSync) {
                try {
                    mSync.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 图像数据回调
         * ImageReader这种方式时间有的长
         */
        long csss=0;
        private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
                = new ImageReader.OnImageAvailableListener() {

            @Override
            public void onImageAvailable(ImageReader reader) {

                long cu=0;
                if (csss==0){
                    csss=System.currentTimeMillis();
                }else{
                    cu=System.currentTimeMillis()-csss;
                    csss=System.currentTimeMillis();
                }
                try (Image image = reader.acquireNextImage()) {
                    Image.Plane[] planes = image.getPlanes();
//                for (int i = 0; i < planes.length; i++) {
//                    ByteBuffer iBuffer = planes[i].getBuffer();
//                    int iSize = iBuffer.remaining();
//                    Log.e(TAG, "pixelStride  " + planes[i].getPixelStride());
//                    Log.e(TAG, "rowStride   " + planes[i].getRowStride());
//                    Log.e(TAG, "width  " + image.getWidth());
//                    Log.e(TAG, "height  " + image.getHeight());
//                    Log.e(TAG, "Finished reading data from plane  " + i);
//                }

                    if (planes.length > 0) {
//                    ByteBuffer buffer = planes[0].getBuffer();
                        //数据格式转换 YUV_420 ===》NV21
                        long  ss=System.currentTimeMillis();
                        byte[] data = ImageUtils.getDataFromImage2(image,1);
                        if (cache!=null){
                            synchronized (object) {
                                if (!cache.isNotSleep){
                                    syncPlay=false;
                                }
                                cache.yuvData=data;
                                queue.offer(cache);
                            }
                            ss=System.currentTimeMillis()-ss;
                            Log.e("======",ss+"====="+cu+"=====cu===="+cache.getCurrentSampleTime());
                            cache=null;
                        }
                    }

                }catch (Exception e){

                }
                notifymSync();
            }

        };

        private void prepareImageReader() {
            mImageReaderThread = new HandlerThread("CameraThread");
            mImageReaderThread.start();
            mImageReaderHandler = new Handler(mImageReaderThread.getLooper());
            mImageReader = ImageReader.newInstance(mediaEditor.getWidth(), mediaEditor.getHeight(),
                    ImageFormat.YUV_420_888, /* maxImages */ 2);


            mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mImageReaderHandler);
        }

        /**
         * 刷新视频渲染
         * @param videoCache  视频数据
         * @param mediaNative  nativewindow添数据
         */
        private  void videoRefresh(VideoCache videoCache,MediaNative mediaNative) {
            if (videoCache != null&&videoCache.yuvData!=null) {
                int outputBufferIndex = videoCache.getOutputBufferIndex();
                long distant = videoCache.getDistant();
                if (distant > 0&&!videoCache.isNotSleep) {
                    if (audioThread != null && mediaEditor.isHasAudio()) {
                        long dif = (audioThread.currentPosition - currentPosition) / AV_TIME_BASE;
                        if (audioThread.currentPosition < mediaEditor.getAudioDuration()) {
                            if (dif >= 20) {
                                distant = 0;
                            } else if (dif > 10) {
                                distant = 5;
                            }
                        }

                    }
                    try {
                        Thread.sleep(distant);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                if (outputBufferIndex >= 0) {
                    if (videoCache.render) {
                        mediaNative.setYUV(videoCache.yuvData, videoCache.strideHeiht,
                                videoCache.strideWidth, videoCache.height, videoCache.width);
                    }
                }
            }
        }

        /**
         * 刷新视频线程方法
         */
        private void videoRender() {
            videoRefreshHandler.post(new Runnable() {
                @Override
                public void run() {
                    while (surface == null) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (!threadRun) return;
                    }
                    while (threadRun) {
                        while (!queue.isEmpty()) {//seekTo优先级大于播放
                            if (!threadRun){
                                queue.clear();
                                break;
                            }

                            if (!playing&&!isSeeking) {//播放、暂停
                                try {
                                    Thread.sleep(5);
                                } catch (InterruptedException e) {
                                }
                                continue;
                            }

                            VideoCache videoCache;
                            synchronized (objectQueue) {
                                videoCache = queue.poll();
                            }
                            if (videoCache==null){
                                continue;
                            }

                            if (!videoCache.isNotSleep){
                                currentPosition = videoCache.getCurrentSampleTime();
                            }

                            long audioTime=0;
                            if (audioThread!=null){
                                audioTime=audioThread.getCurrentPosition();
                            }
                            Log.e("=====",queue.size()+"==="+mediaEditor.getVideoDuration()+"=====videotime===="+getCurrentPosition()+"==="+audioTime);
                            videoRefresh( videoCache, mediaNative);
                        }
                    }
                    queue.clear();
                }
            });

        }

        public void seekTo(int msc) {
            if (mediaEditor==null)return;
            if (msc * AV_TIME_BASE > mediaEditor.getVideoDuration()) {
                msc = (int) (mediaEditor.getVideoDuration() / AV_TIME_BASE);
            }
            if (sameSmc == msc || msc == currentPosition / AV_TIME_BASE) return;//去掉相同
            sameSmc = msc;
            currentPosition = msc * AV_TIME_BASE;
            isSeeking = true;
            synchronized (object) {
                if (seekQueue.size() > 0) {
                    seekQueue.clear();
                    seekQueue.offer(msc);
                } else {
                    seekQueue.offer(msc);
                }
            }
        }

        public void play() {
            sameSmc = -1;
            if (!playing){
                syncPlay= true;
            }
            playing = true;
        }

        public void pause() {
            sameSmc = -1;
            playing = false;
        }

        public int getCurrentPosition() {
            return (int) (currentPosition / AV_TIME_BASE);
        }

        public int getDuration() {
            return (int) (duration / AV_TIME_BASE);
        }

        public boolean isPlaying() {
            return playing;
        }

        public void release() {
            threadRun = false;
            notifymSync();
        }

        public boolean isReleaseEnd() {
            return isReleaseEnd;
        }
        @Override
        public void run() {
            isReleaseEnd = false;
            try {
                videoDecode();
            }catch (Exception e){
                MediaCodeImageReaderPlayer.this.stop();
                e.printStackTrace();
            }

            isReleaseEnd = true;
        }


        void videoDecode(){
            threadRun = true;
            mediaExtractor = mediaEditor.getVideoExtractor();
            duration = mediaEditor.getDuration();
            videoRender();
            prepareImageReader();
            MediaCodec mediaCodec = null;
            try {
                mediaCodec = MediaCodec.createDecoderByType(mediaEditor.getVideoMime());
                mediaCodec.configure(mediaEditor.getVideoMediaFormat(),
                        mImageReader.getSurface(), null, 0);//flag=1的时候为encode
                mediaCodec.start();
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (mediaCodec==null)return;


            MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
            final boolean[] readEnd = {false};
            long lastSampleTime = 0;
            int strideWidth = 0;
            int strideHeiht = 0;
            int keyColorFormat = 0;
            boolean isDecode=false;

            while (threadRun) {
                while (!seekQueue.isEmpty()) {//seekTo优先级大于播放
                    if (!threadRun) break;
                    int msc = 0;
                    if (!queue.isEmpty()) {
                        synchronized (objectQueue) {
                            queue.clear();
                        }
                    }
                    synchronized (object) {
                        msc = seekQueue.poll();
                    }
                    if (msc * AV_TIME_BASE > mediaEditor.getVideoDuration()) {
                        msc = (int) (mediaEditor.getVideoDuration() / AV_TIME_BASE);
                    }
                    Log.e("========", "============msc========" + msc);

                    mediaExtractor.seekTo(msc * AV_TIME_BASE, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
                    readEnd[0] = false;
                    if (isDecode){//鸿蒙系统部分视频如果没解码就flush,会解码失败直接解码结束
                        isDecode=false;
                        mediaCodec.flush();
                    }
                    videoBufferInfo = new MediaCodec.BufferInfo();
                    long current = System.currentTimeMillis();
                    while (true) {
                        if (!threadRun) break;
                        if (!readEnd[0]) {
                            readEnd[0] = BaseMediaPlayer.putBufferToCoderSeek(mediaEditor.getVideoMime(),mediaExtractor,
                                    mediaCodec, true, msc * AV_TIME_BASE);
                            isDecode=true;
                        }

                        int outputBufferIndex = mediaCodec.dequeueOutputBuffer(videoBufferInfo, BaseMediaPlayer.TIMEOUT_US);
                        if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                            keyColorFormat = mediaCodec.getOutputFormat().getInteger(MediaFormat.KEY_COLOR_FORMAT);
                        } else if (outputBufferIndex >= 0) {
                            if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {//最后一帧
                                currentPosition = mediaEditor.getVideoDuration();
                                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                                Log.e("========", "====current==end====="+msc);
                                break;
                            }else{
                                long currentSampleTime = videoBufferInfo.presentationTimeUs;
                                if (currentSampleTime >= msc * AV_TIME_BASE) {
                                    Log.e("========", "========"+msc+"============current======="+ current);
                                    cache = new VideoCache();
                                    cache.strideHeiht = mediaEditor.getHeight();
                                    cache.strideWidth = mediaEditor.getWidth();
                                    cache.height = mediaEditor.getHeight();
                                    cache.width = mediaEditor.getWidth();
                                    cache.isNotSleep = true;
                                    cache.setOutputBufferIndex(outputBufferIndex);
                                    cache.setCurrentSampleTime(videoBufferInfo.presentationTimeUs);
                                    lastSampleTime = videoBufferInfo.presentationTimeUs;
                                    mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
                                    break;
                                } else {
                                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                                }

                            }
                        }
                    }
                }
                if (!playing||queue.size()>=20) {
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                    }
                    continue;
                }
                long re=System.currentTimeMillis();
                sameSmc = -1;
                if (!readEnd[0]) {
                    readEnd[0] = BaseMediaPlayer.
                            putBufferToCoder(mediaExtractor, mediaCodec);
                    isDecode=true;
                }
                isSeeking = false;

                if (currentPosition>=mediaEditor.getVideoDuration()){//解码结束还有音频
                   syncPlay=false;
                }
                int outputBufferIndex = mediaCodec.dequeueOutputBuffer(videoBufferInfo,
                        BaseMediaPlayer.TIMEOUT_US);
                ByteBuffer outputBuffer;


                switch (outputBufferIndex) {
                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:

                        break;
                    case MediaCodec.INFO_TRY_AGAIN_LATER:

                        if (threadRun) {
                            try {
                                // wait 10ms
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                            }

                        }
                        break;
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        keyColorFormat = mediaCodec.getOutputFormat().getInteger(MediaFormat.KEY_COLOR_FORMAT);
                        break;
                    default:
                        if (outputBufferIndex >= 0) {
                            if (cache!=null&&cache.isNotSleep){
                                cache=null;
                            }
                            if (cache!=null){
                                synchronized (mSync) {
                                    try {
                                        mSync.wait();
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                            if (!threadRun) break;
                            if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {//结束
                                VideoCache videoCache = new VideoCache();
                                videoCache.setOutputBufferIndex(outputBufferIndex);
                                videoCache.setCurrentSampleTime(mediaEditor.getVideoDuration());
                                if (lastSampleTime > 0) {
                                    long distant = (mediaEditor.getVideoDuration() - lastSampleTime) / AV_TIME_BASE;
                                    videoCache.setDistant(distant);
                                }
                                lastSampleTime = mediaEditor.getVideoDuration();
                                syncPlay=false;
                                synchronized (objectQueue) {
                                    queue.offer(videoCache);
                                }
                                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                            } else {
                                cache = new VideoCache();
                                cache.strideHeiht = mediaEditor.getHeight();
                                cache.strideWidth = mediaEditor.getWidth();
                                cache.height = mediaEditor.getHeight();
                                cache.width = mediaEditor.getWidth();
                                cache.render = true;
                                cache.setOutputBufferIndex(outputBufferIndex);
                                cache.setCurrentSampleTime(videoBufferInfo.presentationTimeUs);
                                if (lastSampleTime > 0) {
                                    long distant = (videoBufferInfo.presentationTimeUs - lastSampleTime) / AV_TIME_BASE;
                                    cache.setDistant(distant);
                                }
                                lastSampleTime = videoBufferInfo.presentationTimeUs;
                                mediaCodec.releaseOutputBuffer(outputBufferIndex, true);

                            }
                        }
                        break;
                }


                boolean end=false;
                if (!isCompletion&&currentPosition == mediaEditor.getVideoDuration()) {
                    if (mediaEditor.isHasAudio()){
                        if (audioThread != null &&
                                audioThread.currentPosition == mediaEditor.getAudioDuration()){
                            end=true;
                        }
                    }else{
                        end=true;
                    }
                    if (end){
                        isCompletion = true;
                        if (completionListener != null) {
                            completionListener.onCompletion(MediaCodeImageReaderPlayer.this);
                        }
                    }
                }
            }
            if (mediaCodec != null) {
                mediaCodec.release();
            }
            if (mImageReader != null) {
                mImageReader.close();
                mImageReader = null;
                stopImageReaderThread();
            }
            if (mediaExtractor != null) {
                mediaExtractor.release();
                mediaExtractor = null;
            }
        }



        private void stopImageReaderThread() {
            if (mImageReaderThread != null) {
                mImageReaderThread.quitSafely();
                try {
                    mImageReaderThread.join();
                    mImageReaderThread = null;
                    mImageReaderHandler = null;
                } catch (Exception e) {
                    Log.e("=====", e.getMessage());
                }
            }
        }
    }
  • 10
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baoyu45585

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值