diff options
author | Anuradha Suraparaju <anuradha@rd.bbc.co.uk> | 2008-05-03 13:59:45 +0000 |
---|---|---|
committer | Diego Biurrun <diego@biurrun.de> | 2008-05-03 13:59:45 +0000 |
commit | f7cd9eed5dd26335a16baae68075e944b61f1c7b (patch) | |
tree | a76c05e53bdd47d643be0eb0b1ccfce17798ce18 /libavcodec/libschroedingerenc.c | |
parent | 34499963e3eb62d465e48ba869cb687763b522e4 (diff) | |
download | ffmpeg-f7cd9eed5dd26335a16baae68075e944b61f1c7b.tar.gz |
Add Dirac support through libschroedinger.
patch by Anuradha Suraparaju, anuradha rd.bbc.co uk
Originally committed as revision 13046 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libavcodec/libschroedingerenc.c')
-rw-r--r-- | libavcodec/libschroedingerenc.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/libavcodec/libschroedingerenc.c b/libavcodec/libschroedingerenc.c new file mode 100644 index 0000000000..b9d5bc7aff --- /dev/null +++ b/libavcodec/libschroedingerenc.c @@ -0,0 +1,387 @@ +/* + * Dirac encoder support via Schroedinger libraries + * 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 libschroedingerenc.c +* Dirac encoder support via libschroedinger-1.0 libraries. More details about +* the Schroedinger project can be found at http://www.diracvideo.org/. +* The library implements Dirac Specification Version 2.2 +* (http://dirac.sourceforge.net/specification.html). +*/ + +#undef NDEBUG +#include <assert.h> + +#include <schroedinger/schro.h> +#include <schroedinger/schrodebug.h> +#include <schroedinger/schrovideoformat.h> + +#include "avcodec.h" +#include "libdirac_libschro.h" +#include "libschroedinger.h" + + +/** libschroedinger encoder private data */ +typedef struct FfmpegSchroEncoderParams +{ + /** Schroedinger video format */ + SchroVideoFormat *format; + + /** Schroedinger frame format */ + SchroFrameFormat frame_format; + + /** frame being encoded */ + AVFrame picture; + + /** frame size */ + int frame_size; + + /** Schroedinger encoder handle*/ + SchroEncoder* encoder; + + /** queue storing encoded frames */ + FfmpegDiracSchroQueue enc_frame_queue; + + /** end of sequence signalled */ + int eos_signalled; + + /** end of sequence pulled */ + int eos_pulled; +} FfmpegSchroEncoderParams; + +/** +* Works out Schro-compatible chroma format. +*/ +static int SetSchroChromaFormat(AVCodecContext *avccontext) +{ + int num_formats = sizeof(ffmpeg_schro_pixel_format_map) / + sizeof(ffmpeg_schro_pixel_format_map[0]); + int idx; + + FfmpegSchroEncoderParams* p_schro_params = avccontext->priv_data; + + for (idx = 0; idx < num_formats; ++idx) { + if (ffmpeg_schro_pixel_format_map[idx].ff_pix_fmt == + avccontext->pix_fmt) { + p_schro_params->format->chroma_format = + ffmpeg_schro_pixel_format_map[idx].schro_pix_fmt; + return 0; + } + } + + av_log (avccontext, AV_LOG_ERROR, + "This codec currently only supports planar YUV 4:2:0, 4:2:2" + " and 4:4:4 formats.\n"); + + return -1; +} + +static int libschroedinger_encode_init(AVCodecContext *avccontext) +{ + FfmpegSchroEncoderParams* p_schro_params = avccontext->priv_data; + SchroVideoFormatEnum preset; + + /* Initialize the libraries that libschroedinger depends on. */ + schro_init(); + + /* Create an encoder object. */ + p_schro_params->encoder = schro_encoder_new(); + + if (!p_schro_params->encoder) { + av_log(avccontext, AV_LOG_ERROR, + "Unrecoverable Error: schro_encoder_new failed. "); + return -1; + } + + /* Initialize the format. */ + preset = ff_get_schro_video_format_preset(avccontext); + p_schro_params->format = + schro_encoder_get_video_format(p_schro_params->encoder); + schro_video_format_set_std_video_format (p_schro_params->format, preset); + p_schro_params->format->width = avccontext->width; + p_schro_params->format->height = avccontext->height; + + if (SetSchroChromaFormat(avccontext) == -1) + return -1; + + if (ff_get_schro_frame_format(p_schro_params->format->chroma_format, + &p_schro_params->frame_format) == -1) { + av_log (avccontext, AV_LOG_ERROR, + "This codec currently supports only planar YUV 4:2:0, 4:2:2" + " and 4:4:4 formats.\n"); + return -1; + } + + p_schro_params->format->frame_rate_numerator = avccontext->time_base.den; + p_schro_params->format->frame_rate_denominator = avccontext->time_base.num; + + p_schro_params->frame_size = avpicture_get_size(avccontext->pix_fmt, + avccontext->width, + avccontext->height); + + avccontext->coded_frame = &p_schro_params->picture; + + if (avccontext->gop_size == 0){ + schro_encoder_setting_set_double (p_schro_params->encoder, + "gop_structure", + SCHRO_ENCODER_GOP_INTRA_ONLY); + } + else { + schro_encoder_setting_set_double (p_schro_params->encoder, + "gop_structure", + SCHRO_ENCODER_GOP_BIREF); + avccontext->has_b_frames = 1; + } + + /* FIXME - Need to handle SCHRO_ENCODER_RATE_CONTROL_LOW_DELAY. */ + if (avccontext->flags & CODEC_FLAG_QSCALE) { + if (avccontext->global_quality == 0) { + /* lossless coding */ + schro_encoder_setting_set_double (p_schro_params->encoder, + "rate_control", + SCHRO_ENCODER_RATE_CONTROL_LOSSLESS); + } else { + int noise_threshold; + schro_encoder_setting_set_double (p_schro_params->encoder, + "rate_control", + SCHRO_ENCODER_RATE_CONTROL_CONSTANT_NOISE_THRESHOLD); + + noise_threshold = avccontext->global_quality/FF_QP2LAMBDA; + if (noise_threshold > 100) + noise_threshold = 100; + schro_encoder_setting_set_double (p_schro_params->encoder, + "noise_threshold", + noise_threshold); + } + } + else { + schro_encoder_setting_set_double ( p_schro_params->encoder, + "rate_control", + SCHRO_ENCODER_RATE_CONTROL_CONSTANT_BITRATE); + + schro_encoder_setting_set_double (p_schro_params->encoder, + "bitrate", + avccontext->bit_rate); + + } + + if (avccontext->flags & CODEC_FLAG_INTERLACED_ME) { + /* All material can be coded as interlaced or progressive + irrespective of the type of source material. */ + schro_encoder_setting_set_double (p_schro_params->encoder, + "interlaced_coding", 1); + } + + /* FIXME: Signal range hardcoded to 8-bit data until both libschroedinger + * and libdirac support other bit-depth data. */ + schro_video_format_set_std_signal_range(p_schro_params->format, + SCHRO_SIGNAL_RANGE_8BIT_VIDEO); + + + /* Hardcode motion vector precision to quarter pixel. */ + schro_encoder_setting_set_double (p_schro_params->encoder, + "mv_precision", 2); + + /* Set the encoder format. */ + schro_encoder_set_video_format(p_schro_params->encoder, + p_schro_params->format); + + /* Set the debug level. */ + schro_debug_set_level (avccontext->debug); + + schro_encoder_start (p_schro_params->encoder); + + /* Initialize the encoded frame queue. */ + ff_dirac_schro_queue_init (&p_schro_params->enc_frame_queue); + return 0 ; +} + +static SchroFrame *libschroedinger_frame_from_data (AVCodecContext *avccontext, + void *in_data) +{ + FfmpegSchroEncoderParams* p_schro_params = avccontext->priv_data; + SchroFrame *in_frame; + /* 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. */ + in_frame = schro_frame_new_and_alloc (NULL, + p_schro_params->frame_format, + p_schro_params->format->width, + p_schro_params->format->height); + + avpicture_layout ((AVPicture *)in_data, avccontext->pix_fmt, + avccontext->width, avccontext->height, + in_frame->components[0].data, + p_schro_params->frame_size); + + return in_frame; +} + +static void SchroedingerFreeFrame(void *data) +{ + FfmpegDiracSchroEncodedFrame *enc_frame = data; + + av_freep (&(enc_frame->p_encbuf)); + av_free(enc_frame); +} + +static int libschroedinger_encode_frame(AVCodecContext *avccontext, + unsigned char *frame, + int buf_size, void *data) +{ + int enc_size = 0; + FfmpegSchroEncoderParams* p_schro_params = avccontext->priv_data; + SchroEncoder *encoder = p_schro_params->encoder; + struct FfmpegDiracSchroEncodedFrame* p_frame_output = NULL; + int go = 1; + SchroBuffer *enc_buf; + int presentation_frame; + int parse_code; + + if(data == NULL) { + /* Push end of sequence if not already signalled. */ + if (!p_schro_params->eos_signalled) { + schro_encoder_end_of_stream(encoder); + p_schro_params->eos_signalled = 1; + } + } else { + /* Allocate frame data to schro input buffer. */ + SchroFrame *in_frame = libschroedinger_frame_from_data (avccontext, + data); + /* Load next frame. */ + schro_encoder_push_frame(encoder, in_frame); + } + + if (p_schro_params->eos_pulled) + go = 0; + + /* Now check to see if we have any output from the encoder. */ + while (go) { + SchroStateEnum state; + state = schro_encoder_wait(encoder); + switch (state) + { + case SCHRO_STATE_HAVE_BUFFER: + case SCHRO_STATE_END_OF_STREAM: + enc_buf = schro_encoder_pull (encoder, + &presentation_frame); + assert (enc_buf->length > 0); + assert (enc_buf->length <= buf_size); + + /* Create output frame. */ + p_frame_output = av_mallocz(sizeof(FfmpegDiracSchroEncodedFrame)); + /* Set output data. */ + p_frame_output->size = enc_buf->length; + p_frame_output->p_encbuf = av_malloc(enc_buf->length); + memcpy(p_frame_output->p_encbuf, enc_buf->data, enc_buf->length); + + parse_code = enc_buf->data[4]; + if (SCHRO_PARSE_CODE_IS_INTRA(parse_code) && + SCHRO_PARSE_CODE_IS_REFERENCE(parse_code)) { + p_frame_output->key_frame = 1; + } + + /* Parse the coded frame number from the bitstream. Bytes 14 + * through 17 represesent the frame number. */ + if (SCHRO_PARSE_CODE_IS_PICTURE(parse_code)) + { + assert (enc_buf->length >= 17); + p_frame_output->frame_num = (enc_buf->data[13] << 24) + + (enc_buf->data[14] << 16) + + (enc_buf->data[15] << 8) + + enc_buf->data[16]; + } + + ff_dirac_schro_queue_push_back (&p_schro_params->enc_frame_queue, + p_frame_output); + schro_buffer_unref (enc_buf); + + if (state == SCHRO_STATE_END_OF_STREAM) { + p_schro_params->eos_pulled = 1; + go = 0; + } + break; + + case SCHRO_STATE_NEED_FRAME: + go = 0; + break; + + case SCHRO_STATE_AGAIN: + break; + + default: + av_log(avccontext, AV_LOG_ERROR, "Unknown Schro Encoder state\n"); + return -1; + } + } + + /* Copy 'next' frame in queue. */ + p_frame_output = + ff_dirac_schro_queue_pop (&p_schro_params->enc_frame_queue); + + if (p_frame_output == NULL) + return 0; + + memcpy(frame, p_frame_output->p_encbuf, p_frame_output->size); + avccontext->coded_frame->key_frame = p_frame_output->key_frame; + /* Use the frame number of the encoded frame as the pts. It is OK to + * do so since Dirac is a constant frame rate codec. It expects input + * to be of constant frame rate. */ + avccontext->coded_frame->pts = p_frame_output->frame_num; + enc_size = p_frame_output->size; + + /* free frame */ + SchroedingerFreeFrame (p_frame_output); + + return enc_size; +} + + +static int libschroedinger_encode_close(AVCodecContext *avccontext) +{ + + FfmpegSchroEncoderParams* p_schro_params = avccontext->priv_data; + + /* Close the encoder. */ + schro_encoder_free(p_schro_params->encoder); + + /* Free data in the output frame queue. */ + ff_dirac_schro_queue_free (&p_schro_params->enc_frame_queue, + SchroedingerFreeFrame); + + /* Free the video format structure. */ + av_freep(&p_schro_params->format); + + return 0 ; +} + + +AVCodec libschroedinger_encoder = { + "libschroedinger", + CODEC_TYPE_VIDEO, + CODEC_ID_DIRAC, + sizeof(FfmpegSchroEncoderParams), + libschroedinger_encode_init, + libschroedinger_encode_frame, + libschroedinger_encode_close, + .capabilities= CODEC_CAP_DELAY, + .pix_fmts= (enum PixelFormat[]){PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_YUV444P, -1}, +}; |