void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe) { // We might be running in the FFmpeg thread and holding the CameraSource lock // So be careful not to deadlock with anything while toxav locks in toxav_video_send_frame auto it = calls.find(callId); if (it == calls.end()) { return; } ToxFriendCall& call = it->second; if (!call.getVideoEnabled() || !call.isActive() || !(call.getState() & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V)) { return; } if (call.getNullVideoBitrate()) { qDebug() << "Restarting video stream to friend" << callId; #if TOX_VERSION_IS_API_COMPATIBLE(0, 2, 0) toxav_video_set_bit_rate(toxav, callId, VIDEO_DEFAULT_BITRATE, nullptr); #else toxav_bit_rate_set(toxav, callId, -1, VIDEO_DEFAULT_BITRATE, nullptr); #endif call.setNullVideoBitrate(false); } ToxYUVFrame frame = vframe->toToxYUVFrame(); if (!frame) { return; } // TOXAV_ERR_SEND_FRAME_SYNC means toxav failed to lock, retry 5 times in this case // We don't want to be dropping iframes because of some lock held by toxav_iterate TOXAV_ERR_SEND_FRAME err; int retries = 0; do { if (!toxav_video_send_frame(toxav, callId, frame.width, frame.height, frame.y, frame.u, frame.v, &err)) { if (err == TOXAV_ERR_SEND_FRAME_SYNC) { ++retries; QThread::usleep(500); } else { qDebug() << "toxav_video_send_frame error: " << err; } } } while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5); if (err == TOXAV_ERR_SEND_FRAME_SYNC) { qDebug() << "toxav_video_send_frame error: Lock busy, dropping frame"; } }
void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe) { // We might be running in the FFmpeg thread and holding the CameraSource lock // So be careful not to deadlock with anything while toxav locks in toxav_video_send_frame if (!calls.contains(callId)) return; ToxFriendCall& call = calls[callId]; if (!call.videoEnabled || call.inactive || !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V)) return; if (call.nullVideoBitrate) { qDebug() << "Restarting video stream to friend"<<callId; toxav_bit_rate_set(toxav, call.callId, -1, VIDEO_DEFAULT_BITRATE, nullptr); call.nullVideoBitrate = false; } ToxYUVFrame frame = vframe->toToxYUVFrame(); if(!frame) { return; } // TOXAV_ERR_SEND_FRAME_SYNC means toxav failed to lock, retry 5 times in this case // We don't want to be dropping iframes because of some lock held by toxav_iterate TOXAV_ERR_SEND_FRAME err; int retries = 0; do { if (!toxav_video_send_frame(toxav, callId, frame.width, frame.height, frame.y, frame.u, frame.v, &err)) { if (err == TOXAV_ERR_SEND_FRAME_SYNC) { ++retries; QThread::usleep(500); } else { qDebug() << "toxav_video_send_frame error: "<<err; } } } while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5); if (err == TOXAV_ERR_SEND_FRAME_SYNC) qDebug() << "toxav_video_send_frame error: Lock busy, dropping frame"; }
void utox_video_thread(void *args) { ToxAV *av = args; pthread_mutex_init(&video_thread_lock, NULL); init_video_devices(); utox_video_thread_init = 1; while (1) { if (video_thread_msg) { if (!video_msg.msg || video_msg.msg == UTOXVIDEO_KILL) { break; } switch (video_msg.msg) { case UTOXVIDEO_NEW_AV_INSTANCE: { av = video_msg.data; init_video_devices(); break; } } video_thread_msg = false; } if (video_active) { pthread_mutex_lock(&video_thread_lock); // capturing is enabled, capture frames const int r = native_video_getframe(utox_video_frame.y, utox_video_frame.u, utox_video_frame.v, utox_video_frame.w, utox_video_frame.h); if (r == 1) { if (settings.video_preview) { /* Make a copy of the video frame for uTox to display */ UTOX_FRAME_PKG *frame = malloc(sizeof(UTOX_FRAME_PKG)); frame->w = utox_video_frame.w; frame->h = utox_video_frame.h; frame->img = malloc(utox_video_frame.w * utox_video_frame.h * 4); yuv420tobgr(utox_video_frame.w, utox_video_frame.h, utox_video_frame.y, utox_video_frame.u, utox_video_frame.v, utox_video_frame.w, (utox_video_frame.w / 2), (utox_video_frame.w / 2), frame->img); postmessage_utox(AV_VIDEO_FRAME, UINT16_MAX, 1, (void *)frame); } size_t active_video_count = 0; for (size_t i = 0; i < self.friend_list_count; i++) { if (SEND_VIDEO_FRAME(i)) { LOG_TRACE("uToxVideo", "sending video frame to friend %lu" , i); active_video_count++; TOXAV_ERR_SEND_FRAME error = 0; toxav_video_send_frame(av, get_friend(i)->number, utox_video_frame.w, utox_video_frame.h, utox_video_frame.y, utox_video_frame.u, utox_video_frame.v, &error); // LOG_TRACE("uToxVideo", "Sent video frame to friend %u" , i); if (error) { if (error == TOXAV_ERR_SEND_FRAME_SYNC) { LOG_ERR("uToxVideo", "Vid Frame sync error: w=%u h=%u", utox_video_frame.w, utox_video_frame.h); } else if (error == TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED) { LOG_ERR("uToxVideo", "ToxAV disagrees with our AV state for friend %lu, self %u, friend %u", i, get_friend(i)->call_state_self, get_friend(i)->call_state_friend); } else { LOG_ERR("uToxVideo", "toxav_send_video error friend: %i error: %u", get_friend(i)->number, error); } } else { if (active_video_count >= UTOX_MAX_CALLS) { LOG_ERR("uToxVideo", "Trying to send video frame to too many peers. Please report this bug!"); break; } } } } } else if (r == -1) { LOG_ERR("uToxVideo", "Err... something really bad happened trying to get this frame, I'm just going " "to plots now!"); video_device_stop(); close_video_device(video_device); } pthread_mutex_unlock(&video_thread_lock); yieldcpu(1000 / settings.video_fps); /* 60fps = 16.666ms || 25 fps = 40ms || the data quality is SO much better at 25... */ continue; /* We're running video, so don't sleep for an extra 100 ms */ } yieldcpu(100); } video_device_count = 0; video_device_current = 0; video_active = false; for (uint8_t i = 0; i < 16; ++i) { video_device[i] = NULL; } video_thread_msg = 0; utox_video_thread_init = 0; LOG_TRACE("uToxVideo", "Clean thread exit!"); }
static void *call_thread(void *pd) { ToxAV *AliceAV = ((thread_data *) pd)->AliceAV; ToxAV *BobAV = ((thread_data *) pd)->BobAV; CallControl *AliceCC = ((thread_data *) pd)->AliceCC; CallControl *BobCC = ((thread_data *) pd)->BobCC; uint32_t friend_number = ((thread_data *) pd)->friend_number; memset(AliceCC, 0, sizeof(CallControl)); memset(BobCC, 0, sizeof(CallControl)); { /* Call */ TOXAV_ERR_CALL rc; toxav_call(AliceAV, friend_number, 48, 3000, &rc); if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } while (!BobCC->incoming) { c_sleep(10); } { /* Answer */ TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 8, 500, &rc); if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); ck_assert(0); } } c_sleep(30); int16_t *PCM = (int16_t *)calloc(960, sizeof(int16_t)); uint8_t *video_y = (uint8_t *)calloc(800 * 600, sizeof(uint8_t)); uint8_t *video_u = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t)); uint8_t *video_v = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t)); time_t start_time = time(NULL); while (time(NULL) - start_time < 4) { toxav_iterate(AliceAV); toxav_iterate(BobAV); toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL); toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL); toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL); toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL); c_sleep(10); } { /* Hangup */ TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc); if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d %p %p\n", rc, (void *)AliceAV, (void *)BobAV); } } c_sleep(30); free(PCM); free(video_y); free(video_u); free(video_v); printf("Closing thread\n"); pthread_exit(NULL); }