Audiostream stats

This commit is contained in:
Guillaume Beraudo 2013-07-19 15:03:27 +02:00
parent b96b8e793e
commit 9993524ada
9 changed files with 203 additions and 69 deletions

View file

@ -2230,6 +2230,27 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
}
}
// this method should be put in mediastreamer but has been kept here, private,
// to save time.
void linphone_call_stream_stats_hack(MediaStream* stream, LinphoneCallStats *stats, OrtpEvent *ev) {
OrtpEventType evt=ortp_event_get_type(ev);
OrtpEventData *evd=ortp_event_get_data(ev);
if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
stats->round_trip_delay = rtp_session_get_round_trip_propagation(stream->session);
if(stats->received_rtcp != NULL) freemsg(stats->received_rtcp);
stats->received_rtcp = evd->packet;
evd->packet = NULL;
update_local_stats(stats,stream);
} else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
memcpy(&stats->jitter_stats, rtp_session_get_jitter_stats(stream->session), sizeof(jitter_stats_t));
if(stats->sent_rtcp != NULL) freemsg(stats->sent_rtcp);
stats->sent_rtcp = evd->packet;
evd->packet = NULL;
update_local_stats(stats,stream);
}
}
void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){
LinphoneCore* lc = call->core;
int disconnect_timeout = linphone_core_get_nortp_timeout(call->core);
@ -2259,6 +2280,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
#ifdef VIDEO_ENABLED
if (call->videostream!=NULL) {
OrtpEvent *ev;
LinphoneCallStats *stats=&call->stats[LINPHONE_CALL_STATS_VIDEO];
/* Ensure there is no dangling ICE check list. */
if (call->ice_session == NULL) call->videostream->ms.ice_check_list = NULL;
@ -2272,24 +2294,10 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
OrtpEventData *evd=ortp_event_get_data(ev);
if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
} else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->ms.session);
if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL)
freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp);
call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet;
evd->packet = NULL;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO],(MediaStream*)call->videostream);
} else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
linphone_call_stream_stats_hack(&call->videostream->ms, stats, ev);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
} else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->ms.session), sizeof(jitter_stats_t));
if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL)
freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp);
call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet;
evd->packet = NULL;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO],(MediaStream*)call->videostream);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
lc->vtable.call_stats_updated(lc, call, stats);
} else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
|| (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
handle_ice_events(call, ev);
@ -2300,6 +2308,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
#endif
if (call->audiostream!=NULL) {
OrtpEvent *ev;
LinphoneCallStats *stats=&call->stats[LINPHONE_CALL_STATS_AUDIO];
/* Ensure there is no dangling ICE check list. */
if (call->ice_session == NULL) call->audiostream->ms.ice_check_list = NULL;
@ -2315,24 +2324,10 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
} else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
} else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->ms.session);
if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL)
freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp);
call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet;
evd->packet = NULL;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO],(MediaStream*)call->audiostream);
} else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
linphone_call_stream_stats_hack(&call->audiostream->ms, stats, ev);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
} else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->ms.session), sizeof(jitter_stats_t));
if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL)
freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp);
call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet;
evd->packet = NULL;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO],(MediaStream*)call->audiostream);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
lc->vtable.call_stats_updated(lc, call, stats);
} else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
|| (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
handle_ice_events(call, ev);

View file

@ -730,6 +730,7 @@ typedef enum _LinphoneToneID{
void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID id);
bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc);
void linphone_call_stream_stats_hack(MediaStream* stream, LinphoneCallStats *stats, OrtpEvent *ev);
#ifdef __cplusplus
}
#endif

View file

@ -11,6 +11,7 @@ linphone_daemon_SOURCES=daemon.cc \
commands/audio-codec-toggle.cc \
commands/audio-stream-start.cc \
commands/audio-stream-stop.cc \
commands/audio-stream-stats.cc \
commands/call.cc \
commands/call-stats.cc \
commands/call-status.cc \
@ -41,6 +42,7 @@ linphone_daemon_SOURCES=daemon.cc \
commands/audio-codec-toggle.h \
commands/audio-stream-start.h \
commands/audio-stream-stop.h \
commands/audio-stream-stats.h \
commands/call.h \
commands/call-stats.h \
commands/call-status.h \

View file

@ -0,0 +1,46 @@
#include "audio-stream-stats.h"
using namespace std;
AudioStreamStatsCommand::AudioStreamStatsCommand() :
DaemonCommand("audio-stream-stats", "audio-stream-stats <stream id>", "Return stats of a given audio stream.") {
addExample(new DaemonCommandExample("audio-stream-stats 1",
"Status: Ok\n\n"
"Audio-ICE state: Not activated\n"
"Audio-RoundTripDelay: 0.0859833\n"
"Audio-Jitter: 296\n"
"Audio-JitterBufferSizeMs: 47.7778\n"
"Audio-Received-InterarrivalJitter: 154\n"
"Audio-Received-FractionLost: 0\n"
"Audio-Sent-InterarrivalJitter: 296\n"
"Audio-Sent-FractionLost: 0\n"
"Audio-Payload-type-number: 111\n"
"Audio-Clock-rate: 16000\n"
"Audio-Bitrate: 44000\n"
"Audio-Mime: speex\n"
"Audio-Channels: 1\n"
"Audio-Recv-fmtp: vbr=on\n"
"Audio-Send-fmtp: vbr=on"));
addExample(new DaemonCommandExample("audio-stream-stats 2",
"Status: Error\n"
"Reason: No audio stream with such id."));
}
void AudioStreamStatsCommand::exec(Daemon *app, const char *args) {
int sid;
AudioStreamAndOther *stream = NULL;
if (sscanf(args, "%i", &sid) == 1) {
stream = app->findAudioStreamAndOther(sid);
if (!stream) {
app->sendResponse(Response("No audio stream with such id."));
return;
}
} else {
app->sendResponse(Response("No stream specified."));
return;
}
ostringstream ostr;
ostr << AudioStreamStatsResponse(app, stream->stream, &stream->stats, false).getBody();
app->sendResponse(Response(ostr.str().c_str(), Response::Ok));
}

View file

@ -0,0 +1,12 @@
#ifndef COMMAND_AUDIO_STREAM_STATS_H_
#define COMMAND_AUDIO_STREAM_STATS_H_
#include "../daemon.h"
class AudioStreamStatsCommand: public DaemonCommand {
public:
AudioStreamStatsCommand();
virtual void exec(Daemon *app, const char *args);
};
#endif //COMMAND_AUDIO_STREAM_STATS_H_

View file

@ -22,6 +22,7 @@
#include "commands/audio-codec-toggle.h"
#include "commands/audio-stream-start.h"
#include "commands/audio-stream-stop.h"
#include "commands/audio-stream-stats.h"
#include "commands/call.h"
#include "commands/call-stats.h"
#include "commands/call-status.h"
@ -43,6 +44,7 @@
#include "commands/quit.h"
#include "commands/version.h"
#include "private.h"
using namespace std;
#ifndef WIN32
@ -99,25 +101,7 @@ DtmfResponse::DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf) {
ms_free(remote);
}
CallStatsResponse::CallStatsResponse(Daemon *daemon, LinphoneCall *call, const LinphoneCallStats *stats, bool event) {
const LinphoneCallParams *callParams = linphone_call_get_current_params(call);
const char *prefix = "";
ostringstream ostr;
if (event) {
ostr << "Event-type: call-stats\n";
ostr << "Id: " << daemon->updateCallId(call) << "\n";
ostr << "Type: ";
if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
ostr << "Audio";
} else {
ostr << "Video";
}
ostr << "\n";
} else {
prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
}
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
ostr << prefix << "ICE state: " << ice_state_str[stats->ice_state] << "\n";
ostr << prefix << "RoundTripDelay: " << stats->round_trip_delay << "\n";
ostr << prefix << "Jitter: " << stats->jitter_stats.jitter << "\n";
@ -163,6 +147,29 @@ CallStatsResponse::CallStatsResponse(Daemon *daemon, LinphoneCall *call, const L
ostr << prefix << "Sent-InterarrivalJitter: " << ij << "\n";
ostr << prefix << "Sent-FractionLost: " << flost << "\n";
}
return ostr;
}
CallStatsResponse::CallStatsResponse(Daemon *daemon, LinphoneCall *call, const LinphoneCallStats *stats, bool event) {
const LinphoneCallParams *callParams = linphone_call_get_current_params(call);
const char *prefix = "";
ostringstream ostr;
if (event) {
ostr << "Event-type: call-stats\n";
ostr << "Id: " << daemon->updateCallId(call) << "\n";
ostr << "Type: ";
if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
ostr << "Audio";
} else {
ostr << "Video";
}
ostr << "\n";
} else {
prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
}
printCallStatsHelper(ostr, stats, prefix);
if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
const PayloadType *audioCodec = linphone_call_params_get_used_audio_codec(callParams);
@ -175,6 +182,31 @@ CallStatsResponse::CallStatsResponse(Daemon *daemon, LinphoneCall *call, const L
setBody(ostr.str().c_str());
}
AudioStreamStatsResponse::AudioStreamStatsResponse(Daemon* daemon, AudioStream* stream,
const LinphoneCallStats *stats, bool event) {
const char *prefix = "";
ostringstream ostr;
if (event) {
ostr << "Event-type: audio-stream-stats\n";
ostr << "Id: " << daemon->updateAudioStreamId(stream) << "\n";
ostr << "Type: ";
if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
ostr << "Audio";
} else {
ostr << "Video";
}
ostr << "\n";
} else {
prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
}
printCallStatsHelper(ostr, stats, prefix);
setBody(ostr.str().c_str());
}
PayloadTypeResponse::PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index, const string &prefix, bool enabled_status) {
ostringstream ostr;
if (payloadType != NULL) {
@ -336,27 +368,36 @@ LinphoneProxyConfig *Daemon::findProxy(int id) {
}
int Daemon::updateAudioStreamId(AudioStream *audio_stream) {
for (std::map<int, AudioStream*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
if (it->second == audio_stream)
for (std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
if (it->second->stream == audio_stream)
return it->first;
}
++mProxyIds;
mAudioStreams.insert(std::pair<int, AudioStream*>(mProxyIds, audio_stream));
mAudioStreams.insert(make_pair(mProxyIds, new AudioStreamAndOther(audio_stream)));
return mProxyIds;
}
AudioStream *Daemon::findAudioStream(int id) {
std::map<int, AudioStream*>::iterator it = mAudioStreams.find(id);
AudioStreamAndOther *Daemon::findAudioStreamAndOther(int id) {
std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
if (it != mAudioStreams.end())
return it->second;
return NULL;
}
void Daemon::removeAudioStream(int id) {
std::map<int, AudioStream*>::iterator it = mAudioStreams.find(id);
AudioStream *Daemon::findAudioStream(int id) {
std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
if (it != mAudioStreams.end())
return it->second->stream;
return NULL;
}
void Daemon::removeAudioStream(int id) {
std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
if (it != mAudioStreams.end()) {
mAudioStreams.erase(it);
delete(it->second);
}
}
void Daemon::initCommands() {
@ -379,6 +420,7 @@ void Daemon::initCommands() {
mCommands.push_back(new AudioCodecSetCommand());
mCommands.push_back(new AudioStreamStartCommand());
mCommands.push_back(new AudioStreamStopCommand());
mCommands.push_back(new AudioStreamStatsCommand());
mCommands.push_back(new MSFilterAddFmtpCommand());
mCommands.push_back(new PtimeCommand());
mCommands.push_back(new IPv6Command());
@ -452,8 +494,24 @@ void Daemon::dtmfReceived(LinphoneCore *lc, LinphoneCall *call, int dtmf) {
app->dtmfReceived(call, dtmf);
}
void Daemon::iterateStreamStats() {
for (std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
OrtpEvent *ev;
while (it->second->queue && (NULL != (ev=ortp_ev_queue_get(it->second->queue)))){
OrtpEventType evt=ortp_event_get_type(ev);
if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
linphone_call_stream_stats_hack(&it->second->stream->ms, &it->second->stats, ev);
if (mUseStatsEvents) mEventQueue.push(new AudioStreamStatsResponse(this,
it->second->stream, &it->second->stats, true));
}
ortp_event_destroy(ev);
}
}
}
void Daemon::iterate() {
linphone_core_iterate(mLc);
iterateStreamStats();
if (mChildFd == -1) {
if (!mEventQueue.empty()) {
Response *r = mEventQueue.front();
@ -707,8 +765,8 @@ void Daemon::enableLSD(bool enabled) {
Daemon::~Daemon() {
uninitCommands();
for (std::map<int, AudioStream *>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
audio_stream_stop(it->second);
for (std::map<int, AudioStreamAndOther *>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
audio_stream_stop(it->second->stream);
}
enableLSD(false);

View file

@ -126,25 +126,27 @@ private:
class EventResponse: public Response {
public:
EventResponse(Daemon *daemon, LinphoneCall *call, LinphoneCallState state);
private:
};
class CallStatsResponse: public Response {
public:
CallStatsResponse(Daemon *daemon, LinphoneCall *call, const LinphoneCallStats *stats, bool unique);
private:
};
class AudioStreamStatsResponse: public Response {
public:
AudioStreamStatsResponse(Daemon *daemon, AudioStream *stream,
const LinphoneCallStats *stats, bool event);
};
class DtmfResponse: public Response {
public:
DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf);
private:
};
class PayloadTypeResponse: public Response {
public:
PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index = -1, const std::string &prefix = std::string(), bool enabled_status = true);
private:
};
class PayloadTypeParser {
@ -159,6 +161,21 @@ private:
int mPayloadTypeNumber;
};
struct AudioStreamAndOther {
AudioStream *stream;
OrtpEvQueue *queue;
LinphoneCallStats stats;
AudioStreamAndOther(AudioStream *as) : stream(as) {
queue = ortp_ev_queue_new();
rtp_session_register_event_queue(as->ms.session, queue);
memset(&stats, 0, sizeof(stats));
}
~AudioStreamAndOther() {
rtp_session_unregister_event_queue(stream->ms.session, queue);
ortp_ev_queue_destroy(queue);
}
};
class Daemon {
friend class DaemonCommand;
public:
@ -174,6 +191,7 @@ public:
LinphoneCall *findCall(int id);
LinphoneProxyConfig *findProxy(int id);
AudioStream *findAudioStream(int id);
AudioStreamAndOther *findAudioStreamAndOther(int id);
void removeAudioStream(int id);
bool pullEvent();
int updateCallId(LinphoneCall *call);
@ -196,10 +214,12 @@ private:
char *readLine(const char *);
char *readPipe(char *buffer, int buflen);
void iterate();
void iterateStreamStats();
void startThread();
void stopThread();
void initCommands();
void uninitCommands();
void iterateStreamStats(LinphoneCore *lc);
LinphoneCore *mLc;
LinphoneSoundDaemon *mLSD;
std::list<DaemonCommand*> mCommands;
@ -216,7 +236,7 @@ private:
ms_thread_t mThread;
ms_mutex_t mMutex;
static const int sLineSize = 512;
std::map<int, AudioStream*> mAudioStreams;
std::map<int, AudioStreamAndOther*> mAudioStreams;
};
#endif //DAEMON_H_

@ -1 +1 @@
Subproject commit aebfe443e91aa1e1c1e32b2410cc1d9a5db629c1
Subproject commit 43b68b22cf8d0d1b0b5a064516d35899e5527358

2
oRTP

@ -1 +1 @@
Subproject commit 49b16793b9ef8251a4c42434b57387c6e3c6d251
Subproject commit 7c2805c2e2c1edadf9e9efe313d98f06c4c3614a