static int xbm_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) { AVFrame *p = data; const uint8_t *end, *ptr = avpkt->data; uint8_t *dst; int ret, linesize, i, j; end = avpkt->data + avpkt->size; while (!avctx->width || !avctx->height) { char name[256]; int number, len; ptr += strcspn(ptr, "#"); if (sscanf(ptr, "#define %255s %u", name, &number) != 2) { av_log(avctx, AV_LOG_ERROR, "Unexpected preprocessor directive\n"); return AVERROR_INVALIDDATA; } len = strlen(name); if ((len > 6) && !avctx->height && !memcmp(name + len - 7, "_height", 7)) { avctx->height = number; } else if ((len > 5) && !avctx->width && !memcmp(name + len - 6, "_width", 6)) { avctx->width = number; } else { av_log(avctx, AV_LOG_ERROR, "Unknown define '%s'\n", name); return AVERROR_INVALIDDATA; } ptr += strcspn(ptr, "\n\r") + 1; } if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; // goto start of image data ptr += strcspn(ptr, "{") + 1; linesize = (avctx->width + 7) / 8; for (i = 0; i < avctx->height; i++) { dst = p->data[0] + i * p->linesize[0]; for (j = 0; j < linesize; j++) { uint8_t val; ptr += strcspn(ptr, "x") + 1; if (ptr < end && av_isxdigit(*ptr)) { val = convert(*ptr); ptr++; if (av_isxdigit(*ptr)) val = (val << 4) + convert(*ptr); *dst++ = ff_reverse[val]; } else { av_log(avctx, AV_LOG_ERROR, "Unexpected data at '%.8s'\n", ptr); return AVERROR_INVALIDDATA; } } } p->key_frame = 1; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; return avpkt->size; }
static int xbm_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) { AVFrame *p = data; int ret, linesize, i, j; int width = 0; int height = 0; const uint8_t *end, *ptr = avpkt->data; const uint8_t *next; uint8_t *dst; avctx->pix_fmt = AV_PIX_FMT_MONOWHITE; end = avpkt->data + avpkt->size; width = parse_str_int(avpkt->data, avpkt->size, "_width"); height = parse_str_int(avpkt->data, avpkt->size, "_height"); if ((ret = ff_set_dimensions(avctx, width, height)) < 0) return ret; if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; // goto start of image data next = memchr(ptr, '{', avpkt->size); if (!next) next = memchr(ptr, '(', avpkt->size); if (!next) return AVERROR_INVALIDDATA; ptr = next + 1; linesize = (avctx->width + 7) / 8; for (i = 0; i < avctx->height; i++) { dst = p->data[0] + i * p->linesize[0]; for (j = 0; j < linesize; j++) { uint8_t val; while (ptr < end && *ptr != 'x' && *ptr != '$') ptr++; ptr ++; if (ptr < end && av_isxdigit(*ptr)) { val = convert(*ptr++); if (av_isxdigit(*ptr)) val = (val << 4) + convert(*ptr++); *dst++ = ff_reverse[val]; if (av_isxdigit(*ptr) && j+1 < linesize) { j++; val = convert(*ptr++); if (av_isxdigit(*ptr)) val = (val << 4) + convert(*ptr++); *dst++ = ff_reverse[val]; } } else { av_log(avctx, AV_LOG_ERROR, "Unexpected data at %.8s.\n", ptr); return AVERROR_INVALIDDATA; } } } p->key_frame = 1; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; return avpkt->size; }
static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) { H264MetadataContext *ctx = bsf->priv_data; AVPacket *in = NULL; CodedBitstreamFragment *au = &ctx->access_unit; int err, i, j, has_sps; H264RawAUD aud; uint8_t *displaymatrix_side_data = NULL; size_t displaymatrix_side_data_size = 0; err = ff_bsf_get_packet(bsf, &in); if (err < 0) return err; err = ff_cbs_read_packet(ctx->cbc, au, in); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); goto fail; } if (au->nb_units == 0) { av_log(bsf, AV_LOG_ERROR, "No NAL units in packet.\n"); err = AVERROR_INVALIDDATA; goto fail; } // If an AUD is present, it must be the first NAL unit. if (au->units[0].type == H264_NAL_AUD) { if (ctx->aud == REMOVE) ff_cbs_delete_unit(ctx->cbc, au, 0); } else { if (ctx->aud == INSERT) { static const int primary_pic_type_table[] = { 0x084, // 2, 7 0x0a5, // 0, 2, 5, 7 0x0e7, // 0, 1, 2, 5, 6, 7 0x210, // 4, 9 0x318, // 3, 4, 8, 9 0x294, // 2, 4, 7, 9 0x3bd, // 0, 2, 3, 4, 5, 7, 8, 9 0x3ff, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int primary_pic_type_mask = 0xff; for (i = 0; i < au->nb_units; i++) { if (au->units[i].type == H264_NAL_SLICE || au->units[i].type == H264_NAL_IDR_SLICE) { H264RawSlice *slice = au->units[i].content; for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) { if (!(primary_pic_type_table[j] & (1 << slice->header.slice_type))) primary_pic_type_mask &= ~(1 << j); } } } for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) if (primary_pic_type_mask & (1 << j)) break; if (j >= FF_ARRAY_ELEMS(primary_pic_type_table)) { av_log(bsf, AV_LOG_ERROR, "No usable primary_pic_type: " "invalid slice types?\n"); err = AVERROR_INVALIDDATA; goto fail; } aud = (H264RawAUD) { .nal_unit_header.nal_unit_type = H264_NAL_AUD, .primary_pic_type = j, }; err = ff_cbs_insert_unit_content(ctx->cbc, au, 0, H264_NAL_AUD, &aud, NULL); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); goto fail; } } } has_sps = 0; for (i = 0; i < au->nb_units; i++) { if (au->units[i].type == H264_NAL_SPS) { err = h264_metadata_update_sps(bsf, au->units[i].content); if (err < 0) goto fail; has_sps = 1; } } // Only insert the SEI in access units containing SPSs, and also // unconditionally in the first access unit we ever see. if (ctx->sei_user_data && (has_sps || !ctx->done_first_au)) { H264RawSEIPayload payload = { .payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED, }; H264RawSEIUserDataUnregistered *udu = &payload.payload.user_data_unregistered; for (i = j = 0; j < 32 && ctx->sei_user_data[i]; i++) { int c, v; c = ctx->sei_user_data[i]; if (c == '-') { continue; } else if (av_isxdigit(c)) { c = av_tolower(c); v = (c <= '9' ? c - '0' : c - 'a' + 10); } else { goto invalid_user_data; } if (i & 1) udu->uuid_iso_iec_11578[j / 2] |= v; else udu->uuid_iso_iec_11578[j / 2] = v << 4; ++j; } if (j == 32 && ctx->sei_user_data[i] == '+') { size_t len = strlen(ctx->sei_user_data + i + 1); udu->data_ref = av_buffer_alloc(len + 1); if (!udu->data_ref) { err = AVERROR(ENOMEM); goto fail; } udu->data = udu->data_ref->data; udu->data_length = len + 1; memcpy(udu->data, ctx->sei_user_data + i + 1, len + 1); err = ff_cbs_h264_add_sei_message(ctx->cbc, au, &payload); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI " "message to access unit.\n"); goto fail; } } else { invalid_user_data: av_log(bsf, AV_LOG_ERROR, "Invalid user data: " "must be \"UUID+string\".\n"); err = AVERROR(EINVAL); goto fail; } } if (ctx->delete_filler) { for (i = 0; i < au->nb_units; i++) { if (au->units[i].type == H264_NAL_FILLER_DATA) { // Filler NAL units. err = ff_cbs_delete_unit(ctx->cbc, au, i); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to delete " "filler NAL.\n"); goto fail; } --i; continue; } if (au->units[i].type == H264_NAL_SEI) { // Filler SEI messages. H264RawSEI *sei = au->units[i].content; for (j = 0; j < sei->payload_count; j++) { if (sei->payload[j].payload_type == H264_SEI_TYPE_FILLER_PAYLOAD) { err = ff_cbs_h264_delete_sei_message(ctx->cbc, au, &au->units[i], j); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to delete " "filler SEI message.\n"); goto fail; } // Renumbering might have happened, start again at // the same NAL unit position. --i; break; } } } } } if (ctx->display_orientation != PASS) { for (i = 0; i < au->nb_units; i++) { H264RawSEI *sei; if (au->units[i].type != H264_NAL_SEI) continue; sei = au->units[i].content; for (j = 0; j < sei->payload_count; j++) { H264RawSEIDisplayOrientation *disp; int32_t *matrix; if (sei->payload[j].payload_type != H264_SEI_TYPE_DISPLAY_ORIENTATION) continue; disp = &sei->payload[j].payload.display_orientation; if (ctx->display_orientation == REMOVE || ctx->display_orientation == INSERT) { err = ff_cbs_h264_delete_sei_message(ctx->cbc, au, &au->units[i], j); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to delete " "display orientation SEI message.\n"); goto fail; } --i; break; } matrix = av_mallocz(9 * sizeof(int32_t)); if (!matrix) { err = AVERROR(ENOMEM); goto fail; } av_display_rotation_set(matrix, disp->anticlockwise_rotation * 180.0 / 65536.0); av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip); // If there are multiple display orientation messages in an // access unit then ignore all but the last one. av_freep(&displaymatrix_side_data); displaymatrix_side_data = (uint8_t*)matrix; displaymatrix_side_data_size = 9 * sizeof(int32_t); } } } if (ctx->display_orientation == INSERT) { H264RawSEIPayload payload = { .payload_type = H264_SEI_TYPE_DISPLAY_ORIENTATION, }; H264RawSEIDisplayOrientation *disp = &payload.payload.display_orientation; uint8_t *data; int size; int write = 0; data = av_packet_get_side_data(in, AV_PKT_DATA_DISPLAYMATRIX, &size); if (data && size >= 9 * sizeof(int32_t)) { int32_t matrix[9]; int hflip, vflip; double angle; memcpy(matrix, data, sizeof(matrix)); hflip = vflip = 0; if (matrix[0] < 0 && matrix[4] > 0) hflip = 1; else if (matrix[0] > 0 && matrix[4] < 0) vflip = 1; av_display_matrix_flip(matrix, hflip, vflip); angle = av_display_rotation_get(matrix); if (!(angle >= -180.0 && angle <= 180.0 /* also excludes NaN */) || matrix[2] != 0 || matrix[5] != 0 || matrix[6] != 0 || matrix[7] != 0) { av_log(bsf, AV_LOG_WARNING, "Input display matrix is not " "representable in H.264 parameters.\n"); } else { disp->hor_flip = hflip; disp->ver_flip = vflip; disp->anticlockwise_rotation = (uint16_t)rint((angle >= 0.0 ? angle : angle + 360.0) * 65536.0 / 360.0); write = 1; } } if (has_sps || !ctx->done_first_au) { if (!isnan(ctx->rotate)) { disp->anticlockwise_rotation = (uint16_t)rint((ctx->rotate >= 0.0 ? ctx->rotate : ctx->rotate + 360.0) * 65536.0 / 360.0); write = 1; } if (ctx->flip) { disp->hor_flip = !!(ctx->flip & FLIP_HORIZONTAL); disp->ver_flip = !!(ctx->flip & FLIP_VERTICAL); write = 1; } } if (write) { disp->display_orientation_repetition_period = 1; err = ff_cbs_h264_add_sei_message(ctx->cbc, au, &payload); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to add display orientation " "SEI message to access unit.\n"); goto fail; } } }