2017-01-14 23:46:31 +08:00
|
|
|
/*
|
|
|
|
* Copyright © 2016-2017 The TokTok team.
|
|
|
|
* Copyright © 2013-2015 Tox project.
|
2015-04-21 08:31:12 +08:00
|
|
|
*
|
2017-01-14 23:46:31 +08:00
|
|
|
* This file is part of Tox, the free peer to peer instant messenger.
|
2015-04-21 08:31:12 +08:00
|
|
|
*
|
2017-01-14 23:46:31 +08:00
|
|
|
* Tox is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2015-04-21 08:31:12 +08:00
|
|
|
*
|
2017-01-14 23:46:31 +08:00
|
|
|
* Tox is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2015-04-21 08:31:12 +08:00
|
|
|
*
|
2017-01-14 23:46:31 +08:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
2015-04-21 08:31:12 +08:00
|
|
|
*/
|
2015-10-11 05:54:23 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
|
2016-09-11 22:47:51 +08:00
|
|
|
#include "video.h"
|
2015-04-21 08:31:12 +08:00
|
|
|
|
|
|
|
#include "msi.h"
|
2016-09-22 22:20:33 +08:00
|
|
|
#include "ring_buffer.h"
|
2015-04-22 08:09:37 +08:00
|
|
|
#include "rtp.h"
|
2015-04-21 08:31:12 +08:00
|
|
|
|
|
|
|
#include "../toxcore/logger.h"
|
|
|
|
#include "../toxcore/network.h"
|
|
|
|
|
2016-09-11 22:47:51 +08:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2015-10-11 05:54:23 +08:00
|
|
|
#define MAX_DECODE_TIME_US 0 /* Good quality encode. */
|
2015-04-21 08:31:12 +08:00
|
|
|
#define VIDEO_DECODE_BUFFER_SIZE 20
|
|
|
|
|
2016-09-20 04:49:04 +08:00
|
|
|
VCSession *vc_new(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
|
2015-04-21 08:31:12 +08:00
|
|
|
{
|
2016-09-18 08:31:55 +08:00
|
|
|
VCSession *vc = (VCSession *)calloc(sizeof(VCSession), 1);
|
|
|
|
vpx_codec_err_t rc;
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
if (!vc) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_WARNING(log, "Allocation failed! Application might misbehave!");
|
2018-01-29 05:30:39 +08:00
|
|
|
return nullptr;
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
if (create_recursive_mutex(vc->queue_mutex) != 0) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_WARNING(log, "Failed to create recursive mutex!");
|
2015-04-21 08:31:12 +08:00
|
|
|
free(vc);
|
2018-01-29 05:30:39 +08:00
|
|
|
return nullptr;
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2016-09-01 02:12:19 +08:00
|
|
|
if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE))) {
|
2015-04-21 08:31:12 +08:00
|
|
|
goto BASE_CLEANUP;
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2018-01-29 05:30:39 +08:00
|
|
|
rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, nullptr, 0);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(log, "Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
|
2015-04-21 08:31:12 +08:00
|
|
|
goto BASE_CLEANUP;
|
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
/* Set encoder to some initial values
|
|
|
|
*/
|
|
|
|
vpx_codec_enc_cfg_t cfg;
|
|
|
|
rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
|
|
|
|
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(log, "Failed to get config: %s", vpx_codec_err_to_string(rc));
|
2015-10-24 04:52:32 +08:00
|
|
|
goto BASE_CLEANUP_1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.rc_target_bitrate = 500000;
|
|
|
|
cfg.g_w = 800;
|
|
|
|
cfg.g_h = 600;
|
|
|
|
cfg.g_pass = VPX_RC_ONE_PASS;
|
2016-09-12 23:48:35 +08:00
|
|
|
/* TODO(mannol): If we set error resilience the app will crash due to bug in vp8.
|
|
|
|
Perhaps vp9 has solved it?*/
|
|
|
|
#if 0
|
|
|
|
cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
|
|
|
|
#endif
|
2015-10-24 04:52:32 +08:00
|
|
|
cfg.g_lag_in_frames = 0;
|
|
|
|
cfg.kf_min_dist = 0;
|
|
|
|
cfg.kf_max_dist = 48;
|
|
|
|
cfg.kf_mode = VPX_KF_AUTO;
|
|
|
|
|
|
|
|
rc = vpx_codec_enc_init(vc->encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
|
|
|
|
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(log, "Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
|
2015-10-24 04:52:32 +08:00
|
|
|
goto BASE_CLEANUP_1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = vpx_codec_control(vc->encoder, VP8E_SET_CPUUSED, 8);
|
|
|
|
|
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
2015-10-24 04:52:32 +08:00
|
|
|
vpx_codec_destroy(vc->encoder);
|
|
|
|
goto BASE_CLEANUP_1;
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
vc->linfts = current_time_monotonic();
|
|
|
|
vc->lcfd = 60;
|
2016-09-20 04:49:04 +08:00
|
|
|
vc->vcb.first = cb;
|
|
|
|
vc->vcb.second = cb_data;
|
2015-04-29 07:01:25 +08:00
|
|
|
vc->friend_number = friend_number;
|
2015-05-02 04:15:12 +08:00
|
|
|
vc->av = av;
|
2016-08-19 20:07:45 +08:00
|
|
|
vc->log = log;
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
return vc;
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
BASE_CLEANUP_1:
|
|
|
|
vpx_codec_destroy(vc->decoder);
|
2015-04-21 08:31:12 +08:00
|
|
|
BASE_CLEANUP:
|
|
|
|
pthread_mutex_destroy(vc->queue_mutex);
|
2016-09-18 08:31:55 +08:00
|
|
|
rb_kill((RingBuffer *)vc->vbuf_raw);
|
2015-04-21 08:31:12 +08:00
|
|
|
free(vc);
|
2018-01-29 05:30:39 +08:00
|
|
|
return nullptr;
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
void vc_kill(VCSession *vc)
|
2015-04-21 08:31:12 +08:00
|
|
|
{
|
2016-09-01 02:12:19 +08:00
|
|
|
if (!vc) {
|
2015-04-21 08:31:12 +08:00
|
|
|
return;
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
vpx_codec_destroy(vc->encoder);
|
|
|
|
vpx_codec_destroy(vc->decoder);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
|
|
|
void *p;
|
|
|
|
|
2016-09-18 08:31:55 +08:00
|
|
|
while (rb_read((RingBuffer *)vc->vbuf_raw, &p)) {
|
2015-10-11 05:54:23 +08:00
|
|
|
free(p);
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2016-09-18 08:31:55 +08:00
|
|
|
rb_kill((RingBuffer *)vc->vbuf_raw);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
pthread_mutex_destroy(vc->queue_mutex);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_DEBUG(vc->log, "Terminated video handler: %p", vc);
|
2015-04-21 08:31:12 +08:00
|
|
|
free(vc);
|
|
|
|
}
|
2016-09-20 04:49:04 +08:00
|
|
|
void vc_iterate(VCSession *vc)
|
2015-04-21 08:31:12 +08:00
|
|
|
{
|
2016-09-01 02:12:19 +08:00
|
|
|
if (!vc) {
|
2015-04-21 08:31:12 +08:00
|
|
|
return;
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
|
|
|
struct RTPMessage *p;
|
2016-09-01 03:54:20 +08:00
|
|
|
|
2016-09-18 08:31:55 +08:00
|
|
|
vpx_codec_err_t rc;
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
pthread_mutex_lock(vc->queue_mutex);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2016-09-18 08:31:55 +08:00
|
|
|
if (rb_read((RingBuffer *)vc->vbuf_raw, (void **)&p)) {
|
2015-04-21 08:31:12 +08:00
|
|
|
pthread_mutex_unlock(vc->queue_mutex);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2018-01-29 05:30:39 +08:00
|
|
|
rc = vpx_codec_decode(vc->decoder, p->data, p->len, nullptr, MAX_DECODE_TIME_US);
|
2015-04-21 08:31:12 +08:00
|
|
|
free(p);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2016-09-01 02:12:19 +08:00
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(vc->log, "Error decoding video: %s", vpx_codec_err_to_string(rc));
|
2016-09-01 02:12:19 +08:00
|
|
|
} else {
|
2018-01-29 05:30:39 +08:00
|
|
|
vpx_codec_iter_t iter = nullptr;
|
2015-04-26 06:31:03 +08:00
|
|
|
vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
/* Play decoded images */
|
2015-04-26 06:31:03 +08:00
|
|
|
for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
|
2016-09-20 04:49:04 +08:00
|
|
|
if (vc->vcb.first) {
|
|
|
|
vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
|
|
|
|
(const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
|
|
|
|
dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-04-21 08:31:12 +08:00
|
|
|
vpx_img_free(dest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-11 05:54:23 +08:00
|
|
|
return;
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
|
|
|
|
2015-10-11 05:54:23 +08:00
|
|
|
pthread_mutex_unlock(vc->queue_mutex);
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
int vc_queue_message(void *vcp, struct RTPMessage *msg)
|
2015-04-21 08:31:12 +08:00
|
|
|
{
|
2015-10-11 05:54:23 +08:00
|
|
|
/* This function does the reconstruction of video packets.
|
2015-04-21 08:31:12 +08:00
|
|
|
* See more info about video splitting in docs
|
|
|
|
*/
|
2016-09-01 02:12:19 +08:00
|
|
|
if (!vcp || !msg) {
|
2015-04-22 08:09:37 +08:00
|
|
|
return -1;
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2016-09-18 08:31:55 +08:00
|
|
|
VCSession *vc = (VCSession *)vcp;
|
2016-08-19 20:07:45 +08:00
|
|
|
|
2015-10-11 05:54:23 +08:00
|
|
|
if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_WARNING(vc->log, "Got dummy!");
|
2015-10-11 05:54:23 +08:00
|
|
|
free(msg);
|
2015-04-22 08:09:37 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
|
|
|
if (msg->header.pt != rtp_TypeVideo % 128) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_WARNING(vc->log, "Invalid payload type!");
|
2015-10-11 05:54:23 +08:00
|
|
|
free(msg);
|
2015-04-22 08:09:37 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2015-04-21 08:31:12 +08:00
|
|
|
|
2015-10-11 05:54:23 +08:00
|
|
|
pthread_mutex_lock(vc->queue_mutex);
|
2016-09-18 08:31:55 +08:00
|
|
|
free(rb_write((RingBuffer *)vc->vbuf_raw, msg));
|
2015-10-11 05:54:23 +08:00
|
|
|
{
|
|
|
|
/* Calculate time took for peer to send us this frame */
|
|
|
|
uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
|
|
|
|
vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
|
|
|
|
vc->linfts = current_time_monotonic();
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
pthread_mutex_unlock(vc->queue_mutex);
|
2015-04-21 08:31:12 +08:00
|
|
|
|
2015-04-22 08:09:37 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-11-04 02:42:05 +08:00
|
|
|
int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height)
|
2015-04-22 08:09:37 +08:00
|
|
|
{
|
2016-09-01 02:12:19 +08:00
|
|
|
if (!vc) {
|
2015-04-26 06:31:03 +08:00
|
|
|
return -1;
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc;
|
2016-09-18 08:31:55 +08:00
|
|
|
vpx_codec_err_t rc;
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2016-09-01 02:12:19 +08:00
|
|
|
if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) {
|
2015-04-22 08:09:37 +08:00
|
|
|
return 0; /* Nothing changed */
|
2016-09-01 02:12:19 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-11-04 02:42:05 +08:00
|
|
|
if (cfg.g_w == width && cfg.g_h == height) {
|
2015-10-24 04:52:32 +08:00
|
|
|
/* Only bit rate changed */
|
|
|
|
cfg.rc_target_bitrate = bit_rate;
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
rc = vpx_codec_enc_config_set(vc->encoder, &cfg);
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(vc->log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
2015-10-24 04:52:32 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2015-11-04 02:42:05 +08:00
|
|
|
} else {
|
2015-10-24 04:52:32 +08:00
|
|
|
/* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support
|
|
|
|
* reconfiguring encoder to use resolutions greater than initially set.
|
|
|
|
*/
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_DEBUG(vc->log, "Have to reinitialize vpx encoder on session %p", vc);
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
cfg.rc_target_bitrate = bit_rate;
|
|
|
|
cfg.g_w = width;
|
|
|
|
cfg.g_h = height;
|
|
|
|
|
|
|
|
vpx_codec_ctx_t new_c;
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
|
|
|
|
|
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(vc->log, "Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
|
2015-10-24 04:52:32 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, 8);
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
if (rc != VPX_CODEC_OK) {
|
2016-08-19 20:07:45 +08:00
|
|
|
LOGGER_ERROR(vc->log, "Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
|
2015-10-24 04:52:32 +08:00
|
|
|
vpx_codec_destroy(&new_c);
|
|
|
|
return -1;
|
|
|
|
}
|
2015-11-04 02:42:05 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
vpx_codec_destroy(vc->encoder);
|
|
|
|
memcpy(vc->encoder, &new_c, sizeof(new_c));
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-10-11 05:54:23 +08:00
|
|
|
|
2015-10-24 04:52:32 +08:00
|
|
|
return 0;
|
2015-10-11 05:54:23 +08:00
|
|
|
}
|