view libmpcodecs/ad_speex.c @ 30086:f72ea7a67421

Introduce a sh_common struct that contains the parts in common by the audio, video and sub "stream headers". One reason for this is to help avoid/make more obvious things like members with the same function but different name (extradata vs. codecdata etc.), or members with the same name but different semantics (pts for audio vs. pts for video).
author reimar
date Sun, 27 Dec 2009 14:40:56 +0000
parents a95ed9a69caf
children bbb6ebec87a0
line wrap: on
line source

/**
 * Speex decoder by Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>
 * License: GPL v2 or later
 * This code may be be relicensed under the terms of the GNU LGPL when it
 * becomes part of the FFmpeg project (ffmpeg.org)
 */
#include "config.h"
#include <stdlib.h>
#include <speex/speex.h>
#include <speex/speex_stereo.h>
#include <speex/speex_header.h>
#include "ad_internal.h"

static ad_info_t info = {
  "Speex audio decoder",
  "speex",
  "Reimar Döffinger",
  "",
  ""
};

LIBAD_EXTERN(speex)

typedef struct {
  SpeexBits bits;
  void *dec_context;
  SpeexStereoState stereo;
  SpeexHeader *hdr;
} context_t;

#define MAX_FRAMES_PER_PACKET 100

static int preinit(sh_audio_t *sh) {
  sh->audio_out_minsize = 2 * 320 * MAX_FRAMES_PER_PACKET * 2 * sizeof(short);
  return 1;
}

static int init(sh_audio_t *sh) {
  context_t *ctx = calloc(1, sizeof(context_t));
  const SpeexMode *spx_mode;
  const SpeexStereoState st_st = SPEEX_STEREO_STATE_INIT; // hack
  if (!sh->wf || sh->wf->cbSize < 80) {
    mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Missing extradata!\n");
    return 0;
  }
  ctx->hdr = speex_packet_to_header((char *)&sh->wf[1], sh->wf->cbSize);
  if (ctx->hdr->nb_channels != 1 && ctx->hdr->nb_channels != 2) {
    mp_msg(MSGT_DECAUDIO, MSGL_WARN, "Invalid number of channels (%i), "
            "assuming mono\n", ctx->hdr->nb_channels);
    ctx->hdr->nb_channels = 1;
  }
  if (ctx->hdr->frames_per_packet > MAX_FRAMES_PER_PACKET) {
    mp_msg(MSGT_DECAUDIO, MSGL_WARN, "Invalid number of frames per packet (%i), "
            "assuming 1\n", ctx->hdr->frames_per_packet);
    ctx->hdr->frames_per_packet = 1;
  }
  switch (ctx->hdr->mode) {
    case 0:
      spx_mode = &speex_nb_mode; break;
    case 1:
      spx_mode = &speex_wb_mode; break;
    case 2:
      spx_mode = &speex_uwb_mode; break;
    default:
      mp_msg(MSGT_DECAUDIO, MSGL_WARN, "Unknown speex mode (%i)\n", ctx->hdr->mode);
      spx_mode = &speex_nb_mode;
  }
  ctx->dec_context = speex_decoder_init(spx_mode);
  speex_bits_init(&ctx->bits);
  memcpy(&ctx->stereo, &st_st, sizeof(ctx->stereo)); // hack part 2
  sh->channels = ctx->hdr->nb_channels;
  sh->samplerate = ctx->hdr->rate;
  sh->samplesize = 2;
  sh->sample_format = AF_FORMAT_S16_NE;
  sh->context = ctx;
  return 1;
}

static void uninit(sh_audio_t *sh) {
  context_t *ctx = sh->context;
  if (ctx) {
    speex_bits_destroy(&ctx->bits);
    speex_decoder_destroy(ctx->dec_context);
    if (ctx->hdr)
      free(ctx->hdr);
    free(ctx);
  }
  ctx = NULL;
}

static int decode_audio(sh_audio_t *sh, unsigned char *buf,
                        int minlen, int maxlen) {
  context_t *ctx = sh->context;
  int len, framelen, framesamples;
  char *packet;
  int i, err;
  speex_decoder_ctl(ctx->dec_context, SPEEX_GET_FRAME_SIZE, &framesamples);
  framelen = framesamples * ctx->hdr->nb_channels * sizeof(short);
  if (maxlen < ctx->hdr->frames_per_packet * framelen) {
    mp_msg(MSGT_DECAUDIO, MSGL_V, "maxlen too small in decode_audio\n");
    return -1;
  }
  len = ds_get_packet(sh->ds, (unsigned char **)&packet);
  if (len <= 0) return -1;
  speex_bits_read_from(&ctx->bits, packet, len);
  i = ctx->hdr->frames_per_packet;
  do {
    err = speex_decode_int(ctx->dec_context, &ctx->bits, (short *)buf);
    if (err == -2)
      mp_msg(MSGT_DECAUDIO, MSGL_ERR, "Error decoding file.\n");
    if (ctx->hdr->nb_channels == 2)
      speex_decode_stereo_int((short *)buf, framesamples, &ctx->stereo);
    buf = &buf[framelen];
  } while (--i > 0);
  return ctx->hdr->frames_per_packet * framelen;
}

static int control(sh_audio_t *sh, int cmd, void *arg, ...) {
  return CONTROL_UNKNOWN;
}