使用 ffmpeg 和 libvorbis 编码 webm 不起作用

Posted

技术标签:

【中文标题】使用 ffmpeg 和 libvorbis 编码 webm 不起作用【英文标题】:Encoding webm with ffmpeg and libvorbis does not work 【发布时间】:2014-02-13 23:51:28 【问题描述】:

我正在尝试运行 ffmpeg 多路复用示例的修改版本,该示例将 vorbis 编码的音频输出到 webm 容器。

如果我指定 mp3 作为格式,代码可以正常工作,只是在我使用 vorbis 时不行

代码类似于http://www.ffmpeg.org/doxygen/2.0/doc_2examples_2muxing_8c-example.html,但去掉了视频部分。我在启用视频的情况下进行了测试,示例视频已正确编码,但没有音频。

ffmpeg 编译时带有 libvorbis 和 libvpx 支持。


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>

#define STREAM_DURATION 200.0
extern AVCodec ff_libvorbis_encoder;

static AVFrame *frame;
static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec,
                            enum AVCodecID codec_id)

    AVCodecContext *c;
    AVStream *st;
    /* find the encoder */
    //*codec = &ff_libvorbis_encoder;
    *codec = avcodec_find_encoder(codec_id);
    if (!(*codec)) 
        fprintf(stderr, "Could not find encoder for '%s'\n",
                avcodec_get_name(codec_id));
        exit(1);
    
    st = avformat_new_stream(oc, *codec);
    if (!st) 
        fprintf(stderr, "Could not allocate stream\n");
        exit(1);
    
    st->id = oc->nb_streams-1;
    c = st->codec;
    switch ((*codec)->type) 
    case AVMEDIA_TYPE_AUDIO:
        c->sample_fmt  = AV_SAMPLE_FMT_FLTP;
        c->bit_rate    = 64000;
        c->sample_rate = 44100;
        c->channels    = 2;
        break;
    default:
        break;
    
    /* Some formats want stream headers to be separate. */
    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
        c->flags |= CODEC_FLAG_GLOBAL_HEADER;
    return st;


static float t, tincr, tincr2;
static uint8_t **src_samples_data;
static int       src_samples_linesize;
static int       src_nb_samples;
static int max_dst_nb_samples;
uint8_t **dst_samples_data;
int       dst_samples_linesize;
int       dst_samples_size;
struct SwrContext *swr_ctx = NULL;
static void open_audio(AVFormatContext *oc, AVCodec *codec, AVStream *st) 
  AVCodecContext *c;
    int ret;
    c = st->codec;
    /* open it */
    ret = avcodec_open2(c, codec, NULL);
    if (ret sample_rate;
    /* increment frequency by 110 Hz per second */
    tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
    src_nb_samples = c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ?
        10000 : c->frame_size;
    ret = av_samples_alloc_array_and_samples(&src_samples_data, &src_samples_linesize, c->channels,
                                             src_nb_samples, c->sample_fmt, 0);
    if (ret sample_fmt != AV_SAMPLE_FMT_S16) 
        swr_ctx = swr_alloc();
        if (!swr_ctx) 
            fprintf(stderr, "Could not allocate resampler context\n");
            exit(1);
        
        /* set options */
        av_opt_set_int       (swr_ctx, "in_channel_count",   c->channels,       0);
        av_opt_set_int       (swr_ctx, "in_sample_rate",     c->sample_rate,    0);
        av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt",      AV_SAMPLE_FMT_S16, 0);
        av_opt_set_int       (swr_ctx, "out_channel_count",  c->channels,       0);
        av_opt_set_int       (swr_ctx, "out_sample_rate",    c->sample_rate,    0);
        av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt",     AV_SAMPLE_FMT_FLTP,     0);
        /* initialize the resampling context */
        if ((ret = swr_init(swr_ctx)) channels,
                                             max_dst_nb_samples, c->sample_fmt, 0);
    if (ret channels, max_dst_nb_samples,
                                                  c->sample_fmt, 0);


static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels)

    int j, i, v;
    int16_t *q;
    q = samples;
    for (j = 0; j codec;
    get_audio_frame((int16_t *)src_samples_data[0], src_nb_samples, c->channels);
    /* convert samples from native format to destination codec format, using the resampler */
    if (swr_ctx) 
        /* compute destination number of samples */
        dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, c->sample_rate) + src_nb_samples,
                                        c->sample_rate, c->sample_rate, AV_ROUND_UP);
        if (dst_nb_samples > max_dst_nb_samples) 
            av_free(dst_samples_data[0]);
            ret = av_samples_alloc(dst_samples_data, &dst_samples_linesize, c->channels,
                                   dst_nb_samples, c->sample_fmt, 0);
            if (ret channels, dst_nb_samples,
                                                          c->sample_fmt, 0);
        
        /* convert to destination format */
        ret = swr_convert(swr_ctx,
                          dst_samples_data, dst_nb_samples,
                          (const uint8_t **)src_samples_data, src_nb_samples);
        if (ret nb_samples = dst_nb_samples;
    avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
                             dst_samples_data[0], dst_samples_size, 0);
    ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
    if (ret index;
    /* Write the compressed frame to the media file. */
    ret = av_interleaved_write_frame(oc, &pkt);
    if (ret != 0) 
        fprintf(stderr, "Error while writing audio frame: %s\n",
                av_err2str(ret));
        exit(1);
    
    avcodec_free_frame(&frame);


static void close_audio(AVFormatContext *oc, AVStream *st)

    avcodec_close(st->codec);
    av_free(src_samples_data[0]);
    av_free(dst_samples_data[0]);


int main(int argc, char *argv[]) 
  AVOutputFormat *fmt;
  AVFormatContext *oc;
  AVStream *audio_st;
  AVCodec *audio_codec;
  double audio_time, video_time;
  int ret = 0;
  const char *input = argv[1];
  const char *output = argv[2];

  av_register_all();
  avformat_alloc_output_context2(&oc, NULL, NULL, output);
  if(!oc) 
    printf("Could not alloc the output context");
    return 1;
  

  fmt = oc->oformat;

  audio_st = NULL;
  if(fmt->audio_codec != AV_CODEC_ID_NONE) 
    audio_st = add_stream(oc, &audio_codec, fmt->audio_codec);
    printf("Started audio stream with codec %s\n", audio_codec->name);
  

  if(audio_st) 
    open_audio(oc, audio_codec, audio_st);
  
  av_dump_format(oc, 0, output, 1);

  if (!(fmt->flags & AVFMT_NOFILE)) 
    ret = avio_open(&oc->pb, output, AVIO_FLAG_WRITE);
    if (ret pts = 0;
  for (;;) 
    audio_time = audio_st ? audio_st->pts.val * av_q2d(audio_st->time_base) : 0.0;
    if ((!audio_st || audio_time >= STREAM_DURATION))
      break;
    write_audio_frame(oc, audio_st);
  
  av_write_trailer(oc);
  if(audio_st)
    close_audio(oc, audio_st);
  if(!(fmt->flags & AVFMT_NOFILE))
    avio_close(oc->pb);
  avformat_free_context(oc);
  return 0;


编译
clang -o converter -lavcodec -lavformat -lavutil -lswresample -lvorbis converter.c

输出


~/v/converter> ./converter test.wav test.webm
Started audio stream with codec libvorbis
Output #0, webm, to 'test.webm':
    Stream #0:0: Audio: vorbis (libvorbis), 44100 Hz, 2 channels, fltp, 64 kb/s
[libvorbis @ 0x7fdafb800600] 33 frames left in the queue on closing

【问题讨论】:

【参考方案1】:

所以答案是正确设置音频流的 pts 数据。

【讨论】:

是的。这是使用 Vorbis 编码时的常见问题,因为这是 FFMpeg 中唯一不设置音频 PTS 的编码器。 这里有与 WebM/Vorbis 类似的问题。您能否解释一下您最终是如何设置 pts 的?

以上是关于使用 ffmpeg 和 libvorbis 编码 webm 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何创建 WebM 视频文件?

mac 下完全安装 ffmpeg

使用 ffmpeg 进行最快解码的编码

使用 ffmpeg 编码高质量视频 [关闭]

如何使用 libogg 复用 Vorbis 和 Theora 流

使用 FFMPEG 修改编码器元数据的问题