diff options
author | zoff99 <zoff@zoff.cc> | 2018-01-19 22:59:42 +0100 |
---|---|---|
committer | iphydf <iphydf@users.noreply.github.com> | 2018-02-11 23:31:46 +0000 |
commit | 721358208b6650c62aa654be922867f10a5d6f38 (patch) | |
tree | 988aef376cc8c74b19b5e605133072bdf3d23e27 /toxav/toxav.c | |
parent | 0647c2c5bc8c871dbcaed64de40eb252d13d303c (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
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r-- | toxav/toxav.c | 90 |
1 files changed, 67 insertions, 23 deletions
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 | /* | ||
47 | VPX_DL_REALTIME (1) deadline parameter analogous to VPx REALTIME mode. | ||
48 | VPX_DL_GOOD_QUALITY (1000000) deadline parameter analogous to VPx GOOD QUALITY mode. | ||
49 | VPX_DL_BEST_QUALITY (0) deadline parameter analogous to VPx BEST QUALITY mode. | ||
50 | */ | ||
40 | 51 | ||
41 | typedef struct ToxAVCall_s { | 52 | typedef 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 | ||
764 | END: | 774 | END: |
@@ -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 | |||
772 | bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, | 783 | bool 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 | |||
881 | void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *callback, void *user_data) | 920 | void 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 | |||
888 | void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *callback, void *user_data) | 928 | void 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 | |||
1082 | ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error) | 1123 | ToxAVCall *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 | |||
1164 | ToxAVCall *call_get(ToxAV *av, uint32_t friend_number) | 1205 | ToxAVCall *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 | |||
1173 | ToxAVCall *call_remove(ToxAVCall *call) | 1215 | ToxAVCall *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 | |||
1220 | bool call_prepare_transmission(ToxAVCall *call) | 1263 | bool 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 | |||
1308 | void call_kill_transmission(ToxAVCall *call) | 1352 | void call_kill_transmission(ToxAVCall *call) |
1309 | { | 1353 | { |
1310 | if (call == nullptr || call->active == 0) { | 1354 | if (call == nullptr || call->active == 0) { |