FFmpeg
Loading...
Searching...
No Matches
dovi_split.c
Go to the documentation of this file.
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#include <stdbool.h>
20
21#include "libavutil/avassert.h"
22#include "libavutil/dovi_meta.h"
24#include "libavutil/mem.h"
25#include "libavutil/opt.h"
26
27#include "libavcodec/bsf.h"
30#include "libavcodec/packet.h"
31
33
40
41typedef struct DOVISplitContext {
42 const AVClass *class;
43 int mode;
44
45 int nal_length_size; /* 0 means Annex-B input */
46 int out_nal_length_size; /* 0 means Annex-B output */
49
50static int hvcc_nal_length_size(const uint8_t *data, int size)
51{
52 if (size >= 23 && data[0] == 1 && AV_RB24(data) != 1 && AV_RB32(data) != 1)
53 return (data[21] & 3) + 1;
54 return 0;
55}
56
58{
59 DOVISplitContext *s = ctx->priv_data;
60 bool keep_bl = s->mode == DOVI_SPLIT_BL || s->mode == DOVI_SPLIT_BL_RPU;
61 bool keep_el = s->mode == DOVI_SPLIT_EL || s->mode == DOVI_SPLIT_EL_RPU;
62 bool keep_rpu = s->mode == DOVI_SPLIT_BL_RPU || s->mode == DOVI_SPLIT_EL_RPU;
63
64 /* Profile 7 is currently the only supported variant with EL by Dolby.
65 * Default to that in case there is no DOVI config. */
66 uint8_t dv_profile = 7;
67
68 for (int i = 0; i < ctx->par_out->nb_coded_side_data; i++) {
69 AVPacketSideData *sd = &ctx->par_out->coded_side_data[i];
71 if (sd->type != AV_PKT_DATA_DOVI_CONF)
72 continue;
74 cfg->bl_present_flag &= keep_bl;
75 cfg->el_present_flag &= keep_el;
76 cfg->rpu_present_flag &= keep_rpu;
77 dv_profile = cfg->dv_profile;
78 break;
79 }
80
81 if (keep_el) {
82 const AVPacketSideData *sd;
83 sd = av_packet_side_data_get(ctx->par_out->coded_side_data,
84 ctx->par_out->nb_coded_side_data,
86 if (sd && sd->size >= 23) {
87 uint8_t *new_ed = av_mallocz(sd->size + AV_INPUT_BUFFER_PADDING_SIZE);
88 if (!new_ed)
89 return AVERROR(ENOMEM);
90 memcpy(new_ed, sd->data, sd->size);
91 av_freep(&ctx->par_out->extradata);
92 ctx->par_out->extradata = new_ed;
93 ctx->par_out->extradata_size = sd->size;
94 }
95
96 /* DV profile to EL size ratio */
97 static const uint8_t el_div[] = {
98 [2] = 2,
99 [3] = 1,
100 [4] = 2,
101 [6] = 2,
102 [7] = 2,
103 };
104 int div = dv_profile < FF_ARRAY_ELEMS(el_div) ? el_div[dv_profile] : 0;
105 if (!div)
106 av_log(ctx, AV_LOG_WARNING, "Unexpected DV Profile %d.\n", dv_profile);
107
108 /* P7: EL is 1:1 for FHD BL */
109 if (dv_profile == 7 && ctx->par_in->width <= 1920)
110 div = 1;
111 if (div > 1) {
112 ctx->par_out->width = ctx->par_in->width / div;
113 ctx->par_out->height = ctx->par_in->height / div;
114 }
115 }
116
117 /* Drop AV_PKT_DATA_HEVC_CONF as it's no longer valid on output. It's
118 * set as extradata for EL. */
119 av_packet_side_data_remove(ctx->par_out->coded_side_data,
120 &ctx->par_out->nb_coded_side_data,
122
123 s->nal_length_size = hvcc_nal_length_size(ctx->par_in->extradata,
124 ctx->par_in->extradata_size);
125 s->out_nal_length_size = hvcc_nal_length_size(ctx->par_out->extradata,
126 ctx->par_out->extradata_size);
127
128 return 0;
129}
130
132{
133 DOVISplitContext *s = ctx->priv_data;
135}
136
137static int nal_is_kept(const DOVISplitContext *s, const H2645NAL *nal,
138 const uint8_t **payload, int *payload_size)
139{
140 bool keep_el = s->mode == DOVI_SPLIT_EL || s->mode == DOVI_SPLIT_EL_RPU;
141 bool keep_bl = s->mode == DOVI_SPLIT_BL || s->mode == DOVI_SPLIT_BL_RPU;
142 bool keep_rpu = s->mode == DOVI_SPLIT_BL_RPU || s->mode == DOVI_SPLIT_EL_RPU;
143
144 switch (nal->type) {
146 /* EL: keep only when extracting EL, strip two-bytes of outer NAL header */
147 if (!keep_el || nal->raw_size <= 2)
148 return 0;
149 *payload = nal->raw_data + 2;
150 *payload_size = nal->raw_size - 2;
151 return 1;
153 /* RPU: kept verbatim only when the selected mode opted in. */
154 if (!keep_rpu)
155 return 0;
156 *payload = nal->raw_data;
157 *payload_size = nal->raw_size;
158 return 1;
159 default:
160 /* Anything else is a base-layer NAL. */
161 if (!keep_bl)
162 return 0;
163 *payload = nal->raw_data;
164 *payload_size = nal->raw_size;
165 return 1;
166 }
167}
168
170{
171 DOVISplitContext *s = ctx->priv_data;
172 AVPacket *in = NULL;
173 AVBufferRef *out_buf = NULL;
174 uint8_t *dst;
175 size_t out_size = 0;
176 int kept_count = 0;
177 int flags = (s->nal_length_size ? H2645_FLAG_IS_NALFF : 0) |
179 int prefix_size = s->out_nal_length_size ? s->out_nal_length_size : 4;
180 int ret;
181
182 ret = ff_bsf_get_packet(ctx, &in);
183 if (ret < 0)
184 return ret;
185
186 ret = ff_h2645_packet_split(&s->pkt, in->data, in->size, ctx,
187 s->nal_length_size, AV_CODEC_ID_HEVC, flags);
188 if (ret < 0)
189 goto fail;
190
191 for (int i = 0; i < s->pkt.nb_nals; i++) {
192 const uint8_t *payload;
193 int payload_size;
194 if (!nal_is_kept(s, &s->pkt.nals[i], &payload, &payload_size))
195 continue;
196 out_size += prefix_size + payload_size;
197 kept_count++;
198 }
199
200 if (!kept_count) {
201 ret = AVERROR(EAGAIN);
202 goto fail;
203 }
204
206 if (!out_buf) {
207 ret = AVERROR(ENOMEM);
208 goto fail;
209 }
210
211 dst = out_buf->data;
212 for (int i = 0; i < s->pkt.nb_nals; i++) {
213 const uint8_t *payload;
214 int payload_size;
215 if (!nal_is_kept(s, &s->pkt.nals[i], &payload, &payload_size))
216 continue;
217 switch (s->out_nal_length_size) {
218 case 0: AV_WB32(dst, 1); break;
219 case 1: AV_WB8 (dst, payload_size); break;
220 case 2: AV_WB16(dst, payload_size); break;
221 case 3: AV_WB24(dst, payload_size); break;
222 case 4: AV_WB32(dst, payload_size); break;
223 }
224 dst += prefix_size;
225 memcpy(dst, payload, payload_size);
226 dst += payload_size;
227 }
229 av_assert0(dst == out_buf->data + out_size);
230
231 ret = av_packet_copy_props(out, in);
232 if (ret < 0)
233 goto fail;
234
235 out->buf = out_buf;
236 out->data = out_buf->data;
237 out->size = out_size;
238 out_buf = NULL;
239
240fail:
241 av_buffer_unref(&out_buf);
242 av_packet_free(&in);
243 if (ret < 0 && ret != AVERROR(EAGAIN))
245 return ret;
246}
247
248#define OFFSET(x) offsetof(DOVISplitContext, x)
249#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
250static const AVOption dovi_split_options[] = {
251 { "mode", "Which Dolby Vision components to keep in the output bitstream", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = DOVI_SPLIT_BL }, DOVI_SPLIT_BL, DOVI_SPLIT_EL_RPU, FLAGS, .unit = "mode" },
252 { "bl", "Base layer only", 0, AV_OPT_TYPE_CONST, { .i64 = DOVI_SPLIT_BL }, .flags = FLAGS, .unit = "mode" },
253 { "bl_rpu", "Base layer with the RPU NAL", 0, AV_OPT_TYPE_CONST, { .i64 = DOVI_SPLIT_BL_RPU }, .flags = FLAGS, .unit = "mode" },
254 { "el", "Enhancement layer only", 0, AV_OPT_TYPE_CONST, { .i64 = DOVI_SPLIT_EL }, .flags = FLAGS, .unit = "mode" },
255 { "el_rpu", "Enhancement layer with the RPU NAL", 0, AV_OPT_TYPE_CONST, { .i64 = DOVI_SPLIT_EL_RPU }, .flags = FLAGS, .unit = "mode" },
256 { NULL },
257};
258
259static const AVClass dovi_split_class = {
260 .class_name = "dovi_split_bsf",
261 .item_name = av_default_item_name,
262 .option = dovi_split_options,
263 .version = LIBAVUTIL_VERSION_INT,
264};
265
269
271 .p.name = "dovi_split",
272 .p.codec_ids = dovi_split_codec_ids,
273 .p.priv_class = &dovi_split_class,
274 .priv_data_size = sizeof(DOVISplitContext),
278};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
static av_cold void close(AVCodecParserContext *s)
Definition apv_parser.c:197
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
const FFBitStreamFilter ff_dovi_split_bsf
Definition dovi_split.c:270
int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt)
Called by the bitstream filters to get the next packet for filtering.
Definition bsf.c:233
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
static int FUNC nal(CodedBitstreamContext *ctx, RWContext *rw, LCEVCRawNAL *current, int nal_unit_type)
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
DOVI configuration.
static int nal_is_kept(const DOVISplitContext *s, const H2645NAL *nal, const uint8_t **payload, int *payload_size)
Definition dovi_split.c:137
static int dovi_split_init(AVBSFContext *ctx)
Definition dovi_split.c:57
static int hvcc_nal_length_size(const uint8_t *data, int size)
Definition dovi_split.c:50
static enum AVCodecID dovi_split_codec_ids[]
Definition dovi_split.c:266
DOVISplitMode
Definition dovi_split.c:34
@ DOVI_SPLIT_BL_RPU
Definition dovi_split.c:36
@ DOVI_SPLIT_BL
Definition dovi_split.c:35
@ DOVI_SPLIT_EL_RPU
Definition dovi_split.c:38
@ DOVI_SPLIT_EL
Definition dovi_split.c:37
static const AVOption dovi_split_options[]
Definition dovi_split.c:250
static const AVClass dovi_split_class
Definition dovi_split.c:259
#define OFFSET(x)
Definition dovi_split.c:248
static void dovi_split_close(AVBSFContext *ctx)
Definition dovi_split.c:131
static int dovi_split_filter(AVBSFContext *ctx, AVPacket *out)
Definition dovi_split.c:169
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
static CheckasmConfig cfg
Definition checkasm.c:74
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition codec_id.h:47
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
@ AV_CODEC_ID_HEVC
Definition codec_id.h:223
#define AV_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding.
Definition defs.h:40
void av_packet_side_data_remove(AVPacketSideData *sd, int *pnb_sd, enum AVPacketSideDataType type)
Remove side data of the given type from a side data array.
Definition packet.c:642
const AVPacketSideData * av_packet_side_data_get(const AVPacketSideData *sd, int nb_sd, enum AVPacketSideDataType type)
Get side information from a side data array.
Definition packet.c:570
@ AV_PKT_DATA_HEVC_CONF
Dolby Vision enhancement-layer HEVC decoder configuration.
Definition packet.h:384
@ AV_PKT_DATA_DOVI_CONF
DOVI configuration ref: dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2....
Definition packet.h:280
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition packet.c:74
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition packet.c:434
int av_packet_copy_props(AVPacket *dst, const AVPacket *src)
Copy only "properties" fields from src to dst.
Definition packet.c:397
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_alloc(size_t size)
Allocate an AVBuffer of the given size using av_malloc().
Definition buffer.c:77
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
@ H2645_FLAG_SMALL_PADDING
Definition h2645_parse.h:98
@ H2645_FLAG_IS_NALFF
Definition h2645_parse.h:97
#define AV_WB24(p, d)
#define AV_WB32(p, v)
#define AV_RB32(p)
#define AV_WB8(p, d)
#define AV_WB16(p, v)
#define AV_RB24(x)
void ff_h2645_packet_uninit(H2645Packet *pkt)
Free all the allocated memory in the packet.
int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, void *logctx, int nal_length_size, enum AVCodecID codec_id, int flags)
Split an input packet into NAL units.
@ HEVC_NAL_UNSPEC62
Definition hevc.h:91
@ HEVC_NAL_UNSPEC63
Definition hevc.h:92
Memory handling functions.
const char data[16]
Definition mxf.c:149
AVOptions.
#define FF_ARRAY_ELEMS(a)
The bitstream filter state.
Definition bsf.h:68
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
Describe the class of an AVClass context structure.
Definition log.h:76
AVOption.
Definition opt.h:428
This structure stores auxiliary information for decoding, presenting, or otherwise processing the cod...
Definition packet.h:424
uint8_t * data
Definition packet.h:425
enum AVPacketSideDataType type
Definition packet.h:427
This structure stores compressed data.
Definition packet.h:580
int size
Definition packet.h:604
uint8_t * data
Definition packet.h:603
H2645Packet pkt
Definition dovi_split.c:47
Definition swscale.c:71
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
void(* filter)(uint8_t *src, ptrdiff_t stride, int qscale)
Definition h263dsp.c:29
static FILE * out
Definition movenc.c:55
static int out_size
Definition movenc.c:56
static AVFormatContext * ctx
Definition movenc.c:49
int size