Preliminary support of VP9 HW encoder on Android.

Not fully tested yet. Verified in test loopback application
with fake VP9 codec factory.
Assume that encoder generates bitstream in non flexible mode with
one temporal and one spatial layers.

R=magjed@webrtc.org

Review URL: https://codereview.webrtc.org/1451953002 .

Cr-Commit-Position: refs/heads/master@{#10695}
This commit is contained in:
Alex Glaznev
2015-11-18 13:06:42 -08:00
parent 2557b86e76
commit ad948c42a1
3 changed files with 65 additions and 8 deletions

View File

@ -856,7 +856,7 @@ void MediaCodecVideoDecoderFactory::SetEGLContext(
webrtc::VideoDecoder* MediaCodecVideoDecoderFactory::CreateVideoDecoder( webrtc::VideoDecoder* MediaCodecVideoDecoderFactory::CreateVideoDecoder(
VideoCodecType type) { VideoCodecType type) {
if (supported_codec_types_.empty()) { if (supported_codec_types_.empty()) {
ALOGE << "No HW video decoder for type " << (int)type; ALOGW << "No HW video decoder for type " << (int)type;
return NULL; return NULL;
} }
for (VideoCodecType codec_type : supported_codec_types_) { for (VideoCodecType codec_type : supported_codec_types_) {
@ -866,7 +866,7 @@ webrtc::VideoDecoder* MediaCodecVideoDecoderFactory::CreateVideoDecoder(
AttachCurrentThreadIfNeeded(), type, render_egl_context_); AttachCurrentThreadIfNeeded(), type, render_egl_context_);
} }
} }
ALOGE << "Can not find HW video decoder for type " << (int)type; ALOGW << "Can not find HW video decoder for type " << (int)type;
return NULL; return NULL;
} }

View File

@ -57,6 +57,7 @@ using webrtc::VideoCodec;
using webrtc::VideoCodecType; using webrtc::VideoCodecType;
using webrtc::kVideoCodecH264; using webrtc::kVideoCodecH264;
using webrtc::kVideoCodecVP8; using webrtc::kVideoCodecVP8;
using webrtc::kVideoCodecVP9;
namespace webrtc_jni { namespace webrtc_jni {
@ -213,6 +214,12 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
// H264 bitstream parser, used to extract QP from encoded bitstreams. // H264 bitstream parser, used to extract QP from encoded bitstreams.
webrtc::H264BitstreamParser h264_bitstream_parser_; webrtc::H264BitstreamParser h264_bitstream_parser_;
// VP9 variables to populate codec specific structure.
webrtc::GofInfoVP9 gof_; // Contains each frame's temporal information for
// non-flexible VP9 mode.
uint8_t tl0_pic_idx_;
size_t gof_idx_;
}; };
MediaCodecVideoEncoder::~MediaCodecVideoEncoder() { MediaCodecVideoEncoder::~MediaCodecVideoEncoder() {
@ -287,6 +294,7 @@ MediaCodecVideoEncoder::MediaCodecVideoEncoder(
j_info_presentation_timestamp_us_field_ = GetFieldID( j_info_presentation_timestamp_us_field_ = GetFieldID(
jni, j_output_buffer_info_class, "presentationTimestampUs", "J"); jni, j_output_buffer_info_class, "presentationTimestampUs", "J");
CHECK_EXCEPTION(jni) << "MediaCodecVideoEncoder ctor failed"; CHECK_EXCEPTION(jni) << "MediaCodecVideoEncoder ctor failed";
srand(time(NULL));
AllowBlockingCalls(); AllowBlockingCalls();
} }
@ -307,8 +315,8 @@ int32_t MediaCodecVideoEncoder::InitEncode(
<< codecType_; << codecType_;
ALOGD << "InitEncode request"; ALOGD << "InitEncode request";
scale_ = webrtc::field_trial::FindFullName( scale_ = (codecType_ != kVideoCodecVP9) && (webrtc::field_trial::FindFullName(
"WebRTC-MediaCodecVideoEncoder-AutomaticResize") == "Enabled"; "WebRTC-MediaCodecVideoEncoder-AutomaticResize") == "Enabled");
ALOGD << "Encoder automatic resize " << (scale_ ? "enabled" : "disabled"); ALOGD << "Encoder automatic resize " << (scale_ ? "enabled" : "disabled");
if (scale_) { if (scale_) {
if (codecType_ == kVideoCodecVP8) { if (codecType_ == kVideoCodecVP8) {
@ -458,6 +466,9 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
frame_rtc_times_ms_.clear(); frame_rtc_times_ms_.clear();
drop_next_input_frame_ = false; drop_next_input_frame_ = false;
picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF; picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF;
gof_.SetGofInfoVP9(webrtc::TemporalStructureMode::kTemporalStructureMode1);
tl0_pic_idx_ = static_cast<uint8_t>(rand());
gof_idx_ = 0;
// We enforce no extra stride/padding in the format creation step. // We enforce no extra stride/padding in the format creation step.
jobject j_video_codec_enum = JavaEnumFromIndex( jobject j_video_codec_enum = JavaEnumFromIndex(
@ -836,19 +847,42 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) {
info.codecSpecific.VP8.layerSync = false; info.codecSpecific.VP8.layerSync = false;
info.codecSpecific.VP8.tl0PicIdx = webrtc::kNoTl0PicIdx; info.codecSpecific.VP8.tl0PicIdx = webrtc::kNoTl0PicIdx;
info.codecSpecific.VP8.keyIdx = webrtc::kNoKeyIdx; info.codecSpecific.VP8.keyIdx = webrtc::kNoKeyIdx;
picture_id_ = (picture_id_ + 1) & 0x7FFF; } else if (codecType_ == kVideoCodecVP9) {
if (key_frame) {
gof_idx_ = 0;
}
info.codecSpecific.VP9.picture_id = picture_id_;
info.codecSpecific.VP9.inter_pic_predicted = key_frame ? false : true;
info.codecSpecific.VP9.flexible_mode = false;
info.codecSpecific.VP9.ss_data_available = key_frame ? true : false;
info.codecSpecific.VP9.tl0_pic_idx = tl0_pic_idx_++;
info.codecSpecific.VP9.temporal_idx = webrtc::kNoTemporalIdx;
info.codecSpecific.VP9.spatial_idx = webrtc::kNoSpatialIdx;
info.codecSpecific.VP9.temporal_up_switch = true;
info.codecSpecific.VP9.inter_layer_predicted = false;
info.codecSpecific.VP9.gof_idx =
static_cast<uint8_t>(gof_idx_++ % gof_.num_frames_in_gof);
info.codecSpecific.VP9.num_spatial_layers = 1;
info.codecSpecific.VP9.spatial_layer_resolution_present = false;
if (info.codecSpecific.VP9.ss_data_available) {
info.codecSpecific.VP9.spatial_layer_resolution_present = true;
info.codecSpecific.VP9.width[0] = width_;
info.codecSpecific.VP9.height[0] = height_;
info.codecSpecific.VP9.gof.CopyGofInfoVP9(gof_);
}
} }
picture_id_ = (picture_id_ + 1) & 0x7FFF;
// Generate a header describing a single fragment. // Generate a header describing a single fragment.
webrtc::RTPFragmentationHeader header; webrtc::RTPFragmentationHeader header;
memset(&header, 0, sizeof(header)); memset(&header, 0, sizeof(header));
if (codecType_ == kVideoCodecVP8) { if (codecType_ == kVideoCodecVP8 || codecType_ == kVideoCodecVP9) {
header.VerifyAndAllocateFragmentationHeader(1); header.VerifyAndAllocateFragmentationHeader(1);
header.fragmentationOffset[0] = 0; header.fragmentationOffset[0] = 0;
header.fragmentationLength[0] = image->_length; header.fragmentationLength[0] = image->_length;
header.fragmentationPlType[0] = 0; header.fragmentationPlType[0] = 0;
header.fragmentationTimeDiff[0] = 0; header.fragmentationTimeDiff[0] = 0;
if (scale_) { if (codecType_ == kVideoCodecVP8 && scale_) {
int qp; int qp;
if (webrtc::vp8::GetQp(payload, payload_size, &qp)) if (webrtc::vp8::GetQp(payload, payload_size, &qp))
quality_scaler_.ReportQP(qp); quality_scaler_.ReportQP(qp);
@ -973,6 +1007,16 @@ MediaCodecVideoEncoderFactory::MediaCodecVideoEncoderFactory() {
MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS)); MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS));
} }
bool is_vp9_hw_supported = jni->CallStaticBooleanMethod(
j_encoder_class,
GetStaticMethodID(jni, j_encoder_class, "isVp9HwSupported", "()Z"));
CHECK_EXCEPTION(jni);
if (is_vp9_hw_supported) {
ALOGD << "VP9 HW Encoder supported.";
supported_codecs_.push_back(VideoCodec(kVideoCodecVP9, "VP9",
MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS));
}
bool is_h264_hw_supported = jni->CallStaticBooleanMethod( bool is_h264_hw_supported = jni->CallStaticBooleanMethod(
j_encoder_class, j_encoder_class,
GetStaticMethodID(jni, j_encoder_class, "isH264HwSupported", "()Z")); GetStaticMethodID(jni, j_encoder_class, "isH264HwSupported", "()Z"));
@ -989,6 +1033,7 @@ MediaCodecVideoEncoderFactory::~MediaCodecVideoEncoderFactory() {}
webrtc::VideoEncoder* MediaCodecVideoEncoderFactory::CreateVideoEncoder( webrtc::VideoEncoder* MediaCodecVideoEncoderFactory::CreateVideoEncoder(
VideoCodecType type) { VideoCodecType type) {
if (supported_codecs_.empty()) { if (supported_codecs_.empty()) {
ALOGW << "No HW video encoder for type " << (int)type;
return NULL; return NULL;
} }
for (std::vector<VideoCodec>::const_iterator it = supported_codecs_.begin(); for (std::vector<VideoCodec>::const_iterator it = supported_codecs_.begin();
@ -999,6 +1044,7 @@ webrtc::VideoEncoder* MediaCodecVideoEncoderFactory::CreateVideoEncoder(
return new MediaCodecVideoEncoder(AttachCurrentThreadIfNeeded(), type); return new MediaCodecVideoEncoder(AttachCurrentThreadIfNeeded(), type);
} }
} }
ALOGW << "Can not find HW video encoder for type " << (int)type;
return NULL; return NULL;
} }

View File

@ -74,10 +74,14 @@ public class MediaCodecVideoEncoder {
private MediaCodec mediaCodec; private MediaCodec mediaCodec;
private ByteBuffer[] outputBuffers; private ByteBuffer[] outputBuffers;
private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8";
private static final String VP9_MIME_TYPE = "video/x-vnd.on2.vp9";
private static final String H264_MIME_TYPE = "video/avc"; private static final String H264_MIME_TYPE = "video/avc";
// List of supported HW VP8 codecs. // List of supported HW VP8 codecs.
private static final String[] supportedVp8HwCodecPrefixes = private static final String[] supportedVp8HwCodecPrefixes =
{"OMX.qcom.", "OMX.Intel." }; {"OMX.qcom.", "OMX.Intel." };
// List of supported HW VP9 decoders.
private static final String[] supportedVp9HwCodecPrefixes =
{"OMX.qcom."};
// List of supported HW H.264 codecs. // List of supported HW H.264 codecs.
private static final String[] supportedH264HwCodecPrefixes = private static final String[] supportedH264HwCodecPrefixes =
{"OMX.qcom." }; {"OMX.qcom." };
@ -196,13 +200,16 @@ public class MediaCodecVideoEncoder {
} }
} }
} }
return null; // No HW VP8 encoder. return null; // No HW encoder.
} }
public static boolean isVp8HwSupported() { public static boolean isVp8HwSupported() {
return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null; return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null;
} }
public static boolean isVp9HwSupported() {
return findHwEncoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes) != null;
}
public static boolean isH264HwSupported() { public static boolean isH264HwSupported() {
return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null; return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null;
} }
@ -252,6 +259,10 @@ public class MediaCodecVideoEncoder {
mime = VP8_MIME_TYPE; mime = VP8_MIME_TYPE;
properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes);
keyFrameIntervalSec = 100; keyFrameIntervalSec = 100;
} else if (type == VideoCodecType.VIDEO_CODEC_VP9) {
mime = VP9_MIME_TYPE;
properties = findHwEncoder(VP9_MIME_TYPE, supportedH264HwCodecPrefixes);
keyFrameIntervalSec = 100;
} else if (type == VideoCodecType.VIDEO_CODEC_H264) { } else if (type == VideoCodecType.VIDEO_CODEC_H264) {
mime = H264_MIME_TYPE; mime = H264_MIME_TYPE;
properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes);