Android如何获取正播放视频时的解码输出流

移动开发 码拜 6年前 (2015-09-15) 1662次浏览

 

Android播放视频,经过解码解码 获得数据流 再显示到屏幕上。请问这部分数据(解码解码后的数据流)通过什么方法可以获取?

#1
可以通过MediaMetadataRetriever获取到某一帧的Bitmap,感觉这样去做有点不现实
#2

1分

你是想录制视频?
#3

回复2楼:

不是,跟照相机录像机摄像机无关的。

在播放一个视频文件的时候,拿到系统对这个视频文件解码后的数据流,也就是解码器的输出流

#4

1分

回复3楼:

那你直接读这个文件流就可以了啊

#5

回复4楼:

怎么直接读啊?这个数据流没有存成文件,
#6

1分

回复5楼:

视频是你自己控制播放呢还是从播放器播放视频的时候截取视频流?

#7

回复4楼:

你的意思是说直接读取这个要播放的视频文件?我要的是可以画出来的数据啊,也就是一帧一帧的数据,这个是需要解码的过程,直接读文件 没有用的

#8

回复6楼:

这个应该是一样的,我自己控制 的话也是调用系统默认的MediaPlayer解码器,  就是“从播放器播放视频的时候截取视频流”要解码后数据流

#9

5分

最好的应该是从display部分入手,重写一个surfaceview,把这个view传给mediaplayer去播放,这样数据到这个view里面,你先去记录,然后再发出去播放,不过效率上估计有点问题。
#10

1分

回复8楼:

没用过MediaPlayer,但是调用MediaPlayer的话,直接jni处理视频流并描绘图像了,我想应该是取不到了吧

#11

回复9楼:

暂时不考虑效率问题,只是要取出数据,跟做个数据拦截一样。

原本打开一个视频文件,得到文件的输入流,传递到解码器,解码器进行解码,解码的数据流给Java端SurfaceView绘图;;我现在就是想办法拿到这个数据流,没有传到Java端去绘图(也就是播放器没有画面)也没有关系,这边Java层应用感觉实现不了,不知道JNI能不能实现…  对 c/c++ 不熟额

#12

10分

video播放是在graphic层显示的才能抓,有的方案video直接硬件的layer显示的,这种就没辙了。

graphic层可以试下下面两个方法:

1,在native层调用mediaplayer,MediaPlayer::setVideoSurfaceTexture接口是传入一个IGraphicBufferProducer,自己建立一个bufferqueue传进去就可以了。

2,用virtual display试试看,建立一个虚拟的display。然后仿照surfaceview写一个新的view,把window建立在前面建立的virtual display上。这样输出的数据都在virtual display的buffer中。

#13

回复12楼:

Android4.2/4.4源码上作业应该不存在什么方案吧。native层调用是要引入系统库么,需要单独提出来?
#14

5分

回复13楼:

native调用应该是需要一些系统库,你直接抓一份google代码去编译就是了,就是怕有些权限问题。

第二个case在java都有借口,可以试试。

#15

回复14楼:

额小的愚钝,DisplayManager.createVirtualDisplay创建的VirtualDisplay中没有buffer啊?请再指引下…
#16

回复12楼:

public final class VirtualDisplay {
    private final DisplayManagerGlobal mGlobal;
    private final Display mDisplay;
    private IBinder mToken;
    VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token) {
        mGlobal = global;
        mDisplay = display;
        mToken = token;
    }
    /**
     * Gets the virtual display.
     */
    public Display getDisplay() {
        return mDisplay;
    }
    /**
     * Releases the virtual display and destroys its underlying surface.
     * <p>
     * All remaining windows on the virtual display will be forcibly removed
     * as part of releasing the virtual display.
     * </p>
     */
    public void release() {
        if (mToken != null) {
            mGlobal.releaseVirtualDisplay(mToken);
            mToken = null;
        }
    }
    @Override
    public String toString() {
        return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + "}";
    }
}

这是VirtualDisplay,内容好少啊,能用吗,不知道怎么用诶?

#17

回复12楼:

是不是VirtualDisplay.mDisplay.getMetrics(DisplayMetrics outMetrics),这样应该是需要自己写个播放器了吧?
#18

1分

楼主,我也有这样的需求:视频播放过程中获得每一帧图片,将每一帧图片处理后再显示播放,并且需要将处理过后的每一帧重新生成一个视频文件,不知道有没有好的方法?
#19

1分

能获取到视频源,一切就都不是问题了
#20

回复18楼:

前面的需求是一样的,生成一个视频文件这边需要编码了,这边可以去看看MediaRecorder
#21

回复19楼:

你说的是什么视频源呢?

字面上感觉就是这个要播放的源文件,那接下来怎么做呢…

#22

2分

回复16楼:

可以先看下virtualdisplaytest.java里面是怎么用的

#24

6分

在解码之后 和 往显示设备上送 之间可以截获这块内存。

前者要看framework的解码代码,如果是第2方解码的库,应该有输出的接口。

后者在display部分。

#25

回复24楼:

没有内存地址的吧,如何截获
#26

回复22楼:

……突然发现SurfaceView中没有Surface,这个window怎么搞
#27

5分

回复26楼:

有的,SurfaceView.getHolder().getSurface()

#28

回复27楼:

现在出现新问题了,DisplayManager.createVirtualDisplay需要API Lv19,目前一般的都是android4.2才Lv17,这怎么办啊
#29

1分

回复28楼:

这没招了….,这条路就走不通了

#30

1分

顶  做过音频的 好像有个 fft的算法

#31
kanbudongle 啊啊啊啊啊啊啊啊啊啊啊
#36
查了下,貌似用SurfaceTexture从video decode里面获取流是最方便的,先去学学怎么用了,

高手们 求指导~

#37

2分

回复36楼:

还没有弄出来,这两天看了一下ffmpeg,但是NDK开发太难了,我对C也不懂,先看一下你说的这个

#38

3分

回复36楼:

java层的surfacetexture没法获取他的surface,去给meidplayer啊~~~

native才有可能

#39
厉害。我也在找求高手
#40
没有积分 真的很不好啊。
#41

回复38楼:

什么意思啊,不懂诶? SurfaceTexture好复杂额,暂时还不懂如何实现,但是看那个读取camera图像流的例子是在Java层实现的,其中有几句好像是c++实现的:

        private final String mVertexShader =
                "uniform mat4 uMVPMatrix;\n" +
                "uniform mat4 uSTMatrix;\n" +
                "attribute vec4 aPosition;\n" +
                "attribute vec4 aTextureCoord;\n" +
                "varying vec2 vTextureCoord;\n" +
                "void main() {\n" +
                "  gl_Position = uMVPMatrix * aPosition;\n" +
                "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
                "}\n";
        private final String mFragmentShader =
                "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;\n" +
                "varying vec2 vTextureCoord;\n" +
                "uniform samplerExternalOES sTexture;\n" +
                "void main() {\n" +
                "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
                "}\n";
        // 略…
        private int createProgram(String vertexSource, String fragmentSource) {
            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
            if (vertexShader == 0) {
                return 0;
            }
            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
            if (pixelShader == 0) {
                return 0;
            }
            int program = GLES20.glCreateProgram();
            if (program != 0) {
                GLES20.glAttachShader(program, vertexShader);
                checkGlError("glAttachShader");
                GLES20.glAttachShader(program, pixelShader);
                checkGlError("glAttachShader");
                GLES20.glLinkProgram(program);
                int[] linkStatus = new int[1];
                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
                if (linkStatus[0] != GLES20.GL_TRUE) {
                    Log.e(TAG, "Could not link program: ");
                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
                    GLES20.glDeleteProgram(program);
                    program = 0;
                }
            }
            return program;
        }

这看不懂啊,这些又是在哪里执行的? 另外 rs 文件 跟这个原理是一样的么?

#42
错了错了,弄混乱了有点, 上面那个不是“读取camera图像流的例子”,是另外的一个  SurfaceTexture测试video用的,

createProgram对应的俩个参数就是上面的两个字符串,求高手帮忙解释说明下,应该从哪里着手

#43

1分

回复42楼:

是啊,搞到现在一点进度都没有,哎。

#44

1分

回复42楼:

camera里面有个api,setPreviewTexture来接收surfacetexture,但是mediaplayer没有这样的api的。

#45

回复44楼:

能使用SurfaceTexture代替SurfaceView来显示,播放电影应该就可以了吧,能做到吗这样?SurfaceTexture不会用也没有api,google打不开!
#46

回复45楼:

都说了,mediplayer没有这样的api

#47

5分

回复45楼:

下午看了下Surface的api,有个途径可以试下,写一个类似下面的class

public class LocalHolder  implements  SurfaceHolder{
	SurfaceTexture m_st;
	Surface m_surface;
	LocalHolder(SurfaceTexture st){
		m_st = st;
		m_surface = new Suface(m_st);
	}
	@Override
        public Surface getSurface() {
            return m_surface;
        }
	....//其他接口还需要补全
}

这样,你建立一个SurfaceTexture初始化后传给用这个texture构造一个LocalHolder出来,

然后Mediaplayer.setDisplay就传入LocalHolder对象。

#48
很好……..
#49

3分

回复47楼:

new Surface(m_st);这里需要API14(android4.0)

#50

回复47楼:

这个API我知道,问题是播放不出来东西
#51

8分

回复50楼:

surfacetexture界面上肯定是看不到东西的,先setOnFrameAvailableListener()设置个listener看看有没有数据上来,要想看到,还要把texture再画出去。

#52

回复51楼:

onFrameAvailable(SurfaceTexture)的这个SurfaceTexture吗?这个数据不为空也是不改变的……
#53

2分

不知道你是怎么理解的,onFrameAvailable是回调函数,有新数据来的时候,才会被调用,不是看调用的参数。
#54

回复53楼:

是不知道怎么用。播放视频的时候数据流是有更新的,它就会一直被调用的,如果要进行数据处理,应该就是在这进行。 视频正播放的时候无画面输出,只有声音。  如果我要取出SurfaceTexture中的数据应该怎么取呢?
#56

5分

回复54楼:

到jni层去操作就好做了,surfaceTexture.java里面的mSurfaceTexture分别对应了GLConsumer。

新的frame来的时候,先调用SurfaceTexture.updateTexImage()更新一下buffer,然后GLConsumer.getCurrentBuffer()可以拿到GraphicBuffer了,GraphicBuffer.lock()又可以直接拿到指针了。要注意color format。

#57

回复56楼:

不理解你这句话“surfaceTexture.java里面的mSurfaceTexture分别对应了GLConsumer”是什么意思?

ps:这些都是你现学的么?从哪儿学的哇怎么学这么快懂这么多…

#58

10分

回复57楼:

写错了,应该是”surfaceTexture.java里面的成员mSurfaceTexture,他是int类型,实际是一个指向native层的GLConsumer对象的指针。“

这些在android代码里面都有的,不要光看api,往下看看实现。

#59

回复58楼:

mTextureID是int类型的,mSurfaceTexture是SurfaceTexture类型的
#60

1分

回复59楼:

你看的哪个文件?我说的是class SurfaceTexture里面。

#61

回复60楼:

你看的哪个文件?我说的是class SurfaceTexture里面。

class VideoSurfaceView extends GLSurfaceView {
    private VideoRender mRenderer;
    private MediaPlayer mMediaPlayer = null;
    public VideoSurfaceView(Context context, MediaPlayer mp) {
        super(context);
        setEGLContextClientVersion(2);
        mMediaPlayer = mp;
        mRenderer = new VideoRender(context);
        setRenderer(mRenderer);
        mRenderer.setMediaPlayer(mMediaPlayer);
    }
    
    @Override
    public void onResume() {
        queueEvent(new Runnable(){
                public void run() {
                    mRenderer.setMediaPlayer(mMediaPlayer);
                }});
        super.onResume();
    }
    private static class VideoRender
        implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
        ......
        private final float[] mTriangleVerticesData = {
            // X, Y, Z, U, V
            -1.0f, -1.0f, 0, 0.f, 0.f,
            1.0f, -1.0f, 0, 1.f, 0.f,
            -1.0f,  1.0f, 0, 0.f, 1.f,
            1.0f,  1.0f, 0, 1.f, 1.f,
        };
        private FloatBuffer mTriangleVertices;
        ......
        private float[] mMVPMatrix = new float[16];
        private float[] mSTMatrix = new float[16];
        private int mProgram;
        private int mTextureID;
        private int muMVPMatrixHandle;
        private int muSTMatrixHandle;
        private int maPositionHandle;
        private int maTextureHandle;
        private SurfaceTexture mSurfaceTexture;
        
        private boolean updateSurface = false;
        private MediaPlayer mPlayer;
        public VideoRender(Context context) {
            mTriangleVertices = ByteBuffer.allocateDirect(
                mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
                    .order(ByteOrder.nativeOrder()).asFloatBuffer();
            mTriangleVertices.put(mTriangleVerticesData).position(0);
            Matrix.setIdentityM(mSTMatrix, 0);
        }
        private void setMediaPlayer(MediaPlayer player) {
            mPlayer = player;
        }
        
        @Override
        public void onDrawFrame(GL10 glUnused) {
            synchronized(this) {
                if (updateSurface) {
                    mSurfaceTexture.updateTexImage();
                    mSurfaceTexture.getTransformMatrix(mSTMatrix);
                    updateSurface = false;
                }
            }
            ......
        }
        @Override
        public void onSurfaceChanged(GL10 glUnused, int width, int height) {
        	
        }
        
        @Override
        public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
            ......
            int[] textures = new int[1];
            GLES20.glGenTextures(1, textures, 0);
            mTextureID = textures[0];
            GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
            ......
            /*
             * Create the SurfaceTexture that will feed this textureID,
             * and pass it to the MediaPlayer
             */
            mSurfaceTexture = new SurfaceTexture(mTextureID);
            mSurfaceTexture.setOnFrameAvailableListener(this);
            Surface surface = new Surface(mSurfaceTexture);
            mPlayer.setSurface(surface);
            mPlayer.setScreenOnWhilePlaying(true);
            surface.release();
            try {
                mPlayer.prepare();
            } catch (IOException t) {
                Log.e(TAG, "media player prepare failed" + t);
            }
            synchronized(this) {
                updateSurface = false;
            }
            mPlayer.start();
            Log.i(TAG, "media player start");
        }
        
        synchronized public void onFrameAvailable(SurfaceTexture surface) {
            updateSurface = true;
        }
        private int loadShader(int shaderType, String source) {
            ......
            return shader;
        }
        private int createProgram(String vertexSource, String fragmentSource) {
            ......
            return program;
        }
        ......
    }  // End of class VideoRender.
}  // End of class VideoSurfaceView.
#62

2分

VideoSurfaceView这个是你自己写的吧?

我的意思是要对SurfaceTexture扩展一下,前面说的成员都是class SufaceTexture中的,你要拿的buffer就在class SurfaceTexture中。

#63

回复62楼:

也不一定必须对SurfaceTexture扩展,Surface surface = new Surface(mSurfaceTexture);mMediaPlayer.setSurface(surface);这样播放的视频就在SurfaceTexture上了。 在SurfaceTexture.java中,定义的mSurfaceTexture并没有使用只是有那么一行注释,没有任何操作…扩展了SurfaceTexture也是没法从它入手的
#64

5分

回复63楼:

mSurfaceTexture是在jni里面赋值的,java层的确看不到什么东西。从Surface入手,我觉得是不好做的,surface是处于一个生产者的角色,SurfaceTexture才是消费者,你现在是要使用player生产出来的buffer,从SurfaceTexture这边入手更合适。

#65

回复64楼:

绕来绕去的真心把SurfaceTexture和TextureView弄混了,一般都是继承TextureView再设置一个监听器TextureView.SurfaceTextureListener或者SurfaceTexture.OnFrameAvailableListener,
#66

8分

呵呵呵…..

一般android的显示一般有两层(或者两层以上), fb0, 和 fb1,   fb1 在 fb0 的下面,  fb1 显示软件的界面, fb0 平时一般是透明的,  在播放视频 的时候, 如果视频使用硬件解码,  解码后的 YUV 图像帧 直接 memcpy 到  fb0 的 framebuffer中, 这时候你就可以看到视频了, 中间不经过任何JAVA代码, 如果不是全屏, 那么fb0的只有视频区域的 alpha值 为 255, 其它区域为0, 使得后面的界面可以透上来…

你要抓 解码后的数据,  从GPU的驱动下手, HOOK一个接口出来可能有希望……

#67

回复66楼:

SurfaceTexture API有这么句话“The image stream may come from either camera preview or video decode.” the image stream from video decode 就够了,想办法实现就OK,你这个思路 这边行不通再说吧
#68
SurfaceTexture可以用,但是没做成功。参考另外一种方法,提取解码的部分修改成自己的需求再次编译成动态库,导入项目中使用。参考github:https://github.com/bbcallen/ijkplayer.git
#69
我这两天正在研究这个事情 完全没有头绪啊 用的ffmpeg 获得了 rbg的流 可是要怎么播放呢 楼主有没有好的思路
#70

回复47楼:

卡卡西你真是高手,我最近想要实现一个后台录像,也是急得不行,烦死了,现在在考虑怎么调用摄像头然后直接从摄像头获取数据,不让它preview了。

#71

回复70楼:

你这个不难的,不能用surfaceview(到后台后surface会摧毁),surfacetexture就可以了。

有不少监控软件都是这么做的,在前台能显示,到后台还能继续监控。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Android如何获取正播放视频时的解码输出流
喜欢 (0)
[1034331897@qq.com]
分享 (0)