首页 > 编程知识 正文

android音视频同步,安卓mp4转jpg

时间:2023-05-03 05:21:11 阅读:58719 作者:1350

在ByteBuffer模式下,从相机取得原始的NV21图像,COLOR_FORMAT传递到作为COLOR_FormatYUV420SemiPlanar的MediaCodec,结果在不同的Android设备中有些是正常的

theyuvformatsusedbycameraoutputandmediacodecinputhavetheiru/vplanesswapped。

ifyouareabletomovethedatathroughasurfaceyoucanavoidthisissue; however,youlosetheabilitytoexaminetheyuvdata.anexampleofrecordingtoa.MP4 filefromacameracanbefoundonbigflake。

somedetailsaboutthecolorspacesandhowtoswapthemisinthisanswer。

.

注:堆栈概述文章链接:堆栈概述.com/questions/1…

所以,这是MediaCodec自身的错误,交换其自身输入的YUV图像的u/v。 解决方案有两种。

使用ByteBuffer模式,在将NV21图像传输到MediaCodec之前,将NV21转换为NV12。 但是,如上所述,这两种产品只是u/v相反。 但是,在少数设备上发生了这种情况,我觉得很难适应。 不推荐使用表面模式。 可以完全避免这一点,但会失去对原始YUV图像的处理能力。 但是,可以使用OpenGL方法处理图像。 建议3、实现的大致步骤如下。

另一方面,如果使用OpenGL纹理创建纹理,将该纹理打包为摄影机中的“SurfaceTexture”,然后将其用作预览窗口,则摄影机图像将显示在纹理中。 另一方面,mMediaCodec.createInputSurface ()用作MediaCodec的编码数据源。 最后,在inputSurface中绘制纹理上的图像,同时使用相机预览。 说明:相机纹理id (OpenGL )输入表面(mediacodec ) )。

具体实现可以在bigflake的相机主题(demo )中找到。 www.bigflake.com/mediacodec/…

二、缩短录像时间(丢帧)解决这个问题有两个关键。

时间戳对齐: ByteBuffer模式: MediaCodec.queueInputBuffer ) )时,必须手动将YUV图像传递给MediaCodec,同时传递当前时间戳。 请注意,时间单位为微秒(us )。 表面模式:有mEGLDisplay、mEGLSurface与MediaCodec.createInputSurface ()中创建的inputSurface进行对比,EGL14.eglswapbuffers 在egl ext.eglpresentationtimeandroid (megldisplay、meglsurface、nsecs )中,为MediaCodec输入表面的数据设置时间戳。 媒体格式设置:媒体格式的关键帧间隔(KEY_I_FRAME_INTERVAL )和帧率(KEY_FRAME_RATE )必须设置得合适。 说明:这里是核心总结。 一旦跳过向下看,再回去看就很容易理解了。

1、现象录制10秒视频,从设备中取出后,由播放器播放观察。 一些设备正常,发现单个设备录制的视频只有一半的时间。 这也就是网上说的play too fast问题。

亨利: OnlyStopWatch_x64.exe这是一个定时小工具,对于需要录制视频和观察即时速度的场景非常有用。 画面丢失、播放过快等问题不难理解。

2、在前面提到的堆栈溢出答疑分析中,其乖僻仁同时表达了他对媒体录制视频播放过快问题的解释:

.

thereisnotimestampinformationintherawh.264 elementary stream.youneedtopassthetimestampthedecodertomediamuxerorwhateveror资源

e player will just pick a rate, or possibly play the frames as fast as it can.

注:stackoverflow 文章链接:stackoverflow.com/questions/1…

他认为 H.264 不包含时间戳信息,你需要把时间戳通过编码器(MediaCodec)给到媒体复用器(MediaMuxer),否则,播放器会选择一个速率,尽快地播放帧。

3、实现

如果是 ByteBuffer 模式,则核心代码实现如下:

private void feedMediaCodecData(byte[] data) {
if (!isEncoderStart)
return;
int bufferIndex = -1;
try {
bufferIndex = mMediaCodec.dequeueInputBuffer(0);
} catch (IllegalStateException e) {
e.printStackTrace();
}
if (bufferIndex >= 0) {
ByteBuffer buffer = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
buffer = mMediaCodec.getInputBuffer(bufferIndex);
} catch (Exception e) {
e.printStackTrace();
}
} else {
if (inputBuffers != null) {
buffer = inputBuffers[bufferIndex];
}
}
if (buffer != null) {
buffer.clear();
buffer.put(data);
buffer.clear();
// 纳秒(ns) 转 微秒(us)
mMediaCodec.queueInputBuffer(bufferIndex, 0, data.length, System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
}
}
}

这时要注意,MediaCodec 需要的时间单位是微秒(us),如果你不使用正确的时间,可能会出问题,比如:[stackoverflow.com/quest

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

ions/2…]( )

补充:秒(s)、毫秒(ms)、微秒(us)、纳秒(ns),之间的比例都是 1:1000。

如果是 Surface 模式,则核心代码实现如下:

// 更新纹理图像
// Acquire a new frame of input, and render it to the Surface. If we had a GLSurfaceView we could switch EGL contexts and call drawImage() a second time to render it on screen. The texture can be shared between contexts by passing the GLSurfaceView’s EGLContext as eglCreateContext()'s share_context argument.
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(mSTMatrix);

// 传入时间戳信息
// Set the presentation time stamp from the SurfaceTexture’s time stamp. This will be used by MediaMuxer to set the PTS in the video.
mInputSurface.setPresentationTime(mSurfaceTexture.getTimestamp());
// Submit it to the encoder. The eglSwapBuffers call will block if the input is full, which would be bad if it stayed full until we dequeued an output buffer (which we can’t do, since we’re stuck here). So long as we fully drain the encoder before supplying additional input, the system guarantees that we can supply another frame without blocking.
mInputSurface.swapBuffers();

具体实现可以在 bigflake 的 Demo(CameraToMpegTest) 中获取:www.bigflake.com/mediacodec/…

4、修补

尽管按照上面的步骤,把时间戳正确传递给 MediaMuxer 了,但依旧无济于事。经过将我项目中的代码与 bigflake 的 CameraToMpegTest 中的代码进行对比,发现,MediaFormat 的配置上也很关键,必须配置得当,否则也还是会出现时长缩水的问题,于是我将原来项目中的代码进行修改,将帧率修改为 30f,关键帧间距改为 5s,丢帧的问题这才解决了,MediaFormat 配置具体代码如下:

protected static final String MIME_TYPE = “video/avc”;
protected static final int FRAME_INTERVAL = 5; // 间隔5s插入一帧关键帧
protected static final int FRAME_RATE = 30;
protected static final float BPP = 0.50f;
protected int mColorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。