From 92db95e9ca5f8249e69e5ef7e1c31c835813e764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 17 Nov 2011 11:15:27 +0200 Subject: [PATCH 1/8] tls: Use TLSv1_client_method for OpenSSL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TLSv1 is compatible with SSLv3, so this doesn't change much in terms of compatibility. By explicitly using TLSv1, OpenSSL sends the server name indication (SNI) header, which we already set using SSL_set_tlsext_host_name (earlier, this didn't have any effect). SNI allows servers to serve SSL content for different host names with separate certificates on one single port (vhosts). Signed-off-by: Martin Storsjö --- libavformat/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/tls.c b/libavformat/tls.c index 33ee782fa8..72c2b85016 100644 --- a/libavformat/tls.c +++ b/libavformat/tls.c @@ -147,7 +147,7 @@ static int tls_open(URLContext *h, const char *uri, int flags) goto fail; } #elif CONFIG_OPENSSL - c->ctx = SSL_CTX_new(SSLv3_client_method()); + c->ctx = SSL_CTX_new(TLSv1_client_method()); if (!c->ctx) { av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL)); ret = AVERROR(EIO); From e0966eb140b3569b3d6b5b5008961944ef229c06 Mon Sep 17 00:00:00 2001 From: Thierry Foucu Date: Thu, 17 Nov 2011 09:39:52 -0800 Subject: [PATCH 2/8] vp6: Fix illegal read. Found with Address Sanitizer Signed-off-by: Alex Converse --- libavcodec/vp6.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libavcodec/vp6.c b/libavcodec/vp6.c index 03024fa865..9433983be3 100644 --- a/libavcodec/vp6.c +++ b/libavcodec/vp6.c @@ -442,7 +442,8 @@ static void vp6_parse_coeff(VP56Context *s) model1 = model->coeff_dccv[pt]; model2 = model->coeff_dcct[pt][ctx]; - for (coeff_idx=0; coeff_idx<64; ) { + coeff_idx = 0; + for (;;) { if ((coeff_idx>1 && ct==0) || vp56_rac_get_prob(c, model2[0])) { /* parse a coeff */ if (vp56_rac_get_prob(c, model2[2])) { @@ -483,8 +484,10 @@ static void vp6_parse_coeff(VP56Context *s) run += vp56_rac_get_prob(c, model3[i+8]) << i; } } - - cg = vp6_coeff_groups[coeff_idx+=run]; + coeff_idx += run; + if (coeff_idx >= 64) + break; + cg = vp6_coeff_groups[coeff_idx]; model1 = model2 = model->coeff_ract[pt][ct][cg]; } From bb4b0ad83b13c3af57675e80163f3f333adef96f Mon Sep 17 00:00:00 2001 From: Alex Converse Date: Thu, 17 Nov 2011 10:06:14 -0800 Subject: [PATCH 3/8] vp5: Fix illegal read. Found with Address Sanitizer --- libavcodec/vp5.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libavcodec/vp5.c b/libavcodec/vp5.c index a1a38b0a03..56f667cb63 100644 --- a/libavcodec/vp5.c +++ b/libavcodec/vp5.c @@ -185,7 +185,8 @@ static void vp5_parse_coeff(VP56Context *s) model1 = model->coeff_dccv[pt]; model2 = model->coeff_dcct[pt][ctx]; - for (coeff_idx=0; coeff_idx<64; ) { + coeff_idx = 0; + for (;;) { if (vp56_rac_get_prob(c, model2[0])) { if (vp56_rac_get_prob(c, model2[2])) { if (vp56_rac_get_prob(c, model2[3])) { @@ -222,8 +223,11 @@ static void vp5_parse_coeff(VP56Context *s) ct = 0; s->coeff_ctx[vp56_b6to4[b]][coeff_idx] = 0; } + coeff_idx++; + if (coeff_idx >= 64) + break; - cg = vp5_coeff_groups[++coeff_idx]; + cg = vp5_coeff_groups[coeff_idx]; ctx = s->coeff_ctx[vp56_b6to4[b]][coeff_idx]; model1 = model->coeff_ract[pt][ct][cg]; model2 = cg > 2 ? model1 : model->coeff_acct[pt][ct][cg][ctx]; From 856b33f578d3ca0e17ae69273d1c77248bf61a66 Mon Sep 17 00:00:00 2001 From: Michael Bradshaw Date: Thu, 17 Nov 2011 16:57:09 -0700 Subject: [PATCH 4/8] Added support for J2K encoding with libopenjpeg Note: Some of the previous patches have had two bugs that have been fixed in this patch. Signed-off-by: Michael Bradshaw Signed-off-by: Michael Niedermayer --- Changelog | 1 + MAINTAINERS | 1 + configure | 3 +- doc/general.texi | 9 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 2 +- libavcodec/libopenjpegenc.c | 330 ++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- 8 files changed, 346 insertions(+), 3 deletions(-) create mode 100644 libavcodec/libopenjpegenc.c diff --git a/Changelog b/Changelog index a3d713ecfe..01244798e7 100644 --- a/Changelog +++ b/Changelog @@ -241,6 +241,7 @@ version 0.8: - lut, lutrgb, and lutyuv filters added - buffersink libavfilter sink added - Bump libswscale for recently reported ABI break +- New J2K encoder (via OpenJPEG) version 0.7: diff --git a/MAINTAINERS b/MAINTAINERS index 4e59b8bf69..0971e7e4a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -157,6 +157,7 @@ Codecs: libgsm.c Michel Bardiaux libdirac* David Conrad libopenjpeg.c Jaikrishnan Menon + libopenjpegenc.c Michael Bradshaw libschroedinger* David Conrad libspeexdec.c Justin Ruggles libtheoraenc.c David Conrad diff --git a/configure b/configure index 8ff3f7f4e5..2daa079b57 100755 --- a/configure +++ b/configure @@ -183,7 +183,7 @@ External library support: --enable-libmp3lame enable MP3 encoding via libmp3lame [no] --enable-libnut enable NUT (de)muxing via libnut, native (de)muxer exists [no] - --enable-libopenjpeg enable JPEG 2000 decoding via OpenJPEG [no] + --enable-libopenjpeg enable JPEG 2000 encoding/decoding via OpenJPEG [no] --enable-librtmp enable RTMP[E] support via librtmp [no] --enable-libschroedinger enable Dirac support via libschroedinger [no] --enable-libspeex enable Speex support via libspeex [no] @@ -1519,6 +1519,7 @@ libopencore_amrnb_decoder_deps="libopencore_amrnb" libopencore_amrnb_encoder_deps="libopencore_amrnb" libopencore_amrwb_decoder_deps="libopencore_amrwb" libopenjpeg_decoder_deps="libopenjpeg" +libopenjpeg_encoder_deps="libopenjpeg" libschroedinger_decoder_deps="libschroedinger" libschroedinger_encoder_deps="libschroedinger" libspeex_decoder_deps="libspeex" diff --git a/doc/general.texi b/doc/general.texi index e530e11703..1e8fc80e5e 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -15,6 +15,14 @@ FFmpeg can be hooked up with a number of external libraries to add support for more formats. None of them are used by default, their use has to be explicitly requested by passing the appropriate flags to @file{./configure}. +@section OpenJPEG + +FFmpeg can use the OpenJPEG libraries for encoding/decoding J2K videos. Go to +@url{http://www.openjpeg.org/} to get the libraries and follow the installation +instructions. To enable using OpenJPEG in FFmpeg, pass @code{--enable-libopenjpeg} to +@file{./configure}. + + @section OpenCORE AMR FFmpeg can make use of the OpenCORE libraries for AMR-NB @@ -447,6 +455,7 @@ following image formats are supported: @tab Used in the game Cyberia from Interplay. @item Interplay MVE video @tab @tab X @tab Used in Interplay .MVE files. +@item J2K @tab X @tab X @item Karl Morton's video codec @tab @tab X @tab Codec used in Worms games. @item Kega Game Video (KGV1) @tab @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e385a208b0..4f65141252 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -606,6 +606,7 @@ OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o OBJS-$(CONFIG_LIBOPENCORE_AMRWB_DECODER) += libopencore-amr.o OBJS-$(CONFIG_LIBOPENJPEG_DECODER) += libopenjpegdec.o +OBJS-$(CONFIG_LIBOPENJPEG_ENCODER) += libopenjpegenc.o OBJS-$(CONFIG_LIBSCHROEDINGER_DECODER) += libschroedingerdec.o \ libschroedinger.o \ libdirac_libschro.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index c1778136f6..88fad5e936 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -391,7 +391,7 @@ void avcodec_register_all(void) REGISTER_ENCODER (LIBMP3LAME, libmp3lame); REGISTER_ENCDEC (LIBOPENCORE_AMRNB, libopencore_amrnb); REGISTER_DECODER (LIBOPENCORE_AMRWB, libopencore_amrwb); - REGISTER_DECODER (LIBOPENJPEG, libopenjpeg); + REGISTER_ENCDEC (LIBOPENJPEG, libopenjpeg); REGISTER_ENCDEC (LIBSCHROEDINGER, libschroedinger); REGISTER_ENCDEC (LIBSPEEX, libspeex); REGISTER_DECODER (LIBSTAGEFRIGHT_H264, libstagefright_h264); diff --git a/libavcodec/libopenjpegenc.c b/libavcodec/libopenjpegenc.c new file mode 100644 index 0000000000..737cd587dd --- /dev/null +++ b/libavcodec/libopenjpegenc.c @@ -0,0 +1,330 @@ +/* + * JPEG 2000 encoding support via OpenJPEG + * Copyright (c) 2011 Michael Bradshaw + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** +* @file +* JPEG 2000 encoder using libopenjpeg +*/ + +#include "libavutil/imgutils.h" +#include "avcodec.h" +#include "libavutil/intreadwrite.h" +#define OPJ_STATIC +#include + +typedef struct { + opj_image_t *image; + opj_cparameters_t enc_params; + opj_cinfo_t *compress; + opj_event_mgr_t event_mgr; +} LibOpenJPEGContext; + +static void error_callback(const char *msg, void *data) +{ + av_log((AVCodecContext*)data, AV_LOG_ERROR, "libopenjpeg: %s\n", msg); +} + +static void warning_callback(const char *msg, void *data) +{ + av_log((AVCodecContext*)data, AV_LOG_WARNING, "libopenjpeg: %s\n", msg); +} + +static opj_image_t *mj2_create_image(AVCodecContext *avctx, opj_cparameters_t *parameters) +{ + opj_image_cmptparm_t *cmptparm; + opj_image_t *img; + int i; + int bpp; + int sub_dx[4]; + int sub_dy[4]; + int numcomps = 0; + OPJ_COLOR_SPACE color_space = CLRSPC_UNKNOWN; + + switch (avctx->pix_fmt) { + case PIX_FMT_GRAY8: + color_space = CLRSPC_GRAY; + numcomps = 1; + bpp = 8; + sub_dx[0] = 1; + sub_dy[0] = 1; + break; + case PIX_FMT_RGB24: + color_space = CLRSPC_SRGB; + numcomps = 3; + bpp = 24; + sub_dx[0] = sub_dx[1] = sub_dx[2] = 1; + sub_dy[0] = sub_dy[1] = sub_dy[2] = 1; + break; + case PIX_FMT_RGBA: + color_space = CLRSPC_SRGB; + numcomps = 4; + bpp = 32; + sub_dx[0] = sub_dx[1] = sub_dx[2] = sub_dx[3] = 1; + sub_dy[0] = sub_dy[1] = sub_dy[2] = sub_dy[3] = 1; + break; + case PIX_FMT_YUV420P: + color_space = CLRSPC_SYCC; + numcomps = 3; + bpp = 12; + sub_dx[0] = 1; + sub_dx[1] = sub_dx[2] = 2; + sub_dy[0] = 1; + sub_dy[1] = sub_dy[2] = 2; + break; + case PIX_FMT_YUV422P: + color_space = CLRSPC_SYCC; + numcomps = 3; + bpp = 16; + sub_dx[0] = 1; + sub_dx[1] = sub_dx[2] = 2; + sub_dy[0] = sub_dy[1] = sub_dy[2] = 1; + break; + case PIX_FMT_YUV440P: + color_space = CLRSPC_SYCC; + numcomps = 3; + bpp = 16; + sub_dx[0] = sub_dx[1] = sub_dx[2] = 1; + sub_dy[0] = 1; + sub_dy[1] = sub_dy[2] = 2; + break; + case PIX_FMT_YUV444P: + color_space = CLRSPC_SYCC; + numcomps = 3; + bpp = 24; + sub_dx[0] = sub_dx[1] = sub_dx[2] = 1; + sub_dy[0] = sub_dy[1] = sub_dy[2] = 1; + break; + default: + av_log(avctx, AV_LOG_ERROR, "The requested pixel format '%s' is not supported\n", av_get_pix_fmt_name(avctx->pix_fmt)); + return NULL; + } + + cmptparm = av_malloc(numcomps * sizeof(opj_image_cmptparm_t)); + if (!cmptparm) { + av_log(avctx, AV_LOG_ERROR, "Not enough memory"); + return NULL; + } + memset(cmptparm, 0, numcomps * sizeof(opj_image_cmptparm_t)); + for (i = 0; i < numcomps; i++) { + cmptparm[i].prec = 8; + cmptparm[i].bpp = bpp; + cmptparm[i].sgnd = 0; + cmptparm[i].dx = sub_dx[i]; + cmptparm[i].dy = sub_dy[i]; + cmptparm[i].w = avctx->width / sub_dx[i]; + cmptparm[i].h = avctx->height / sub_dy[i]; + } + + img = opj_image_create(numcomps, cmptparm, color_space); + av_freep(&cmptparm); + return img; +} + +static av_cold int libopenjpeg_encode_init(AVCodecContext *avctx) +{ + LibOpenJPEGContext *ctx = avctx->priv_data; + + opj_set_default_encoder_parameters(&ctx->enc_params); + ctx->enc_params.tcp_numlayers = 1; + ctx->enc_params.tcp_rates[0] = avctx->compression_level > 0 ? avctx->compression_level : 0; + ctx->enc_params.cp_disto_alloc = 1; + + ctx->compress = opj_create_compress(CODEC_J2K); + if (!ctx->compress) { + av_log(avctx, AV_LOG_ERROR, "Error creating the compressor\n"); + return AVERROR(ENOMEM); + } + + avctx->coded_frame = avcodec_alloc_frame(); + if (!avctx->coded_frame) { + av_freep(&ctx->compress); + ctx->compress = NULL; + av_log(avctx, AV_LOG_ERROR, "Error allocating coded frame\n"); + return AVERROR(ENOMEM); + } + + ctx->image = mj2_create_image(avctx, &ctx->enc_params); + if (!ctx->image) { + av_freep(&ctx->compress); + ctx->compress = NULL; + av_freep(&avctx->coded_frame); + avctx->coded_frame = NULL; + av_log(avctx, AV_LOG_ERROR, "Error creating the mj2 image\n"); + return AVERROR(EINVAL); + } + + memset(&ctx->event_mgr, 0, sizeof(opj_event_mgr_t)); + ctx->event_mgr.error_handler = error_callback; + ctx->event_mgr.warning_handler = warning_callback; + ctx->event_mgr.info_handler = NULL; + opj_set_event_mgr((opj_common_ptr)ctx->compress, &ctx->event_mgr, avctx); + + return 0; +} + +static int libopenjpeg_copy_rgba(AVCodecContext *avctx, AVFrame *frame, opj_image_t *image, int numcomps) +{ + int compno; + int x; + int y; + + if (numcomps != 1 && numcomps != 3 && numcomps != 4) { + return 0; + } + + for (compno = 0; compno < numcomps; ++compno) { + if (image->comps[compno].w > frame->linesize[0] / numcomps) { + return 0; + } + } + + for (compno = 0; compno < numcomps; ++compno) { + for (y = 0; y < avctx->height; ++y) { + for (x = 0; x < avctx->width; ++x) { + image->comps[compno].data[y * avctx->width + x] = frame->data[0][y * frame->linesize[0] + x * numcomps + compno]; + } + } + } + + return 1; +} + +static int libopenjpeg_copy_yuv(AVCodecContext *avctx, AVFrame *frame, opj_image_t *image) +{ + int compno; + int x; + int y; + int width; + int height; + const int numcomps = 3; + + for (compno = 0; compno < numcomps; ++compno) { + if (image->comps[compno].w > frame->linesize[compno]) { + return 0; + } + } + + for (compno = 0; compno < numcomps; ++compno) { + width = avctx->width / image->comps[compno].dx; + height = avctx->height / image->comps[compno].dy; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + image->comps[compno].data[y * width + x] = frame->data[compno][y * frame->linesize[compno] + x]; + } + } + } + + return 1; +} + +static int libopenjpeg_encode_frame(AVCodecContext *avctx, uint8_t *buf, int buf_size, void *data) +{ + AVFrame *frame = data; + LibOpenJPEGContext *ctx = avctx->priv_data; + opj_cinfo_t *compress = ctx->compress; + opj_image_t *image = ctx->image; + opj_cio_t *stream; + int cpyresult = 0; + int len = 0; + + // x0, y0 is the top left corner of the image + // x1, y1 is the width, height of the reference grid + image->x0 = 0; + image->y0 = 0; + image->x1 = (avctx->width - 1) * ctx->enc_params.subsampling_dx + 1; + image->y1 = (avctx->height - 1) * ctx->enc_params.subsampling_dy + 1; + + switch (avctx->pix_fmt) { + case PIX_FMT_GRAY8: + cpyresult = libopenjpeg_copy_rgba(avctx, frame, image, 1); + break; + case PIX_FMT_RGB24: + cpyresult = libopenjpeg_copy_rgba(avctx, frame, image, 3); + break; + case PIX_FMT_RGBA: + cpyresult = libopenjpeg_copy_rgba(avctx, frame, image, 4); + break; + case PIX_FMT_YUV420P: + case PIX_FMT_YUV422P: + case PIX_FMT_YUV440P: + case PIX_FMT_YUV444P: + cpyresult = libopenjpeg_copy_yuv(avctx, frame, image); + break; + default: + av_log(avctx, AV_LOG_ERROR, "The frame's pixel format '%s' is not supported\n", av_get_pix_fmt_name(avctx->pix_fmt)); + return AVERROR(EINVAL); + break; + } + + if (!cpyresult) { + av_log(avctx, AV_LOG_ERROR, "Could not copy the frame data to the internal image buffer\n"); + return -1; + } + + opj_setup_encoder(compress, &ctx->enc_params, image); + stream = opj_cio_open((opj_common_ptr)compress, NULL, 0); + if (!stream) { + av_log(avctx, AV_LOG_ERROR, "Error creating the cio stream\n"); + return AVERROR(ENOMEM); + } + + if (!opj_encode(compress, stream, image, NULL)) { + opj_cio_close(stream); + av_log(avctx, AV_LOG_ERROR, "Error during the opj encode\n"); + return -1; + } + + len = cio_tell(stream); + if (len > buf_size) { + opj_cio_close(stream); + av_log(avctx, AV_LOG_ERROR, "Error with buf_size, not large enough to hold the frame\n"); + return -1; + } + + memcpy(buf, stream->buffer, len); + opj_cio_close(stream); + return len; +} + +static av_cold int libopenjpeg_encode_close(AVCodecContext *avctx) +{ + LibOpenJPEGContext *ctx = avctx->priv_data; + + opj_destroy_compress(ctx->compress); + opj_image_destroy(ctx->image); + av_freep(&avctx->coded_frame); + return 0 ; +} + + +AVCodec ff_libopenjpeg_encoder = { + .name = "libopenjpeg", + .type = AVMEDIA_TYPE_VIDEO, + .id = CODEC_ID_JPEG2000, + .priv_data_size = sizeof(LibOpenJPEGContext), + .init = libopenjpeg_encode_init, + .encode = libopenjpeg_encode_frame, + .close = libopenjpeg_encode_close, + .decode = NULL, + .capabilities = 0, + .pix_fmts = (const enum PixelFormat[]){PIX_FMT_GRAY8,PIX_FMT_RGB24,PIX_FMT_RGBA,PIX_FMT_YUV420P,PIX_FMT_YUV422P,PIX_FMT_YUV440P,PIX_FMT_YUV444P}, + .long_name = NULL_IF_CONFIG_SMALL("OpenJPEG based JPEG 2000 encoder/decoder"), +} ; diff --git a/libavcodec/version.h b/libavcodec/version.h index b7f01e95c6..cdc983f640 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -21,7 +21,7 @@ #define AVCODEC_VERSION_H #define LIBAVCODEC_VERSION_MAJOR 53 -#define LIBAVCODEC_VERSION_MINOR 34 +#define LIBAVCODEC_VERSION_MINOR 35 #define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ From 640424e080cbff3733706321d05c32eb7c87b90b Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 18 Nov 2011 02:35:49 +0100 Subject: [PATCH 5/8] j2kdec: fix 10l typo in initializing sgnd. Signed-off-by: Michael Niedermayer --- libavcodec/j2kdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/j2kdec.c b/libavcodec/j2kdec.c index 0fb1600a6d..1f4acc53fd 100644 --- a/libavcodec/j2kdec.c +++ b/libavcodec/j2kdec.c @@ -227,7 +227,7 @@ static int get_siz(J2kDecoderContext *s) uint8_t x = bytestream_get_byte(&s->buf); s->cbps[i] = (x & 0x7f) + 1; s->precision = FFMAX(s->cbps[i], s->precision); - s->sgnd[i] = (x & 0x80) == 1; + s->sgnd[i] = !!(x & 0x80); s->cdx[i] = bytestream_get_byte(&s->buf); s->cdy[i] = bytestream_get_byte(&s->buf); } From a7e567905421dfee3609cc98bc90eb20aae176d3 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Mon, 7 Nov 2011 15:58:41 +0100 Subject: [PATCH 6/8] ffprobe: make writer_print_integer support long long int values This makes possible to use writer_print_integer for printing int64_t values. --- ffprobe.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ffprobe.c b/ffprobe.c index 8864594196..52f07e72ff 100644 --- a/ffprobe.c +++ b/ffprobe.c @@ -141,7 +141,7 @@ typedef struct Writer { void (*print_chapter_footer)(WriterContext *wctx, const char *); void (*print_section_header)(WriterContext *wctx, const char *); void (*print_section_footer)(WriterContext *wctx, const char *); - void (*print_integer) (WriterContext *wctx, const char *, int); + void (*print_integer) (WriterContext *wctx, const char *, long long int); void (*print_string) (WriterContext *wctx, const char *, const char *); void (*show_tags) (WriterContext *wctx, AVDictionary *dict); int flags; ///< a combination or WRITER_FLAG_* @@ -254,7 +254,7 @@ static inline void writer_print_section_footer(WriterContext *wctx, } static inline void writer_print_integer(WriterContext *wctx, - const char *key, int val) + const char *key, long long int val) { wctx->writer->print_integer(wctx, key, val); wctx->nb_item++; @@ -285,13 +285,10 @@ static void writer_print_time(WriterContext *wctx, const char *key, static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts) { - char buf[128]; - if (ts == AV_NOPTS_VALUE) { writer_print_string(wctx, key, "N/A", 1); } else { - snprintf(buf, sizeof(buf), "%"PRId64, ts); - writer_print_string(wctx, key, buf, 0); + writer_print_integer(wctx, key, ts); } } @@ -436,9 +433,9 @@ static void default_print_str(WriterContext *wctx, const char *key, const char * printf("%s=%s\n", key, value); } -static void default_print_int(WriterContext *wctx, const char *key, int value) +static void default_print_int(WriterContext *wctx, const char *key, long long int value) { - printf("%s=%d\n", key, value); + printf("%s=%lld\n", key, value); } static void default_show_tags(WriterContext *wctx, AVDictionary *dict) @@ -649,14 +646,14 @@ static void compact_print_str(WriterContext *wctx, const char *key, const char * value, compact->item_sep, wctx)); } -static void compact_print_int(WriterContext *wctx, const char *key, int value) +static void compact_print_int(WriterContext *wctx, const char *key, long long int value) { CompactContext *compact = wctx->priv; if (wctx->nb_item) printf("%c", compact->item_sep); if (!compact->nokey) printf("%s=", key); - printf("%d", value); + printf("%lld", value); } static void compact_show_tags(WriterContext *wctx, AVDictionary *dict) @@ -825,12 +822,12 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val json_print_item_str(wctx, key, value, INDENT); } -static void json_print_int(WriterContext *wctx, const char *key, int value) +static void json_print_int(WriterContext *wctx, const char *key, long long int value) { JSONContext *json = wctx->priv; if (wctx->nb_item) printf(",\n"); - printf(INDENT "\"%s\": %d", + printf(INDENT "\"%s\": %lld", json_escape_str(&json->buf, &json->buf_size, key, wctx), value); } From 7ddb0607f31850ea4d4e87252c8835de63cc6845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Wed, 9 Nov 2011 11:47:57 +0100 Subject: [PATCH 7/8] mxfdec: Parse IndexTableSegments and convert them into AVIndexEntry arrays Based on work by Georg Lippitsch Signed-off-by: Michael Niedermayer --- libavformat/mxfdec.c | 291 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 283 insertions(+), 8 deletions(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 5180d21f86..2ec6363740 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -137,6 +137,20 @@ typedef struct { typedef struct { UID uid; enum MXFMetadataSetType type; + int edit_unit_byte_count; + int index_sid; + int body_sid; + int slice_count; + AVRational index_edit_rate; + uint64_t index_start_position; + uint64_t index_duration; + int *slice; + int *element_delta; + int nb_delta_entries; + int *flag_entries; + uint64_t *stream_offset_entries; + uint32_t **slice_offset_entries; + int nb_index_entries; } MXFIndexTableSegment; typedef struct { @@ -167,6 +181,11 @@ typedef struct { uint8_t *local_tags; int local_tags_count; uint64_t footer_partition; + int64_t essence_offset; + int first_essence_kl_length; + int64_t first_essence_length; + KLVPacket current_klv_data; + int current_klv_index; } MXFContext; enum MXFWrappingScheme { @@ -631,15 +650,97 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size return 0; } +static int mxf_read_delta_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) +{ + int i, length; + + segment->nb_delta_entries = avio_rb32(pb); + length = avio_rb32(pb); + + if (!(segment->slice = av_calloc(segment->nb_delta_entries, sizeof(*segment->slice))) || + !(segment->element_delta = av_calloc(segment->nb_delta_entries, sizeof(*segment->element_delta)))) + return AVERROR(ENOMEM); + + for (i = 0; i < segment->nb_delta_entries; i++) { + avio_r8(pb); /* PosTableIndex */ + segment->slice[i] = avio_r8(pb); + segment->element_delta[i] = avio_rb32(pb); + } + return 0; +} + +static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) +{ + int i, j, length; + + segment->nb_index_entries = avio_rb32(pb); + length = avio_rb32(pb); + + if (!(segment->flag_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->flag_entries))) || + !(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries)))) + return AVERROR(ENOMEM); + + if (segment->slice_count && + !(segment->slice_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->slice_offset_entries)))) + return AVERROR(ENOMEM); + + for (i = 0; i < segment->nb_index_entries; i++) { + avio_rb16(pb); /* TemporalOffset and KeyFrameOffset */ + segment->flag_entries[i] = avio_r8(pb); + segment->stream_offset_entries[i] = avio_rb64(pb); + if (segment->slice_count) { + if (!(segment->slice_offset_entries[i] = av_calloc(segment->slice_count, sizeof(**segment->slice_offset_entries)))) + return AVERROR(ENOMEM); + + for (j = 0; j < segment->slice_count; j++) + segment->slice_offset_entries[i][j] = avio_rb32(pb); + } + + avio_skip(pb, length - 11 - 4 * segment->slice_count); + } + return 0; +} + static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int size, UID uid) { + MXFIndexTableSegment *segment = arg; switch(tag) { - case 0x3F05: av_dlog(NULL, "EditUnitByteCount %d\n", avio_rb32(pb)); break; - case 0x3F06: av_dlog(NULL, "IndexSID %d\n", avio_rb32(pb)); break; - case 0x3F07: av_dlog(NULL, "BodySID %d\n", avio_rb32(pb)); break; - case 0x3F0B: av_dlog(NULL, "IndexEditRate %d/%d\n", avio_rb32(pb), avio_rb32(pb)); break; - case 0x3F0C: av_dlog(NULL, "IndexStartPosition %"PRIu64"\n", avio_rb64(pb)); break; - case 0x3F0D: av_dlog(NULL, "IndexDuration %"PRIu64"\n", avio_rb64(pb)); break; + case 0x3F05: + segment->edit_unit_byte_count = avio_rb32(pb); + av_dlog(NULL, "EditUnitByteCount %d\n", segment->edit_unit_byte_count); + break; + case 0x3F06: + segment->index_sid = avio_rb32(pb); + av_dlog(NULL, "IndexSID %d\n", segment->index_sid); + break; + case 0x3F07: + segment->body_sid = avio_rb32(pb); + av_dlog(NULL, "BodySID %d\n", segment->body_sid); + break; + case 0x3F08: + segment->slice_count = avio_r8(pb); + av_dlog(NULL, "SliceCount %d\n", segment->slice_count); + break; + case 0x3F09: + av_dlog(NULL, "DeltaEntryArray found\n"); + return mxf_read_delta_entry_array(pb, segment); + case 0x3F0A: + av_dlog(NULL, "IndexEntryArray found\n"); + return mxf_read_index_entry_array(pb, segment); + case 0x3F0B: + segment->index_edit_rate.num = avio_rb32(pb); + segment->index_edit_rate.den = avio_rb32(pb); + av_dlog(NULL, "IndexEditRate %d/%d\n", segment->index_edit_rate.num, + segment->index_edit_rate.den); + break; + case 0x3F0C: + segment->index_start_position = avio_rb64(pb); + av_dlog(NULL, "IndexStartPosition %"PRId64"\n", segment->index_start_position); + break; + case 0x3F0D: + segment->index_duration = avio_rb64(pb); + av_dlog(NULL, "IndexDuration %"PRId64"\n", segment->index_duration); + break; } return 0; } @@ -776,11 +877,171 @@ static const MXFCodecUL mxf_essence_container_uls[] = { { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE }, }; +static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments) +{ + int i, j, nb_segments = 0; + MXFIndexTableSegment **unsorted_segments; + int last_body_sid = -1, last_index_sid = -1, last_index_start = -1; + + /* count number of segments, allocate arrays and copy unsorted segments */ + for (i = 0; i < mxf->metadata_sets_count; i++) + if (mxf->metadata_sets[i]->type == IndexTableSegment) + nb_segments++; + + if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) || + !(*sorted_segments = av_calloc(nb_segments, sizeof(**sorted_segments)))) { + av_free(unsorted_segments); + return AVERROR(ENOMEM); + } + + for (i = j = 0; i < mxf->metadata_sets_count; i++) + if (mxf->metadata_sets[i]->type == IndexTableSegment) + unsorted_segments[j++] = (MXFIndexTableSegment*)mxf->metadata_sets[i]; + + *nb_sorted_segments = 0; + + /* sort segments by {BodySID, IndexSID, IndexStartPosition}, remove duplicates while we're at it */ + for (i = 0; i < nb_segments; i++) { + int best = -1, best_body_sid = -1, best_index_sid = -1, best_index_start = -1; + + for (j = 0; j < nb_segments; j++) { + MXFIndexTableSegment *s = unsorted_segments[j]; + + /* Require larger BosySID, IndexSID or IndexStartPosition then the previous entry. This removes duplicates. + * We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around. + */ + if ((i == 0 || s->body_sid > last_body_sid || s->index_sid > last_index_sid || s->index_start_position > last_index_start) && + (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start)) { + best = j; + best_body_sid = s->body_sid; + best_index_sid = s->index_sid; + best_index_start = s->index_start_position; + } + } + + /* no suitable entry found -> we're done */ + if (best == -1) + break; + + (*sorted_segments)[(*nb_sorted_segments)++] = unsorted_segments[best]; + last_body_sid = best_body_sid; + last_index_sid = best_index_sid; + last_index_start = best_index_start; + } + + av_free(unsorted_segments); + + return 0; +} + +static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) +{ + int64_t accumulated_offset = 0; + int j, k, ret, nb_sorted_segments; + MXFIndexTableSegment **sorted_segments; + + if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments))) + return ret; + + for (j = 0; j < nb_sorted_segments; j++) { + int n_delta = i; + int duration, sample_duration = 1, last_sample_size = 0; + int64_t segment_size; + MXFIndexTableSegment *tableseg = sorted_segments[j]; + + /* reset accumulated_offset on BodySID change */ + if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid) + accumulated_offset = 0; + + /* HACK: How to correctly link between streams and slices? */ + if (i < st->index) + n_delta++; + if (n_delta >= tableseg->nb_delta_entries && st->index != 0) + continue; + duration = tableseg->index_duration > 0 ? tableseg->index_duration : + st->duration - st->nb_index_entries; + segment_size = tableseg->edit_unit_byte_count * duration; + /* check small EditUnitByteCount for audio */ + if (tableseg->edit_unit_byte_count && tableseg->edit_unit_byte_count < 32 + && !tableseg->index_duration) { + /* duration might be prime relative to the new sample_duration, + * which means we need to handle the last frame differently */ + sample_duration = 8192; + last_sample_size = (duration % sample_duration) * tableseg->edit_unit_byte_count; + tableseg->edit_unit_byte_count *= sample_duration; + duration /= sample_duration; + if (last_sample_size) duration++; + } + + for (k = 0; k < duration; k++) { + int64_t pos; + int size, flags = 0; + + if (k < tableseg->nb_index_entries) { + pos = tableseg->stream_offset_entries[k]; + if (n_delta < tableseg->nb_delta_entries) { + if (n_delta < tableseg->nb_delta_entries - 1) { + size = + tableseg->slice_offset_entries[k][tableseg->slice[n_delta+1]-1] + + tableseg->element_delta[n_delta+1] - + tableseg->element_delta[n_delta]; + if (tableseg->slice[n_delta] > 0) + size -= tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1]; + } else if (k < duration - 1) { + size = tableseg->stream_offset_entries[k+1] - + tableseg->stream_offset_entries[k] - + tableseg->slice_offset_entries[k][tableseg->slice[tableseg->nb_delta_entries-1]-1] - + tableseg->element_delta[tableseg->nb_delta_entries-1]; + } else + size = 0; + if (tableseg->slice[n_delta] > 0) + pos += tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1]; + pos += tableseg->element_delta[n_delta]; + } else + size = 0; + flags = !(tableseg->flag_entries[k] & 0x30) ? AVINDEX_KEYFRAME : 0; + } else { + pos = (int64_t)k * tableseg->edit_unit_byte_count + accumulated_offset; + if (n_delta < tableseg->nb_delta_entries - 1) + size = tableseg->element_delta[n_delta+1] - tableseg->element_delta[n_delta]; + else { + /* use smaller size for last sample if we should */ + if (last_sample_size && k == duration - 1) + size = last_sample_size; + else + size = tableseg->edit_unit_byte_count; + if (tableseg->nb_delta_entries) + size -= tableseg->element_delta[tableseg->nb_delta_entries-1]; + } + if (n_delta < tableseg->nb_delta_entries) + pos += tableseg->element_delta[n_delta]; + flags = AVINDEX_KEYFRAME; + } + + if (k > 0 && pos < mxf->first_essence_length && accumulated_offset == 0) + pos += mxf->first_essence_kl_length; + + pos += mxf->essence_offset; + + av_dlog(mxf->fc, "Stream %d IndexEntry %d n_Delta %d Offset %"PRIx64" Timestamp %"PRId64"\n", + st->index, st->nb_index_entries, n_delta, pos, sample_duration * st->nb_index_entries); + + if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0) + return ret; + } + accumulated_offset += segment_size; + } + + av_free(sorted_segments); + + return 0; +} + static int mxf_parse_structural_metadata(MXFContext *mxf) { MXFPackage *material_package = NULL; MXFPackage *temp_package = NULL; - int i, j, k; + int i, j, k, ret; av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count); /* TODO: handle multiple material packages (OP3x) */ @@ -954,6 +1215,9 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) av_log(mxf->fc, AV_LOG_WARNING, "only frame wrapped mappings are correctly supported\n"); st->need_parsing = AVSTREAM_PARSE_FULL; } + + if ((ret = mxf_parse_index(mxf, i, st))) + return ret; } return 0; } @@ -1081,7 +1345,8 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; - int i; + MXFIndexTableSegment *seg; + int i, j; av_freep(&mxf->packages_refs); @@ -1100,6 +1365,16 @@ static int mxf_read_close(AVFormatContext *s) case MaterialPackage: av_freep(&((MXFPackage *)mxf->metadata_sets[i])->tracks_refs); break; + case IndexTableSegment: + seg = (MXFIndexTableSegment *)mxf->metadata_sets[i]; + for (j = 0; j < seg->nb_index_entries; j++) + av_freep(&seg->slice_offset_entries[j]); + av_freep(&seg->slice); + av_freep(&seg->element_delta); + av_freep(&seg->flag_entries); + av_freep(&seg->stream_offset_entries); + av_freep(&seg->slice_offset_entries); + break; default: break; } From fd1cea6549c29c557b22021451ef6d0fe6ef2123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Fri, 11 Nov 2011 15:01:34 +0100 Subject: [PATCH 8/8] mxfdec: Add hack that adjusts the n_delta calculation when system items are present Signed-off-by: Michael Niedermayer --- libavformat/mxfdec.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 2ec6363740..9bf4183298 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -181,6 +181,7 @@ typedef struct { uint8_t *local_tags; int local_tags_count; uint64_t footer_partition; + int system_item; int64_t essence_offset; int first_essence_kl_length; int64_t first_essence_length; @@ -205,6 +206,7 @@ typedef struct { /* partial keys to match */ static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 }; static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 }; +static const uint8_t mxf_system_item_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04 }; static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 }; /* complete keys to match */ static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 }; @@ -954,7 +956,7 @@ static int mxf_parse_index(MXFContext *mxf, int i, AVStream *st) accumulated_offset = 0; /* HACK: How to correctly link between streams and slices? */ - if (i < st->index) + if (i < mxf->system_item + st->index) n_delta++; if (n_delta >= tableseg->nb_delta_entries && st->index != 0) continue; @@ -1318,6 +1320,11 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) avio_seek(s->pb, klv.offset, SEEK_SET); break; } + if (IS_KLV_KEY(klv.key, mxf_system_item_key)) { + mxf->system_item = 1; + avio_skip(s->pb, klv.length); + continue; + } for (metadata = mxf_metadata_read_table; metadata->read; metadata++) { if (IS_KLV_KEY(klv.key, metadata->key)) {