FFmpeg
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"
23 #include "libavutil/intreadwrite.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/opt.h"
26 
27 #include "bsf.h"
28 #include "bsf_internal.h"
29 #include "h2645_parse.h"
30 #include "packet.h"
31 
32 #include "hevc/hevc.h"
33 
39 };
40 
41 typedef 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 
50 static 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 {
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 {
134  ff_h2645_packet_uninit(&s->pkt);
135 }
136 
137 static 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) {
145  case HEVC_NAL_UNSPEC63:
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;
152  case HEVC_NAL_UNSPEC62:
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 {
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  }
228  memset(dst, 0, AV_INPUT_BUFFER_PADDING_SIZE);
229  av_assert0(dst == out_buf->data + out_size);
230 
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 
240 fail:
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)
250 static 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 
259 static 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 
266 static const enum AVCodecID dovi_split_codec_ids[] = {
268 };
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 };
flags
const SwsFlags flags[]
Definition: swscale.c:79
av_packet_unref
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: packet.c:434
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
h2645_parse.h
dovi_split_options
static const AVOption dovi_split_options[]
Definition: dovi_split.c:250
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
bsf_internal.h
opt.h
nal_is_kept
static int nal_is_kept(const DOVISplitContext *s, const H2645NAL *nal, const uint8_t **payload, int *payload_size)
Definition: dovi_split.c:137
out
static FILE * out
Definition: movenc.c:55
DOVISplitContext
Definition: dovi_split.c:41
AVBufferRef::data
uint8_t * data
The data buffer.
Definition: buffer.h:90
DOVI_SPLIT_EL_RPU
@ DOVI_SPLIT_EL_RPU
Definition: dovi_split.c:38
ff_h2645_packet_split
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.
Definition: h2645_parse.c:527
AVBitStreamFilter::name
const char * name
Definition: bsf.h:112
out_size
static int out_size
Definition: movenc.c:56
mode
Definition: swscale.c:67
AVPacketSideData
This structure stores auxiliary information for decoding, presenting, or otherwise processing the cod...
Definition: packet.h:424
AVPacket::data
uint8_t * data
Definition: packet.h:603
AVOption
AVOption.
Definition: opt.h:429
data
const char data[16]
Definition: mxf.c:149
av_packet_side_data_remove
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:718
filter
void(* filter)(uint8_t *src, int stride, int qscale)
Definition: h263dsp.c:29
ff_bsf_get_packet
int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt)
Called by the bitstream filters to get the next packet for filtering.
Definition: bsf.c:235
av_packet_free
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition: packet.c:74
AVBSFContext
The bitstream filter state.
Definition: bsf.h:68
AV_PKT_DATA_DOVI_CONF
@ AV_PKT_DATA_DOVI_CONF
DOVI configuration ref: dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2....
Definition: packet.h:280
close
static av_cold void close(AVCodecParserContext *s)
Definition: apv_parser.c:197
AVPacketSideData::size
size_t size
Definition: packet.h:426
bsf.h
fail
#define fail()
Definition: checkasm.h:225
dovi_split_codec_ids
static enum AVCodecID dovi_split_codec_ids[]
Definition: dovi_split.c:266
ff_h2645_packet_uninit
void ff_h2645_packet_uninit(H2645Packet *pkt)
Free all the allocated memory in the packet.
Definition: h2645_parse.c:685
avassert.h
dovi_split_init
static int dovi_split_init(AVBSFContext *ctx)
Definition: dovi_split.c:57
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
DOVI_SPLIT_BL
@ DOVI_SPLIT_BL
Definition: dovi_split.c:35
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:198
HEVC_NAL_UNSPEC62
@ HEVC_NAL_UNSPEC62
Definition: hevc.h:91
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
DOVISplitContext::pkt
H2645Packet pkt
Definition: dovi_split.c:47
dovi_split_close
static void dovi_split_close(AVBSFContext *ctx)
Definition: dovi_split.c:131
AVPacketSideData::data
uint8_t * data
Definition: packet.h:425
AVDOVIDecoderConfigurationRecord::dv_profile
uint8_t dv_profile
Definition: dovi_meta.h:58
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
hevc.h
DOVISplitContext::nal_length_size
int nal_length_size
Definition: dovi_split.c:45
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
DOVI_SPLIT_BL_RPU
@ DOVI_SPLIT_BL_RPU
Definition: dovi_split.c:36
ff_dovi_split_bsf
const FFBitStreamFilter ff_dovi_split_bsf
Definition: dovi_split.c:270
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
FFBitStreamFilter
Definition: bsf_internal.h:27
av_buffer_unref
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
AV_WB16
#define AV_WB16(p, v)
Definition: intreadwrite.h:401
AVPacketSideData::type
enum AVPacketSideDataType type
Definition: packet.h:427
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:242
DOVISplitMode
DOVISplitMode
Definition: dovi_split.c:34
AVCodecID
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition: codec_id.h:49
av_packet_side_data_get
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:646
AV_WB32
#define AV_WB32(p, v)
Definition: intreadwrite.h:415
FFBitStreamFilter::p
AVBitStreamFilter p
The public AVBitStreamFilter.
Definition: bsf_internal.h:31
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts.c:579
AVPacket::size
int size
Definition: packet.h:604
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
size
int size
Definition: twinvq_data.h:10344
AV_RB32
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_RB32
Definition: bytestream.h:96
dovi_split_class
static const AVClass dovi_split_class
Definition: dovi_split.c:259
H2645NAL
Definition: h2645_parse.h:34
AV_WB24
#define AV_WB24(p, d)
Definition: intreadwrite.h:446
av_buffer_alloc
AVBufferRef * av_buffer_alloc(size_t size)
Allocate an AVBuffer of the given size using av_malloc().
Definition: buffer.c:77
av_packet_copy_props
int av_packet_copy_props(AVPacket *dst, const AVPacket *src)
Copy only "properties" fields from src to dst.
Definition: packet.c:397
AV_CODEC_ID_NONE
@ AV_CODEC_ID_NONE
Definition: codec_id.h:50
nal
static int FUNC() nal(CodedBitstreamContext *ctx, RWContext *rw, LCEVCRawNAL *current, int nal_unit_type)
Definition: cbs_lcevc_syntax_template.c:657
packet.h
hvcc_nal_length_size
static int hvcc_nal_length_size(const uint8_t *data, int size)
Definition: dovi_split.c:50
AV_CODEC_ID_HEVC
@ AV_CODEC_ID_HEVC
Definition: codec_id.h:228
DOVISplitContext::out_nal_length_size
int out_nal_length_size
Definition: dovi_split.c:46
AV_WB8
#define AV_WB8(p, d)
Definition: intreadwrite.h:392
ret
ret
Definition: filter_design.txt:187
dovi_split_filter
static int dovi_split_filter(AVBSFContext *ctx, AVPacket *out)
Definition: dovi_split.c:169
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:81
dovi_meta.h
AV_INPUT_BUFFER_PADDING_SIZE
#define AV_INPUT_BUFFER_PADDING_SIZE
Definition: defs.h:40
AVDOVIDecoderConfigurationRecord::bl_present_flag
uint8_t bl_present_flag
Definition: dovi_meta.h:62
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
AVDOVIDecoderConfigurationRecord::rpu_present_flag
uint8_t rpu_present_flag
Definition: dovi_meta.h:60
AVDOVIDecoderConfigurationRecord::el_present_flag
uint8_t el_present_flag
Definition: dovi_meta.h:61
DOVISplitContext::mode
int mode
Definition: dovi_split.c:43
DOVI_SPLIT_EL
@ DOVI_SPLIT_EL
Definition: dovi_split.c:37
mem.h
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
H2645_FLAG_IS_NALFF
@ H2645_FLAG_IS_NALFF
Definition: h2645_parse.h:97
H2645_FLAG_SMALL_PADDING
@ H2645_FLAG_SMALL_PADDING
Definition: h2645_parse.h:98
HEVC_NAL_UNSPEC63
@ HEVC_NAL_UNSPEC63
Definition: hevc.h:92
AV_PKT_DATA_HEVC_CONF
@ AV_PKT_DATA_HEVC_CONF
Dolby Vision enhancement-layer HEVC decoder configuration.
Definition: packet.h:384
AVPacket
This structure stores compressed data.
Definition: packet.h:580
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
OFFSET
#define OFFSET(x)
Definition: dovi_split.c:248
FLAGS
#define FLAGS
Definition: dovi_split.c:249
AV_RB24
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_RB24
Definition: bytestream.h:97
H2645Packet
Definition: h2645_parse.h:82
AV_OPT_TYPE_CONST
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition: opt.h:299
AVFormatContext::priv_data
void * priv_data
Format private data.
Definition: avformat.h:1342
AVDOVIDecoderConfigurationRecord
Definition: dovi_meta.h:55