示例#1
0
文件: tsfix.c 项目: JPP1/tvheadend
static void
tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm)
{
  th_pkt_t *pkt = pkt_copy_shallow(sm->sm_data);
  tfstream_t *tfs = tfs_find(tf, pkt);
  streaming_msg_free(sm);
  
  if(tfs == NULL || dispatch_clock < tf->tf_start_time) {
    pkt_ref_dec(pkt);
    return;
  }

  if(tf->tf_tsref == PTS_UNSET &&
     (!tf->tf_hasvideo ||
      (SCT_ISVIDEO(tfs->tfs_type) && pkt->pkt_frametype == PKT_I_FRAME))) {
      tf->tf_tsref = pkt->pkt_dts & PTS_MASK;
      tsfixprintf("reference clock set to %"PRId64"\n", tf->tf_tsref);
  } else {
    /* For teletext, the encoders might use completely different timestamps */
    /* If the difference is greater than 2 seconds, use the actual dts value */
    if (tfs->tfs_type == SCT_TELETEXT && tfs->tfs_local_ref == PTS_UNSET &&
        tf->tf_tsref != PTS_UNSET && pkt->pkt_dts != PTS_UNSET) {
      int64_t diff = tsfix_ts_diff(tf->tf_tsref, pkt->pkt_dts);
      if (diff > 2 * 90000) {
        tfstream_t *tfs2;
        tvhwarn("parser", "The timediff for TELETEXT is big (%"PRId64"), using current dts", diff);
        tfs->tfs_local_ref = pkt->pkt_dts;
        /* Text subtitles extracted from teletext have same timebase */
        LIST_FOREACH(tfs2, &tf->tf_streams, tfs_link)
          if(tfs2->tfs_type == SCT_TEXTSUB)
            tfs2->tfs_local_ref = pkt->pkt_dts;
      } else {
示例#2
0
static void
tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm)
{
  th_pkt_t *pkt = pkt_copy_shallow(sm->sm_data);
  tfstream_t *tfs = tfs_find(tf, pkt);
  streaming_msg_free(sm);
  
  if(tfs == NULL || dispatch_clock < tf->tf_start_time) {
    pkt_ref_dec(pkt);
    return;
  }

  if(tf->tf_tsref == PTS_UNSET &&
     (!tf->tf_hasvideo ||
      (SCT_ISVIDEO(tfs->tfs_type) && pkt->pkt_frametype == PKT_I_FRAME))) {
      tf->tf_tsref = pkt->pkt_dts & PTS_MASK;
      tsfixprintf("reference clock set to %"PRId64"\n", tf->tf_tsref);
  }

  if(pkt->pkt_dts == PTS_UNSET) {

    int pdur = pkt->pkt_duration >> pkt->pkt_field;

    if(tfs->tfs_last_dts_in == PTS_UNSET) {
      pkt_ref_dec(pkt);
      return;
    }

    pkt->pkt_dts = (tfs->tfs_last_dts_in + pdur) & PTS_MASK;

    tsfixprintf("TSFIX: %-12s DTS set to last %"PRId64" +%d == %"PRId64"\n",
		streaming_component_type2txt(tfs->tfs_type),
		tfs->tfs_last_dts_in, pdur, pkt->pkt_dts);
  }
示例#3
0
/**
 * Figure out the mimetype
 */
static const char*
tvh_muxer_mime(muxer_t* m, const struct streaming_start *ss)
{
  int i;
  int has_audio;
  int has_video;
  const streaming_start_component_t *ssc;
  
  has_audio = 0;
  has_video = 0;

  for(i=0; i < ss->ss_num_components; i++) {
    ssc = &ss->ss_components[i];

    if(ssc->ssc_disabled)
      continue;

    has_video |= SCT_ISVIDEO(ssc->ssc_type);
    has_audio |= SCT_ISAUDIO(ssc->ssc_type);
  }

  if(has_video)
    return muxer_container_type2mime(m->m_container, 1);
  else if(has_audio)
    return muxer_container_type2mime(m->m_container, 0);
  else
    return muxer_container_type2mime(MC_UNKNOWN, 0);
}
示例#4
0
/**
 * Check if a container supports a given streaming component
 */
static int
lav_muxer_support_stream(muxer_container_type_t mc, 
			 streaming_component_type_t type)
{
  int ret = 0;

  switch(mc) {
  case MC_MATROSKA:
  case MC_AVMATROSKA:
    ret |= SCT_ISAUDIO(type);
    ret |= SCT_ISVIDEO(type);
    ret |= SCT_ISSUBTITLE(type);
    break;

  case MC_WEBM:
  case MC_AVWEBM:
    ret |= type == SCT_VP8;
    ret |= type == SCT_VORBIS;
    break;

  case MC_MPEGTS:
    ret |= (type == SCT_MPEG2VIDEO);
    ret |= (type == SCT_H264);
    ret |= (type == SCT_HEVC);

    ret |= (type == SCT_MPEG2AUDIO);
    ret |= (type == SCT_AC3);
    ret |= (type == SCT_AAC);
    ret |= (type == SCT_MP4A);
    ret |= (type == SCT_EAC3);

    //Some pids lack pts, disable for now
    //ret |= (type == SCT_TELETEXT);
    ret |= (type == SCT_DVBSUB);
    break;

  case MC_MPEGPS:
    ret |= (type == SCT_MPEG2VIDEO);
    ret |= (type == SCT_MPEG2AUDIO);
    ret |= (type == SCT_AC3);
    break;

  case MC_AVMP4:
    ret |= (type == SCT_MPEG2VIDEO);
    ret |= (type == SCT_H264);
    ret |= (type == SCT_HEVC);

    ret |= (type == SCT_MPEG2AUDIO);
    ret |= (type == SCT_AC3);
    ret |= (type == SCT_AAC);
    ret |= (type == SCT_MP4A);
    ret |= (type == SCT_EAC3);
    break;

  default:
    break;
  }

  return ret;
}
示例#5
0
static inline ssize_t _process_msg0
  ( timeshift_t *ts, timeshift_file_t *tsf, streaming_message_t **smp )
{
  int i;
  ssize_t err;
  streaming_start_t *ss;
  streaming_message_t *sm = *smp;
  if (sm->sm_type == SMT_START) {
    err = 0;
    timeshift_index_data_t *ti = calloc(1, sizeof(timeshift_index_data_t));
    ti->pos  = tsf->size;
    ti->data = sm;
    *smp = NULL;
    TAILQ_INSERT_TAIL(&tsf->sstart, ti, link);

    /* Update video index */
    ss = sm->sm_data;
    for (i = 0; i < ss->ss_num_components; i++)
      if (SCT_ISVIDEO(ss->ss_components[i].ssc_type))
        ts->vididx = ss->ss_components[i].ssc_index;
  } else if (sm->sm_type == SMT_SIGNAL_STATUS)
    err = timeshift_write_sigstat(tsf, sm->sm_time, sm->sm_data);
  else if (sm->sm_type == SMT_PACKET) {
    err = timeshift_write_packet(tsf, sm->sm_time, sm->sm_data);
    if (err > 0) {
      th_pkt_t *pkt = sm->sm_data;

      /* Index video iframes */
      if (pkt->pkt_componentindex == ts->vididx &&
          pkt->pkt_frametype      == PKT_I_FRAME) {
        timeshift_index_iframe_t *ti = calloc(1, sizeof(timeshift_index_iframe_t));
        ti->pos  = tsf->size;
        ti->time = sm->sm_time;
        TAILQ_INSERT_TAIL(&tsf->iframes, ti, link);
      }
    }
  } else if (sm->sm_type == SMT_MPEGTS)
    err = timeshift_write_mpegts(tsf, sm->sm_time, sm->sm_data);
  else
    err = 0;

  /* OK */
  if (err > 0) {
    tsf->last  = sm->sm_time;
    tsf->size += err;
    atomic_add_u64(&timeshift_total_size, err);
    if (tsf->ram)
      atomic_add_u64(&timeshift_total_ram_size, err);
  }
  return err;
}
示例#6
0
/*
 * Update smt_start
 */
static void _update_smt_start ( timeshift_t *ts, streaming_start_t *ss )
{
  int i;

  if (ts->smt_start)
    streaming_start_unref(ts->smt_start);
  streaming_start_ref(ss);
  ts->smt_start = ss;

  /* Update video index */
  for (i = 0; i < ss->ss_num_components; i++)
    if (SCT_ISVIDEO(ss->ss_components[i].ssc_type)) {
      ts->vididx = ss->ss_components[i].ssc_index;
      break;
    }
}
示例#7
0
/**
 * Compute PTS (if not known)
 */
static void
compute_pts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt)
{
  // If PTS is missing, set it to DTS if not video
  if(pkt->pkt_pts == PTS_UNSET && !SCT_ISVIDEO(tfs->tfs_type)) {
    pkt->pkt_pts = pkt->pkt_dts;
    tsfixprintf("TSFIX: %-12s PTS set to %"PRId64"\n",
		streaming_component_type2txt(tfs->tfs_type),
		pkt->pkt_pts);
  }

  /* PTS known and no other packets in queue, deliver at once */
  if(pkt->pkt_pts != PTS_UNSET && TAILQ_FIRST(&tf->tf_ptsq) == NULL)
    normalize_ts(tf, tfs, pkt);
  else
    recover_pts(tf, tfs, pkt);
}
示例#8
0
static void
tsfix_start(tsfix_t *tf, streaming_start_t *ss)
{
  int i;
  int hasvideo = 0;

  for(i = 0; i < ss->ss_num_components; i++) {
    const streaming_start_component_t *ssc = &ss->ss_components[i];
    tsfix_add_stream(tf, ssc->ssc_index, ssc->ssc_type);
    hasvideo |= SCT_ISVIDEO(ssc->ssc_type);
  }

  TAILQ_INIT(&tf->tf_ptsq);

  tf->tf_tsref = PTS_UNSET;
  tf->tf_hasvideo = hasvideo;
}
示例#9
0
文件: tsfix.c 项目: JPP1/tvheadend
static void
tsfix_start(tsfix_t *tf, streaming_start_t *ss)
{
  int i, hasvideo = 0, vwait = 0;

  for(i = 0; i < ss->ss_num_components; i++) {
    const streaming_start_component_t *ssc = &ss->ss_components[i];
    tsfix_add_stream(tf, ssc->ssc_index, ssc->ssc_type);
    if (SCT_ISVIDEO(ssc->ssc_type)) {
      hasvideo = 1;
      if (ssc->ssc_width == 0 || ssc->ssc_height == 0)
        vwait = 1;
    }
  }

  TAILQ_INIT(&tf->tf_ptsq);

  tf->tf_tsref = PTS_UNSET;
  tf->tf_hasvideo = hasvideo;
  tf->tf_wait_for_video = vwait;
}
示例#10
0
void
pkt_trace_(const char *file, int line, int subsys, th_pkt_t *pkt,
           const char *fmt, ...)
{
  char buf[512], _pcr[22], _dts[22], _pts[22], _type[2], _meta[20];
  va_list args;

  va_start(args, fmt);
  if (SCT_ISVIDEO(pkt->pkt_type) && pkt->v.pkt_frametype) {
    _type[0] = pkt_frametype_to_char(pkt->v.pkt_frametype);
    _type[1] = '\0';
  } else {
    _type[0] = '\0';
  }
  if (pkt->pkt_meta)
    snprintf(_meta, sizeof(_meta), " meta %zu", pktbuf_len(pkt->pkt_meta));
  else
    _meta[0] = '\0';
  snprintf(buf, sizeof(buf),
           "%s%spkt stream %d %s%s%s"
           " pcr %s dts %s pts %s"
           " dur %d len %zu err %i%s%s",
           fmt ? fmt : "",
           fmt ? " (" : "",
           pkt->pkt_componentindex,
           streaming_component_type2txt(pkt->pkt_type),
           _type[0] ? " type " : "", _type,
           pts_to_string(pkt->pkt_pcr, _pcr),
           pts_to_string(pkt->pkt_dts, _dts),
           pts_to_string(pkt->pkt_pts, _pts),
           pkt->pkt_duration,
           pktbuf_len(pkt->pkt_payload),
           pkt->pkt_err,
           _meta,
           fmt ? ")" : "");
  tvhlogv(file, line, LOG_TRACE, subsys, buf, &args);
  va_end(args);
}
示例#11
0
/**
 * Figure out the mime-type for the muxed data stream
 */
static const char*
pass_muxer_mime(muxer_t* m, const struct streaming_start *ss)
{
  int i;
  int has_audio;
  int has_video;
  muxer_container_type_t mc;
  const streaming_start_component_t *ssc;
  const source_info_t *si = &ss->ss_si;

  has_audio = 0;
  has_video = 0;

  for(i=0; i < ss->ss_num_components; i++) {
    ssc = &ss->ss_components[i];

    if(ssc->ssc_disabled)
      continue;

    has_video |= SCT_ISVIDEO(ssc->ssc_type);
    has_audio |= SCT_ISAUDIO(ssc->ssc_type);
  }

  if(si->si_type == S_MPEG_TS)
    mc = MC_MPEGTS;
  else if(si->si_type == S_MPEG_PS)
    mc = MC_MPEGPS;
  else
    mc = MC_UNKNOWN;

  if(has_video)
    return muxer_container_type2mime(mc, 1);
  else if(has_audio)
    return muxer_container_type2mime(mc, 0);
  else
    return muxer_container_type2mime(MC_UNKNOWN, 0);
}
示例#12
0
static void
normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt)
{
  int64_t dts, d;

  int checkts = SCT_ISAUDIO(tfs->tfs_type) || SCT_ISVIDEO(tfs->tfs_type);

  if(tf->tf_tsref == PTS_UNSET) {
    pkt_ref_dec(pkt);
    return;
  }

  pkt->pkt_dts &= PTS_MASK;
  pkt->pkt_pts &= PTS_MASK;

  /* Subtract the transport wide start offset */
  dts = pkt->pkt_dts - tf->tf_tsref;

  if(tfs->tfs_last_dts_norm == PTS_UNSET) {
    if(dts < 0) {
      /* Early packet with negative time stamp, drop those */
      pkt_ref_dec(pkt);
      return;
    }
  } else if(checkts) {
    d = dts + tfs->tfs_dts_epoch - tfs->tfs_last_dts_norm;

    if(d < 0 || d > 90000) {

      if(d < -PTS_MASK || d > -PTS_MASK + 180000) {

	tfs->tfs_bad_dts++;

	if(tfs->tfs_bad_dts < 5) {
	  tvhlog(LOG_ERR, "parser", 
		 "transport stream %s, DTS discontinuity. "
		 "DTS = %" PRId64 ", last = %" PRId64,
		 streaming_component_type2txt(tfs->tfs_type),
		 dts, tfs->tfs_last_dts_norm);
	}
      } else {
	/* DTS wrapped, increase upper bits */
	tfs->tfs_dts_epoch += PTS_MASK + 1;
	tfs->tfs_bad_dts = 0;
      }
    } else {
      tfs->tfs_bad_dts = 0;
    }
  }

  dts += tfs->tfs_dts_epoch;
  tfs->tfs_last_dts_norm = dts;

  if(pkt->pkt_pts != PTS_UNSET) {
    /* Compute delta between PTS and DTS (and watch out for 33 bit wrap) */
    int64_t ptsoff = (pkt->pkt_pts - pkt->pkt_dts) & PTS_MASK;
    
    pkt->pkt_pts = dts + ptsoff;
  }

  pkt->pkt_dts = dts;

  tsfixprintf("TSFIX: %-12s %d %10"PRId64" %10"PRId64" %10d %zd\n",
	      streaming_component_type2txt(tfs->tfs_type),
	      pkt->pkt_frametype,
	      pkt->pkt_dts,
	      pkt->pkt_pts,
	      pkt->pkt_duration,
	      pktbuf_len(pkt->pkt_payload));

  streaming_message_t *sm = streaming_msg_create_pkt(pkt);
  streaming_target_deliver2(tf->tf_output, sm);
  pkt_ref_dec(pkt);
}
示例#13
0
static int
dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
{
  const source_info_t *si = &ss->ss_si;
  const streaming_start_component_t *ssc;
  int i;
  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);

  de->de_mux = muxer_create(de->de_mc);
  if(!de->de_mux) {
    dvr_rec_fatal_error(de, "Unable to create muxer");
    return -1;
  }

  if(pvr_generate_filename(de, ss) != 0) {
    dvr_rec_fatal_error(de, "Unable to create directories");
    return -1;
  }

  if(muxer_open_file(de->de_mux, de->de_filename)) {
    dvr_rec_fatal_error(de, "Unable to open file");
    return -1;
  }

  if(muxer_init(de->de_mux, ss, lang_str_get(de->de_title, NULL))) {
    dvr_rec_fatal_error(de, "Unable to init file");
    return -1;
  }

  if(cfg->dvr_flags & DVR_TAG_FILES && de->de_bcast) {
    if(muxer_write_meta(de->de_mux, de->de_bcast)) {
      dvr_rec_fatal_error(de, "Unable to write meta data");
      return -1;
    }
  }

  tvhlog(LOG_INFO, "dvr", "%s from "
	 "adapter: \"%s\", "
	 "network: \"%s\", mux: \"%s\", provider: \"%s\", "
	 "service: \"%s\"",
	 de->de_filename ?: lang_str_get(de->de_title, NULL),
	 si->si_adapter  ?: "<N/A>",
	 si->si_network  ?: "<N/A>",
	 si->si_mux      ?: "<N/A>",
	 si->si_provider ?: "<N/A>",
	 si->si_service  ?: "<N/A>");


  tvhlog(LOG_INFO, "dvr",
	 " #  %-16s  %-4s  %-10s  %-12s  %-11s  %-8s",
	 "type",
	 "lang",
	 "resolution",
	 "aspect ratio",
	 "sample rate",
	 "channels");

  for(i = 0; i < ss->ss_num_components; i++) {
    ssc = &ss->ss_components[i];

    char res[11];
    char asp[6];
    char sr[6];
    char ch[7];

    if(SCT_ISAUDIO(ssc->ssc_type)) {
      if(ssc->ssc_sri)
	snprintf(sr, sizeof(sr), "%d", sri_to_rate(ssc->ssc_sri));
      else
	strcpy(sr, "?");

      if(ssc->ssc_channels == 6)
	snprintf(ch, sizeof(ch), "5.1");
      else if(ssc->ssc_channels == 0)
	strcpy(ch, "?");
      else
	snprintf(ch, sizeof(ch), "%d", ssc->ssc_channels);
    } else {
      sr[0] = 0;
      ch[0] = 0;
    }

    if(SCT_ISVIDEO(ssc->ssc_type)) {
      if(ssc->ssc_width && ssc->ssc_height)
	snprintf(res, sizeof(res), "%dx%d",
		 ssc->ssc_width, ssc->ssc_height);
      else
	strcpy(res, "?");
    } else {
      res[0] = 0;
    }

    if(SCT_ISVIDEO(ssc->ssc_type)) {
      if(ssc->ssc_aspect_num &&  ssc->ssc_aspect_den)
	snprintf(asp, sizeof(asp), "%d:%d",
		 ssc->ssc_aspect_num, ssc->ssc_aspect_den);
      else
	strcpy(asp, "?");
    } else {
      asp[0] = 0;
    }

    tvhlog(LOG_INFO, "dvr",
	   "%2d  %-16s  %-4s  %-10s  %-12s  %-11s  %-8s  %s",
	   ssc->ssc_index,
	   streaming_component_type2txt(ssc->ssc_type),
	   ssc->ssc_lang,
	   res,
	   asp,
	   sr,
	   ch,
	   ssc->ssc_disabled ? "<disabled, no valid input>" : "");
  }

  return 0;
}
示例#14
0
/**
 * Add a stream to the muxer
 */
static int
lav_muxer_add_stream(lav_muxer_t *lm, 
		     const streaming_start_component_t *ssc)
{
  AVStream *st;
  AVCodecContext *c;

  st = avformat_new_stream(lm->lm_oc, NULL);
  if (!st)
    return -1;

  st->id = ssc->ssc_index;
  c = st->codec;
  c->codec_id = streaming_component_type2codec_id(ssc->ssc_type);

  switch(lm->m_container) {
  case MC_MATROSKA:
    st->time_base.num = 1000000;
    st->time_base.den = 1;
    break;

  case MC_MPEGPS:
    c->rc_buffer_size = 224*1024*8;
    //Fall-through
  case MC_MPEGTS:
    st->time_base.num = 90000;
    st->time_base.den = 1;
    break;

  default:
    st->time_base = AV_TIME_BASE_Q;
    break;
  }



  if(ssc->ssc_gh) {
    c->extradata_size = pktbuf_len(ssc->ssc_gh);
    c->extradata = av_malloc(c->extradata_size);
    memcpy(c->extradata, pktbuf_ptr(ssc->ssc_gh), 
	   pktbuf_len(ssc->ssc_gh));
  }

  if(SCT_ISAUDIO(ssc->ssc_type)) {
    c->codec_type    = AVMEDIA_TYPE_AUDIO;
    c->sample_fmt    = AV_SAMPLE_FMT_S16;

    c->sample_rate   = sri_to_rate(ssc->ssc_sri);
    c->channels      = ssc->ssc_channels;

    c->time_base.num = 1;
    c->time_base.den = c->sample_rate;

    av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0);

  } else if(SCT_ISVIDEO(ssc->ssc_type)) {
    c->codec_type = AVMEDIA_TYPE_VIDEO;
    c->width      = ssc->ssc_width;
    c->height     = ssc->ssc_height;

    c->time_base.num  = 1;
    c->time_base.den = 25;

    c->sample_aspect_ratio.num = ssc->ssc_aspect_num;
    c->sample_aspect_ratio.den = ssc->ssc_aspect_den;

    st->sample_aspect_ratio.num = c->sample_aspect_ratio.num;
    st->sample_aspect_ratio.den = c->sample_aspect_ratio.den;

  } else if(SCT_ISSUBTITLE(ssc->ssc_type)) {
    c->codec_type = AVMEDIA_TYPE_SUBTITLE;
    av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0);
  }

  if(lm->lm_oc->oformat->flags & AVFMT_GLOBALHEADER)
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;

  return 0;
}
示例#15
0
/**
 * Add a stream to the muxer
 */
static int
lav_muxer_add_stream(lav_muxer_t *lm, 
		     const streaming_start_component_t *ssc)
{
  AVStream *st;
  AVCodecContext *c;

  st = avformat_new_stream(lm->lm_oc, NULL);
  if (!st)
    return -1;

  st->id = ssc->ssc_index;
  c = st->codec;
  c->codec_id = streaming_component_type2codec_id(ssc->ssc_type);

  switch(lm->m_config.m_type) {
  case MC_MATROSKA:
  case MC_AVMATROSKA:
  case MC_AVMP4:
    st->time_base.num = 1000000;
    st->time_base.den = 1;
    break;

  case MC_MPEGPS:
    c->rc_buffer_size = 224*1024*8;
    //Fall-through
  case MC_MPEGTS:
    st->time_base.num = 90000;
    st->time_base.den = 1;
    break;

  default:
    st->time_base = AV_TIME_BASE_Q;
    break;
  }

  if(ssc->ssc_gh) {
    if (ssc->ssc_type == SCT_H264 || ssc->ssc_type == SCT_HEVC) {
      sbuf_t hdr;
      sbuf_init(&hdr);
      if (ssc->ssc_type == SCT_H264) {
          isom_write_avcc(&hdr, pktbuf_ptr(ssc->ssc_gh),
                          pktbuf_len(ssc->ssc_gh));
      } else {
          isom_write_hvcc(&hdr, pktbuf_ptr(ssc->ssc_gh),
                          pktbuf_len(ssc->ssc_gh));
      }
      c->extradata_size = hdr.sb_ptr;
      c->extradata = av_malloc(hdr.sb_ptr);
      memcpy(c->extradata, hdr.sb_data, hdr.sb_ptr);
      sbuf_free(&hdr);
    } else {
      c->extradata_size = pktbuf_len(ssc->ssc_gh);
      c->extradata = av_malloc(c->extradata_size);
      memcpy(c->extradata, pktbuf_ptr(ssc->ssc_gh),
             pktbuf_len(ssc->ssc_gh));
    }
  }

  if(SCT_ISAUDIO(ssc->ssc_type)) {
    c->codec_type    = AVMEDIA_TYPE_AUDIO;
    c->sample_fmt    = AV_SAMPLE_FMT_S16;

    c->sample_rate   = sri_to_rate(ssc->ssc_sri);
    c->channels      = ssc->ssc_channels;

#if 0
    c->time_base.num = 1;
    c->time_base.den = c->sample_rate;
#else
    c->time_base     = st->time_base;
#endif

    av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0);

  } else if(SCT_ISVIDEO(ssc->ssc_type)) {
    c->codec_type = AVMEDIA_TYPE_VIDEO;
    c->width      = ssc->ssc_width;
    c->height     = ssc->ssc_height;

    c->time_base.num = 1;
    c->time_base.den = 25;

    c->sample_aspect_ratio.num = ssc->ssc_aspect_num;
    c->sample_aspect_ratio.den = ssc->ssc_aspect_den;

    if (lm->m_config.m_type == MC_AVMP4) {
      /* this is a whole hell */
      AVRational ratio = { c->height, c->width };
      c->sample_aspect_ratio = av_mul_q(c->sample_aspect_ratio, ratio);
    }

    st->sample_aspect_ratio.num = c->sample_aspect_ratio.num;
    st->sample_aspect_ratio.den = c->sample_aspect_ratio.den;

  } else if(SCT_ISSUBTITLE(ssc->ssc_type)) {
    c->codec_type = AVMEDIA_TYPE_SUBTITLE;
    av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0);
  }

  if(lm->lm_oc->oformat->flags & AVFMT_GLOBALHEADER)
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;

  return 0;
}