summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzoff99 <zoff@zoff.cc>2018-01-19 22:59:42 +0100
committeriphydf <iphydf@users.noreply.github.com>2018-02-11 23:31:46 +0000
commit721358208b6650c62aa654be922867f10a5d6f38 (patch)
tree988aef376cc8c74b19b5e605133072bdf3d23e27
parent0647c2c5bc8c871dbcaed64de40eb252d13d303c (diff)
Improve video key frame sending.
This change does not include the addition of VP9. We do that in a separate pull request. Changes: * fix the video bug (video frames larger than 65KBytes) by sending full frame length in alternate header field * improve video frame reconstruction logic with slots * configure video encoder and decoder to be multihtreaded * set error resilience flags on video codec * change encoder and decoder softdeadline
-rw-r--r--testing/BUILD.bazel4
-rw-r--r--toxav/BUILD.bazel2
-rw-r--r--toxav/bwcontroller.c99
-rw-r--r--toxav/bwcontroller.h2
-rw-r--r--toxav/rtp.c874
-rw-r--r--toxav/rtp.h63
-rw-r--r--toxav/rtp_test.cpp2
-rw-r--r--toxav/toxav.c90
-rw-r--r--toxav/video.c292
-rw-r--r--toxav/video.h5
-rw-r--r--toxcore/BUILD.bazel4
11 files changed, 1031 insertions, 406 deletions
diff --git a/testing/BUILD.bazel b/testing/BUILD.bazel
index afb1a8aa..6cc7018e 100644
--- a/testing/BUILD.bazel
+++ b/testing/BUILD.bazel
@@ -34,9 +34,9 @@ cc_binary(
34 "//c-toxcore/toxav", 34 "//c-toxcore/toxav",
35 "//c-toxcore/toxav:monolith", 35 "//c-toxcore/toxav:monolith",
36 "//c-toxcore/toxcore", 36 "//c-toxcore/toxcore",
37 "@portaudio",
38 "@sndfile",
39 "@opencv//:core", 37 "@opencv//:core",
40 "@opencv//:highgui", 38 "@opencv//:highgui",
39 "@portaudio",
40 "@sndfile",
41 ], 41 ],
42) 42)
diff --git a/toxav/BUILD.bazel b/toxav/BUILD.bazel
index ff775fdd..cf905910 100644
--- a/toxav/BUILD.bazel
+++ b/toxav/BUILD.bazel
@@ -41,7 +41,7 @@ cc_test(
41 deps = [ 41 deps = [
42 ":rtp", 42 ":rtp",
43 "//c-toxcore/toxcore:crypto_core", 43 "//c-toxcore/toxcore:crypto_core",
44 "@gtest", 44 "@com_google_googletest//:gtest_main",
45 ], 45 ],
46) 46)
47 47
diff --git a/toxav/bwcontroller.c b/toxav/bwcontroller.c
index 9d1ad9ce..defb3fde 100644
--- a/toxav/bwcontroller.c
+++ b/toxav/bwcontroller.c
@@ -32,9 +32,10 @@
32#include <errno.h> 32#include <errno.h>
33 33
34#define BWC_PACKET_ID 196 34#define BWC_PACKET_ID 196
35#define BWC_SEND_INTERVAL_MS 1000 35#define BWC_SEND_INTERVAL_MS 950 /* 0.95s */
36#define BWC_REFRESH_INTERVAL_MS 10000 36#define BWC_REFRESH_INTERVAL_MS 2000 /* 2.00s */
37#define BWC_AVG_PKT_COUNT 20 37#define BWC_AVG_PKT_COUNT 20
38#define BWC_AVG_LOSS_OVER_CYCLES_COUNT 30
38 39
39struct BWController_s { 40struct BWController_s {
40 void (*mcb)(BWController *, uint32_t, float, void *); 41 void (*mcb)(BWController *, uint32_t, float, void *);
@@ -56,6 +57,8 @@ struct BWController_s {
56 uint32_t packet_length_array[BWC_AVG_PKT_COUNT]; 57 uint32_t packet_length_array[BWC_AVG_PKT_COUNT];
57 RingBuffer *rb; 58 RingBuffer *rb;
58 } rcvpkt; /* To calculate average received packet (this means split parts, not the full message!) */ 59 } rcvpkt; /* To calculate average received packet (this means split parts, not the full message!) */
60
61 uint32_t packet_loss_counted_cycles;
59}; 62};
60 63
61struct BWCMessage { 64struct BWCMessage {
@@ -71,23 +74,23 @@ BWController *bwc_new(Messenger *m, uint32_t friendnumber,
71 void *udata) 74 void *udata)
72{ 75{
73 BWController *retu = (BWController *)calloc(sizeof(struct BWController_s), 1); 76 BWController *retu = (BWController *)calloc(sizeof(struct BWController_s), 1);
74 77 LOGGER_DEBUG(m->log, "Creating bandwidth controller");
75 retu->mcb = mcb; 78 retu->mcb = mcb;
76 retu->mcb_data = udata; 79 retu->mcb_data = udata;
77 retu->m = m; 80 retu->m = m;
78 retu->friend_number = friendnumber; 81 retu->friend_number = friendnumber;
79 retu->cycle.last_sent_timestamp = retu->cycle.last_refresh_timestamp = current_time_monotonic(); 82 retu->cycle.last_sent_timestamp = retu->cycle.last_refresh_timestamp = current_time_monotonic();
80 retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT); 83 retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
84 retu->cycle.lost = 0;
85 retu->cycle.recv = 0;
86 retu->packet_loss_counted_cycles = 0;
81 87
82 /* Fill with zeros */ 88 /* Fill with zeros */
83 int i = 0; 89 for (int i = 0; i < BWC_AVG_PKT_COUNT; i++) {
84 90 rb_write(retu->rcvpkt.rb, &retu->rcvpkt.packet_length_array[i]);
85 for (; i < BWC_AVG_PKT_COUNT; i ++) {
86 rb_write(retu->rcvpkt.rb, retu->rcvpkt.packet_length_array + i);
87 } 91 }
88 92
89 m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu); 93 m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
90
91 return retu; 94 return retu;
92} 95}
93 96
@@ -98,47 +101,21 @@ void bwc_kill(BWController *bwc)
98 } 101 }
99 102
100 m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, nullptr, nullptr); 103 m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, nullptr, nullptr);
101
102 rb_kill(bwc->rcvpkt.rb); 104 rb_kill(bwc->rcvpkt.rb);
103 free(bwc); 105 free(bwc);
104} 106}
105 107
106void bwc_feed_avg(BWController *bwc, uint32_t bytes)
107{
108 uint32_t *p;
109
110 rb_read(bwc->rcvpkt.rb, (void **) &p);
111 rb_write(bwc->rcvpkt.rb, p);
112
113 *p = bytes;
114}
115
116void bwc_add_lost(BWController *bwc, uint32_t bytes_lost) 108void bwc_add_lost(BWController *bwc, uint32_t bytes_lost)
117{ 109{
118 if (!bwc) { 110 if (!bwc) {
119 return; 111 return;
120 } 112 }
121 113
122 if (!bytes_lost) { 114 if (bytes_lost > 0) {
123 uint32_t *t_avg[BWC_AVG_PKT_COUNT], c = 1; 115 LOGGER_DEBUG(bwc->m->log, "BWC lost(1): %d", (int)bytes_lost);
124 116 bwc->cycle.lost += bytes_lost;
125 rb_data(bwc->rcvpkt.rb, (void **) t_avg); 117 send_update(bwc);
126
127 int i = 0;
128
129 for (; i < BWC_AVG_PKT_COUNT; i ++) {
130 bytes_lost += *(t_avg[i]);
131
132 if (*(t_avg[i])) {
133 c++;
134 }
135 }
136
137 bytes_lost /= c;
138 } 118 }
139
140 bwc->cycle.lost += bytes_lost;
141 send_update(bwc);
142} 119}
143 120
144void bwc_add_recv(BWController *bwc, uint32_t recv_bytes) 121void bwc_add_recv(BWController *bwc, uint32_t recv_bytes)
@@ -147,37 +124,35 @@ void bwc_add_recv(BWController *bwc, uint32_t recv_bytes)
147 return; 124 return;
148 } 125 }
149 126
127 bwc->packet_loss_counted_cycles++;
150 bwc->cycle.recv += recv_bytes; 128 bwc->cycle.recv += recv_bytes;
151 send_update(bwc); 129 send_update(bwc);
152} 130}
153 131
154
155void send_update(BWController *bwc) 132void send_update(BWController *bwc)
156{ 133{
157 if (current_time_monotonic() - bwc->cycle.last_refresh_timestamp > BWC_REFRESH_INTERVAL_MS) { 134 if (bwc->packet_loss_counted_cycles > BWC_AVG_LOSS_OVER_CYCLES_COUNT &&
158 135 current_time_monotonic() - bwc->cycle.last_sent_timestamp > BWC_SEND_INTERVAL_MS) {
159 bwc->cycle.lost /= 10; 136 bwc->packet_loss_counted_cycles = 0;
160 bwc->cycle.recv /= 10;
161 bwc->cycle.last_refresh_timestamp = current_time_monotonic();
162 } else if (current_time_monotonic() - bwc->cycle.last_sent_timestamp > BWC_SEND_INTERVAL_MS) {
163 137
164 if (bwc->cycle.lost) { 138 if (bwc->cycle.lost) {
165 LOGGER_DEBUG(bwc->m->log, "%p Sent update rcv: %u lost: %u", 139 LOGGER_DEBUG(bwc->m->log, "%p Sent update rcv: %u lost: %u percent: %f %%",
166 bwc, bwc->cycle.recv, bwc->cycle.lost); 140 bwc, bwc->cycle.recv, bwc->cycle.lost,
167 141 (((float) bwc->cycle.lost / (bwc->cycle.recv + bwc->cycle.lost)) * 100.0f));
168 uint8_t p_msg[sizeof(struct BWCMessage) + 1]; 142 uint8_t bwc_packet[sizeof(struct BWCMessage) + 1];
169 struct BWCMessage *b_msg = (struct BWCMessage *)(p_msg + 1); 143 struct BWCMessage *msg = (struct BWCMessage *)(bwc_packet + 1);
170 144 bwc_packet[0] = BWC_PACKET_ID; // set packet ID
171 p_msg[0] = BWC_PACKET_ID; 145 msg->lost = net_htonl(bwc->cycle.lost);
172 b_msg->lost = net_htonl(bwc->cycle.lost); 146 msg->recv = net_htonl(bwc->cycle.recv);
173 b_msg->recv = net_htonl(bwc->cycle.recv); 147
174 148 if (-1 == m_send_custom_lossy_packet(bwc->m, bwc->friend_number, bwc_packet, sizeof(bwc_packet))) {
175 if (-1 == m_send_custom_lossy_packet(bwc->m, bwc->friend_number, p_msg, sizeof(p_msg))) { 149 LOGGER_WARNING(bwc->m->log, "BWC send failed (len: %d)! std error: %s", sizeof(bwc_packet), strerror(errno));
176 LOGGER_WARNING(bwc->m->log, "BWC send failed (len: %d)! std error: %s", sizeof(p_msg), strerror(errno));
177 } 150 }
178 } 151 }
179 152
180 bwc->cycle.last_sent_timestamp = current_time_monotonic(); 153 bwc->cycle.last_sent_timestamp = current_time_monotonic();
154 bwc->cycle.lost = 0;
155 bwc->cycle.recv = 0;
181 } 156 }
182} 157}
183 158
@@ -185,9 +160,9 @@ static int on_update(BWController *bwc, const struct BWCMessage *msg)
185{ 160{
186 LOGGER_DEBUG(bwc->m->log, "%p Got update from peer", bwc); 161 LOGGER_DEBUG(bwc->m->log, "%p Got update from peer", bwc);
187 162
188 /* Peer must respect time boundary */ 163 /* Peers sent update too soon */
189 if (current_time_monotonic() < bwc->cycle.last_recv_timestamp + BWC_SEND_INTERVAL_MS) { 164 if (bwc->cycle.last_recv_timestamp + BWC_SEND_INTERVAL_MS > current_time_monotonic()) {
190 LOGGER_DEBUG(bwc->m->log, "%p Rejecting extra update", bwc); 165 LOGGER_INFO(bwc->m->log, "%p Rejecting extra update", bwc);
191 return -1; 166 return -1;
192 } 167 }
193 168
@@ -196,9 +171,9 @@ static int on_update(BWController *bwc, const struct BWCMessage *msg)
196 uint32_t recv = net_ntohl(msg->recv); 171 uint32_t recv = net_ntohl(msg->recv);
197 uint32_t lost = net_ntohl(msg->lost); 172 uint32_t lost = net_ntohl(msg->lost);
198 173
199 LOGGER_DEBUG(bwc->m->log, "recved: %u lost: %u", recv, lost);
200
201 if (lost && bwc->mcb) { 174 if (lost && bwc->mcb) {
175 LOGGER_DEBUG(bwc->m->log, "recved: %u lost: %u percentage: %f %%", recv, lost,
176 (((float) lost / (recv + lost)) * 100.0f));
202 bwc->mcb(bwc, bwc->friend_number, 177 bwc->mcb(bwc, bwc->friend_number,
203 ((float) lost / (recv + lost)), 178 ((float) lost / (recv + lost)),
204 bwc->mcb_data); 179 bwc->mcb_data);
diff --git a/toxav/bwcontroller.h b/toxav/bwcontroller.h
index 43475252..be5eb191 100644
--- a/toxav/bwcontroller.h
+++ b/toxav/bwcontroller.h
@@ -27,9 +27,9 @@ typedef struct BWController_s BWController;
27BWController *bwc_new(Messenger *m, uint32_t friendnumber, 27BWController *bwc_new(Messenger *m, uint32_t friendnumber,
28 void (*mcb)(BWController *, uint32_t, float, void *), 28 void (*mcb)(BWController *, uint32_t, float, void *),
29 void *udata); 29 void *udata);
30
30void bwc_kill(BWController *bwc); 31void bwc_kill(BWController *bwc);
31 32
32void bwc_feed_avg(BWController *bwc, uint32_t bytes);
33void bwc_add_lost(BWController *bwc, uint32_t bytes); 33void bwc_add_lost(BWController *bwc, uint32_t bytes);
34void bwc_add_recv(BWController *bwc, uint32_t bytes); 34void bwc_add_recv(BWController *bwc, uint32_t bytes);
35 35
diff --git a/toxav/rtp.c b/toxav/rtp.c
index 06bc6df5..c5d940e2 100644
--- a/toxav/rtp.c
+++ b/toxav/rtp.c
@@ -33,11 +33,557 @@
33#include <errno.h> 33#include <errno.h>
34#include <stdlib.h> 34#include <stdlib.h>
35 35
36enum {
37 /**
38 * The number of milliseconds we want to keep a keyframe in the buffer for,
39 * even though there are no free slots for incoming frames.
40 */
41 VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS = 15,
42};
43
44// allocate_len is NOT including header!
45static struct RTPMessage *new_message(const struct RTPHeader *header, size_t allocate_len, const uint8_t *data,
46 uint16_t data_length)
47{
48 assert(allocate_len >= data_length);
49 struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + allocate_len);
50
51 if (msg == nullptr) {
52 return nullptr;
53 }
54
55 msg->len = data_length; // result without header
56 msg->header = *header;
57 memcpy(msg->data, data, msg->len);
58 return msg;
59}
60
61enum {
62 /**
63 * Instruct the caller to clear slot 0.
64 */
65 GET_SLOT_RESULT_DROP_OLDEST_SLOT = -1,
66 /**
67 * Instruct the caller to drop the incoming packet.
68 */
69 GET_SLOT_RESULT_DROP_INCOMING = -2,
70};
71
72/**
73 * Find the next free slot in work_buffer for the incoming data packet.
74 *
75 * - If the data packet belongs to a frame thats already in the work_buffer then
76 * use that slot.
77 * - If there is no free slot return GET_SLOT_RESULT_DROP_OLDEST_SLOT.
78 * - If the data packet is too old return GET_SLOT_RESULT_DROP_INCOMING.
79 *
80 * If there is a keyframe beeing assembled in slot 0, keep it a bit longer and
81 * do not kick it out right away if all slots are full instead kick out the new
82 * incoming interframe.
83 */
84static int8_t get_slot(Logger *log, struct RTPWorkBufferList *wkbl, bool is_keyframe,
85 const struct RTPHeader *header, bool is_multipart)
86{
87 if (is_multipart) {
88 // This RTP message is part of a multipart frame, so we try to find an
89 // existing slot with the previous parts of the frame in it.
90 for (uint8_t i = 0; i < wkbl->next_free_entry; i++) {
91 const struct RTPWorkBuffer *slot = &wkbl->work_buffer[i];
92
93 if ((slot->buf->header.sequnum == header->sequnum) && (slot->buf->header.timestamp == header->timestamp)) {
94 // Sequence number and timestamp match, so this slot belongs to
95 // the same frame.
96 //
97 // In reality, these will almost certainly either both match or
98 // both not match. Only if somehow there were 65535 frames
99 // between, the timestamp will matter.
100 return i;
101 }
102 }
103 }
104
105 // The message may or may not be part of a multipart frame.
106 //
107 // If it is part of a multipart frame, then this is an entirely new frame
108 // for which we did not have a slot *or* the frame is so old that its slot
109 // has been evicted by now.
110 //
111 // |----------- time ----------->
112 // _________________
113 // slot 0 | |
114 // -----------------
115 // _________________
116 // slot 1 | |
117 // -----------------
118 // ____________
119 // slot 2 | | -> frame too old, drop
120 // ------------
121 //
122 //
123 //
124 // |----------- time ----------->
125 // _________________
126 // slot 0 | |
127 // -----------------
128 // _________________
129 // slot 1 | |
130 // -----------------
131 // ____________
132 // slot 2 | | -> ok, start filling in a new slot
133 // ------------
134
135 // If there is a free slot:
136 if (wkbl->next_free_entry < USED_RTP_WORKBUFFER_COUNT) {
137 // If there is at least one filled slot:
138 if (wkbl->next_free_entry > 0) {
139 // Get the most recently filled slot.
140 const struct RTPWorkBuffer *slot = &wkbl->work_buffer[wkbl->next_free_entry - 1];
141
142 // If the incoming packet is older than our newest slot, drop it.
143 // This is the first situation in the above diagram.
144 if (slot->buf->header.timestamp > header->timestamp) {
145 LOGGER_DEBUG(log, "workbuffer:2:timestamp too old");
146 return GET_SLOT_RESULT_DROP_INCOMING;
147 }
148 }
149
150 // Not all slots are filled, and the packet is newer than our most
151 // recent slot, so it's a new frame we want to start assembling. This is
152 // the second situation in the above diagram.
153 return wkbl->next_free_entry;
154 }
155
156 // If the incoming frame is a key frame, then stop assembling the oldest
157 // slot, regardless of whether there was a keyframe in that or not.
158 if (is_keyframe) {
159 return GET_SLOT_RESULT_DROP_OLDEST_SLOT;
160 }
161
162 // The incoming slot is not a key frame, so we look at slot 0 to see what to
163 // do next.
164 const struct RTPWorkBuffer *slot = &wkbl->work_buffer[0];
165
166 // The incoming frame is not a key frame, but the existing slot 0 is also
167 // not a keyframe, so we stop assembling the existing frame and make space
168 // for the new one.
169 if (!slot->is_keyframe) {
170 return GET_SLOT_RESULT_DROP_OLDEST_SLOT;
171 }
172
173 // If this key frame is fully received, we also stop assembling and clear
174 // slot 0. This also means sending the frame to the decoder.
175 if (slot->received_len == slot->buf->header.data_length_full) {
176 return GET_SLOT_RESULT_DROP_OLDEST_SLOT;
177 }
178
179 // This is a key frame, not fully received yet, but it's already much older
180 // than the incoming frame, so we stop assembling it and send whatever part
181 // we did receive to the decoder.
182 if (slot->buf->header.timestamp + VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS <= header->timestamp) {
183 return GET_SLOT_RESULT_DROP_OLDEST_SLOT;
184 }
185
186 // This is a key frame, it's not too old yet, so we keep it in its slot for
187 // a little longer.
188 LOGGER_INFO(log, "keep KEYFRAME in workbuffer");
189 return GET_SLOT_RESULT_DROP_INCOMING;
190}
191
192/**
193 * Returns an assembled frame (as much data as we currently have for this frame,
194 * some pieces may be missing)
195 *
196 * If there are no frames ready, we return NULL. If this function returns
197 * non-NULL, it transfers ownership of the message to the caller, i.e. the
198 * caller is responsible for storing it elsewhere or calling free().
199 */
200static struct RTPMessage *process_frame(Logger *log, struct RTPWorkBufferList *wkbl, uint8_t slot_id)
201{
202 assert(wkbl->next_free_entry >= 0);
203
204 if (wkbl->next_free_entry == 0) {
205 // There are no frames in any slot.
206 return nullptr;
207 }
208
209 // Slot 0 contains a key frame, slot_id points at an interframe that is
210 // relative to that key frame, so we don't use it yet.
211 if (wkbl->work_buffer[0].is_keyframe && slot_id != 0) {
212 LOGGER_DEBUG(log, "process_frame:KEYFRAME waiting in slot 0");
213 return nullptr;
214 }
215
216 // Either slot_id is 0 and slot 0 is a key frame, or there is no key frame
217 // in slot 0 (and slot_id is anything).
218 struct RTPWorkBuffer *const slot = &wkbl->work_buffer[slot_id];
219
220 // Move ownership of the frame out of the slot into m_new.
221 struct RTPMessage *const m_new = slot->buf;
222 slot->buf = nullptr;
223
224 assert(wkbl->next_free_entry >= 1);
225
226 if (slot_id != wkbl->next_free_entry - 1) {
227 // The slot is not the last slot, so we created a gap. We move all the
228 // entries after it one step up.
229 for (uint8_t i = slot_id; i < wkbl->next_free_entry - 1; i++) {
230 // Move entry (i+1) into entry (i).
231 wkbl->work_buffer[i] = wkbl->work_buffer[i + 1];
232 }
233 }
234
235 // We now have a free entry at the end of the array.
236 wkbl->next_free_entry--;
237
238 // Clear the newly freed entry.
239 const struct RTPWorkBuffer empty = {0};
240 wkbl->work_buffer[wkbl->next_free_entry] = empty;
241
242 // Move ownership of the frame to the caller.
243 return m_new;
244}
245
246/**
247 * @param log A logger.
248 * @param wkbl The list of in-progress frames, i.e. all the slots.
249 * @param slot_id The slot we want to fill the data into.
250 * @param is_keyframe Whether the data is part of a key frame.
251 * @param header The RTP header from the incoming packet.
252 * @param incoming_data The pure payload without header.
253 * @param incoming_data_length The length in bytes of the incoming data payload.
254 */
255static bool fill_data_into_slot(Logger *log, struct RTPWorkBufferList *wkbl, const uint8_t slot_id, bool is_keyframe,
256 const struct RTPHeader *header, const uint8_t *incoming_data, uint16_t incoming_data_length)
257{
258 // We're either filling the data into an existing slot, or in a new one that
259 // is the next free entry.
260 assert(slot_id <= wkbl->next_free_entry);
261 struct RTPWorkBuffer *const slot = &wkbl->work_buffer[slot_id];
262
263 assert(header != nullptr);
264 assert(is_keyframe == (bool)(header->flags & RTP_KEY_FRAME));
265
266 if (slot->received_len == 0) {
267 assert(slot->buf == nullptr);
268
269 // No data for this slot has been received, yet, so we create a new
270 // message for it with enough memory for the entire frame.
271 struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + header->data_length_full);
272
273 if (msg == nullptr) {
274 LOGGER_ERROR(log, "Out of memory while trying to allocate for frame of size %u\n",
275 (unsigned)header->data_length_full);
276 // Out of memory: throw away the incoming data.
277 return false;
278 }
279
280 // Unused in the new video receiving code, as it's 16 bit and can't hold
281 // the full length of large frames. Instead, we use slot->received_len.
282 msg->len = 0;
283 msg->header = *header;
284
285 slot->buf = msg;
286 slot->is_keyframe = is_keyframe;
287 slot->received_len = 0;
288
289 assert(wkbl->next_free_entry < USED_RTP_WORKBUFFER_COUNT);
290 wkbl->next_free_entry++;
291 }
292
293 // We already checked this when we received the packet, but we rely on it
294 // here, so assert again.
295 assert(header->offset_full < header->data_length_full);
296
297 // Copy the incoming chunk of data into the correct position in the full
298 // frame data array.
299 memcpy(
300 slot->buf->data + header->offset_full,
301 incoming_data,
302 incoming_data_length
303 );
304
305 // Update the total received length of this slot.
306 slot->received_len += incoming_data_length;
307
308 // Update received length also in the header of the message, for later use.
309 slot->buf->header.received_length_full = slot->received_len;
310
311 return slot->received_len == header->data_length_full;
312}
313
314static void update_bwc_values(Logger *log, RTPSession *session, const struct RTPMessage *msg)
315{
316 if (session->first_packets_counter < DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT) {
317 session->first_packets_counter++;
318 } else {
319 uint32_t data_length_full = msg->header.data_length_full; // without header
320 uint32_t received_length_full = msg->header.received_length_full; // without header
321 bwc_add_recv(session->bwc, data_length_full);
322
323 if (received_length_full < data_length_full) {
324 LOGGER_DEBUG(log, "BWC: full length=%u received length=%d", data_length_full, received_length_full);
325 bwc_add_lost(session->bwc, (data_length_full - received_length_full));
326 }
327 }
328}
329
330/**
331 * Handle a single RTP video packet.
332 *
333 * The packet may or may not be part of a multipart frame. This function will
334 * find out and handle it appropriately.
335 *
336 * @param session The current RTP session with:
337 * session->mcb == vc_queue_message() // this function is called from here
338 * session->mp == struct RTPMessage *
339 * session->cs == call->video.second // == VCSession created by vc_new() call
340 * @param header The RTP header deserialised from the packet.
341 * @param incoming_data The packet data *not* header, i.e. this is the actual
342 * payload.
343 * @param incoming_data_length The packet length *not* including header, i.e.
344 * this is the actual payload length.
345 * @param log A logger.
346 *
347 * @return -1 on error, 0 on success.
348 */
349static int handle_video_packet(RTPSession *session, const struct RTPHeader *header,
350 const uint8_t *incoming_data, uint16_t incoming_data_length, Logger *log)
351{
352 // Full frame length in bytes. The frame may be split into multiple packets,
353 // but this value is the complete assembled frame size.
354 const uint32_t full_frame_length = header->data_length_full;
355
356 // Current offset in the frame. If this is the first packet of a multipart
357 // frame or it's not a multipart frame, then this value is 0.
358 const uint32_t offset = header->offset_full; // without header
359
360 // The sender tells us whether this is a key frame.
361 const bool is_keyframe = (header->flags & RTP_KEY_FRAME) != 0;
362
363 LOGGER_DEBUG(log, "-- handle_video_packet -- full lens=%u len=%u offset=%u is_keyframe=%s",
364 (unsigned)incoming_data_length, (unsigned)full_frame_length, (unsigned)offset, is_keyframe ? "K" : ".");
365 LOGGER_DEBUG(log, "wkbl->next_free_entry:003=%d", session->work_buffer_list->next_free_entry);
366
367 const bool is_multipart = full_frame_length != incoming_data_length;
368
369 /* The message was sent in single part */
370 int8_t slot_id = get_slot(log, session->work_buffer_list, is_keyframe, header, is_multipart);
371 LOGGER_DEBUG(log, "slot num=%d", slot_id);
372
373 // get_slot told us to drop the packet, so we ignore it.
374 if (slot_id == GET_SLOT_RESULT_DROP_INCOMING) {
375 return -1;
376 }
377
378 // get_slot said there is no free slot.
379 if (slot_id == GET_SLOT_RESULT_DROP_OLDEST_SLOT) {
380 LOGGER_DEBUG(log, "there was no free slot, so we process the oldest frame");
381 // We now own the frame.
382 struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, 0);
383
384 // The process_frame function returns NULL if there is no slot 0, i.e.
385 // the work buffer list is completely empty. It can't be empty, because
386 // get_slot just told us it's full, so process_frame must return non-null.
387 assert(m_new != nullptr);
388
389 LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d b1=%d", (int)m_new->data[0], (int)m_new->data[1]);
390 update_bwc_values(log, session, m_new);
391 // Pass ownership of m_new to the callback.
392 session->mcb(session->cs, m_new);
393 // Now we no longer own m_new.
394 m_new = nullptr;
395
396 // Now we must have a free slot, so we either get that slot, i.e. >= 0,
397 // or get told to drop the incoming packet if it's too old.
398 slot_id = get_slot(log, session->work_buffer_list, is_keyframe, header, /* is_multipart */false);
399
400 if (slot_id == GET_SLOT_RESULT_DROP_INCOMING) {
401 // The incoming frame is too old, so we drop it.
402 return -1;
403 }
404 }
405
406 // We must have a valid slot here.
407 assert(slot_id >= 0);
408
409 LOGGER_DEBUG(log, "fill_data_into_slot.1");
410
411 // fill in this part into the slot buffer at the correct offset
412 if (!fill_data_into_slot(
413 log,
414 session->work_buffer_list,
415 slot_id,
416 is_keyframe,
417 header,
418 incoming_data,
419 incoming_data_length)) {
420 // Memory allocation failed. Return error.
421 return -1;
422 }
423
424 struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, slot_id);
425
426 if (m_new) {
427 LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d b1=%d", (int)m_new->data[0], (int)m_new->data[1]);
428 update_bwc_values(log, session, m_new);
429 session->mcb(session->cs, m_new);
430
431 m_new = nullptr;
432 }
433
434 return 0;
435}
436
437/**
438 * @return -1 on error, 0 on success.
439 */
440static int handle_rtp_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
441{
442 RTPSession *session = (RTPSession *)object;
443
444 if (!session || length < RTP_HEADER_SIZE + 1) {
445 LOGGER_WARNING(m->log, "No session or invalid length of received buffer!");
446 return -1;
447 }
448
449 // Get the packet type.
450 const uint8_t packet_type = data[0];
451 ++data;
452 --length;
453
454 // Unpack the header.
455 struct RTPHeader header;
456 rtp_header_unpack(data, &header);
457
458 if (header.pt != packet_type % 128) {
459 LOGGER_WARNING(m->log, "RTPHeader packet type and Tox protocol packet type did not agree: %d != %d",
460 header.pt, packet_type % 128);
461 return -1;
462 }
463
464 if (header.pt != session->payload_type % 128) {
465 LOGGER_WARNING(m->log, "RTPHeader packet type does not match this session's payload type: %d != %d",
466 header.pt, session->payload_type % 128);
467 return -1;
468 }
469
470 if (header.offset_full >= header.data_length_full) {
471 LOGGER_ERROR(m->log, "Invalid video packet: frame offset (%u) >= full frame length (%u)",
472 (unsigned)header.offset_full, (unsigned)header.data_length_full);
473 return -1;
474 }
475
476 if (header.offset_lower >= header.data_length_lower) {
477 LOGGER_ERROR(m->log, "Invalid old protocol video packet: frame offset (%u) >= full frame length (%u)",
478 (unsigned)header.offset_lower, (unsigned)header.data_length_lower);
479 return -1;
480 }
481
482 LOGGER_DEBUG(m->log, "header.pt %d, video %d", (uint8_t)header.pt, (rtp_TypeVideo % 128));
483
484 // The sender uses the new large-frame capable protocol and is sending a
485 // video packet.
486 if ((header.flags & RTP_LARGE_FRAME) && header.pt == (rtp_TypeVideo % 128)) {
487 return handle_video_packet(session, &header, data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE, m->log);
488 }
489
490 // everything below here is for the old 16 bit protocol ------------------
491
492 if (header.data_length_lower == length - RTP_HEADER_SIZE) {
493 /* The message is sent in single part */
494
495 /* Message is not late; pick up the latest parameters */
496 session->rsequnum = header.sequnum;
497 session->rtimestamp = header.timestamp;
498 bwc_add_recv(session->bwc, length);
499
500 /* Invoke processing of active multiparted message */
501 if (session->mp) {
502 session->mcb(session->cs, session->mp);
503 session->mp = nullptr;
504 }
505
506 /* The message came in the allowed time;
507 */
508
509 return session->mcb(session->cs, new_message(&header, length - RTP_HEADER_SIZE, data + RTP_HEADER_SIZE,
510 length - RTP_HEADER_SIZE));
511 }
512
513 /* The message is sent in multiple parts */
514
515 if (session->mp) {
516 /* There are 2 possible situations in this case:
517 * 1) being that we got the part of already processing message.
518 * 2) being that we got the part of a new/old message.
519 *
520 * We handle them differently as we only allow a single multiparted
521 * processing message
522 */
523 if (session->mp->header.sequnum == header.sequnum &&
524 session->mp->header.timestamp == header.timestamp) {
525 /* First case */
526
527 /* Make sure we have enough allocated memory */
528 if (session->mp->header.data_length_lower - session->mp->len < length - RTP_HEADER_SIZE ||
529 session->mp->header.data_length_lower <= header.offset_lower) {
530 /* There happened to be some corruption on the stream;
531 * continue wihtout this part
532 */
533 return 0;
534 }
535
536 memcpy(session->mp->data + header.offset_lower, data + RTP_HEADER_SIZE,
537 length - RTP_HEADER_SIZE);
538 session->mp->len += length - RTP_HEADER_SIZE;
539 bwc_add_recv(session->bwc, length);
540
541 if (session->mp->len == session->mp->header.data_length_lower) {
542 /* Received a full message; now push it for the further
543 * processing.
544 */
545 session->mcb(session->cs, session->mp);
546 session->mp = nullptr;
547 }
548 } else {
549 /* Second case */
550 if (session->mp->header.timestamp > header.timestamp) {
551 /* The received message part is from the old message;
552 * discard it.
553 */
554 return 0;
555 }
556
557 /* Push the previous message for processing */
558 session->mcb(session->cs, session->mp);
559
560 session->mp = nullptr;
561 goto NEW_MULTIPARTED;
562 }
563 } else {
564 /* In this case threat the message as if it was received in order
565 */
566 /* This is also a point for new multiparted messages */
567NEW_MULTIPARTED:
568
569 /* Message is not late; pick up the latest parameters */
570 session->rsequnum = header.sequnum;
571 session->rtimestamp = header.timestamp;
572 bwc_add_recv(session->bwc, length);
573
574 /* Store message.
575 */
576 session->mp = new_message(&header, header.data_length_lower, data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE);
577 memmove(session->mp->data + header.offset_lower, session->mp->data, session->mp->len);
578 }
579
580 return 0;
581}
36 582
37size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header) 583size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header)
38{ 584{
39 uint8_t *p = rdata; 585 uint8_t *p = rdata;
40 *p++ = (header->protocol_version & 3) << 6 586 *p++ = (header->ve & 3) << 6
41 | (header->pe & 1) << 5 587 | (header->pe & 1) << 5
42 | (header->xe & 1) << 4 588 | (header->xe & 1) << 4
43 | (header->cc & 0xf); 589 | (header->cc & 0xf);
@@ -62,11 +608,10 @@ size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header)
62 return p - rdata; 608 return p - rdata;
63} 609}
64 610
65
66size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header) 611size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header)
67{ 612{
68 const uint8_t *p = data; 613 const uint8_t *p = data;
69 header->protocol_version = (*p >> 6) & 3; 614 header->ve = (*p >> 6) & 3;
70 header->pe = (*p >> 5) & 1; 615 header->pe = (*p >> 5) & 1;
71 header->xe = (*p >> 4) & 1; 616 header->xe = (*p >> 4) & 1;
72 header->cc = *p & 0xf; 617 header->cc = *p & 0xf;
@@ -93,44 +638,56 @@ size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header)
93} 638}
94 639
95 640
96int handle_rtp_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
97
98
99RTPSession *rtp_new(int payload_type, Messenger *m, uint32_t friendnumber, 641RTPSession *rtp_new(int payload_type, Messenger *m, uint32_t friendnumber,
100 BWController *bwc, void *cs, 642 BWController *bwc, void *cs,
101 int (*mcb)(void *, struct RTPMessage *)) 643 int (*mcb)(void *, struct RTPMessage *))
102{ 644{
103 assert(mcb); 645 assert(mcb != nullptr);
104 assert(cs); 646 assert(cs != nullptr);
105 assert(m); 647 assert(m != nullptr);
106 648
107 RTPSession *retu = (RTPSession *)calloc(1, sizeof(RTPSession)); 649 RTPSession *session = (RTPSession *)calloc(1, sizeof(RTPSession));
108 650
109 if (!retu) { 651 if (!session) {
110 LOGGER_WARNING(m->log, "Alloc failed! Program might misbehave!"); 652 LOGGER_WARNING(m->log, "Alloc failed! Program might misbehave!");
111 return nullptr; 653 return nullptr;
112 } 654 }
113 655
114 retu->ssrc = random_u32(); 656 session->work_buffer_list = (struct RTPWorkBufferList *)calloc(1, sizeof(struct RTPWorkBufferList));
115 retu->payload_type = payload_type;
116 657
117 retu->m = m; 658 if (session->work_buffer_list == nullptr) {
118 retu->friend_number = friendnumber; 659 LOGGER_ERROR(m->log, "out of memory while allocating work buffer list");
660 free(session);
661 return nullptr;
662 }
119 663
120 /* Also set payload type as prefix */ 664 // First entry is free.
665 session->work_buffer_list->next_free_entry = 0;
666
667 session->ssrc = payload_type == rtp_TypeVideo ? 0 : random_u32();
668 session->payload_type = payload_type;
669 session->m = m;
670 session->friend_number = friendnumber;
121 671
122 retu->bwc = bwc; 672 // set NULL just in case
123 retu->cs = cs; 673 session->mp = nullptr;
124 retu->mcb = mcb; 674 session->first_packets_counter = 1;
125 675
126 if (-1 == rtp_allow_receiving(retu)) { 676 /* Also set payload type as prefix */
677 session->bwc = bwc;
678 session->cs = cs;
679 session->mcb = mcb;
680
681 if (-1 == rtp_allow_receiving(session)) {
127 LOGGER_WARNING(m->log, "Failed to start rtp receiving mode"); 682 LOGGER_WARNING(m->log, "Failed to start rtp receiving mode");
128 free(retu); 683 free(session->work_buffer_list);
684 free(session);
129 return nullptr; 685 return nullptr;
130 } 686 }
131 687
132 return retu; 688 return session;
133} 689}
690
134void rtp_kill(RTPSession *session) 691void rtp_kill(RTPSession *session)
135{ 692{
136 if (!session) { 693 if (!session) {
@@ -138,10 +695,15 @@ void rtp_kill(RTPSession *session)
138 } 695 }
139 696
140 LOGGER_DEBUG(session->m->log, "Terminated RTP session: %p", session); 697 LOGGER_DEBUG(session->m->log, "Terminated RTP session: %p", session);
141
142 rtp_stop_receiving(session); 698 rtp_stop_receiving(session);
699
700 LOGGER_DEBUG(session->m->log, "Terminated RTP session V3 work_buffer_list->next_free_entry: %d",
701 (int)session->work_buffer_list->next_free_entry);
702
703 free(session->work_buffer_list);
143 free(session); 704 free(session);
144} 705}
706
145int rtp_allow_receiving(RTPSession *session) 707int rtp_allow_receiving(RTPSession *session)
146{ 708{
147 if (session == nullptr) { 709 if (session == nullptr) {
@@ -157,6 +719,7 @@ int rtp_allow_receiving(RTPSession *session)
157 LOGGER_DEBUG(session->m->log, "Started receiving on session: %p", session); 719 LOGGER_DEBUG(session->m->log, "Started receiving on session: %p", session);
158 return 0; 720 return 0;
159} 721}
722
160int rtp_stop_receiving(RTPSession *session) 723int rtp_stop_receiving(RTPSession *session)
161{ 724{
162 if (session == nullptr) { 725 if (session == nullptr) {
@@ -168,42 +731,76 @@ int rtp_stop_receiving(RTPSession *session)
168 LOGGER_DEBUG(session->m->log, "Stopped receiving on session: %p", session); 731 LOGGER_DEBUG(session->m->log, "Stopped receiving on session: %p", session);
169 return 0; 732 return 0;
170} 733}
171int rtp_send_data(RTPSession *session, const uint8_t *data, uint16_t length, Logger *log) 734
735/**
736 * @param input is raw vpx data.
737 * @param length is the length of the raw data.
738 */
739int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
740 bool is_keyframe, Logger *log)
172{ 741{
173 if (!session) { 742 if (!session) {
174 LOGGER_ERROR(log, "No session!"); 743 LOGGER_ERROR(log, "No session!");
175 return -1; 744 return -1;
176 } 745 }
177 746
178 VLA(uint8_t, rdata, length + RTP_HEADER_SIZE + 1); 747 uint8_t is_video_payload = 0;
179 memset(rdata, 0, SIZEOF_VLA(rdata));
180 748
181 rdata[0] = session->payload_type; 749 if (session->payload_type == rtp_TypeVideo) {
750 is_video_payload = 1;
751 }
182 752
183 struct RTPHeader header = {0}; 753 struct RTPHeader header = {0};
184 754
185 header.protocol_version = 2; 755 header.ve = 2; // this is unused in toxav
756
186 header.pe = 0; 757 header.pe = 0;
758
187 header.xe = 0; 759 header.xe = 0;
760
188 header.cc = 0; 761 header.cc = 0;
189 762
190 header.ma = 0; 763 header.ma = 0;
764
191 header.pt = session->payload_type % 128; 765 header.pt = session->payload_type % 128;
192 766
193 header.sequnum = session->sequnum; 767 header.sequnum = session->sequnum;
768
194 header.timestamp = current_time_monotonic(); 769 header.timestamp = current_time_monotonic();
770
195 header.ssrc = session->ssrc; 771 header.ssrc = session->ssrc;
196 772
197 header.offset_lower = 0; 773 header.offset_lower = 0;
774
775 // here the highest bits gets stripped anyway, no need to do keyframe bit magic here!
198 header.data_length_lower = length; 776 header.data_length_lower = length;
199 777
200 if (MAX_CRYPTO_DATA_SIZE > length + RTP_HEADER_SIZE + 1) { 778 header.flags = RTP_LARGE_FRAME;
201 779
780 uint16_t length_safe = (uint16_t)length;
781
782 if (length > UINT16_MAX) {
783 length_safe = UINT16_MAX;
784 }
785
786 header.data_length_lower = length_safe;
787 header.data_length_full = length; // without header
788 header.offset_lower = 0;
789 header.offset_full = 0;
790
791 if (is_keyframe) {
792 header.flags |= RTP_KEY_FRAME;
793 }
794
795 VLA(uint8_t, rdata, length + RTP_HEADER_SIZE + 1);
796 memset(rdata, 0, SIZEOF_VLA(rdata));
797 rdata[0] = session->payload_type; // packet id == payload_type
798
799 if (MAX_CRYPTO_DATA_SIZE > (length + RTP_HEADER_SIZE + 1)) {
202 /** 800 /**
203 * The length is lesser than the maximum allowed length (including header) 801 * The length is lesser than the maximum allowed length (including header)
204 * Send the packet in single piece. 802 * Send the packet in single piece.
205 */ 803 */
206
207 rtp_header_pack(rdata + 1, &header); 804 rtp_header_pack(rdata + 1, &header);
208 memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length); 805 memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length);
209 806
@@ -211,13 +808,11 @@ int rtp_send_data(RTPSession *session, const uint8_t *data, uint16_t length, Log
211 LOGGER_WARNING(session->m->log, "RTP send failed (len: %d)! std error: %s", SIZEOF_VLA(rdata), strerror(errno)); 808 LOGGER_WARNING(session->m->log, "RTP send failed (len: %d)! std error: %s", SIZEOF_VLA(rdata), strerror(errno));
212 } 809 }
213 } else { 810 } else {
214
215 /** 811 /**
216 * The length is greater than the maximum allowed length (including header) 812 * The length is greater than the maximum allowed length (including header)
217 * Send the packet in multiple pieces. 813 * Send the packet in multiple pieces.
218 */ 814 */
219 815 uint32_t sent = 0;
220 uint16_t sent = 0;
221 uint16_t piece = MAX_CRYPTO_DATA_SIZE - (RTP_HEADER_SIZE + 1); 816 uint16_t piece = MAX_CRYPTO_DATA_SIZE - (RTP_HEADER_SIZE + 1);
222 817
223 while ((length - sent) + RTP_HEADER_SIZE + 1 > MAX_CRYPTO_DATA_SIZE) { 818 while ((length - sent) + RTP_HEADER_SIZE + 1 > MAX_CRYPTO_DATA_SIZE) {
@@ -232,6 +827,7 @@ int rtp_send_data(RTPSession *session, const uint8_t *data, uint16_t length, Log
232 827
233 sent += piece; 828 sent += piece;
234 header.offset_lower = sent; 829 header.offset_lower = sent;
830 header.offset_full = sent; // raw data offset, without any header
235 } 831 }
236 832
237 /* Send remaining */ 833 /* Send remaining */
@@ -252,215 +848,3 @@ int rtp_send_data(RTPSession *session, const uint8_t *data, uint16_t length, Log
252 session->sequnum ++; 848 session->sequnum ++;
253 return 0; 849 return 0;
254} 850}
255
256
257static bool chloss(const RTPSession *session, const struct RTPHeader *header)
258{
259 if (header->timestamp < session->rtimestamp) {
260 uint16_t hosq, lost = 0;
261
262 hosq = header->sequnum;
263
264 lost = (hosq > session->rsequnum) ?
265 (session->rsequnum + 65535) - hosq :
266 session->rsequnum - hosq;
267
268 fprintf(stderr, "Lost packet\n");
269
270 while (lost --) {
271 bwc_add_lost(session->bwc, 0);
272 }
273
274 return true;
275 }
276
277 return false;
278}
279static struct RTPMessage *new_message(size_t allocate_len, const uint8_t *data, uint16_t data_length)
280{
281 assert(allocate_len >= data_length);
282
283 struct RTPMessage *msg = (struct RTPMessage *)calloc(sizeof(struct RTPMessage) +
284 (allocate_len - RTP_HEADER_SIZE), 1);
285
286 if (msg == nullptr) {
287 return nullptr;
288 }
289
290 msg->len = data_length - RTP_HEADER_SIZE;
291 rtp_header_unpack(data, &msg->header);
292 memcpy(msg->data, data + RTP_HEADER_SIZE, allocate_len - RTP_HEADER_SIZE);
293
294 return msg;
295}
296
297int handle_rtp_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
298{
299 (void) m;
300 (void) friendnumber;
301
302 RTPSession *session = (RTPSession *)object;
303
304 data ++;
305 length--;
306
307 if (!session || length < RTP_HEADER_SIZE) {
308 LOGGER_WARNING(m->log, "No session or invalid length of received buffer!");
309 return -1;
310 }
311
312 struct RTPHeader header;
313
314 rtp_header_unpack(data, &header);
315
316 if (header.pt != session->payload_type % 128) {
317 LOGGER_WARNING(m->log, "Invalid payload type with the session");
318 return -1;
319 }
320
321 if (header.offset_lower >= header.data_length_lower) {
322 /* Never allow this case to happen */
323 return -1;
324 }
325
326 bwc_feed_avg(session->bwc, length);
327
328 if (header.data_length_lower == length - RTP_HEADER_SIZE) {
329 /* The message is sent in single part */
330
331 /* Only allow messages which have arrived in order;
332 * drop late messages
333 */
334 if (chloss(session, &header)) {
335 return 0;
336 }
337
338 /* Message is not late; pick up the latest parameters */
339 session->rsequnum = header.sequnum;
340 session->rtimestamp = header.timestamp;
341
342 bwc_add_recv(session->bwc, length);
343
344 /* Invoke processing of active multiparted message */
345 if (session->mp) {
346 if (session->mcb) {
347 session->mcb(session->cs, session->mp);
348 } else {
349 free(session->mp);
350 }
351
352 session->mp = nullptr;
353 }
354
355 /* The message came in the allowed time;
356 * process it only if handler for the session is present.
357 */
358
359 if (!session->mcb) {
360 return 0;
361 }
362
363 return session->mcb(session->cs, new_message(length, data, length));
364 }
365
366 /* The message is sent in multiple parts */
367
368 if (session->mp) {
369 /* There are 2 possible situations in this case:
370 * 1) being that we got the part of already processing message.
371 * 2) being that we got the part of a new/old message.
372 *
373 * We handle them differently as we only allow a single multiparted
374 * processing message
375 */
376
377 if (session->mp->header.sequnum == header.sequnum &&
378 session->mp->header.timestamp == header.timestamp) {
379 /* First case */
380
381 /* Make sure we have enough allocated memory */
382 if (session->mp->header.data_length_lower - session->mp->len < length - RTP_HEADER_SIZE ||
383 session->mp->header.data_length_lower <= header.offset_lower) {
384 /* There happened to be some corruption on the stream;
385 * continue wihtout this part
386 */
387 return 0;
388 }
389
390 memcpy(session->mp->data + header.offset_lower, data + RTP_HEADER_SIZE,
391 length - RTP_HEADER_SIZE);
392
393 session->mp->len += length - RTP_HEADER_SIZE;
394
395 bwc_add_recv(session->bwc, length);
396
397 if (session->mp->len == session->mp->header.data_length_lower) {
398 /* Received a full message; now push it for the further
399 * processing.
400 */
401 if (session->mcb) {
402 session->mcb(session->cs, session->mp);
403 } else {
404 free(session->mp);
405 }
406
407 session->mp = nullptr;
408 }
409 } else {
410 /* Second case */
411
412 if (session->mp->header.timestamp > header.timestamp) {
413 /* The received message part is from the old message;
414 * discard it.
415 */
416 return 0;
417 }
418
419 /* Measure missing parts of the old message */
420 bwc_add_lost(session->bwc,
421 (session->mp->header.data_length_lower - session->mp->len) +
422
423 /* Must account sizes of rtp headers too */
424 ((session->mp->header.data_length_lower - session->mp->len) /
425 MAX_CRYPTO_DATA_SIZE) * RTP_HEADER_SIZE);
426
427 /* Push the previous message for processing */
428 if (session->mcb) {
429 session->mcb(session->cs, session->mp);
430 } else {
431 free(session->mp);
432 }
433
434 session->mp = nullptr;
435 goto NEW_MULTIPARTED;
436 }
437 } else {
438 /* In this case threat the message as if it was received in order
439 */
440
441 /* This is also a point for new multiparted messages */
442NEW_MULTIPARTED:
443
444 /* Only allow messages which have arrived in order;
445 * drop late messages
446 */
447 if (chloss(session, &header)) {
448 return 0;
449 }
450
451 /* Message is not late; pick up the latest parameters */
452 session->rsequnum = header.sequnum;
453 session->rtimestamp = header.timestamp;
454
455 bwc_add_recv(session->bwc, length);
456
457 /* Again, only store message if handler is present
458 */
459 if (session->mcb) {
460 session->mp = new_message(header.data_length_lower + RTP_HEADER_SIZE, data, length);
461 memmove(session->mp->data + header.offset_lower, session->mp->data, session->mp->len);
462 }
463 }
464
465 return 0;
466}
diff --git a/toxav/rtp.h b/toxav/rtp.h
index c8af08d7..a310d58a 100644
--- a/toxav/rtp.h
+++ b/toxav/rtp.h
@@ -66,9 +66,10 @@ enum RTPFlags {
66 RTP_KEY_FRAME = 1 << 1, 66 RTP_KEY_FRAME = 1 << 1,
67}; 67};
68 68
69
69struct RTPHeader { 70struct RTPHeader {
70 /* Standard RTP header */ 71 /* Standard RTP header */
71 unsigned protocol_version: 2; /* Version has only 2 bits! */ 72 unsigned ve: 2; /* Version has only 2 bits! */
72 unsigned pe: 1; /* Padding */ 73 unsigned pe: 1; /* Padding */
73 unsigned xe: 1; /* Extra header */ 74 unsigned xe: 1; /* Extra header */
74 unsigned cc: 4; /* Contributing sources count */ 75 unsigned cc: 4; /* Contributing sources count */
@@ -113,33 +114,71 @@ struct RTPHeader {
113 uint16_t data_length_lower; 114 uint16_t data_length_lower;
114}; 115};
115 116
117
116struct RTPMessage { 118struct RTPMessage {
119 /**
120 * This is used in the old code that doesn't deal with large frames, i.e.
121 * the audio code or receiving code for old 16 bit messages. We use it to
122 * record the number of bytes received so far in a multi-part message. The
123 * multi-part message in the old code is stored in \ref RTPSession::mp.
124 */
117 uint16_t len; 125 uint16_t len;
118 126
119 struct RTPHeader header; 127 struct RTPHeader header;
120 uint8_t data[]; 128 uint8_t data[];
121}; 129};
122 130
131#define USED_RTP_WORKBUFFER_COUNT 3
132
133/**
134 * One slot in the work buffer list. Represents one frame that is currently
135 * being assembled.
136 */
137struct RTPWorkBuffer {
138 /**
139 * Whether this slot contains a key frame. This is true iff
140 * buf->header.flags & RTP_KEY_FRAME.
141 */
142 bool is_keyframe;
143 /**
144 * The number of bytes received so far, regardless of which pieces. I.e. we
145 * could have received the first 1000 bytes and the last 1000 bytes with
146 * 4000 bytes in the middle still to come, and this number would be 2000.
147 */
148 uint32_t received_len;
149 /**
150 * The message currently being assembled.
151 */
152 struct RTPMessage *buf;
153};
154
155struct RTPWorkBufferList {
156 int8_t next_free_entry;
157 struct RTPWorkBuffer work_buffer[USED_RTP_WORKBUFFER_COUNT];
158};
159
160#define DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT 10
161
123/** 162/**
124 * RTP control session. 163 * RTP control session.
125 */ 164 */
126typedef struct { 165typedef struct RTPSession {
127 uint8_t payload_type; 166 uint8_t payload_type;
128 uint16_t sequnum; /* Sending sequence number */ 167 uint16_t sequnum; /* Sending sequence number */
129 uint16_t rsequnum; /* Receiving sequence number */ 168 uint16_t rsequnum; /* Receiving sequence number */
130 uint32_t rtimestamp; 169 uint32_t rtimestamp;
131 uint32_t ssrc; 170 uint32_t ssrc; // this seems to be unused!?
132
133 struct RTPMessage *mp; /* Expected parted message */ 171 struct RTPMessage *mp; /* Expected parted message */
134 172 struct RTPWorkBufferList *work_buffer_list;
173 uint8_t first_packets_counter; /* dismiss first few lost video packets */
135 Messenger *m; 174 Messenger *m;
136 uint32_t friend_number; 175 uint32_t friend_number;
137
138 BWController *bwc; 176 BWController *bwc;
139 void *cs; 177 void *cs;
140 int (*mcb)(void *, struct RTPMessage *msg); 178 int (*mcb)(void *, struct RTPMessage *msg);
141} RTPSession; 179} RTPSession;
142 180
181
143/** 182/**
144 * Serialise an RTPHeader to bytes to be sent over the network. 183 * Serialise an RTPHeader to bytes to be sent over the network.
145 * 184 *
@@ -164,7 +203,17 @@ RTPSession *rtp_new(int payload_type, Messenger *m, uint32_t friendnumber,
164void rtp_kill(RTPSession *session); 203void rtp_kill(RTPSession *session);
165int rtp_allow_receiving(RTPSession *session); 204int rtp_allow_receiving(RTPSession *session);
166int rtp_stop_receiving(RTPSession *session); 205int rtp_stop_receiving(RTPSession *session);
167int rtp_send_data(RTPSession *session, const uint8_t *data, uint16_t length, Logger *log); 206/**
207 * Send a frame of audio or video data, chunked in \ref RTPMessage instances.
208 *
209 * @param session The A/V session to send the data for.
210 * @param data A byte array of length \p length.
211 * @param length The number of bytes to send from @p data.
212 * @param is_keyframe Whether this video frame is a key frame. If it is an
213 * audio frame, this parameter is ignored.
214 */
215int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
216 bool is_keyframe, Logger *log);
168 217
169#ifdef __cplusplus 218#ifdef __cplusplus
170} // extern "C" 219} // extern "C"
diff --git a/toxav/rtp_test.cpp b/toxav/rtp_test.cpp
index 60534b07..d6717a28 100644
--- a/toxav/rtp_test.cpp
+++ b/toxav/rtp_test.cpp
@@ -38,7 +38,7 @@ TEST(Rtp, Deserialisation)
38 RTPHeader unpacked = {0}; 38 RTPHeader unpacked = {0};
39 EXPECT_EQ(rtp_header_unpack(rdata, &unpacked), RTP_HEADER_SIZE); 39 EXPECT_EQ(rtp_header_unpack(rdata, &unpacked), RTP_HEADER_SIZE);
40 40
41 EXPECT_EQ(header.protocol_version, unpacked.protocol_version); 41 EXPECT_EQ(header.ve, unpacked.ve);
42 EXPECT_EQ(header.pe, unpacked.pe); 42 EXPECT_EQ(header.pe, unpacked.pe);
43 EXPECT_EQ(header.xe, unpacked.xe); 43 EXPECT_EQ(header.xe, unpacked.xe);
44 EXPECT_EQ(header.cc, unpacked.cc); 44 EXPECT_EQ(header.cc, unpacked.cc);
diff --git a/toxav/toxav.c b/toxav/toxav.c
index c48fc192..4e45b747 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -36,7 +36,18 @@
36#include <stdlib.h> 36#include <stdlib.h>
37#include <string.h> 37#include <string.h>
38 38
39#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) 39// TODO: don't hardcode this, let the application choose it
40// VPX Info: Time to spend encoding, in microseconds (it's a *soft* deadline)
41#define WANTED_MAX_ENCODER_FPS (40)
42#define MAX_ENCODE_TIME_US (1000000 / WANTED_MAX_ENCODER_FPS) // to allow x fps
43
44#define VIDEO_SEND_X_KEYFRAMES_FIRST 7 // force the first n frames to be keyframes!
45
46/*
47VPX_DL_REALTIME (1) deadline parameter analogous to VPx REALTIME mode.
48VPX_DL_GOOD_QUALITY (1000000) deadline parameter analogous to VPx GOOD QUALITY mode.
49VPX_DL_BEST_QUALITY (0) deadline parameter analogous to VPx BEST QUALITY mode.
50*/
40 51
41typedef struct ToxAVCall_s { 52typedef struct ToxAVCall_s {
42 ToxAV *av; 53 ToxAV *av;
@@ -752,13 +763,12 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pc
752 goto END; 763 goto END;
753 } 764 }
754 765
755 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), av->m->log) != 0) { 766 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), false, av->m->log) != 0) {
756 LOGGER_WARNING(av->m->log, "Failed to send audio packet"); 767 LOGGER_WARNING(av->m->log, "Failed to send audio packet");
757 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; 768 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
758 } 769 }
759 } 770 }
760 771
761
762 pthread_mutex_unlock(call->mutex_audio); 772 pthread_mutex_unlock(call->mutex_audio);
763 773
764END: 774END:
@@ -769,12 +779,15 @@ END:
769 779
770 return rc == TOXAV_ERR_SEND_FRAME_OK; 780 return rc == TOXAV_ERR_SEND_FRAME_OK;
771} 781}
782
772bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, 783bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
773 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error) 784 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error)
774{ 785{
775 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; 786 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
776 ToxAVCall *call; 787 ToxAVCall *call;
777 788
789 int vpx_encode_flags = 0;
790
778 if (m_friend_exists(av->m, friend_number) == 0) { 791 if (m_friend_exists(av->m, friend_number) == 0) {
779 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; 792 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
780 goto END; 793 goto END;
@@ -810,12 +823,28 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
810 goto END; 823 goto END;
811 } 824 }
812 825
813 if (vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height) != 0) { 826 if (vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height, -1) != 0) {
814 pthread_mutex_unlock(call->mutex_video); 827 pthread_mutex_unlock(call->mutex_video);
815 rc = TOXAV_ERR_SEND_FRAME_INVALID; 828 rc = TOXAV_ERR_SEND_FRAME_INVALID;
816 goto END; 829 goto END;
817 } 830 }
818 831
832 if (call->video.first->ssrc < VIDEO_SEND_X_KEYFRAMES_FIRST) {
833 // Key frame flag for first frames
834 vpx_encode_flags = VPX_EFLAG_FORCE_KF;
835 LOGGER_INFO(av->m->log, "I_FRAME_FLAG:%d only-i-frame mode", call->video.first->ssrc);
836
837 call->video.first->ssrc++;
838 } else if (call->video.first->ssrc == VIDEO_SEND_X_KEYFRAMES_FIRST) {
839 // normal keyframe placement
840 vpx_encode_flags = 0;
841 LOGGER_INFO(av->m->log, "I_FRAME_FLAG:%d normal mode", call->video.first->ssrc);
842
843 call->video.first->ssrc++;
844 }
845
846 // we start with I-frames (full frames) and then switch to normal mode later
847
819 { /* Encode */ 848 { /* Encode */
820 vpx_image_t img; 849 vpx_image_t img;
821 img.w = img.h = img.d_w = img.d_h = 0; 850 img.w = img.h = img.d_w = img.d_h = 0;
@@ -829,7 +858,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
829 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2)); 858 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
830 859
831 vpx_codec_err_t vrc = vpx_codec_encode(call->video.second->encoder, &img, 860 vpx_codec_err_t vrc = vpx_codec_encode(call->video.second->encoder, &img,
832 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 861 call->video.second->frame_counter, 1, vpx_encode_flags, MAX_ENCODE_TIME_US);
833 862
834 vpx_img_free(&img); 863 vpx_img_free(&img);
835 864
@@ -847,22 +876,31 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
847 vpx_codec_iter_t iter = nullptr; 876 vpx_codec_iter_t iter = nullptr;
848 const vpx_codec_cx_pkt_t *pkt; 877 const vpx_codec_cx_pkt_t *pkt;
849 878
850 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) { 879 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter)) != nullptr) {
851 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 880 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
852 const uint8_t *buf = (const uint8_t *)pkt->data.frame.buf; 881 const bool is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
853 const uint8_t *end = buf + pkt->data.frame.sz; 882
854 883 // https://www.webmproject.org/docs/webm-sdk/structvpx__codec__cx__pkt.html
855 while (buf < end) { 884 // pkt->data.frame.sz -> size_t
856 uint16_t size = MIN(UINT16_MAX, end - buf); 885 const uint32_t frame_length_in_bytes = pkt->data.frame.sz;
857 886
858 if (rtp_send_data(call->video.first, buf, size, av->m->log) < 0) { 887 const int res = rtp_send_data(
859 pthread_mutex_unlock(call->mutex_video); 888 call->video.first,
860 LOGGER_WARNING(av->m->log, "Could not send video frame: %s\n", strerror(errno)); 889 (const uint8_t *)pkt->data.frame.buf,
861 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; 890 frame_length_in_bytes,
862 goto END; 891 is_keyframe,
863 } 892 av->m->log);
864 893
865 buf += size; 894 LOGGER_DEBUG(av->m->log, "+ _sending_FRAME_TYPE_==%s bytes=%d frame_len=%d", is_keyframe ? "K" : ".",
895 (int)pkt->data.frame.sz, (int)frame_length_in_bytes);
896 LOGGER_DEBUG(av->m->log, "+ _sending_FRAME_ b0=%d b1=%d", ((const uint8_t *)pkt->data.frame.buf)[0],
897 ((const uint8_t *)pkt->data.frame.buf)[1]);
898
899 if (res < 0) {
900 pthread_mutex_unlock(call->mutex_video);
901 LOGGER_WARNING(av->m->log, "Could not send video frame: %s", strerror(errno));
902 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
903 goto END;
866 } 904 }
867 } 905 }
868 } 906 }
@@ -878,6 +916,7 @@ END:
878 916
879 return rc == TOXAV_ERR_SEND_FRAME_OK; 917 return rc == TOXAV_ERR_SEND_FRAME_OK;
880} 918}
919
881void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *callback, void *user_data) 920void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *callback, void *user_data)
882{ 921{
883 pthread_mutex_lock(av->mutex); 922 pthread_mutex_lock(av->mutex);
@@ -885,6 +924,7 @@ void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb
885 av->acb.second = user_data; 924 av->acb.second = user_data;
886 pthread_mutex_unlock(av->mutex); 925 pthread_mutex_unlock(av->mutex);
887} 926}
927
888void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *callback, void *user_data) 928void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *callback, void *user_data)
889{ 929{
890 pthread_mutex_lock(av->mutex); 930 pthread_mutex_lock(av->mutex);
@@ -893,7 +933,6 @@ void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb
893 pthread_mutex_unlock(av->mutex); 933 pthread_mutex_unlock(av->mutex);
894} 934}
895 935
896
897/******************************************************************************* 936/*******************************************************************************
898 * 937 *
899 * :: Internal 938 * :: Internal
@@ -913,7 +952,8 @@ void callback_bwc(BWController *bwc, uint32_t friend_number, float loss, void *u
913 952
914 LOGGER_DEBUG(call->av->m->log, "Reported loss of %f%%", loss * 100); 953 LOGGER_DEBUG(call->av->m->log, "Reported loss of %f%%", loss * 100);
915 954
916 if (loss < .01f) { 955 /* if less than 10% data loss we do nothing! */
956 if (loss < 0.1f) {
917 return; 957 return;
918 } 958 }
919 959
@@ -1079,6 +1119,7 @@ bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t stat
1079 1119
1080 return true; 1120 return true;
1081} 1121}
1122
1082ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error) 1123ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
1083{ 1124{
1084 /* Assumes mutex locked */ 1125 /* Assumes mutex locked */
@@ -1100,7 +1141,6 @@ ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
1100 goto END; 1141 goto END;
1101 } 1142 }
1102 1143
1103
1104 call = (ToxAVCall *)calloc(sizeof(ToxAVCall), 1); 1144 call = (ToxAVCall *)calloc(sizeof(ToxAVCall), 1);
1105 1145
1106 if (call == nullptr) { 1146 if (call == nullptr) {
@@ -1161,6 +1201,7 @@ END:
1161 1201
1162 return call; 1202 return call;
1163} 1203}
1204
1164ToxAVCall *call_get(ToxAV *av, uint32_t friend_number) 1205ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
1165{ 1206{
1166 /* Assumes mutex locked */ 1207 /* Assumes mutex locked */
@@ -1170,6 +1211,7 @@ ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
1170 1211
1171 return av->calls[friend_number]; 1212 return av->calls[friend_number];
1172} 1213}
1214
1173ToxAVCall *call_remove(ToxAVCall *call) 1215ToxAVCall *call_remove(ToxAVCall *call)
1174{ 1216{
1175 if (call == nullptr) { 1217 if (call == nullptr) {
@@ -1217,6 +1259,7 @@ CLEAR:
1217 1259
1218 return nullptr; 1260 return nullptr;
1219} 1261}
1262
1220bool call_prepare_transmission(ToxAVCall *call) 1263bool call_prepare_transmission(ToxAVCall *call)
1221{ 1264{
1222 /* Assumes mutex locked */ 1265 /* Assumes mutex locked */
@@ -1305,6 +1348,7 @@ FAILURE_3:
1305 pthread_mutex_destroy(call->mutex_audio); 1348 pthread_mutex_destroy(call->mutex_audio);
1306 return false; 1349 return false;
1307} 1350}
1351
1308void call_kill_transmission(ToxAVCall *call) 1352void call_kill_transmission(ToxAVCall *call)
1309{ 1353{
1310 if (call == nullptr || call->active == 0) { 1354 if (call == nullptr || call->active == 0) {
diff --git a/toxav/video.c b/toxav/video.c
index eee542a2..0014dbb6 100644
--- a/toxav/video.c
+++ b/toxav/video.c
@@ -33,8 +33,124 @@
33#include <assert.h> 33#include <assert.h>
34#include <stdlib.h> 34#include <stdlib.h>
35 35
36#define MAX_DECODE_TIME_US 0 /* Good quality encode. */ 36/**
37#define VIDEO_DECODE_BUFFER_SIZE 20 37 * Soft deadline the decoder should attempt to meet, in "us" (microseconds).
38 * Set to zero for unlimited.
39 *
40 * By convention, the value 1 is used to mean "return as fast as possible."
41 */
42// TODO: don't hardcode this, let the application choose it
43#define WANTED_MAX_DECODER_FPS 40
44
45/**
46 * VPX_DL_REALTIME (1)
47 * deadline parameter analogous to VPx REALTIME mode.
48 *
49 * VPX_DL_GOOD_QUALITY (1000000)
50 * deadline parameter analogous to VPx GOOD QUALITY mode.
51 *
52 * VPX_DL_BEST_QUALITY (0)
53 * deadline parameter analogous to VPx BEST QUALITY mode.
54 */
55#define MAX_DECODE_TIME_US (1000000 / WANTED_MAX_DECODER_FPS) // to allow x fps
56
57/**
58 * Codec control function to set encoder internal speed settings. Changes in
59 * this value influences, among others, the encoder's selection of motion
60 * estimation methods. Values greater than 0 will increase encoder speed at the
61 * expense of quality.
62 *
63 * Note Valid range for VP8: -16..16
64 */
65#define VP8E_SET_CPUUSED_VALUE 16
66
67/**
68 * Initialize encoder with this value. Target bandwidth to use for this stream, in kilobits per second.
69 */
70#define VIDEO_BITRATE_INITIAL_VALUE 5000
71#define VIDEO_DECODE_BUFFER_SIZE 5 // this buffer has normally max. 1 entry
72
73#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
74#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
75
76#define VIDEO_CODEC_DECODER_MAX_WIDTH 800 // its a dummy value, because the struct needs a value there
77#define VIDEO_CODEC_DECODER_MAX_HEIGHT 600 // its a dummy value, because the struct needs a value there
78
79#define VPX_MAX_DIST_NORMAL 40
80#define VPX_MAX_DIST_START 40
81
82#define VPX_MAX_ENCODER_THREADS 4
83#define VPX_MAX_DECODER_THREADS 4
84#define VIDEO__VP8_DECODER_POST_PROCESSING_ENABLED 0
85
86void vc_init_encoder_cfg(Logger *log, vpx_codec_enc_cfg_t *cfg, int16_t kf_max_dist)
87{
88 vpx_codec_err_t rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, cfg, 0);
89
90 if (rc != VPX_CODEC_OK) {
91 LOGGER_ERROR(log, "vc_init_encoder_cfg:Failed to get config: %s", vpx_codec_err_to_string(rc));
92 }
93
94 /* Target bandwidth to use for this stream, in kilobits per second */
95 cfg->rc_target_bitrate = VIDEO_BITRATE_INITIAL_VALUE;
96 cfg->g_w = VIDEO_CODEC_DECODER_MAX_WIDTH;
97 cfg->g_h = VIDEO_CODEC_DECODER_MAX_HEIGHT;
98 cfg->g_pass = VPX_RC_ONE_PASS;
99 cfg->g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
100 cfg->g_lag_in_frames = 0;
101
102 /* Allow lagged encoding
103 *
104 * If set, this value allows the encoder to consume a number of input
105 * frames before producing output frames. This allows the encoder to
106 * base decisions for the current frame on future frames. This does
107 * increase the latency of the encoding pipeline, so it is not appropriate
108 * in all situations (ex: realtime encoding).
109 *
110 * Note that this is a maximum value -- the encoder may produce frames
111 * sooner than the given limit. Set this value to 0 to disable this
112 * feature.
113 */
114 cfg->kf_min_dist = 0;
115 cfg->kf_mode = VPX_KF_AUTO; // Encoder determines optimal placement automatically
116 cfg->rc_end_usage = VPX_VBR; // what quality mode?
117
118 /*
119 * VPX_VBR Variable Bit Rate (VBR) mode
120 * VPX_CBR Constant Bit Rate (CBR) mode
121 * VPX_CQ Constrained Quality (CQ) mode -> give codec a hint that we may be on low bandwidth connection
122 * VPX_Q Constant Quality (Q) mode
123 */
124 if (kf_max_dist > 1) {
125 cfg->kf_max_dist = kf_max_dist; // a full frame every x frames minimum (can be more often, codec decides automatically)
126 LOGGER_DEBUG(log, "kf_max_dist=%d (1)", cfg->kf_max_dist);
127 } else {
128 cfg->kf_max_dist = VPX_MAX_DIST_START;
129 LOGGER_DEBUG(log, "kf_max_dist=%d (2)", cfg->kf_max_dist);
130 }
131
132 cfg->g_threads = VPX_MAX_ENCODER_THREADS; // Maximum number of threads to use
133 /* TODO: set these to something reasonable */
134 // cfg->g_timebase.num = 1;
135 // cfg->g_timebase.den = 60; // 60 fps
136 cfg->rc_resize_allowed = 1; // allow encoder to resize to smaller resolution
137 cfg->rc_resize_up_thresh = 40;
138 cfg->rc_resize_down_thresh = 5;
139
140 /* TODO: make quality setting an API call, but start with normal quality */
141#if 0
142 /* Highest-resolution encoder settings */
143 cfg->rc_dropframe_thresh = 0;
144 cfg->rc_resize_allowed = 0;
145 cfg->rc_min_quantizer = 2;
146 cfg->rc_max_quantizer = 56;
147 cfg->rc_undershoot_pct = 100;
148 cfg->rc_overshoot_pct = 15;
149 cfg->rc_buf_initial_sz = 500;
150 cfg->rc_buf_optimal_sz = 600;
151 cfg->rc_buf_sz = 1000;
152#endif
153}
38 154
39VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data) 155VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
40{ 156{
@@ -52,49 +168,72 @@ VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_re
52 return nullptr; 168 return nullptr;
53 } 169 }
54 170
171 int cpu_used_value = VP8E_SET_CPUUSED_VALUE;
172
55 if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE))) { 173 if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE))) {
56 goto BASE_CLEANUP; 174 goto BASE_CLEANUP;
57 } 175 }
58 176
59 rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, nullptr, 0); 177 /*
178 * VPX_CODEC_USE_FRAME_THREADING
179 * Enable frame-based multi-threading
180 *
181 * VPX_CODEC_USE_ERROR_CONCEALMENT
182 * Conceal errors in decoded frames
183 */
184 vpx_codec_dec_cfg_t dec_cfg;
185 dec_cfg.threads = VPX_MAX_DECODER_THREADS; // Maximum number of threads to use
186 dec_cfg.w = VIDEO_CODEC_DECODER_MAX_WIDTH;
187 dec_cfg.h = VIDEO_CODEC_DECODER_MAX_HEIGHT;
188
189 LOGGER_DEBUG(log, "Using VP8 codec for decoder (0)");
190 rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, &dec_cfg,
191 VPX_CODEC_USE_FRAME_THREADING | VPX_CODEC_USE_POSTPROC);
192
193 if (rc == VPX_CODEC_INCAPABLE) {
194 LOGGER_WARNING(log, "Postproc not supported by this decoder (0)");
195 rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, &dec_cfg, VPX_CODEC_USE_FRAME_THREADING);
196 }
60 197
61 if (rc != VPX_CODEC_OK) { 198 if (rc != VPX_CODEC_OK) {
62 LOGGER_ERROR(log, "Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); 199 LOGGER_ERROR(log, "Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
63 goto BASE_CLEANUP; 200 goto BASE_CLEANUP;
64 } 201 }
65 202
66 /* Set encoder to some initial values 203 if (VIDEO__VP8_DECODER_POST_PROCESSING_ENABLED == 1) {
67 */ 204 vp8_postproc_cfg_t pp = {VP8_DEBLOCK, 1, 0};
68 vpx_codec_enc_cfg_t cfg; 205 vpx_codec_err_t cc_res = vpx_codec_control(vc->decoder, VP8_SET_POSTPROC, &pp);
69 rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
70 206
71 if (rc != VPX_CODEC_OK) { 207 if (cc_res != VPX_CODEC_OK) {
72 LOGGER_ERROR(log, "Failed to get config: %s", vpx_codec_err_to_string(rc)); 208 LOGGER_WARNING(log, "Failed to turn on postproc");
73 goto BASE_CLEANUP_1; 209 } else {
210 LOGGER_DEBUG(log, "turn on postproc: OK");
211 }
212 } else {
213 vp8_postproc_cfg_t pp = {0, 0, 0};
214 vpx_codec_err_t cc_res = vpx_codec_control(vc->decoder, VP8_SET_POSTPROC, &pp);
215
216 if (cc_res != VPX_CODEC_OK) {
217 LOGGER_WARNING(log, "Failed to turn OFF postproc");
218 } else {
219 LOGGER_DEBUG(log, "Disable postproc: OK");
220 }
74 } 221 }
75 222
76 cfg.rc_target_bitrate = 500000; 223 /* Set encoder to some initial values
77 cfg.g_w = 800; 224 */
78 cfg.g_h = 600; 225 vpx_codec_enc_cfg_t cfg;
79 cfg.g_pass = VPX_RC_ONE_PASS; 226 vc_init_encoder_cfg(log, &cfg, 1);
80 /* TODO(mannol): If we set error resilience the app will crash due to bug in vp8.
81 Perhaps vp9 has solved it?*/
82#if 0
83 cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
84#endif
85 cfg.g_lag_in_frames = 0;
86 cfg.kf_min_dist = 0;
87 cfg.kf_max_dist = 48;
88 cfg.kf_mode = VPX_KF_AUTO;
89 227
90 rc = vpx_codec_enc_init(vc->encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); 228 LOGGER_DEBUG(log, "Using VP8 codec for encoder (0.1)");
229 rc = vpx_codec_enc_init(vc->encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, VPX_CODEC_USE_FRAME_THREADING);
91 230
92 if (rc != VPX_CODEC_OK) { 231 if (rc != VPX_CODEC_OK) {
93 LOGGER_ERROR(log, "Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); 232 LOGGER_ERROR(log, "Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
94 goto BASE_CLEANUP_1; 233 goto BASE_CLEANUP_1;
95 } 234 }
96 235
97 rc = vpx_codec_control(vc->encoder, VP8E_SET_CPUUSED, 8); 236 rc = vpx_codec_control(vc->encoder, VP8E_SET_CPUUSED, cpu_used_value);
98 237
99 if (rc != VPX_CODEC_OK) { 238 if (rc != VPX_CODEC_OK) {
100 LOGGER_ERROR(log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); 239 LOGGER_ERROR(log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
@@ -102,6 +241,20 @@ VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_re
102 goto BASE_CLEANUP_1; 241 goto BASE_CLEANUP_1;
103 } 242 }
104 243
244 /*
245 VPX_CTRL_USE_TYPE(VP8E_SET_NOISE_SENSITIVITY, unsigned int)
246 control function to set noise sensitivity
247 0: off, 1: OnYOnly, 2: OnYUV, 3: OnYUVAggressive, 4: Adaptive
248 */
249 /*
250 rc = vpx_codec_control(vc->encoder, VP8E_SET_NOISE_SENSITIVITY, 2);
251
252 if (rc != VPX_CODEC_OK) {
253 LOGGER_ERROR(log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
254 vpx_codec_destroy(vc->encoder);
255 goto BASE_CLEANUP_1;
256 }
257 */
105 vc->linfts = current_time_monotonic(); 258 vc->linfts = current_time_monotonic();
106 vc->lcfd = 60; 259 vc->lcfd = 60;
107 vc->vcb.first = cb; 260 vc->vcb.first = cb;
@@ -109,9 +262,7 @@ VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_re
109 vc->friend_number = friend_number; 262 vc->friend_number = friend_number;
110 vc->av = av; 263 vc->av = av;
111 vc->log = log; 264 vc->log = log;
112
113 return vc; 265 return vc;
114
115BASE_CLEANUP_1: 266BASE_CLEANUP_1:
116 vpx_codec_destroy(vc->decoder); 267 vpx_codec_destroy(vc->decoder);
117BASE_CLEANUP: 268BASE_CLEANUP:
@@ -120,6 +271,7 @@ BASE_CLEANUP:
120 free(vc); 271 free(vc);
121 return nullptr; 272 return nullptr;
122} 273}
274
123void vc_kill(VCSession *vc) 275void vc_kill(VCSession *vc)
124{ 276{
125 if (!vc) { 277 if (!vc) {
@@ -128,7 +280,6 @@ void vc_kill(VCSession *vc)
128 280
129 vpx_codec_destroy(vc->encoder); 281 vpx_codec_destroy(vc->encoder);
130 vpx_codec_destroy(vc->decoder); 282 vpx_codec_destroy(vc->decoder);
131
132 void *p; 283 void *p;
133 284
134 while (rb_read((RingBuffer *)vc->vbuf_raw, &p)) { 285 while (rb_read((RingBuffer *)vc->vbuf_raw, &p)) {
@@ -136,12 +287,11 @@ void vc_kill(VCSession *vc)
136 } 287 }
137 288
138 rb_kill((RingBuffer *)vc->vbuf_raw); 289 rb_kill((RingBuffer *)vc->vbuf_raw);
139
140 pthread_mutex_destroy(vc->queue_mutex); 290 pthread_mutex_destroy(vc->queue_mutex);
141
142 LOGGER_DEBUG(vc->log, "Terminated video handler: %p", vc); 291 LOGGER_DEBUG(vc->log, "Terminated video handler: %p", vc);
143 free(vc); 292 free(vc);
144} 293}
294
145void vc_iterate(VCSession *vc) 295void vc_iterate(VCSession *vc)
146{ 296{
147 if (!vc) { 297 if (!vc) {
@@ -154,45 +304,63 @@ void vc_iterate(VCSession *vc)
154 304
155 pthread_mutex_lock(vc->queue_mutex); 305 pthread_mutex_lock(vc->queue_mutex);
156 306
307 uint32_t full_data_len;
308
157 if (rb_read((RingBuffer *)vc->vbuf_raw, (void **)&p)) { 309 if (rb_read((RingBuffer *)vc->vbuf_raw, (void **)&p)) {
158 pthread_mutex_unlock(vc->queue_mutex); 310 pthread_mutex_unlock(vc->queue_mutex);
311 const struct RTPHeader *const header = &p->header;
312
313 if (header->flags & RTP_LARGE_FRAME) {
314 full_data_len = header->data_length_full;
315 LOGGER_DEBUG(vc->log, "vc_iterate:001:full_data_len=%d", (int)full_data_len);
316 } else {
317 full_data_len = p->len;
318 LOGGER_DEBUG(vc->log, "vc_iterate:002");
319 }
159 320
160 rc = vpx_codec_decode(vc->decoder, p->data, p->len, nullptr, MAX_DECODE_TIME_US); 321 LOGGER_DEBUG(vc->log, "vc_iterate: rb_read p->len=%d p->header.xe=%d", (int)full_data_len, p->header.xe);
322 LOGGER_DEBUG(vc->log, "vc_iterate: rb_read rb size=%d", (int)rb_size((RingBuffer *)vc->vbuf_raw));
323 rc = vpx_codec_decode(vc->decoder, p->data, full_data_len, nullptr, MAX_DECODE_TIME_US);
161 free(p); 324 free(p);
162 325
163 if (rc != VPX_CODEC_OK) { 326 if (rc != VPX_CODEC_OK) {
164 LOGGER_ERROR(vc->log, "Error decoding video: %s", vpx_codec_err_to_string(rc)); 327 LOGGER_ERROR(vc->log, "Error decoding video: %d %s", (int)rc, vpx_codec_err_to_string(rc));
165 } else { 328 } else {
329 /* Play decoded images */
166 vpx_codec_iter_t iter = nullptr; 330 vpx_codec_iter_t iter = nullptr;
167 vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter); 331 vpx_image_t *dest = nullptr;
168 332
169 /* Play decoded images */ 333 while ((dest = vpx_codec_get_frame(vc->decoder, &iter)) != nullptr) {
170 for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
171 if (vc->vcb.first) { 334 if (vc->vcb.first) {
172 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h, 335 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
173 (const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2], 336 (const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
174 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second); 337 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
175 } 338 }
176 339
177 vpx_img_free(dest); 340 vpx_img_free(dest); // is this needed? none of the VPx examples show that
178 } 341 }
179 } 342 }
180 343
181 return; 344 return;
345 } else {
346 LOGGER_TRACE(vc->log, "no Video frame data available");
182 } 347 }
183 348
184 pthread_mutex_unlock(vc->queue_mutex); 349 pthread_mutex_unlock(vc->queue_mutex);
185} 350}
351
186int vc_queue_message(void *vcp, struct RTPMessage *msg) 352int vc_queue_message(void *vcp, struct RTPMessage *msg)
187{ 353{
188 /* This function does the reconstruction of video packets. 354 /* This function is called with complete messages
189 * See more info about video splitting in docs 355 * they have already been assembled.
356 * this function gets called from handle_rtp_packet() and handle_rtp_packet_v3()
190 */ 357 */
191 if (!vcp || !msg) { 358 if (!vcp || !msg) {
192 return -1; 359 return -1;
193 } 360 }
194 361
195 VCSession *vc = (VCSession *)vcp; 362 VCSession *vc = (VCSession *)vcp;
363 const struct RTPHeader *const header = &msg->header;
196 364
197 if (msg->header.pt == (rtp_TypeVideo + 2) % 128) { 365 if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
198 LOGGER_WARNING(vc->log, "Got dummy!"); 366 LOGGER_WARNING(vc->log, "Got dummy!");
@@ -201,41 +369,45 @@ int vc_queue_message(void *vcp, struct RTPMessage *msg)
201 } 369 }
202 370
203 if (msg->header.pt != rtp_TypeVideo % 128) { 371 if (msg->header.pt != rtp_TypeVideo % 128) {
204 LOGGER_WARNING(vc->log, "Invalid payload type!"); 372 LOGGER_WARNING(vc->log, "Invalid payload type! pt=%d", (int)msg->header.pt);
205 free(msg); 373 free(msg);
206 return -1; 374 return -1;
207 } 375 }
208 376
209 pthread_mutex_lock(vc->queue_mutex); 377 pthread_mutex_lock(vc->queue_mutex);
210 free(rb_write((RingBuffer *)vc->vbuf_raw, msg)); 378
211 { 379 if ((header->flags & RTP_LARGE_FRAME) && header->pt == rtp_TypeVideo % 128) {
212 /* Calculate time took for peer to send us this frame */ 380 LOGGER_DEBUG(vc->log, "rb_write msg->len=%d b0=%d b1=%d", (int)msg->len, (int)msg->data[0], (int)msg->data[1]);
213 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
214 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
215 vc->linfts = current_time_monotonic();
216 } 381 }
217 pthread_mutex_unlock(vc->queue_mutex);
218 382
383 free(rb_write((RingBuffer *)vc->vbuf_raw, msg));
384
385 /* Calculate time it took for peer to send us this frame */
386 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
387 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
388 vc->linfts = current_time_monotonic();
389 pthread_mutex_unlock(vc->queue_mutex);
219 return 0; 390 return 0;
220} 391}
221int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height) 392
393int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height, int16_t kf_max_dist)
222{ 394{
223 if (!vc) { 395 if (!vc) {
224 return -1; 396 return -1;
225 } 397 }
226 398
227 vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc; 399 vpx_codec_enc_cfg_t cfg2 = *vc->encoder->config.enc;
228 vpx_codec_err_t rc; 400 vpx_codec_err_t rc;
229 401
230 if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) { 402 if (cfg2.rc_target_bitrate == bit_rate && cfg2.g_w == width && cfg2.g_h == height && kf_max_dist == -1) {
231 return 0; /* Nothing changed */ 403 return 0; /* Nothing changed */
232 } 404 }
233 405
234 if (cfg.g_w == width && cfg.g_h == height) { 406 if (cfg2.g_w == width && cfg2.g_h == height && kf_max_dist == -1) {
235 /* Only bit rate changed */ 407 /* Only bit rate changed */
236 cfg.rc_target_bitrate = bit_rate; 408 LOGGER_INFO(vc->log, "bitrate change from: %u to: %u", (uint32_t)cfg2.rc_target_bitrate, (uint32_t)bit_rate);
237 409 cfg2.rc_target_bitrate = bit_rate;
238 rc = vpx_codec_enc_config_set(vc->encoder, &cfg); 410 rc = vpx_codec_enc_config_set(vc->encoder, &cfg2);
239 411
240 if (rc != VPX_CODEC_OK) { 412 if (rc != VPX_CODEC_OK) {
241 LOGGER_ERROR(vc->log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); 413 LOGGER_ERROR(vc->log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
@@ -245,23 +417,25 @@ int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uin
245 /* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support 417 /* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support
246 * reconfiguring encoder to use resolutions greater than initially set. 418 * reconfiguring encoder to use resolutions greater than initially set.
247 */ 419 */
248
249 LOGGER_DEBUG(vc->log, "Have to reinitialize vpx encoder on session %p", vc); 420 LOGGER_DEBUG(vc->log, "Have to reinitialize vpx encoder on session %p", vc);
250 421 vpx_codec_ctx_t new_c;
422 vpx_codec_enc_cfg_t cfg;
423 vc_init_encoder_cfg(vc->log, &cfg, kf_max_dist);
251 cfg.rc_target_bitrate = bit_rate; 424 cfg.rc_target_bitrate = bit_rate;
252 cfg.g_w = width; 425 cfg.g_w = width;
253 cfg.g_h = height; 426 cfg.g_h = height;
254 427
255 vpx_codec_ctx_t new_c; 428 LOGGER_DEBUG(vc->log, "Using VP8 codec for encoder");
256 429 rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, VPX_CODEC_USE_FRAME_THREADING);
257 rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
258 430
259 if (rc != VPX_CODEC_OK) { 431 if (rc != VPX_CODEC_OK) {
260 LOGGER_ERROR(vc->log, "Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); 432 LOGGER_ERROR(vc->log, "Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
261 return -1; 433 return -1;
262 } 434 }
263 435
264 rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, 8); 436 int cpu_used_value = VP8E_SET_CPUUSED_VALUE;
437
438 rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, cpu_used_value);
265 439
266 if (rc != VPX_CODEC_OK) { 440 if (rc != VPX_CODEC_OK) {
267 LOGGER_ERROR(vc->log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); 441 LOGGER_ERROR(vc->log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
diff --git a/toxav/video.h b/toxav/video.h
index 02670e0a..85ad3129 100644
--- a/toxav/video.h
+++ b/toxav/video.h
@@ -31,8 +31,7 @@
31 31
32#include <vpx/vp8cx.h> 32#include <vpx/vp8cx.h>
33#include <vpx/vp8dx.h> 33#include <vpx/vp8dx.h>
34#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) 34
35#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
36 35
37#include <pthread.h> 36#include <pthread.h>
38 37
@@ -64,6 +63,6 @@ VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_re
64void vc_kill(VCSession *vc); 63void vc_kill(VCSession *vc);
65void vc_iterate(VCSession *vc); 64void vc_iterate(VCSession *vc);
66int vc_queue_message(void *vcp, struct RTPMessage *msg); 65int vc_queue_message(void *vcp, struct RTPMessage *msg);
67int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height); 66int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height, int16_t kf_max_dist);
68 67
69#endif /* VIDEO_H */ 68#endif /* VIDEO_H */
diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel
index 934ff16c..d6c3693e 100644
--- a/toxcore/BUILD.bazel
+++ b/toxcore/BUILD.bazel
@@ -33,7 +33,7 @@ cc_test(
33 srcs = ["crypto_core_test.cpp"], 33 srcs = ["crypto_core_test.cpp"],
34 deps = [ 34 deps = [
35 ":crypto_core", 35 ":crypto_core",
36 "@gtest", 36 "@com_google_googletest//:gtest_main",
37 ], 37 ],
38) 38)
39 39
@@ -77,7 +77,7 @@ cc_test(
77 srcs = ["util_test.cpp"], 77 srcs = ["util_test.cpp"],
78 deps = [ 78 deps = [
79 ":network", 79 ":network",
80 "@gtest", 80 "@com_google_googletest//:gtest_main",
81 ], 81 ],
82) 82)
83 83