History

avformat/movenccenc: add support for CENC AV1 encryption

James Almer committed 1 year ago

Commit 08e334e1 parent 563170410 files · +298 −7Browse files

Unified · Split

10 files
  1. Changelog +1 −0
  2. libavformat/Makefile +1 −1
  3. libavformat/cbs.c +2 −0
  4. libavformat/cbs.h +35 −0
  5. libavformat/cbs_av1.c +5 −0
  6. libavformat/movenc.c +10 −3
  7. libavformat/movenccenc.c +222 −1
  8. libavformat/movenccenc.h +19 −1
  9. libavformat/version.h +1 −1
  10. tests/ref/fate/source +2 −0
Changelog+1−0
@@ -11,6 +11,7 @@ version <next>: · 10 unmodified lines
1111 - Enhanced FLV v2: Multitrack audio/video, modern codec support
1212 - Animated JPEG XL encoding (via libjxl)
1313 - VVC in Matroska
14+- CENC AV1 support in MP4 muxer
1415
1516 version 7.1:
1617 - Raw Captions with Time (RCWT) closed caption demuxer
libavformat/Makefile+1−1
@@ -377,7 +377,7 @@ OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \ · 376 unmodified lines
377377 OBJS-$(CONFIG_MOV_MUXER) += movenc.o \
378378 movenchint.o mov_chan.o rtp.o \
379379 movenccenc.o movenc_ttml.o rawutils.o \
380− dovi_isom.o evc.o
380+ dovi_isom.o evc.o cbs.o cbs_av1.o
381381 OBJS-$(CONFIG_MP2_MUXER) += rawenc.o
382382 OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
383383 OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o
libavformat/cbs.c+2−0
@@ -0,0 +1,2 @@
1+#include "cbs.h"
2+#include "libavcodec/cbs.c"
libavformat/cbs.h+35−0
@@ -0,0 +1,35 @@
1+/*
2+ * This file is part of FFmpeg.
3+ *
4+ * FFmpeg is free software; you can redistribute it and/or
5+ * modify it under the terms of the GNU Lesser General Public
6+ * License as published by the Free Software Foundation; either
7+ * version 2.1 of the License, or (at your option) any later version.
8+ *
9+ * FFmpeg is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General Public
15+ * License along with FFmpeg; if not, write to the Free Software
16+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+ */
18+
19+#ifndef AVFORMAT_CBS_H
20+#define AVFORMAT_CBS_H
21+
22+#define CBS_PREFIX lavf_cbs
23+#define CBS_WRITE 0
24+#define CBS_TRACE 0
25+#define CBS_H264 0
26+#define CBS_H265 0
27+#define CBS_H266 0
28+#define CBS_JPEG 0
29+#define CBS_MPEG2 0
30+#define CBS_VP8 0
31+#define CBS_VP9 0
32+
33+#include "libavcodec/cbs.h"
34+
35+#endif /* AVFORMAT_CBS_H */
libavformat/cbs_av1.c+5−0
@@ -0,0 +1,5 @@
1+#define CBS_AV1_OBU_TILE_LIST 0
2+#define CBS_AV1_OBU_METADATA 0
3+#define CBS_AV1_OBU_PADDING 0
4+#include "cbs.h"
5+#include "libavcodec/cbs_av1.c"
libavformat/movenc.c+10−3
@@ -6767,7 +6767,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) · 6766 unmodified lines
67676767 } else {
67686768 size = ff_vvc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
67696769 }
6770− } else if (par->codec_id == AV_CODEC_ID_AV1) {
6770+ } else if (par->codec_id == AV_CODEC_ID_AV1 && !trk->cenc.aes_ctr) {
67716771 if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) {
67726772 ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data,
67736773 &size, &offset);
@@ -6815,6 +6815,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) · 41 unmodified lines
68156815 ret = ff_mov_cenc_avc_write_nal_units(s, &trk->cenc, nal_size_length, pb, pkt->data, size);
68166816 } else if(par->codec_id == AV_CODEC_ID_VVC) {
68176817 ret = AVERROR_PATCHWELCOME;
6818+ } else if(par->codec_id == AV_CODEC_ID_AV1) {
6819+ av_assert0(size == pkt->size);
6820+ ret = ff_mov_cenc_av1_write_obus(s, &trk->cenc, pb, pkt);
6821+ if (ret > 0) {
6822+ size = ret;
6823+ ret = 0;
6824+ }
68186825 } else {
68196826 ret = ff_mov_cenc_write_packet(&trk->cenc, pb, pkt->data, size);
68206827 }
@@ -8135,8 +8142,8 @@ static int mov_init(AVFormatContext *s) · 1314 unmodified lines
81358142 if (mov->encryption_scheme == MOV_ENC_CENC_AES_CTR) {
81368143 ret = ff_mov_cenc_init(&track->cenc, mov->encryption_key,
81378144 (track->par->codec_id == AV_CODEC_ID_H264 || track->par->codec_id == AV_CODEC_ID_HEVC ||
8138− track->par->codec_id == AV_CODEC_ID_VVC),
8139− s->flags & AVFMT_FLAG_BITEXACT);
8145+ track->par->codec_id == AV_CODEC_ID_VVC || track->par->codec_id == AV_CODEC_ID_AV1),
8146+ track->par->codec_id, s->flags & AVFMT_FLAG_BITEXACT);
81408147 if (ret)
81418148 return ret;
81428149 }
libavformat/movenccenc.c+222−1
@@ -19,6 +19,9 @@ · 18 unmodified lines
1919 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2020 */
2121 #include "movenccenc.h"
22+#include "libavcodec/av1_parse.h"
23+#include "libavcodec/bytestream.h"
24+#include "libavcodec/cbs_av1.h"
2225 #include "libavutil/intreadwrite.h"
2326 #include "libavutil/mem.h"
2427 #include "avio_internal.h"
@@ -280,6 +283,203 @@ int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext* ctx, · 255 unmodified lines
280283 return 0;
281284 }
282285
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+
283483 /* TODO: reuse this function from movenc.c */
284484 static int64_t update_size(AVIOContext *pb, int64_t pos)
285485 {
@@ -388,8 +588,16 @@ int ff_mov_cenc_write_sinf_tag(MOVTrack* track, AVIOContext *pb, uint8_t* kid) · 102 unmodified lines
388588 return update_size(pb, pos);
389589 }
390590
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+
391599 int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key,
392− int use_subsamples, int bitexact)
600+ int use_subsamples, enum AVCodecID codec_id, int bitexact)
393601 {
394602 int ret;
395603
@@ -409,6 +617,15 @@ int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, · 13 unmodified lines
409617
410618 ctx->use_subsamples = use_subsamples;
411619
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+
412629 return 0;
413630 }
414631
@@ -417,4 +634,8 @@ void ff_mov_cenc_free(MOVMuxCencContext* ctx) · 2 unmodified lines
417634 av_aes_ctr_free(ctx->aes_ctr);
418635 av_freep(&ctx->auxiliary_info);
419636 av_freep(&ctx->auxiliary_info_sizes);
637+
638+ av_freep(&ctx->tile_group_sizes);
639+ ff_lavf_cbs_fragment_free(&ctx->temporal_unit);
640+ ff_lavf_cbs_close(&ctx->cbc);
420641 }
libavformat/movenccenc.h+19−1
@@ -25,11 +25,18 @@ · 24 unmodified lines
2525 #include "libavutil/aes_ctr.h"
2626 #include "avformat.h"
2727 #include "avio.h"
28+#include "cbs.h"
2829
2930 #define CENC_KID_SIZE (16)
3031
3132 struct MOVTrack;
3233
34+struct MOVMuxCencAV1TGInfo {
35+ uint32_t encrypted_bytes;
36+ uint32_t write_clear_bytes;
37+ uint32_t aux_clear_bytes;
38+};
39+
3340 typedef struct {
3441 struct AVAESCTR* aes_ctr;
3542 uint8_t* auxiliary_info;
@@ -43,6 +50,14 @@ typedef struct { · 7 unmodified lines
4350 size_t auxiliary_info_subsample_start;
4451 uint8_t* auxiliary_info_sizes;
4552 size_t auxiliary_info_sizes_alloc_size;
53+
54+ /* AV1 */
55+ struct MOVMuxCencAV1TGInfo *tile_group_sizes;
56+ uint32_t clear_bytes;
57+ int tile_num;
58+ /* CBS */
59+ CodedBitstreamContext *cbc;
60+ CodedBitstreamFragment temporal_unit;
4661 } MOVMuxCencContext;
4762
4863 /**
@@ -50,7 +65,8 @@ typedef struct { · 1 unmodified line
5065 * @param key encryption key, must have a length of AES_CTR_KEY_SIZE
5166 * @param use_subsamples when enabled parts of a packet can be encrypted, otherwise the whole packet is encrypted
5267 */
53−int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, int use_subsamples, int bitexact);
68+int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, int use_subsamples,
69+ enum AVCodecID codec_id, int bitexact);
5470
5571 /**
5672 * Free a CENC context
@@ -73,6 +89,8 @@ int ff_mov_cenc_avc_parse_nal_units(MOVMuxCencContext* ctx, AVIOContext *pb, con · 16 unmodified lines
7389 int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext* ctx, int nal_length_size,
7490 AVIOContext *pb, const uint8_t *buf_in, int size);
7591
92+int ff_mov_cenc_av1_write_obus(AVFormatContext *s, MOVMuxCencContext* ctx,
93+ AVIOContext *pb, const AVPacket *pkt);
7694 /**
7795 * Write the cenc atoms that should reside inside stbl
7896 */
libavformat/version.h+1−1
@@ -32,7 +32,7 @@ · 31 unmodified lines
3232 #include "version_major.h"
3333
3434 #define LIBAVFORMAT_VERSION_MINOR 9
35−#define LIBAVFORMAT_VERSION_MICRO 107
35+#define LIBAVFORMAT_VERSION_MICRO 108
3636
3737 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
3838 LIBAVFORMAT_VERSION_MINOR, \
tests/ref/fate/source+2−0
@@ -10,6 +10,8 @@ libavdevice/riscv/cpu_common.c · 9 unmodified lines
1010 libavfilter/file_open.c
1111 libavfilter/log2_tab.c
1212 libavfilter/riscv/cpu_common.c
13+libavformat/cbs.c
14+libavformat/cbs_av1.c
1315 libavformat/file_open.c
1416 libavformat/golomb_tab.c
1517 libavformat/log2_tab.c

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t