Skip to content

Commit 65a9c05

Browse files
committed
增加对输出帧率的控制
1 parent 3907921 commit 65a9c05

2 files changed

Lines changed: 53 additions & 51 deletions

File tree

app/src/main/java/com/demo/mediacodec/MediaCodecUtils.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ public static MediaFormat createOutputFormat(@NonNull Context ctx, @NonNull Uri
155155
@NonNull VideoOutputConfig outputConfig) {
156156
MediaFormat outputFormat;
157157
String inMimeType = mOriVideoFormat.getString(MediaFormat.KEY_MIME);
158+
int inFrameRate = mOriVideoFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
158159
boolean isH265 = false;
159160
String mime;
160161
if (config.h265) {
@@ -190,10 +191,22 @@ public static MediaFormat createOutputFormat(@NonNull Context ctx, @NonNull Uri
190191
} else {
191192
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 3 * 1024 * 1024);
192193
}
193-
if (config.fps > 0) {
194-
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, config.fps);
194+
195+
//这里的设置是为了让能够获取编码器,实际输出帧率并不受这个控制。而是受render绘制影响
196+
if (inFrameRate > 0 && inFrameRate < config.fps) {
197+
config.fps = inFrameRate;
198+
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, inFrameRate);
195199
} else {
196-
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
200+
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, config.fps);
201+
}
202+
203+
//O及以上可以通过KEY_MAX_FPS_TO_ENCODER来控制输出帧率
204+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
205+
outputFormat.setFloat(MediaFormat.KEY_MAX_FPS_TO_ENCODER, config.fps);
206+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
207+
//O以上就有了,Q以上才开放。也有说Android 6也有用
208+
//https://github.com/Genymobile/scrcpy/issues/488#issuecomment-567321437
209+
outputFormat.setFloat("max-fps-to-encoder", config.fps);
197210
}
198211
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
199212
if (Build.VERSION.SDK_INT > 23 && isH265) {

app/src/main/java/com/demo/mediacodec/transcode/TranscodeRunner.java

Lines changed: 37 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import android.media.MediaMuxer;
99
import android.net.Uri;
1010
import android.os.Build;
11-
import android.os.Bundle;
1211
import android.os.Handler;
1312
import android.os.HandlerThread;
1413
import android.os.ParcelFileDescriptor;
@@ -62,6 +61,7 @@ public interface OnTranscodeListener {
6261

6362
private String mOriVideoMime;
6463
private int mOriVideoWidth, mOriVideoHeight;
64+
private int mOriVideoFps;
6565
private long mVideoDurationUs;
6666

6767
private OnTranscodeListener listener;
@@ -85,8 +85,6 @@ public interface OnTranscodeListener {
8585
private HandlerThread mEncodeCodecThread;
8686
private Handler mEncodeCodecHandler;
8787

88-
private final Object hdrInfoLock = new Object();
89-
9088
public TranscodeRunner(Context context, Uri uri) {
9189
mContext = context;
9290
mVideoUri = uri;
@@ -266,6 +264,7 @@ private void _getOriVideoInfo() {
266264
mOriVideoMime = mOriVideoFormat.getString(MediaFormat.KEY_MIME);
267265
mOriVideoWidth = mOriVideoFormat.getInteger(MediaFormat.KEY_WIDTH);
268266
mOriVideoHeight = mOriVideoFormat.getInteger(MediaFormat.KEY_HEIGHT);
267+
mOriVideoFps = mOriVideoFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
269268
mVideoDurationUs = mOriVideoFormat.getLong(MediaFormat.KEY_DURATION);
270269
}
271270

@@ -307,18 +306,6 @@ public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index,
307306
long presentationTimeUs = info.presentationTimeUs;
308307
callProgress((int) (presentationTimeUs * 100 / mVideoDurationUs));
309308
Log.i("Encoder", "编码pts: " + presentationTimeUs);
310-
311-
synchronized (hdrInfoLock) {
312-
int count = hdrCounter.get();
313-
if (count > 0) {
314-
count = hdrCounter.decrementAndGet();
315-
Log.i("Encoder", "消费一个hdrInfo, pts: " + info.presentationTimeUs +
316-
" 剩余:" + count);
317-
if (count == 0) {
318-
hdrInfoLock.notifyAll();
319-
}
320-
}
321-
}
322309
}
323310
codec.releaseOutputBuffer(index, false);
324311
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
@@ -367,10 +354,15 @@ public void onOutputFormatChanged(@NonNull MediaCodec codec,
367354
}
368355
}
369356

357+
private int decodeFrameIndex;
358+
private int encodeFrameIndex;
359+
370360
/**
371361
* 准备解码器
372362
*/
373363
private void prepareDecoder(VideoOutputConfig outputConfig, AtomicInteger hdrCounter) throws Exception {
364+
decodeFrameIndex = 0;
365+
encodeFrameIndex = 0;
374366
String codecName = MediaCodecUtils.findDecoderByFormat(mOriVideoFormat);
375367
if (TextUtils.isEmpty(codecName)) {
376368
if (MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION.equals(mOriVideoMime)) {
@@ -422,44 +414,39 @@ public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
422414
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index,
423415
@NonNull MediaCodec.BufferInfo info) {
424416
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
425-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
426-
byte[] hdr10Info = null;
427-
MediaFormat f = codec.getOutputFormat(index);
428-
if (f.containsKey(MediaFormat.KEY_HDR10_PLUS_INFO)) {
429-
ByteBuffer byteBuffer =
430-
f.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO);
431-
if (byteBuffer != null) {
432-
int len = byteBuffer.limit();
433-
hdr10Info = new byte[len];
434-
byteBuffer.get(hdr10Info, 0, len);
435-
}
436-
}
437-
if (hdr10Info != null) {
438-
synchronized (hdrInfoLock) {
439-
if (hdrCounter.get() != 0) {
440-
Log.i("Decoder", "有没被消费的hdrInfo,等待消费");
441-
try {
442-
hdrInfoLock.wait(1000);
443-
} catch (InterruptedException e) {
444-
e.printStackTrace();
417+
MediaFormat f = codec.getOutputFormat(index);
418+
boolean render = info.size > 0;
419+
if (render && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
420+
//如果是Android O以下,进行手动丢帧来降低帧率
421+
if (Math.abs(info.presentationTimeUs - mVideoDurationUs) < 100_000L) {
422+
//最后100ms之内,不丢帧
423+
} else {
424+
if (mOriVideoFps > 0 && mConfig.fps < mOriVideoFps) {
425+
//如果相比原视频需要降低帧率,那么需要计算是否需要丢帧
426+
long oriTimeInternal = 1000000000L / mOriVideoFps;
427+
long dstTimeInternal = 1000000000L / mConfig.fps;
428+
long dstTime = encodeFrameIndex * dstTimeInternal;
429+
int indexPre = (int) (dstTime / oriTimeInternal);
430+
int indexAfter = indexPre + 1;
431+
//比较pre和after对应的时间,看取哪个合适
432+
long offset1 = Math.abs(oriTimeInternal * indexPre - dstTime);
433+
long offset2 = Math.abs(oriTimeInternal * indexAfter - dstTime);
434+
if (offset1 <= offset2) {
435+
//采用indexPre
436+
if (decodeFrameIndex != indexPre) {
437+
//和indexPre不等,则进行丢帧
438+
render = false;
439+
}
440+
} else {
441+
//采用indexAfter
442+
if (decodeFrameIndex != indexAfter) {
443+
//和indexAfter不等,则进行丢帧
444+
render = false;
445445
}
446-
}
447-
448-
int count = hdrCounter.incrementAndGet();
449-
Log.i("Decoder",
450-
"新增一个hdrInfo等待消费, pts: " + info.presentationTimeUs +
451-
" 当前数量: " + count);
452-
Bundle codecParameters = new Bundle();
453-
codecParameters.putByteArray(MediaCodec.PARAMETER_KEY_HDR10_PLUS_INFO,
454-
hdr10Info);
455-
if (mEncoder != null) {
456-
mEncoder.setParameters(codecParameters);
457446
}
458447
}
459448
}
460449
}
461-
462-
boolean render = info.size > 0;
463450
codec.releaseOutputBuffer(index, render);
464451
if (render) {
465452
// 切换GL线程
@@ -473,7 +460,9 @@ public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index,
473460
mEncoderInputSurface.setPresentationTime(info.presentationTimeUs * 1000);
474461
mEncoderInputSurface.swapBuffers();
475462
mEncoderInputSurface.makeUnCurrent();
463+
encodeFrameIndex++;
476464
}
465+
decodeFrameIndex++;
477466
Log.i("Decoder", "解码pts: " + info.presentationTimeUs);
478467
} else {
479468
codec.releaseOutputBuffer(index, false);

0 commit comments

Comments
 (0)