aboutsummaryrefslogtreecommitdiff
path: root/common/EncodeHelpers.cpp
blob: 2ab0e711b7fec84e6883b2b097c6af208c4bae90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//#define LOG_NDEBUG 0
#define LOG_TAG "EncodeHelpers"

#include <v4l2_codec2/common/EncodeHelpers.h>

#include <linux/v4l2-controls.h>

#include <C2AllocatorGralloc.h>
#include <cutils/native_handle.h>
#include <ui/GraphicBuffer.h>
#include <utils/Log.h>

#include <v4l2_codec2/common/NalParser.h>

namespace android {

media::VideoCodecProfile c2ProfileToVideoCodecProfile(C2Config::profile_t profile) {
    switch (profile) {
    case C2Config::PROFILE_AVC_BASELINE:
        return media::VideoCodecProfile::H264PROFILE_BASELINE;
    case C2Config::PROFILE_AVC_MAIN:
        return media::VideoCodecProfile::H264PROFILE_MAIN;
    case C2Config::PROFILE_AVC_EXTENDED:
        return media::VideoCodecProfile::H264PROFILE_EXTENDED;
    case C2Config::PROFILE_AVC_HIGH:
        return media::VideoCodecProfile::H264PROFILE_HIGH;
    case C2Config::PROFILE_AVC_HIGH_10:
        return media::VideoCodecProfile::H264PROFILE_HIGH10PROFILE;
    case C2Config::PROFILE_AVC_HIGH_422:
        return media::VideoCodecProfile::H264PROFILE_HIGH422PROFILE;
    case C2Config::PROFILE_AVC_HIGH_444_PREDICTIVE:
        return media::VideoCodecProfile::H264PROFILE_HIGH444PREDICTIVEPROFILE;
    case C2Config::PROFILE_AVC_SCALABLE_BASELINE:
        return media::VideoCodecProfile::H264PROFILE_SCALABLEBASELINE;
    case C2Config::PROFILE_AVC_SCALABLE_HIGH:
        return media::VideoCodecProfile::H264PROFILE_SCALABLEHIGH;
    case C2Config::PROFILE_AVC_STEREO_HIGH:
        return media::VideoCodecProfile::H264PROFILE_STEREOHIGH;
    case C2Config::PROFILE_AVC_MULTIVIEW_HIGH:
        return media::VideoCodecProfile::H264PROFILE_MULTIVIEWHIGH;
    case C2Config::PROFILE_VP9_0:
        return media::VideoCodecProfile::VP9PROFILE_PROFILE0;
    case C2Config::PROFILE_VP9_1:
        return media::VideoCodecProfile::VP9PROFILE_PROFILE1;
    case C2Config::PROFILE_VP9_2:
        return media::VideoCodecProfile::VP9PROFILE_PROFILE2;
    case C2Config::PROFILE_VP9_3:
        return media::VideoCodecProfile::VP9PROFILE_PROFILE3;
    default:
        ALOGE("Unrecognizable C2 profile (value = 0x%x)...", profile);
        return media::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN;
    }
}

uint8_t c2LevelToV4L2Level(C2Config::level_t level) {
    switch (level) {
    case C2Config::LEVEL_AVC_1:
        return V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
    case C2Config::LEVEL_AVC_1B:
        return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
    case C2Config::LEVEL_AVC_1_1:
        return V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
    case C2Config::LEVEL_AVC_1_2:
        return V4L2_MPEG_VIDEO_H264_LEVEL_1_2;
    case C2Config::LEVEL_AVC_1_3:
        return V4L2_MPEG_VIDEO_H264_LEVEL_1_3;
    case C2Config::LEVEL_AVC_2:
        return V4L2_MPEG_VIDEO_H264_LEVEL_2_0;
    case C2Config::LEVEL_AVC_2_1:
        return V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
    case C2Config::LEVEL_AVC_2_2:
        return V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
    case C2Config::LEVEL_AVC_3:
        return V4L2_MPEG_VIDEO_H264_LEVEL_3_0;
    case C2Config::LEVEL_AVC_3_1:
        return V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
    case C2Config::LEVEL_AVC_3_2:
        return V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
    case C2Config::LEVEL_AVC_4:
        return V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
    case C2Config::LEVEL_AVC_4_1:
        return V4L2_MPEG_VIDEO_H264_LEVEL_4_1;
    case C2Config::LEVEL_AVC_4_2:
        return V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
    case C2Config::LEVEL_AVC_5:
        return V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
    case C2Config::LEVEL_AVC_5_1:
        return V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
    default:
        ALOGE("Unrecognizable C2 level (value = 0x%x)...", level);
        return 0;
    }
}

android_ycbcr getGraphicBlockInfo(const C2ConstGraphicBlock& block) {
    uint32_t width, height, format, stride, igbp_slot, generation;
    uint64_t usage, igbp_id;
    android::_UnwrapNativeCodec2GrallocMetadata(block.handle(), &width, &height, &format, &usage,
                                                &stride, &generation, &igbp_id, &igbp_slot);
    native_handle_t* grallocHandle = android::UnwrapNativeCodec2GrallocHandle(block.handle());
    sp<GraphicBuffer> buf = new GraphicBuffer(grallocHandle, GraphicBuffer::CLONE_HANDLE, width,
                                              height, format, 1, usage, stride);
    native_handle_delete(grallocHandle);

    // Pass SW flag so that ARCVM returns the guest buffer dimensions instead
    // of the host buffer dimensions. This means we will have to convert the
    // return value from ptrs to buffer offsets ourselves.
    android_ycbcr ycbcr = {};
    int32_t status = buf->lockYCbCr(GRALLOC_USAGE_SW_READ_OFTEN, &ycbcr);
    if (status != OK) ALOGE("lockYCbCr is failed: %d", (int)status);
    buf->unlock();

    uintptr_t y = reinterpret_cast<uintptr_t>(ycbcr.y);
    ycbcr.y = nullptr;
    ycbcr.cb = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ycbcr.cb) - y);
    ycbcr.cr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ycbcr.cr) - y);

    return ycbcr;
}

void extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data,
                    size_t length) {
    // Android frameworks needs 4 bytes start code.
    constexpr uint8_t kStartCode[] = {0x00, 0x00, 0x00, 0x01};
    constexpr int kStartCodeLength = 4;

    csd->reset();

    // Temporarily allocate a byte array to copy codec config data. This should be freed after
    // codec config data extraction is done.
    auto tmpConfigData = std::make_unique<uint8_t[]>(length);
    uint8_t* tmpOutput = tmpConfigData.get();
    uint8_t* tmpConfigDataEnd = tmpOutput + length;

    NalParser parser(data, length);
    while (parser.locateNextNal()) {
        if (parser.length() == 0) continue;
        uint8_t nalType = parser.type();
        ALOGV("find next NAL: type=%d, length=%zu", nalType, parser.length());
        if (nalType != NalParser::kSPSType && nalType != NalParser::kPPSType) continue;

        if (tmpOutput + kStartCodeLength + parser.length() > tmpConfigDataEnd) {
            ALOGE("Buffer overflow on extracting codec config data (length=%zu)", length);
            return;
        }
        std::memcpy(tmpOutput, kStartCode, kStartCodeLength);
        tmpOutput += kStartCodeLength;
        std::memcpy(tmpOutput, parser.data(), parser.length());
        tmpOutput += parser.length();
    }

    size_t configDataLength = tmpOutput - tmpConfigData.get();
    ALOGV("Extracted codec config data: length=%zu", configDataLength);
    *csd = C2StreamInitDataInfo::output::AllocUnique(configDataLength, 0u);
    std::memcpy((*csd)->m.value, tmpConfigData.get(), configDataLength);
}

}  // namespace android