H265硬编码demo
时间: 2023-07-10 10:35:44 浏览: 167
Elecard-HEVC码流分析软件.zip
以下是一个使用H.265硬编码的Android Camera示例:
```
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private static final String TAG = "MainActivity";
private static final String MIME_TYPE = "video/hevc";
private static final int VIDEO_WIDTH = 640;
private static final int VIDEO_HEIGHT = 480;
private static final int FRAME_RATE = 30;
private static final int I_FRAME_INTERVAL = 1;
private static final int BIT_RATE = 2000000;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private MediaCodec mEncoder;
private MediaMuxer mMuxer;
private int mTrackIndex;
private boolean mMuxerStarted;
private long mStartTime;
private int mFrameCount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
prepareEncoder();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
releaseEncoder();
}
private void prepareEncoder() throws IOException {
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, VIDEO_WIDTH, VIDEO_HEIGHT);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mEncoder.start();
mMuxer = new MediaMuxer(new File(Environment.getExternalStorageDirectory(), "output.mp4").toString(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mTrackIndex = -1;
mMuxerStarted = false;
mStartTime = System.currentTimeMillis();
mFrameCount = 0;
}
private void releaseEncoder() {
if (mEncoder != null) {
mEncoder.stop();
mEncoder.release();
mEncoder = null;
}
if (mMuxer != null) {
if (mMuxerStarted) {
mMuxer.stop();
}
mMuxer.release();
mMuxer = null;
}
}
private void drainEncoder(boolean endOfStream) {
final int TIMEOUT_USEC = 10000;
if (endOfStream) {
mEncoder.signalEndOfInputStream();
}
ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();
while (true) {
int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
if (!endOfStream) {
break;
}
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = mEncoder.getOutputFormat();
mTrackIndex = mMuxer.addTrack(newFormat);
mMuxer.start();
mMuxerStarted = true;
} else if (encoderStatus < 0) {
// ignore
} else {
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
if (encodedData == null) {
throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null");
}
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
mBufferInfo.size = 0;
}
if (mBufferInfo.size != 0) {
if (!mMuxerStarted) {
throw new RuntimeException("muxer hasn't started");
}
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);
mFrameCount++;
long now = System.currentTimeMillis();
long elapsedTime = now - mStartTime;
if (elapsedTime > 1000) {
float frameRate = (float) mFrameCount * 1000 / elapsedTime;
mFrameCount = 0;
mStartTime = now;
}
}
mEncoder.releaseOutputBuffer(encoderStatus, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
if (!endOfStream) {
// ignore
}
break;
}
}
}
}
private final MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
private void encodeFrame() {
try {
ByteBuffer[] inputBuffers = mEncoder.getInputBuffers();
int inputBufferIndex = mEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
mSurfaceView.setDrawingCacheEnabled(true);
mSurfaceView.buildDrawingCache();
Bitmap bitmap = mSurfaceView.getDrawingCache();
if (bitmap != null) {
Canvas canvas = new Canvas();
canvas.setBitmap(bitmap);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawText("Frame: " + mFrameCount, 10, 10, paint);
} else {
return;
}
if (mSurfaceHolder == null) {
return;
}
Canvas canvas = mSurfaceHolder.lockCanvas();
if (canvas == null) {
return;
}
canvas.drawBitmap(bitmap, 0, 0, null);
mSurfaceHolder.unlockCanvasAndPost(canvas);
long ptsUsec = computePresentationTime(mFrameCount);
mEncoder.queueInputBuffer(inputBufferIndex, 0, 0, ptsUsec, 0);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
private long computePresentationTime(int frameIndex) {
return 132 + frameIndex * 1000000 / FRAME_RATE;
}
}
```
该示例使用了MediaCodec API编码SurfaceView的视频,并将其保存到文件中。其中,我们使用了HEVC(H.265)编码器。需要注意的是,HEVC编码器可能不支持所有Android设备上的所有分辨率和帧率。因此,在实际使用时,您需要测试编码器支持的分辨率和帧率,并相应地进行配置。
阅读全文