gle*_*man 30 android video-encoding video-streaming h.264 android-camera
我试图让它在Android 4.1上运行(使用升级的Asus Transformer平板电脑).感谢Alex对我之前的问题的回答,我已经能够将一些原始的H.264数据写入文件,但是这个文件只能播放ffplay -f h264,而且似乎丢失了有关帧速率的所有信息(极快的播放).此外,色彩空间看起来不正确(atm使用摄像机默认的编码器侧).
public class AvcEncoder {
private MediaCodec mediaCodec;
private BufferedOutputStream outputStream;
public AvcEncoder() {
File f = new File(Environment.getExternalStorageDirectory(), "Download/video_encoded.264");
touch (f);
try {
outputStream = new BufferedOutputStream(new FileOutputStream(f));
Log.i("AvcEncoder", "outputStream initialized");
} catch (Exception e){
e.printStackTrace();
}
mediaCodec = MediaCodec.createEncoderByType("video/avc");
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
}
public void close() {
try {
mediaCodec.stop();
mediaCodec.release();
outputStream.flush();
outputStream.close();
} catch (Exception e){
e.printStackTrace();
}
}
// called from Camera.setPreviewCallbackWithBuffer(...) in other class
public void offerEncoder(byte[] input) {
try {
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(input);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
outputStream.write(outData, 0, outData.length);
Log.i("AvcEncoder", outData.length + " bytes written");
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
将编码器类型更改为"video/mp4"显然可以解决帧速率问题,但由于主要目标是制作流媒体服务,因此这不是一个好的解决方案.
我知道考虑到SPS和PPS NALU,我放弃了一些Alex的代码,但是我希望这不是必要的,因为那些信息也来自outData我,我认为编码器会正确格式化.如果不是这种情况,我应该如何在我的文件/流中安排不同类型的NALU?
那么,为了制作有效的,有效的H.264流,我在这里缺少什么?我应该使用哪些设置来匹配相机的色彩空间和编码器的色彩空间?
我觉得这更像是与H.264相关的问题,而不是Android/MediaCodec主题.或者我还没有正确使用MediaCodec API?
提前致谢.
对于快速播放 - 帧速率问题,您无需在此处执行任何操作.由于它是流式传输解决方案,因此必须提前告知另一方的帧速率或每帧的时间戳.这两者都不是基本流的一部分.选择预先确定的帧率,或者传递某些sdp或类似的东西,或者使用现有的协议,如rtsp.在第二种情况下,时间戳是以rtp之类的形式发送的流的一部分.然后客户端必须删除rtp流并播放它.这就是基本流媒体的工作原理.[如果您有固定速率编码器或提供时间戳,请修复帧速率]
本地PC播放速度很快,因为它不会知道fps.通过在输入之前给出fps参数,例如
ffplay -fps 30 in.264
Run Code Online (Sandbox Code Playgroud)
您可以控制PC上的播放.
至于文件不可播放:它是否有SPS和PPS.您还应该启用NAL标头 - 附件b格式.我不太了解android,但是当任何h.264基本流不在任何容器中并且需要转储并稍后播放时,这是必需的.如果android默认为mp4,但默认的annexb标题将被关闭,所以也许有一个开关来启用它.或者,如果您逐帧获取数据,请自行添加.
至于颜色格式:我猜测默认应该有效.所以尽量不要设置它.如果没有尝试422 Planar或UVYV/VYUY交错格式.通常相机就是其中之一.(但不是必要的,这些可能是我经常遇到的).
Android 4.3(API 18)提供了一个简单的解决方案.该MediaCodec班现在接受输入从表面上,这意味着你可以在相机的表面预览连接到编码器,并绕过所有古怪的YUV格式的问题.
还有一个新的MediaMuxer类,它将您的原始H.264流转换为.mp4文件(可选地在音频流中混合).
有关完成此操作的示例,请参阅CameraToMpegTest源.(它还演示了使用OpenGL ES片段着色器在录制视频时对其进行简单编辑.)
如果已将预览颜色空间设置为YV12,则可以像这样转换颜色空间:
public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height) {
/*
* COLOR_TI_FormatYUV420PackedSemiPlanar is NV12
* We convert by putting the corresponding U and V bytes together (interleaved).
*/
final int frameSize = width * height;
final int qFrameSize = frameSize/4;
System.arraycopy(input, 0, output, 0, frameSize); // Y
for (int i = 0; i < qFrameSize; i++) {
output[frameSize + i*2] = input[frameSize + i + qFrameSize]; // Cb (U)
output[frameSize + i*2 + 1] = input[frameSize + i]; // Cr (V)
}
return output;
}
Run Code Online (Sandbox Code Playgroud)
要么
public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) {
/*
* COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V reversed.
* So we just have to reverse U and V.
*/
final int frameSize = width * height;
final int qFrameSize = frameSize/4;
System.arraycopy(input, 0, output, 0, frameSize); // Y
System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V)
System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U)
return output;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
50057 次 |
| 最近记录: |