James Almer: > Signed-off-by: James Almer <jamr...@gmail.com> > --- > configure | 2 +- > libavformat/Makefile | 1 + > libavformat/cbs.c | 1 + > libavformat/cbs_av1.c | 1 + > libavformat/movenc.c | 13 ++- > libavformat/movenccenc.c | 223 ++++++++++++++++++++++++++++++++++++++- > libavformat/movenccenc.h | 20 +++- > 7 files changed, 255 insertions(+), 6 deletions(-) > create mode 100644 libavformat/cbs.c > create mode 100644 libavformat/cbs_av1.c > > diff --git a/configure b/configure > index 14f7bcde0e..0f6b6c20fb 100755 > --- a/configure > +++ b/configure > @@ -3689,7 +3689,7 @@ mlp_demuxer_select="mlp_parser" > mmf_muxer_select="riffenc" > mov_demuxer_select="iso_media riffdec" > mov_demuxer_suggest="iamfdec zlib" > -mov_muxer_select="iso_media iso_writer riffenc rtpenc_chain > vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser" > +mov_muxer_select="iso_media iso_writer riffenc rtpenc_chain > vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser cbs_av1" > mov_muxer_suggest="iamfenc" > mp3_demuxer_select="mpegaudio_parser" > mp3_muxer_select="mpegaudioheader" > diff --git a/libavformat/Makefile b/libavformat/Makefile > index 7730e7c4e6..1e57ae7d8a 100644 > --- a/libavformat/Makefile > +++ b/libavformat/Makefile > @@ -740,6 +740,7 @@ SHLIBOBJS-$(CONFIG_JNI) += ffjni.o > SHLIBOBJS-$(CONFIG_JPEGXL_ANIM_DEMUXER) += jpegxl_parse.o > SHLIBOBJS-$(CONFIG_MATROSKA_DEMUXER) += mpeg4audio_sample_rates.o > SHLIBOBJS-$(CONFIG_MOV_DEMUXER) += ac3_channel_layout_tab.o > +SHLIBOBJS-$(CONFIG_MOV_MUXER) += cbs.o cbs_av1.o > SHLIBOBJS-$(CONFIG_MP3_MUXER) += mpegaudiotabs.o > SHLIBOBJS-$(CONFIG_MXF_MUXER) += golomb_tab.o \ > rangecoder_dec.o > diff --git a/libavformat/cbs.c b/libavformat/cbs.c > new file mode 100644 > index 0000000000..8f1235da8d > --- /dev/null > +++ b/libavformat/cbs.c > @@ -0,0 +1 @@ > +#include "libavcodec/cbs.c" > diff --git a/libavformat/cbs_av1.c b/libavformat/cbs_av1.c > new file mode 100644 > index 0000000000..32b9e72c57 > --- /dev/null > +++ b/libavformat/cbs_av1.c > @@ -0,0 +1 @@ > +#include "libavcodec/cbs_av1.c" > diff --git a/libavformat/movenc.c b/libavformat/movenc.c > index af013e1fc6..c60c46012f 100644 > --- a/libavformat/movenc.c > +++ b/libavformat/movenc.c > @@ -6767,7 +6767,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket > *pkt) > } else { > size = ff_vvc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL); > } > - } else if (par->codec_id == AV_CODEC_ID_AV1) { > + } else if (par->codec_id == AV_CODEC_ID_AV1 && !trk->cenc.aes_ctr) { > if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) { > ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data, > &size, &offset); > @@ -6815,6 +6815,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket > *pkt) > ret = ff_mov_cenc_avc_write_nal_units(s, &trk->cenc, > nal_size_length, pb, pkt->data, size); > } else if(par->codec_id == AV_CODEC_ID_VVC) { > ret = AVERROR_PATCHWELCOME; > + } else if(par->codec_id == AV_CODEC_ID_AV1) { > + av_assert0(size == pkt->size); > + ret = ff_mov_cenc_av1_write_obus(s, &trk->cenc, > pb, pkt); > + if (ret > 0) { > + size = ret; > + ret = 0; > + } > } else { > ret = ff_mov_cenc_write_packet(&trk->cenc, pb, pkt->data, > size); > } > @@ -8135,8 +8142,8 @@ static int mov_init(AVFormatContext *s) > if (mov->encryption_scheme == MOV_ENC_CENC_AES_CTR) { > ret = ff_mov_cenc_init(&track->cenc, mov->encryption_key, > (track->par->codec_id == AV_CODEC_ID_H264 || > track->par->codec_id == AV_CODEC_ID_HEVC || > - track->par->codec_id == AV_CODEC_ID_VVC), > - s->flags & AVFMT_FLAG_BITEXACT); > + track->par->codec_id == AV_CODEC_ID_VVC || > track->par->codec_id == AV_CODEC_ID_AV1), > + track->par->codec_id, s->flags & AVFMT_FLAG_BITEXACT); > if (ret) > return ret; > } > diff --git a/libavformat/movenccenc.c b/libavformat/movenccenc.c > index f54d3bcbca..af801eb3a0 100644 > --- a/libavformat/movenccenc.c > +++ b/libavformat/movenccenc.c > @@ -19,6 +19,9 @@ > * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 > USA > */ > #include "movenccenc.h" > +#include "libavcodec/av1_parse.h" > +#include "libavcodec/bytestream.h" > +#include "libavcodec/cbs_av1.h" > #include "libavutil/intreadwrite.h" > #include "libavutil/mem.h" > #include "avio_internal.h" > @@ -280,6 +283,203 @@ int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, > MOVMuxCencContext* ctx, > return 0; > } > > +static int write_tiles(AVFormatContext *s, MOVMuxCencContext *ctx, > AVIOContext *pb, AV1_OBU_Type type, > + const AV1RawFrameHeader *frame_header, const uint8_t > *fh_data, size_t fh_data_size, > + const AV1RawTileGroup *tile_group) > +{ > + GetByteContext gb; > + size_t tgh_data_size = tile_group->data_size; > + int cur_tile_num = frame_header->tile_cols * frame_header->tile_rows; > + int total = 0; > + > + // Get the Frame Header size > + if (type == AV1_OBU_FRAME) > + fh_data_size -= tgh_data_size; > + // Get the Tile Group Header size > + tgh_data_size -= tile_group->tile_data.data_size; > + > + if (ctx->tile_num < cur_tile_num) { > + int ret = av_reallocp_array(&ctx->tile_group_sizes, cur_tile_num, > + sizeof(*ctx->tile_group_sizes)); > + if (ret < 0) { > + ctx->tile_num = 0; > + return ret; > + } > + } > + ctx->tile_num = cur_tile_num; > + > + total = fh_data_size + tgh_data_size; > + ctx->clear_bytes += total; > + > + bytestream2_init(&gb, tile_group->tile_data.data, > tile_group->tile_data.data_size); > + > + // Build a table with block sizes for encrypted bytes and clear bytes > + for (unsigned tile_num = tile_group->tg_start; tile_num <= > tile_group->tg_end; tile_num++) { > + uint32_t encrypted_bytes, tile_size_bytes, tile_size = 0; > + > + if (tile_num == tile_group->tg_end) { > + tile_size = bytestream2_get_bytes_left(&gb); > + encrypted_bytes = tile_size & ~0xFU; > + ctx->clear_bytes += tile_size & 0xFU; > + > + ctx->tile_group_sizes[tile_num].encrypted_bytes = > encrypted_bytes; > + ctx->tile_group_sizes[tile_num].aux_clear_bytes = > encrypted_bytes ? ctx->clear_bytes : 0; > + ctx->tile_group_sizes[tile_num].write_clear_bytes = tile_size & > 0xFU; > + > + if (encrypted_bytes) > + ctx->clear_bytes = 0; > + total += tile_size; > + > + break; > + } > + > + tile_size_bytes = frame_header->tile_size_bytes_minus1 + 1; > + if (bytestream2_get_bytes_left(&gb) < tile_size_bytes) > + return AVERROR_INVALIDDATA; > + > + for (int i = 0; i < tile_size_bytes; i++) > + tile_size |= bytestream2_get_byteu(&gb) << 8 * i; > + if (bytestream2_get_bytes_left(&gb) <= tile_size) > + return AVERROR_INVALIDDATA; > + tile_size++; > + > + // The spec requires encrypted bytes to be in blocks multiple of 16 > + encrypted_bytes = tile_size & ~0xFU; > + ctx->clear_bytes += (tile_size & 0xFU) + tile_size_bytes; > + > + ctx->tile_group_sizes[tile_num].encrypted_bytes = encrypted_bytes; > + ctx->tile_group_sizes[tile_num].aux_clear_bytes = encrypted_bytes > ? ctx->clear_bytes : 0; > + ctx->tile_group_sizes[tile_num].write_clear_bytes = (tile_size & > 0xFU) + tile_size_bytes; > + > + if (encrypted_bytes) > + ctx->clear_bytes = 0; > + > + total += tile_size + tile_size_bytes; > + bytestream2_skipu(&gb, tile_size); > + } > + > + bytestream2_init(&gb, tile_group->tile_data.data, > tile_group->tile_data.data_size); > + > + avio_write(pb, fh_data, fh_data_size); > + avio_write(pb, tile_group->data, tgh_data_size); > + > + for (unsigned tile_num = tile_group->tg_start; tile_num <= > tile_group->tg_end; tile_num++) { > + const struct MOVMuxCencAV1TGInfo *sizes = > &ctx->tile_group_sizes[tile_num]; > + > + avio_write(pb, gb.buffer, sizes->write_clear_bytes); > + bytestream2_skipu(&gb, sizes->write_clear_bytes); > + mov_cenc_write_encrypted(ctx, pb, gb.buffer, sizes->encrypted_bytes); > + bytestream2_skipu(&gb, sizes->encrypted_bytes); > + if (sizes->encrypted_bytes) { > + unsigned clear_bytes = sizes->aux_clear_bytes; > + if (clear_bytes > UINT16_MAX) { > + auxiliary_info_add_subsample(ctx, UINT16_MAX, 0); > + clear_bytes -= UINT16_MAX; > + } > + auxiliary_info_add_subsample(ctx, clear_bytes, > sizes->encrypted_bytes); > + } > + } > + > + return total; > +} > + > +int ff_mov_cenc_av1_write_obus(AVFormatContext *s, MOVMuxCencContext* ctx, > + AVIOContext *pb, const AVPacket *pkt) > +{ > + CodedBitstreamFragment *td = &ctx->temporal_unit; > + const CodedBitstreamAV1Context *av1 = ctx->cbc->priv_data; > + const AV1RawFrameHeader *frame_header = NULL; > + const uint8_t *fh_data = NULL; > + size_t fh_data_size; > + int out_size = 0, ret; > + > + ret = mov_cenc_start_packet(ctx); > + if (ret) { > + return ret; > + } > + > + ret = ff_cbs_read_packet(ctx->cbc, td, pkt); > + if (ret < 0) { > + av_log(s, AV_LOG_ERROR, "CENC-AV1: Failed to parse temporal > unit.\n"); > + return ret; > + } > + > + if (!av1->sequence_header) { > + av_log(s, AV_LOG_ERROR, "CENC-AV1: No sequence header available\n"); > + ret = AVERROR_INVALIDDATA; > + goto end; > + } > + > + for (int i = 0; i < td->nb_units; i++) { > + const CodedBitstreamUnit *unit = &td->units[i]; > + const AV1RawOBU *obu = unit->content; > + > + switch (unit->type) { > + case AV1_OBU_FRAME_HEADER: > + if (!obu->obu.frame_header.show_existing_frame) { > + frame_header = &obu->obu.frame_header; > + fh_data = unit->data; > + fh_data_size = unit->data_size; > + break; > + } > + // fall-through > + case AV1_OBU_SEQUENCE_HEADER: > + case AV1_OBU_METADATA: > + avio_write(pb, unit->data, unit->data_size); > + ctx->clear_bytes += unit->data_size; > + out_size += unit->data_size; > + break; > + case AV1_OBU_FRAME: > + frame_header = &obu->obu.frame.header; > + fh_data = unit->data; > + fh_data_size = unit->data_size; > + // fall-through > + case AV1_OBU_TILE_GROUP: > + { > + const AV1RawTileGroup *tile_group; > + > + if (!frame_header){ > + ret = AVERROR_INVALIDDATA; > + goto end; > + } > + > + if (unit->type == AV1_OBU_FRAME) > + tile_group = &obu->obu.frame.tile_group; > + else > + tile_group = &obu->obu.tile_group; > + > + ret = write_tiles(s, ctx, pb, unit->type, > + frame_header, fh_data, fh_data_size, > tile_group); > + if (ret < 0) { > + av_log(s, AV_LOG_ERROR, "CENC-AV1: Failed to write > tiles\n"); > + goto end; > + } > + av_assert0(ret == unit->data_size); > + out_size += unit->data_size; > + frame_header = NULL; > + } > + break; > + default: > + break; > + } > + } > + > + if (ctx->clear_bytes) > + auxiliary_info_add_subsample(ctx, ctx->clear_bytes, 0); > + ctx->clear_bytes = 0; > + > + ret = mov_cenc_end_packet(ctx); > + if (ret) { > + ret = AVERROR_INVALIDDATA; > + goto end; > + } > + > + ret = out_size; > +end: > + ff_cbs_fragment_reset(td); > + return ret; > +} > + > /* TODO: reuse this function from movenc.c */ > static int64_t update_size(AVIOContext *pb, int64_t pos) > { > @@ -388,8 +588,16 @@ int ff_mov_cenc_write_sinf_tag(MOVTrack* track, > AVIOContext *pb, uint8_t* kid) > return update_size(pb, pos); > } > > +static const CodedBitstreamUnitType decompose_unit_types[] = { > + AV1_OBU_TEMPORAL_DELIMITER, > + AV1_OBU_SEQUENCE_HEADER, > + AV1_OBU_FRAME_HEADER, > + AV1_OBU_TILE_GROUP, > + AV1_OBU_FRAME, > +}; > + > int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, > - int use_subsamples, int bitexact) > + int use_subsamples, enum AVCodecID codec_id, int > bitexact) > { > int ret; > > @@ -409,6 +617,15 @@ int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* > encryption_key, > > ctx->use_subsamples = use_subsamples; > > + if (codec_id == AV_CODEC_ID_AV1) { > + ret = ff_cbs_init(&ctx->cbc, codec_id, NULL); > + if (ret < 0) > + return ret; > + > + ctx->cbc->decompose_unit_types = decompose_unit_types; > + ctx->cbc->nb_decompose_unit_types = > FF_ARRAY_ELEMS(decompose_unit_types); > + } > + > return 0; > } > > @@ -417,4 +634,8 @@ void ff_mov_cenc_free(MOVMuxCencContext* ctx) > av_aes_ctr_free(ctx->aes_ctr); > av_freep(&ctx->auxiliary_info); > av_freep(&ctx->auxiliary_info_sizes); > + > + av_freep(&ctx->tile_group_sizes); > + ff_cbs_fragment_free(&ctx->temporal_unit); > + ff_cbs_close(&ctx->cbc); > } > diff --git a/libavformat/movenccenc.h b/libavformat/movenccenc.h > index 7da5268090..722914dc1c 100644 > --- a/libavformat/movenccenc.h > +++ b/libavformat/movenccenc.h > @@ -23,6 +23,7 @@ > #define AVFORMAT_MOVENCCENC_H > > #include "libavutil/aes_ctr.h" > +#include "libavcodec/cbs.h" > #include "avformat.h" > #include "avio.h" > > @@ -30,6 +31,12 @@ > > struct MOVTrack; > > +struct MOVMuxCencAV1TGInfo { > + uint32_t encrypted_bytes; > + uint32_t write_clear_bytes; > + uint32_t aux_clear_bytes; > +}; > + > typedef struct { > struct AVAESCTR* aes_ctr; > uint8_t* auxiliary_info; > @@ -43,6 +50,14 @@ typedef struct { > size_t auxiliary_info_subsample_start; > uint8_t* auxiliary_info_sizes; > size_t auxiliary_info_sizes_alloc_size; > + > + /* AV1 */ > + struct MOVMuxCencAV1TGInfo *tile_group_sizes; > + uint32_t clear_bytes; > + int tile_num; > + /* CBS */ > + CodedBitstreamContext *cbc; > + CodedBitstreamFragment temporal_unit; > } MOVMuxCencContext; > > /** > @@ -50,7 +65,8 @@ typedef struct { > * @param key encryption key, must have a length of AES_CTR_KEY_SIZE > * @param use_subsamples when enabled parts of a packet can be encrypted, > otherwise the whole packet is encrypted > */ > -int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, int > use_subsamples, int bitexact); > +int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, int > use_subsamples, > + enum AVCodecID codec_id, int bitexact); > > /** > * Free a CENC context > @@ -73,6 +89,8 @@ int ff_mov_cenc_avc_parse_nal_units(MOVMuxCencContext* ctx, > AVIOContext *pb, con > int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext* > ctx, int nal_length_size, > AVIOContext *pb, const uint8_t *buf_in, int size); > > +int ff_mov_cenc_av1_write_obus(AVFormatContext *s, MOVMuxCencContext* ctx, > + AVIOContext *pb, const AVPacket *pkt); > /** > * Write the cenc atoms that should reside inside stbl > */
Duplicating .o files is meant for small stuff, not for gigantic things like cbs_av1.o (whose .text is 86519B here). - Andreas _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".