Hi list,

I already posted once about problems regarding the synchronization of audio and video streams... To recapitulize: I have a multithreaded app, one thread reads live-generated audio and video frames, and another encodes them (using ffmpeg) and streams 'em using live555. The frames are transferred between the two threads using two queues. The first thread stores a video frame every 40ms and an audio frame every 24ms. When I stream out only the video stream, everything works fine. But when I add the audio stream, the video source gets polled way too few times and the video starts lagging behind. I built a small test app that approximately simulates this behaviour (see attachments). The results are the same: if I stream only video, everything works as it should, if I add the audio stream, everything starts to stutter and lag. Could someone please verify that my pipeline (CustomSource -> MPEG1or2AudioFramer -> MPEG1or2AudioRTPSink and CustomSource -> MPEG4ESVideoFramer -> MPEG4ESRTPSink) is set up correctly? And could someone point me into the right direction to synchronize the two streams?
Any answers are highly appreciated!

cheers, Severin
#include "FFMPEGEncoder.hh"
#include "FFMPEGDummySource.hh"

#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"

//Define which streams to generate (only  streaming audio does not seem to 
work, though...)
#define VIDEO
#define AUDIO

//#define AUDIO_USING_ADUS // not working...?

#define FORMAT              "avi"
#define VIDEO_CODEC         "mpeg4"
#define AUDIO_CODEC         "mp2"
#define VIDEO_BIT_RATE      400000
#define VIDEO_WIDTH         768
#define VIDEO_HEIGHT        432
#define AUDIO_BIT_RATE      128000
#define AUDIO_SAMPLE_RATE   48000
#define AUDIO_NUM_CHANNELS  2

char const* remoteStreamName = "test.sdp"; 
const char* dssNameOrAddress = "127.0.0.1";

UsageEnvironment*       env;
FFMPEGEncoder*          encoder;

FramedSource*           videoSource;
FramedSource*           audioSource;
RTPSink*                videoSink;
RTPSink*                audioSink;

// function declarations
Boolean awaitConfigInfo(RTPSink* sink);
void play();

const char* programName;

int main(int argc, char** argv) 
{  
    programName = argv[0];
    
    encoder = new FFMPEGEncoder(FORMAT, VIDEO_CODEC, AUDIO_CODEC, VIDEO_WIDTH, 
VIDEO_HEIGHT, VIDEO_BIT_RATE, 
                                AUDIO_BIT_RATE, AUDIO_SAMPLE_RATE, 
AUDIO_NUM_CHANNELS);
    
    // Begin by setting up our usage environment:
    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
    env = BasicUsageEnvironment::createNew(*scheduler);
    
    // Create a 'Darwin injector' object:
    DarwinInjector* injector = DarwinInjector::createNew(*env, programName);
    
    // Create 'groupsocks' for RTP and RTCP:
    char* destinationAddressStr = "127.0.0.1";
    const unsigned short rtpPortNumAudio = 6666;
    const unsigned short rtcpPortNumAudio = rtpPortNumAudio+1;
    const unsigned short rtpPortNumVideo = 8888;
    const unsigned short rtcpPortNumVideo = rtpPortNumVideo+1;
    const unsigned char ttl = 7;
    
    struct in_addr destinationAddress;
    destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
    const Port rtpPortAudio(rtpPortNumAudio);
    const Port rtcpPortAudio(rtcpPortNumAudio);
    const Port rtpPortVideo(rtpPortNumVideo);
    const Port rtcpPortVideo(rtcpPortNumVideo);
    
    const unsigned maxCNAMElen = 100;
    unsigned char CNAME[maxCNAMElen+1];
    gethostname((char*)CNAME, maxCNAMElen);
    CNAME[maxCNAMElen] = '\0';
#ifdef AUDIO    
    ////////// AUDIO //////////
    struct in_addr dummyDestAddress;
    dummyDestAddress.s_addr = 0;
    Groupsock rtpGroupsockAudio(*env, destinationAddress, rtpPortAudio, ttl);
    Groupsock rtcpGroupsockAudio(*env, destinationAddress, rtcpPortAudio, ttl);
    
    // Create a 'MPEG Audio RTP' sink from the RTP 'groupsock':
#ifdef AUDIO_USING_ADUS
    audioSink= MP3ADURTPSink::createNew(*env, &rtpGroupsockAudio, 96);
#else
    audioSink = MPEG1or2AudioRTPSink::createNew(*env, &rtpGroupsockAudio);
#endif    
    // Create (and start) a 'RTCP instance' for this RTP sink:
    const unsigned estimatedSessionBandwidthAudio = AUDIO_BIT_RATE/1000; // in 
kbps; for RTCP b/w share
    RTCPInstance* audioRTCP =
        RTCPInstance::createNew(*env, &rtcpGroupsockAudio,
                                estimatedSessionBandwidthAudio, CNAME,
                                audioSink, NULL);
    // Note: This starts RTCP running automatically
    
    // Add these to our 'Darwin injector':
    injector->addStream(audioSink, audioRTCP);
    ////////// END AUDIO //////////
#endif    
#ifdef VIDEO
    ////////// VIDEO //////////
    Groupsock rtpGroupsockVideo(*env, destinationAddress, rtpPortVideo, ttl);
    Groupsock rtcpGroupsockVideo(*env, destinationAddress, rtcpPortVideo, ttl);
    
    // Create a 'MPEG-4 Video RTP' sink from the RTP 'groupsock':
    videoSink = MPEG4ESVideoRTPSink::createNew(*env, &rtpGroupsockVideo, 96);
    
    // HACK, specifically for MPEG-4 video:
    // Before we can use this RTP sink, we need its MPEG-4 'config' information 
(for
    // use in the SDP description).  Unfortunately, this config information 
depends
    // on the the properties of the MPEG-4 input data.  Therefore, we need to 
start
    // 'playing' this RTP sink from the input source now, and wait until we get
    // the needed config information, before continuing:
    // that we need:
    *env << "Beginning streaming...\n";
    play();
    
    if (!awaitConfigInfo(videoSink)) 
    {
        *env << "Failed to get MPEG-4 'config' information from input file: "
        << env->getResultMsg() << "\n";
        exit(1);
    }
    
    // Create (and start) a 'RTCP instance' for this RTP sink:
    const unsigned estimatedSessionBandwidthVideo = VIDEO_BIT_RATE/1000; // in 
kbps; for RTCP b/w share
    RTCPInstance* videoRTCP =
        RTCPInstance::createNew(*env, &rtcpGroupsockVideo,
                                estimatedSessionBandwidthVideo, CNAME,
                                videoSink, NULL);
    // Note: This starts RTCP running automatically
    
    // Add these to our 'Darwin injector':
    injector->addStream(videoSink, videoRTCP);
    ////////// END VIDEO //////////
#endif    
    
    // Next, specify the destination Darwin Streaming Server:
    if (!injector->setDestination(dssNameOrAddress, remoteStreamName,
                                  programName, "LIVE555 Streaming Media")) 
    {
        *env << "injector->setDestination() failed: "
        << env->getResultMsg() << "\n";
        exit(1);
    }
    
    *env << "Play this stream (from the Darwin Streaming Server) using the 
URL:\n"
        << "\trtsp://" << dssNameOrAddress << "/" << remoteStreamName << "\n";
    
    env->taskScheduler().doEventLoop(); // does not return
    
    return 0; // only to prevent compiler warning
}

void afterPlaying(void* clientData)
{
    *env << "...done reading from file\n";
    
    Medium::close(videoSource);
    Medium::close(audioSource);
    
    // Start playing once again:
    play();
}

void play() 
{
#ifdef VIDEO
    // Open the video input
    FFMPEGDummySource* videoFileSource = FFMPEGDummySource::createNew(*env, 
encoder, VIDEO_SOURCE);
    if (videoFileSource == NULL) 
    {
        *env << "Unable to open video input\n";
        exit(1);
    }
    
    FramedSource* videoES = videoFileSource;
    videoSource = MPEG4VideoStreamFramer::createNew(*env, videoES);
#endif
#ifdef AUDIO    
    // Open the audio input
    FFMPEGDummySource* audioFileSource = FFMPEGDummySource::createNew(*env, 
encoder, AUDIO_SOURCE);
    if (audioFileSource == NULL) 
    {
        *env << "Unable to open audio input\n";
        exit(1);
    }
    
    FramedSource* audioES = audioFileSource;
    audioSource = MPEG1or2AudioStreamFramer::createNew(*env, audioES);

#ifdef AUDIO_USING_ADUS
    audioSource = ADUFromMP3Source::createNew(*env, audioSource);
    if (audioSource == NULL) {
      *env << "Unable to create a MP3->ADU filter for the source\n";
      exit(1);
    }
#endif
#endif    
    // Finally, start playing:
#ifdef VIDEO
    *env << "Beginning to read from video input...\n";
    videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
#endif
#ifdef AUDIO
    *env << "Beginning to read from audio input...\n";
    audioSink->startPlaying(*audioSource, afterPlaying, audioSink);
#endif
}

static char doneFlag = 0;

static void checkForAuxSDPLine(void* clientData) 
{
    RTPSink* sink = (RTPSink*)clientData;
    if (sink->auxSDPLine() != NULL) 
    {
        // Signal the event loop that we're done:
        doneFlag = ~0;
    } else 
    {
        // No luck yet.  Try again, after a brief delay:
        int uSecsToDelay = 100000; // 100 ms
        env->taskScheduler().scheduleDelayedTask(uSecsToDelay,
                                                 (TaskFunc*)checkForAuxSDPLine, 
sink);
    }
}

Boolean awaitConfigInfo(RTPSink* sink) 
{
    // Check whether the sink's 'auxSDPLine()' is ready:
    checkForAuxSDPLine(sink);
    
    env->taskScheduler().doEventLoop(&doneFlag);
    
    char const* auxSDPLine = sink->auxSDPLine();
    return auxSDPLine != NULL;
}
/*
 *  FFMPEGDummySource.cpp
 *  live555 Test
 *
 *  Created by Severin Schoepke on 05.06.07.
 *  Copyright 2007 Neotrivium AG. All rights reserved.
 *
 */

#include "FFMPEGDummySource.hh"
#include <math.h>

#define LOGGING

static long diffInMicroseconds(struct timeval t1, struct timeval t2)
{
    struct timeval diff;
    timersub(&t1, &t2, &diff);
    
    return abs(1000000 * diff.tv_sec + diff.tv_usec);
}

static long timevalInMicroseconds(struct timeval t)
{
    return abs(1000000 * t.tv_sec + t.tv_usec);
}

FFMPEGDummySource* FFMPEGDummySource::createNew(UsageEnvironment& env, 
                                                FFMPEGEncoder *encoder,
                                                int sourceType)
{
    return new FFMPEGDummySource(env, encoder, sourceType);
}

FFMPEGDummySource::FFMPEGDummySource(UsageEnvironment& env, 
                                     FFMPEGEncoder *encoder,
                                     int sourceType) : FramedSource(env) 
{
     _encoder       = encoder;
     _sourceType    = sourceType;
     _frameDuration = sourceType == AUDIO_SOURCE ? 
         (encoder->audioFrameSize()*1000000/encoder->sampleRate()) : 
         (1000000/encoder->frameRate());
     
     _inputBuffer    = NULL;
     _inputBufferSize= 0;
     
     gettimeofday(&_startTime, NULL);
     _lastTime = _startTime;
     
     _streamedFrames    = 0;
     _streamedBytes     = 0;
}

FFMPEGDummySource::~FFMPEGDummySource() {}

void FFMPEGDummySource::generateDummyImage(void)
{
    if(!_inputBuffer)
    {    
        _inputBuffer = avcodec_alloc_frame();
        if(!_inputBuffer) return;
    
        _inputBufferSize       = avpicture_get_size(_encoder->pixelFormat(), 
_encoder->width(), _encoder->height());
        uint8_t *picture_buf   = (uint8_t*)av_malloc(_inputBufferSize);
    
        if (!picture_buf) {
            av_free(_inputBuffer);
            _inputBuffer = NULL;
            _inputBufferSize = 0;
            return;
        }
    
        avpicture_fill((AVPicture*)_inputBuffer, picture_buf, 
_encoder->pixelFormat(), _encoder->width(), _encoder->height());
    }
    
    struct timeval t;
    gettimeofday(&t, NULL);
    
    int x, y;
    
    float bias      = 0.5 * sin(timevalInMicroseconds(t)/1000000.) + 0.5;
    int focus_col   = (int)((float)_encoder->width() * bias);
    
    AVFrame *picture = (AVFrame*)_inputBuffer;
    
    for(y = 0; y < _encoder->height(); y++) {
        for(x = 0; x < _encoder->width(); x++) {
            int val = 255 - abs(x - focus_col); if(val < 0) val = 0;
            picture->data[0][y * picture->linesize[0] + x] = val;
        }
    }
    
    int deviation = (int)(32. * bias);
    for(y = 0; y < _encoder->height()/2; y++) {
        for(x = 0; x <_encoder->width()/2; x++) {
            picture->data[1][y * picture->linesize[1] + x] = 128 + deviation;
            picture->data[2][y * picture->linesize[2] + x] = 255 - deviation;
        }
    }
}


float t = 0., tincr = 0., tincr2 = 0.;

void FFMPEGDummySource::generateDummySound(void)
{    
    if(tincr == 0. || tincr2 == 0.)
    {
        tincr   = 2. * M_PI * 110.0 / _encoder->sampleRate();
        tincr2  = 2. * M_PI * 110.0 / _encoder->sampleRate() / 
_encoder->sampleRate();
    }
    
    if(!_inputBuffer) 
    {
        _inputBufferSize = _encoder->audioFrameSize() * _encoder->numChannels() 
* sizeof(int16_t);
        _inputBuffer     = (int16_t*)malloc(_inputBufferSize);
    }
        
    struct timeval time;
    gettimeofday(&time, NULL);
    
    float bias = 0.5 * sin(timevalInMicroseconds(time)/1000000.) + 0.5;
    
    int16_t *q = (int16_t*)_inputBuffer;
    
    int j, i;
    for(j = 0; j < _encoder->audioFrameSize(); j++) 
    {    
        int v = (int)(bias * sin(t) * 10000);
        for(i = 0; i < _encoder->numChannels(); i++)
            *q++ = v;
        t       += tincr;
        tincr   += tincr2;
    }
    
    if(t > 100000.)
    {
        t = tincr = tincr2 = 0.;
    }
}

void FFMPEGDummySource::doGetNextFrame() 
{
#ifdef LOGGING  
    printf("%s::doGetNextFrame() ", _sourceType == AUDIO_SOURCE ? "Audio" : 
"Video");
#endif
    // We haven't provided any data (yet)
    fFrameSize = 0;
    
    struct timeval current;

    // if we return when we don't have a frame ready, the source will never be 
polled again???
#if 1    
    do {
        gettimeofday(&current, NULL);        
    } while(diffInMicroseconds(current, _lastTime) < _frameDuration); 
#else
    gettimeofday(&current, NULL);
    if(diffInMicroseconds(current, _lastTime) < _frameDuration) return;
#endif

    _lastTime = current;
    deliverFrame();
}

void FFMPEGDummySource::deliverFrame() 
{
    struct timeval timestamp;
    gettimeofday(&timestamp, NULL);
    
    void *buffer            = NULL;
    unsigned int out_size   = 0;
    
    if(_sourceType == AUDIO_SOURCE)
    {
        generateDummySound();
        uint16_t *sound = (uint16_t*)_inputBuffer;
        buffer          = (int16_t*)malloc(_inputBufferSize);
        out_size        = _encoder->encodeAudio(buffer, sound, 
_inputBufferSize);
    } 
    else
    {
        generateDummyImage();
        AVFrame *picture = (AVFrame*)_inputBuffer;
        buffer           = (uint8_t*)malloc(_inputBufferSize);
        out_size         = _encoder->encodeVideo(buffer, picture->data[0], 
_inputBufferSize);
    }
    
    if(out_size > 0) {      
        
        int toCopy = out_size;
        if(out_size > fMaxSize) {
            toCopy = fMaxSize;
            fNumTruncatedBytes = out_size - fMaxSize;
        }
        
        // write frame to output
        memcpy(fTo, buffer, toCopy);
        fFrameSize              = toCopy;
        fPresentationTime       = timestamp;
        fDurationInMicroseconds = _frameDuration;
        
        _streamedFrames ++;
        _streamedBytes  += toCopy;
        
        free(buffer);
        
#ifdef LOGGING        
        printf("...delivered %i bytes of data (during %i usec, presentation 
time is %i:%i)\n", 
            fFrameSize, fDurationInMicroseconds, fPresentationTime.tv_sec, 
fPresentationTime.tv_usec);
        
        float kbits   = _streamedBytes * 8. / 1000.;
        float secs    = diffInMicroseconds(timestamp, _startTime)/1000000.;
        printf("(streamed %i frames / %i bytes / %f seconds in total (= %f 
kbit/sec))\n", _streamedFrames, _streamedBytes, secs, kbits/secs);
#endif
    }
    FFMPEGDummySource::afterGetting(this);
}

/*
 *  FFMPEGDummySource.h
 *  live555 Test
 *
 *  Created by Severin Schoepke on 05.06.07.
 *  Copyright 2007 Neotrivium AG. All rights reserved.
 *
 */

#ifndef FFMPEG_DUMMY_SOURCE_H
#define FFMPEG_DUMMY_SOURCE_H

#include "FFMPEGEncoder.hh"

#ifndef _FRAMED_SOURCE_HH
#include "FramedSource.hh"
#endif

#define AUDIO_SOURCE 0
#define VIDEO_SOURCE 1

class FFMPEGDummySource: public FramedSource 
{
public:
    static FFMPEGDummySource* createNew(UsageEnvironment& env, 
                                        FFMPEGEncoder *encoder,
                                        int sourceType);
    
protected:
    FFMPEGDummySource(UsageEnvironment& env, 
                      FFMPEGEncoder *encoder,
                      int sourceType);
    virtual         ~FFMPEGDummySource();
    
private:
    virtual void    doGetNextFrame();
    void            deliverFrame();
    
    void            generateDummyImage(void);
    void            generateDummySound(void);
    
    FFMPEGEncoder   *_encoder;
    int             _sourceType;
    int             _frameDuration;
    struct timeval  _startTime;
    struct timeval  _lastTime;
    
    void            *_inputBuffer;
    unsigned int    _inputBufferSize;
    
    unsigned int    _streamedFrames;
    unsigned int    _streamedBytes;
};

#endif
/*
 *  FFMPEGEncoder.cpp
 *  live555 Test
 *
 *  Created by Severin Schoepke on 05.06.07.
 *  Copyright 2007 Neotrivium AG. All rights reserved.
 *
 */

#include "FFMPEGEncoder.hh"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>

//#define WRITE_TO_FILE
//#define BENCHMARK

class Benchmark
{
public:
    Benchmark() {};
    virtual ~Benchmark() {};
    
    void start();
    void stop();
    void print(const char *title);

private:
    struct rusage _start, _stop;
};

void Benchmark::start()
{
  getrusage(RUSAGE_SELF, &_start);
}  

void Benchmark::stop()
{
  getrusage(RUSAGE_SELF, &_stop);
}

void Benchmark::print(const char *title)
{
  int userSecs  = _stop.ru_utime.tv_sec - _start.ru_utime.tv_sec; 
  int userUsecs = _stop.ru_utime.tv_usec - _start.ru_utime.tv_usec; 
  int sysSecs   = _stop.ru_stime.tv_sec - _start.ru_stime.tv_sec; 
  int sysUsecs  = _stop.ru_stime.tv_usec - _start.ru_stime.tv_usec;
  
  printf("%s: user time = %i:%i, system time = %i:%i\n", title, userSecs, 
userUsecs, sysSecs, sysUsecs);
}

FFMPEGEncoder::FFMPEGEncoder(const char *format, 
                             const char *vcodec, 
                             const char *acodec, 
                             int width, 
                             int height, 
                             int videoBitRate, 
                             int audioBitRate, 
                             int sampleRate, 
                             int numChannels)
{
    av_register_all();
    
    // search output format
    _fmt = NULL;
    _fmt = guess_format(format, NULL, NULL);
    if(!_fmt) {
        printf("output format not found.\n");
        exit(1);
    }
    
    // allocate the output media context
    _oc = av_alloc_format_context();
    if (!_oc) {
        printf("could not alloc memory.\n");
        exit(1); 
    }
    _oc->oformat        = _fmt;
    
#ifdef WRITE_TO_FILE
    snprintf(_oc->filename, sizeof(_oc->filename), "dump.%s", format);
#else
    _oc->oformat->flags |= AVFMT_NOFILE;
#endif
    
    // add video stream
    _video_st = av_new_stream(_oc, 0);
    if (!_video_st) {
        printf("could not alloc video stream.\n");
        exit(1); 
    }
    
    AVCodecContext *video_codec;
    
    video_codec               = _video_st->codec;
    video_codec->codec_id     = _fmt->video_codec;
    video_codec->codec_type   = CODEC_TYPE_VIDEO;
    
    video_codec->bit_rate     = videoBitRate;
    video_codec->width        = width;
    video_codec->height       = height;
    video_codec->time_base    = (AVRational){1,25}; //frame rate
    video_codec->gop_size     = 12;                 //num frames between 
I-frames   
    video_codec->pix_fmt      = PIX_FMT_YUV420P;
    
    if(!strcmp(_oc->oformat->name, "mp4") || !strcmp(_oc->oformat->name, "mov") 
|| !strcmp(_oc->oformat->name, "3gp"))
        video_codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    
    // add audio stream
    _audio_st = av_new_stream(_oc, 0);
    if (!_audio_st) {
        printf("could not alloc audio stream.\n");
        exit(1); 
    }
    
    AVCodecContext *audio_codec;
    
    audio_codec                 = _audio_st->codec;
    audio_codec->codec_id       = _fmt->audio_codec;
    audio_codec->codec_type     = CODEC_TYPE_AUDIO;
    
    audio_codec->bit_rate       = audioBitRate;
    audio_codec->sample_rate    = sampleRate;
    audio_codec->channels       = numChannels;
    
    // set the output parameters (must be done even if no parameters).
    if (av_set_parameters(_oc, NULL) < 0) {
        printf("invalid output format parameters.\n");
        exit(1); 
    }
    
    // prepare video codec
    AVCodec *video;
    video = avcodec_find_encoder_by_name(vcodec);
    if (!video) {
        printf("could not find video codec (%s).\n", vcodec);
        exit(1); 
    }
    
    if (avcodec_open(video_codec, video) < 0) {
        printf("could not open video codec.\n");
        exit(1); 
    }
    
    _tmp_frame = avcodec_alloc_frame();    
    if (! _tmp_frame) {
        printf("Could not alloc frame.\n");
        exit(1);
    }
    
    _video_outbuf_size   = 500000;
    _video_outbuf        = (uint8_t*)av_malloc(_video_outbuf_size);
    
    // prepare audio codec
    AVCodec *audio;
    audio = avcodec_find_encoder_by_name(acodec);
    if (!audio) {
        printf("could not find audio codec (%s).\n", acodec);
        exit(1); 
    }
    
    if (avcodec_open(audio_codec, audio) < 0) {
        printf("could not open audio codec.\n");
        exit(1); 
    }
    
    _audio_outbuf_size  = 500000;
    _audio_outbuf       = (uint8_t*)av_malloc(_audio_outbuf_size);

#ifdef WRITE_TO_FILE
    if (!(_fmt->flags & AVFMT_NOFILE)) {
        if (url_fopen(&_oc->pb, _oc->filename, URL_WRONLY) < 0) {
            printf("%s could not open file %s.\n", _oc->filename);
        }
    }

    dump_format(_oc, 0, _oc->filename, 1);
    av_write_header(_oc);
#else
    dump_format(_oc, 0, "NO FILE", 1);
#endif

    _benchmark = new Benchmark();
}

FFMPEGEncoder::~FFMPEGEncoder()
{
    // close video
    avcodec_close(_video_st->codec);
    av_free(_video_outbuf);
    
    // close audio
    avcodec_close(_audio_st->codec);
    av_free(_audio_outbuf);
    
    // free streams
    unsigned int i;
    for(i = 0; i < _oc->nb_streams; i++) {
        av_freep(&_oc->streams[i]->codec);
        av_freep(&_oc->streams[i]);
    }
    
    av_free(_oc);
}

int FFMPEGEncoder::encodeVideo(void *output, void *input, unsigned int 
input_size) 
{
#ifdef BENCHMARK
    _benchmark->start();
#endif

    if(input_size > _video_outbuf_size) {
        printf("Too much data, I can't write that.\n");
        return 0;
    }
    
    AVCodecContext *c = _video_st->codec;
    
    avpicture_fill((AVPicture *)_tmp_frame, (uint8_t*)input, c->pix_fmt, 
c->width, c->height);
    int out_size = avcodec_encode_video(c, _video_outbuf, _video_outbuf_size, 
_tmp_frame);    
    
    if(out_size == 0) return 0;

#ifdef WRITE_TO_FILE
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.size            = out_size;
    pkt.pts             = _video_pts;
    pkt.stream_index    = _video_st->index;
    pkt.data            = _video_outbuf;
    if(c->coded_frame->key_frame) 
        pkt.flags       |= PKT_FLAG_KEY;

    if (av_write_frame(_oc, &pkt) != 0) {
        printf("Error writing video frame to file.\n");
    }
#endif
    
    memcpy(output, _video_outbuf, out_size);
    
#ifdef BENCHMARK
    _benchmark->stop();
    _benchmark->print("Video Encoding");
#endif

    return out_size;
}

int FFMPEGEncoder::encodeAudio(void *output, void *input, unsigned int 
input_size) 
{
#ifdef BENCHMARK
    _benchmark->start();
#endif

    if(input_size > _audio_outbuf_size) {
        printf("Too much data, I can't write that.\n");
        return 0;
    }
    
    AVCodecContext *c = _audio_st->codec;
    
    int out_size = avcodec_encode_audio(c, _audio_outbuf, input_size, 
(int16_t*)input); 
    
    if(out_size == 0) return 0;

#ifdef WRITE_TO_FILE
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.size            = out_size;
    pkt.pts             = _audio_pts;
    pkt.stream_index    = _audio_st->index;
    pkt.data            = _audio_outbuf;
    pkt.flags          |= PKT_FLAG_KEY;

    if (av_write_frame(_oc, &pkt) != 0) {
        printf("Error writing audio frame to file.\n");
    }
#endif
    
    memcpy(output, _audio_outbuf, out_size);    
    
#ifdef BENCHMARK
    _benchmark->stop();
    _benchmark->print("Audio Encoding");
#endif

    return out_size;
}

PixelFormat FFMPEGEncoder::pixelFormat()
{
    return _video_st->codec->pix_fmt;
}

int FFMPEGEncoder::width()
{
    return _video_st->codec->width;
}

int FFMPEGEncoder::height()
{
    return _video_st->codec->height;
}

int FFMPEGEncoder::frameRate()
{
    return _video_st->codec->time_base.den;
}

int FFMPEGEncoder::videoBitRate()
{
    return _video_st->codec->bit_rate;
}

int FFMPEGEncoder::audioBitRate()
{
    return _audio_st->codec->bit_rate;
}

int FFMPEGEncoder::audioFrameSize()
{
    return _audio_st->codec->frame_size;
}

int FFMPEGEncoder::sampleRate()
{
    return _audio_st->codec->sample_rate;
}

int FFMPEGEncoder::numChannels()
{
    return _audio_st->codec->channels;
}
/*
 *  FFMPEGEncoder.h
 *  live555 Test
 *
 *  Created by Severin Schoepke on 05.06.07.
 *  Copyright 2007 Neotrivium AG. All rights reserved.
 *
 */

#ifndef FFMPEG_ENCODER_H
#define FFMPEG_ENCODER_H

#include <ffmpeg/avformat.h>

class Benchmark;

class FFMPEGEncoder
{
public:
    FFMPEGEncoder(const char *format, 
                    const char *vcodec, 
                    const char *acodec, 
                    int width, 
                    int height, 
                    int videoBitRate, 
                    int audioBitRate, 
                    int sampleRate, 
                    int numChannels);
    virtual         ~FFMPEGEncoder();
    
    int encodeVideo(void *output, void *input, unsigned int input_size);
    int encodeAudio(void *output, void *input, unsigned int input_size);
    
    PixelFormat pixelFormat();
    int         width();
    int         height();
    int         frameRate();
    int         videoBitRate();
    int         audioBitRate();
    int         audioFrameSize();
    int         sampleRate();
    int         numChannels();
    
private:
    
    AVOutputFormat  *_fmt;
    AVFormatContext *_oc;
    
    AVStream        *_audio_st; 
    AVStream        *_video_st;
    
    int64_t         _audio_pts;
    int64_t         _video_pts;
    
    uint8_t         *_audio_outbuf;
    unsigned int    _audio_outbuf_size;
    
    uint8_t         *_video_outbuf;
    unsigned int    _video_outbuf_size;
    AVFrame         *_tmp_frame;
    
    Benchmark       *_benchmark;
};
#endif
_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to