task.h 9.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/******************************************************************************\
Copyright (c) 2018, Intel Corporation
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.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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.

This sample was distributed or derived from the Intel's Media Samples package.
The original version of this sample may be obtained from https://software.intel.com/en-us/intel-media-server-studio
or https://software.intel.com/en-us/media-client-solutions-support.
\**********************************************************************************/

20 21
#ifndef __HEVC_FEI_ABR_TASK__
#define __HEVC_FEI_ABR_TASK__
22 23

#include "buffer_pool.h"
24 25
#include "base_allocator.h"

26 27
#include <queue>
#include <cmath>
28
#include <map>
29 30 31

struct FrameStatData
{
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
    mfxU32 EncodedOrder                  = 0xffffffff;
    mfxU32 DisplayOrder                  = 0xffffffff;
    mfxU8  QP                            = 0xff;
    mfxF64 QstepOriginal                 = -1.0;
    mfxF64 QstepCalculated               = -1.0;
    mfxU16 FrameType                     = MFX_FRAMETYPE_UNKNOWN;
    mfxI32 POC                           = -100;
    mfxF64 ShareIntra                    = 0.0;
    mfxF64 Propagation                   = 0.0;
    mfxU32 FrameSize                     = 0;
    mfxF64 VisualDistortion              = 0.0; // MSE or any approximation like number of nonzero coefficients
    mfxF64 ComplexityOriginal            = 0.0;
    mfxF64 ComplexitySmoothed            = 0.0;

    mfxU32 NPixelsPropagated             = 0;
    mfxF64 NPixelsTransitivelyPropagated = 1.0;
    mfxU32 NPixelsInFrame                = 0;

    mfxU64 BitsEncoded                   = 0;
    mfxU64 BitsPredicted                 = 0;

    // Map of FrameDisplayOrder <=> NumOfPixels.
    // Stores amount of pixels predicted FROM reference frame.
    std::map<mfxU32, mfxU32> NumPixelsPredictedFromRef;
    // Map of FrameDisplayOrder <=> ShareOfPixels.
    // Shows share of INTER pixels predicted FROM reference frame.
    std::map<mfxU32, mfxF64> ShareOfPredictedPixelsFromRef;

    bool Contains(mfxU32 fo)
    {
        return NumPixelsPredictedFromRef.find(fo) != NumPixelsPredictedFromRef.end();
    }

    void AddPxlCount(mfxU32 fo, mfxU32 amount)
    {
        if (Contains(fo))
        {
            NumPixelsPredictedFromRef[fo] += amount;
        }
        else
        {
            NumPixelsPredictedFromRef[fo] = amount;
        }
    }

    void FillProp()
    {
        for (auto & item : NumPixelsPredictedFromRef)
        {
            mfxF64 denom = (1.0 - ShareIntra) * NPixelsInFrame;
            if (denom == 0.0)
                throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "ERROR: FillProp - zero denominator");

            ShareOfPredictedPixelsFromRef[item.first] = mfxF64(item.second) / denom;
        }
    }
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
};

struct HevcTaskDSO
{
    HevcTaskDSO()                              = default;
    HevcTaskDSO(HevcTaskDSO const&)            = delete;
    HevcTaskDSO(HevcTaskDSO &&)                = default;
    HevcTaskDSO& operator=(HevcTaskDSO const&) = delete;
    HevcTaskDSO& operator=(HevcTaskDSO &&)     = default;

    mfxFrameSurface1 *  m_surf       = nullptr;
    mfxU16              m_frameType  = MFX_FRAMETYPE_UNKNOWN;
    mfxU16              m_frameOrder = 0xffff;

    std::vector<mfxI32> m_dpb;
    std::vector<mfxI32> m_refListActive[2];

    MVPPool::Type       m_mvp;
106
    mfxU32              m_nMvPredictors[2] = {0, 0};
107 108

    CTUCtrlPool::Type   m_ctuCtrl;
109
    bool                m_isGPBFrame = false;
110 111 112 113 114 115 116 117 118 119

    // Data for BRC
    FrameStatData       m_statData;
};

class LA_queue
{
public:
    LA_queue(mfxU16 la_depth = 1, const msdk_char* yuv_file = nullptr)
        : m_LookAheadDepth(la_depth)
120
        , m_calc_mse_from_file(yuv_file[0])
121
    {
122
        if (m_calc_mse_from_file)
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
        {
            m_fpYUV = fopen(yuv_file, "rb");

            if (!m_fpYUV)
                throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to open YUV file");
        }
    }

    ~LA_queue()
    {
        if (m_fpYUV)
            fclose(m_fpYUV);
    }

    LA_queue(LA_queue const&)            = delete;
    LA_queue(LA_queue &&)                = default;
    LA_queue& operator=(LA_queue const&) = delete;
    LA_queue& operator=(LA_queue &&)     = default;

    void AddTask(std::shared_ptr<HevcTaskDSO> && task)
    {
        if (!task.get())
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to AddTask: task pointer is null");

        if (task->m_surf)
            msdk_atomic_inc16((volatile mfxU16*)&task->m_surf->Data.Locked);

        task->m_statData.EncodedOrder = m_submittedTasks;

152
        if (m_calc_mse_from_file)
153
        {
154
            task->m_statData.VisualDistortion = CalcMSE(*task);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
        }

        la_queue.push(std::move(task));
        ++m_submittedTasks;
    }

    bool GetTask(std::shared_ptr<HevcTaskDSO> & task)
    {
        if (la_queue.empty())
        {
            if (!m_drain)
                throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to GetTask: la_queue is empty");
            else
                return false;
        }

171
        if (!m_drain && la_queue.size() <= m_LookAheadDepth)
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
        {
            return false;
        }

        task = std::move(la_queue.front());
        la_queue.pop();

        if (task.get() && task->m_surf)
            msdk_atomic_dec16((volatile mfxU16*)&task->m_surf->Data.Locked);

        return true;
    }

    void SetAllocator(MFXFrameAllocator* pAllocator)
    {
        m_pAllocator = pAllocator;
    }

    mfxStatus QueryIOSurf(mfxFrameAllocRequest* request)
    {
        if (!request)
        {
            return MFX_ERR_NULL_PTR;
        }

        request->NumFrameMin = request->NumFrameSuggested = m_LookAheadDepth + 1;

        return MFX_ERR_NONE;
    }

    std::shared_ptr<HevcTaskDSO> & Back()
    {
        if (la_queue.empty())
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Calling Back() for empty queue");

        return la_queue.back();
    }

    void StartDrain()
    {
        m_drain = true;
    }

private:
    std::queue<std::shared_ptr<HevcTaskDSO>> la_queue;

218 219 220 221
    mfxU16 m_LookAheadDepth     = 100;
    bool   m_calc_mse_from_file = false;
    mfxU32 m_submittedTasks     = 0;
    bool   m_drain              = false;
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

    // for MSE calculation
    MFXFrameAllocator* m_pAllocator = nullptr;

    std::vector<mfxU8> m_YUVdata;
    FILE* m_fpYUV = nullptr;

    mfxF64 CalcMSE(HevcTaskDSO& task)
    {
        if (!m_pAllocator)
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to calculate MSE: m_pAllocator is null");

        if (!task.m_surf)
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to calculate MSE: task.m_surf is null");

        mfxU32 planeYsize = task.m_surf->Info.CropW * task.m_surf->Info.CropH;
        mfxU64 NthFramePlaneYshift = mfxU64(task.m_statData.DisplayOrder)*(planeYsize + (((task.m_surf->Info.CropW + 1) >> 1)*((task.m_surf->Info.CropH + 1) >> 1) << 1));

        if (fseek(m_fpYUV, NthFramePlaneYshift, SEEK_SET))
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to calculate MSE: failed to fseek to current frame in YUV file");

        m_YUVdata.resize(planeYsize);

        if (fread(m_YUVdata.data(), 1, planeYsize, m_fpYUV) != planeYsize)
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to calculate MSE: failed to fread current frame from YUV file");

        mfxStatus sts = m_pAllocator->Lock(m_pAllocator->pthis, task.m_surf->Data.MemId, &(task.m_surf->Data));
        if (sts != MFX_ERR_NONE)
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to calculate MSE: m_pAllocator->Lock failed");

252
        mfxU64 l2normSq = 0;
253 254
        for (mfxU32 i = 0; i < m_YUVdata.size(); ++i)
        {
255
            l2normSq += std::pow(m_YUVdata[i] - task.m_surf->Data.Y[(i / task.m_surf->Info.Width)*task.m_surf->Data.Pitch + (i % task.m_surf->Info.Width)], 2);
256 257 258 259 260 261
        }

        sts = m_pAllocator->Unlock(m_pAllocator->pthis, task.m_surf->Data.MemId, &(task.m_surf->Data));
        if (sts != MFX_ERR_NONE)
            throw mfxError(MFX_ERR_UNDEFINED_BEHAVIOR, "Failed to calculate MSE: m_pAllocator->Unlock failed");

262
        return mfxF64(l2normSq) / m_YUVdata.size();
263 264
    }
};
265 266

#endif // __HEVC_FEI_ABR_TASK__