H264VideoToolBoxEncoder.mm   [plain text]


/*
 * Copyright (C) 2017 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "H264VideoToolBoxEncoder.h"

#include "Logging.h"
#include <wtf/RetainPtr.h>
#include <wtf/SoftLinking.h>

SOFT_LINK_FRAMEWORK_OPTIONAL(VideoToolBox)
SOFT_LINK_POINTER_OPTIONAL(VideoToolBox, kVTVideoEncoderSpecification_Usage, NSString *)

#if USE(LIBWEBRTC) && PLATFORM(COCOA)

namespace WebCore {

static bool isHardwareEncoderForWebRTCAllowed = true;

void H264VideoToolboxEncoder::setHardwareEncoderForWebRTCAllowed(bool allowed)
{
    isHardwareEncoderForWebRTCAllowed = allowed;
}

bool H264VideoToolboxEncoder::hardwareEncoderForWebRTCAllowed()
{
    return isHardwareEncoderForWebRTCAllowed;
}

#if PLATFORM(MAC) && ENABLE(MAC_VIDEO_TOOLBOX)
static inline bool isStandardFrameSize(int32_t width, int32_t height)
{
    // FIXME: Envision relaxing this rule, something like width and height dividable by 4 or 8 should be good enough.
    if (width == 1280)
        return height == 720;
    if (width == 720)
        return height == 1280;
    if (width == 960)
        return height == 540;
    if (width == 540)
        return height == 960;
    if (width == 640)
        return height == 480;
    if (width == 480)
        return height == 640;
    if (width == 288)
        return height == 352;
    if (width == 352)
        return height == 288;
    if (width == 320)
        return height == 240;
    if (width == 240)
        return height == 320;
    return false;
}
#endif

int H264VideoToolboxEncoder::CreateCompressionSession(VTCompressionSessionRef& compressionSession, VTCompressionOutputCallback outputCallback, int32_t width, int32_t height, bool useHardwareEncoder)
{
#if PLATFORM(MAC) && ENABLE(MAC_VIDEO_TOOLBOX)
    // This code is deriving from libwebrtc h264_video_toolbox_encoder.h
    // Set source image buffer attributes. These attributes will be present on
    // buffers retrieved from the encoder's pixel buffer pool.
    const size_t attributesSize = 3;
    CFTypeRef keys[attributesSize] = {
        kCVPixelBufferOpenGLCompatibilityKey,
        kCVPixelBufferIOSurfacePropertiesKey,
        kCVPixelBufferPixelFormatTypeKey
    };
    auto ioSurfaceValue = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    int64_t nv12type = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
    auto pixelFormat = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &nv12type));
    CFTypeRef values[attributesSize] = {kCFBooleanTrue, ioSurfaceValue.get(), pixelFormat.get()};
    auto sourceAttributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, attributesSize, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));

    OSStatus status = -1;
    if (useHardwareEncoder) {
        NSDictionary* encoderSpecification = @{ (NSString*)kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder : @true };
        status = VTCompressionSessionCreate(kCFAllocatorDefault, width, height, kCMVideoCodecType_H264,
            (__bridge CFDictionaryRef)encoderSpecification, sourceAttributes.get(), nullptr, outputCallback, this, &compressionSession);
    }

    if (status == noErr && compressionSession) {
        RELEASE_LOG(WebRTC, "H264VideoToolboxEncoder: Using H264 hardware encoder");
        return noErr;
    }

    if (!isStandardFrameSize(width, height)) {
        RELEASE_LOG(WebRTC, "Using H264 software encoder with non standard size is not supported");
        DestroyCompressionSession();
        return -1;
    }

    if (!getkVTVideoEncoderSpecification_Usage()) {
        RELEASE_LOG(WebRTC, "H264VideoToolboxEncoder: Cannot create a H264 software encoder");
        return -1;
    }

    RELEASE_LOG(WebRTC, "H264VideoToolboxEncoder: Using H264 software encoder");

    NSDictionary* encoderSpecification = @{ (NSString*)kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder : @false, (NSString*)getkVTVideoEncoderSpecification_Usage() : @1 };
    status = VTCompressionSessionCreate(kCFAllocatorDefault, width, height, kCMVideoCodecType_H264,
        (__bridge CFDictionaryRef)encoderSpecification, sourceAttributes.get(), nullptr, outputCallback, this, &compressionSession);

    return status;
#else
    UNUSED_PARAM(useHardwareEncoder);
    return webrtc::H264VideoToolboxEncoder::CreateCompressionSession(compressionSession, outputCallback, width, height, hardwareEncoderForWebRTCAllowed() ? useHardwareEncoder : false);
#endif
}

}

#endif