Mercurial > libavcodec.hg
changeset 6734:df467cebd0d3 libavcodec
Dirac decoding/encoding support via libdirac.
patch by Anuradha Suraparaju, anuradha rd.bbc.co uk
author | diego |
---|---|
date | Sat, 03 May 2008 03:47:26 +0000 |
parents | cebe9c3422a8 |
children | 459fe563a4dc |
files | Makefile allcodecs.c libdirac.h libdiracdec.c libdiracenc.c |
diffstat | 5 files changed, 628 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sat May 03 03:33:21 2008 +0000 +++ b/Makefile Sat May 03 03:47:26 2008 +0000 @@ -319,6 +319,8 @@ OBJS-$(CONFIG_LIBA52) += liba52.o OBJS-$(CONFIG_LIBAMR_NB) += libamr.o OBJS-$(CONFIG_LIBAMR_WB) += libamr.o +OBJS-$(CONFIG_LIBDIRAC_DECODER) += libdiracdec.o +OBJS-$(CONFIG_LIBDIRAC_ENCODER) += libdiracenc.o libdirac_libschro.o OBJS-$(CONFIG_LIBFAAC) += libfaac.o OBJS-$(CONFIG_LIBFAAD) += libfaad.o OBJS-$(CONFIG_LIBGSM) += libgsm.o
--- a/allcodecs.c Sat May 03 03:33:21 2008 +0000 +++ b/allcodecs.c Sat May 03 03:47:26 2008 +0000 @@ -277,6 +277,7 @@ REGISTER_DECODER (LIBA52, liba52); REGISTER_ENCDEC (LIBAMR_NB, libamr_nb); REGISTER_ENCDEC (LIBAMR_WB, libamr_wb); + REGISTER_ENCDEC (LIBDIRAC, libdirac); REGISTER_ENCODER (LIBFAAC, libfaac); REGISTER_DECODER (LIBFAAD, libfaad); REGISTER_ENCDEC (LIBGSM, libgsm);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libdirac.h Sat May 03 03:47:26 2008 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008 BBC, Anuradha Suraparaju <asuraparaju at gmail dot com > + * + * 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 libdirac.h +* data structures common to libdiracenc.c and libdiracdec.c +*/ + +#ifndef FFMPEG_LIBDIRAC_H +#define FFMPEG_LIBDIRAC_H + +#include "avcodec.h" +#include <libdirac_common/dirac_types.h> + +/** +* Table providing a Dirac chroma format to FFmpeg pixel format mapping. +*/ +static const struct { + enum PixelFormat ff_pix_fmt; + dirac_chroma_t dirac_pix_fmt; +} ffmpeg_dirac_pixel_format_map[] = { + { PIX_FMT_YUV420P, format420 }, + { PIX_FMT_YUV422P, format422 }, + { PIX_FMT_YUV444P, format444 }, +}; + +#endif /* FFMPEG_LIBDIRAC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libdiracdec.c Sat May 03 03:47:26 2008 +0000 @@ -0,0 +1,208 @@ +/* + * Dirac decoder support via libdirac library + * Copyright (c) 2005 BBC, Andrew Kennedy <dirac at rd dot bbc dot co dot uk> + * Copyright (c) 2006-2008 BBC, Anuradha Suraparaju <asuraparaju at gmail dot com > + * + * 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 libdiracdec.c +* Dirac decoder support via libdirac library; more details about the Dirac +* project can be found at http://dirac.sourceforge.net/. +* The libdirac_decoder library implements Dirac specification version 2.2 +* (http://dirac.sourceforge.net/specification.html). +*/ + +#include "libdirac.h" + +#undef NDEBUG +#include <assert.h> + +#include <libdirac_decoder/dirac_parser.h> + +/** contains a single frame returned from Dirac */ +typedef struct FfmpegDiracDecoderParams +{ + /** decoder handle */ + dirac_decoder_t* p_decoder; + + /** buffer to hold decoded frame */ + unsigned char* p_out_frame_buf; +} FfmpegDiracDecoderParams; + + +/** +* returns FFmpeg chroma format +*/ +static enum PixelFormat GetFfmpegChromaFormat(dirac_chroma_t dirac_pix_fmt) +{ + int num_formats = sizeof(ffmpeg_dirac_pixel_format_map) / + sizeof(ffmpeg_dirac_pixel_format_map[0]); + int idx; + + for (idx = 0; idx < num_formats; ++idx) { + if (ffmpeg_dirac_pixel_format_map[idx].dirac_pix_fmt == dirac_pix_fmt) { + return ffmpeg_dirac_pixel_format_map[idx].ff_pix_fmt; + } + } + return PIX_FMT_NONE; +} + +static int libdirac_decode_init(AVCodecContext *avccontext) +{ + + FfmpegDiracDecoderParams *p_dirac_params = avccontext->priv_data ; + p_dirac_params->p_decoder = dirac_decoder_init(avccontext->debug); + + if (!p_dirac_params->p_decoder) + return -1; + + return 0 ; +} + +static int libdirac_decode_frame(AVCodecContext *avccontext, + void *data, int *data_size, + const uint8_t *buf, int buf_size) +{ + + FfmpegDiracDecoderParams *p_dirac_params = avccontext->priv_data; + AVPicture *picture = data; + AVPicture pic; + int pict_size; + unsigned char *buffer[3]; + + *data_size = 0; + + if (buf_size>0) + /* set data to decode into buffer */ + dirac_buffer (p_dirac_params->p_decoder, buf, buf+buf_size); + + while (1) { + /* parse data and process result */ + DecoderState state = dirac_parse (p_dirac_params->p_decoder); + switch (state) + { + case STATE_BUFFER: + return buf_size; + + case STATE_SEQUENCE: + { + /* tell FFmpeg about sequence details */ + dirac_sourceparams_t *src_params = + &p_dirac_params->p_decoder->src_params; + + if (avcodec_check_dimensions(avccontext, src_params->width, + src_params->height) < 0) { + av_log(avccontext, AV_LOG_ERROR, "Invalid dimensions (%dx%d)\n", + src_params->width, src_params->height); + avccontext->height = avccontext->width = 0; + return -1; + } + + avccontext->height = src_params->height; + avccontext->width = src_params->width; + + avccontext->pix_fmt = GetFfmpegChromaFormat(src_params->chroma); + if (avccontext->pix_fmt == PIX_FMT_NONE) { + av_log (avccontext, AV_LOG_ERROR, + "Dirac chroma format %d not supported currently\n", + src_params->chroma); + return -1; + } + + avccontext->time_base.den = src_params->frame_rate.numerator; + avccontext->time_base.num = src_params->frame_rate.denominator; + + /* calculate output dimensions */ + avpicture_fill(&pic, NULL, avccontext->pix_fmt, + avccontext->width, avccontext->height); + + pict_size = avpicture_get_size(avccontext->pix_fmt, + avccontext->width, + avccontext->height); + + /* allocate output buffer */ + if (p_dirac_params->p_out_frame_buf == NULL) + p_dirac_params->p_out_frame_buf = av_malloc (pict_size); + buffer[0] = p_dirac_params->p_out_frame_buf; + buffer[1] = p_dirac_params->p_out_frame_buf + + pic.linesize[0] * avccontext->height; + buffer[2] = buffer[1] + + pic.linesize[1] * src_params->chroma_height; + + /* tell Dirac about output destination */ + dirac_set_buf(p_dirac_params->p_decoder, buffer, NULL); + break; + } + case STATE_SEQUENCE_END: + break; + + case STATE_PICTURE_AVAIL: + /* fill picture with current buffer data from Dirac */ + avpicture_fill(picture, p_dirac_params->p_out_frame_buf, + avccontext->pix_fmt, + avccontext->width, avccontext->height); + *data_size = sizeof(AVPicture); + return buf_size; + + case STATE_INVALID: + return -1; + + default: + break; + } + } + + return buf_size; +} + + +static int libdirac_decode_close(AVCodecContext *avccontext) +{ + FfmpegDiracDecoderParams *p_dirac_params = avccontext->priv_data; + dirac_decoder_close (p_dirac_params->p_decoder); + + av_freep(&p_dirac_params->p_out_frame_buf); + + return 0 ; +} + +static void libdirac_flush (AVCodecContext *avccontext) +{ + /* Got a seek request. We will need free memory held in the private + * context and free the current Dirac decoder handle and then open + * a new decoder handle. */ + libdirac_decode_close (avccontext); + libdirac_decode_init (avccontext); + return; +} + + + +AVCodec libdirac_decoder = { + "libdirac", + CODEC_TYPE_VIDEO, + CODEC_ID_DIRAC, + sizeof(FfmpegDiracDecoderParams), + libdirac_decode_init, + NULL, + libdirac_decode_close, + libdirac_decode_frame, + CODEC_CAP_DELAY, + .flush = libdirac_flush +} ;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libdiracenc.c Sat May 03 03:47:26 2008 +0000 @@ -0,0 +1,373 @@ +/* + * Dirac encoding support via libdirac library + * Copyright (c) 2005 BBC, Andrew Kennedy <dirac at rd dot bbc dot co dot uk> + * Copyright (c) 2006-2008 BBC, Anuradha Suraparaju <asuraparaju at gmail dot com > + * + * 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 libdiracenc.c +* Dirac encoding support via libdirac library; more details about the +* Dirac project can be found at http://dirac.sourceforge.net/. +* The libdirac_encoder library implements Dirac specification version 2.2 +* (http://dirac.sourceforge.net/specification.html). +*/ + +#include "libdirac_libschro.h" +#include "libdirac.h" + +#undef NDEBUG +#include <assert.h> + + +#include <libdirac_encoder/dirac_encoder.h> + +/** Dirac encoder private data */ +typedef struct FfmpegDiracEncoderParams +{ + /** Dirac encoder context */ + dirac_encoder_context_t enc_ctx; + + /** frame being encoded */ + AVFrame picture; + + /** frame size */ + int frame_size; + + /** Dirac encoder handle */ + dirac_encoder_t* p_encoder; + + /** input frame buffer */ + unsigned char *p_in_frame_buf; + + /** queue storing encoded frames */ + FfmpegDiracSchroQueue enc_frame_queue; + + /** end of sequence signalled by user, 0 - false, 1 - true */ + int eos_signalled; + + /** end of sequence returned by encoder, 0 - false, 1 - true */ + int eos_pulled; +} FfmpegDiracEncoderParams; + +/** +* Works out Dirac-compatible chroma format. +*/ +static dirac_chroma_t GetDiracChromaFormat(enum PixelFormat ff_pix_fmt) +{ + int num_formats = sizeof(ffmpeg_dirac_pixel_format_map) / + sizeof(ffmpeg_dirac_pixel_format_map[0]); + int idx; + + for (idx = 0; idx < num_formats; ++idx) { + if (ffmpeg_dirac_pixel_format_map[idx].ff_pix_fmt == ff_pix_fmt) { + return ffmpeg_dirac_pixel_format_map[idx].dirac_pix_fmt; + } + } + return formatNK; +} + +/** +* Dirac video preset table. Ensure that this tables matches up correctly +* with the ff_dirac_schro_video_format_info table in libdirac_libschro.c. +*/ +static const VideoFormat ff_dirac_video_formats[]={ + VIDEO_FORMAT_CUSTOM , + VIDEO_FORMAT_QSIF525 , + VIDEO_FORMAT_QCIF , + VIDEO_FORMAT_SIF525 , + VIDEO_FORMAT_CIF , + VIDEO_FORMAT_4SIF525 , + VIDEO_FORMAT_4CIF , + VIDEO_FORMAT_SD_480I60 , + VIDEO_FORMAT_SD_576I50 , + VIDEO_FORMAT_HD_720P60 , + VIDEO_FORMAT_HD_720P50 , + VIDEO_FORMAT_HD_1080I60 , + VIDEO_FORMAT_HD_1080I50 , + VIDEO_FORMAT_HD_1080P60 , + VIDEO_FORMAT_HD_1080P50 , + VIDEO_FORMAT_DIGI_CINEMA_2K24 , + VIDEO_FORMAT_DIGI_CINEMA_4K24 , +}; + +/** +* Returns the video format preset matching the input video dimensions and +* time base. +*/ +static VideoFormat GetDiracVideoFormatPreset (AVCodecContext *avccontext) +{ + unsigned int num_formats = sizeof(ff_dirac_video_formats) / + sizeof(ff_dirac_video_formats[0]); + + unsigned int idx = ff_dirac_schro_get_video_format_idx (avccontext); + + return (idx < num_formats) ? + ff_dirac_video_formats[idx] : VIDEO_FORMAT_CUSTOM; +} + +static int libdirac_encode_init(AVCodecContext *avccontext) +{ + + FfmpegDiracEncoderParams* p_dirac_params = avccontext->priv_data; + int no_local = 1; + int verbose = avccontext->debug; + VideoFormat preset; + + /* get Dirac preset */ + preset = GetDiracVideoFormatPreset(avccontext); + + /* initialize the encoder context */ + dirac_encoder_context_init (&(p_dirac_params->enc_ctx), preset); + + p_dirac_params->enc_ctx.src_params.chroma = + GetDiracChromaFormat(avccontext->pix_fmt); + + if (p_dirac_params->enc_ctx.src_params.chroma == formatNK) { + av_log (avccontext, AV_LOG_ERROR, + "Unsupported pixel format %d. This codec supports only " + "Planar YUV formats (yuv420p, yuv422p, yuv444p\n", + avccontext->pix_fmt); + return -1; + } + + p_dirac_params->enc_ctx.src_params.frame_rate.numerator = + avccontext->time_base.den; + p_dirac_params->enc_ctx.src_params.frame_rate.denominator = + avccontext->time_base.num; + + p_dirac_params->enc_ctx.src_params.width = avccontext->width; + p_dirac_params->enc_ctx.src_params.height = avccontext->height; + + p_dirac_params->frame_size = avpicture_get_size(avccontext->pix_fmt, + avccontext->width, + avccontext->height); + + avccontext->coded_frame = &p_dirac_params->picture; + + if (no_local) { + p_dirac_params->enc_ctx.decode_flag = 0; + p_dirac_params->enc_ctx.instr_flag = 0; + } else { + p_dirac_params->enc_ctx.decode_flag = 1; + p_dirac_params->enc_ctx.instr_flag = 1; + } + + /* Intra-only sequence */ + if (avccontext->gop_size == 0 ) + p_dirac_params->enc_ctx.enc_params.num_L1 = 0; + else + avccontext->has_b_frames = 1; + + if (avccontext->flags & CODEC_FLAG_QSCALE) { + if (avccontext->global_quality != 0) { + p_dirac_params->enc_ctx.enc_params.qf = + avccontext->global_quality / (FF_QP2LAMBDA*10.0); + /* if it is not default bitrate then send target rate. */ + if (avccontext->bit_rate >= 1000 && + avccontext->bit_rate != 200000) { + p_dirac_params->enc_ctx.enc_params.trate = + avccontext->bit_rate / 1000; + } + } else + p_dirac_params->enc_ctx.enc_params.lossless = 1; + } else if (avccontext->bit_rate >= 1000) + p_dirac_params->enc_ctx.enc_params.trate = avccontext->bit_rate / 1000; + + if ((preset > VIDEO_FORMAT_QCIF || preset < VIDEO_FORMAT_QSIF525) && + avccontext->bit_rate == 200000) { + p_dirac_params->enc_ctx.enc_params.trate = 0; + } + + if (avccontext->flags & CODEC_FLAG_INTERLACED_ME) { + /* all material can be coded as interlaced or progressive + * irrespective of the type of source material */ + p_dirac_params->enc_ctx.enc_params.picture_coding_mode = 1; + } + + p_dirac_params->p_encoder = dirac_encoder_init (&(p_dirac_params->enc_ctx), + verbose ); + + if (!p_dirac_params->p_encoder) { + av_log(avccontext, AV_LOG_ERROR, + "Unrecoverable Error: dirac_encoder_init failed. "); + return EXIT_FAILURE; + } + + /* allocate enough memory for the incoming data */ + p_dirac_params->p_in_frame_buf = av_malloc(p_dirac_params->frame_size); + + /* initialize the encoded frame queue */ + ff_dirac_schro_queue_init(&p_dirac_params->enc_frame_queue); + + return 0 ; +} + +static void DiracFreeFrame (void *data) +{ + FfmpegDiracSchroEncodedFrame *enc_frame = data; + + av_freep (&(enc_frame->p_encbuf)); + av_free(enc_frame); +} + +static int libdirac_encode_frame(AVCodecContext *avccontext, + unsigned char *frame, + int buf_size, void *data) +{ + int enc_size = 0; + dirac_encoder_state_t state; + FfmpegDiracEncoderParams* p_dirac_params = avccontext->priv_data; + FfmpegDiracSchroEncodedFrame* p_frame_output = NULL; + FfmpegDiracSchroEncodedFrame* p_next_output_frame = NULL; + int go = 1; + + if (data == NULL) { + /* push end of sequence if not already signalled */ + if (!p_dirac_params->eos_signalled) { + dirac_encoder_end_sequence( p_dirac_params->p_encoder ); + p_dirac_params->eos_signalled = 1; + } + } else { + + /* Allocate frame data to Dirac input buffer. + * Input line size may differ from what the codec supports, + * especially when transcoding from one format to another. + * So use avpicture_layout to copy the frame. */ + avpicture_layout ((AVPicture *)data, avccontext->pix_fmt, + avccontext->width, avccontext->height, + p_dirac_params->p_in_frame_buf, + p_dirac_params->frame_size); + + /* load next frame */ + if (dirac_encoder_load (p_dirac_params->p_encoder, + p_dirac_params->p_in_frame_buf, + p_dirac_params->frame_size ) < 0) { + av_log(avccontext, AV_LOG_ERROR, "Unrecoverable Encoder Error." + " dirac_encoder_load failed...\n"); + return -1; + } + } + + if (p_dirac_params->eos_pulled) + go = 0; + + while(go) { + p_dirac_params->p_encoder->enc_buf.buffer = frame; + p_dirac_params->p_encoder->enc_buf.size = buf_size; + /* process frame */ + state = dirac_encoder_output ( p_dirac_params->p_encoder ); + + switch (state) + { + case ENC_STATE_AVAIL: + case ENC_STATE_EOS: + assert (p_dirac_params->p_encoder->enc_buf.size > 0); + /* create output frame */ + p_frame_output = av_mallocz(sizeof(FfmpegDiracSchroEncodedFrame)); + /* set output data */ + p_frame_output->p_encbuf = + av_malloc(p_dirac_params->p_encoder->enc_buf.size); + + memcpy(p_frame_output->p_encbuf, + p_dirac_params->p_encoder->enc_buf.buffer, + p_dirac_params->p_encoder->enc_buf.size); + + p_frame_output->size = p_dirac_params->p_encoder->enc_buf.size; + + p_frame_output->frame_num = + p_dirac_params->p_encoder->enc_pparams.pnum; + + if (p_dirac_params->p_encoder->enc_pparams.ptype == INTRA_PICTURE && + p_dirac_params->p_encoder->enc_pparams.rtype == REFERENCE_PICTURE) + p_frame_output->key_frame = 1; + + ff_dirac_schro_queue_push_back (&p_dirac_params->enc_frame_queue, + p_frame_output); + + if (state == ENC_STATE_EOS) { + p_dirac_params->eos_pulled = 1; + go = 0; + } + break; + + case ENC_STATE_BUFFER: + go = 0; + break; + + case ENC_STATE_INVALID: + av_log(avccontext, AV_LOG_ERROR, + "Unrecoverable Dirac Encoder Error. Quitting...\n"); + return -1; + + default: + av_log(avccontext, AV_LOG_ERROR, "Unknown Dirac Encoder state\n"); + return -1; + } + } + + /* copy 'next' frame in queue */ + p_next_output_frame = + ff_dirac_schro_queue_pop(&p_dirac_params->enc_frame_queue); + + if (p_next_output_frame == NULL) + return 0; + + memcpy(frame, p_next_output_frame->p_encbuf, p_next_output_frame->size); + avccontext->coded_frame->key_frame = p_next_output_frame->key_frame; + /* Use the frame number of the encoded frame as the pts. It is OK to do + * so since Dirac is a constant framerate codec. It expects input to be + * of constant framerate. */ + avccontext->coded_frame->pts = p_next_output_frame->frame_num; + enc_size = p_next_output_frame->size; + + /* free frame */ + DiracFreeFrame(p_next_output_frame); + + return enc_size; +} + +static int libdirac_encode_close(AVCodecContext *avccontext) +{ + FfmpegDiracEncoderParams* p_dirac_params = avccontext->priv_data; + + /* close the encoder */ + dirac_encoder_close(p_dirac_params->p_encoder ); + + /* free data in the output frame queue */ + ff_dirac_schro_queue_free(&p_dirac_params->enc_frame_queue, + DiracFreeFrame); + + /* free the input frame buffer */ + av_freep(&p_dirac_params->p_in_frame_buf); + + return 0 ; +} + + +AVCodec libdirac_encoder = { + "libdirac", + CODEC_TYPE_VIDEO, + CODEC_ID_DIRAC, + sizeof(FfmpegDiracEncoderParams), + libdirac_encode_init, + libdirac_encode_frame, + libdirac_encode_close, + .capabilities= CODEC_CAP_DELAY, + .pix_fmts= (enum PixelFormat[]){PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_YUV444P, -1}, +} ;