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