MediaCodec编码后保存的视频时间不正确,视频播放太快

Jing丶無雙 2017-10-09 02:36:35
最近在做摄像头录像的时候,用MediaCodec编码摄像头的yuv数据成h264, 但是发现编码后的h264文件时间不对..比如录像录了50秒,但是h264文件就只有18秒..视频播放速度加快了..但是在摄像头fps和编码器fps设置都是一样的25, h264文件属性里面显示的也是25fps.. 所以很困惑,想问问这是什么原因

目前我已经发现一个问题当我把mediaCodec.dequeueOutputBuffer(bufferInfo, 0);这里面的0改成别的值时,播放速度会变慢。举个例子将0改成30000原本录制的50秒,生成的视频播放时间则为57秒。我不知道这个值的设定是怎么来确定的,并且这个值在不同的手机上设置相同值时则会表现不一,假使就写死为30000,我同样录制50秒时间,小米手机生成视频为57秒,而一款老点儿的三星手机生成的视频却只有30秒

望大牛指导.. 编码代码如下:



package net.majorkernelpanic.streaming.video;

import java.nio.ByteBuffer;
import net.majorkernelpanic.streaming.misc.VideoServer;
import android.annotation.SuppressLint;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.util.Log;

public class AvcEncoder {
private final static String TAG = "TAG";
// private int TIMEOUT_USEC = 12 * 1000;// 微秒 1ms(毫秒)=1000(微秒)
private MediaCodec mediaCodec;
private int m_width;
private int m_height;
private int m_framerate;
private int m_bitrate;
private byte[] m_info = null;
private String mimeType = "video/avc";
public byte[] configbyte;
public VideoDecoderListener callBack;

public interface VideoDecoderListener {
public void onVideoDataCom(byte[] data, int len);

public void onAudioDataCom(byte[] data, int len);
}

private byte[] yuv420 = null;

@SuppressLint("NewApi")
public AvcEncoder(int width, int height, int framerate, int bitrate,
VideoDecoderListener callBack) {
m_width = width;
m_height = height;
m_framerate = framerate;
m_bitrate = bitrate;
this.callBack = callBack;
initMediaCodec();
yuv420 = new byte[width * height * 3 / 2];
}

@SuppressLint("NewApi")
private void initMediaCodec() {
int numCodecs = MediaCodecList.getCodecCount();
MediaCodecInfo codecInfo = null;
for (int i = 0; i < numCodecs && codecInfo == null; i++) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if (!info.isEncoder()) {
continue;
}
String[] types = info.getSupportedTypes();
boolean found = false;
for (int j = 0; j < types.length && !found; j++) {
if (types[j].equals(mimeType))
found = true;
}
if (!found)
continue;
codecInfo = info;
}
int colorFormat = 0;
MediaCodecInfo.CodecCapabilities capabilities = codecInfo
.getCapabilitiesForType(mimeType);
for (int i = 0; i < capabilities.colorFormats.length
&& colorFormat == 0; i++) {
int format = capabilities.colorFormats[i];
switch (format) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
colorFormat = format;
break;
default:
Log.i(TAG, "Skipping unsupported color format " + format);
break;
}
}
MediaFormat mediaFormat = MediaFormat.createVideoFormat(mimeType,
m_width, m_height);
if (android.os.Build.MODEL.equals("HUAWEI NXT-AL10")) {
colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
}
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); //
mediaFormat
.setInteger(MediaFormat.KEY_BIT_RATE, m_width * m_height * 5);// width*height*5
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, m_framerate);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
try {
Log.i(TAG, "---------initMediaCodec-------3----------colorFormat="
+ colorFormat);
mediaCodec = MediaCodec.createByCodecName(codecInfo.getName());
} catch (Exception e) {
e.printStackTrace();
}
mediaCodec.configure(mediaFormat, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
Log.d(TAG, "mediaCodec.start ");
}


@SuppressLint("NewApi")
private void StopEncoder() {
try {
mediaCodec.stop();
mediaCodec.release();
} catch (Exception e) {
e.printStackTrace();
}
}

ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;

public boolean isRuning = false;

public void StopThread() {
if (!isRuning)
return;
isRuning = false;
try {
StopEncoder();
} catch (Exception e) {
e.printStackTrace();
}
}

public void StartEncoderThread() {
Thread EncoderThread = new Thread(new Runnable() {

@SuppressLint("NewApi")
@Override
public void run() {
isRuning = true;
byte[] input = null;
long pts = 0;
long generateIndex = 0;
long lastTime = 0;
long bufferSize = 0;
while (isRuning) {
if (VideoServer.YUVQueue.size() > 0) {
input = VideoServer.YUVQueue.poll();
byte[] yuv420sp = new byte[m_width * m_height * 3 / 2];
NV21ToNV12(input, yuv420sp, m_width, m_height);
input = yuv420sp;
}

if (input != null) {
try {
ByteBuffer[] inputBuffers = mediaCodec
.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec
.getOutputBuffers();
int inputBufferIndex = mediaCodec
.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
// pts = 132 + (curTime - startTime) * 1000;
// Log.d("差值", pts + "微秒");
pts = computePresentationTime(generateIndex);
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(input);
mediaCodec.queueInputBuffer(inputBufferIndex,
0, input.length, pts, 0);
generateIndex += 1;
}

MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

int outputBufferIndex = mediaCodec
.dequeueOutputBuffer(bufferInfo, 0);// 1000000/m_framerate
while (outputBufferIndex >= 0) {
Log.d("视频流", "索引=" + generateIndex + ",长度="
+ bufferInfo.size);
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
if (bufferInfo.flags == 2
|| bufferInfo.flags == 3) {// 台电=3
configbyte = outData;
} else if (bufferInfo.flags == 1
|| bufferInfo.flags == 9) { // LETV(MTK)==9
byte[] keyframe = new byte[bufferInfo.size
+ configbyte.length];
System.arraycopy(configbyte, 0, keyframe,
0, configbyte.length);
System.arraycopy(outData, 0, keyframe,
configbyte.length, outData.length);
callBack.onVideoDataCom(keyframe,
keyframe.length);
} else {
callBack.onVideoDataCom(outData,
outData.length);
}

mediaCodec.releaseOutputBuffer(
outputBufferIndex, false);
outputBufferIndex = mediaCodec
.dequeueOutputBuffer(bufferInfo, 0);// 1000000/m_framerate
}

} catch (Throwable t) {
t.printStackTrace();
}
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
EncoderThread.start();

}

private void NV21ToNV12(byte[] nv21, byte[] nv12, int width, int height) {
if (nv21 == null || nv12 == null)
return;
int framesize = width * height;
int i = 0, j = 0;
System.arraycopy(nv21, 0, nv12, 0, framesize);
for (i = 0; i < framesize; i++) {
nv12[i] = nv21[i];
}
for (j = 0; j < framesize / 2; j += 2) {
nv12[framesize + j - 1] = nv21[j + framesize];
}
for (j = 0; j < framesize / 2; j += 2) {
nv12[framesize + j] = nv21[j + framesize - 1];
}
}

/**
* Generates the presentation time for frame N, in microseconds.
*/
private long computePresentationTime(long frameIndex) {
return 132 + frameIndex * 1000000 / m_framerate;
}
}

...全文
1324 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
ChoRyan Quan 2020-08-16
  • 打赏
  • 举报
回复
首先保持时间戳一致,取数据和等待数据,音视频的要一致。第二你可以直接把拿到数据的bufferinfo传入到muxer中。我也出现过这种问题,改成这样子就好了。
Jing丶無雙 2020-08-12
  • 打赏
  • 举报
回复
没有解决
冷薄荷 2020-08-06
  • 打赏
  • 举报
回复
问题解决了吗,还是用其他方法解决的,楼主,求帮忙
Jing丶無雙 2018-09-03
  • 打赏
  • 举报
回复
引用 5 楼 duguju 的回复:
原因及解决方案,可能会帮到楼主:
https://blog.csdn.net/duguju/article/details/82149856

你好,感谢你的回复。这是你提到的解决方案:
1、使用MediaMuxer即混合器进行时间戳对齐;
2、把每一帧相机数据都存储在一个池子里,每帧都不遗漏,编码时每次去池子里取;这样的缺点是,由于时间差会导致池子里数据会越来越多,在点击“录制结束”时池子中的很多数据其实还没取完,即编码要在录制结束操作之后很长时间才完成。


因为我本身对视频流的处理能力就很薄弱。所以这里提到的时间戳对齐以及数据存储池,具体操作不大明白,有更具体一点儿的代码示例么
duguju 2018-08-28
  • 打赏
  • 举报
回复
原因及解决方案,可能会帮到楼主:
https://blog.csdn.net/duguju/article/details/82149856
Jing丶無雙 2017-10-17
  • 打赏
  • 举报
回复
看来是没人会了,来些老哥,散分了
Jing丶無雙 2017-10-13
  • 打赏
  • 举报
回复
帮忙顶下,谢谢了
Jing丶無雙 2017-10-11
  • 打赏
  • 举报
回复
帮忙顶下,谢谢了

80,350

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧