/***************************************************************************
                          RTPAVProfileSampleBasedAudioSender.cc  -  description
                             -------------------
    begin                : Sat Sep 21 2002
    copyright            : (C) 2002 by Matthias Oppitz
    email                : matthias.oppitz@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include <audiofile.h>

#include "RTPAVProfileSampleBasedAudioSender.h"


Define_Module(RTPAVProfileSampleBasedAudioSender);


void RTPAVProfileSampleBasedAudioSender::initializeSenderModule(RTPInnerPacket *rinp) {
    RTPPayloadSender::initializeSenderModule(rinp);
    _startTime = simTime();
};


void RTPAVProfileSampleBasedAudioSender::openSourceFile(const char *fileName) {
    _audioFile = afOpenFile(fileName, "r", 0);
    if (_audioFile == AF_NULL_FILEHANDLE) {
        opp_error("sender module: error opening audio file");
    }
    else {
        if (_samplingRate != afGetRate(_audioFile, AF_DEFAULT_TRACK)) {
            opp_error("sender module: audio file has wrong sampling rate");
        }
        else {
            int sampleFormat, sampleWidth;
            afGetSampleFormat(_audioFile, AF_DEFAULT_TRACK, &sampleFormat, &sampleWidth);
            if (_sampleWidth != sampleWidth) {
                opp_error("sender module: audio file has wrong sample width");
            }
            else {
                if (_numberOfChannels != afGetChannels(_audioFile, AF_DEFAULT_TRACK)) {
                    opp_error("sender module: audio file has wrong number of channels");
                }
            }
        }
    }
};


void RTPAVProfileSampleBasedAudioSender::closeSourceFile() {
    int closeReturnValue = afCloseFile(_audioFile);
    if (closeReturnValue) {
        opp_error("sender module: error closing audio file");
    }
};


void RTPAVProfileSampleBasedAudioSender::play() {
    if (_status == STOPPED) {
        _status = PLAYING;
        RTPSenderStatusMessage *rssm = new RTPSenderStatusMessage("PLAYING");
        rssm->setStatus("PLAYING");
        RTPInnerPacket *rinp = new RTPInnerPacket("senderStatus(PLAYING)");
        rinp->senderModuleStatus(_ssrc, rssm);
        send(rinp, "toProfile");
        sendPacket();
    }
    else if (_status == PLAYING) {
        EV << "sender module: already playing" << endl;
    }
};


void RTPAVProfileSampleBasedAudioSender::stop() {
    RTPPayloadSender::stop();
    afSeekFrame(_audioFile, AF_DEFAULT_TRACK, 0);
};


void RTPAVProfileSampleBasedAudioSender::seekTime(simtime_t moment) {
    if (_status = STOPPED) {
        int frameNumber = (int)(moment * (float)_samplingRate);
        afSeekFrame(_audioFile, AF_DEFAULT_TRACK, frameNumber);
        RTPSenderStatusMessage *rssm = new RTPSenderStatusMessage("SEEKED");
        rssm->setStatus("SEEKED");
        RTPInnerPacket *rinp = new RTPInnerPacket("senderModuleStatus(SEEKED)");
        rinp->senderModuleStatus(_ssrc, rssm);
        send(rinp, "toProfile");
    }
    else {
        EV << "sender module: seeking not allowed while playing" << endl;
    };
};


void RTPAVProfileSampleBasedAudioSender::seekByte(int position) {
    if (_status = STOPPED) {
        int frameNumber = (int)(((float)position) / afGetFrameSize(_audioFile, AF_DEFAULT_TRACK, 0));
        afSeekFrame(_audioFile, AF_DEFAULT_TRACK, frameNumber);
        RTPSenderStatusMessage *rssm = new RTPSenderStatusMessage("SEEKED");
        rssm->setStatus("SEEKED");
        RTPInnerPacket *rinp = new RTPInnerPacket("senderModuleStatus(SEEKED)");
        rinp->senderModuleStatus(_ssrc, rssm);
        send(rinp, "toProfile");
    }
    else {
        EV << "sender module: seeking not allowed while playing" << endl;
    };
};


bool RTPAVProfileSampleBasedAudioSender::sendPacket() {
    RTPPacket *packet = new RTPPacket();
    int bytesPerSample = (int)afGetFrameSize(_audioFile, AF_DEFAULT_TRACK, 0);
    int maxDataSize = _mtu - packet->getHeaderLength();
    maxDataSize = maxDataSize - (maxDataSize % bytesPerSample);

    // AV profile: packetization interval for audio is 20 ms
    simtime_t packetizationInterval = 0.020;

    int samplesPerPacketNeeded = (int)(((float)_samplingRate) * packetizationInterval);
    int samplesPerPacketPossible = maxDataSize / bytesPerSample;

    int samplesInPacket;

    // do samples for packetization interval fit into one packet?
    // if not put less samples in a packet
    if (samplesPerPacketPossible < samplesPerPacketNeeded) {
        packetizationInterval = ((float)samplesPerPacketPossible) / ((float)_samplingRate);
        samplesInPacket = samplesPerPacketPossible;
    }
    else {
        samplesInPacket = samplesPerPacketNeeded;
    }

    int dataSize = samplesInPacket * bytesPerSample;

    packet->setPayloadType(_payloadType);
    packet->setSequenceNumber(_sequenceNumber++);
    packet->setTimeStamp(_timeStampBase + (simTime() - _startTime) * (float)_samplingRate);
    packet->setSSRC(_ssrc);
    void *sampleData = malloc(dataSize);
    int samplesRead = afReadFrames(_audioFile, AF_DEFAULT_TRACK, sampleData, samplesInPacket);
    packet->addPar("data") = sampleData;
    packet->addBitLength(dataSize);
    RTPInnerPacket *rinp = new RTPInnerPacket("getData()");
    rinp->dataOut(packet);
    send(rinp, "toProfile");
    if (afTellFrame(_audioFile, AF_DEFAULT_TRACK) >= afGetFrameCount(_audioFile, AF_DEFAULT_TRACK)) {
        return false;
    }
    else {
        _reminderMessage = new cMessage();
        scheduleAt(simTime() + packetizationInterval, _reminderMessage);
        return true;
    };
};
