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 163 164 165
|
cimport libav as lib
from libc.stdint cimport int32_t, uint8_t
from enum import IntEnum
VideoEncParamsType = IntEnum(
"AVVideoEncParamsType",
{
"NONE": <int>lib.AV_VIDEO_ENC_PARAMS_NONE,
"VP9": <int>lib.AV_VIDEO_ENC_PARAMS_VP9,
"H264": <int>lib.AV_VIDEO_ENC_PARAMS_H264,
"MPEG2": <int>lib.AV_VIDEO_ENC_PARAMS_MPEG2,
},
)
cdef class VideoEncParams(SideData):
def __repr__(self):
return f"<av.sidedata.VideoEncParams, nb_blocks={self.nb_blocks}, codec_type={self.codec_type}, qp={self.qp}>"
@property
def nb_blocks(self):
"""
Number of blocks in the array
May be 0, in which case no per-block information is present. In this case
the values of blocks_offset / block_size are unspecified and should not
be accessed.
"""
return (<lib.AVVideoEncParams*> self.ptr.data).nb_blocks
@property
def blocks_offset(self):
"""
Offset in bytes from the beginning of this structure at which the array of blocks starts.
"""
return (<lib.AVVideoEncParams*> self.ptr.data).blocks_offset
@property
def block_size(self):
"""
Size of each block in bytes. May not match sizeof(AVVideoBlockParams).
"""
return (<lib.AVVideoEncParams*> self.ptr.data).block_size
@property
def codec_type(self):
"""
Type of the parameters (the codec they are used with).
"""
cdef lib.AVVideoEncParamsType t = (<lib.AVVideoEncParams*> self.ptr.data).type
return VideoEncParamsType(<int>t)
@property
def qp(self):
"""
Base quantisation parameter for the frame. The final quantiser for a
given block in a given plane is obtained from this value, possibly
combined with `delta_qp` and the per-block delta in a manner
documented for each type.
"""
return (<lib.AVVideoEncParams*> self.ptr.data).qp
@property
def delta_qp(self):
"""
Quantisation parameter offset from the base (per-frame) qp for a given
plane (first index) and AC/DC coefficients (second index).
"""
cdef lib.AVVideoEncParams *p = <lib.AVVideoEncParams*> self.ptr.data
return [[p.delta_qp[i][j] for j in range(2)] for i in range(4)]
def block_params(self, idx):
"""
Get the encoding parameters for a given block
"""
# Validate given index
if idx < 0 or idx >= self.nb_blocks:
raise ValueError("Expected idx in range [0, nb_blocks)")
return VideoBlockParams(self, idx)
def qp_map(self):
"""
Convenience method that creates a 2-D map with the quantization parameters per macroblock.
Only for MPEG2 and H264 encoded videos.
"""
import numpy as np
cdef int mb_h = (self.frame.ptr.height + 15) // 16
cdef int mb_w = (self.frame.ptr.width + 15) // 16
cdef int nb_mb = mb_h * mb_w
cdef int block_idx
cdef int y
cdef int x
cdef VideoBlockParams block
# Validate number of blocks
if self.nb_blocks != nb_mb:
raise RuntimeError("Expected frame size to match number of blocks in side data")
# Validate type
cdef lib.AVVideoEncParamsType type = (<lib.AVVideoEncParams*> self.ptr.data).type
if type != lib.AVVideoEncParamsType.AV_VIDEO_ENC_PARAMS_MPEG2 and type != lib.AVVideoEncParamsType.AV_VIDEO_ENC_PARAMS_H264:
raise ValueError("Expected MPEG2 or H264")
# Create a 2-D map with the number of macroblocks
cdef int32_t[:, ::1] map = np.empty((mb_h, mb_w), dtype=np.int32)
# Fill map with quantization parameter per macroblock
for block_idx in range(nb_mb):
block = VideoBlockParams(self, block_idx)
y = block.src_y // 16
x = block.src_x // 16
map[y, x] = self.qp + block.delta_qp
return np.asarray(map)
cdef class VideoBlockParams:
def __init__(self, VideoEncParams video_enc_params, int idx) -> None:
cdef uint8_t* base = <uint8_t*> video_enc_params.ptr.data
cdef Py_ssize_t offset = video_enc_params.blocks_offset + idx * video_enc_params.block_size
self.ptr = <lib.AVVideoBlockParams*> (base + offset)
def __repr__(self):
return f"<av.sidedata.VideoBlockParams, src=({self.src_x}, {self.src_y}), size={self.w}x{self.h}, delta_qp={self.delta_qp}>"
@property
def src_x(self):
"""
Horizontal distance in luma pixels from the top-left corner of the visible frame
to the top-left corner of the block.
Can be negative if top/right padding is present on the coded frame.
"""
return self.ptr.src_x
@property
def src_y(self):
"""
Vertical distance in luma pixels from the top-left corner of the visible frame
to the top-left corner of the block.
Can be negative if top/right padding is present on the coded frame.
"""
return self.ptr.src_y
@property
def w(self):
"""
Width of the block in luma pixels
"""
return self.ptr.w
@property
def h(self):
"""
Height of the block in luma pixels
"""
return self.ptr.h
@property
def delta_qp(self):
"""
Difference between this block's final quantization parameter and the
corresponding per-frame value.
"""
return self.ptr.delta_qp
|