PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mit bestimmter Bitrate senden


baerbelita
01.11.2006, 10:06
Hallo,

ich versuche den Berkeley MPEG-2 Codec so umzubauen, dass er über das Protokoll RTP kodierte Videodaten über das Netzwerk sendet. Hierfür verwende ich die Bibliothek rtplib (www-out.bell-labs.com/project/RTPlib/).

Ich kodiere eine GOP mit einer vorgegebenen, von GOP zu GOP variierenden Bitrate (50 kbps - 10000 kbps) und schreibe die kodierten Daten in eine Liste. Nachem eine GOP kodiert wurde, möchte ich die erzeugten Daten gerne mit der selben Bitrate versenden. Wie kann ich das erreichen? So, wie ich es mir überlegt hatte (s.h. Code), klappt es jedenfalls nicht :(

double hanldeRTP(float byte_rate) {

// ...

while(1) {

// Check whether there is data that hasn't been sent
tmp = readFromEncDataQueue();

if(tmp == NULL) {

// finished
return (double) total_read;
}

//********************* Send the video data ***************************
bytes_read = tmp -> encDataNum;

err = RTPSend(cid, (int32) tsinc, marker, PAYLOAD_TYPE_MPEG_VIDEO,
tmp ->encDataBuffer, tmp -> encDataNum);

if (err != RTP_OK) {

fprintf(stderr, "Packet could not be sent: %s\n", RTPStrError(err));
exit(1);
}

free(tmp);

// ******************* Calculate when to send the next packet **********
total_read += bytes_read;
last_read = bytes_read;
play_time = start_time + (total_read * byte_rate);


// ******************* RTP and RTCP packet handling ********************
/* Handle RTP events and received RTP and RTCP packets until the next
* play time. */

while (gettimeofday(&now_tv, NULL), (now = tv2dbl(now_tv)) < play_time) {

// do other things until we have to send next video data
}
}
}

Die RTP Bibliotheksfunktionen sehen so aus (sorry für den vielen Code).

rtperror RTPSend(context cid, int32 tsinc, int8 marker,
int16 pti, int8 *payload, int len) {

struct iovec payload_vec[1];
payload_vec[0].iov_base = payload;
payload_vec[0].iov_len = len;

return RTPSendVector(cid, tsinc, marker, pti, payload_vec, 1);
}

rtperror RTPSendVector(context cid, int32 tsinc, int8 marker,

int16 pti, struct iovec *payload, int vec_count) {
/* Our packet will be composed of vec_count + 2 buffers:
buffer 0 is the header
buffers 1 .. n-1 are the payload
buffer n is the payload padding */

struct iovec *pktpart;
int pktlen;
int buflen;

struct msghdr the_message;
int errchk, i, data_len, rundelete;
address_holder_t *s, *prevs;
rtperror err;
hl_context *uc;

err = RTPSessionGetHighLevelInfo(cid, (void**)&uc);

if (err != RTP_OK)
/* The cid is bogus */
return err;

if (uc->PreventEntryIntoFlaggingFunctions) {
return errordebug(RTP_CANT_CALL_FUNCTION, "RTPSendVector",
"context %d, cannot be called now",
(int)cid);
}

if(uc->send_addr_list == NULL) {
return errordebug(RTP_BAD_ADDR, "RTPSendVector",
"context %d, no send addresses",
(int)cid);
}

pktlen = vec_count + 2;

pktpart = (struct iovec *) calloc(sizeof(struct iovec), pktlen);

if (pktpart == NULL) {
return errordebug(RTP_CANT_ALLOC_MEM, "RTPSendVector",
"context %d, couldn't allocate iovec",
(int)cid);
}

uc->PreventEntryIntoFlaggingFunctions = TRUE;

data_len = 0;

for (i = 0; i < vec_count; i++) {
pktpart[i+1].iov_base = payload[i].iov_base;
pktpart[i+1].iov_len = payload[i].iov_len;
data_len += payload[i].iov_len;
/* octet count only measures the payload (no extension, no SSRC lists,
etc.) */
}

buflen = _RTP_MAX_PKT_SIZE;

err = RTPBuildRTPHeader(cid, tsinc, marker, pti, FALSE /* ... XXX encrypt */,
data_len, uc->packet_buffer, &buflen);

if (err)
goto cleanup;

pktpart[0].iov_base = uc->packet_buffer;

pktpart[0].iov_len = buflen;

/* Add padding to the payload if necessary */
/* XXX encryption */
pktpart[pktlen-1].iov_base = NULL;

pktpart[pktlen-1].iov_len = 0;

/* XXX do encryption here */

/* Packet is not encrypted. Then it is ready to be sent. */
memset((char*) &the_message, 0, sizeof(the_message));

the_message.msg_name = (caddr_t) NULL;

the_message.msg_namelen = 0;

the_message.msg_iov = pktpart;

the_message.msg_iovlen = pktlen;

s = uc->send_addr_list;

err = RTP_OK;

rundelete = FALSE;

while(s != NULL) {
if(s->deleteflag == FALSE) {

errchk = _sys_sendmsg(s->rtpsocket, (struct msghdr*)
&the_message, 0);

if (errchk < 0) {
err = errordebug(RTP_SOCKET_WRITE_FAILURE, "RTPSendVector",
"context %d could not write to RTP socket",
(int)cid);

if (uc->SendErrorCallBack != NULL) {
uc->SendErrorCallBack(cid,
inet_ntoa(s->address),
ntohs(s->port),
s->ttl);
}

if (s->deleteflag == TRUE) {
rundelete = TRUE;
}
}

} else {
rundelete = TRUE;
}

s = s->next;
}

/* Now, we clean up the send list and remove all that have been deleted.
We know that this needs to be done if rundelete is TRUE */

prevs = NULL;

if(rundelete == TRUE) {
s = uc->send_addr_list;

while(s != NULL) {
if(s->deleteflag == TRUE) {

if(prevs == NULL)
uc->send_addr_list = s->next;
else
prevs->next = s->next;

_sys_close_socket(s->rtpsocket);

_sys_close_socket(s->rtcpsocket);

free(s);
}

prevs = s;
s = s->next;
}
}

cleanup:
free(pktpart);

uc->PreventEntryIntoFlaggingFunctions = FALSE;

return(err);
}


Felix Kaiser
01.11.2006, 10:38
Das größte Problem dürfte wohl sein, dass wir eine paketorientierte und keine serielle Übertragung haben. Ein TCP Segment über IP im Ethernet kann maximal 1460 Bytes, also etwa 11.6kBit übertragen. Sich am MTU Wert des Gerätes orientieren wäre wohl eine clevere Lösung. Man kann berechnen wie viele solcher Segmente pro Sekunde oder pro Viertelsekunde übertragen werden sollen und stimmt das danach ab. Dazu wäre eine Zeitmessung mit Genauigkeit im Millisekundenbereich hilfreich. Außerdem solltest du mit blockierenden Sockets arbeiten. Beispielsweise sendest du einen Block mit Größe 2x1460 Bytes, der Sendeaufruf könnte dann durchaus auch etwas dauern, weil wenn der Sendepuffer voll ist wartet er bis der Empfänger genügend Daten erhalten hat um den aktuellen Block im Sendepuffer unterzubringen. Und wenn der Sendeaufruf schneller von statten ging als es deiner Bitrate entspricht, dann wartest du die übrigen Millisekunden einfach.

Beispiel: 128kBit, das sind 16000 Bytes pro Sekunde, sind etwa 11 volle TCP Segmente. Genau 11 Segmente wären 16060 Bytes. Die Toleranz kann man akzeptieren. Ein Segment zu senden sollte dann idealerweise 91ms benötigen. Ich würde in dem Fall Blöcke von 3x1460 Bytes senden, also 3 volle Segmente, der Sendeaufruf dürfte dann bis zu 273ms benötigen. Benötigt er weniger wartest du den Rest der Zeit einfach in Millisekunden. Benötigt er länger solltest du diese Zeitspanne ebenfalls merken und beim nächsten Durchlauf mit berücksichtigen und dann trotz niedrigerer Dauer beim Sendeaufruf ggf. dann mal nicht warten.