| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * MOV CENC (Common Encryption) writer | ||
| 3 | * Copyright (c) 2015 Eran Kornblau <erankor at gmail dot com> | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | #include "movenccenc.h" | ||
| 22 | #include "libavcodec/av1_parse.h" | ||
| 23 | #include "libavcodec/bytestream.h" | ||
| 24 | #include "libavcodec/cbs_av1.h" | ||
| 25 | #include "libavutil/attributes.h" | ||
| 26 | #include "libavutil/intreadwrite.h" | ||
| 27 | #include "libavutil/mem.h" | ||
| 28 | #include "avio_internal.h" | ||
| 29 | #include "movenc.h" | ||
| 30 | #include "avc.h" | ||
| 31 | #include "nal.h" | ||
| 32 | |||
| 33 | ✗ | static int auxiliary_info_alloc_size(MOVMuxCencContext* ctx, int size) | |
| 34 | { | ||
| 35 | size_t new_alloc_size; | ||
| 36 | |||
| 37 | ✗ | if (ctx->auxiliary_info_size + size > ctx->auxiliary_info_alloc_size) { | |
| 38 | ✗ | new_alloc_size = FFMAX(ctx->auxiliary_info_size + size, ctx->auxiliary_info_alloc_size * 2); | |
| 39 | ✗ | if (av_reallocp(&ctx->auxiliary_info, new_alloc_size)) { | |
| 40 | ✗ | return AVERROR(ENOMEM); | |
| 41 | } | ||
| 42 | |||
| 43 | ✗ | ctx->auxiliary_info_alloc_size = new_alloc_size; | |
| 44 | } | ||
| 45 | |||
| 46 | ✗ | return 0; | |
| 47 | } | ||
| 48 | |||
| 49 | ✗ | static int auxiliary_info_write(MOVMuxCencContext* ctx, | |
| 50 | const uint8_t *buf_in, int size) | ||
| 51 | { | ||
| 52 | int ret; | ||
| 53 | |||
| 54 | ✗ | ret = auxiliary_info_alloc_size(ctx, size); | |
| 55 | ✗ | if (ret) { | |
| 56 | ✗ | return ret; | |
| 57 | } | ||
| 58 | ✗ | memcpy(ctx->auxiliary_info + ctx->auxiliary_info_size, buf_in, size); | |
| 59 | ✗ | ctx->auxiliary_info_size += size; | |
| 60 | |||
| 61 | ✗ | return 0; | |
| 62 | } | ||
| 63 | |||
| 64 | ✗ | static int auxiliary_info_add_subsample(MOVMuxCencContext* ctx, | |
| 65 | uint16_t clear_bytes, uint32_t encrypted_bytes) | ||
| 66 | { | ||
| 67 | uint8_t* p; | ||
| 68 | int ret; | ||
| 69 | |||
| 70 | ✗ | if (!ctx->use_subsamples) { | |
| 71 | ✗ | return 0; | |
| 72 | } | ||
| 73 | |||
| 74 | ✗ | ret = auxiliary_info_alloc_size(ctx, 6); | |
| 75 | ✗ | if (ret) { | |
| 76 | ✗ | return ret; | |
| 77 | } | ||
| 78 | |||
| 79 | ✗ | p = ctx->auxiliary_info + ctx->auxiliary_info_size; | |
| 80 | |||
| 81 | ✗ | AV_WB16(p, clear_bytes); | |
| 82 | ✗ | p += sizeof(uint16_t); | |
| 83 | |||
| 84 | ✗ | AV_WB32(p, encrypted_bytes); | |
| 85 | |||
| 86 | ✗ | ctx->auxiliary_info_size += 6; | |
| 87 | ✗ | ctx->subsample_count++; | |
| 88 | |||
| 89 | ✗ | return 0; | |
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Encrypt the input buffer and write using avio_write | ||
| 94 | */ | ||
| 95 | ✗ | static void mov_cenc_write_encrypted(MOVMuxCencContext* ctx, AVIOContext *pb, | |
| 96 | const uint8_t *buf_in, int size) | ||
| 97 | { | ||
| 98 | uint8_t chunk[4096]; | ||
| 99 | ✗ | const uint8_t* cur_pos = buf_in; | |
| 100 | ✗ | int size_left = size; | |
| 101 | int cur_size; | ||
| 102 | |||
| 103 | ✗ | while (size_left > 0) { | |
| 104 | ✗ | cur_size = FFMIN(size_left, sizeof(chunk)); | |
| 105 | ✗ | av_aes_ctr_crypt(ctx->aes_ctr, chunk, cur_pos, cur_size); | |
| 106 | ✗ | avio_write(pb, chunk, cur_size); | |
| 107 | ✗ | cur_pos += cur_size; | |
| 108 | ✗ | size_left -= cur_size; | |
| 109 | } | ||
| 110 | ✗ | } | |
| 111 | |||
| 112 | /** | ||
| 113 | * Start writing a packet | ||
| 114 | */ | ||
| 115 | ✗ | static int mov_cenc_start_packet(MOVMuxCencContext* ctx) | |
| 116 | { | ||
| 117 | int ret; | ||
| 118 | |||
| 119 | /* write the iv */ | ||
| 120 | ✗ | ret = auxiliary_info_write(ctx, av_aes_ctr_get_iv(ctx->aes_ctr), AES_CTR_IV_SIZE); | |
| 121 | ✗ | if (ret) { | |
| 122 | ✗ | return ret; | |
| 123 | } | ||
| 124 | |||
| 125 | ✗ | if (!ctx->use_subsamples) { | |
| 126 | ✗ | return 0; | |
| 127 | } | ||
| 128 | |||
| 129 | /* write a zero subsample count */ | ||
| 130 | ✗ | ctx->auxiliary_info_subsample_start = ctx->auxiliary_info_size; | |
| 131 | ✗ | ctx->subsample_count = 0; | |
| 132 | ✗ | ret = auxiliary_info_write(ctx, (uint8_t*)&ctx->subsample_count, sizeof(ctx->subsample_count)); | |
| 133 | ✗ | if (ret) { | |
| 134 | ✗ | return ret; | |
| 135 | } | ||
| 136 | |||
| 137 | ✗ | return 0; | |
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Finalize a packet | ||
| 142 | */ | ||
| 143 | ✗ | static int mov_cenc_end_packet(MOVMuxCencContext* ctx) | |
| 144 | { | ||
| 145 | size_t new_alloc_size; | ||
| 146 | |||
| 147 | ✗ | av_aes_ctr_increment_iv(ctx->aes_ctr); | |
| 148 | |||
| 149 | ✗ | if (!ctx->use_subsamples) { | |
| 150 | ✗ | ctx->auxiliary_info_entries++; | |
| 151 | ✗ | return 0; | |
| 152 | } | ||
| 153 | |||
| 154 | /* add the auxiliary info entry size*/ | ||
| 155 | ✗ | if (ctx->auxiliary_info_entries >= ctx->auxiliary_info_sizes_alloc_size) { | |
| 156 | ✗ | new_alloc_size = ctx->auxiliary_info_entries * 2 + 1; | |
| 157 | ✗ | if (av_reallocp(&ctx->auxiliary_info_sizes, new_alloc_size)) { | |
| 158 | ✗ | return AVERROR(ENOMEM); | |
| 159 | } | ||
| 160 | |||
| 161 | ✗ | ctx->auxiliary_info_sizes_alloc_size = new_alloc_size; | |
| 162 | } | ||
| 163 | ✗ | ctx->auxiliary_info_sizes[ctx->auxiliary_info_entries] = | |
| 164 | ✗ | AES_CTR_IV_SIZE + ctx->auxiliary_info_size - ctx->auxiliary_info_subsample_start; | |
| 165 | ✗ | ctx->auxiliary_info_entries++; | |
| 166 | |||
| 167 | /* update the subsample count*/ | ||
| 168 | ✗ | AV_WB16(ctx->auxiliary_info + ctx->auxiliary_info_subsample_start, ctx->subsample_count); | |
| 169 | |||
| 170 | ✗ | return 0; | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | int ff_mov_cenc_write_packet(MOVMuxCencContext* ctx, AVIOContext *pb, | |
| 174 | const uint8_t *buf_in, int size) | ||
| 175 | { | ||
| 176 | int ret; | ||
| 177 | |||
| 178 | ✗ | ret = mov_cenc_start_packet(ctx); | |
| 179 | ✗ | if (ret) { | |
| 180 | ✗ | return ret; | |
| 181 | } | ||
| 182 | |||
| 183 | ✗ | ret = auxiliary_info_add_subsample(ctx, 0, size); | |
| 184 | ✗ | if (ret) { | |
| 185 | ✗ | return ret; | |
| 186 | } | ||
| 187 | |||
| 188 | ✗ | mov_cenc_write_encrypted(ctx, pb, buf_in, size); | |
| 189 | |||
| 190 | ✗ | ret = mov_cenc_end_packet(ctx); | |
| 191 | ✗ | if (ret) { | |
| 192 | ✗ | return ret; | |
| 193 | } | ||
| 194 | |||
| 195 | ✗ | return 0; | |
| 196 | } | ||
| 197 | |||
| 198 | ✗ | int ff_mov_cenc_avc_parse_nal_units(MOVMuxCencContext* ctx, AVIOContext *pb, | |
| 199 | const uint8_t *buf_in, int size) | ||
| 200 | { | ||
| 201 | ✗ | const uint8_t *p = buf_in; | |
| 202 | ✗ | const uint8_t *end = p + size; | |
| 203 | const uint8_t *nal_start, *nal_end; | ||
| 204 | int ret; | ||
| 205 | |||
| 206 | ✗ | ret = mov_cenc_start_packet(ctx); | |
| 207 | ✗ | if (ret) { | |
| 208 | ✗ | return ret; | |
| 209 | } | ||
| 210 | |||
| 211 | ✗ | size = 0; | |
| 212 | ✗ | nal_start = ff_nal_find_startcode(p, end); | |
| 213 | for (;;) { | ||
| 214 | ✗ | while (nal_start < end && !*(nal_start++)); | |
| 215 | ✗ | if (nal_start == end) | |
| 216 | ✗ | break; | |
| 217 | |||
| 218 | ✗ | nal_end = ff_nal_find_startcode(nal_start, end); | |
| 219 | |||
| 220 | ✗ | avio_wb32(pb, nal_end - nal_start); | |
| 221 | ✗ | avio_w8(pb, *nal_start); | |
| 222 | ✗ | mov_cenc_write_encrypted(ctx, pb, nal_start + 1, nal_end - nal_start - 1); | |
| 223 | |||
| 224 | ✗ | auxiliary_info_add_subsample(ctx, 5, nal_end - nal_start - 1); | |
| 225 | |||
| 226 | ✗ | size += 4 + nal_end - nal_start; | |
| 227 | ✗ | nal_start = nal_end; | |
| 228 | } | ||
| 229 | |||
| 230 | ✗ | ret = mov_cenc_end_packet(ctx); | |
| 231 | ✗ | if (ret) { | |
| 232 | ✗ | return ret; | |
| 233 | } | ||
| 234 | |||
| 235 | ✗ | return size; | |
| 236 | } | ||
| 237 | |||
| 238 | ✗ | int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext* ctx, | |
| 239 | int nal_length_size, AVIOContext *pb, const uint8_t *buf_in, int size) | ||
| 240 | { | ||
| 241 | int nalsize; | ||
| 242 | int ret; | ||
| 243 | int j; | ||
| 244 | |||
| 245 | ✗ | ret = mov_cenc_start_packet(ctx); | |
| 246 | ✗ | if (ret) { | |
| 247 | ✗ | return ret; | |
| 248 | } | ||
| 249 | |||
| 250 | ✗ | while (size > 0) { | |
| 251 | /* parse the nal size */ | ||
| 252 | ✗ | if (size < nal_length_size + 1) { | |
| 253 | ✗ | av_log(s, AV_LOG_ERROR, "CENC-AVC: remaining size %d smaller than nal length+type %d\n", | |
| 254 | size, nal_length_size + 1); | ||
| 255 | ✗ | return -1; | |
| 256 | } | ||
| 257 | |||
| 258 | ✗ | avio_write(pb, buf_in, nal_length_size + 1); | |
| 259 | |||
| 260 | ✗ | nalsize = 0; | |
| 261 | ✗ | for (j = 0; j < nal_length_size; j++) { | |
| 262 | ✗ | nalsize = (nalsize << 8) | *buf_in++; | |
| 263 | } | ||
| 264 | ✗ | size -= nal_length_size; | |
| 265 | |||
| 266 | /* encrypt the nal body */ | ||
| 267 | ✗ | if (nalsize <= 0 || nalsize > size) { | |
| 268 | ✗ | av_log(s, AV_LOG_ERROR, "CENC-AVC: nal size %d remaining %d\n", nalsize, size); | |
| 269 | ✗ | return -1; | |
| 270 | } | ||
| 271 | |||
| 272 | ✗ | mov_cenc_write_encrypted(ctx, pb, buf_in + 1, nalsize - 1); | |
| 273 | ✗ | buf_in += nalsize; | |
| 274 | ✗ | size -= nalsize; | |
| 275 | |||
| 276 | ✗ | auxiliary_info_add_subsample(ctx, nal_length_size + 1, nalsize - 1); | |
| 277 | } | ||
| 278 | |||
| 279 | ✗ | ret = mov_cenc_end_packet(ctx); | |
| 280 | ✗ | if (ret) { | |
| 281 | ✗ | return ret; | |
| 282 | } | ||
| 283 | |||
| 284 | ✗ | return 0; | |
| 285 | } | ||
| 286 | |||
| 287 | ✗ | static int write_tiles(AVFormatContext *s, MOVMuxCencContext *ctx, AVIOContext *pb, AV1_OBU_Type type, | |
| 288 | const AV1RawFrameHeader *frame_header, const uint8_t *fh_data, size_t fh_data_size, | ||
| 289 | const AV1RawTileGroup *tile_group) | ||
| 290 | { | ||
| 291 | GetByteContext gb; | ||
| 292 | ✗ | size_t tgh_data_size = tile_group->data_size; | |
| 293 | ✗ | int cur_tile_num = frame_header->tile_cols * frame_header->tile_rows; | |
| 294 | ✗ | int total = 0; | |
| 295 | |||
| 296 | // Get the Frame Header size | ||
| 297 | ✗ | if (type == AV1_OBU_FRAME) | |
| 298 | ✗ | fh_data_size -= tgh_data_size; | |
| 299 | // Get the Tile Group Header size | ||
| 300 | ✗ | tgh_data_size -= tile_group->tile_data.data_size; | |
| 301 | |||
| 302 | ✗ | if (ctx->tile_num < cur_tile_num) { | |
| 303 | ✗ | int ret = av_reallocp_array(&ctx->tile_group_sizes, cur_tile_num, | |
| 304 | sizeof(*ctx->tile_group_sizes)); | ||
| 305 | ✗ | if (ret < 0) { | |
| 306 | ✗ | ctx->tile_num = 0; | |
| 307 | ✗ | return ret; | |
| 308 | } | ||
| 309 | } | ||
| 310 | ✗ | ctx->tile_num = cur_tile_num; | |
| 311 | |||
| 312 | ✗ | total = fh_data_size + tgh_data_size; | |
| 313 | ✗ | ctx->clear_bytes += total; | |
| 314 | |||
| 315 | ✗ | bytestream2_init(&gb, tile_group->tile_data.data, tile_group->tile_data.data_size); | |
| 316 | |||
| 317 | // Build a table with block sizes for encrypted bytes and clear bytes | ||
| 318 | ✗ | for (unsigned tile_num = tile_group->tg_start; tile_num <= tile_group->tg_end; tile_num++) { | |
| 319 | ✗ | uint32_t encrypted_bytes, tile_size_bytes, tile_size = 0; | |
| 320 | |||
| 321 | ✗ | if (tile_num == tile_group->tg_end) { | |
| 322 | ✗ | tile_size = bytestream2_get_bytes_left(&gb); | |
| 323 | ✗ | encrypted_bytes = tile_size & ~0xFU; | |
| 324 | ✗ | ctx->clear_bytes += tile_size & 0xFU; | |
| 325 | |||
| 326 | ✗ | ctx->tile_group_sizes[tile_num].encrypted_bytes = encrypted_bytes; | |
| 327 | ✗ | ctx->tile_group_sizes[tile_num].aux_clear_bytes = encrypted_bytes ? ctx->clear_bytes : 0; | |
| 328 | ✗ | ctx->tile_group_sizes[tile_num].write_clear_bytes = tile_size & 0xFU; | |
| 329 | |||
| 330 | ✗ | if (encrypted_bytes) | |
| 331 | ✗ | ctx->clear_bytes = 0; | |
| 332 | ✗ | total += tile_size; | |
| 333 | |||
| 334 | ✗ | break; | |
| 335 | } | ||
| 336 | |||
| 337 | ✗ | tile_size_bytes = frame_header->tile_size_bytes_minus1 + 1; | |
| 338 | ✗ | if (bytestream2_get_bytes_left(&gb) < tile_size_bytes) | |
| 339 | ✗ | return AVERROR_INVALIDDATA; | |
| 340 | |||
| 341 | ✗ | for (int i = 0; i < tile_size_bytes; i++) | |
| 342 | ✗ | tile_size |= bytestream2_get_byteu(&gb) << 8 * i; | |
| 343 | ✗ | if (bytestream2_get_bytes_left(&gb) <= tile_size) | |
| 344 | ✗ | return AVERROR_INVALIDDATA; | |
| 345 | ✗ | tile_size++; | |
| 346 | |||
| 347 | // The spec requires encrypted bytes to be in blocks multiple of 16 | ||
| 348 | ✗ | encrypted_bytes = tile_size & ~0xFU; | |
| 349 | ✗ | ctx->clear_bytes += (tile_size & 0xFU) + tile_size_bytes; | |
| 350 | |||
| 351 | ✗ | ctx->tile_group_sizes[tile_num].encrypted_bytes = encrypted_bytes; | |
| 352 | ✗ | ctx->tile_group_sizes[tile_num].aux_clear_bytes = encrypted_bytes ? ctx->clear_bytes : 0; | |
| 353 | ✗ | ctx->tile_group_sizes[tile_num].write_clear_bytes = (tile_size & 0xFU) + tile_size_bytes; | |
| 354 | |||
| 355 | ✗ | if (encrypted_bytes) | |
| 356 | ✗ | ctx->clear_bytes = 0; | |
| 357 | |||
| 358 | ✗ | total += tile_size + tile_size_bytes; | |
| 359 | ✗ | bytestream2_skipu(&gb, tile_size); | |
| 360 | } | ||
| 361 | |||
| 362 | ✗ | bytestream2_init(&gb, tile_group->tile_data.data, tile_group->tile_data.data_size); | |
| 363 | |||
| 364 | ✗ | avio_write(pb, fh_data, fh_data_size); | |
| 365 | ✗ | avio_write(pb, tile_group->data, tgh_data_size); | |
| 366 | |||
| 367 | ✗ | for (unsigned tile_num = tile_group->tg_start; tile_num <= tile_group->tg_end; tile_num++) { | |
| 368 | ✗ | const struct MOVMuxCencAV1TGInfo *sizes = &ctx->tile_group_sizes[tile_num]; | |
| 369 | |||
| 370 | ✗ | avio_write(pb, gb.buffer, sizes->write_clear_bytes); | |
| 371 | ✗ | bytestream2_skipu(&gb, sizes->write_clear_bytes); | |
| 372 | ✗ | mov_cenc_write_encrypted(ctx, pb, gb.buffer, sizes->encrypted_bytes); | |
| 373 | ✗ | bytestream2_skipu(&gb, sizes->encrypted_bytes); | |
| 374 | ✗ | if (sizes->encrypted_bytes) { | |
| 375 | ✗ | unsigned clear_bytes = sizes->aux_clear_bytes; | |
| 376 | ✗ | if (clear_bytes > UINT16_MAX) { | |
| 377 | ✗ | auxiliary_info_add_subsample(ctx, UINT16_MAX, 0); | |
| 378 | ✗ | clear_bytes -= UINT16_MAX; | |
| 379 | } | ||
| 380 | ✗ | auxiliary_info_add_subsample(ctx, clear_bytes, sizes->encrypted_bytes); | |
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | ✗ | return total; | |
| 385 | } | ||
| 386 | |||
| 387 | ✗ | int ff_mov_cenc_av1_write_obus(AVFormatContext *s, MOVMuxCencContext* ctx, | |
| 388 | AVIOContext *pb, const AVPacket *pkt) | ||
| 389 | { | ||
| 390 | ✗ | CodedBitstreamFragment *td = &ctx->temporal_unit; | |
| 391 | ✗ | const CodedBitstreamAV1Context *av1 = ctx->cbc->priv_data; | |
| 392 | ✗ | const AV1RawFrameHeader *frame_header = NULL; | |
| 393 | ✗ | const uint8_t *fh_data = NULL; | |
| 394 | size_t fh_data_size; | ||
| 395 | ✗ | int out_size = 0, ret; | |
| 396 | |||
| 397 | ✗ | ret = mov_cenc_start_packet(ctx); | |
| 398 | ✗ | if (ret) { | |
| 399 | ✗ | return ret; | |
| 400 | } | ||
| 401 | |||
| 402 | ✗ | ret = ff_lavf_cbs_read_packet(ctx->cbc, td, pkt); | |
| 403 | ✗ | if (ret < 0) { | |
| 404 | ✗ | av_log(s, AV_LOG_ERROR, "CENC-AV1: Failed to parse temporal unit.\n"); | |
| 405 | ✗ | return ret; | |
| 406 | } | ||
| 407 | |||
| 408 | ✗ | if (!av1->sequence_header) { | |
| 409 | ✗ | av_log(s, AV_LOG_ERROR, "CENC-AV1: No sequence header available\n"); | |
| 410 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 411 | ✗ | goto end; | |
| 412 | } | ||
| 413 | |||
| 414 | ✗ | for (int i = 0; i < td->nb_units; i++) { | |
| 415 | ✗ | const CodedBitstreamUnit *unit = &td->units[i]; | |
| 416 | ✗ | const AV1RawOBU *obu = unit->content; | |
| 417 | |||
| 418 | ✗ | switch (unit->type) { | |
| 419 | ✗ | case AV1_OBU_FRAME_HEADER: | |
| 420 | ✗ | if (!obu->obu.frame_header.show_existing_frame) { | |
| 421 | ✗ | frame_header = &obu->obu.frame_header; | |
| 422 | ✗ | fh_data = unit->data; | |
| 423 | ✗ | fh_data_size = unit->data_size; | |
| 424 | ✗ | break; | |
| 425 | } | ||
| 426 | av_fallthrough; | ||
| 427 | case AV1_OBU_SEQUENCE_HEADER: | ||
| 428 | case AV1_OBU_METADATA: | ||
| 429 | ✗ | avio_write(pb, unit->data, unit->data_size); | |
| 430 | ✗ | ctx->clear_bytes += unit->data_size; | |
| 431 | ✗ | out_size += unit->data_size; | |
| 432 | ✗ | break; | |
| 433 | ✗ | case AV1_OBU_FRAME: | |
| 434 | ✗ | frame_header = &obu->obu.frame.header; | |
| 435 | ✗ | fh_data = unit->data; | |
| 436 | ✗ | fh_data_size = unit->data_size; | |
| 437 | av_fallthrough; | ||
| 438 | ✗ | case AV1_OBU_TILE_GROUP: | |
| 439 | { | ||
| 440 | const AV1RawTileGroup *tile_group; | ||
| 441 | |||
| 442 | ✗ | if (!frame_header){ | |
| 443 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 444 | ✗ | goto end; | |
| 445 | } | ||
| 446 | |||
| 447 | ✗ | if (unit->type == AV1_OBU_FRAME) | |
| 448 | ✗ | tile_group = &obu->obu.frame.tile_group; | |
| 449 | else | ||
| 450 | ✗ | tile_group = &obu->obu.tile_group; | |
| 451 | |||
| 452 | ✗ | ret = write_tiles(s, ctx, pb, unit->type, | |
| 453 | frame_header, fh_data, fh_data_size, tile_group); | ||
| 454 | ✗ | if (ret < 0) { | |
| 455 | ✗ | av_log(s, AV_LOG_ERROR, "CENC-AV1: Failed to write tiles\n"); | |
| 456 | ✗ | goto end; | |
| 457 | } | ||
| 458 | ✗ | av_assert0(ret == unit->data_size); | |
| 459 | ✗ | out_size += unit->data_size; | |
| 460 | ✗ | frame_header = NULL; | |
| 461 | } | ||
| 462 | ✗ | break; | |
| 463 | ✗ | default: | |
| 464 | ✗ | break; | |
| 465 | } | ||
| 466 | } | ||
| 467 | |||
| 468 | ✗ | if (ctx->clear_bytes) | |
| 469 | ✗ | auxiliary_info_add_subsample(ctx, ctx->clear_bytes, 0); | |
| 470 | ✗ | ctx->clear_bytes = 0; | |
| 471 | |||
| 472 | ✗ | ret = mov_cenc_end_packet(ctx); | |
| 473 | ✗ | if (ret) { | |
| 474 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 475 | ✗ | goto end; | |
| 476 | } | ||
| 477 | |||
| 478 | ✗ | ret = out_size; | |
| 479 | ✗ | end: | |
| 480 | ✗ | ff_lavf_cbs_fragment_reset(td); | |
| 481 | ✗ | return ret; | |
| 482 | } | ||
| 483 | |||
| 484 | /* TODO: reuse this function from movenc.c */ | ||
| 485 | ✗ | static int64_t update_size(AVIOContext *pb, int64_t pos) | |
| 486 | { | ||
| 487 | ✗ | int64_t curpos = avio_tell(pb); | |
| 488 | ✗ | avio_seek(pb, pos, SEEK_SET); | |
| 489 | ✗ | avio_wb32(pb, curpos - pos); /* rewrite size */ | |
| 490 | ✗ | avio_seek(pb, curpos, SEEK_SET); | |
| 491 | |||
| 492 | ✗ | return curpos - pos; | |
| 493 | } | ||
| 494 | |||
| 495 | ✗ | static int mov_cenc_write_senc_tag(MOVMuxCencContext* ctx, AVIOContext *pb, | |
| 496 | int64_t* auxiliary_info_offset) | ||
| 497 | { | ||
| 498 | ✗ | int64_t pos = avio_tell(pb); | |
| 499 | |||
| 500 | ✗ | avio_wb32(pb, 0); /* size */ | |
| 501 | ✗ | ffio_wfourcc(pb, "senc"); | |
| 502 | ✗ | avio_wb32(pb, ctx->use_subsamples ? 0x02 : 0); /* version & flags */ | |
| 503 | ✗ | avio_wb32(pb, ctx->auxiliary_info_entries); /* entry count */ | |
| 504 | ✗ | *auxiliary_info_offset = avio_tell(pb); | |
| 505 | ✗ | avio_write(pb, ctx->auxiliary_info, ctx->auxiliary_info_size); | |
| 506 | ✗ | return update_size(pb, pos); | |
| 507 | } | ||
| 508 | |||
| 509 | ✗ | static int mov_cenc_write_saio_tag(AVIOContext *pb, int64_t auxiliary_info_offset) | |
| 510 | { | ||
| 511 | ✗ | int64_t pos = avio_tell(pb); | |
| 512 | uint8_t version; | ||
| 513 | |||
| 514 | ✗ | avio_wb32(pb, 0); /* size */ | |
| 515 | ✗ | ffio_wfourcc(pb, "saio"); | |
| 516 | ✗ | version = auxiliary_info_offset > 0xffffffff ? 1 : 0; | |
| 517 | ✗ | avio_w8(pb, version); | |
| 518 | ✗ | avio_wb24(pb, 0); /* flags */ | |
| 519 | ✗ | avio_wb32(pb, 1); /* entry count */ | |
| 520 | ✗ | if (version) { | |
| 521 | ✗ | avio_wb64(pb, auxiliary_info_offset); | |
| 522 | } else { | ||
| 523 | ✗ | avio_wb32(pb, auxiliary_info_offset); | |
| 524 | } | ||
| 525 | ✗ | return update_size(pb, pos); | |
| 526 | } | ||
| 527 | |||
| 528 | ✗ | static int mov_cenc_write_saiz_tag(MOVMuxCencContext* ctx, AVIOContext *pb) | |
| 529 | { | ||
| 530 | ✗ | int64_t pos = avio_tell(pb); | |
| 531 | ✗ | avio_wb32(pb, 0); /* size */ | |
| 532 | ✗ | ffio_wfourcc(pb, "saiz"); | |
| 533 | ✗ | avio_wb32(pb, 0); /* version & flags */ | |
| 534 | ✗ | avio_w8(pb, ctx->use_subsamples ? 0 : AES_CTR_IV_SIZE); /* default size*/ | |
| 535 | ✗ | avio_wb32(pb, ctx->auxiliary_info_entries); /* entry count */ | |
| 536 | ✗ | if (ctx->use_subsamples) { | |
| 537 | ✗ | avio_write(pb, ctx->auxiliary_info_sizes, ctx->auxiliary_info_entries); | |
| 538 | } | ||
| 539 | ✗ | return update_size(pb, pos); | |
| 540 | } | ||
| 541 | |||
| 542 | ✗ | void ff_mov_cenc_write_stbl_atoms(MOVMuxCencContext* ctx, AVIOContext *pb, | |
| 543 | int64_t moof_offset) | ||
| 544 | { | ||
| 545 | int64_t auxiliary_info_offset; | ||
| 546 | |||
| 547 | ✗ | mov_cenc_write_senc_tag(ctx, pb, &auxiliary_info_offset); | |
| 548 | ✗ | mov_cenc_write_saio_tag(pb, auxiliary_info_offset - moof_offset); | |
| 549 | ✗ | mov_cenc_write_saiz_tag(ctx, pb); | |
| 550 | ✗ | } | |
| 551 | |||
| 552 | ✗ | static int mov_cenc_write_schi_tag(AVIOContext *pb, uint8_t* kid) | |
| 553 | { | ||
| 554 | ✗ | int64_t pos = avio_tell(pb); | |
| 555 | ✗ | avio_wb32(pb, 0); /* size */ | |
| 556 | ✗ | ffio_wfourcc(pb, "schi"); | |
| 557 | |||
| 558 | ✗ | avio_wb32(pb, 32); /* size */ | |
| 559 | ✗ | ffio_wfourcc(pb, "tenc"); | |
| 560 | ✗ | avio_wb32(pb, 0); /* version & flags */ | |
| 561 | ✗ | avio_wb24(pb, 1); /* is encrypted */ | |
| 562 | ✗ | avio_w8(pb, AES_CTR_IV_SIZE); /* iv size */ | |
| 563 | ✗ | avio_write(pb, kid, CENC_KID_SIZE); | |
| 564 | |||
| 565 | ✗ | return update_size(pb, pos); | |
| 566 | } | ||
| 567 | |||
| 568 | ✗ | int ff_mov_cenc_write_sinf_tag(MOVTrack* track, AVIOContext *pb, uint8_t* kid) | |
| 569 | { | ||
| 570 | ✗ | int64_t pos = avio_tell(pb); | |
| 571 | ✗ | avio_wb32(pb, 0); /* size */ | |
| 572 | ✗ | ffio_wfourcc(pb, "sinf"); | |
| 573 | |||
| 574 | /* frma */ | ||
| 575 | ✗ | avio_wb32(pb, 12); /* size */ | |
| 576 | ✗ | ffio_wfourcc(pb, "frma"); | |
| 577 | ✗ | avio_wl32(pb, track->tag); | |
| 578 | |||
| 579 | /* schm */ | ||
| 580 | ✗ | avio_wb32(pb, 20); /* size */ | |
| 581 | ✗ | ffio_wfourcc(pb, "schm"); | |
| 582 | ✗ | avio_wb32(pb, 0); /* version & flags */ | |
| 583 | ✗ | ffio_wfourcc(pb, "cenc"); /* scheme type*/ | |
| 584 | ✗ | avio_wb32(pb, 0x10000); /* scheme version */ | |
| 585 | |||
| 586 | /* schi */ | ||
| 587 | ✗ | mov_cenc_write_schi_tag(pb, kid); | |
| 588 | |||
| 589 | ✗ | return update_size(pb, pos); | |
| 590 | } | ||
| 591 | |||
| 592 | static const CodedBitstreamUnitType decompose_unit_types[] = { | ||
| 593 | AV1_OBU_TEMPORAL_DELIMITER, | ||
| 594 | AV1_OBU_SEQUENCE_HEADER, | ||
| 595 | AV1_OBU_FRAME_HEADER, | ||
| 596 | AV1_OBU_TILE_GROUP, | ||
| 597 | AV1_OBU_FRAME, | ||
| 598 | }; | ||
| 599 | |||
| 600 | ✗ | int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, | |
| 601 | int use_subsamples, enum AVCodecID codec_id, int bitexact) | ||
| 602 | { | ||
| 603 | int ret; | ||
| 604 | |||
| 605 | ✗ | ctx->aes_ctr = av_aes_ctr_alloc(); | |
| 606 | ✗ | if (!ctx->aes_ctr) { | |
| 607 | ✗ | return AVERROR(ENOMEM); | |
| 608 | } | ||
| 609 | |||
| 610 | ✗ | ret = av_aes_ctr_init(ctx->aes_ctr, encryption_key); | |
| 611 | ✗ | if (ret != 0) { | |
| 612 | ✗ | return ret; | |
| 613 | } | ||
| 614 | |||
| 615 | ✗ | if (!bitexact) { | |
| 616 | ✗ | av_aes_ctr_set_random_iv(ctx->aes_ctr); | |
| 617 | } | ||
| 618 | |||
| 619 | ✗ | ctx->use_subsamples = use_subsamples; | |
| 620 | |||
| 621 | ✗ | if (codec_id == AV_CODEC_ID_AV1) { | |
| 622 | ✗ | ret = ff_lavf_cbs_init(&ctx->cbc, codec_id, NULL); | |
| 623 | ✗ | if (ret < 0) | |
| 624 | ✗ | return ret; | |
| 625 | |||
| 626 | ✗ | ctx->cbc->decompose_unit_types = decompose_unit_types; | |
| 627 | ✗ | ctx->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types); | |
| 628 | } | ||
| 629 | |||
| 630 | ✗ | return 0; | |
| 631 | } | ||
| 632 | |||
| 633 | ✗ | void ff_mov_cenc_flush(MOVMuxCencContext* ctx) | |
| 634 | { | ||
| 635 | ✗ | ctx->auxiliary_info_entries = 0; | |
| 636 | ✗ | ctx->auxiliary_info_size = 0; | |
| 637 | ✗ | } | |
| 638 | |||
| 639 | 290 | void ff_mov_cenc_free(MOVMuxCencContext* ctx) | |
| 640 | { | ||
| 641 | 290 | av_aes_ctr_free(ctx->aes_ctr); | |
| 642 | 290 | av_freep(&ctx->auxiliary_info); | |
| 643 | 290 | av_freep(&ctx->auxiliary_info_sizes); | |
| 644 | |||
| 645 | 290 | av_freep(&ctx->tile_group_sizes); | |
| 646 | 290 | ff_lavf_cbs_fragment_free(&ctx->temporal_unit); | |
| 647 | 290 | ff_lavf_cbs_close(&ctx->cbc); | |
| 648 | 290 | } | |
| 649 |