linux/drivers/media/pci/tw5864/tw5864-h264.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  TW5864 driver - H.264 headers generation functions
   4 *
   5 *  Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com>
   6 */
   7
   8#include <linux/log2.h>
   9
  10#include "tw5864.h"
  11
  12static u8 marker[] = { 0x00, 0x00, 0x00, 0x01 };
  13
  14/*
  15 * Exponential-Golomb coding functions
  16 *
  17 * These functions are used for generation of H.264 bitstream headers.
  18 *
  19 * This code is derived from tw5864 reference driver by manufacturers, which
  20 * itself apparently was derived from x264 project.
  21 */
  22
  23/* Bitstream writing context */
  24struct bs {
  25        u8 *buf; /* pointer to buffer beginning */
  26        u8 *buf_end; /* pointer to buffer end */
  27        u8 *ptr; /* pointer to current byte in buffer */
  28        unsigned int bits_left; /* number of available bits in current byte */
  29};
  30
  31static void bs_init(struct bs *s, void *buf, int size)
  32{
  33        s->buf = buf;
  34        s->ptr = buf;
  35        s->buf_end = s->ptr + size;
  36        s->bits_left = 8;
  37}
  38
  39static int bs_len(struct bs *s)
  40{
  41        return s->ptr - s->buf;
  42}
  43
  44static void bs_write(struct bs *s, int count, u32 bits)
  45{
  46        if (s->ptr >= s->buf_end - 4)
  47                return;
  48        while (count > 0) {
  49                if (count < 32)
  50                        bits &= (1 << count) - 1;
  51                if (count < s->bits_left) {
  52                        *s->ptr = (*s->ptr << count) | bits;
  53                        s->bits_left -= count;
  54                        break;
  55                }
  56                *s->ptr = (*s->ptr << s->bits_left) |
  57                        (bits >> (count - s->bits_left));
  58                count -= s->bits_left;
  59                s->ptr++;
  60                s->bits_left = 8;
  61        }
  62}
  63
  64static void bs_write1(struct bs *s, u32 bit)
  65{
  66        if (s->ptr < s->buf_end) {
  67                *s->ptr <<= 1;
  68                *s->ptr |= bit;
  69                s->bits_left--;
  70                if (s->bits_left == 0) {
  71                        s->ptr++;
  72                        s->bits_left = 8;
  73                }
  74        }
  75}
  76
  77static void bs_write_ue(struct bs *s, u32 val)
  78{
  79        if (val == 0) {
  80                bs_write1(s, 1);
  81        } else {
  82                val++;
  83                bs_write(s, 2 * fls(val) - 1, val);
  84        }
  85}
  86
  87static void bs_write_se(struct bs *s, int val)
  88{
  89        bs_write_ue(s, val <= 0 ? -val * 2 : val * 2 - 1);
  90}
  91
  92static void bs_rbsp_trailing(struct bs *s)
  93{
  94        bs_write1(s, 1);
  95        if (s->bits_left != 8)
  96                bs_write(s, s->bits_left, 0x00);
  97}
  98
  99/* H.264 headers generation functions */
 100
 101static int tw5864_h264_gen_sps_rbsp(u8 *buf, size_t size, int width, int height)
 102{
 103        struct bs bs, *s;
 104
 105        s = &bs;
 106        bs_init(s, buf, size);
 107        bs_write(s, 8, 0x42); /* profile_idc, baseline */
 108        bs_write(s, 1, 1); /* constraint_set0_flag */
 109        bs_write(s, 1, 1); /* constraint_set1_flag */
 110        bs_write(s, 1, 0); /* constraint_set2_flag */
 111        bs_write(s, 5, 0); /* reserved_zero_5bits */
 112        bs_write(s, 8, 0x1e); /* level_idc */
 113        bs_write_ue(s, 0); /* seq_parameter_set_id */
 114        bs_write_ue(s, ilog2(MAX_GOP_SIZE) - 4); /* log2_max_frame_num_minus4 */
 115        bs_write_ue(s, 0); /* pic_order_cnt_type */
 116        /* log2_max_pic_order_cnt_lsb_minus4 */
 117        bs_write_ue(s, ilog2(MAX_GOP_SIZE) - 4);
 118        bs_write_ue(s, 1); /* num_ref_frames */
 119        bs_write(s, 1, 0); /* gaps_in_frame_num_value_allowed_flag */
 120        bs_write_ue(s, width / 16 - 1); /* pic_width_in_mbs_minus1 */
 121        bs_write_ue(s, height / 16 - 1); /* pic_height_in_map_units_minus1 */
 122        bs_write(s, 1, 1); /* frame_mbs_only_flag */
 123        bs_write(s, 1, 0); /* direct_8x8_inference_flag */
 124        bs_write(s, 1, 0); /* frame_cropping_flag */
 125        bs_write(s, 1, 0); /* vui_parameters_present_flag */
 126        bs_rbsp_trailing(s);
 127        return bs_len(s);
 128}
 129
 130static int tw5864_h264_gen_pps_rbsp(u8 *buf, size_t size, int qp)
 131{
 132        struct bs bs, *s;
 133
 134        s = &bs;
 135        bs_init(s, buf, size);
 136        bs_write_ue(s, 0); /* pic_parameter_set_id */
 137        bs_write_ue(s, 0); /* seq_parameter_set_id */
 138        bs_write(s, 1, 0); /* entropy_coding_mode_flag */
 139        bs_write(s, 1, 0); /* pic_order_present_flag */
 140        bs_write_ue(s, 0); /* num_slice_groups_minus1 */
 141        bs_write_ue(s, 0); /* i_num_ref_idx_l0_active_minus1 */
 142        bs_write_ue(s, 0); /* i_num_ref_idx_l1_active_minus1 */
 143        bs_write(s, 1, 0); /* weighted_pred_flag */
 144        bs_write(s, 2, 0); /* weighted_bipred_idc */
 145        bs_write_se(s, qp - 26); /* pic_init_qp_minus26 */
 146        bs_write_se(s, qp - 26); /* pic_init_qs_minus26 */
 147        bs_write_se(s, 0); /* chroma_qp_index_offset */
 148        bs_write(s, 1, 0); /* deblocking_filter_control_present_flag */
 149        bs_write(s, 1, 0); /* constrained_intra_pred_flag */
 150        bs_write(s, 1, 0); /* redundant_pic_cnt_present_flag */
 151        bs_rbsp_trailing(s);
 152        return bs_len(s);
 153}
 154
 155static int tw5864_h264_gen_slice_head(u8 *buf, size_t size,
 156                                      unsigned int idr_pic_id,
 157                                      unsigned int frame_gop_seqno,
 158                                      int *tail_nb_bits, u8 *tail)
 159{
 160        struct bs bs, *s;
 161        int is_i_frame = frame_gop_seqno == 0;
 162
 163        s = &bs;
 164        bs_init(s, buf, size);
 165        bs_write_ue(s, 0); /* first_mb_in_slice */
 166        bs_write_ue(s, is_i_frame ? 2 : 5); /* slice_type - I or P */
 167        bs_write_ue(s, 0); /* pic_parameter_set_id */
 168        bs_write(s, ilog2(MAX_GOP_SIZE), frame_gop_seqno); /* frame_num */
 169        if (is_i_frame)
 170                bs_write_ue(s, idr_pic_id);
 171
 172        /* pic_order_cnt_lsb */
 173        bs_write(s, ilog2(MAX_GOP_SIZE), frame_gop_seqno);
 174
 175        if (is_i_frame) {
 176                bs_write1(s, 0); /* no_output_of_prior_pics_flag */
 177                bs_write1(s, 0); /* long_term_reference_flag */
 178        } else {
 179                bs_write1(s, 0); /* num_ref_idx_active_override_flag */
 180                bs_write1(s, 0); /* ref_pic_list_reordering_flag_l0 */
 181                bs_write1(s, 0); /* adaptive_ref_pic_marking_mode_flag */
 182        }
 183
 184        bs_write_se(s, 0); /* slice_qp_delta */
 185
 186        if (s->bits_left != 8) {
 187                *tail = ((s->ptr[0]) << s->bits_left);
 188                *tail_nb_bits = 8 - s->bits_left;
 189        } else {
 190                *tail = 0;
 191                *tail_nb_bits = 0;
 192        }
 193
 194        return bs_len(s);
 195}
 196
 197void tw5864_h264_put_stream_header(u8 **buf, size_t *space_left, int qp,
 198                                   int width, int height)
 199{
 200        int nal_len;
 201
 202        /* SPS */
 203        memcpy(*buf, marker, sizeof(marker));
 204        *buf += 4;
 205        *space_left -= 4;
 206
 207        **buf = 0x67; /* SPS NAL header */
 208        *buf += 1;
 209        *space_left -= 1;
 210
 211        nal_len = tw5864_h264_gen_sps_rbsp(*buf, *space_left, width, height);
 212        *buf += nal_len;
 213        *space_left -= nal_len;
 214
 215        /* PPS */
 216        memcpy(*buf, marker, sizeof(marker));
 217        *buf += 4;
 218        *space_left -= 4;
 219
 220        **buf = 0x68; /* PPS NAL header */
 221        *buf += 1;
 222        *space_left -= 1;
 223
 224        nal_len = tw5864_h264_gen_pps_rbsp(*buf, *space_left, qp);
 225        *buf += nal_len;
 226        *space_left -= nal_len;
 227}
 228
 229void tw5864_h264_put_slice_header(u8 **buf, size_t *space_left,
 230                                  unsigned int idr_pic_id,
 231                                  unsigned int frame_gop_seqno,
 232                                  int *tail_nb_bits, u8 *tail)
 233{
 234        int nal_len;
 235
 236        memcpy(*buf, marker, sizeof(marker));
 237        *buf += 4;
 238        *space_left -= 4;
 239
 240        /* Frame NAL header */
 241        **buf = (frame_gop_seqno == 0) ? 0x25 : 0x21;
 242        *buf += 1;
 243        *space_left -= 1;
 244
 245        nal_len = tw5864_h264_gen_slice_head(*buf, *space_left, idr_pic_id,
 246                                             frame_gop_seqno, tail_nb_bits,
 247                                             tail);
 248        *buf += nal_len;
 249        *space_left -= nal_len;
 250}
 251