0

I am trying to develop a webrtc simulator in C/C++. For media handling, I plan to use libav. I am thinking of below steps to realize media exchange between two webrtc simulator. Say I have two webrtc simulators A and B.

  1. Read media at A from a input webm file using av_read_frame api.
  2. I assume I will get the encoded media (audio / video) data, am I correct here?
  3. Send the encoded media data to simulator B over a UDP socket.
  4. Simulator B receives the media data in UDP socket as RTP packets.
  5. Simulator B extracts audio/video data from just received RTP packet.
  6. I assume the extracted media data at simulator B are the encoded data only (am I correct here). I do not want to decode it. I want to write it to a file. Later I will play the file to check if I have done everything right.

To simplify this problem lets take out UDP socket part. Then my question reduces to read data from a webm input file, get the encoded media, prepare the packet and write to a output file using av_interleaved_write_frame or any other appropriate api. All these things I want to do using libav.

Is there any example code I can refer.
Or can somebody please guide me to develop it.

I am trying with a test program. As a first step, my aim is to read from a file and write to an output file. I have below code, but it is not working properly.

//#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;
}

When I execute this program, it outputs "codec not found". Now sure whats going wrong, Can somebody help please.

Codec not found issue is resolved by separately building libvpx1.4 version. Still struggling to read from source file, and writing to a destination file.

EDIT 1: After code modification, only video stuff I am able to write to a file, though some more errors are still present.

EDIT 2: With modified code (2nd round), I see video frames are written properly. For audio frames I added the code under a macro _AUDIO_WRITE_ENABLED_ , but if I enable this macro program crashing. Can somebody guide whats wrong in audio write part (code under macro _AUDIO_WRITE_ENABLED_).

Austin
  • 1,709
  • 20
  • 40

1 Answers1

0

I am not fully answering your question, but I hope we will get to the final solution eventually. When I tried to run your code, I got this error "time base not set".

Time base and other header specs are part of codec. This is, how I have this thing specified for writing into file (vStream is of AVStream):

#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;

EDIT: I ran your program in Valgrind and it segfaults on av_write_frame. Looks like its time_base and other specs for output are not set properly. Add the specs before avformat_write_header(), before it is too late.

golstar
  • 56
  • 1
  • 7
  • I have modified code (given above) as per suggestion. But still it is crashing while trying to write header. Is there any appropriate example available. I came across https://www.ffmpeg.org/doxygen/0.6/output-example_8c-source.html , but it again it has encoding stuff. I want to read from a input file, get data (this data I will send over UDP and receive at remote), write data to a file. – Austin Jun 27 '15 at 14:58
  • I have modified code (updated in original question section) and become able to avoid the crash. But I still have two more issues. 1. Only video data is written, can not hear any audio data (may be I need to add code for audio stuff), 2. Towards end of file it is not able to play. It says "Failed to decode frame". – Austin Jun 28 '15 at 00:44
  • With revisio 2, it looks I am able to read and write back video frames. I am trying audio frame write (see macro _AUDIO_WRITE_ENABLED_ in code), then program crashing.., any clue... thanks – Austin Jun 28 '15 at 02:53
  • I see the crash with cues operations in libav. Now sure if I need to call/set something cue specific. – Austin Jun 28 '15 at 03:27
  • I got a similar (little bit different) example at https://libav.org/doxygen/master/examples.html. , in that output.c file is doing bit similar stuff. I will check and try to understand. May be after that I will be able to comeup with test code. – Austin Jun 29 '15 at 09:03
  • Hi golden, it is really becoming difficult for me. Can you please help me in this. If possible can we have a skype chat. – Austin Jul 01 '15 at 03:23
  • Hi, I'm not sure I can help, but we can try that. My skype is chest3r_is_xxx, add me. I am working fulltime, but I will be available at around 19:00(my time) - 17:00 (5:00pm) UTC or later. What is your time zone? – golstar Jul 02 '15 at 09:07
  • I am in India. My skype id is palei.kamal , can we talk tomorrow anytime, you suggest anytime, will try to make it. – Austin Jul 02 '15 at 09:35