[FFmpeg-devel] [PATCH 23/26] lavc/cbs: Add JPEG support

James Almer jamrial at gmail.com
Tue Apr 24 05:16:27 EEST 2018


On 4/22/2018 12:29 PM, Mark Thompson wrote:
> +static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx,
> +                                   CodedBitstreamFragment *frag,
> +                                   int header)
> +{
> +    uint8_t *unit_data;
> +    size_t unit_size;
> +    int unit, start, end, marker, next_start, next_marker;
> +    int err, i, j;
> +
> +    if (frag->data_size < 4) {
> +        // Definitely too short to be meaningful.
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++);
> +    if (i > 0) {
> +        av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at "
> +               "beginning of image.\n", i);
> +    }
> +    for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
> +    if (i + 1 >= frag->data_size && frag->data[i]) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
> +               "no SOI marker found.\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    marker = frag->data[i];
> +    if (marker != JPEG_MARKER_SOI) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first "
> +               "marker is %02x, should be SOI.\n", marker);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
> +    if (i + 1 >= frag->data_size) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
> +               "no image content found.\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    marker = frag->data[i];
> +    start  = i + 1;
> +
> +    for (unit = 0;; unit++) {
> +        if (marker == JPEG_MARKER_EOI) {
> +            break;
> +        } else if (marker == JPEG_MARKER_SOS) {
> +            for (i = start; i + 1 < frag->data_size; i++) {
> +                if (frag->data[i] != 0xff)
> +                    continue;
> +                end = i;
> +                for (++i; i + 1 < frag->data_size &&
> +                          frag->data[i] == 0xff; i++);
> +                if (i + 1 >= frag->data_size) {
> +                    next_marker = -1;
> +                } else {
> +                    if (frag->data[i] == 0x00)
> +                        continue;
> +                    next_marker = frag->data[i];
> +                    next_start  = i + 1;
> +                }
> +                break;
> +            }
> +        } else {
> +            int length;
> +            i = start;
> +            if (i + 2 > frag->data_size)
> +                break;
> +            length = frag->data[i] << 8 | frag->data[i + 1];
> +            av_log(ctx->log_ctx, AV_LOG_DEBUG, "Length = %d (i = %d)\n", length, i);
> +            if (i + length > frag->data_size)
> +                break;
> +            end = start + length;
> +
> +            i = end;
> +            if (frag->data[i] != 0xff) {
> +                next_marker = -1;
> +            } else {
> +                for (++i; i + 1 < frag->data_size &&
> +                          frag->data[i] == 0xff; i++);
> +                if (i + 1 >= frag->data_size) {
> +                    next_marker = -1;
> +                } else {
> +                    next_marker = frag->data[i];
> +                    next_start  = i + 1;
> +                }
> +            }
> +        }
> +
> +        av_log(ctx->log_ctx, AV_LOG_DEBUG, "start = %d, end = %d, marker = %02x\n",
> +               start, end, marker);
> +
> +        unit_size = end - start;
> +        unit_data = av_malloc(unit_size + AV_INPUT_BUFFER_PADDING_SIZE);
> +        if (!unit_data)
> +            return AVERROR(ENOMEM);
> +        if (marker == JPEG_MARKER_SOS) {
> +            for (i = start, j = 0; i < end; i++, j++) {
> +                if (frag->data[i] == 0xff) {
> +                    while (frag->data[i] == 0xff)
> +                        ++i;
> +                    unit_data[j] = 0xff;
> +                } else {
> +                    unit_data[j] = frag->data[i];
> +                }
> +            }
> +            unit_size = j;
> +        } else {
> +            memcpy(unit_data, frag->data + start, unit_size);

Create a new frag->data_ref reference instead in this case. There's no
need to allocate a new buffer for these units.

> +        }
> +        memset(unit_data + unit_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
> +
> +        err = ff_cbs_insert_unit_data(ctx, frag, unit, marker,
> +                                      unit_data, unit_size, NULL);
> +        if (err < 0) {
> +            av_free(&unit_data);
> +            return err;
> +        }
> +
> +        if (next_marker == -1)
> +            break;
> +        marker = next_marker;
> +        start  = next_start;
> +    }
> +
> +    return 0;
> +}

[...]

> +static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx,
> +                                       CodedBitstreamFragment *frag)
> +{
> +    const CodedBitstreamUnit *unit;
> +    uint8_t *data;
> +    size_t size, dp, sp;
> +    int i;
> +
> +    size = 4; // SOI + EOI.
> +    for (i = 0; i < frag->nb_units; i++) {
> +        unit = &frag->units[i];
> +        size += 2 + unit->data_size;
> +        if (unit->type == JPEG_MARKER_SOS) {
> +            for (sp = 0; sp < unit->data_size; sp++) {
> +                if (unit->data[sp] == 0xff)
> +                    ++size;
> +            }
> +        }
> +    }
> +
> +    frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
> +    if (!frag->data_ref)
> +        return AVERROR(ENOMEM);
> +    data = frag->data_ref->data;
> +
> +    dp = 0;
> +
> +    data[dp++] = 0xff;
> +    data[dp++] = JPEG_MARKER_SOI;
> +
> +    for (i = 0; i < frag->nb_units; i++) {
> +        unit = &frag->units[i];
> +
> +        data[dp++] = 0xff;
> +        data[dp++] = unit->type;
> +
> +        if (unit->type != JPEG_MARKER_SOS) {
> +            for (sp = 0; sp < unit->data_size; sp++)
> +                data[dp++] = unit->data[sp];

memcpy(data + dp, unit->data, unit->data_size);
dp += unit->data_size;

Will probably be faster on big units.

> +        } else {
> +            for (sp = 0; sp < unit->data_size; sp++) {
> +                if (unit->data[sp] == 0xff) {
> +                    data[dp++] = 0xff;
> +                    data[dp++] = 0x00;
> +                } else {
> +                    data[dp++] = unit->data[sp];
> +                }
> +            }
> +        }
> +    }
> +
> +    data[dp++] = 0xff;
> +    data[dp++] = JPEG_MARKER_EOI;
> +
> +    av_assert0(dp == size);
> +
> +    memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
> +    frag->data      = data;
> +    frag->data_size = size;
> +
> +    return 0;
> +}


More information about the ffmpeg-devel mailing list