stagefright: prefer B frames for high frame-rate recording
Some codecs do not support B-frames with temporal layering.
- Prefer temporal layering for slow-motion and time lapse videos
(since B-frames are not as useful for time lapse, and layering is
needed for slow-motion)
- Prefer B-frames for HFR capture
Bug: 27596987
Change-Id: I12f530e9f624e2948e176b5548e0d9d65eed95d8
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 653c30f..24ca582 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1573,6 +1573,7 @@
}
uint32_t tsLayers = 1;
+ bool preferBFrames = true; // we like B-frames as it produces better quality per bitrate
format->setInt32("priority", 0 /* realtime */);
float maxPlaybackFps = mFrameRate; // assume video is only played back at normal speed
@@ -1581,9 +1582,11 @@
// enable layering for all time lapse and high frame rate recordings
if (mFrameRate / mCaptureFps >= 1.9) { // time lapse
+ preferBFrames = false;
tsLayers = 2; // use at least two layers as resulting video will likely be sped up
} else if (mCaptureFps > maxPlaybackFps) { // slow-mo
maxPlaybackFps = mCaptureFps; // assume video will be played back at full capture speed
+ preferBFrames = false;
}
}
@@ -1603,6 +1606,10 @@
uint32_t pLayers = tsLayers - bLayers;
format->setString(
"ts-schema", AStringPrintf("android.generic.%u+%u", pLayers, bLayers));
+
+ // TODO: some encoders do not support B-frames with temporal layering, and we have a
+ // different preference based on use-case. We could move this into camera profiles.
+ format->setInt32("android._prefer-b-frames", preferBFrames);
}
if (mMetaDataStoredInVideoBuffers != kMetadataBufferTypeInvalid) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 0d368e6..4fa4bf6 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -4317,6 +4317,34 @@
return err;
}
+ // TRICKY: if we are enabling temporal layering as well, some codecs may not support layering
+ // when B-frames are enabled. Detect this now so we can disable B frames if temporal layering
+ // is preferred.
+ AString tsSchema;
+ int32_t preferBFrames = (int32_t)false;
+ if (msg->findString("ts-schema", &tsSchema)
+ && (!msg->findInt32("android._prefer-b-frames", &preferBFrames) || !preferBFrames)) {
+ OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE layering;
+ InitOMXParams(&layering);
+ layering.nPortIndex = kPortIndexOutput;
+ if (mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layering, sizeof(layering)) == OK
+ && layering.eSupportedPatterns
+ && layering.nBLayerCountMax == 0) {
+ h264type.nBFrames = 0;
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
+ h264type.nAllowedPictureTypes &= ~OMX_VIDEO_PictureTypeB;
+ ALOGI("disabling B-frames");
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+
return configureBitrate(bitrate, bitrateMode);
}