Webm(VP8 / Opus)文件读写

时间:2015-06-25 03:14:49

标签: webrtc webm libav vp8 opus

我正在尝试用C / C ++开发一个webrtc模拟器。对于媒体处理,我计划使用 libav 。我正在考虑以下步骤来实现两个webrtc模拟器之间的媒体交换。假设我有两个webrtc模拟器 A B

  1. 使用 av_read_frame api从输入的webm文件中读取A处的媒体。
  2. 我假设我会获得编码媒体(音频/视频)数据,我在这里是否正确?
  3. 通过UDP套接字将编码的媒体数据发送到模拟器 B
  4. 模拟器 B 接收UDP套接字中的媒体数据作为RTP数据包。
  5. 模拟器 B 从刚收到的RTP数据包中提取音频/视频数据。
  6. 我假设模拟器 B 中提取的媒体数据只是编码数据(我在这里更正)。我不想解码它。我想把它写到一个文件。稍后我会播放文件以检查我是否做得对。
  7. 为简化此问题,请取出UDP套接字部分。然后我的问题减少从webm输入文件读取数据,获取编码媒体,准备数据包并使用 av_interleaved_write_frame 或任何其他适当的api写入输出文件。我想用libav做的所有这些事情。

    有没有我可以参考的示例代码。
    或者有人可以指导我开发它。

    我正在尝试测试程序。作为第一步,我的目标是从文件中读取并写入输出文件。我有以下代码,但它无法正常工作。

    //#define _AUDIO_WRITE_ENABLED_
    
    #include "libavutil/imgutils.h"
    #include "libavutil/samplefmt.h"
    #include "libavformat/avformat.h"
    
    static AVPacket pkt;
    static AVFormatContext *fmt_ctx = NULL;
    static AVFormatContext *av_format_context = NULL;
    static AVOutputFormat *av_output_format = NULL;
    
    static AVCodec *video_codec = NULL;
    static AVStream *video_stream = NULL;
    
    static AVCodec *audio_codec = NULL;
    static AVStream *audio_stream = NULL;
    
    
    static const char *src_filename = NULL;
    static const char *dst_filename = NULL;
    
    int main (int argc, char **argv)
    {
        int ret = 0;
        int index = 0;
    
        if (argc != 3) 
        {
            printf("Usage: ./webm input_video_file output_video_file \n");
            exit(0);
        }
    
        src_filename = argv[1];
        dst_filename = argv[2];
    
        printf("Source file = %s , Destination file = %s\n", src_filename, dst_filename);
    
        av_register_all();
    
        /* open input file, and allocate format context */
        if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) 
        {
            fprintf(stderr, "Could not open source file %s\n", src_filename);
            exit(1);
        }
    
        /* retrieve stream information */
        if (avformat_find_stream_info(fmt_ctx, NULL) < 0) 
        {
            fprintf(stderr, "Could not find stream information\n");
            exit(2);
        }
    
        av_output_format = av_guess_format(NULL, dst_filename, NULL);
        if(!av_output_format)
        {
            fprintf(stderr, "Could not guess output file format\n");
            exit(3);
        }
    
        av_output_format->audio_codec = AV_CODEC_ID_VORBIS;
        av_output_format->video_codec = AV_CODEC_ID_VP8;
    
        av_format_context = avformat_alloc_context();
        if(!av_format_context) 
        {
            fprintf(stderr, "Could not allocation av format context\n");
            exit(4);
        }   
        av_format_context->oformat = av_output_format;
        strcpy(av_format_context->filename, dst_filename);
    
    
        video_codec = avcodec_find_encoder(av_output_format->video_codec);
        if (!video_codec) 
        {
            fprintf(stderr, "Codec not found\n");
            exit(5);
        }
    
        video_stream = avformat_new_stream(av_format_context, video_codec);
        if (!video_stream) 
        {
            fprintf(stderr, "Could not alloc stream\n");
            exit(6);
        }
    
        avcodec_get_context_defaults3(video_stream->codec, video_codec);
        video_stream->codec->codec_id = AV_CODEC_ID_VP8;
        video_stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
    
        video_stream->time_base = (AVRational) {1, 30};   
    
        video_stream->codec->width = 640; 
        video_stream->codec->height = 480; 
    
        video_stream->codec->pix_fmt = PIX_FMT_YUV420P;
        video_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
        video_stream->codec->bit_rate = 400000;
        video_stream->codec->gop_size = 10;
        video_stream->codec->max_b_frames=1;
    
    #ifdef _AUDIO_WRITE_ENABLED_    
        audio_codec = avcodec_find_encoder(av_output_format->audio_codec);
        if (!audio_codec) 
        {
            fprintf(stderr, "Codec not found audio codec\n");
            exit(5);
        }
    
    
        audio_stream = avformat_new_stream(av_format_context, audio_codec);
        if (!audio_stream) 
        {
            fprintf(stderr, "Could not alloc stream for audio\n");
            exit(6);
        }
    
        avcodec_get_context_defaults3(audio_stream->codec, audio_codec);
        audio_stream->codec->codec_id = AV_CODEC_ID_VORBIS;
        audio_stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
        audio_stream->time_base = (AVRational) {1, 30};
        audio_stream->codec->sample_rate = 8000;
        audio_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    #endif
    
        if(!(av_output_format->flags & AVFMT_NOFILE)) 
        {
            if (avio_open(&av_format_context->pb, dst_filename, AVIO_FLAG_WRITE) < 0)
            {
                fprintf(stderr, "Could not open '%s'\n", dst_filename);
            }
        }
    
        /* Before avformat_write_header set the stream */
        avformat_write_header(av_format_context, NULL);
    
        /* initialize packet, set data to NULL, let the demuxer fill it */
        av_init_packet(&pkt);
        pkt.data = NULL;
        pkt.size = 0;
    
        pkt.stream_index = video_stream->index;
    
        ret = av_read_frame(fmt_ctx, &pkt);
        while (ret >= 0) 
        {
            index++;
    
            //pkt.stream_index = video_avstream->index;
            if(pkt.stream_index == video_stream->index)
            {
                printf("Video: Read cycle %d, bytes read = %d, pkt stream index=%d\n", index, pkt.size, pkt.stream_index);
                av_write_frame(av_format_context, &pkt);
            }
    #ifdef _AUDIO_WRITE_ENABLED_        
            else if(pkt.stream_index == audio_stream->index)
            {
                printf("Audio: Read cycle %d, bytes read = %d, pkt stream index=%d\n", index, pkt.size, pkt.stream_index);
                av_write_frame(av_format_context, &pkt);
            }
    #endif        
            av_free_packet(&pkt);
            ret = av_read_frame(fmt_ctx, &pkt);
        }
    
        av_write_trailer(av_format_context);
    
        /** Exit procedure starts */
        avformat_close_input(&fmt_ctx);
        avformat_free_context(av_format_context);
    
        return 0;
    }
    

    执行此程序时,输出“找不到编解码器”。现在肯定哪里出错了,请有人帮忙。

    未找到编解码器问题通过单独构建libvpx1.4版本来解决。仍在努力从源文件中读取并写入目标文件。

    编辑1:代码修改后,我只能将视频内容写入文件,但仍会出现更多错误。

    编辑2:使用修改后的代码(第2轮),我看到视频帧写得正确。对于音频帧,我在宏 _AUDIO_WRITE_ENABLED _ 下添加了代码,但是如果我启用此宏程序崩溃。有人可以指导音频写入部分中的错误(宏_AUDIO_WRITE_ENABLED _下的代码)。

1 个答案:

答案 0 :(得分:0)

我没有完全回答你的问题,但我希望我们最终会得到最终解决方案。当我尝试运行您的代码时,我收到此错误“time base not set”。

时基和其他标题规范是编解码器的一部分。这就是我如何指定写入文件(vStreamAVStream):

#if LIBAVCODEC_VER_AT_LEAST(53, 21)
    avcodec_get_context_defaults3(rc->vStream->codec, AVMEDIA_TYPE_VIDEO);
#else
    avcodec_get_context_defaults2(rc->vStream->codec, AVMEDIA_TYPE_VIDEO);
#endif
#if LIBAVCODEC_VER_AT_LEAST(54, 25)
    vStream->codec->codec_id = AV_CODEC_ID_VP8;
#else
    vStream->codec->codec_id = CODEC_ID_VP8;
#endif
    vStream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
    vStream->codec->time_base = (AVRational) {1, 30};   
    vStream->codec->width = 640; 
    vStream->codec->height = 480; 
    vStream->codec->pix_fmt = PIX_FMT_YUV420P;
编辑:我在Valgrind中运行了你的程序,并在av_write_frame上进行了段错误。看起来它的time_base和其他输出规格没有正确设置。 在avformat_write_header()之前添加规范,之后为时已晚。