Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

397 lines (330 sloc) 13.899 kb
/*
* Dirac decoder 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
* Dirac decoder 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).
*/
#include <string.h>
#include "libavutil/imgutils.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "avcodec.h"
#include "libschroedinger.h"
#include <schroedinger/schro.h>
#include <schroedinger/schrodebug.h>
#include <schroedinger/schrovideoformat.h>
/** SchroFrame and Pts relation */
typedef struct LibSchroFrameContext {
SchroFrame *frame;
int64_t pts;
} LibSchroFrameContext;
/** libschroedinger decoder private data */
typedef struct SchroDecoderParams {
/** Schroedinger video format */
SchroVideoFormat *format;
/** Schroedinger frame format */
SchroFrameFormat frame_format;
/** decoder handle */
SchroDecoder* decoder;
/** queue storing decoded frames */
FFSchroQueue dec_frame_queue;
/** end of sequence signalled */
int eos_signalled;
/** end of sequence pulled */
int eos_pulled;
/** decoded picture */
AVFrame dec_frame;
} SchroDecoderParams;
typedef struct SchroParseUnitContext {
const uint8_t *buf;
int buf_size;
} SchroParseUnitContext;
static void libschroedinger_decode_buffer_free(SchroBuffer *schro_buf,
void *priv)
{
av_freep(&priv);
}
static void parse_context_init(SchroParseUnitContext *parse_ctx,
const uint8_t *buf, int buf_size)
{
parse_ctx->buf = buf;
parse_ctx->buf_size = buf_size;
}
static SchroBuffer *find_next_parse_unit(SchroParseUnitContext *parse_ctx)
{
SchroBuffer *enc_buf = NULL;
int next_pu_offset = 0;
unsigned char *in_buf;
if (parse_ctx->buf_size < 13 ||
parse_ctx->buf[0] != 'B' ||
parse_ctx->buf[1] != 'B' ||
parse_ctx->buf[2] != 'C' ||
parse_ctx->buf[3] != 'D')
return NULL;
next_pu_offset = (parse_ctx->buf[5] << 24) +
(parse_ctx->buf[6] << 16) +
(parse_ctx->buf[7] << 8) +
parse_ctx->buf[8];
if (next_pu_offset == 0 &&
SCHRO_PARSE_CODE_IS_END_OF_SEQUENCE(parse_ctx->buf[4]))
next_pu_offset = 13;
if (next_pu_offset <= 0 || parse_ctx->buf_size < next_pu_offset)
return NULL;
in_buf = av_malloc(next_pu_offset);
if (!in_buf) {
av_log(parse_ctx, AV_LOG_ERROR, "Unable to allocate input buffer\n");
return NULL;
}
memcpy(in_buf, parse_ctx->buf, next_pu_offset);
enc_buf = schro_buffer_new_with_data(in_buf, next_pu_offset);
enc_buf->free = libschroedinger_decode_buffer_free;
enc_buf->priv = in_buf;
parse_ctx->buf += next_pu_offset;
parse_ctx->buf_size -= next_pu_offset;
return enc_buf;
}
/**
* Returns FFmpeg chroma format.
*/
static enum PixelFormat get_chroma_format(SchroChromaFormat schro_pix_fmt)
{
int num_formats = sizeof(schro_pixel_format_map) /
sizeof(schro_pixel_format_map[0]);
int idx;
for (idx = 0; idx < num_formats; ++idx)
if (schro_pixel_format_map[idx].schro_pix_fmt == schro_pix_fmt)
return schro_pixel_format_map[idx].ff_pix_fmt;
return PIX_FMT_NONE;
}
static av_cold int libschroedinger_decode_init(AVCodecContext *avccontext)
{
SchroDecoderParams *p_schro_params = avccontext->priv_data;
/* First of all, initialize our supporting libraries. */
schro_init();
schro_debug_set_level(avccontext->debug);
p_schro_params->decoder = schro_decoder_new();
schro_decoder_set_skip_ratio(p_schro_params->decoder, 1);
if (!p_schro_params->decoder)
return -1;
/* Initialize the decoded frame queue. */
ff_schro_queue_init(&p_schro_params->dec_frame_queue);
return 0;
}
static void libschroedinger_decode_frame_free(void *frame)
{
schro_frame_unref(frame);
}
static void libschroedinger_handle_first_access_unit(AVCodecContext *avccontext)
{
SchroDecoderParams *p_schro_params = avccontext->priv_data;
SchroDecoder *decoder = p_schro_params->decoder;
p_schro_params->format = schro_decoder_get_video_format(decoder);
/* Tell FFmpeg about sequence details. */
if (av_image_check_size(p_schro_params->format->width,
p_schro_params->format->height, 0, avccontext) < 0) {
av_log(avccontext, AV_LOG_ERROR, "invalid dimensions (%dx%d)\n",
p_schro_params->format->width, p_schro_params->format->height);
avccontext->height = avccontext->width = 0;
return;
}
avccontext->height = p_schro_params->format->height;
avccontext->width = p_schro_params->format->width;
avccontext->pix_fmt = get_chroma_format(p_schro_params->format->chroma_format);
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 only supports planar YUV 4:2:0, 4:2:2 "
"and 4:4:4 formats.\n");
return;
}
avccontext->time_base.den = p_schro_params->format->frame_rate_numerator;
avccontext->time_base.num = p_schro_params->format->frame_rate_denominator;
}
static int libschroedinger_decode_frame(AVCodecContext *avccontext,
void *data, int *data_size,
AVPacket *avpkt)
{
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size;
int64_t pts = avpkt->pts;
SchroTag *tag;
SchroDecoderParams *p_schro_params = avccontext->priv_data;
SchroDecoder *decoder = p_schro_params->decoder;
SchroBuffer *enc_buf;
SchroFrame* frame;
int state;
int go = 1;
int outer = 1;
SchroParseUnitContext parse_ctx;
LibSchroFrameContext *framewithpts = NULL;
*data_size = 0;
parse_context_init(&parse_ctx, buf, buf_size);
if (!buf_size) {
if (!p_schro_params->eos_signalled) {
state = schro_decoder_push_end_of_stream(decoder);
p_schro_params->eos_signalled = 1;
}
}
/* Loop through all the individual parse units in the input buffer */
do {
if ((enc_buf = find_next_parse_unit(&parse_ctx))) {
/* Set Schrotag with the pts to be recovered after decoding*/
enc_buf->tag = schro_tag_new(av_malloc(sizeof(int64_t)), av_free);
if (!enc_buf->tag->value) {
av_log(avccontext, AV_LOG_ERROR, "Unable to allocate SchroTag\n");
return AVERROR(ENOMEM);
}
AV_WN(64, enc_buf->tag->value, pts);
/* Push buffer into decoder. */
if (SCHRO_PARSE_CODE_IS_PICTURE(enc_buf->data[4]) &&
SCHRO_PARSE_CODE_NUM_REFS(enc_buf->data[4]) > 0)
avccontext->has_b_frames = 1;
state = schro_decoder_push(decoder, enc_buf);
if (state == SCHRO_DECODER_FIRST_ACCESS_UNIT)
libschroedinger_handle_first_access_unit(avccontext);
go = 1;
} else
outer = 0;
while (go) {
/* Parse data and process result. */
state = schro_decoder_wait(decoder);
switch (state) {
case SCHRO_DECODER_FIRST_ACCESS_UNIT:
libschroedinger_handle_first_access_unit(avccontext);
break;
case SCHRO_DECODER_NEED_BITS:
/* Need more input data - stop iterating over what we have. */
go = 0;
break;
case SCHRO_DECODER_NEED_FRAME:
/* Decoder needs a frame - create one and push it in. */
frame = ff_create_schro_frame(avccontext,
p_schro_params->frame_format);
schro_decoder_add_output_picture(decoder, frame);
break;
case SCHRO_DECODER_OK:
/* Pull a frame out of the decoder. */
tag = schro_decoder_get_picture_tag(decoder);
frame = schro_decoder_pull(decoder);
if (frame) {
/* Add relation between schroframe and pts. */
framewithpts = av_malloc(sizeof(LibSchroFrameContext));
if (!framewithpts) {
av_log(avccontext, AV_LOG_ERROR, "Unable to allocate FrameWithPts\n");
return AVERROR(ENOMEM);
}
framewithpts->frame = frame;
framewithpts->pts = AV_RN64(tag->value);
ff_schro_queue_push_back(&p_schro_params->dec_frame_queue,
framewithpts);
}
break;
case SCHRO_DECODER_EOS:
go = 0;
p_schro_params->eos_pulled = 1;
schro_decoder_reset(decoder);
outer = 0;
break;
case SCHRO_DECODER_ERROR:
return -1;
break;
}
}
} while (outer);
/* Grab next frame to be returned from the top of the queue. */
framewithpts = ff_schro_queue_pop(&p_schro_params->dec_frame_queue);
if (framewithpts && framewithpts->frame) {
if (p_schro_params->dec_frame.data[0])
avccontext->release_buffer(avccontext, &p_schro_params->dec_frame);
if (avccontext->get_buffer(avccontext, &p_schro_params->dec_frame) < 0) {
av_log(avccontext, AV_LOG_ERROR, "Unable to allocate buffer\n");
return AVERROR(ENOMEM);
}
memcpy(p_schro_params->dec_frame.data[0],
framewithpts->frame->components[0].data,
framewithpts->frame->components[0].length);
memcpy(p_schro_params->dec_frame.data[1],
framewithpts->frame->components[1].data,
framewithpts->frame->components[1].length);
memcpy(p_schro_params->dec_frame.data[2],
framewithpts->frame->components[2].data,
framewithpts->frame->components[2].length);
/* Fill frame with current buffer data from Schroedinger. */
p_schro_params->dec_frame.format = -1; /* Unknown -1 */
p_schro_params->dec_frame.width = framewithpts->frame->width;
p_schro_params->dec_frame.height = framewithpts->frame->height;
p_schro_params->dec_frame.pkt_pts = framewithpts->pts;
p_schro_params->dec_frame.linesize[0] = framewithpts->frame->components[0].stride;
p_schro_params->dec_frame.linesize[1] = framewithpts->frame->components[1].stride;
p_schro_params->dec_frame.linesize[2] = framewithpts->frame->components[2].stride;
*(AVFrame*)data = p_schro_params->dec_frame;
*data_size = sizeof(AVFrame);
/* Now free the frame resources. */
libschroedinger_decode_frame_free(framewithpts->frame);
av_free(framewithpts);
} else {
data = NULL;
*data_size = 0;
}
return buf_size;
}
static av_cold int libschroedinger_decode_close(AVCodecContext *avccontext)
{
SchroDecoderParams *p_schro_params = avccontext->priv_data;
/* Free the decoder. */
schro_decoder_free(p_schro_params->decoder);
av_freep(&p_schro_params->format);
if (p_schro_params->dec_frame.data[0])
avccontext->release_buffer(avccontext, &p_schro_params->dec_frame);
/* Free data in the output frame queue. */
ff_schro_queue_free(&p_schro_params->dec_frame_queue,
libschroedinger_decode_frame_free);
return 0;
}
static void libschroedinger_flush(AVCodecContext *avccontext)
{
/* Got a seek request. Free the decoded frames queue and then reset
* the decoder */
SchroDecoderParams *p_schro_params = avccontext->priv_data;
/* Free data in the output frame queue. */
ff_schro_queue_free(&p_schro_params->dec_frame_queue,
libschroedinger_decode_frame_free);
ff_schro_queue_init(&p_schro_params->dec_frame_queue);
schro_decoder_reset(p_schro_params->decoder);
p_schro_params->eos_pulled = 0;
p_schro_params->eos_signalled = 0;
}
AVCodec ff_libschroedinger_decoder = {
.name = "libschroedinger",
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_DIRAC,
.priv_data_size = sizeof(SchroDecoderParams),
.init = libschroedinger_decode_init,
.close = libschroedinger_decode_close,
.decode = libschroedinger_decode_frame,
.capabilities = CODEC_CAP_DELAY,
.flush = libschroedinger_flush,
.long_name = NULL_IF_CONFIG_SMALL("libschroedinger Dirac 2.2"),
};
Jump to Line
Something went wrong with that request. Please try again.