summaryrefslogtreecommitdiff
path: root/libavcodec/cbs.c
diff options
context:
space:
mode:
authorMark Thompson <sw@jkqxz.net>2020-07-27 17:32:21 +0100
committerMark Thompson <sw@jkqxz.net>2020-09-02 00:00:57 +0100
commit42daac1c74169ec4373b5199932c5f3176a2e897 (patch)
tree648c634f72e60fb0d6fff3da6ea9a1ac577de23b /libavcodec/cbs.c
parent2cb152d52f28081acf3c2306557944105af8374f (diff)
cbs: Add support functions for handling unit content references
Use the unit type table to determine what we need to do to clone the internals of the unit content when making copies for refcounting or writeability. (This will still fail for units with complex content if they do not have a defined clone function.) Setup and naming from a patch by Andreas Rheinhardt <andreas.rheinhardt@gmail.com>, but with the implementation changed to use the unit type information if possible rather than requiring a codec-specific function.
Diffstat (limited to 'libavcodec/cbs.c')
-rw-r--r--libavcodec/cbs.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index 61cf8e3466..7c1aa005c2 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -871,3 +871,166 @@ int ff_cbs_alloc_unit_content2(CodedBitstreamContext *ctx,
return 0;
}
+
+static int cbs_clone_unit_content(AVBufferRef **clone_ref,
+ CodedBitstreamUnit *unit,
+ const CodedBitstreamUnitTypeDescriptor *desc)
+{
+ uint8_t *src, *copy;
+ uint8_t **src_ptr, **copy_ptr;
+ AVBufferRef **src_buf, **copy_buf;
+ int err, i;
+
+ av_assert0(unit->content);
+ src = unit->content;
+
+ copy = av_memdup(src, desc->content_size);
+ if (!copy)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < desc->nb_ref_offsets; i++) {
+ src_ptr = (uint8_t**)(src + desc->ref_offsets[i]);
+ src_buf = (AVBufferRef**)(src_ptr + 1);
+ copy_ptr = (uint8_t**)(copy + desc->ref_offsets[i]);
+ copy_buf = (AVBufferRef**)(copy_ptr + 1);
+
+ if (!*src_ptr) {
+ av_assert0(!*src_buf);
+ continue;
+ }
+ if (!*src_buf) {
+ // We can't handle a non-refcounted pointer here - we don't
+ // have enough information to handle whatever structure lies
+ // at the other end of it.
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ // src_ptr is required to point somewhere inside src_buf. If it
+ // doesn't, there is a bug somewhere.
+ av_assert0(*src_ptr >= (*src_buf)->data &&
+ *src_ptr < (*src_buf)->data + (*src_buf)->size);
+
+ *copy_buf = av_buffer_ref(*src_buf);
+ if (!*copy_buf) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ *copy_ptr = (*copy_buf)->data + (*src_ptr - (*src_buf)->data);
+ }
+
+ *clone_ref = av_buffer_create(copy, desc->content_size,
+ desc->content_free ? desc->content_free :
+ cbs_default_free_unit_content,
+ (void*)desc, 0);
+ if (!*clone_ref) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ for (--i; i >= 0; i--)
+ av_buffer_unref((AVBufferRef**)(copy + desc->ref_offsets[i]));
+ av_freep(&copy);
+ *clone_ref = NULL;
+ return err;
+}
+
+int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ const CodedBitstreamUnitTypeDescriptor *desc;
+ AVBufferRef *ref;
+ int err;
+
+ av_assert0(unit->content);
+ if (unit->content_ref) {
+ // Already refcounted, nothing to do.
+ return 0;
+ }
+
+ desc = cbs_find_unit_type_desc(ctx, unit);
+ if (!desc)
+ return AVERROR(ENOSYS);
+
+ switch (desc->content_type) {
+ case CBS_CONTENT_TYPE_POD:
+ ref = av_buffer_alloc(desc->content_size);
+ if (!ref)
+ return AVERROR(ENOMEM);
+ memcpy(ref->data, unit->content, desc->content_size);
+ err = 0;
+ break;
+
+ case CBS_CONTENT_TYPE_INTERNAL_REFS:
+ err = cbs_clone_unit_content(&ref, unit, desc);
+ break;
+
+ case CBS_CONTENT_TYPE_COMPLEX:
+ if (!desc->content_clone)
+ return AVERROR_PATCHWELCOME;
+ err = desc->content_clone(&ref, unit);
+ break;
+
+ default:
+ av_assert0(0 && "Invalid content type.");
+ }
+
+ if (err < 0)
+ return err;
+
+ unit->content_ref = ref;
+ unit->content = ref->data;
+ return 0;
+}
+
+int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ const CodedBitstreamUnitTypeDescriptor *desc;
+ AVBufferRef *ref;
+ int err;
+
+ // This can only be applied to refcounted units.
+ err = ff_cbs_make_unit_refcounted(ctx, unit);
+ if (err < 0)
+ return err;
+ av_assert0(unit->content && unit->content_ref);
+
+ if (av_buffer_is_writable(unit->content_ref))
+ return 0;
+
+ desc = cbs_find_unit_type_desc(ctx, unit);
+ if (!desc)
+ return AVERROR(ENOSYS);
+
+ switch (desc->content_type) {
+ case CBS_CONTENT_TYPE_POD:
+ err = av_buffer_make_writable(&unit->content_ref);
+ break;
+
+ case CBS_CONTENT_TYPE_INTERNAL_REFS:
+ err = cbs_clone_unit_content(&ref, unit, desc);
+ break;
+
+ case CBS_CONTENT_TYPE_COMPLEX:
+ if (!desc->content_clone)
+ return AVERROR_PATCHWELCOME;
+ err = desc->content_clone(&ref, unit);
+ break;
+
+ default:
+ av_assert0(0 && "Invalid content type.");
+ }
+ if (err < 0)
+ return err;
+
+ if (desc->content_type != CBS_CONTENT_TYPE_POD) {
+ av_buffer_unref(&unit->content_ref);
+ unit->content_ref = ref;
+ }
+ unit->content = unit->content_ref->data;
+ return 0;
+}