X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fcbs_mpeg2.c;h=52252093b3ca9eeb838b30fb99c23fc7d559eb8f;hb=81abf90aeae6ab88e820b664df4ea011b345b725;hp=0df4234b1223b4254b3a355ae5d04a9d75840923;hpb=23ce57af3ad684363881cdd66d5724f40963a65c;p=ffmpeg diff --git a/libavcodec/cbs_mpeg2.c b/libavcodec/cbs_mpeg2.c index 0df4234b122..52252093b3c 100644 --- a/libavcodec/cbs_mpeg2.c +++ b/libavcodec/cbs_mpeg2.c @@ -41,79 +41,123 @@ #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) #define ui(width, name) \ - xui(width, name, current->name, 0) + xui(width, name, current->name, 0, MAX_UINT_BITS(width), 0) +#define uir(width, name) \ + xui(width, name, current->name, 1, MAX_UINT_BITS(width), 0) #define uis(width, name, subs, ...) \ - xui(width, name, current->name, subs, __VA_ARGS__) + xui(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define uirs(width, name, subs, ...) \ + xui(width, name, current->name, 1, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define xui(width, name, var, range_min, range_max, subs, ...) \ + xuia(width, #name, var, range_min, range_max, subs, __VA_ARGS__) +#define sis(width, name, subs, ...) \ + xsi(width, name, current->name, subs, __VA_ARGS__) + +#define marker_bit() \ + bit("marker_bit", 1) +#define bit(string, value) do { \ + av_unused uint32_t bit = value; \ + xuia(1, string, bit, value, value, 0); \ + } while (0) #define READ #define READWRITE read #define RWContext GetBitContext -#define xui(width, name, var, subs, ...) do { \ - uint32_t value = 0; \ - CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ +#define xuia(width, string, var, range_min, range_max, subs, ...) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, string, \ SUBSCRIPTS(subs, __VA_ARGS__), \ - &value, 0, (1 << width) - 1)); \ + &value, range_min, range_max)); \ var = value; \ } while (0) -#define marker_bit() do { \ - av_unused uint32_t one; \ - CHECK(ff_cbs_read_unsigned(ctx, rw, 1, "marker_bit", NULL, &one, 1, 1)); \ +#define xsi(width, name, var, subs, ...) do { \ + int32_t value; \ + CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value, \ + MIN_INT_BITS(width), \ + MAX_INT_BITS(width))); \ + var = value; \ } while (0) #define nextbits(width, compare, var) \ (get_bits_left(rw) >= width && \ (var = show_bits(rw, width)) == (compare)) +#define infer(name, value) do { \ + current->name = value; \ + } while (0) + #include "cbs_mpeg2_syntax_template.c" #undef READ #undef READWRITE #undef RWContext -#undef xui -#undef marker_bit +#undef xuia +#undef xsi #undef nextbits +#undef infer #define WRITE #define READWRITE write #define RWContext PutBitContext -#define xui(width, name, var, subs, ...) do { \ - CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ +#define xuia(width, string, var, range_min, range_max, subs, ...) do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, string, \ SUBSCRIPTS(subs, __VA_ARGS__), \ - var, 0, (1 << width) - 1)); \ + var, range_min, range_max)); \ } while (0) -#define marker_bit() do { \ - CHECK(ff_cbs_write_unsigned(ctx, rw, 1, "marker_bit", NULL, 1, 1, 1)); \ +#define xsi(width, name, var, subs, ...) do { \ + CHECK(ff_cbs_write_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), var, \ + MIN_INT_BITS(width), \ + MAX_INT_BITS(width))); \ } while (0) #define nextbits(width, compare, var) (var) +#define infer(name, value) do { \ + if (current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + "%s does not match inferred value: " \ + "%"PRId64", but should be %"PRId64".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + } \ + } while (0) + #include "cbs_mpeg2_syntax_template.c" -#undef READ +#undef WRITE #undef READWRITE #undef RWContext -#undef xui -#undef marker_bit +#undef xuia +#undef xsi #undef nextbits +#undef infer -static void cbs_mpeg2_free_user_data(void *unit, uint8_t *content) +static void cbs_mpeg2_free_picture_header(void *opaque, uint8_t *content) +{ + MPEG2RawPictureHeader *picture = (MPEG2RawPictureHeader*)content; + av_buffer_unref(&picture->extra_information_picture.extra_information_ref); + av_freep(&content); +} + +static void cbs_mpeg2_free_user_data(void *opaque, uint8_t *content) { MPEG2RawUserData *user = (MPEG2RawUserData*)content; av_buffer_unref(&user->user_data_ref); av_freep(&content); } -static void cbs_mpeg2_free_slice(void *unit, uint8_t *content) +static void cbs_mpeg2_free_slice(void *opaque, uint8_t *content) { MPEG2RawSlice *slice = (MPEG2RawSlice*)content; - av_buffer_unref(&slice->header.extra_information_ref); + av_buffer_unref(&slice->header.extra_information_slice.extra_information_ref); av_buffer_unref(&slice->data_ref); av_freep(&content); } @@ -123,41 +167,54 @@ static int cbs_mpeg2_split_fragment(CodedBitstreamContext *ctx, int header) { const uint8_t *start, *end; - uint8_t *unit_data; - uint32_t start_code = -1, next_start_code = -1; + CodedBitstreamUnitType unit_type; + uint32_t start_code = -1; size_t unit_size; - int err, i, unit_type; + int err, i, final = 0; start = avpriv_find_start_code(frag->data, frag->data + frag->data_size, &start_code); - for (i = 0;; i++) { - end = avpriv_find_start_code(start, frag->data + frag->data_size, - &next_start_code); + if (start_code >> 8 != 0x000001) { + // No start code found. + return AVERROR_INVALIDDATA; + } + for (i = 0;; i++) { unit_type = start_code & 0xff; - // The start and end pointers point at to the byte following the - // start_code_identifier in the start code that they found. - if (end == frag->data + frag->data_size) { - // We didn't find a start code, so this is the final unit. - unit_size = end - (start - 1); - } else { + if (start == frag->data + frag->data_size) { + // The last four bytes form a start code which constitutes + // a unit of its own. In this situation avpriv_find_start_code + // won't modify start_code at all so modify start_code so that + // the next unit will be treated as the last unit. + start_code = 0; + } + + end = avpriv_find_start_code(start--, frag->data + frag->data_size, + &start_code); + + // start points to the byte containing the start_code_identifier + // (may be the last byte of fragment->data); end points to the byte + // following the byte containing the start code identifier (or to + // the end of fragment->data). + if (start_code >> 8 == 0x000001) { // Unit runs from start to the beginning of the start code // pointed to by end (including any padding zeroes). - unit_size = (end - 4) - (start - 1); + unit_size = (end - 4) - start; + } else { + // We didn't find a start code, so this is the final unit. + unit_size = end - start; + final = 1; } - unit_data = (uint8_t *)start - 1; - - err = ff_cbs_insert_unit_data(ctx, frag, i, unit_type, - unit_data, unit_size, frag->data_ref); + err = ff_cbs_insert_unit_data(ctx, frag, i, unit_type, (uint8_t*)start, + unit_size, frag->data_ref); if (err < 0) return err; - if (end == frag->data + frag->data_size) + if (final) break; - start_code = next_start_code; start = end; } @@ -215,18 +272,21 @@ static int cbs_mpeg2_read_unit(CodedBitstreamContext *ctx, return err; \ } \ break; - START(0x00, MPEG2RawPictureHeader, picture_header, NULL); - START(0xb2, MPEG2RawUserData, user_data, - &cbs_mpeg2_free_user_data); - START(0xb3, MPEG2RawSequenceHeader, sequence_header, NULL); - START(0xb5, MPEG2RawExtensionData, extension_data, NULL); - START(0xb8, MPEG2RawGroupOfPicturesHeader, - group_of_pictures_header, NULL); + START(MPEG2_START_PICTURE, MPEG2RawPictureHeader, + picture_header, &cbs_mpeg2_free_picture_header); + START(MPEG2_START_USER_DATA, MPEG2RawUserData, + user_data, &cbs_mpeg2_free_user_data); + START(MPEG2_START_SEQUENCE_HEADER, MPEG2RawSequenceHeader, + sequence_header, NULL); + START(MPEG2_START_EXTENSION, MPEG2RawExtensionData, + extension_data, NULL); + START(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader, + group_of_pictures_header, NULL); + START(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd, + sequence_end, NULL); #undef START default: - av_log(ctx->log_ctx, AV_LOG_ERROR, "Unknown start code %02"PRIx32".\n", - unit->type); - return AVERROR_INVALIDDATA; + return AVERROR(ENOSYS); } } @@ -244,11 +304,13 @@ static int cbs_mpeg2_write_header(CodedBitstreamContext *ctx, case start_code: \ err = cbs_mpeg2_write_ ## func(ctx, pbc, unit->content); \ break; - START(0x00, MPEG2RawPictureHeader, picture_header); - START(0xb2, MPEG2RawUserData, user_data); - START(0xb3, MPEG2RawSequenceHeader, sequence_header); - START(0xb5, MPEG2RawExtensionData, extension_data); - START(0xb8, MPEG2RawGroupOfPicturesHeader, group_of_pictures_header); + START(MPEG2_START_PICTURE, MPEG2RawPictureHeader, picture_header); + START(MPEG2_START_USER_DATA, MPEG2RawUserData, user_data); + START(MPEG2_START_SEQUENCE_HEADER, MPEG2RawSequenceHeader, sequence_header); + START(MPEG2_START_EXTENSION, MPEG2RawExtensionData, extension_data); + START(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader, + group_of_pictures_header); + START(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd, sequence_end); #undef START default: av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for start " @@ -264,8 +326,6 @@ static int cbs_mpeg2_write_slice(CodedBitstreamContext *ctx, PutBitContext *pbc) { MPEG2RawSlice *slice = unit->content; - GetBitContext gbc; - size_t bits_left; int err; err = cbs_mpeg2_write_slice_header(ctx, pbc, &slice->header); @@ -273,21 +333,38 @@ static int cbs_mpeg2_write_slice(CodedBitstreamContext *ctx, return err; if (slice->data) { + size_t rest = slice->data_size - (slice->data_bit_start + 7) / 8; + uint8_t *pos = slice->data + slice->data_bit_start / 8; + + av_assert0(slice->data_bit_start >= 0 && + 8 * slice->data_size > slice->data_bit_start); + if (slice->data_size * 8 + 8 > put_bits_left(pbc)) return AVERROR(ENOSPC); - init_get_bits(&gbc, slice->data, slice->data_size * 8); - skip_bits_long(&gbc, slice->data_bit_start); - - while (get_bits_left(&gbc) > 15) - put_bits(pbc, 16, get_bits(&gbc, 16)); + // First copy the remaining bits of the first byte + if (slice->data_bit_start % 8) + put_bits(pbc, 8 - slice->data_bit_start % 8, + *pos++ & MAX_UINT_BITS(8 - slice->data_bit_start % 8)); + + if (put_bits_count(pbc) % 8 == 0) { + // If the writer is aligned at this point, + // memcpy can be used to improve performance. + // This is the normal case. + flush_put_bits(pbc); + memcpy(put_bits_ptr(pbc), pos, rest); + skip_put_bytes(pbc, rest); + } else { + // If not, we have to copy manually: + for (; rest > 3; rest -= 4, pos += 4) + put_bits32(pbc, AV_RB32(pos)); - bits_left = get_bits_left(&gbc); - put_bits(pbc, bits_left, get_bits(&gbc, bits_left)); + for (; rest; rest--, pos++) + put_bits(pbc, 8, *pos); - // Align with zeroes. - while (put_bits_count(pbc) % 8 != 0) - put_bits(pbc, 1, 0); + // Align with zeros + put_bits(pbc, 8 - put_bits_count(pbc) % 8, 0); + } } return 0; @@ -316,7 +393,7 @@ static int cbs_mpeg2_write_unit(CodedBitstreamContext *ctx, init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - if (unit->type >= 0x01 && unit->type <= 0xaf) + if (MPEG2_START_IS_SLICE(unit->type)) err = cbs_mpeg2_write_slice(ctx, unit, &pbc); else err = cbs_mpeg2_write_header(ctx, unit, &pbc);