void CPlayerMedia::wait_on_bytestream (void) { m_decode_thread_waiting = 1; #ifdef DEBUG_DECODE if (m_media_info) media_message(LOG_INFO, "decode thread %s waiting", m_media_info->media); else media_message(LOG_INFO, "decode thread waiting"); #endif SDL_SemWait(m_decode_thread_sem); m_decode_thread_waiting = 0; }
/* * parse_decode_message - handle messages to the decode task */ void CPlayerMedia::parse_decode_message (int &thread_stop, int &decoding) { CMsg *newmsg; if ((newmsg = m_decode_msg_queue.get_message()) != NULL) { #ifdef DEBUG_DECODE_MSGS media_message(LOG_DEBUG, "decode thread message %d",newmsg->get_value()); #endif switch (newmsg->get_value()) { case MSG_STOP_THREAD: thread_stop = 1; break; case MSG_PAUSE_SESSION: decoding = 0; if (m_sync != NULL) { m_sync->flush_decode_buffers(); } break; case MSG_START_DECODING: if (m_sync != NULL) { m_sync->flush_decode_buffers(); } decoding = 1; break; } delete newmsg; } }
int CPlayerMedia::srtp_init (void) { if (m_rtp_session == NULL) return -1; const char *crypto; crypto = find_unparsed_a_value(m_media_info->unparsed_a_lines, "a=crypto"); if (crypto == NULL) { media_message(LOG_CRIT, "%s: can't find a=crypto line in sdp for srtp", get_name()); return -1; } m_srtp_session = srtp_setup_from_sdp(get_name(), m_rtp_session, crypto); if (m_srtp_session == NULL) media_message(LOG_ERR, "player_media_rtp.srtp_init: srtp_setup failed"); return 0; }
/* * determine_payload_type_from_rtp - determine with protocol we're dealing with * in the rtp session. Set various calculations for the sync task, as well... */ int CPlayerMedia::determine_payload_type_from_rtp(void) { uint8_t payload_type, temp; format_list_t *fmt; uint64_t tickpersec; if (m_head != NULL) { payload_type = m_head->rtp_pak_pt; } else { payload_type = atoi(m_media_info->fmt_list->fmt); } fmt = m_media_info->fmt_list; while (fmt != NULL) { // rtp payloads are all numeric temp = atoi(fmt->fmt); if (temp == payload_type) { m_media_fmt = fmt; if (fmt->rtpmap_name != NULL) { tickpersec = fmt->rtpmap_clock_rate; } else { if (payload_type >= 96) { media_message(LOG_ERR, "Media %s, rtp payload type of %u, no rtp map", m_media_info->media, payload_type); return (FALSE); } else { // generic payload type. between 0 and 23 are audio - most // are 8000 // all video (above 25) are 90000 tickpersec = 90000; // this will handle the >= 0 case as well. if (payload_type <= 23) { tickpersec = 8000; if (payload_type == 6) { tickpersec = 16000; } else if (payload_type == 10 || payload_type == 11) { tickpersec = 44100; } else if (payload_type == 14) tickpersec = 90000; } } } create_rtp_byte_stream(payload_type, tickpersec, fmt); uint64_t start_time = (uint64_t)(m_play_start_time * 1000.0); m_rtp_byte_stream->play(start_time); m_byte_stream = m_rtp_byte_stream; if (is_audio()) { m_rtp_byte_stream->set_sync(m_parent); } else { m_parent->synchronize_rtp_bytestreams(NULL); } #if 1 media_message(LOG_DEBUG, "media %s - rtp tps %u ntp per rtp ", m_media_info->media, m_rtptime_tickpersec); #endif return (TRUE); } fmt = fmt->next; } media_message(LOG_ERR, "Payload type %d not in format list for media %s", payload_type, get_name()); return (FALSE); }
/* * CPlayerMedia::recv_callback - callback from RTP with valid data */ void CPlayerMedia::recv_callback (struct rtp *session, rtp_event *e) { if (e == NULL) return; /* * If we're paused, just dump the packet. Multicast case */ if (m_paused) { if (e->type == RX_RTP) { media_message(LOG_DEBUG, "%s dropping pak", get_name()); xfree(e->data); return; } } #if DROP_PAKS if (e->type == RX_RTP && dropcount >= 50) { xfree((rtp_packet *)e->data); dropcount = 0; return; } else { dropcount++; } #endif if (m_rtp_byte_stream != NULL) { if ((m_rtp_byte_stream->recv_callback(session, e) > 0) && (e->type == RX_RTP)) { // indicates they are done buffering. if (m_rtp_buffering == 0) { m_rtp_buffering = 1; start_decoding(); } else { // we're not buffering, but the decode thread might be waiting; if so, // tweak it if we have a complete frame. if (m_decode_thread_waiting) { if (m_rtp_byte_stream->check_rtp_frame_complete_for_payload_type()) { bytestream_primed(); } } } } return; } // we only get here if there is not a valid rtp bytestream yet. switch (e->type) { case RX_RTP: /* regular rtp packet - add it to the queue */ rtp_packet *rpak; rpak = (rtp_packet *)e->data; if (rpak->rtp_data_len == 0) { xfree(rpak); } else { rpak->pd.rtp_pd_timestamp = get_time_of_day(); rpak->pd.rtp_pd_have_timestamp = true; add_rtp_packet_to_queue(rpak, &m_head, &m_tail, get_name()); m_rtp_queue_len++; rtp_check_payload(); } break; case RX_SR: rtcp_sr *srpak; srpak = (rtcp_sr *)e->data; m_rtcp_ntp_frac = srpak->ntp_frac; m_rtcp_ntp_sec = srpak->ntp_sec; m_rtcp_rtp_ts = srpak->rtp_ts; m_rtcp_received = 1; break; case RX_APP: free(e->data); break; default: case RX_RR: case RX_SDES: case RX_BYE: case SOURCE_CREATED: case SOURCE_DELETED: case RX_RR_EMPTY: case RX_RTCP_START: case RX_RTCP_FINISH: case RR_TIMEOUT: #if 0 media_message(LOG_DEBUG, "Thread %u - Callback from rtp with %d %p", SDL_ThreadID(),e->type, e->data); #endif break; } }
/* * 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); }