int CPlayerSession::sync_thread_audio_resync (void) { int state; uint64_t audio_resync_time = 0; int64_t wait_audio_time; bool video_have_next_time = false; uint64_t video_next_time; bool have_audio_eof = false; bool need_audio_resync = false; bool have_video_eof = false; bool need_audio_restart = false; state = process_msg_queue(SYNC_STATE_AUDIO_RESYNC); if (state == SYNC_STATE_AUDIO_RESYNC) { get_current_time(); need_audio_resync = m_audio_sync->check_audio_sync(m_current_time, audio_resync_time, wait_audio_time, have_audio_eof, need_audio_restart); if (need_audio_restart) { // this occurs when the time is in the past, but we're continuing // video return SYNC_STATE_WAIT_SYNC; } if (need_audio_resync == false) { // video continued, so, restart audio sync_message(LOG_ERR, "resync but no audio resync"); return SYNC_STATE_PLAYING; } have_video_eof = true; video_next_time = MAX_UINT64; for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { uint64_t cmp; bool have_eof; if (ts->play_at(m_current_time, true, cmp, have_eof)) { video_have_next_time = true; video_next_time = MIN(cmp, video_next_time); have_video_eof &= have_eof; } } sync_message(LOG_DEBUG, "audio resync - "U64" have_next %d "U64, m_current_time, video_have_next_time, video_next_time); sync_message(LOG_DEBUG, "audio time "U64, audio_resync_time); if (have_audio_eof && have_video_eof) { return SYNC_STATE_DONE; } if (video_have_next_time == false) { SDL_SemWaitTimeout(m_sync_sem, 10); } else { int64_t diff = video_next_time - audio_resync_time; if (diff >= TO_D64(-5000) && diff <= TO_D64(5000)) { return SYNC_STATE_WAIT_SYNC; } diff = video_next_time - m_current_time; if (diff < 0 || diff > TO_D64(1000)) { // drop this frame return SYNC_STATE_WAIT_SYNC; } if (diff > 9) { diff = 9; } if (diff >= 9) { int delay = (int)diff; SDL_SemWaitTimeout(m_sync_sem, delay); } } } return state; }
void CRtpByteStreamBase::set_wallclock_offset (uint64_t wclock, uint32_t rtp_ts) { int32_t rtp_ts_diff; int64_t wclock_diff; uint64_t wclock_calc; bool set = true; bool had_recvd_rtcp; if (m_rtcp_received == 1 /*&& m_stream_ondemand == 0*/) { rtp_ts_diff = rtp_ts; rtp_ts_diff -= m_rtcp_rtp_ts; wclock_diff = (int64_t)rtp_ts_diff; wclock_diff *= TO_D64(1000); wclock_diff /= (int64_t)m_timescale; wclock_calc = m_rtcp_ts; wclock_calc += wclock_diff; set = false; if (wclock_calc != wclock) { #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "%s - set wallclock - wclock should be "U64" is "U64, m_name, wclock_calc, wclock); #endif // don't change wclock offset if it's > 100 msec - otherwise, // it's annoying noise int64_t diff = wclock_calc - wclock; if (abs(diff) > 2 && abs(diff) < 100) { set = false; // rtp_message(LOG_DEBUG, "not changing"); // we'll allow a msec drift here or there to allow for rounding - // we want this to change every so often } } } had_recvd_rtcp = m_rtcp_received; m_rtcp_received = true; SDL_LockMutex(m_rtp_packet_mutex); if (set) { m_rtcp_ts = wclock; m_rtcp_rtp_ts = rtp_ts; } if (m_have_first_pak_ts) { // we only want positives here int32_t diff; diff = rtp_ts - m_first_pak_rtp_ts; int32_t compare = 3600 * m_timescale; #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "%s - 1st rtp ts %u rtp %u %u", m_name, m_first_pak_rtp_ts, rtp_ts, diff); rtp_message(LOG_DEBUG, "%s - 1st ts "U64, m_name, m_first_pak_ts); #endif if (diff > compare) { // adjust once an hour, to keep errors low // we'll adjust the timestamp and rtp timestamp int64_t ts_diff; ts_diff = (int64_t)diff; ts_diff *= TO_U64(1000); ts_diff /= (int64_t)m_timescale; m_first_pak_ts += ts_diff; m_first_pak_rtp_ts += diff; #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "CHANGE %s - first pak ts is now "U64" rtp %u", m_name, m_first_pak_ts, m_first_pak_rtp_ts); #endif } // We've received an RTCP - see if we need to syncronize // the video streams. if (m_psptr != NULL) { rtcp_sync_t sync; sync.first_pak_ts = m_first_pak_ts; sync.first_pak_rtp_ts = m_first_pak_rtp_ts; sync.rtcp_ts = m_rtcp_ts; sync.rtcp_rtp_ts = m_rtcp_rtp_ts; sync.timescale = m_timescale; m_psptr->synchronize_rtp_bytestreams(&sync); } else { // if this is our first rtcp, try to synchronize if (!had_recvd_rtcp) synchronize(NULL); } } SDL_UnlockMutex(m_rtp_packet_mutex); }
uint64_t CRtpByteStreamBase::rtp_ts_to_msec (uint32_t rtp_ts, uint64_t uts, uint64_t &wrap_offset) { uint64_t timetick; uint64_t adjusted_rtp_ts; uint64_t adjusted_wc_rtp_ts; bool have_wrap = false; uint32_t this_mask, last_mask; last_mask = m_last_rtp_ts & (1U << 31); this_mask = rtp_ts & (1U << 31); if (last_mask != this_mask) { if (this_mask == 0) { wrap_offset += (TO_U64(1) << 32); have_wrap = true; rtp_message(LOG_DEBUG, "%s - have wrap %x new %x", m_name, m_last_rtp_ts, rtp_ts); } else { // need to do something here } } if (m_stream_ondemand) { adjusted_rtp_ts = wrap_offset; adjusted_rtp_ts += rtp_ts; adjusted_wc_rtp_ts = m_base_rtp_ts; if (adjusted_wc_rtp_ts > adjusted_rtp_ts) { timetick = adjusted_wc_rtp_ts - adjusted_rtp_ts; timetick *= TO_U64(1000); timetick /= m_timescale; if (timetick > m_play_start_time) { timetick = 0; } else { timetick = m_play_start_time - timetick; } } else { timetick = adjusted_rtp_ts - adjusted_wc_rtp_ts; timetick *= TO_U64(1000); timetick /= m_timescale; timetick += m_play_start_time; } } else { // We've got a broadcast scenario here... if (m_have_first_pak_ts == false) { // We haven't processed the first packet yet - we record // the data here. m_first_pak_rtp_ts = rtp_ts; m_first_pak_ts = uts; m_have_first_pak_ts = true; rtp_message(LOG_DEBUG, "%s first pak ts %u "U64, m_name, m_first_pak_rtp_ts, m_first_pak_ts); // if we have received RTCP, set the wallclock offset, which // triggers the synchronization effort. if (m_rtcp_received) { // calculate other stuff //rtp_message(LOG_DEBUG, "%s rtp_ts_to_msec calling wallclock", m_name); set_wallclock_offset(m_rtcp_ts, m_rtcp_rtp_ts); } } SDL_LockMutex(m_rtp_packet_mutex); // fairly simple calculation to calculate the timestamp // based on this rtp timestamp, the first pak rtp timestamp and // the first packet timestamp. int32_t adder; int64_t ts_adder; if (have_wrap) { adder = rtp_ts - m_first_pak_rtp_ts; // adjust once an hour, to keep errors low // we'll adjust the timestamp and rtp timestamp ts_adder = (int64_t)adder; ts_adder *= TO_D64(1000); ts_adder /= (int64_t)m_timescale; m_first_pak_ts += ts_adder; m_first_pak_rtp_ts = rtp_ts; #ifdef DEBUG_RTP_BCAST rtp_message(LOG_DEBUG, "%s adjust for wrap - first pak ts is now "U64" rtp %u", m_name, m_first_pak_ts, m_first_pak_rtp_ts); #endif } // adder could be negative here, based on the RTCP we receive adder = rtp_ts - m_first_pak_rtp_ts; ts_adder = (int64_t)adder; ts_adder *= TO_D64(1000); ts_adder /= (int64_t)m_timescale; timetick = m_first_pak_ts; timetick += ts_adder; SDL_UnlockMutex(m_rtp_packet_mutex); #ifdef DEBUG_RTP_BCAST rtp_message(LOG_DEBUG, "%s ts %x base %x "U64" tp "U64" adder %d "D64, m_name, rtp_ts, m_first_pak_rtp_ts, m_first_pak_ts, timetick, adder, ts_adder); #endif } #ifdef DEBUG_RTP_TS rtp_message(LOG_DEBUG,"%s time "U64" %u", m_name, timetick, rtp_ts); #endif // record time m_last_rtp_ts = rtp_ts; m_last_realtime = timetick; return (timetick); }
/* * Main decode thread. */ int CPlayerMedia::decode_thread (void) { // uint32_t msec_per_frame = 0; int ret = 0; int thread_stop = 0, decoding = 0; uint32_t decode_skipped_frames = 0; frame_timestamp_t ourtime, lasttime; // Tell bytestream we're starting the next frame - they'll give us // the time. uint8_t *frame_buffer; uint32_t frame_len; void *ud = NULL; uint32_t frames_decoded; uint64_t start_decode_time = 0; uint64_t last_decode_time = 0; bool have_start_time = false; bool have_frame_ts = false; bool found_out_of_range_ts = false; uint64_t bytes_decoded; lasttime.msec_timestamp = 0; frames_decoded = 0; bytes_decoded = 0; while (thread_stop == 0) { // waiting here for decoding or thread stop ret = SDL_SemWait(m_decode_thread_sem); #ifdef DEBUG_DECODE media_message(LOG_DEBUG, "%s Decode thread awake", get_name()); #endif parse_decode_message(thread_stop, decoding); if (decoding == 1) { // We've been told to start decoding - if we don't have a codec, // create one m_sync->set_wait_sem(m_decode_thread_sem); if (m_plugin == NULL) { switch (m_sync_type) { case VIDEO_SYNC: create_video_plugin(NULL, STREAM_TYPE_RTP, NULL, -1, -1, m_media_fmt, NULL, m_user_data, m_user_data_size); break; case AUDIO_SYNC: create_audio_plugin(NULL, STREAM_TYPE_RTP, NULL, -1, -1, m_media_fmt, NULL, m_user_data, m_user_data_size); break; case TIMED_TEXT_SYNC: create_text_plugin(NULL, STREAM_TYPE_RTP, NULL, m_media_fmt, m_user_data, m_user_data_size); break; } if (m_plugin_data == NULL) { m_plugin = NULL; } else { media_message(LOG_DEBUG, "Starting %s codec from decode thread", m_plugin->c_name); } } if (m_plugin != NULL) { m_plugin->c_do_pause(m_plugin_data); } else { while (thread_stop == 0 && decoding) { SDL_Delay(100); if (m_rtp_byte_stream) { m_rtp_byte_stream->flush_rtp_packets(); } parse_decode_message(thread_stop, decoding); } } } /* * this is our main decode loop */ #ifdef DEBUG_DECODE media_message(LOG_DEBUG, "%s Into decode loop", get_name()); #endif while ((thread_stop == 0) && decoding) { parse_decode_message(thread_stop, decoding); if (thread_stop != 0) continue; if (decoding == 0) { m_plugin->c_do_pause(m_plugin_data); have_frame_ts = false; continue; } if (m_byte_stream->eof()) { media_message(LOG_INFO, "%s hit eof", get_name()); if (m_sync) m_sync->set_eof(); decoding = 0; continue; } if (m_byte_stream->have_frame() == false) { // Indicate that we're waiting, and wait for a message from RTP // task. wait_on_bytestream(); continue; } frame_buffer = NULL; bool have_frame; memset(&ourtime, 0, sizeof(ourtime)); have_frame = m_byte_stream->start_next_frame(&frame_buffer, &frame_len, &ourtime, &ud); if (have_frame == false) continue; /* * If we're decoding video, see if we're playing - if so, check * if we've fallen significantly behind the audio */ if (get_sync_type() == VIDEO_SYNC && (m_parent->get_session_state() == SESSION_PLAYING) && have_frame_ts) { int64_t ts_diff = ourtime.msec_timestamp - lasttime.msec_timestamp; if (ts_diff > TO_D64(1000) || ts_diff < TO_D64(-1000)) { // out of range timestamp - we'll want to not skip here found_out_of_range_ts = true; media_message(LOG_INFO, "found out of range ts "U64" last "U64" "D64, ourtime.msec_timestamp, lasttime.msec_timestamp, ts_diff); } else { uint64_t current_time = m_parent->get_playing_time(); if (found_out_of_range_ts) { ts_diff = current_time - ourtime.msec_timestamp; if (ts_diff > TO_D64(0) && ts_diff < TO_D64(5000)) { found_out_of_range_ts = false; media_message(LOG_INFO, "ts back in playing range "U64" "D64, ourtime.msec_timestamp, ts_diff); } } else { // regular time if (current_time >= ourtime.msec_timestamp) { media_message(LOG_INFO, "Candidate for skip decode "U64" our "U64, current_time, ourtime.msec_timestamp); // If the bytestream can skip ahead, let's do so if (m_byte_stream->can_skip_frame() != 0) { int ret; int hassync; int count; current_time += 200; count = 0; // Skip up to the current time + 200 msec ud = NULL; do { if (ud != NULL) free(ud); frame_buffer = NULL; ret = m_byte_stream->skip_next_frame(&ourtime, &hassync, &frame_buffer, &frame_len, &ud); decode_skipped_frames++; } while (ret != 0 && !m_byte_stream->eof() && current_time > ourtime.msec_timestamp); if (m_byte_stream->eof() || ret == 0) continue; media_message(LOG_INFO, "Skipped ahead "U64 " to "U64, current_time - 200, ourtime.msec_timestamp); /* * Ooh - fun - try to match to the next sync value - if not, * 15 frames */ do { if (ud != NULL) free(ud); ret = m_byte_stream->skip_next_frame(&ourtime, &hassync, &frame_buffer, &frame_len, &ud); if (hassync < 0) { uint64_t diff = ourtime.msec_timestamp - current_time; if (diff > TO_U64(200)) { hassync = 1; } } decode_skipped_frames++; count++; } while (ret != 0 && hassync <= 0 && count < 30 && !m_byte_stream->eof()); if (m_byte_stream->eof() || ret == 0) continue; #ifdef DEBUG_DECODE media_message(LOG_INFO, "Matched ahead - count %d, sync %d time "U64, count, hassync, ourtime.msec_timestamp); #endif } } } // end regular time } } lasttime = ourtime; have_frame_ts = true; #ifdef DEBUG_DECODE media_message(LOG_DEBUG, "Decoding %s frame " U64, get_name(), ourtime.msec_timestamp); #endif if (frame_buffer != NULL && frame_len != 0) { int sync_frame; ret = m_plugin->c_decode_frame(m_plugin_data, &ourtime, m_streaming, &sync_frame, frame_buffer, frame_len, ud); #ifdef DEBUG_DECODE media_message(LOG_DEBUG, "Decoding %s frame return %d", get_name(), ret); #endif if (ret > 0) { frames_decoded++; if (have_start_time == false) { have_start_time = true; start_decode_time = ourtime.msec_timestamp; } last_decode_time = ourtime.msec_timestamp; m_byte_stream->used_bytes_for_frame(ret); bytes_decoded += ret; } else { m_byte_stream->used_bytes_for_frame(frame_len); } } } // calculate frame rate for session } if (is_audio() == false) media_message(LOG_NOTICE, "Video decoder skipped %u frames", decode_skipped_frames); if (last_decode_time > start_decode_time) { double fps, bps; double secs; uint64_t total_time = last_decode_time - start_decode_time; secs = UINT64_TO_DOUBLE(total_time); secs /= 1000.0; #if 0 media_message(LOG_DEBUG, "last time "U64" first "U64, last_decode_time, start_decode_time); #endif fps = frames_decoded; fps /= secs; bps = UINT64_TO_DOUBLE(bytes_decoded); bps *= 8.0 / secs; media_message(LOG_NOTICE, "%s - bytes "U64", seconds %g, fps %g bps %g", get_name(), bytes_decoded, secs, fps, bps); } if (m_plugin) { m_plugin->c_close(m_plugin_data); m_plugin_data = NULL; } return (0); }