├── .gitignore ├── Makefile ├── bitstream.c ├── bitstream.h ├── csc.c ├── csc.h ├── draw.c ├── draw.h ├── h264-rate-control.c ├── h264-rate-control.h ├── h264.c ├── h264.h ├── media.c ├── media.h ├── unit.c ├── unit.h ├── v4l2-encoder.c ├── v4l2-encoder.h ├── v4l2-hantro-h264-encoder.c ├── v4l2.c └── v4l2.h /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /v4l2-hantro-h264-encoder 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | CC = gcc 4 | 5 | # Project 6 | 7 | NAME = v4l2-hantro-h264-encoder 8 | 9 | # Directories 10 | 11 | BUILD = build 12 | OUTPUT = . 13 | 14 | # Sources 15 | 16 | SOURCES = \ 17 | v4l2-hantro-h264-encoder.c \ 18 | v4l2-encoder.c \ 19 | h264.c \ 20 | h264-rate-control.c \ 21 | media.c \ 22 | v4l2.c \ 23 | unit.c \ 24 | bitstream.c \ 25 | draw.c \ 26 | csc.c 27 | OBJECTS = $(SOURCES:.c=.o) 28 | DEPS = $(SOURCES:.c=.d) 29 | 30 | # Compiler 31 | 32 | CFLAGS = -I. $(shell pkg-config --cflags cairo libudev) -Ofast 33 | LDFLAGS = -lcairo -lm $(shell pkg-config --libs libudev) 34 | 35 | # Produced files 36 | 37 | BUILD_OBJECTS = $(addprefix $(BUILD)/,$(OBJECTS)) 38 | BUILD_DEPS = $(addprefix $(BUILD)/,$(DEPS)) 39 | BUILD_BINARY = $(BUILD)/$(NAME) 40 | BUILD_DIRS = $(sort $(dir $(BUILD_BINARY) $(BUILD_OBJECTS))) 41 | 42 | OUTPUT_BINARY = $(OUTPUT)/$(NAME) 43 | OUTPUT_DIRS = $(sort $(dir $(OUTPUT_BINARY))) 44 | 45 | all: $(OUTPUT_BINARY) 46 | 47 | $(BUILD_DIRS): 48 | @mkdir -p $@ 49 | 50 | $(BUILD_OBJECTS): $(BUILD)/%.o: %.c | $(BUILD_DIRS) 51 | @echo " CC $<" 52 | @$(CC) $(CFLAGS) -MMD -MF $(BUILD)/$*.d -c $< -o $@ 53 | 54 | $(BUILD_BINARY): $(BUILD_OBJECTS) 55 | @echo " LINK $@" 56 | @$(CC) $(CFLAGS) -o $@ $(BUILD_OBJECTS) $(LDFLAGS) 57 | 58 | $(OUTPUT_DIRS): 59 | @mkdir -p $@ 60 | 61 | $(OUTPUT_BINARY): $(BUILD_BINARY) | $(OUTPUT_DIRS) 62 | @echo " BINARY $@" 63 | @cp $< $@ 64 | 65 | .PHONY: clean 66 | clean: 67 | @echo " CLEAN" 68 | @rm -rf $(foreach object,$(basename $(BUILD_OBJECTS)),$(object)*) $(basename $(BUILD_BINARY))* 69 | @rm -rf $(OUTPUT_BINARY) 70 | 71 | .PHONY: distclean 72 | distclean: clean 73 | @echo " DISTCLEAN" 74 | @rm -rf $(BUILD) 75 | 76 | -include $(BUILD_DEPS) 77 | -------------------------------------------------------------------------------- /bitstream.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | struct bitstream *bitstream_create(void) 13 | { 14 | struct bitstream *bitstream = NULL; 15 | 16 | bitstream = calloc(1, sizeof(*bitstream)); 17 | if (!bitstream) 18 | goto error; 19 | 20 | bitstream->length = 1024; 21 | bitstream->buffer = calloc(1, bitstream->length); 22 | if (!bitstream->buffer) 23 | goto error; 24 | 25 | return bitstream; 26 | 27 | error: 28 | if (bitstream) { 29 | if (bitstream->buffer) 30 | free(bitstream->buffer); 31 | 32 | free(bitstream); 33 | } 34 | 35 | return NULL; 36 | } 37 | 38 | void bitstream_destroy(struct bitstream *bitstream) 39 | { 40 | if (!bitstream) 41 | return; 42 | 43 | if (bitstream->buffer) 44 | free(bitstream->buffer); 45 | 46 | free(bitstream); 47 | } 48 | 49 | int bitstream_reset(struct bitstream *bitstream) 50 | { 51 | if (!bitstream) 52 | return -EINVAL; 53 | 54 | memset(bitstream->buffer, 0, bitstream->length); 55 | 56 | bitstream->offset_bytes = 0; 57 | bitstream->offset_bits = 0; 58 | 59 | return 0; 60 | } 61 | 62 | int bitstream_append_bits(struct bitstream *bitstream, uint32_t bits, 63 | unsigned int bits_count) 64 | { 65 | uint8_t *buffer; 66 | unsigned int buffer_bits_left; 67 | unsigned int bits_left = bits_count; 68 | unsigned int bits_offset = 0; 69 | 70 | if (!bitstream || bits_count > 32) 71 | return -EINVAL; 72 | 73 | buffer = bitstream->buffer + bitstream->offset_bytes; 74 | 75 | while (bits_left > 0) { 76 | uint32_t chunk; 77 | uint32_t base; 78 | uint32_t shift; 79 | uint32_t bits_masked; 80 | 81 | buffer = bitstream->buffer + bitstream->offset_bytes; 82 | 83 | buffer_bits_left = 8 - bitstream->offset_bits; 84 | chunk = bits_left < buffer_bits_left ? bits_left : buffer_bits_left; 85 | 86 | base = (1 << chunk) - 1; 87 | shift = bits_count - bits_offset - chunk; 88 | bits_masked = (bits & (base << shift)) >> shift; 89 | *buffer |= bits_masked << (8 - bitstream->offset_bits - chunk); 90 | 91 | bitstream->offset_bits += chunk; 92 | bits_left -= chunk; 93 | bits_offset += chunk; 94 | 95 | bitstream->offset_bytes += bitstream->offset_bits / 8; 96 | bitstream->offset_bits = bitstream->offset_bits % 8; 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | int bitstream_append_ue(struct bitstream *bitstream, uint32_t value) 103 | { 104 | uint32_t value_bit_count = 0; 105 | uint32_t zero_bit_count; 106 | 107 | if (value >= 0x7fffffff) 108 | return -EINVAL; 109 | 110 | value++; 111 | 112 | while (value >> value_bit_count) 113 | value_bit_count++; 114 | 115 | zero_bit_count = value_bit_count - 1; 116 | 117 | bitstream_append_bits(bitstream, 0, zero_bit_count); 118 | bitstream_append_bits(bitstream, value, value_bit_count); 119 | 120 | return 0; 121 | } 122 | 123 | int bitstream_append_se(struct bitstream *bitstream, int32_t value) 124 | { 125 | uint32_t value_ue; 126 | 127 | if (value > 0) 128 | value_ue = (uint32_t)(2 * value - 1); 129 | else 130 | value_ue = (uint32_t)(-2 * value); 131 | 132 | return bitstream_append_ue(bitstream, value_ue); 133 | } 134 | 135 | int bitstream_align(struct bitstream *bitstream) 136 | { 137 | if (!bitstream->offset_bits) 138 | return 0; 139 | 140 | bitstream_append_bits(bitstream, 0, 8 - bitstream->offset_bits); 141 | 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /bitstream.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #ifndef _BITSTREAM_H_ 6 | #define _BITSTREAM_H_ 7 | 8 | struct bitstream { 9 | void *buffer; 10 | unsigned int length; 11 | 12 | unsigned int offset_bytes; 13 | unsigned int offset_bits; 14 | }; 15 | 16 | struct bitstream *bitstream_create(void); 17 | void bitstream_destroy(struct bitstream *bitstream); 18 | int bitstream_reset(struct bitstream *bitstream); 19 | int bitstream_append_bits(struct bitstream *bitstream, uint32_t bits, 20 | unsigned int bits_count); 21 | int bitstream_append_ue(struct bitstream *bitstream, uint32_t value); 22 | int bitstream_append_se(struct bitstream *bitstream, int32_t value); 23 | int bitstream_align(struct bitstream *bitstream); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /csc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | int rgb2yuv420(struct draw_buffer *buffer, void *buffer_y, void *buffer_u, 13 | void *buffer_v) 14 | { 15 | unsigned int width, height, stride; 16 | unsigned int x, y; 17 | void *data; 18 | 19 | if (!buffer) 20 | return -EINVAL; 21 | 22 | data = buffer->data; 23 | width = buffer->width; 24 | height = buffer->height; 25 | stride = buffer->stride; 26 | 27 | for (y = 0; y < height; y++) { 28 | for (x = 0; x < width; x++) { 29 | uint8_t *crgb; 30 | uint8_t *cy; 31 | float value; 32 | 33 | crgb = data + stride * y + x * 4; 34 | value = crgb[2] * 0.299 + crgb[1] * 0.587 + crgb[0] * 0.114; 35 | 36 | cy = buffer_y + width * y + x; 37 | *cy = byte_range(value); 38 | 39 | if ((x % 2) == 0 && (y % 2) == 0) { 40 | uint8_t *cu; 41 | uint8_t *cv; 42 | 43 | value = crgb[2] * -0.14713 + crgb[1] * -0.28886 + crgb[0] * 0.436 + 128.; 44 | 45 | cu = buffer_u + width / 2 * y / 2 + x / 2; 46 | *cu = byte_range(value); 47 | 48 | value = crgb[2] * 0.615 + crgb[1] * -0.51499 + crgb[0] * -0.10001 + 128.; 49 | 50 | cv = buffer_v + width / 2 * y / 2 + x / 2; 51 | *cv = byte_range(value); 52 | } 53 | } 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | int rgb2nv12(struct draw_buffer *buffer, void *buffer_y, void *buffer_uv) 60 | { 61 | unsigned int width, height, stride; 62 | unsigned int x, y; 63 | void *data; 64 | 65 | if (!buffer) 66 | return -EINVAL; 67 | 68 | data = buffer->data; 69 | width = buffer->width; 70 | height = buffer->height; 71 | stride = buffer->stride; 72 | 73 | for (y = 0; y < height; y++) { 74 | for (x = 0; x < width; x++) { 75 | uint8_t *crgb; 76 | uint8_t *cy; 77 | float value; 78 | 79 | crgb = data + stride * y + x * 4; 80 | value = crgb[2] * 0.299 + crgb[1] * 0.587 + crgb[0] * 0.114; 81 | 82 | cy = buffer_y + width * y + x; 83 | *cy = byte_range(value); 84 | 85 | if ((x % 2) == 0 && (y % 2) == 0) { 86 | uint8_t *cu; 87 | uint8_t *cv; 88 | 89 | value = crgb[2] * -0.14713 + crgb[1] * -0.28886 + crgb[0] * 0.436 + 128.; 90 | 91 | cu = buffer_uv + width * y / 2 + x; 92 | *cu = byte_range(value); 93 | 94 | value = crgb[2] * 0.615 + crgb[1] * -0.51499 + crgb[0] * -0.10001 + 128.; 95 | 96 | cv = buffer_uv + width * y / 2 + x + 1; 97 | *cv = byte_range(value); 98 | } 99 | } 100 | } 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /csc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #ifndef _CSC_H_ 6 | #define _CSC_H_ 7 | 8 | static inline uint8_t byte_range(float v) 9 | { 10 | if (v < 0.) 11 | return 0; 12 | else if (v > 255.) 13 | return 255; 14 | else 15 | return (uint8_t)v; 16 | } 17 | 18 | int rgb2yuv420(struct draw_buffer *buffer, void *buffer_y, void *buffer_u, 19 | void *buffer_v); 20 | int rgb2nv12(struct draw_buffer *buffer, void *buffer_y, void *buffer_uv); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /draw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | struct draw_buffer *draw_buffer_create(unsigned int width, unsigned int height) 15 | { 16 | struct draw_buffer *buffer = NULL; 17 | unsigned int stride; 18 | unsigned int size; 19 | 20 | if (!width || !height) 21 | return NULL; 22 | 23 | buffer = calloc(1, sizeof(*buffer)); 24 | if (!buffer) 25 | goto error; 26 | 27 | stride = width * 4; 28 | size = stride * height; 29 | 30 | buffer->width = width; 31 | buffer->height = height; 32 | buffer->stride = stride; 33 | 34 | buffer->size = size; 35 | buffer->data = calloc(1, size); 36 | 37 | if (!buffer->data) 38 | goto error; 39 | 40 | return buffer; 41 | 42 | error: 43 | if (buffer) 44 | free(buffer); 45 | 46 | return NULL; 47 | } 48 | 49 | void draw_buffer_destroy(struct draw_buffer *buffer) 50 | { 51 | if (!buffer) 52 | return; 53 | 54 | if (buffer->data) 55 | free(buffer->data); 56 | 57 | free(buffer); 58 | } 59 | 60 | void draw_png(struct draw_buffer *buffer, char *path) 61 | { 62 | cairo_surface_t *surface = NULL; 63 | unsigned int width; 64 | unsigned int height; 65 | unsigned int stride; 66 | unsigned int y; 67 | void *data; 68 | 69 | surface = cairo_image_surface_create_from_png(path); 70 | if (!surface) 71 | return; 72 | 73 | data = cairo_image_surface_get_data(surface); 74 | width = cairo_image_surface_get_width(surface); 75 | height = cairo_image_surface_get_height(surface); 76 | stride = cairo_image_surface_get_stride(surface); 77 | 78 | for (y = 0; y < height; y++) 79 | memcpy(buffer->data + y * buffer->stride, 80 | data + y * stride, width * sizeof(uint32_t)); 81 | 82 | cairo_surface_destroy(surface); 83 | } 84 | 85 | void draw_background(struct draw_buffer *buffer, uint32_t color) 86 | { 87 | wmemset(buffer->data, color, buffer->size / sizeof(color)); 88 | } 89 | 90 | void draw_gradient(struct draw_buffer *buffer) 91 | { 92 | unsigned int x, y; 93 | uint32_t *pixel; 94 | 95 | for (y = 0; y < buffer->height; y++) { 96 | for (x = 0; x < buffer->width; x++) { 97 | unsigned int red = 255 * x / (buffer->width - 1); 98 | unsigned int blue = 255 * y / (buffer->height - 1); 99 | uint32_t value = 0x00000000; 100 | 101 | value |= (red & 0xff) << 0; 102 | value |= (blue & 0xff) << 8; 103 | 104 | pixel = draw_buffer_pixel(buffer, x, y); 105 | *pixel = value; 106 | } 107 | } 108 | } 109 | 110 | void draw_rectangle(struct draw_buffer *buffer, unsigned int x_start, 111 | unsigned int y_start, unsigned int width, 112 | unsigned int height, uint32_t color) 113 | { 114 | unsigned int x_stop = x_start + width; 115 | unsigned int y_stop = y_start + height; 116 | unsigned int x, y; 117 | uint32_t *pixel; 118 | 119 | for (y = y_start; y < y_stop; y++) { 120 | pixel = draw_buffer_pixel(buffer, x_start, y); 121 | wmemset(pixel, color, width); 122 | } 123 | } 124 | 125 | void draw_mandelbrot(struct draw_mandelbrot *mandelbrot, 126 | struct draw_buffer *buffer) 127 | { 128 | unsigned int *pixel; 129 | unsigned int x, y; 130 | unsigned int width; 131 | unsigned int height; 132 | unsigned int leftover; 133 | float diff_x; 134 | float diff_y; 135 | float fact_x; 136 | float fact_y; 137 | float start_x; 138 | float start_y; 139 | unsigned int iterations; 140 | float scale_iter; 141 | float scale_depth = 255.; 142 | 143 | if (!mandelbrot) 144 | return; 145 | 146 | pixel = buffer->data; 147 | 148 | width = buffer->width; 149 | height = buffer->height; 150 | leftover = (buffer->stride / sizeof(*pixel) - buffer->width); 151 | 152 | diff_x = mandelbrot->bounds_x[1] - mandelbrot->bounds_x[0]; 153 | diff_y = mandelbrot->bounds_y[1] - mandelbrot->bounds_y[0]; 154 | fact_x = diff_x / buffer->width; 155 | fact_y = diff_x / buffer->height; 156 | start_x = mandelbrot->bounds_x[0]; 157 | start_y = mandelbrot->bounds_y[0]; 158 | iterations = mandelbrot->iterations; 159 | scale_iter = 1.0f / iterations; 160 | 161 | for (y = 0; y < height; y++) { 162 | for (x = 0; x < width; x++) { 163 | float cr = x * fact_x + start_x; 164 | float ci = y * fact_y + start_y; 165 | float zr = cr; 166 | float zi = ci; 167 | float mkr = 0.0f; 168 | float mkg = 0.0f; 169 | float mkb = 0.0f; 170 | unsigned int k = 0; 171 | unsigned int vr, vg, vb; 172 | 173 | while (++k < iterations) { 174 | float zr_k = zr * zr - zi * zi + cr; 175 | float zi_k = zr * zi + zr * zi + ci; 176 | zr = zr_k; 177 | zi = zi_k; 178 | mkr += 1.0f; 179 | 180 | if (zr * zr + zi * zi >= 1.0f) 181 | mkg += (zr * zr + zi * zi) - 1.f; 182 | 183 | if (zr * zr + zi * zi >= 2.0f) 184 | mkb += sqrtf(zr * zr + zi * zi) - 2.f; 185 | 186 | if (zr * zr + zi * zi >= 4.0f) 187 | break; 188 | } 189 | 190 | *pixel = 255 << 24; 191 | 192 | mkr *= scale_iter; 193 | mkr = sqrtf(mkr); 194 | mkr *= scale_depth; 195 | 196 | vr = (unsigned int)mkr; 197 | if (vr > 255) 198 | vr = 255; 199 | 200 | *pixel |= vr << 16; 201 | 202 | mkg *= scale_iter; 203 | mkg *= scale_depth; 204 | 205 | vg = (unsigned int)mkg; 206 | if (vg > 255) 207 | vg = 255; 208 | 209 | *pixel |= vg << 8; 210 | 211 | mkb *= scale_iter; 212 | mkb *= scale_depth; 213 | 214 | vb = (unsigned int)mkb; 215 | if (vb > 255) 216 | vb = 255; 217 | 218 | *pixel |= vb << 0; 219 | 220 | pixel++; 221 | } 222 | 223 | pixel += leftover; 224 | } 225 | } 226 | 227 | void draw_mandelbrot_zoom(struct draw_mandelbrot *mandelbrot) 228 | { 229 | if (!mandelbrot) 230 | return; 231 | 232 | mandelbrot->view_width /= mandelbrot->zoom; 233 | mandelbrot->view_height /= mandelbrot->zoom; 234 | 235 | mandelbrot->iterations_zoom += sqrtf(mandelbrot->zoom) / 2.; 236 | mandelbrot->iterations = (unsigned int)mandelbrot->iterations_zoom; 237 | 238 | mandelbrot->bounds_x[0] = mandelbrot->center_x - mandelbrot->view_width / 2.; 239 | mandelbrot->bounds_x[1] = mandelbrot->center_x + mandelbrot->view_width / 2.; 240 | 241 | mandelbrot->bounds_y[0] = mandelbrot->center_y - mandelbrot->view_height / 2.; 242 | mandelbrot->bounds_y[1] = mandelbrot->center_y + mandelbrot->view_height / 2.; 243 | } 244 | 245 | void draw_mandelbrot_init(struct draw_mandelbrot *mandelbrot) 246 | { 247 | if (!mandelbrot) 248 | return; 249 | 250 | mandelbrot->zoom = 1.02; 251 | mandelbrot->center_x = -0.743643887037151; 252 | mandelbrot->center_y = 0.13182590420533; 253 | mandelbrot->view_width = 0.005671; 254 | mandelbrot->view_height = mandelbrot->view_width * 720. / 1280.; 255 | mandelbrot->iterations_zoom = 200.; 256 | } 257 | -------------------------------------------------------------------------------- /draw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #ifndef _DRAW_H_ 6 | #define _DRAW_H_ 7 | 8 | struct draw_buffer { 9 | void *data; 10 | unsigned int size; 11 | 12 | unsigned int width; 13 | unsigned int height; 14 | unsigned int stride; 15 | }; 16 | 17 | struct draw_mandelbrot { 18 | float center_x; 19 | float center_y; 20 | float view_width; 21 | float view_height; 22 | 23 | float zoom; 24 | 25 | float bounds_x[2]; 26 | float bounds_y[2]; 27 | float iterations_zoom; 28 | unsigned int iterations; 29 | }; 30 | 31 | static inline uint32_t *draw_buffer_pixel(struct draw_buffer *buffer, 32 | unsigned int x, unsigned int y) 33 | { 34 | unsigned int offset = y * buffer->stride + x * sizeof(uint32_t); 35 | 36 | return (uint32_t *)(buffer->data + offset); 37 | } 38 | 39 | struct draw_buffer *draw_buffer_create(unsigned int width, unsigned int height); 40 | void draw_buffer_destroy(struct draw_buffer *buffer); 41 | void draw_png(struct draw_buffer *buffer, char *path); 42 | void draw_gradient(struct draw_buffer *buffer); 43 | void draw_background(struct draw_buffer *buffer, uint32_t color); 44 | void draw_rectangle(struct draw_buffer *buffer, unsigned int x_start, 45 | unsigned int y_start, unsigned int width, 46 | unsigned int height, uint32_t color); 47 | void draw_mandelbrot(struct draw_mandelbrot *mandelbrot, 48 | struct draw_buffer *buffer); 49 | void draw_mandelbrot_zoom(struct draw_mandelbrot *mandelbrot); 50 | void draw_mandelbrot_init(struct draw_mandelbrot *mandelbrot); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /h264-rate-control.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) 20 | 21 | static uint32_t hantro_qp_estimation[2][11] = { 22 | /* Bitrate-related estimation factors. */ 23 | { 27, 44, 72, 119, 192, 314, 453, 653, 952, 1395, 0xffffffff }, 24 | /* Corresponding QP values. */ 25 | { 51, 47, 43, 39, 35, 31, 27, 23, 19, 15, 11} 26 | }; 27 | 28 | static unsigned int rlc_upscale = 256; 29 | 30 | static unsigned int hantro_qp_inital_estimate(struct v4l2_encoder_setup *setup, 31 | struct h264_rate_control *rc) 32 | { 33 | uint32_t estimation; 34 | uint32_t pixels; 35 | uint32_t pixels_down; 36 | uint64_t upscale = 8000; 37 | unsigned int qp; 38 | unsigned int i; 39 | 40 | /* Very high bitrates are capped by minimal QP. */ 41 | if (rc->bits_per_frame > 1000000) 42 | return setup->qp_min; 43 | 44 | pixels = 16 * 16 * setup->width_mbs * setup->height_mbs; 45 | pixels_down = pixels >> 8; 46 | 47 | /* Calculate a bitrate-related estimation value, taking in account the 48 | number of pixels to encode. This seems highly hardware-specific. */ 49 | estimation = rc->bits_per_frame >> 5; 50 | estimation *= pixels_down + 250; 51 | estimation /= 350 + 3 * pixels_down / 4; 52 | estimation = upscale * estimation / (pixels_down << 6); 53 | 54 | for (i = 0; hantro_qp_estimation[0][i] < estimation; i++) 55 | continue; 56 | 57 | qp = hantro_qp_estimation[1][i]; 58 | 59 | if (qp > setup->qp_max) 60 | return setup->qp_max; 61 | else if (qp < setup->qp_min) 62 | return setup->qp_min; 63 | else 64 | return qp; 65 | } 66 | 67 | static void hantro_checkpoints_prepare(struct v4l2_encoder_setup *setup, 68 | struct h264_rate_control *rc, 69 | bool gop_start) 70 | { 71 | unsigned int macroblocks; 72 | unsigned int rlc_target; 73 | unsigned int rlc_max; 74 | unsigned int error_base; 75 | unsigned int i; 76 | 77 | /* Don't apply checkpoints without statistics or at GOP start (intra 78 | * frames), for which bitrate is best unconstrained. Also go easy on 79 | * last GOP frames with insufficient leftover bits. */ 80 | if (!rc->bits_per_rlc_upscaled || gop_start || 81 | (!rc->gop_left && rc->bits_target < rc->bits_per_frame)) { 82 | rc->cp_enabled = false; 83 | return; 84 | } 85 | 86 | macroblocks = setup->width_mbs * setup->height_mbs; 87 | 88 | /* H.264 has a maximum of 24 * 16 coefficients per macroblock. */ 89 | rlc_max = setup->width_mbs * setup->height_mbs * 24 * 16; 90 | 91 | /* Calculate target number of coefficients based on target bits. */ 92 | rlc_target = rc->bits_target * rlc_upscale / rc->bits_per_rlc_upscaled; 93 | 94 | if (rlc_target > rlc_max) 95 | rlc_target = rlc_max; 96 | 97 | /* Evenly spread target coefficients count across checkpoints. */ 98 | for (i = 0; i < rc->cp_count; i++) { 99 | /* RLC target is / 32 to match hardware register expectations. */ 100 | rc->cp_target[i] = ((i + 1) * rlc_target * rc->cp_distance_mbs / 101 | macroblocks + 31) / 32; 102 | } 103 | 104 | /* Base error unit for QP delta ladder, set to a quarter of the RLC 105 | * count interval between two checkpoints. */ 106 | error_base = rlc_target * rc->cp_distance_mbs / macroblocks / 4; 107 | 108 | /* Target error is / 4 to match hardware register expectations. */ 109 | /* Decrease QP (increase quality) for negative error. */ 110 | rc->cp_qp_delta[0] = -3; 111 | rc->cp_target_error[0] = -error_base * 3 / 4; 112 | rc->cp_qp_delta[1] = -2; 113 | rc->cp_target_error[1] = -error_base * 2 / 4; 114 | rc->cp_qp_delta[2] = -1; 115 | rc->cp_target_error[2] = -error_base * 1 / 4; 116 | /* Keep QP for nearly no error. */ 117 | rc->cp_qp_delta[3] = 0; 118 | rc->cp_target_error[3] = error_base * 1 / 4; 119 | /* Increase QP (decrease quality) for positive error. */ 120 | rc->cp_qp_delta[4] = 1; 121 | rc->cp_target_error[4] = error_base * 2 / 4; 122 | rc->cp_qp_delta[5] = 2; 123 | rc->cp_target_error[5] = error_base * 3 / 4; 124 | rc->cp_qp_delta[6] = 3; 125 | 126 | rc->cp_enabled = true; 127 | } 128 | 129 | void h264_rate_control_feedback(struct v4l2_encoder *encoder, 130 | unsigned int bytes_used, unsigned int rlc_count, 131 | unsigned int qp_sum) 132 | { 133 | struct v4l2_encoder_setup *setup; 134 | struct h264_rate_control *rc; 135 | unsigned int bits_used = bytes_used * 8; 136 | unsigned int macroblocks; 137 | unsigned int qp_average; 138 | 139 | setup = &encoder->setup; 140 | rc = &encoder->rc; 141 | 142 | macroblocks = setup->width_mbs * setup->height_mbs; 143 | qp_average = qp_sum / macroblocks; 144 | 145 | /* Collect statistics. */ 146 | 147 | rc->qp_sum += qp_average; 148 | 149 | /* Calculate how many bits are used per non-zero coefficient, with an 150 | * upscaling factor for precision. */ 151 | rc->bits_per_rlc_upscaled = bits_used * rlc_upscale / rlc_count; 152 | 153 | /* For (privileged) intra frames, remove privilege and don't 154 | * check for intra bit target error. */ 155 | if (rc->qp_intra_privilege) { 156 | rc->qp += setup->qp_intra_delta; 157 | rc->qp_intra_privilege = false; 158 | } 159 | 160 | if (!rc->bits_left || bits_used >= rc->bits_left) { 161 | rc->bits_left = 0; 162 | 163 | /* Drastically increase QP for each over-bitrate frame in 164 | * remaining GOP. */ 165 | rc->qp += 2; 166 | } else if (bits_used < (7 * rc->bits_target / 8) && rc->qp) { 167 | rc->qp--; 168 | } else if (bits_used > (9 * rc->bits_target / 8)) { 169 | rc->qp++; 170 | } 171 | 172 | if (rc->qp < setup->qp_min) 173 | rc->qp = setup->qp_min; 174 | else if (rc->qp > setup->qp_max) 175 | rc->qp = setup->qp_max; 176 | 177 | if (rc->bits_left) 178 | rc->bits_left -= bits_used; 179 | } 180 | 181 | void h264_rate_control_step(struct v4l2_encoder *encoder) 182 | { 183 | struct v4l2_encoder_setup *setup; 184 | struct h264_rate_control *rc; 185 | bool gop_start; 186 | 187 | if (!encoder) 188 | return; 189 | 190 | setup = &encoder->setup; 191 | rc = &encoder->rc; 192 | gop_start = !encoder->gop_index || rc->intra_request; 193 | 194 | if (gop_start) { 195 | /* Starting a new GOP. */ 196 | rc->gop_left = setup->gop_size; 197 | 198 | /* Start from the previous GOP average QP. Otherwise, initial 199 | * QP estimation is used or current QP for intra request. */ 200 | if (rc->qp_sum && !rc->intra_request) 201 | rc->qp = rc->qp_sum / setup->gop_size; 202 | 203 | rc->qp_sum = 0; 204 | 205 | /* Apply intra QP delta privilege. */ 206 | if (rc->qp > setup->qp_intra_delta) 207 | rc->qp -= setup->qp_intra_delta; 208 | else 209 | rc->qp = 0; 210 | 211 | rc->qp_intra_privilege = true; 212 | 213 | /* Keep the benefit of previous under-bitrate GOP. */ 214 | rc->bits_left += rc->bits_per_gop; 215 | rc->bits_target = rc->bits_per_frame; 216 | } else if (!rc->bits_left) { 217 | /* Already out of bits to match bitrate. */ 218 | rc->bits_target = 0; 219 | } else { 220 | /* Evenly split remaining bits for GOP inter frames. */ 221 | rc->bits_target = rc->bits_left / rc->gop_left; 222 | 223 | /* Limit to 1.5x the average bits per frame. */ 224 | if (rc->bits_target > (2 * rc->bits_per_frame / 3)) 225 | rc->bits_target = rc->bits_per_frame; 226 | } 227 | 228 | /* Checkpoint algorithm needs to care about last GOP frame. */ 229 | rc->gop_left--; 230 | 231 | hantro_checkpoints_prepare(setup, rc, gop_start); 232 | 233 | if (rc->intra_request) 234 | rc->intra_request = false; 235 | } 236 | 237 | int h264_rate_control_intra_request(struct v4l2_encoder *encoder) 238 | { 239 | struct h264_rate_control *rc; 240 | 241 | if (!encoder) 242 | return -EINVAL; 243 | 244 | rc = &encoder->rc; 245 | rc->intra_request = true; 246 | 247 | return 0; 248 | } 249 | 250 | int h264_rate_control_setup(struct v4l2_encoder *encoder) 251 | { 252 | struct v4l2_encoder_setup *setup; 253 | struct h264_rate_control *rc; 254 | unsigned int cp_count; 255 | 256 | if (!encoder) 257 | return -EINVAL; 258 | 259 | setup = &encoder->setup; 260 | rc = &encoder->rc; 261 | 262 | memset(rc, 0, sizeof(*rc)); 263 | 264 | /* Start with intra request to ensure GOP start. */ 265 | rc->intra_request = true; 266 | 267 | rc->bits_per_frame = setup->bitrate * setup->fps_den / setup->fps_num; 268 | rc->bits_per_gop = rc->bits_per_frame * setup->gop_size; 269 | 270 | rc->qp = hantro_qp_inital_estimate(setup, rc); 271 | 272 | /* Checkpoints */ 273 | 274 | cp_count = setup->height_mbs - 1; 275 | if (cp_count > ARRAY_SIZE(rc->cp_target)) 276 | cp_count = ARRAY_SIZE(rc->cp_target); 277 | 278 | rc->cp_count = cp_count; 279 | rc->cp_distance_mbs = setup->width_mbs * setup->height_mbs / 280 | (cp_count + 1); 281 | 282 | return 0; 283 | } 284 | -------------------------------------------------------------------------------- /h264-rate-control.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #ifndef _H264_RATE_CONTROL_H_ 6 | #define _H264_RATE_CONTROL_H_ 7 | 8 | #include 9 | 10 | struct v4l2_encoder; 11 | 12 | struct h264_rate_control { 13 | unsigned int bits_per_frame; 14 | unsigned int bits_per_gop; 15 | 16 | unsigned int bits_target; 17 | unsigned int bits_left; 18 | unsigned int gop_left; 19 | 20 | unsigned int bits_per_rlc_upscaled; 21 | 22 | bool cp_enabled; 23 | unsigned int cp_count; 24 | unsigned int cp_distance_mbs; 25 | 26 | unsigned int cp_target[10]; 27 | int cp_target_error[6]; 28 | int cp_qp_delta[7]; 29 | 30 | unsigned int qp; 31 | unsigned int qp_sum; 32 | bool qp_intra_privilege; 33 | 34 | bool intra_request; 35 | }; 36 | 37 | 38 | void h264_rate_control_feedback(struct v4l2_encoder *encoder, 39 | unsigned int bytes_used, unsigned int rlc_count, 40 | unsigned int qp_sum); 41 | void h264_rate_control_step(struct v4l2_encoder *encoder); 42 | int h264_rate_control_intra_request(struct v4l2_encoder *encoder); 43 | int h264_rate_control_setup(struct v4l2_encoder *encoder); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /h264.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) 23 | 24 | static void bitstream_sps(struct bitstream *bitstream, 25 | struct v4l2_encoder *encoder) 26 | { 27 | struct v4l2_ctrl_h264_sps *sps = &encoder->sps; 28 | bool frame_cropping_flag = false; 29 | 30 | bitstream_reset(bitstream); 31 | 32 | /* NALU header */ 33 | bitstream_append_bits(bitstream, 0, 1); 34 | bitstream_append_bits(bitstream, 3, 2); 35 | bitstream_append_bits(bitstream, 7, 5); 36 | 37 | bitstream_append_bits(bitstream, sps->profile_idc, 8); 38 | /* constraint_set0_flag */ 39 | /* fixed by hardware */ 40 | bitstream_append_bits(bitstream, 1, 1); 41 | /* constraint_set1_flag */ 42 | /* fixed by hardware */ 43 | bitstream_append_bits(bitstream, 1, 1); 44 | /* constraint_set2_flag */ 45 | bitstream_append_bits(bitstream, 0, 1); 46 | /* constraint_setn_flag + reserved_zero_2bits */ 47 | bitstream_append_bits(bitstream, 0, 5); 48 | bitstream_append_bits(bitstream, sps->level_idc, 8); 49 | bitstream_append_ue(bitstream, sps->seq_parameter_set_id); 50 | 51 | switch (sps->profile_idc) { 52 | case 100: 53 | case 110: 54 | case 122: 55 | case 244: 56 | case 44: 57 | case 83: 58 | case 86: 59 | case 118: 60 | case 128: 61 | case 138: 62 | case 139: 63 | case 134: 64 | case 135: 65 | bitstream_append_ue(bitstream, sps->chroma_format_idc); 66 | bitstream_append_ue(bitstream, sps->bit_depth_luma_minus8); 67 | bitstream_append_ue(bitstream, sps->bit_depth_chroma_minus8); 68 | /* qpprime_y_zero_transform_bypass_flag */ 69 | bitstream_append_bits(bitstream, 0, 1); 70 | /* seq_scaling_matrix_present_flag */ 71 | bitstream_append_bits(bitstream, 0, 1); 72 | break; 73 | default: 74 | break; 75 | } 76 | 77 | bitstream_append_ue(bitstream, sps->log2_max_frame_num_minus4); 78 | bitstream_append_ue(bitstream, sps->pic_order_cnt_type); 79 | 80 | if (sps->pic_order_cnt_type == 0) { 81 | bitstream_append_ue(bitstream, sps->log2_max_pic_order_cnt_lsb_minus4); 82 | } // XXX: == 1 83 | 84 | bitstream_append_ue(bitstream, sps->max_num_ref_frames); 85 | /* gaps_in_frame_num_value_allowed_flag */ 86 | bitstream_append_bits(bitstream, 0, 1); 87 | bitstream_append_ue(bitstream, sps->pic_width_in_mbs_minus1); 88 | bitstream_append_ue(bitstream, sps->pic_height_in_map_units_minus1); 89 | /* frame_mbs_only_flag */ 90 | /* XXX: fixed by hardware */ 91 | bitstream_append_bits(bitstream, 1, 1); 92 | /* direct_8x8_inference_flag */ 93 | bitstream_append_bits(bitstream, !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE), 1); 94 | 95 | if ((encoder->setup.width_mbs * 16) != encoder->setup.width || 96 | (encoder->setup.height_mbs * 16) != encoder->setup.height) 97 | frame_cropping_flag = true; 98 | 99 | bitstream_append_bits(bitstream, frame_cropping_flag, 1); 100 | 101 | if (frame_cropping_flag) { 102 | uint32_t crop_right = ((encoder->setup.width_mbs * 16) - 103 | encoder->setup.width) >> 1; 104 | uint32_t crop_bottom = ((encoder->setup.height_mbs * 16) - 105 | encoder->setup.height) >> 1; 106 | 107 | /* frame_crop_left_offset */ 108 | bitstream_append_ue(bitstream, 0); 109 | /* frame_crop_right_offset */ 110 | bitstream_append_ue(bitstream, crop_right); 111 | /* frame_crop_top_offset */ 112 | bitstream_append_ue(bitstream, 0); 113 | /* frame_crop_bottom_offset */ 114 | bitstream_append_ue(bitstream, crop_bottom); 115 | } 116 | 117 | /* vui_parameters_present_flag */ 118 | bitstream_append_bits(bitstream, 0, 1); 119 | 120 | /* rbsp_stop_one_bit */ 121 | bitstream_append_bits(bitstream, 1, 1); 122 | } 123 | 124 | static void bitstream_pps(struct bitstream *bitstream, 125 | struct v4l2_encoder *encoder) 126 | { 127 | struct v4l2_ctrl_h264_pps *pps = &encoder->pps; 128 | 129 | bitstream_reset(bitstream); 130 | 131 | /* NALU header */ 132 | bitstream_append_bits(bitstream, 0, 1); 133 | bitstream_append_bits(bitstream, 3, 2); 134 | bitstream_append_bits(bitstream, 8, 5); 135 | 136 | bitstream_append_ue(bitstream, pps->pic_parameter_set_id); 137 | bitstream_append_ue(bitstream, pps->seq_parameter_set_id); 138 | /* entropy_coding_mode_flag */ 139 | bitstream_append_bits(bitstream, !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE), 1); 140 | /* bottom_field_pic_order_in_frame_present_flag */ 141 | bitstream_append_bits(bitstream, 0, 1); 142 | bitstream_append_ue(bitstream, pps->num_slice_groups_minus1); 143 | bitstream_append_ue(bitstream, pps->num_ref_idx_l0_default_active_minus1); 144 | bitstream_append_ue(bitstream, pps->num_ref_idx_l1_default_active_minus1); 145 | /* weighted_pred_flag */ 146 | bitstream_append_bits(bitstream, 0, 1); 147 | bitstream_append_bits(bitstream, pps->weighted_bipred_idc, 2); 148 | bitstream_append_se(bitstream, pps->pic_init_qp_minus26); 149 | bitstream_append_se(bitstream, pps->pic_init_qs_minus26); 150 | bitstream_append_se(bitstream, pps->chroma_qp_index_offset); 151 | /* deblocking_filter_control_present_flag */ 152 | /* XXX: fixed by hardware */ 153 | bitstream_append_bits(bitstream, 1, 1); 154 | /* constrained_intra_pred_flag */ 155 | bitstream_append_bits(bitstream, 0, 1); 156 | /* XXX: fixed by hardware */ 157 | /* redundant_pic_cnt_present_flag */ 158 | bitstream_append_bits(bitstream, 0, 1); 159 | 160 | /* rbsp_stop_one_bit */ 161 | bitstream_append_bits(bitstream, 1, 1); 162 | } 163 | 164 | int h264_complete(struct v4l2_encoder *encoder) 165 | { 166 | struct v4l2_encoder_buffer *capture_buffer; 167 | struct v4l2_ctrl_h264_encode_feedback *encode_feedback; 168 | unsigned int bytes_used; 169 | unsigned int capture_index; 170 | 171 | capture_index = encoder->capture_buffers_index; 172 | capture_buffer = &encoder->capture_buffers[capture_index]; 173 | 174 | /* Feedback */ 175 | 176 | encode_feedback = &encoder->h264_dst_controls.encode_feedback; 177 | bytes_used = capture_buffer->buffer.m.planes[0].bytesused; 178 | 179 | h264_rate_control_feedback(encoder, bytes_used, 180 | encode_feedback->rlc_count, 181 | encode_feedback->qp_sum); 182 | 183 | /* Slice */ 184 | 185 | if (encoder->bitstream_fd >= 0) 186 | write(encoder->bitstream_fd, capture_buffer->mmap_data[0], 187 | capture_buffer->buffer.m.planes[0].bytesused); 188 | 189 | /* GOP */ 190 | 191 | encoder->gop_index++; 192 | encoder->gop_index %= encoder->setup.gop_size; 193 | 194 | return 0; 195 | } 196 | 197 | int h264_prepare(struct v4l2_encoder *encoder) 198 | { 199 | struct v4l2_ctrl_h264_encode_params *encode_params = 200 | &encoder->h264_src_controls.encode_params; 201 | struct v4l2_ctrl_h264_encode_rc *encode_rc = 202 | &encoder->h264_src_controls.encode_rc; 203 | struct v4l2_ctrl_h264_sps *sps = &encoder->sps; 204 | struct v4l2_ctrl_h264_pps *pps = &encoder->pps; 205 | unsigned int i; 206 | 207 | /* Encode */ 208 | 209 | if (!encoder->gop_index) { 210 | encode_params->slice_type = V4L2_H264_SLICE_TYPE_I; 211 | encode_params->idr_pic_id++; 212 | encode_params->frame_num = 0; 213 | } else { 214 | encode_params->slice_type = V4L2_H264_SLICE_TYPE_P; 215 | encode_params->reference_ts = encoder->reference_timestamp; 216 | encode_params->frame_num++; 217 | encode_params->frame_num %= (1 << (sps->log2_max_frame_num_minus4 + 4)); 218 | } 219 | 220 | encode_params->pic_parameter_set_id = 0; 221 | encode_params->cabac_init_idc = 0; 222 | 223 | encode_params->pic_init_qp_minus26 = pps->pic_init_qp_minus26; 224 | encode_params->chroma_qp_index_offset = pps->chroma_qp_index_offset; 225 | 226 | if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) 227 | encode_params->flags |= V4L2_H264_ENCODE_FLAG_ENTROPY_CODING_MODE; 228 | if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) 229 | encode_params->flags |= V4L2_H264_ENCODE_FLAG_TRANSFORM_8X8_MODE; 230 | if (pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) 231 | encode_params->flags |= V4L2_H264_ENCODE_FLAG_CONSTRAINED_INTRA_PRED; 232 | 233 | /* Rate Control */ 234 | 235 | h264_rate_control_step(encoder); 236 | 237 | encode_rc->qp = encoder->rc.qp; 238 | encode_rc->qp_min = encoder->setup.qp_min; 239 | encode_rc->qp_max = encoder->setup.qp_max; 240 | 241 | /* Checkpoints */ 242 | 243 | /* Only apply checkpoints to intra frames. */ 244 | if (encoder->rc.cp_enabled) { 245 | encode_rc->cp_distance_mbs = encoder->rc.cp_distance_mbs; 246 | 247 | for (i = 0; i < encoder->rc.cp_count; i++) 248 | encode_rc->cp_target[i] = encoder->rc.cp_target[i]; 249 | 250 | for (i = 0; i < ARRAY_SIZE(encode_rc->cp_target_error); i++) 251 | encode_rc->cp_target_error[i] = 252 | encoder->rc.cp_target_error[i]; 253 | 254 | for (i = 0; i < ARRAY_SIZE(encode_rc->cp_qp_delta); i++) 255 | encode_rc->cp_qp_delta[i] = encoder->rc.cp_qp_delta[i]; 256 | } else { 257 | encode_rc->cp_distance_mbs = 0; 258 | 259 | for (i = 0; i < encoder->rc.cp_count; i++) 260 | encode_rc->cp_target[i] = 0; 261 | 262 | for (i = 0; i < ARRAY_SIZE(encode_rc->cp_target_error); i++) 263 | encode_rc->cp_target_error[i] = 0; 264 | 265 | for (i = 0; i < ARRAY_SIZE(encode_rc->cp_qp_delta); i++) 266 | encode_rc->cp_qp_delta[i] = 0; 267 | } 268 | 269 | /* MAD */ 270 | 271 | encode_rc->mad_threshold = 0; 272 | encode_rc->mad_qp_delta = 0; 273 | 274 | return 0; 275 | } 276 | 277 | int h264_setup(struct v4l2_encoder *encoder) 278 | { 279 | struct v4l2_ctrl_h264_sps *sps = &encoder->sps; 280 | struct v4l2_ctrl_h264_pps *pps = &encoder->pps; 281 | struct bitstream *bitstream; 282 | struct unit *unit; 283 | 284 | /* SPS */ 285 | 286 | sps->profile_idc = 100; 287 | sps->level_idc = 31; 288 | sps->seq_parameter_set_id = 0; 289 | sps->chroma_format_idc = 1; /* YUV 4:2:0 */ 290 | 291 | sps->pic_width_in_mbs_minus1 = encoder->setup.width_mbs - 1; 292 | sps->pic_height_in_map_units_minus1 = encoder->setup.height_mbs - 1; 293 | 294 | sps->max_num_ref_frames = 1; 295 | 296 | // XXX: fixed by hardware 297 | sps->pic_order_cnt_type = 2; 298 | 299 | // XXX: fixed by hardware FOSHO 300 | sps->log2_max_frame_num_minus4 = 12; 301 | 302 | // XXX: fixed by hardware (at least constant in MPP) 303 | sps->flags |= V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE; 304 | 305 | /* PPS */ 306 | 307 | pps->pic_parameter_set_id = 0; 308 | pps->seq_parameter_set_id = 0; 309 | 310 | pps->flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; 311 | 312 | /* XXX: fixed by hardware */ 313 | pps->weighted_bipred_idc = 0; 314 | 315 | pps->chroma_qp_index_offset = 4; 316 | pps->pic_init_qp_minus26 = 20; 317 | 318 | /* Bitstream */ 319 | 320 | bitstream = bitstream_create(); 321 | 322 | /* Bitstream SPS */ 323 | 324 | bitstream_sps(bitstream, encoder); 325 | 326 | unit = unit_pack(bitstream); 327 | 328 | if (encoder->bitstream_fd >= 0) 329 | write(encoder->bitstream_fd, unit->buffer, unit->length); 330 | 331 | unit_destroy(unit); 332 | 333 | /* Bitstream PPS */ 334 | 335 | bitstream_pps(bitstream, encoder); 336 | 337 | unit = unit_pack(bitstream); 338 | 339 | if (encoder->bitstream_fd >= 0) 340 | write(encoder->bitstream_fd, unit->buffer, unit->length); 341 | 342 | unit_destroy(unit); 343 | 344 | bitstream_destroy(bitstream); 345 | 346 | /* Rate control */ 347 | 348 | h264_rate_control_setup(encoder); 349 | 350 | return 0; 351 | } 352 | 353 | int h264_teardown(struct v4l2_encoder *encoder) 354 | { 355 | return 0; 356 | } 357 | -------------------------------------------------------------------------------- /h264.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #ifndef _H264_H_ 6 | #define _H264_H_ 7 | 8 | #include 9 | 10 | int h264_complete(struct v4l2_encoder *encoder); 11 | int h264_prepare(struct v4l2_encoder *encoder); 12 | int h264_setup(struct v4l2_encoder *encoder); 13 | int h264_teardown(struct v4l2_encoder *encoder); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /media.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2020 Paul Kocialkowski 3 | * Copyright (C) 2020 Bootlin 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | int media_device_info(int media_fd, struct media_device_info *device_info) 22 | { 23 | int ret; 24 | 25 | ret = ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, device_info); 26 | if (ret) 27 | return -errno; 28 | 29 | return 0; 30 | } 31 | 32 | int media_topology_get(int media_fd, struct media_v2_topology *topology) 33 | { 34 | int ret; 35 | 36 | ret = ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, topology); 37 | if (ret) 38 | return -errno; 39 | 40 | return 0; 41 | } 42 | 43 | struct media_v2_entity *media_topology_entity_find_by_function(struct media_v2_topology *topology, 44 | unsigned int function) 45 | { 46 | struct media_v2_entity *entities; 47 | unsigned int i; 48 | 49 | if (!topology || !topology->num_entities || !topology->ptr_entities) 50 | return NULL; 51 | 52 | entities = (struct media_v2_entity *)topology->ptr_entities; 53 | 54 | for (i = 0; i < topology->num_entities; i++) { 55 | struct media_v2_entity *entity = &entities[i]; 56 | 57 | if (entity->function == function) 58 | return entity; 59 | } 60 | 61 | return NULL; 62 | } 63 | 64 | struct media_v2_interface *media_topology_interface_find_by_id(struct media_v2_topology *topology, 65 | unsigned int id) 66 | { 67 | struct media_v2_interface *interfaces; 68 | unsigned int interfaces_count; 69 | unsigned int i; 70 | 71 | if (!topology || !topology->num_interfaces || !topology->ptr_interfaces) 72 | return NULL; 73 | 74 | interfaces = (struct media_v2_interface *)topology->ptr_interfaces; 75 | interfaces_count = topology->num_interfaces; 76 | 77 | for (i = 0; i < interfaces_count; i++) { 78 | struct media_v2_interface *interface = &interfaces[i]; 79 | 80 | if (interface->id == id) 81 | return interface; 82 | } 83 | 84 | return NULL; 85 | } 86 | 87 | struct media_v2_pad *media_topology_pad_find_by_entity(struct media_v2_topology *topology, 88 | unsigned int entity_id, 89 | unsigned int flags) 90 | { 91 | struct media_v2_pad *pads; 92 | unsigned int pads_count; 93 | unsigned int i; 94 | 95 | if (!topology || !topology->num_pads || !topology->ptr_pads) 96 | return NULL; 97 | 98 | pads = (struct media_v2_pad *)topology->ptr_pads; 99 | pads_count = topology->num_pads; 100 | 101 | for (i = 0; i < pads_count; i++) { 102 | struct media_v2_pad *pad = &pads[i]; 103 | 104 | if (pad->entity_id == entity_id && 105 | (pad->flags & flags) == flags) 106 | return pad; 107 | } 108 | 109 | return NULL; 110 | } 111 | 112 | struct media_v2_pad *media_topology_pad_find_by_id(struct media_v2_topology *topology, 113 | unsigned int id) 114 | { 115 | struct media_v2_pad *pads; 116 | unsigned int pads_count; 117 | unsigned int i; 118 | 119 | if (!topology || !topology->num_pads || !topology->ptr_pads) 120 | return NULL; 121 | 122 | pads = (struct media_v2_pad *)topology->ptr_pads; 123 | pads_count = topology->num_pads; 124 | 125 | for (i = 0; i < pads_count; i++) { 126 | struct media_v2_pad *pad = &pads[i]; 127 | 128 | if (pad->id == id) 129 | return pad; 130 | } 131 | 132 | return NULL; 133 | } 134 | 135 | struct media_v2_link *media_topology_link_find_by_pad(struct media_v2_topology *topology, 136 | unsigned int pad_id, 137 | unsigned int pad_flags) 138 | { 139 | struct media_v2_link *links; 140 | unsigned int links_count; 141 | unsigned int i; 142 | 143 | if (!topology || !topology->num_links || !topology->ptr_links) 144 | return NULL; 145 | 146 | links = (struct media_v2_link *)topology->ptr_links; 147 | links_count = topology->num_links; 148 | 149 | for (i = 0; i < links_count; i++) { 150 | struct media_v2_link *link = &links[i]; 151 | 152 | if ((pad_flags & MEDIA_PAD_FL_SINK && link->sink_id == pad_id) || 153 | (pad_flags & MEDIA_PAD_FL_SOURCE && link->source_id == pad_id)) 154 | return link; 155 | } 156 | 157 | return NULL; 158 | } 159 | 160 | struct media_v2_link *media_topology_link_find_by_entity(struct media_v2_topology *topology, 161 | unsigned int entity_id, 162 | unsigned int pad_flags) 163 | { 164 | struct media_v2_link *links; 165 | unsigned int links_count; 166 | unsigned int i; 167 | 168 | if (!topology || !topology->num_links || !topology->ptr_links) 169 | return NULL; 170 | 171 | links = (struct media_v2_link *)topology->ptr_links; 172 | links_count = topology->num_links; 173 | 174 | for (i = 0; i < links_count; i++) { 175 | struct media_v2_link *link = &links[i]; 176 | 177 | if ((pad_flags & MEDIA_PAD_FL_SINK && link->sink_id == entity_id) || 178 | (pad_flags & MEDIA_PAD_FL_SOURCE && link->source_id == entity_id)) 179 | return link; 180 | } 181 | 182 | return NULL; 183 | } 184 | 185 | int media_request_alloc(int media_fd) 186 | { 187 | int request_fd; 188 | int ret; 189 | 190 | ret = ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &request_fd); 191 | if (ret) 192 | return -errno; 193 | 194 | return request_fd; 195 | } 196 | 197 | int media_request_queue(int request_fd) 198 | { 199 | int ret; 200 | 201 | ret = ioctl(request_fd, MEDIA_REQUEST_IOC_QUEUE, NULL); 202 | if (ret) 203 | return -errno; 204 | 205 | return 0; 206 | } 207 | 208 | int media_request_reinit(int request_fd) 209 | { 210 | int ret; 211 | 212 | ret = ioctl(request_fd, MEDIA_REQUEST_IOC_REINIT, NULL); 213 | if (ret) 214 | return -errno; 215 | 216 | return 0; 217 | } 218 | 219 | int media_request_poll(int request_fd, struct timeval *timeout) 220 | { 221 | fd_set except_fds; 222 | int ret; 223 | 224 | FD_ZERO(&except_fds); 225 | FD_SET(request_fd, &except_fds); 226 | 227 | ret = select(request_fd + 1, NULL, NULL, &except_fds, timeout); 228 | if (ret < 0) 229 | return -errno; 230 | 231 | if (!FD_ISSET(request_fd, &except_fds)) 232 | return 0; 233 | 234 | return ret; 235 | } 236 | -------------------------------------------------------------------------------- /media.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2020 Paul Kocialkowski 3 | * Copyright (C) 2020 Bootlin 4 | */ 5 | 6 | #ifndef _MEDIA_H_ 7 | #define _MEDIA_H_ 8 | 9 | #include 10 | #include 11 | 12 | int media_device_info(int media_fd, struct media_device_info *device_info); 13 | int media_topology_get(int media_fd, struct media_v2_topology *topology); 14 | struct media_v2_entity *media_topology_entity_find_by_function(struct media_v2_topology *topology, 15 | unsigned int function); 16 | struct media_v2_interface *media_topology_interface_find_by_id(struct media_v2_topology *topology, 17 | unsigned int id); 18 | struct media_v2_pad *media_topology_pad_find_by_entity(struct media_v2_topology *topology, 19 | unsigned int entity_id, 20 | unsigned int flags); 21 | struct media_v2_pad *media_topology_pad_find_by_id(struct media_v2_topology *topology, 22 | unsigned int id); 23 | struct media_v2_link *media_topology_link_find_by_pad(struct media_v2_topology *topology, 24 | unsigned int pad_id, 25 | unsigned int pad_flags); 26 | struct media_v2_link *media_topology_link_find_by_entity(struct media_v2_topology *topology, 27 | unsigned int entity_id, 28 | unsigned int pad_flags); 29 | int media_request_alloc(int media_fd); 30 | int media_request_queue(int request_fd); 31 | int media_request_reinit(int request_fd); 32 | int media_request_poll(int request_fd, struct timeval *timeout); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | uint8_t start_code_prefix[] = { 0x00, 0x00, 0x00, 0x01 }; 14 | unsigned int start_code_prefix_size = sizeof(start_code_prefix); 15 | 16 | uint8_t forbidden_pattern_mask[] = { 0xff, 0xff, 0xfc }; 17 | uint8_t forbidden_pattern_match[] = { 0x00, 0x00, 0x00 }; 18 | unsigned int forbidden_pattern_size = sizeof(forbidden_pattern_mask); 19 | 20 | struct unit *unit_pack(struct bitstream *bitstream) 21 | { 22 | struct unit *unit = NULL; 23 | unsigned int unit_length; 24 | unsigned int i, j; 25 | unsigned int bitstream_length; 26 | uint8_t *bitstream_values; 27 | uint8_t *unit_values; 28 | 29 | if (!bitstream) 30 | return NULL; 31 | 32 | bitstream_align(bitstream); 33 | 34 | bitstream_length = bitstream->offset_bytes; 35 | bitstream_values = bitstream->buffer; 36 | unit_length = start_code_prefix_size + bitstream_length; 37 | 38 | for (i = 0; i < (bitstream_length - forbidden_pattern_size + 1); i++) { 39 | bool forbidden_pattern_found = true; 40 | 41 | for (j = 0; j < forbidden_pattern_size; j++) { 42 | if ((bitstream_values[j] & forbidden_pattern_mask[j]) != 43 | forbidden_pattern_match[j]) { 44 | forbidden_pattern_found = false; 45 | break; 46 | } 47 | } 48 | 49 | if (forbidden_pattern_found) { 50 | unit_length++; 51 | 52 | /* Skip the forbidden pattern. */ 53 | i += forbidden_pattern_size - 1; 54 | bitstream_values += forbidden_pattern_size - 1; 55 | } 56 | 57 | bitstream_values++; 58 | } 59 | 60 | bitstream_values = bitstream->buffer + bitstream_length - 1; 61 | 62 | if (!*bitstream_values) 63 | unit_length++; 64 | 65 | unit = calloc(1, sizeof(*unit)); 66 | if (!unit) 67 | goto error; 68 | 69 | unit->buffer = calloc(1, unit_length); 70 | unit->length = unit_length; 71 | 72 | bitstream_values = bitstream->buffer; 73 | unit_values = unit->buffer; 74 | 75 | /* Add the start code prefix. */ 76 | for (i = 0; i < start_code_prefix_size; i++) 77 | *unit_values++ = start_code_prefix[i]; 78 | 79 | /* Escape any forbidden pattern. */ 80 | for (i = 0; i < (bitstream_length - forbidden_pattern_size + 1); i++) { 81 | bool forbidden_pattern_found = true; 82 | 83 | for (j = 0; j < forbidden_pattern_size; j++) { 84 | if ((bitstream_values[j] & forbidden_pattern_mask[j]) != 85 | forbidden_pattern_match[j]) { 86 | forbidden_pattern_found = false; 87 | break; 88 | } 89 | } 90 | 91 | if (forbidden_pattern_found) { 92 | for (j = 0; j < (forbidden_pattern_size - 1); j++) { 93 | *unit_values++ = *bitstream_values++; 94 | i++; 95 | } 96 | 97 | *unit_values++ = 0x03; 98 | } 99 | 100 | *unit_values++ = *bitstream_values++; 101 | } 102 | 103 | /* Copy leftover data */ 104 | for (; i < bitstream_length; i++) 105 | *unit_values++ = *bitstream_values++; 106 | 107 | bitstream_values = bitstream->buffer + bitstream_length - 1; 108 | 109 | /* Escape a forbidden final zero byte. */ 110 | if (!*bitstream_values) 111 | *unit_values++ = 0x03; 112 | 113 | return unit; 114 | 115 | error: 116 | if (unit) 117 | free(unit); 118 | 119 | return NULL; 120 | } 121 | 122 | void unit_destroy(struct unit *unit) 123 | { 124 | if (!unit) 125 | return; 126 | 127 | if (unit->buffer) 128 | free(unit->buffer); 129 | 130 | free(unit); 131 | } 132 | -------------------------------------------------------------------------------- /unit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #ifndef _UNIT_H_ 6 | #define _UNIT_H_ 7 | 8 | struct bitstream; 9 | 10 | struct unit { 11 | void *buffer; 12 | unsigned int length; 13 | }; 14 | 15 | extern uint8_t start_code_prefix[]; 16 | extern unsigned int start_code_prefix_size; 17 | 18 | struct unit *unit_pack(struct bitstream *bitstream); 19 | void unit_destroy(struct unit *unit); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /v4l2-encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2020 Paul Kocialkowski 3 | * Copyright (C) 2020 Bootlin 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) 30 | 31 | int v4l2_encoder_complete(struct v4l2_encoder *encoder) 32 | { 33 | struct v4l2_encoder_buffer *output_buffer; 34 | unsigned int output_index; 35 | int ret; 36 | 37 | if (!encoder) 38 | return -EINVAL; 39 | 40 | output_index = encoder->output_buffers_index; 41 | output_buffer = &encoder->output_buffers[output_index]; 42 | 43 | ret = h264_complete(encoder); 44 | if (ret) 45 | return ret; 46 | 47 | /* The reference buffer is always the previous frame. */ 48 | v4l2_buffer_timestamp_get(&output_buffer->buffer, 49 | &encoder->reference_timestamp); 50 | 51 | encoder->output_buffers_index++; 52 | encoder->output_buffers_index %= encoder->output_buffers_count; 53 | 54 | encoder->capture_buffers_index++; 55 | encoder->capture_buffers_index %= encoder->capture_buffers_count; 56 | 57 | return 0; 58 | } 59 | 60 | int v4l2_encoder_prepare(struct v4l2_encoder *encoder) 61 | { 62 | struct v4l2_encoder_buffer *output_buffer; 63 | unsigned int output_index; 64 | unsigned int width, height; 65 | int fd; 66 | int ret; 67 | 68 | if (!encoder) 69 | return -EINVAL; 70 | 71 | width = encoder->setup.width; 72 | height = encoder->setup.height; 73 | 74 | output_index = encoder->output_buffers_index; 75 | output_buffer = &encoder->output_buffers[output_index]; 76 | 77 | ret = h264_prepare(encoder); 78 | if (ret) 79 | return ret; 80 | 81 | #define MANDELBROT 82 | 83 | #ifdef MANDELBROT 84 | draw_mandelbrot_zoom(&encoder->draw_mandelbrot); 85 | draw_mandelbrot(&encoder->draw_mandelbrot, encoder->draw_buffer); 86 | #endif 87 | #ifdef GRADIENT 88 | draw_gradient(encoder->draw_buffer); 89 | #endif 90 | #ifdef RECTANGLE 91 | draw_background(encoder->draw_buffer, 0xff00ffff); 92 | draw_rectangle(encoder->draw_buffer, encoder->x, height / 3, width / 3, 93 | height / 3, 0x00ff0000); 94 | 95 | if (!encoder->direction) { 96 | if (encoder->x >= 20) { 97 | encoder->x -= 20; 98 | } else { 99 | encoder->x = 0; 100 | encoder->direction = 1; 101 | } 102 | } else { 103 | if (encoder->x < (2 * width / 3 - 20)) { 104 | encoder->x += 20; 105 | } else { 106 | encoder->x = 2 * width / 3; 107 | encoder->direction = 0; 108 | } 109 | } 110 | #endif 111 | #ifdef PATTERN 112 | if (!encoder->pattern_drawn) { 113 | draw_png(encoder->draw_buffer, "test-pattern.png"); 114 | encoder->pattern_drawn = true; 115 | } 116 | #endif 117 | 118 | if (encoder->setup.format == V4L2_PIX_FMT_YUV420M) 119 | ret = rgb2yuv420(encoder->draw_buffer, 120 | output_buffer->mmap_data[0], 121 | output_buffer->mmap_data[1], 122 | output_buffer->mmap_data[2]); 123 | else 124 | ret = rgb2nv12(encoder->draw_buffer, 125 | output_buffer->mmap_data[0], 126 | output_buffer->mmap_data[1]); 127 | 128 | #ifdef OUTPUT_DUMP 129 | fd = open("output.yuv", O_WRONLY | O_CREAT | O_TRUNC, 0644); 130 | if (fd < 0) { 131 | printf("output open error!\n"); 132 | return -1; 133 | } 134 | 135 | write(fd, output_buffer->mmap_data[0], width * height); 136 | write(fd, output_buffer->mmap_data[1], width * height / 4); 137 | write(fd, output_buffer->mmap_data[2], width * height / 4); 138 | 139 | close(fd); 140 | #endif 141 | 142 | return 0; 143 | } 144 | 145 | int v4l2_encoder_run(struct v4l2_encoder *encoder) 146 | { 147 | struct v4l2_encoder_buffer *output_buffer; 148 | unsigned int output_index; 149 | struct v4l2_encoder_buffer *capture_buffer; 150 | unsigned int capture_index; 151 | struct timeval timeout = { 0, 300000 }; 152 | int ret; 153 | 154 | if (!encoder) 155 | return -EINVAL; 156 | 157 | output_index = encoder->output_buffers_index; 158 | output_buffer = &encoder->output_buffers[output_index]; 159 | 160 | v4l2_buffer_request_attach(&output_buffer->buffer, 161 | output_buffer->request_fd); 162 | 163 | ret = v4l2_buffer_queue(encoder->video_fd, &output_buffer->buffer); 164 | if (ret) 165 | return ret; 166 | 167 | v4l2_buffer_request_detach(&output_buffer->buffer); 168 | 169 | capture_index = encoder->capture_buffers_index; 170 | capture_buffer = &encoder->capture_buffers[capture_index]; 171 | 172 | ret = v4l2_buffer_queue(encoder->video_fd, &capture_buffer->buffer); 173 | if (ret) 174 | return ret; 175 | 176 | v4l2_ext_controls_request_attach(&encoder->h264_src_controls.ext_controls, 177 | output_buffer->request_fd); 178 | 179 | ret = v4l2_ext_controls_set(encoder->video_fd, 180 | &encoder->h264_src_controls.ext_controls); 181 | if (ret) 182 | return ret; 183 | 184 | v4l2_ext_controls_request_detach(&encoder->h264_src_controls.ext_controls); 185 | 186 | ret = media_request_queue(output_buffer->request_fd); 187 | if (ret) 188 | return ret; 189 | 190 | ret = media_request_poll(output_buffer->request_fd, &timeout); 191 | if (ret <= 0) 192 | return ret; 193 | 194 | v4l2_ext_controls_request_attach(&encoder->h264_dst_controls.ext_controls, 195 | output_buffer->request_fd); 196 | 197 | ret = v4l2_ext_controls_get(encoder->video_fd, 198 | &encoder->h264_dst_controls.ext_controls); 199 | if (ret) 200 | return ret; 201 | 202 | v4l2_ext_controls_request_detach(&encoder->h264_dst_controls.ext_controls); 203 | 204 | do { 205 | ret = v4l2_buffer_dequeue(encoder->video_fd, &output_buffer->buffer); 206 | if (ret && ret != -EAGAIN) 207 | return ret; 208 | } while (ret == -EAGAIN); 209 | 210 | do { 211 | ret = v4l2_buffer_dequeue(encoder->video_fd, &capture_buffer->buffer); 212 | if (ret && ret != -EAGAIN) 213 | return ret; 214 | } while (ret == -EAGAIN); 215 | 216 | ret = media_request_reinit(output_buffer->request_fd); 217 | if (ret) 218 | return ret; 219 | 220 | return 0; 221 | } 222 | 223 | int v4l2_encoder_start(struct v4l2_encoder *encoder) 224 | { 225 | int ret; 226 | 227 | if (!encoder || encoder->started) 228 | return -EINVAL; 229 | 230 | ret = v4l2_stream_on(encoder->video_fd, encoder->output_type); 231 | if (ret) 232 | return ret; 233 | 234 | ret = v4l2_stream_on(encoder->video_fd, encoder->capture_type); 235 | if (ret) 236 | return ret; 237 | 238 | encoder->started = true; 239 | 240 | return 0; 241 | } 242 | 243 | int v4l2_encoder_stop(struct v4l2_encoder *encoder) 244 | { 245 | int ret; 246 | 247 | if (!encoder || !encoder->started) 248 | return -EINVAL; 249 | 250 | ret = v4l2_stream_off(encoder->video_fd, encoder->output_type); 251 | if (ret) 252 | return ret; 253 | 254 | ret = v4l2_stream_off(encoder->video_fd, encoder->capture_type); 255 | if (ret) 256 | return ret; 257 | 258 | encoder->started = false; 259 | 260 | return 0; 261 | } 262 | 263 | int v4l2_encoder_intra_request(struct v4l2_encoder *encoder) 264 | { 265 | int ret; 266 | 267 | if (!encoder || !encoder->started) 268 | return -EINVAL; 269 | 270 | encoder->gop_index = 0; 271 | 272 | ret = h264_rate_control_intra_request(encoder); 273 | if (ret) 274 | return ret; 275 | 276 | return 0; 277 | } 278 | 279 | int v4l2_encoder_buffer_setup(struct v4l2_encoder_buffer *buffer, 280 | unsigned int type, unsigned int index) 281 | { 282 | struct v4l2_encoder *encoder; 283 | int ret; 284 | 285 | if (!buffer || !buffer->encoder) 286 | return -EINVAL; 287 | 288 | encoder = buffer->encoder; 289 | 290 | v4l2_buffer_setup_base(&buffer->buffer, type, encoder->memory, index, 291 | buffer->planes, buffer->planes_count); 292 | 293 | ret = v4l2_buffer_query(encoder->video_fd, &buffer->buffer); 294 | if (ret) { 295 | fprintf(stderr, "Failed to query buffer\n"); 296 | goto complete; 297 | } 298 | 299 | if(encoder->memory == V4L2_MEMORY_MMAP) { 300 | unsigned int i; 301 | 302 | for (i = 0; i < buffer->planes_count; i++) { 303 | unsigned int offset; 304 | unsigned int length; 305 | 306 | ret = v4l2_buffer_plane_offset(&buffer->buffer, i, 307 | &offset); 308 | if (ret) 309 | goto complete; 310 | 311 | ret = v4l2_buffer_plane_length(&buffer->buffer, i, 312 | &length); 313 | if (ret) 314 | goto complete; 315 | 316 | buffer->mmap_data[i] = 317 | mmap(NULL, length, PROT_READ | PROT_WRITE, 318 | MAP_SHARED, encoder->video_fd, offset); 319 | if (buffer->mmap_data[i] == MAP_FAILED) { 320 | ret = -errno; 321 | goto complete; 322 | } 323 | } 324 | } 325 | 326 | if (type == encoder->output_type) { 327 | buffer->request_fd = media_request_alloc(encoder->media_fd); 328 | if (buffer->request_fd < 0) { 329 | ret = -EINVAL; 330 | goto complete; 331 | } 332 | 333 | v4l2_buffer_timestamp_set(&buffer->buffer, index * 1000); 334 | } else { 335 | buffer->request_fd = -1; 336 | } 337 | 338 | ret = 0; 339 | 340 | complete: 341 | return ret; 342 | } 343 | 344 | int v4l2_encoder_buffer_teardown(struct v4l2_encoder_buffer *buffer) 345 | { 346 | struct v4l2_encoder *encoder; 347 | 348 | if (!buffer || !buffer->encoder) 349 | return -EINVAL; 350 | 351 | encoder = buffer->encoder; 352 | 353 | if(encoder->memory == V4L2_MEMORY_MMAP) { 354 | unsigned int i; 355 | 356 | for (i = 0; i < buffer->planes_count; i++) { 357 | unsigned int length; 358 | 359 | if (!buffer->mmap_data[i] || 360 | buffer->mmap_data[i] == MAP_FAILED) 361 | continue; 362 | 363 | v4l2_buffer_plane_length(&buffer->buffer, i, &length); 364 | munmap(buffer->mmap_data[i], length); 365 | } 366 | } 367 | 368 | if (buffer->request_fd >= 0) 369 | close(buffer->request_fd); 370 | 371 | memset(buffer, 0, sizeof(*buffer)); 372 | buffer->request_fd = -1; 373 | 374 | return 0; 375 | } 376 | 377 | int v4l2_encoder_h264_src_controls_setup(struct v4l2_encoder_h264_src_controls *h264_src_controls) 378 | { 379 | unsigned int controls_count; 380 | unsigned int index = 0; 381 | 382 | if (!h264_src_controls) 383 | return -EINVAL; 384 | 385 | h264_src_controls->encode_params_control = 386 | &h264_src_controls->controls[index]; 387 | v4l2_ext_control_setup_base(h264_src_controls->encode_params_control, 388 | V4L2_CID_STATELESS_H264_ENCODE_PARAMS); 389 | v4l2_ext_control_setup_compound(h264_src_controls->encode_params_control, 390 | &h264_src_controls->encode_params, 391 | sizeof(h264_src_controls->encode_params)); 392 | index++; 393 | 394 | h264_src_controls->encode_rc_control = 395 | &h264_src_controls->controls[index]; 396 | v4l2_ext_control_setup_base(h264_src_controls->encode_rc_control, 397 | V4L2_CID_STATELESS_H264_ENCODE_RC); 398 | v4l2_ext_control_setup_compound(h264_src_controls->encode_rc_control, 399 | &h264_src_controls->encode_rc, 400 | sizeof(h264_src_controls->encode_rc)); 401 | index++; 402 | 403 | controls_count = index; 404 | 405 | v4l2_ext_controls_setup(&h264_src_controls->ext_controls, 406 | h264_src_controls->controls, controls_count); 407 | 408 | h264_src_controls->controls_count = controls_count; 409 | 410 | return 0; 411 | } 412 | 413 | int v4l2_encoder_h264_dst_controls_setup(struct v4l2_encoder_h264_dst_controls *h264_dst_controls) 414 | { 415 | unsigned int controls_count; 416 | unsigned int index = 0; 417 | 418 | if (!h264_dst_controls) 419 | return -EINVAL; 420 | 421 | h264_dst_controls->encode_feedback_control = 422 | &h264_dst_controls->controls[index]; 423 | v4l2_ext_control_setup_base(h264_dst_controls->encode_feedback_control, 424 | V4L2_CID_STATELESS_H264_ENCODE_FEEDBACK); 425 | v4l2_ext_control_setup_compound(h264_dst_controls->encode_feedback_control, 426 | &h264_dst_controls->encode_feedback, 427 | sizeof(h264_dst_controls->encode_feedback)); 428 | index++; 429 | 430 | controls_count = index; 431 | 432 | v4l2_ext_controls_setup(&h264_dst_controls->ext_controls, 433 | h264_dst_controls->controls, controls_count); 434 | 435 | h264_dst_controls->controls_count = controls_count; 436 | 437 | return 0; 438 | } 439 | 440 | int v4l2_encoder_setup_defaults(struct v4l2_encoder *encoder) 441 | { 442 | int ret; 443 | 444 | if (!encoder) 445 | return -EINVAL; 446 | 447 | if (encoder->up) 448 | return -EBUSY; 449 | 450 | ret = v4l2_encoder_setup_dimensions(encoder, 1280, 720); 451 | if (ret) 452 | return ret; 453 | 454 | ret = v4l2_encoder_setup_format(encoder, V4L2_PIX_FMT_NV12M); 455 | if (ret) 456 | return ret; 457 | 458 | ret = v4l2_encoder_setup_fps(encoder, 25); 459 | if (ret) 460 | return ret; 461 | 462 | ret = v4l2_encoder_setup_bitrate(encoder, 500000); 463 | if (ret) 464 | return ret; 465 | 466 | encoder->setup.gop_size = 10; 467 | 468 | encoder->setup.qp_intra_delta = 2; 469 | encoder->setup.qp_min = 11; 470 | encoder->setup.qp_max = 51; 471 | 472 | return 0; 473 | } 474 | 475 | int v4l2_encoder_setup_dimensions(struct v4l2_encoder *encoder, 476 | unsigned int width, unsigned int height) 477 | { 478 | if (!encoder || !width || !height) 479 | return -EINVAL; 480 | 481 | if (encoder->up) 482 | return -EBUSY; 483 | 484 | encoder->setup.width = width; 485 | encoder->setup.width_mbs = (width + 15) / 16; 486 | 487 | encoder->setup.height = height; 488 | encoder->setup.height_mbs = (height + 15) / 16; 489 | 490 | return 0; 491 | } 492 | 493 | int v4l2_encoder_setup_format(struct v4l2_encoder *encoder, uint32_t format) 494 | { 495 | if (!encoder) 496 | return -EINVAL; 497 | 498 | if (encoder->up) 499 | return -EBUSY; 500 | 501 | encoder->setup.format = format; 502 | 503 | return 0; 504 | } 505 | 506 | int v4l2_encoder_setup_fps(struct v4l2_encoder *encoder, float fps) 507 | { 508 | if (!encoder || !fps) 509 | return -EINVAL; 510 | 511 | encoder->setup.fps_den = 1000; 512 | encoder->setup.fps_num = fps * encoder->setup.fps_den; 513 | 514 | if (encoder->up) 515 | h264_rate_control_setup(encoder); 516 | 517 | return 0; 518 | } 519 | 520 | int v4l2_encoder_setup_bitrate(struct v4l2_encoder *encoder, uint64_t bitrate) 521 | { 522 | if (!encoder || !bitrate) 523 | return -EINVAL; 524 | 525 | encoder->setup.bitrate = bitrate; 526 | 527 | if (encoder->up) 528 | h264_rate_control_setup(encoder); 529 | 530 | return 0; 531 | } 532 | 533 | int v4l2_encoder_setup(struct v4l2_encoder *encoder) 534 | { 535 | unsigned int width, height; 536 | unsigned int buffers_count; 537 | unsigned int capture_size; 538 | uint32_t format; 539 | unsigned int i; 540 | int ret; 541 | 542 | if (!encoder || encoder->up) 543 | return -EINVAL; 544 | 545 | capture_size = 512 * 1024; 546 | width = encoder->setup.width; 547 | height = encoder->setup.height; 548 | format = encoder->setup.format; 549 | 550 | /* Capture format */ 551 | 552 | v4l2_format_setup_pixel(&encoder->capture_format, encoder->capture_type, 553 | width, height, V4L2_PIX_FMT_H264_SLICE); 554 | 555 | if (v4l2_type_mplane_check(encoder->capture_type)) 556 | encoder->capture_format.fmt.pix_mp.plane_fmt[0].sizeimage = 557 | capture_size; 558 | else 559 | encoder->capture_format.fmt.pix.sizeimage = capture_size; 560 | 561 | ret = v4l2_format_try(encoder->video_fd, &encoder->capture_format); 562 | if (ret) { 563 | fprintf(stderr, "Failed to try capture format\n"); 564 | goto complete; 565 | } 566 | 567 | ret = v4l2_format_set(encoder->video_fd, &encoder->capture_format); 568 | if (ret) { 569 | fprintf(stderr, "Failed to set capture format\n"); 570 | goto complete; 571 | } 572 | 573 | /* Output format */ 574 | 575 | v4l2_format_setup_pixel(&encoder->output_format, encoder->output_type, 576 | width, height, format); 577 | 578 | ret = v4l2_format_try(encoder->video_fd, &encoder->output_format); 579 | if (ret) { 580 | fprintf(stderr, "Failed to try output format\n"); 581 | goto complete; 582 | } 583 | 584 | ret = v4l2_format_set(encoder->video_fd, &encoder->output_format); 585 | if (ret) { 586 | fprintf(stderr, "Failed to set output format\n"); 587 | goto complete; 588 | } 589 | 590 | /* Capture buffers */ 591 | 592 | buffers_count = ARRAY_SIZE(encoder->capture_buffers); 593 | 594 | ret = v4l2_buffers_request(encoder->video_fd, encoder->capture_type, 595 | encoder->memory, buffers_count); 596 | if (ret) { 597 | fprintf(stderr, "Failed to allocate capture buffers\n"); 598 | goto error; 599 | } 600 | 601 | for (i = 0; i < buffers_count; i++) { 602 | struct v4l2_encoder_buffer *buffer = &encoder->capture_buffers[i]; 603 | 604 | buffer->encoder = encoder; 605 | buffer->planes_count = 606 | encoder->capture_format.fmt.pix_mp.num_planes; 607 | 608 | ret = v4l2_encoder_buffer_setup(buffer, encoder->capture_type, i); 609 | if (ret) { 610 | fprintf(stderr, "Failed to setup capture buffer\n"); 611 | goto error; 612 | } 613 | } 614 | 615 | encoder->capture_buffers_count = buffers_count; 616 | 617 | /* Output buffers */ 618 | 619 | buffers_count = ARRAY_SIZE(encoder->output_buffers); 620 | 621 | ret = v4l2_buffers_request(encoder->video_fd, encoder->output_type, 622 | encoder->memory, buffers_count); 623 | if (ret) { 624 | fprintf(stderr, "Failed to allocate output buffers\n"); 625 | goto complete; 626 | } 627 | 628 | for (i = 0; i < buffers_count; i++) { 629 | struct v4l2_encoder_buffer *buffer = &encoder->output_buffers[i]; 630 | 631 | buffer->encoder = encoder; 632 | buffer->planes_count = 633 | encoder->output_format.fmt.pix_mp.num_planes; 634 | 635 | ret = v4l2_encoder_buffer_setup(buffer, encoder->output_type, i); 636 | if (ret) { 637 | fprintf(stderr, "Failed to setup output buffer\n"); 638 | goto error; 639 | } 640 | } 641 | 642 | encoder->output_buffers_count = buffers_count; 643 | 644 | /* Source controls */ 645 | 646 | ret = v4l2_encoder_h264_src_controls_setup(&encoder->h264_src_controls); 647 | if (ret) { 648 | fprintf(stderr, "Failed to setup source controls\n"); 649 | goto error; 650 | } 651 | 652 | /* Destination controls */ 653 | 654 | ret = v4l2_encoder_h264_dst_controls_setup(&encoder->h264_dst_controls); 655 | if (ret) { 656 | fprintf(stderr, "Failed to setup destination controls\n"); 657 | goto error; 658 | } 659 | 660 | /* H.264 */ 661 | 662 | ret = h264_setup(encoder); 663 | if (ret) { 664 | fprintf(stderr, "Failed to setup H.264 parameters\n"); 665 | goto error; 666 | } 667 | 668 | /* Draw buffer */ 669 | 670 | encoder->draw_buffer = draw_buffer_create(width, height); 671 | if (!encoder->draw_buffer) { 672 | fprintf(stderr, "Failed to create draw buffer\n"); 673 | goto error; 674 | } 675 | 676 | /* Mandelbrot */ 677 | 678 | draw_mandelbrot_init(&encoder->draw_mandelbrot); 679 | 680 | encoder->up = true; 681 | 682 | ret = 0; 683 | goto complete; 684 | 685 | error: 686 | buffers_count = ARRAY_SIZE(encoder->output_buffers); 687 | 688 | for (i = 0; i < buffers_count; i++) 689 | v4l2_encoder_buffer_teardown(&encoder->output_buffers[i]); 690 | 691 | v4l2_buffers_destroy(encoder->video_fd, encoder->output_type, 692 | encoder->memory); 693 | 694 | buffers_count = ARRAY_SIZE(encoder->capture_buffers); 695 | 696 | for (i = 0; i < buffers_count; i++) 697 | v4l2_encoder_buffer_teardown(&encoder->capture_buffers[i]); 698 | 699 | v4l2_buffers_destroy(encoder->video_fd, encoder->capture_type, 700 | encoder->memory); 701 | 702 | complete: 703 | return ret; 704 | } 705 | 706 | int v4l2_encoder_teardown(struct v4l2_encoder *encoder) 707 | { 708 | unsigned int buffers_count; 709 | unsigned int i; 710 | 711 | if (!encoder || !encoder->up) 712 | return -EINVAL; 713 | 714 | buffers_count = ARRAY_SIZE(encoder->output_buffers); 715 | 716 | for (i = 0; i < buffers_count; i++) 717 | v4l2_encoder_buffer_teardown(&encoder->output_buffers[i]); 718 | 719 | v4l2_buffers_destroy(encoder->video_fd, encoder->output_type, 720 | encoder->memory); 721 | 722 | buffers_count = ARRAY_SIZE(encoder->capture_buffers); 723 | 724 | for (i = 0; i < buffers_count; i++) 725 | v4l2_encoder_buffer_teardown(&encoder->capture_buffers[i]); 726 | 727 | v4l2_buffers_destroy(encoder->video_fd, encoder->capture_type, 728 | encoder->memory); 729 | 730 | h264_teardown(encoder); 731 | 732 | encoder->up = false; 733 | 734 | return 0; 735 | } 736 | 737 | int v4l2_encoder_probe(struct v4l2_encoder *encoder) 738 | { 739 | bool check, mplane_check; 740 | int ret; 741 | 742 | if (!encoder || encoder->video_fd < 0) 743 | return -EINVAL; 744 | 745 | ret = v4l2_capabilities_probe(encoder->video_fd, &encoder->capabilities, 746 | (char *)&encoder->driver, 747 | (char *)&encoder->card); 748 | if (ret) { 749 | fprintf(stderr, "Failed to probe V4L2 capabilities\n"); 750 | return ret; 751 | } 752 | 753 | printf("Probed driver %s card %s\n", encoder->driver, encoder->card); 754 | 755 | mplane_check = v4l2_capabilities_check(encoder->capabilities, 756 | V4L2_CAP_VIDEO_M2M_MPLANE); 757 | check = v4l2_capabilities_check(encoder->capabilities, 758 | V4L2_CAP_VIDEO_M2M); 759 | if (mplane_check) { 760 | encoder->output_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 761 | encoder->capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 762 | } else if (check) { 763 | encoder->output_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 764 | encoder->capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 765 | } else { 766 | fprintf(stderr, "Missing V4L2 M2M support\n"); 767 | return -1; 768 | } 769 | 770 | ret = v4l2_buffers_capabilities_probe(encoder->video_fd, 771 | encoder->output_type, 772 | &encoder->output_capabilities); 773 | if (ret) 774 | return ret; 775 | 776 | check = v4l2_capabilities_check(encoder->output_capabilities, 777 | V4L2_BUF_CAP_SUPPORTS_REQUESTS); 778 | if (!check) { 779 | fprintf(stderr, "Missing output requests support\n"); 780 | return -EINVAL; 781 | } 782 | 783 | ret = v4l2_buffers_capabilities_probe(encoder->video_fd, 784 | encoder->capture_type, 785 | &encoder->capture_capabilities); 786 | if (ret) 787 | return ret; 788 | 789 | encoder->memory = V4L2_MEMORY_MMAP; 790 | 791 | check = v4l2_pixel_format_check(encoder->video_fd, encoder->capture_type, 792 | V4L2_PIX_FMT_H264_SLICE); 793 | if (!check) { 794 | fprintf(stderr, "Missing H.264 slice pixel format\n"); 795 | return -EINVAL; 796 | } 797 | 798 | return 0; 799 | } 800 | 801 | static int media_device_probe(struct v4l2_encoder *encoder, struct udev *udev, 802 | struct udev_device *device) 803 | { 804 | const char *path = udev_device_get_devnode(device); 805 | struct media_device_info device_info = { 0 }; 806 | struct media_v2_topology topology = { 0 }; 807 | struct media_v2_interface *interfaces = NULL; 808 | struct media_v2_entity *entities = NULL; 809 | struct media_v2_pad *pads = NULL; 810 | struct media_v2_link *links = NULL; 811 | struct media_v2_entity *encoder_entity; 812 | struct media_v2_interface *encoder_interface; 813 | struct media_v2_pad *sink_pad; 814 | struct media_v2_link *sink_link; 815 | struct media_v2_pad *source_pad; 816 | struct media_v2_link *source_link; 817 | const char *driver = "hantro-vpu"; 818 | int media_fd = -1; 819 | int video_fd = -1; 820 | dev_t devnum; 821 | int ret; 822 | 823 | media_fd = open(path, O_RDWR); 824 | if (media_fd < 0) 825 | return -errno; 826 | 827 | ret = media_device_info(media_fd, &device_info); 828 | if (ret) 829 | goto error; 830 | 831 | ret = strncmp(device_info.driver, driver, strlen(driver)); 832 | if (ret) 833 | goto error; 834 | 835 | ret = media_topology_get(media_fd, &topology); 836 | if (ret) 837 | goto error; 838 | 839 | if (!topology.num_interfaces || !topology.num_entities || 840 | !topology.num_pads || !topology.num_links) { 841 | ret = -ENODEV; 842 | goto error; 843 | } 844 | 845 | interfaces = calloc(1, topology.num_interfaces * sizeof(*interfaces)); 846 | if (!interfaces) { 847 | ret = -ENOMEM; 848 | goto error; 849 | } 850 | 851 | topology.ptr_interfaces = (__u64)interfaces; 852 | 853 | entities = calloc(1, topology.num_entities * sizeof(*entities)); 854 | if (!entities) { 855 | ret = -ENOMEM; 856 | goto error; 857 | } 858 | 859 | topology.ptr_entities = (__u64)entities; 860 | 861 | pads = calloc(1, topology.num_pads * sizeof(*pads)); 862 | if (!pads) { 863 | ret = -ENOMEM; 864 | goto error; 865 | } 866 | 867 | topology.ptr_pads = (__u64)pads; 868 | 869 | links = calloc(1, topology.num_links * sizeof(*links)); 870 | if (!links) { 871 | ret = -ENOMEM; 872 | goto error; 873 | } 874 | 875 | topology.ptr_links = (__u64)links; 876 | 877 | ret = media_topology_get(media_fd, &topology); 878 | if (ret) 879 | goto error; 880 | 881 | encoder_entity = media_topology_entity_find_by_function(&topology, 882 | MEDIA_ENT_F_PROC_VIDEO_ENCODER); 883 | if (!encoder_entity) { 884 | ret = -ENODEV; 885 | goto error; 886 | } 887 | 888 | sink_pad = media_topology_pad_find_by_entity(&topology, 889 | encoder_entity->id, 890 | MEDIA_PAD_FL_SINK); 891 | if (!sink_pad) { 892 | ret = -ENODEV; 893 | goto error; 894 | } 895 | 896 | sink_link = media_topology_link_find_by_pad(&topology, sink_pad->id, 897 | sink_pad->flags); 898 | if (!sink_link) { 899 | ret = -ENODEV; 900 | goto error; 901 | } 902 | 903 | source_pad = media_topology_pad_find_by_id(&topology, 904 | sink_link->source_id); 905 | if (!source_pad) { 906 | ret = -ENODEV; 907 | goto error; 908 | } 909 | 910 | source_link = media_topology_link_find_by_entity(&topology, 911 | source_pad->entity_id, 912 | MEDIA_PAD_FL_SINK); 913 | if (!source_link) { 914 | ret = -ENODEV; 915 | goto error; 916 | } 917 | 918 | encoder_interface = media_topology_interface_find_by_id(&topology, 919 | source_link->source_id); 920 | if (!encoder_interface) { 921 | ret = -ENODEV; 922 | goto error; 923 | } 924 | 925 | devnum = makedev(encoder_interface->devnode.major, 926 | encoder_interface->devnode.minor); 927 | 928 | device = udev_device_new_from_devnum(udev, 'c', devnum); 929 | if (!device) { 930 | ret = -ENODEV; 931 | goto error; 932 | } 933 | 934 | path = udev_device_get_devnode(device); 935 | 936 | video_fd = open(path, O_RDWR | O_NONBLOCK); 937 | if (video_fd < 0) { 938 | ret = -errno; 939 | goto error; 940 | } 941 | 942 | encoder->media_fd = media_fd; 943 | encoder->video_fd = video_fd; 944 | 945 | ret = 0; 946 | goto complete; 947 | 948 | error: 949 | if (media_fd >= 0) 950 | close(media_fd); 951 | 952 | if (video_fd >= 0) 953 | close(video_fd); 954 | 955 | complete: 956 | if (links) 957 | free(links); 958 | 959 | if (pads) 960 | free(pads); 961 | 962 | if (entities) 963 | free(entities); 964 | 965 | if (interfaces) 966 | free(interfaces); 967 | 968 | return ret; 969 | } 970 | 971 | int v4l2_encoder_open(struct v4l2_encoder *encoder) 972 | { 973 | struct udev *udev = NULL; 974 | struct udev_enumerate *enumerate = NULL; 975 | struct udev_list_entry *devices; 976 | struct udev_list_entry *entry; 977 | int ret; 978 | 979 | if (!encoder) 980 | return -EINVAL; 981 | 982 | encoder->media_fd = -1; 983 | encoder->video_fd = -1; 984 | 985 | udev = udev_new(); 986 | if (!udev) 987 | goto error; 988 | 989 | enumerate = udev_enumerate_new(udev); 990 | if (!enumerate) 991 | goto error; 992 | 993 | udev_enumerate_add_match_subsystem(enumerate, "media"); 994 | udev_enumerate_scan_devices(enumerate); 995 | 996 | devices = udev_enumerate_get_list_entry(enumerate); 997 | 998 | udev_list_entry_foreach(entry, devices) { 999 | struct udev_device *device; 1000 | const char *path; 1001 | 1002 | path = udev_list_entry_get_name(entry); 1003 | if (!path) 1004 | continue; 1005 | 1006 | device = udev_device_new_from_syspath(udev, path); 1007 | if (!device) 1008 | continue; 1009 | 1010 | ret = media_device_probe(encoder, udev, device); 1011 | 1012 | udev_device_unref(device); 1013 | 1014 | if (!ret) 1015 | break; 1016 | } 1017 | 1018 | if (encoder->media_fd < 0) { 1019 | fprintf(stderr, "Failed to open encoder media device\n"); 1020 | goto error; 1021 | } 1022 | 1023 | if (encoder->video_fd < 0) { 1024 | fprintf(stderr, "Failed to open encoder video device\n"); 1025 | goto error; 1026 | } 1027 | 1028 | encoder->bitstream_fd = open("capture.h264", 1029 | O_WRONLY | O_CREAT | O_TRUNC, 0644); 1030 | if (encoder->bitstream_fd < 0) { 1031 | fprintf(stderr, "Failed to open bitstream file\n"); 1032 | ret = -errno; 1033 | goto error; 1034 | } 1035 | 1036 | ret = 0; 1037 | goto complete; 1038 | 1039 | error: 1040 | if (encoder->media_fd) { 1041 | close(encoder->media_fd); 1042 | encoder->media_fd = -1; 1043 | } 1044 | 1045 | if (encoder->video_fd) { 1046 | close(encoder->video_fd); 1047 | encoder->video_fd = -1; 1048 | } 1049 | 1050 | ret = -1; 1051 | 1052 | complete: 1053 | if (enumerate) 1054 | udev_enumerate_unref(enumerate); 1055 | 1056 | if (udev) 1057 | udev_unref(udev); 1058 | 1059 | return ret; 1060 | } 1061 | 1062 | void v4l2_encoder_close(struct v4l2_encoder *encoder) 1063 | { 1064 | if (!encoder) 1065 | return; 1066 | 1067 | if (encoder->bitstream_fd > 0) { 1068 | close(encoder->bitstream_fd); 1069 | encoder->bitstream_fd = -1; 1070 | } 1071 | 1072 | if (encoder->media_fd > 0) { 1073 | close(encoder->media_fd); 1074 | encoder->media_fd = -1; 1075 | } 1076 | 1077 | if (encoder->video_fd > 0) { 1078 | close(encoder->video_fd); 1079 | encoder->video_fd = -1; 1080 | } 1081 | } 1082 | -------------------------------------------------------------------------------- /v4l2-encoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2020 Paul Kocialkowski 3 | * Copyright (C) 2020 Bootlin 4 | */ 5 | 6 | #ifndef _V4L2_ENCODER_H_ 7 | #define _V4L2_ENCODER_H_ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | struct v4l2_encoder; 15 | 16 | struct v4l2_encoder_buffer { 17 | struct v4l2_encoder *encoder; 18 | 19 | struct v4l2_buffer buffer; 20 | 21 | struct v4l2_plane planes[4]; 22 | void *mmap_data[4]; 23 | unsigned int planes_count; 24 | 25 | int request_fd; 26 | }; 27 | 28 | struct v4l2_encoder_h264_src_controls { 29 | struct v4l2_ext_controls ext_controls; 30 | struct v4l2_ext_control controls[2]; 31 | unsigned int controls_count; 32 | 33 | struct v4l2_ctrl_h264_encode_params encode_params; 34 | struct v4l2_ext_control *encode_params_control; 35 | 36 | struct v4l2_ctrl_h264_encode_rc encode_rc; 37 | struct v4l2_ext_control *encode_rc_control; 38 | }; 39 | 40 | struct v4l2_encoder_h264_dst_controls { 41 | struct v4l2_ext_controls ext_controls; 42 | struct v4l2_ext_control controls[1]; 43 | unsigned int controls_count; 44 | 45 | struct v4l2_ext_control *encode_feedback_control; 46 | struct v4l2_ctrl_h264_encode_feedback encode_feedback; 47 | }; 48 | 49 | struct v4l2_encoder_setup { 50 | /* Dimensions */ 51 | unsigned int width; 52 | unsigned int width_mbs; 53 | unsigned int height; 54 | unsigned int height_mbs; 55 | 56 | /* Format */ 57 | uint32_t format; 58 | 59 | /* Framerate */ 60 | unsigned int fps_num; 61 | unsigned int fps_den; 62 | 63 | /* Bitrate */ 64 | uint64_t bitrate; 65 | unsigned int gop_size; 66 | 67 | /* Quality */ 68 | unsigned int qp_intra_delta; 69 | unsigned int qp_min; 70 | unsigned int qp_max; 71 | 72 | }; 73 | 74 | struct v4l2_encoder { 75 | int video_fd; 76 | int media_fd; 77 | 78 | char driver[32]; 79 | char card[32]; 80 | 81 | unsigned int capabilities; 82 | unsigned int memory; 83 | 84 | bool up; 85 | bool started; 86 | 87 | struct v4l2_encoder_setup setup; 88 | 89 | unsigned int output_type; 90 | unsigned int output_capabilities; 91 | struct v4l2_format output_format; 92 | struct v4l2_encoder_buffer output_buffers[3]; 93 | unsigned int output_buffers_count; 94 | unsigned int output_buffers_index; 95 | 96 | unsigned int capture_type; 97 | unsigned int capture_capabilities; 98 | struct v4l2_format capture_format; 99 | struct v4l2_encoder_buffer capture_buffers[3]; 100 | unsigned int capture_buffers_count; 101 | unsigned int capture_buffers_index; 102 | 103 | struct v4l2_encoder_h264_src_controls h264_src_controls; 104 | struct v4l2_encoder_h264_dst_controls h264_dst_controls; 105 | 106 | struct v4l2_ctrl_h264_sps sps; 107 | struct v4l2_ctrl_h264_pps pps; 108 | 109 | struct h264_rate_control rc; 110 | uint64_t reference_timestamp; 111 | unsigned int gop_index; 112 | 113 | struct draw_mandelbrot draw_mandelbrot; 114 | struct draw_buffer *draw_buffer; 115 | 116 | unsigned int x, y; 117 | bool pattern_drawn; 118 | bool direction; 119 | 120 | int bitstream_fd; 121 | }; 122 | 123 | int v4l2_encoder_prepare(struct v4l2_encoder *encoder); 124 | int v4l2_encoder_complete(struct v4l2_encoder *encoder); 125 | int v4l2_encoder_run(struct v4l2_encoder *encoder); 126 | int v4l2_encoder_start(struct v4l2_encoder *encoder); 127 | int v4l2_encoder_stop(struct v4l2_encoder *encoder); 128 | int v4l2_encoder_intra_request(struct v4l2_encoder *encoder); 129 | int v4l2_encoder_buffer_setup(struct v4l2_encoder_buffer *buffer, 130 | unsigned int type, unsigned int index); 131 | int v4l2_encoder_buffer_teardown(struct v4l2_encoder_buffer *buffer); 132 | int v4l2_encoder_setup_defaults(struct v4l2_encoder *encoder); 133 | int v4l2_encoder_setup_dimensions(struct v4l2_encoder *encoder, 134 | unsigned int width, unsigned int height); 135 | int v4l2_encoder_setup_format(struct v4l2_encoder *encoder, uint32_t format); 136 | int v4l2_encoder_setup_fps(struct v4l2_encoder *encoder, float fps); 137 | int v4l2_encoder_setup_bitrate(struct v4l2_encoder *encoder, uint64_t bitrate); 138 | int v4l2_encoder_setup(struct v4l2_encoder *encoder); 139 | int v4l2_encoder_teardown(struct v4l2_encoder *encoder); 140 | int v4l2_encoder_probe(struct v4l2_encoder *encoder); 141 | int v4l2_encoder_open(struct v4l2_encoder *encoder); 142 | void v4l2_encoder_close(struct v4l2_encoder *encoder); 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /v4l2-hantro-h264-encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Bootlin 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | int main(int argc, char *argv[]) 22 | { 23 | struct v4l2_encoder *encoder = NULL; 24 | unsigned int width = 640; 25 | unsigned int height = 480; 26 | unsigned int frames = 10; 27 | int ret; 28 | 29 | encoder = calloc(1, sizeof(*encoder)); 30 | if (!encoder) 31 | goto error; 32 | 33 | ret = v4l2_encoder_open(encoder); 34 | if (ret) 35 | goto error; 36 | 37 | ret = v4l2_encoder_probe(encoder); 38 | if (ret) 39 | goto error; 40 | 41 | ret = v4l2_encoder_setup_defaults(encoder); 42 | if (ret) 43 | goto error; 44 | 45 | ret = v4l2_encoder_setup_dimensions(encoder, width, height); 46 | if (ret) 47 | return ret; 48 | 49 | ret = v4l2_encoder_setup(encoder); 50 | if (ret) 51 | goto error; 52 | 53 | ret = v4l2_encoder_start(encoder); 54 | if (ret) 55 | goto error; 56 | 57 | while (frames--) { 58 | ret = v4l2_encoder_prepare(encoder); 59 | if (ret) 60 | goto error; 61 | 62 | ret = v4l2_encoder_run(encoder); 63 | if (ret) 64 | goto error; 65 | 66 | ret = v4l2_encoder_complete(encoder); 67 | if (ret) 68 | goto error; 69 | } 70 | 71 | ret = 0; 72 | goto complete; 73 | 74 | error: 75 | ret = 1; 76 | 77 | complete: 78 | if (encoder) { 79 | v4l2_encoder_stop(encoder); 80 | v4l2_encoder_teardown(encoder); 81 | v4l2_encoder_close(encoder); 82 | 83 | free(encoder); 84 | } 85 | 86 | return ret; 87 | } 88 | -------------------------------------------------------------------------------- /v4l2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2020 Paul Kocialkowski 3 | * Copyright (C) 2020 Bootlin 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | bool v4l2_type_mplane_check(unsigned int type) 23 | { 24 | switch (type) { 25 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: 26 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: 27 | return true; 28 | 29 | default: 30 | return false; 31 | } 32 | } 33 | 34 | bool v4l2_capabilities_check(unsigned int capabilities_probed, 35 | unsigned int capabilities) 36 | { 37 | unsigned int capabilities_mask = capabilities_probed & capabilities; 38 | 39 | return capabilities_mask == capabilities; 40 | } 41 | 42 | int v4l2_stream_set(int video_fd, unsigned int type, bool on) 43 | { 44 | int ret; 45 | 46 | ret = ioctl(video_fd, on ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type); 47 | if (ret) 48 | return -errno; 49 | 50 | return 0; 51 | } 52 | 53 | int v4l2_stream_on(int video_fd, unsigned int type) 54 | { 55 | return v4l2_stream_set(video_fd, type, true); 56 | } 57 | 58 | int v4l2_stream_off(int video_fd, unsigned int type) 59 | { 60 | return v4l2_stream_set(video_fd, type, false); 61 | } 62 | 63 | int v4l2_ext_controls_set(int video_fd, struct v4l2_ext_controls *ext_controls) 64 | { 65 | int ret; 66 | 67 | if (!ext_controls) 68 | return -EINVAL; 69 | 70 | ret = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, ext_controls); 71 | if (ret) 72 | return -errno; 73 | 74 | return 0; 75 | } 76 | 77 | int v4l2_ext_controls_get(int video_fd, struct v4l2_ext_controls *ext_controls) 78 | { 79 | int ret; 80 | 81 | if (!ext_controls) 82 | return -EINVAL; 83 | 84 | ret = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, ext_controls); 85 | if (ret) 86 | return -errno; 87 | 88 | return 0; 89 | } 90 | 91 | int v4l2_ext_controls_try(int video_fd, struct v4l2_ext_controls *ext_controls) 92 | { 93 | int ret; 94 | 95 | if (!ext_controls) 96 | return -EINVAL; 97 | 98 | ret = ioctl(video_fd, VIDIOC_TRY_EXT_CTRLS, ext_controls); 99 | if (ret) 100 | return -errno; 101 | 102 | return 0; 103 | } 104 | 105 | void v4l2_ext_controls_request_attach(struct v4l2_ext_controls *ext_controls, 106 | int request_fd) 107 | { 108 | if (!ext_controls) 109 | return; 110 | 111 | ext_controls->which = V4L2_CTRL_WHICH_REQUEST_VAL; 112 | ext_controls->request_fd = request_fd; 113 | } 114 | 115 | void v4l2_ext_controls_request_detach(struct v4l2_ext_controls *ext_controls) 116 | { 117 | if (!ext_controls) 118 | return; 119 | 120 | if (ext_controls->which == V4L2_CTRL_WHICH_REQUEST_VAL) 121 | ext_controls->which = 0; 122 | 123 | ext_controls->request_fd = -1; 124 | } 125 | 126 | void v4l2_ext_controls_setup(struct v4l2_ext_controls *ext_controls, 127 | struct v4l2_ext_control *controls, 128 | unsigned int controls_count) 129 | { 130 | if (!controls) 131 | return; 132 | 133 | ext_controls->controls = controls; 134 | ext_controls->count = controls_count; 135 | } 136 | 137 | void v4l2_ext_control_setup_compound(struct v4l2_ext_control *control, 138 | void *data, unsigned int size) 139 | { 140 | if (!control) 141 | return; 142 | 143 | control->ptr = data; 144 | control->size = size; 145 | } 146 | 147 | void v4l2_ext_control_setup_base(struct v4l2_ext_control *control, 148 | unsigned int id) 149 | { 150 | if (!control) 151 | return; 152 | 153 | memset(control, 0, sizeof(*control)); 154 | 155 | control->id = id; 156 | } 157 | 158 | int v4l2_buffers_create(int video_fd, unsigned int type, unsigned int memory, 159 | struct v4l2_format *format, unsigned int count, 160 | unsigned int *index) 161 | { 162 | struct v4l2_create_buffers create_buffers = { 0 }; 163 | int ret; 164 | 165 | if (format) { 166 | create_buffers.format = *format; 167 | } else { 168 | ret = ioctl(video_fd, VIDIOC_G_FMT, &create_buffers.format); 169 | if (ret) 170 | return -errno; 171 | } 172 | 173 | create_buffers.format.type = type; 174 | create_buffers.memory = memory; 175 | create_buffers.count = count; 176 | 177 | ret = ioctl(video_fd, VIDIOC_CREATE_BUFS, &create_buffers); 178 | if (ret) 179 | return -errno; 180 | 181 | if (index) 182 | *index = create_buffers.index; 183 | 184 | return 0; 185 | } 186 | 187 | int v4l2_buffers_request(int video_fd, unsigned int type, unsigned int memory, 188 | unsigned int count) 189 | { 190 | struct v4l2_requestbuffers requestbuffers = { 0 }; 191 | int ret; 192 | 193 | requestbuffers.type = type; 194 | requestbuffers.memory = memory; 195 | requestbuffers.count = count; 196 | 197 | ret = ioctl(video_fd, VIDIOC_REQBUFS, &requestbuffers); 198 | if (ret) 199 | return -errno; 200 | 201 | return 0; 202 | } 203 | 204 | 205 | int v4l2_buffers_destroy(int video_fd, unsigned int type, unsigned int memory) 206 | { 207 | struct v4l2_requestbuffers requestbuffers = { 0 }; 208 | int ret; 209 | 210 | requestbuffers.type = type; 211 | requestbuffers.memory = memory; 212 | requestbuffers.count = 0; 213 | 214 | ret = ioctl(video_fd, VIDIOC_REQBUFS, &requestbuffers); 215 | if (ret) 216 | return -errno; 217 | 218 | return 0; 219 | } 220 | 221 | int v4l2_buffers_capabilities_probe(int video_fd, unsigned int type, 222 | unsigned int *capabilities) 223 | { 224 | struct v4l2_create_buffers create_buffers = { 0 }; 225 | int ret; 226 | 227 | if (!capabilities) 228 | return -EINVAL; 229 | 230 | create_buffers.format.type = type; 231 | create_buffers.memory = V4L2_MEMORY_MMAP; 232 | create_buffers.count = 0; 233 | 234 | ret = ioctl(video_fd, VIDIOC_CREATE_BUFS, &create_buffers); 235 | if (ret) 236 | return -errno; 237 | 238 | if (create_buffers.capabilities) 239 | *capabilities = create_buffers.capabilities; 240 | else 241 | *capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP; 242 | 243 | return 0; 244 | } 245 | 246 | int v4l2_buffer_query(int video_fd, struct v4l2_buffer *buffer) 247 | { 248 | int ret; 249 | 250 | if (!buffer) 251 | return -EINVAL; 252 | 253 | ret = ioctl(video_fd, VIDIOC_QUERYBUF, buffer); 254 | if (ret) 255 | return -errno; 256 | 257 | return 0; 258 | } 259 | 260 | int v4l2_buffer_queue(int video_fd, struct v4l2_buffer *buffer) 261 | { 262 | int ret; 263 | 264 | if (!buffer) 265 | return -EINVAL; 266 | 267 | ret = ioctl(video_fd, VIDIOC_QBUF, buffer); 268 | if (ret) 269 | return -errno; 270 | 271 | return 0; 272 | } 273 | 274 | int v4l2_buffer_dequeue(int video_fd, struct v4l2_buffer *buffer) 275 | { 276 | int ret; 277 | 278 | if (!buffer) 279 | return -EINVAL; 280 | 281 | ret = ioctl(video_fd, VIDIOC_DQBUF, buffer); 282 | if (ret) 283 | return -errno; 284 | 285 | return 0; 286 | } 287 | 288 | int v4l2_buffer_plane_offset(struct v4l2_buffer *buffer, 289 | unsigned int plane_index, unsigned int *offset) 290 | { 291 | bool mplane_check; 292 | 293 | if (!buffer || !offset) 294 | return -EINVAL; 295 | 296 | mplane_check = v4l2_type_mplane_check(buffer->type); 297 | if (mplane_check) { 298 | if (!buffer->m.planes || plane_index >= buffer->length) 299 | return -EINVAL; 300 | 301 | *offset = buffer->m.planes[plane_index].m.mem_offset; 302 | } else { 303 | if (plane_index > 0) 304 | return -EINVAL; 305 | 306 | *offset = buffer->m.offset; 307 | } 308 | 309 | return 0; 310 | } 311 | 312 | int v4l2_buffer_plane_length(struct v4l2_buffer *buffer, 313 | unsigned int plane_index, unsigned int *length) 314 | { 315 | bool mplane_check; 316 | 317 | if (!buffer || !length) 318 | return -EINVAL; 319 | 320 | mplane_check = v4l2_type_mplane_check(buffer->type); 321 | if (mplane_check) { 322 | if (!buffer->m.planes || plane_index >= buffer->length) 323 | return -EINVAL; 324 | 325 | *length = buffer->m.planes[plane_index].length; 326 | } else { 327 | if (plane_index > 0) 328 | return -EINVAL; 329 | 330 | *length = buffer->length; 331 | } 332 | 333 | return 0; 334 | } 335 | 336 | bool v4l2_buffer_error_check(struct v4l2_buffer *buffer) 337 | { 338 | if (!buffer) 339 | return true; 340 | 341 | return buffer->flags & V4L2_BUF_FLAG_ERROR; 342 | } 343 | 344 | void v4l2_buffer_request_attach(struct v4l2_buffer *buffer, int request_fd) 345 | { 346 | if (!buffer) 347 | return; 348 | 349 | buffer->flags |= V4L2_BUF_FLAG_REQUEST_FD; 350 | buffer->request_fd = request_fd; 351 | } 352 | 353 | void v4l2_buffer_request_detach(struct v4l2_buffer *buffer) 354 | { 355 | if (!buffer) 356 | return; 357 | 358 | buffer->flags &= ~V4L2_BUF_FLAG_REQUEST_FD; 359 | buffer->request_fd = -1; 360 | } 361 | 362 | void v4l2_buffer_timestamp_set(struct v4l2_buffer *buffer, uint64_t timestamp) 363 | { 364 | if (!buffer) 365 | return; 366 | 367 | buffer->timestamp.tv_sec = timestamp / 1000000000ULL; 368 | buffer->timestamp.tv_usec = (timestamp % 1000000000ULL) / 1000; 369 | } 370 | 371 | void v4l2_buffer_timestamp_get(struct v4l2_buffer *buffer, uint64_t *timestamp) 372 | { 373 | if (!buffer || !timestamp) 374 | return; 375 | 376 | *timestamp = v4l2_timeval_to_ns(&buffer->timestamp); 377 | } 378 | 379 | void v4l2_buffer_setup_base(struct v4l2_buffer *buffer, unsigned int type, 380 | unsigned int memory, unsigned int index, 381 | struct v4l2_plane *planes, 382 | unsigned int planes_count) 383 | { 384 | bool mplane_check; 385 | 386 | if (!buffer) 387 | return; 388 | 389 | memset(buffer, 0, sizeof(*buffer)); 390 | 391 | buffer->type = type; 392 | buffer->memory = memory; 393 | buffer->index = index; 394 | 395 | mplane_check = v4l2_type_mplane_check(type); 396 | if (mplane_check && planes) { 397 | buffer->m.planes = planes; 398 | buffer->length = planes_count; 399 | } 400 | } 401 | 402 | int v4l2_pixel_format_enum(int video_fd, unsigned int type, unsigned int index, 403 | unsigned int *pixel_format, char *description) 404 | { 405 | struct v4l2_fmtdesc fmtdesc = { 0 }; 406 | int ret; 407 | 408 | if (!pixel_format) 409 | return -EINVAL; 410 | 411 | fmtdesc.type = type; 412 | fmtdesc.index = index; 413 | 414 | ret = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc); 415 | if (ret) 416 | return -errno; 417 | 418 | *pixel_format = fmtdesc.pixelformat; 419 | 420 | if (description) 421 | strncpy(description, fmtdesc.description, 422 | sizeof(fmtdesc.description)); 423 | 424 | return 0; 425 | } 426 | 427 | bool v4l2_pixel_format_check(int video_fd, unsigned int type, 428 | unsigned int pixel_format) 429 | { 430 | unsigned int index = 0; 431 | unsigned int ret; 432 | 433 | do { 434 | unsigned int pixel_format_enum; 435 | 436 | ret = v4l2_pixel_format_enum(video_fd, type, index, 437 | &pixel_format_enum, NULL); 438 | if (ret) 439 | break; 440 | 441 | if (pixel_format_enum == pixel_format) 442 | return true; 443 | 444 | index++; 445 | } while (ret >= 0); 446 | 447 | return false; 448 | } 449 | 450 | int v4l2_format_try(int video_fd, struct v4l2_format *format) 451 | { 452 | int ret; 453 | 454 | if (!format) 455 | return -EINVAL; 456 | 457 | ret = ioctl(video_fd, VIDIOC_TRY_FMT, format); 458 | if (ret) 459 | return -errno; 460 | 461 | return 0; 462 | } 463 | 464 | int v4l2_format_set(int video_fd, struct v4l2_format *format) 465 | { 466 | int ret; 467 | 468 | if (!format) 469 | return -EINVAL; 470 | 471 | ret = ioctl(video_fd, VIDIOC_S_FMT, format); 472 | if (ret) 473 | return -errno; 474 | 475 | return 0; 476 | } 477 | 478 | int v4l2_format_get(int video_fd, struct v4l2_format *format) 479 | { 480 | int ret; 481 | 482 | if (!format) 483 | return -EINVAL; 484 | 485 | ret = ioctl(video_fd, VIDIOC_G_FMT, format); 486 | if (ret) 487 | return -errno; 488 | 489 | return 0; 490 | } 491 | 492 | void v4l2_format_setup_pixel(struct v4l2_format *format, unsigned int type, 493 | unsigned int width, unsigned int height, 494 | unsigned int pixel_format) 495 | { 496 | bool mplane_check; 497 | 498 | if (!format) 499 | return; 500 | 501 | memset(format, 0, sizeof(*format)); 502 | 503 | format->type = type; 504 | 505 | mplane_check = v4l2_type_mplane_check(type); 506 | if (mplane_check) { 507 | format->fmt.pix_mp.width = width; 508 | format->fmt.pix_mp.height = height; 509 | format->fmt.pix_mp.pixelformat = pixel_format; 510 | } else { 511 | format->fmt.pix.width = width; 512 | format->fmt.pix.height = height; 513 | format->fmt.pix.pixelformat = pixel_format; 514 | } 515 | } 516 | 517 | int v4l2_capabilities_probe(int video_fd, unsigned int *capabilities, 518 | char *driver, char *card) 519 | { 520 | struct v4l2_capability capability = { 0 }; 521 | int ret; 522 | 523 | if (!capabilities) 524 | return -EINVAL; 525 | 526 | ret = ioctl(video_fd, VIDIOC_QUERYCAP, &capability); 527 | if (ret < 0) 528 | return -errno; 529 | 530 | if (capability.capabilities & V4L2_CAP_DEVICE_CAPS) 531 | *capabilities = capability.device_caps; 532 | else 533 | *capabilities = capability.capabilities; 534 | 535 | if (driver) 536 | strncpy(driver, capability.driver, sizeof(capability.driver)); 537 | 538 | if (card) 539 | strncpy(card, capability.card, sizeof(capability.card)); 540 | 541 | return 0; 542 | } 543 | -------------------------------------------------------------------------------- /v4l2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2020 Paul Kocialkowski 3 | * Copyright (C) 2020 Bootlin 4 | */ 5 | 6 | #ifndef _V4L2_H_ 7 | #define _V4L2_H_ 8 | 9 | #include 10 | 11 | #include 12 | 13 | bool v4l2_type_mplane_check(unsigned int type); 14 | bool v4l2_capabilities_check(unsigned int capabilities_probed, 15 | unsigned int capabilities); 16 | 17 | int v4l2_stream_set(int video_fd, unsigned int type, bool on); 18 | int v4l2_stream_on(int video_fd, unsigned int type); 19 | int v4l2_stream_off(int video_fd, unsigned int type); 20 | 21 | int v4l2_ext_controls_set(int video_fd, struct v4l2_ext_controls *ext_controls); 22 | int v4l2_ext_controls_get(int video_fd, struct v4l2_ext_controls *ext_controls); 23 | int v4l2_ext_controls_try(int video_fd, struct v4l2_ext_controls *ext_controls); 24 | void v4l2_ext_controls_request_attach(struct v4l2_ext_controls *ext_controls, 25 | int request_fd); 26 | void v4l2_ext_controls_request_detach(struct v4l2_ext_controls *ext_controls); 27 | void v4l2_ext_control_setup_compound(struct v4l2_ext_control *control, 28 | void *data, unsigned int size); 29 | void v4l2_ext_control_setup_base(struct v4l2_ext_control *control, 30 | unsigned int id); 31 | void v4l2_ext_controls_setup(struct v4l2_ext_controls *ext_controls, 32 | struct v4l2_ext_control *controls, 33 | unsigned int controls_count); 34 | 35 | int v4l2_buffers_create(int video_fd, unsigned int type, unsigned int memory, 36 | struct v4l2_format *format, unsigned int count, 37 | unsigned int *index); 38 | int v4l2_buffers_request(int video_fd, unsigned int type, unsigned int memory, 39 | unsigned int count); 40 | int v4l2_buffers_destroy(int video_fd, unsigned int type, unsigned int memory); 41 | int v4l2_buffers_capabilities_probe(int video_fd, unsigned int type, 42 | unsigned int *capabilities); 43 | 44 | int v4l2_buffer_query(int video_fd, struct v4l2_buffer *buffer); 45 | int v4l2_buffer_queue(int video_fd, struct v4l2_buffer *buffer); 46 | int v4l2_buffer_dequeue(int video_fd, struct v4l2_buffer *buffer); 47 | bool v4l2_buffer_error_check(struct v4l2_buffer *buffer); 48 | int v4l2_buffer_plane_offset(struct v4l2_buffer *buffer, 49 | unsigned int plane_index, unsigned int *offset); 50 | int v4l2_buffer_plane_length(struct v4l2_buffer *buffer, 51 | unsigned int plane_index, unsigned int *length); 52 | void v4l2_buffer_request_attach(struct v4l2_buffer *buffer, int request_fd); 53 | void v4l2_buffer_request_detach(struct v4l2_buffer *buffer); 54 | void v4l2_buffer_timestamp_set(struct v4l2_buffer *buffer, uint64_t timestamp); 55 | void v4l2_buffer_timestamp_get(struct v4l2_buffer *buffer, uint64_t *timestamp); 56 | void v4l2_buffer_setup_base(struct v4l2_buffer *buffer, unsigned int type, 57 | unsigned int memory, unsigned int index, 58 | struct v4l2_plane *planes, 59 | unsigned int planes_count); 60 | 61 | int v4l2_pixel_format_enum(int video_fd, unsigned int type, unsigned int index, 62 | unsigned int *pixel_format, char *description); 63 | bool v4l2_pixel_format_check(int video_fd, unsigned int type, 64 | unsigned int pixel_format); 65 | 66 | int v4l2_format_try(int video_fd, struct v4l2_format *format); 67 | int v4l2_format_set(int video_fd, struct v4l2_format *format); 68 | int v4l2_format_get(int video_fd, struct v4l2_format *format); 69 | void v4l2_format_setup_pixel(struct v4l2_format *format, unsigned int type, 70 | unsigned int width, unsigned int height, 71 | unsigned int pixel_format); 72 | 73 | int v4l2_capabilities_probe(int video_fd, unsigned int *capabilities, 74 | char *driver, char *card); 75 | 76 | #endif 77 | --------------------------------------------------------------------------------