/*
 *  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);
}

