From fe5ae89555ead1863420c1a9c389553e75e4527c Mon Sep 17 00:00:00 2001 From: "N.N." Date: Tue, 30 Mar 2004 02:59:55 +0000 Subject: New in PiDiP 0.12.13 svn path=/trunk/externals/pidip/; revision=1509 --- modules/pdp_mp4playermedia.cpp | 2029 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2029 insertions(+) create mode 100644 modules/pdp_mp4playermedia.cpp (limited to 'modules/pdp_mp4playermedia.cpp') diff --git a/modules/pdp_mp4playermedia.cpp b/modules/pdp_mp4playermedia.cpp new file mode 100644 index 0000000..75235e7 --- /dev/null +++ b/modules/pdp_mp4playermedia.cpp @@ -0,0 +1,2029 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is MPEG4IP. + * + * The Initial Developer of the Original Code is Cisco Systems Inc. + * Portions created by Cisco Systems Inc. are + * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. + * + * Contributor(s): + * Bill May wmay@cisco.com + * + * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr) + */ + +/* + * player_media.cpp - handle generic information about a stream + */ +#include "mpeg4ip.h" +#include "pdp_mp4playersession.h" +#include "pdp_mp4playermedia.h" +#include "pdp_mp4videosync.h" +#include "pdp_mp4audiosync.h" +#include "player_sdp.h" +#include "player_util.h" +#include +#include "pdp_mp4rtpbytestream.h" +#include "our_config_file.h" +#include "media_utils.h" +#include "ip_port.h" +#include "codec_plugin.h" +#include "audio.h" +#include +#include +#include "our_config_file.h" +#include "rtp_plugin.h" +#include "media_utils.h" +#include "rfc3119_bytestream.h" +#include "mpeg3_rtp_bytestream.h" +#include "codec/mp3/mp3_rtp_bytestream.h" +#include "rtp_bytestream_plugin.h" +#include "codec_plugin_private.h" + +static int pdp_recv_thread (void *data) +{ + CPlayerMedia *media; + + media = (CPlayerMedia *)data; + return (media->recv_thread()); +} + +static int pdp_decode_thread (void *data) +{ + CPlayerMedia *media; + media = (CPlayerMedia *)data; + return (media->decode_thread()); +} + +static void pdp_rtp_packet_callback (void *data, + unsigned char interleaved, + struct rtp_packet *pak, + int len) +{ + ((CPlayerMedia *)data)->rtp_receive_packet(interleaved, pak, len); +} + +static int pdp_init_rtp_tcp (void *data) +{ + ((CPlayerMedia *)data)->rtp_init_tcp(); + return 0; +} + +static int pdp_rtp_start (void *data) +{ + ((CPlayerMedia *)data)->rtp_start(); + return 0; +} + +static int pdp_rtp_periodic (void *data) +{ + ((CPlayerMedia *)data)->rtp_periodic(); + return 0; +} + +static void pdp_recv_callback (struct rtp *session, rtp_event *e) +{ + CPlayerMedia *m = (CPlayerMedia *)rtp_get_userdata(session); + m->recv_callback(session, e); +} + +static int pdp_rtcp_send_packet (void *ud, uint8_t *buffer, int buflen) +{ + return ((CPlayerMedia *)ud)->rtcp_send_packet(buffer, buflen); +} + +CPlayerMedia::CPlayerMedia (CPlayerSession *p) +{ + m_plugin = NULL; + m_plugin_data = NULL; + m_next = NULL; + m_parent = p; + m_media_info = NULL; + m_media_fmt = NULL; + m_our_port = 0; + m_ports = NULL; + m_server_port = 0; + m_source_addr = NULL; + m_recv_thread = NULL; + m_rtptime_tickpersec = 0; + m_rtsp_base_seq_received = 0; + m_rtsp_base_ts_received = 0; + + m_head = NULL; + m_rtp_queue_len = 0; + + m_rtp_ssrc_set = FALSE; + + m_rtsp_session = NULL; + m_decode_thread_waiting = 0; + m_sync_time_set = FALSE; + m_decode_thread = NULL; + m_decode_thread_sem = NULL; + m_video_sync = NULL; + m_audio_sync = NULL; + m_paused = 0; + m_byte_stream = NULL; + m_rtp_byte_stream = NULL; + m_video_info = NULL; + m_audio_info = NULL; + m_user_data = NULL; + m_rtcp_received = 0; + m_streaming = 0; + m_stream_ondemand = 0; + m_rtp_use_rtsp = 0; +} + +CPlayerMedia::~CPlayerMedia() +{ + rtsp_decode_t *rtsp_decode; + + media_message(LOG_DEBUG, "closing down media %d", m_is_video); + if (m_rtsp_session) { + // If control is aggregate, m_rtsp_session will be freed by + // CPDPPlayerSession + if (m_parent->session_control_is_aggregate() == 0) { + rtsp_send_teardown(m_rtsp_session, NULL, &rtsp_decode); + free_decode_response(rtsp_decode); + } + m_rtsp_session = NULL; + } + + if (m_recv_thread) { + m_rtp_msg_queue.send_message(MSG_STOP_THREAD); + SDL_WaitThread(m_recv_thread, NULL); + m_recv_thread = NULL; + } + + if (m_decode_thread) { + m_decode_msg_queue.send_message(MSG_STOP_THREAD, + NULL, + 0, + m_decode_thread_sem); + SDL_WaitThread(m_decode_thread, NULL); + m_decode_thread = NULL; + } + + + + if (m_source_addr != NULL) free(m_source_addr); + m_next = NULL; + m_parent = NULL; + + if (m_ports) { + delete m_ports; + m_ports = NULL; + } + if (m_rtp_byte_stream) { + double diff; + diff = difftime(time(NULL), m_start_time); + media_message(LOG_INFO, "Media %s", m_media_info->media); + + media_message(LOG_INFO, "Time: %g seconds", diff); +#if 0 + double div; + player_debug_message("Packets received: %u", m_rtp_packet_received); + player_debug_message("Payload received: "LLU" bytes", m_rtp_data_received); + div = m_rtp_packet_received / diff; + player_debug_message("Packets per sec : %g", div); + div = UINT64_TO_DOUBLE(m_rtp_data_received); + div *= 8.0; + div /= diff; + media_message(LOG_INFO, "Bits per sec : %g", div); +#endif + + } + if (m_byte_stream) { + delete m_byte_stream; + m_byte_stream = NULL; + m_rtp_byte_stream = NULL; + } + if (m_video_info) { + free(m_video_info); + m_video_info = NULL; + } + if (m_audio_info) { + free(m_audio_info); + m_audio_info = NULL; + } + if (m_user_data) { + free((void *)m_user_data); + m_user_data = NULL; + } + if (m_decode_thread_sem) { + SDL_DestroySemaphore(m_decode_thread_sem); + m_decode_thread_sem = NULL; + } +} + +void CPlayerMedia::clear_rtp_packets (void) +{ + if (m_head != NULL) { + m_tail->rtp_next = NULL; + while (m_head != NULL) { + rtp_packet *p; + p = m_head; + m_head = m_head->rtp_next; + p->rtp_next = p->rtp_prev = NULL; + xfree(p); + } + } + m_tail = NULL; + m_rtp_queue_len = 0; +} + +int CPlayerMedia::create_common (int is_video, char *errmsg, uint32_t errlen) +{ + m_parent->add_media(this); + m_is_video = is_video; + + m_decode_thread_sem = SDL_CreateSemaphore(0); + m_decode_thread = SDL_CreateThread(pdp_decode_thread, this); + if (m_decode_thread_sem == NULL || m_decode_thread == NULL) { + const char *outmedia; + if (m_media_info == NULL) { + outmedia = m_is_video ? "video" : "audio"; + } else outmedia = m_media_info->media; + + if (errmsg != NULL) + snprintf(errmsg, errlen, "Couldn't start media thread for %s", + outmedia); + media_message(LOG_ERR, "Failed to create decode thread for media %s", + outmedia); + return (-1); + } + return 0; +} +/* + * CPlayerMedia::create - create when we've already got a + * bytestream + */ +int CPlayerMedia::create (COurInByteStream *b, + int is_video, + char *errmsg, + uint32_t errlen, + int streaming) +{ + m_byte_stream = b; + m_streaming = streaming; + return create_common(is_video, errmsg, errlen); +} + +/* + * CPlayerMedia::create_streaming - create a streaming media session, + * including setting up rtsp session, rtp and rtp bytestream + */ +int CPlayerMedia::create_streaming (media_desc_t *sdp_media, + char *errmsg, + uint32_t errlen, + int ondemand, + int use_rtsp, + int media_number_in_session) +{ + char buffer[80]; + rtsp_command_t cmd; + rtsp_decode_t *decode; + + m_streaming = 1; + if (sdp_media == NULL) { + snprintf(errmsg, errlen, "Internal media error - sdp is NULL"); + return(-1); + } + + if (strncasecmp(sdp_media->proto, "RTP", strlen("RTP")) != 0) { + snprintf(errmsg, errlen, "Media %s doesn't use RTP", sdp_media->media); + media_message(LOG_ERR, "%s doesn't use RTP", sdp_media->media); + return (-1); + } + if (sdp_media->fmt == NULL) { + snprintf(errmsg, errlen, "Media %s doesn't have any usuable formats", + sdp_media->media); + media_message(LOG_ERR, "%s doesn't have any formats", + sdp_media->media); + return (-1); + } + + m_media_info = sdp_media; + m_stream_ondemand = ondemand; + if (ondemand != 0) { + /* + * Get 2 consecutive IP ports. If we don't have this, don't even + * bother + */ + if (use_rtsp == 0) { + m_ports = new C2ConsecIpPort(m_parent->get_unused_ip_port_ptr()); + if (m_ports == NULL || !m_ports->valid()) { + snprintf(errmsg, errlen, "Could not find any valid IP ports"); + media_message(LOG_ERR, "Couldn't get valid IP ports"); + return (-1); + } + m_our_port = m_ports->first_port(); + + /* + * Send RTSP setup message - first create the transport string for that + * message + */ + create_rtsp_transport_from_sdp(m_parent->get_sdp_info(), + m_media_info, + m_our_port, + buffer, + sizeof(buffer)); + } else { + m_rtp_use_rtsp = 1; + m_rtp_media_number_in_session = media_number_in_session; + snprintf(buffer, sizeof(buffer), "RTP/AVP/TCP;unicast;interleaved=%d-%d", + media_number_in_session * 2, (media_number_in_session * 2) + 1); + } + memset(&cmd, 0, sizeof(rtsp_command_t)); + cmd.transport = buffer; + int err = + rtsp_send_setup(m_parent->get_rtsp_client(), + m_media_info->control_string, + &cmd, + &m_rtsp_session, + &decode, + m_parent->session_control_is_aggregate()); + if (err != 0) { + snprintf(errmsg, errlen, "Couldn't set up session %s", + m_media_info->control_string); + media_message(LOG_ERR, "Can't create session %s - error code %d", + m_media_info->media, err); + if (decode != NULL) + free_decode_response(decode); + return (-1); + } + cmd.transport = NULL; + media_message(LOG_INFO, "Transport returned is %s", decode->transport); + + /* + * process the transport they sent. They need to send port numbers, + * addresses, rtptime information, that sort of thing + */ + if (m_source_addr == NULL) { + m_source_addr = rtsp_get_server_ip_address_string(m_rtsp_session); + media_message(LOG_INFO, "setting default source address from rtsp %s", m_source_addr); + } + + if (process_rtsp_transport(decode->transport) != 0) { + snprintf(errmsg, errlen, "Couldn't process transport information in RTSP response: %s", decode->transport); + free_decode_response(decode); + return(-1); + } + free_decode_response(decode); + } else { + m_server_port = m_our_port = m_media_info->port; + } + connect_desc_t *cptr; + cptr = get_connect_desc_from_media(m_media_info); + if (cptr == NULL) { + snprintf(errmsg, errlen, "Server did not return address"); + return (-1); + } + + // + // okay - here we want to check that the server port is set up, and + // go ahead and init rtp, and the associated task + // + m_start_time = time(NULL); + + if (create_common(strcmp(sdp_media->media, "video") == 0, + errmsg, errlen) < 0) { + return -1; + } + + if (ondemand == 0 || use_rtsp == 0) { + m_rtp_inited = 0; + m_recv_thread = SDL_CreateThread(pdp_recv_thread, this); + if (m_recv_thread == NULL) { + snprintf(errmsg, errlen, "Couldn't create media %s RTP recv thread", + m_media_info->media); + media_message(LOG_ERR, errmsg); + return (-1); + } + while (m_rtp_inited == 0) { + SDL_Delay(10); + } + if (m_rtp_session == NULL) { + snprintf(errmsg, errlen, "Could not start RTP - check debug log"); + media_message(LOG_ERR, errmsg); + return (-1); + } + } else { + int ret; + ret = rtsp_thread_set_rtp_callback(m_parent->get_rtsp_client(), + pdp_rtp_packet_callback, + pdp_rtp_periodic, + m_rtp_media_number_in_session, + this); + if (ret < 0) { + snprintf(errmsg, errlen, "Can't setup TCP/RTP callback"); + return -1; + } + ret = rtsp_thread_perform_callback(m_parent->get_rtsp_client(), + pdp_init_rtp_tcp, + this); + if (ret < 0) { + snprintf(errmsg, errlen, "Can't init RTP in RTSP thread"); + return -1; + } + } + if (m_rtp_session == NULL) { + snprintf(errmsg, errlen, "Couldn't create RTP session for media %s", + m_media_info->media); + media_message(LOG_ERR, errmsg); + return (-1); + } + return (0); +} + +int CPlayerMedia::create_video_plugin (const codec_plugin_t *p, + const char *compressor, + int type, + int profile, + format_list_t *sdp_media, + video_info_t *video, + const uint8_t *user_data, + uint32_t userdata_size) +{ + if (m_video_sync == NULL) { + m_video_sync = m_parent->set_up_video_sync(); + } + if (m_video_sync == NULL) return -1; + + m_plugin = p; + m_video_info = video; + m_plugin_data = (p->vc_create)(compressor, + type, + profile, sdp_media, + video, + user_data, + userdata_size, + get_video_vft(), + m_video_sync); + if (m_plugin_data == NULL) + return -1; + + if (user_data != NULL) + set_user_data(user_data, userdata_size); + return 0; +} + +void CPlayerMedia::set_plugin_data (const codec_plugin_t *p, + codec_data_t *d, + video_vft_t *v, + audio_vft_t *a) +{ + m_plugin = p; + m_plugin_data = d; + if (is_video()) { + if (m_video_sync == NULL) { + m_video_sync = m_parent->set_up_video_sync(); + } + d->ifptr = m_video_sync; + d->v.video_vft = v; + } else { + if (m_audio_sync == NULL) { + m_audio_sync = m_parent->set_up_audio_sync(); + } + d->ifptr = m_audio_sync; + d->v.audio_vft = a; + } + +} + +int CPlayerMedia::get_plugin_status (char *buffer, uint32_t buflen) +{ + if (m_plugin == NULL) return -1; + + if (m_plugin->c_print_status == NULL) return -1; + + return ((m_plugin->c_print_status)(m_plugin_data, buffer, buflen)); +} + +int CPlayerMedia::create_audio_plugin (const codec_plugin_t *p, + const char *compressor, + int type, + int profile, + format_list_t *sdp_media, + audio_info_t *audio, + const uint8_t *user_data, + uint32_t userdata_size) +{ + if (m_audio_sync == NULL) { + m_audio_sync = m_parent->set_up_audio_sync(); + } + if (m_audio_sync == NULL) return -1; + + m_audio_info = audio; + m_plugin = p; + m_plugin_data = (p->ac_create)(compressor, + type, + profile, + sdp_media, + audio, + user_data, + userdata_size, + get_audio_vft(), + m_audio_sync); + if (m_plugin_data == NULL) return -1; + + if (user_data != NULL) + set_user_data(user_data, userdata_size); + return 0; +} + +/* + * CPlayerMedia::do_play - get play command + */ +int CPlayerMedia::do_play (double start_time_offset, + char *errmsg, + uint32_t errlen) +{ + + if (m_streaming != 0) { + if (m_stream_ondemand != 0) { + /* + * We're streaming - send the RTSP play command + */ + if (m_parent->session_control_is_aggregate() == 0) { + char buffer[80]; + rtsp_command_t cmd; + rtsp_decode_t *decode; + range_desc_t *range; + memset(&cmd, 0, sizeof(rtsp_command_t)); + + // only do range if we're not paused + range = get_range_from_media(m_media_info); + if (range != NULL) { + if (start_time_offset < range->range_start || + start_time_offset > range->range_end) + start_time_offset = range->range_start; + // need to check for smpte + sprintf(buffer, "npt=%g-%g", start_time_offset, range->range_end); + cmd.range = buffer; + } + + if (rtsp_send_play(m_rtsp_session, &cmd, &decode) != 0) { + media_message(LOG_ERR, "RTSP play command failed"); + if (errmsg != NULL) { + snprintf(errmsg, errlen, "RTSP Play Error %s-%s", + decode->retcode, + decode->retresp != NULL ? decode->retresp : ""); + } + free_decode_response(decode); + return (-1); + } + + /* + * process the return information + */ + int ret = pdp_process_rtsp_rtpinfo(decode->rtp_info, m_parent, this); + if (ret < 0) { + media_message(LOG_ERR, "rtsp rtpinfo failed"); + free_decode_response(decode); + if (errmsg != NULL) { + snprintf(errmsg, errlen, "RTSP aggregate RtpInfo response failure"); + } + return (-1); + } + free_decode_response(decode); + } + if (m_source_addr == NULL) { + // get the ip address of the server from the rtsp stack + m_source_addr = rtsp_get_server_ip_address_string(m_rtsp_session); + media_message(LOG_INFO, "Setting source address from rtsp - %s", + m_source_addr); + } + // ASDF - probably need to do some stuff here for no rtpinfo... + /* + * set the various play times, and send a message to the recv task + * that it needs to start + */ + m_play_start_time = start_time_offset; + } + if (m_byte_stream != NULL) { + m_byte_stream->play((uint64_t)(start_time_offset * 1000.0)); + } + m_paused = 0; + if (m_rtp_use_rtsp) { + rtsp_thread_perform_callback(m_parent->get_rtsp_client(), + pdp_rtp_start, + this); + } + } else { + /* + * File (or other) playback. + */ + if (m_paused == 0 || start_time_offset == 0.0) { + m_byte_stream->reset(); + } + m_byte_stream->play((uint64_t)(start_time_offset * 1000.0)); + m_play_start_time = start_time_offset; + m_paused = 0; + start_decoding(); + } + return (0); +} + +/* + * CPlayerMedia::do_pause - stop what we're doing + */ +int CPlayerMedia::do_pause (void) +{ + + if (m_streaming != 0) { + if (m_stream_ondemand != 0) { + /* + * streaming - send RTSP pause + */ + if (m_parent->session_control_is_aggregate() == 0) { + rtsp_command_t cmd; + rtsp_decode_t *decode; + memset(&cmd, 0, sizeof(rtsp_command_t)); + + if (rtsp_send_pause(m_rtsp_session, &cmd, &decode) != 0) { + media_message(LOG_ERR, "RTSP play command failed"); + free_decode_response(decode); + return (-1); + } + free_decode_response(decode); + } + } + if (m_recv_thread != NULL) { + m_rtp_msg_queue.send_message(MSG_PAUSE_SESSION); + } + } + + if (m_byte_stream != NULL) + m_byte_stream->pause(); + /* + * Pause the various threads + */ + m_decode_msg_queue.send_message(MSG_PAUSE_SESSION, + NULL, + 0, + m_decode_thread_sem); + m_paused = 1; + return (0); +} + +double CPlayerMedia::get_max_playtime (void) +{ + if (m_byte_stream) { + return (m_byte_stream->get_max_playtime()); + } + return (0.0); +} + +/*************************************************************************** + * Transport and RTP-Info RTSP header line parsing. + ***************************************************************************/ +#define ADV_SPACE(a) {while (isspace(*(a)) && (*(a) != '\0'))(a)++;} + +#define TTYPE(a,b) {a, sizeof(a), b} + +static char *transport_parse_unicast (char *transport, CPlayerMedia *m) +{ + ADV_SPACE(transport); + if (*transport == '\0') return (transport); + + if (*transport != ';') + return (NULL); + transport++; + ADV_SPACE(transport); + return (transport); +} + +static char *transport_parse_multicast (char *transport, CPlayerMedia *m) +{ + media_message(LOG_ERR,"Received multicast indication during SETUP"); + return (NULL); +} + +static char *convert_number (char *transport, uint32_t &value) +{ + value = 0; + while (isdigit(*transport)) { + value *= 10; + value += *transport - '0'; + transport++; + } + return (transport); +} + +static char *convert_hex (char *transport, uint32_t &value) +{ + value = 0; + while (isxdigit(*transport)) { + value *= 16; + if (isdigit(*transport)) + value += *transport - '0'; + else + value += tolower(*transport) - 'a' + 10; + transport++; + } + return (transport); +} + +static char *transport_parse_client_port (char *transport, CPlayerMedia *m) +{ + uint32_t port; + uint16_t our_port, our_port_max; + if (*transport++ != '=') { + return (NULL); + } + ADV_SPACE(transport); + transport = convert_number(transport, port); + ADV_SPACE(transport); + our_port = m->get_our_port(); + our_port_max = our_port + 1; + + if (port != our_port) { + media_message(LOG_ERR, "Returned client port %u doesn't match sent %u", + port, our_port); + return (NULL); + } + if (*transport == ';') { + transport++; + return (transport); + } + if (*transport == '\0') { + return (transport); + } + if (*transport != '-') { + return (NULL); + } + transport++; + ADV_SPACE(transport); + transport = convert_number(transport, port); + if ((port < our_port) || + (port > our_port_max)) { + media_message(LOG_ERR, "Illegal client to port %u, range %u to %u", + port, our_port, our_port_max); + return (NULL); + } + ADV_SPACE(transport); + if (*transport == ';') { + transport++; + } + return(transport); +} + +static char *transport_parse_server_port (char *transport, CPlayerMedia *m) +{ + uint32_t fromport, toport; + + if (*transport++ != '=') { + return (NULL); + } + ADV_SPACE(transport); + transport = convert_number(transport, fromport); + ADV_SPACE(transport); + + m->set_server_port((uint16_t)fromport); + + if (*transport == ';') { + transport++; + return (transport); + } + if (*transport == '\0') { + return (transport); + } + if (*transport != '-') { + return (NULL); + } + transport++; + ADV_SPACE(transport); + transport = convert_number(transport, toport); + if (toport < fromport || toport > fromport + 1) { + media_message(LOG_ERR, "Illegal server to port %u, from is %u", + toport, fromport); + return (NULL); + } + ADV_SPACE(transport); + if (*transport == ';') { + transport++; + } + return(transport); +} + +static char *transport_parse_source (char *transport, CPlayerMedia *m) +{ + char *ptr, *newone; + uint32_t addrlen; + + if (*transport != '=') { + return (NULL); + } + transport++; + ADV_SPACE(transport); + ptr = transport; + while (*transport != ';' && *transport != '\0') transport++; + addrlen = transport - ptr; + if (addrlen == 0) { + return (NULL); + } + newone = (char *)malloc(addrlen + 1); + if (newone == NULL) { + media_message(LOG_ERR, "Can't alloc memory for transport source"); + return (NULL); + } + strncpy(newone, ptr, addrlen); + newone[addrlen] = '\0'; + m->set_source_addr(newone); + if (*transport == ';') transport++; + return (transport); +} + +static char *transport_parse_ssrc (char *transport, CPlayerMedia *m) +{ + uint32_t ssrc; + if (*transport != '=') { + return (NULL); + } + transport++; + ADV_SPACE(transport); + transport = convert_hex(transport, ssrc); + ADV_SPACE(transport); + if (*transport != '\0') { + if (*transport != ';') { + return (NULL); + } + transport++; + } + m->set_rtp_ssrc(ssrc); + return (transport); +} + +static char *transport_parse_interleave (char *transport, CPlayerMedia *m) +{ + uint32_t chan, chan2; + if (*transport != '=') { + return (NULL); + } + transport++; + ADV_SPACE(transport); + transport = convert_number(transport, chan); + chan2 = m->get_rtp_media_number() * 2; + if (chan != chan2) { + media_message(LOG_ERR, "Transport interleave not what was requested %d %d", + chan, chan2); + return NULL; + } + ADV_SPACE(transport); + if (*transport != '\0') { + if (*transport != '-') { + return (NULL); + } + transport++; + transport = convert_number(transport, chan2); + if (chan + 1 != chan2) { + media_message(LOG_ERR, "Error in transport interleaved field"); + return (NULL); + } + + if (*transport == '\0') return (transport); + } + if (*transport != ';') return (NULL); + transport++; + return (transport); +} + +static char *rtpinfo_parse_ssrc (char *transport, CPlayerMedia *m, int &end) +{ + uint32_t ssrc; + if (*transport != '=') { + return (NULL); + } + transport++; + ADV_SPACE(transport); + transport = convert_hex(transport, ssrc); + ADV_SPACE(transport); + if (*transport != '\0') { + if (*transport == ',') { + end = 1; + } else if (*transport != ';') { + return (NULL); + } + transport++; + } + m->set_rtp_ssrc(ssrc); + return (transport); +} + +static char *rtpinfo_parse_seq (char *rtpinfo, CPlayerMedia *m, int &endofurl) +{ + uint32_t seq; + if (*rtpinfo != '=') { + return (NULL); + } + rtpinfo++; + ADV_SPACE(rtpinfo); + rtpinfo = convert_number(rtpinfo, seq); + ADV_SPACE(rtpinfo); + if (*rtpinfo != '\0') { + if (*rtpinfo == ',') { + endofurl = 1; + } else if (*rtpinfo != ';') { + return (NULL); + } + rtpinfo++; + } + m->set_rtp_base_seq(seq); + return (rtpinfo); +} + +static char *rtpinfo_parse_rtptime (char *rtpinfo, + CPlayerMedia *m, + int &endofurl) +{ + uint32_t rtptime; + int neg = 0; + if (*rtpinfo != '=') { + return (NULL); + } + rtpinfo++; + ADV_SPACE(rtpinfo); + if (*rtpinfo == '-') { + neg = 1; + rtpinfo++; + ADV_SPACE(rtpinfo); + } + rtpinfo = convert_number(rtpinfo, rtptime); + ADV_SPACE(rtpinfo); + if (*rtpinfo != '\0') { + if (*rtpinfo == ',') { + endofurl = 1; + } else if (*rtpinfo != ';') { + return (NULL); + } + rtpinfo++; + } + if (neg != 0) { + player_error_message("Warning - negative time returned in rtpinfo"); + rtptime = 0 - rtptime; + } + m->set_rtp_base_ts(rtptime); + return (rtpinfo); +} +struct { + const char *name; + uint32_t namelen; + char *(*routine)(char *transport, CPlayerMedia *); +} transport_types[] = +{ + TTYPE("unicast", transport_parse_unicast), + TTYPE("multicast", transport_parse_multicast), + TTYPE("client_port", transport_parse_client_port), + TTYPE("server_port", transport_parse_server_port), + TTYPE("source", transport_parse_source), + TTYPE("ssrc", transport_parse_ssrc), + TTYPE("interleaved", transport_parse_interleave), + {NULL, 0, NULL}, +}; + +int CPlayerMedia::process_rtsp_transport (char *transport) +{ + uint32_t protolen; + int ix; + + if (transport == NULL) + return (-1); + + protolen = strlen(m_media_info->proto); + + if (strncasecmp(transport, m_media_info->proto, protolen) != 0) { + media_message(LOG_ERR, "transport %s doesn't match %s", transport, + m_media_info->proto); + return (-1); + } + transport += protolen; + if (*transport == '/') { + transport++; + if (m_rtp_use_rtsp) { + if (strncasecmp(transport, "TCP", strlen("TCP")) != 0) { + media_message(LOG_ERR, "Transport is not TCP"); + return (-1); + } + transport += strlen("TCP"); + } else { + if (strncasecmp(transport, "UDP", strlen("UDP")) != 0) { + media_message(LOG_ERR, "Transport is not UDP"); + return (-1); + } + transport += strlen("UDP"); + } + } + if (*transport != ';') { + return (-1); + } + transport++; + do { + ADV_SPACE(transport); + for (ix = 0; transport_types[ix].name != NULL; ix++) { + if (strncasecmp(transport, + transport_types[ix].name, + transport_types[ix].namelen - 1) == 0) { + transport += transport_types[ix].namelen - 1; + ADV_SPACE(transport); + transport = (transport_types[ix].routine)(transport, this); + break; + } + } + if (transport_types[ix].name == NULL) { + media_message(LOG_INFO, "Illegal mime type in transport - skipping %s", + transport); + while (*transport != ';' && *transport != '\0') transport++; + if (*transport != '\0') transport++; + } + } while (transport != NULL && *transport != '\0'); + + if (transport == NULL) { + return (-1); + } + return (0); +} + +struct { + const char *name; + uint32_t namelen; + char *(*routine)(char *transport, CPlayerMedia *, int &end_for_url); +} rtpinfo_types[] = +{ + TTYPE("seq", rtpinfo_parse_seq), + TTYPE("rtptime", rtpinfo_parse_rtptime), + TTYPE("ssrc", rtpinfo_parse_ssrc), + {NULL, 0, NULL}, +}; + +int pdp_process_rtsp_rtpinfo (char *rtpinfo, + CPlayerSession *session, + CPlayerMedia *media) +{ + int ix; + CPlayerMedia *newmedia; + if (rtpinfo == NULL) + return (0); + + do { + int no_mimes = 0; + ADV_SPACE(rtpinfo); + if (strncasecmp(rtpinfo, "url", strlen("url")) != 0) { + media_message(LOG_ERR, "Url not found"); + return (-1); + } + rtpinfo += strlen("url"); + ADV_SPACE(rtpinfo); + if (*rtpinfo != '=') { + media_message(LOG_ERR, "Can't find = after url"); + return (-1); + } + rtpinfo++; + ADV_SPACE(rtpinfo); + char *url = rtpinfo; + while (*rtpinfo != '\0' && *rtpinfo != ';' && *rtpinfo != ',') { + rtpinfo++; + } + if (*rtpinfo == '\0') { + no_mimes = 1; + } else { + if (*rtpinfo == ',') { + no_mimes = 1; + } + *rtpinfo++ = '\0'; + } + char *temp = url; + newmedia = session->rtsp_url_to_media(url); + if (newmedia == NULL) { + media_message(LOG_ERR, "Can't find media from %s", url); + return -1; + } else if (media != NULL && media != newmedia) { + media_message(LOG_ERR, "Url in rtpinfo does not match media %s", url); + return -1; + } + if (temp != url) + free(url); + + if (no_mimes == 0) { + int endofurl = 0; + do { + ADV_SPACE(rtpinfo); + for (ix = 0; rtpinfo_types[ix].name != NULL; ix++) { + if (strncasecmp(rtpinfo, + rtpinfo_types[ix].name, + rtpinfo_types[ix].namelen - 1) == 0) { + rtpinfo += rtpinfo_types[ix].namelen - 1; + ADV_SPACE(rtpinfo); + rtpinfo = (rtpinfo_types[ix].routine)(rtpinfo, newmedia, endofurl); + break; + } + } + if (rtpinfo_types[ix].name == NULL) { +#if 1 + media_message(LOG_INFO, "Unknown mime-type in RtpInfo - skipping %s", + rtpinfo); +#endif + while (*rtpinfo != ';' && *rtpinfo != '\0') rtpinfo++; + if (*rtpinfo != '\0') rtpinfo++; + } + } while (endofurl == 0 && rtpinfo != NULL && *rtpinfo != '\0'); + } + newmedia = NULL; + } while (rtpinfo != NULL && *rtpinfo != '\0'); + + if (rtpinfo == NULL) { + return (-1); + } + + return (1); +} + +int CPlayerMedia::rtp_receive_packet (unsigned char interleaved, + struct rtp_packet *pak, + int len) +{ + int ret; + if ((interleaved & 1) == 0) { + ret = rtp_process_recv_data(m_rtp_session, 0, pak, len); + if (ret < 0) { + xfree(pak); + } + } else { + uint8_t *pakbuf = (uint8_t *)pak; + pakbuf += sizeof(rtp_packet_data); + + rtp_process_ctrl(m_rtp_session, pakbuf, len); + xfree(pak); + ret = 0; + } + return ret; +} + +void CPlayerMedia::rtp_periodic (void) +{ + rtp_send_ctrl(m_rtp_session, + m_rtp_byte_stream != NULL ? + m_rtp_byte_stream->get_last_rtp_timestamp() : 0, + NULL); + rtp_update(m_rtp_session); + if (m_rtp_byte_stream != NULL) { + int ret = m_rtp_byte_stream->recv_task(m_decode_thread_waiting); + if (ret > 0) { + if (m_rtp_buffering == 0) { + m_rtp_buffering = 1; + start_decoding(); + } else { + bytestream_primed(); + } + } + return; + } + if (m_head != NULL) { + /* + * Make sure that the payload type is the same + */ + if (m_head->rtp_pak_pt == m_tail->rtp_pak_pt) { + if (m_rtp_queue_len > 10) { // 10 packets consecutive proto same + if (determine_payload_type_from_rtp() == FALSE) { + clear_rtp_packets(); + } + } + } else { + clear_rtp_packets(); + } + } +} + +void CPlayerMedia::rtp_start (void) +{ + if (m_rtp_ssrc_set == TRUE) { + rtp_set_my_ssrc(m_rtp_session, m_rtp_ssrc); + } else { + // For now - we'll set up not to wait for RTCP validation + // before indicating if rtp library should accept. + rtp_set_option(m_rtp_session, RTP_OPT_WEAK_VALIDATION, FALSE); + rtp_set_option(m_rtp_session, RTP_OPT_PROMISC, TRUE); + } + if (m_rtp_byte_stream != NULL) { + //m_rtp_byte_stream->reset(); - gets called when pausing + m_rtp_byte_stream->flush_rtp_packets(); + } + m_rtp_buffering = 0; +} + +void CPlayerMedia::rtp_end(void) +{ + if (m_rtp_session != NULL) { + rtp_send_bye(m_rtp_session); + rtp_done(m_rtp_session); + } + m_rtp_session = NULL; +} + +int CPlayerMedia::rtcp_send_packet (uint8_t *buffer, int buflen) +{ + if (config.get_config_value(CONFIG_SEND_RTCP_IN_RTP_OVER_RTSP) != 0) { + return rtsp_thread_send_rtcp(m_parent->get_rtsp_client(), + m_rtp_media_number_in_session, + buffer, + buflen); + } + return buflen; +} + +int CPlayerMedia::recv_thread (void) +{ + struct timeval timeout; + int retcode; + CMsg *newmsg; + int recv_thread_stop = 0; + connect_desc_t *cptr; + cptr = get_connect_desc_from_media(m_media_info); + + + m_rtp_buffering = 0; + if (m_stream_ondemand != 0) { + /* + * We need to free up the ports that we got before RTP tries to set + * them up, so we don't have any re-use conflicts. There is a small + * window here that they might get used... + */ + delete m_ports; // free up the port numbers + m_ports = NULL; + } + + double bw; + + if (find_rtcp_bandwidth_from_media(m_media_info, &bw) < 0) { + bw = 5000.0; + } else { + media_message(LOG_DEBUG, "Using bw from sdp %g", bw); + } + m_rtp_session = rtp_init(m_source_addr == NULL ? + cptr->conn_addr : m_source_addr, + m_our_port, + m_server_port, + cptr == NULL ? 1 : cptr->ttl, // need ttl here + bw, // rtcp bandwidth ? + pdp_recv_callback, + (uint8_t *)this); + if (m_rtp_session != NULL) { + rtp_set_option(m_rtp_session, RTP_OPT_WEAK_VALIDATION, FALSE); + rtp_set_option(m_rtp_session, RTP_OPT_PROMISC, TRUE); + rtp_start(); + } + m_rtp_inited = 1; + + while (recv_thread_stop == 0) { + if ((newmsg = m_rtp_msg_queue.get_message()) != NULL) { + //player_debug_message("recv thread message %d", newmsg->get_value()); + switch (newmsg->get_value()) { + case MSG_STOP_THREAD: + recv_thread_stop = 1; + break; + case MSG_PAUSE_SESSION: + // Indicate that we need to restart the session. + // But keep going... + rtp_start(); + break; + } + delete newmsg; + newmsg = NULL; + } + if (recv_thread_stop == 1) { + continue; + } + if (m_rtp_session == NULL) { + SDL_Delay(50); + } else { + timeout.tv_sec = 0; + timeout.tv_usec = 500000; + retcode = rtp_recv(m_rtp_session, &timeout, 0); + // player_debug_message("rtp_recv return %d", retcode); + // Run rtp periodic after each packet received or idle time. + if (m_paused == 0 || m_stream_ondemand != 0) + rtp_periodic(); + } + + } + /* + * When we're done, send a bye, close up rtp, and go home + */ + rtp_end(); + return (0); +} + +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 != 0) { + if (e->type == RX_RTP) { + 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) { + m_rtp_byte_stream->recv_callback(session, e); + return; + } + 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, m_is_video ? "video" : "audio"); + m_rtp_queue_len++; + } + 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: +#if 0 + media_message(LOG_DEBUG, "Thread %u - Callback from rtp with %d %p", + SDL_ThreadID(),e->type, e->data); +#endif + break; + } +} + +int CPlayerMedia::determine_payload_type_from_rtp(void) +{ + uint8_t payload_type = m_head->rtp_pak_pt, temp; + format_list_t *fmt; + uint64_t tickpersec; + + fmt = m_media_info->fmt; + while (fmt != NULL) { + // rtp payloads are all numeric + temp = atoi(fmt->fmt); + if (temp == payload_type) { + m_media_fmt = fmt; + if (fmt->rtpmap != 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); + m_rtp_byte_stream->play((uint64_t)(m_play_start_time * 1000.0)); + m_byte_stream = m_rtp_byte_stream; + if (!is_video()) { + m_rtp_byte_stream->set_sync(m_parent); + } else { + m_parent->syncronize_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, m_is_video ? "video" : "audio"); + return (FALSE); +} + +/* + * set up rtptime + */ +void CPlayerMedia::set_rtp_base_ts (uint32_t time) +{ + m_rtsp_base_ts_received = 1; + m_rtp_base_ts = time; + if (m_rtp_byte_stream != NULL) { + m_rtp_byte_stream->set_rtp_base_ts(time); + } +} + +void CPlayerMedia::set_rtp_base_seq (uint16_t seq) +{ + m_rtsp_base_seq_received = 1; + m_rtp_base_seq = seq; + if (m_rtp_byte_stream != NULL) { + m_rtp_byte_stream->set_rtp_base_seq(seq); + } +} + +void CPlayerMedia::rtp_init_tcp (void) +{ + connect_desc_t *cptr; + double bw; + + if (find_rtcp_bandwidth_from_media(m_media_info, &bw) < 0) { + bw = 5000.0; + } + cptr = get_connect_desc_from_media(m_media_info); + m_rtp_session = rtp_init_extern_net(m_source_addr == NULL ? + cptr->conn_addr : m_source_addr, + m_our_port, + m_server_port, + cptr->ttl, + bw, // rtcp bandwidth ? + pdp_recv_callback, + pdp_rtcp_send_packet, + (uint8_t *)this); + rtp_set_option(m_rtp_session, RTP_OPT_WEAK_VALIDATION, FALSE); + rtp_set_option(m_rtp_session, RTP_OPT_PROMISC, TRUE); + m_rtp_inited = 1; + +} + +void CPlayerMedia::create_rtp_byte_stream (uint8_t rtp_pt, + uint64_t tps, + format_list_t *fmt) +{ + int codec; + rtp_check_return_t plugin_ret; + rtp_plugin_t *rtp_plugin; + + rtp_plugin = NULL; + plugin_ret = check_for_rtp_plugins(fmt, rtp_pt, &rtp_plugin); + + if (plugin_ret != RTP_PLUGIN_NO_MATCH) { + switch (plugin_ret) { + case RTP_PLUGIN_MATCH: + player_debug_message("Starting rtp bytestream %s from plugin", + rtp_plugin->name); + m_rtp_byte_stream = new CPluginRtpByteStream(rtp_plugin, + fmt, + rtp_pt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + return; + case RTP_PLUGIN_MATCH_USE_VIDEO_DEFAULT: + // just fall through... + break; + case RTP_PLUGIN_MATCH_USE_AUDIO_DEFAULT: + m_rtp_byte_stream = + new CAudioRtpByteStream(rtp_pt, + fmt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + if (m_rtp_byte_stream != NULL) { + player_debug_message("Starting generic audio byte stream"); + return; + } + + default: + break; + } + } else { + if (is_video() && (rtp_pt == 32)) { + codec = VIDEO_MPEG12; + m_rtp_byte_stream = new CMpeg3RtpByteStream(rtp_pt, + fmt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + if (m_rtp_byte_stream != NULL) { + return; + } + } else { + if (rtp_pt == 14) { + codec = MPEG4IP_AUDIO_MP3; + } else if (rtp_pt <= 23) { + codec = MPEG4IP_AUDIO_GENERIC; + } else { + if (fmt->rtpmap == NULL) return; + + codec = lookup_audio_codec_by_name(fmt->rtpmap->encode_name); + if (codec < 0) { + codec = MPEG4IP_AUDIO_NONE; // fall through everything to generic + } + } + switch (codec) { + case MPEG4IP_AUDIO_MP3: { + m_rtp_byte_stream = + new CAudioRtpByteStream(rtp_pt, fmt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + if (m_rtp_byte_stream != NULL) { + m_rtp_byte_stream->set_skip_on_advance(4); + player_debug_message("Starting mp3 2250 audio byte stream"); + return; + } + } + break; + case MPEG4IP_AUDIO_MP3_ROBUST: + m_rtp_byte_stream = + new CRfc3119RtpByteStream(rtp_pt, fmt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + if (m_rtp_byte_stream != NULL) { + player_debug_message("Starting mpa robust byte stream"); + return; + } + break; + case MPEG4IP_AUDIO_GENERIC: + m_rtp_byte_stream = + new CAudioRtpByteStream(rtp_pt, fmt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + if (m_rtp_byte_stream != NULL) { + player_debug_message("Starting generic audio byte stream"); + return; + } + default: + break; + } + } + m_rtp_byte_stream = new CRtpByteStream(fmt->media->media, + fmt, + rtp_pt, + m_stream_ondemand, + tps, + &m_head, + &m_tail, + m_rtsp_base_seq_received, + m_rtp_base_seq, + m_rtsp_base_ts_received, + m_rtp_base_ts, + m_rtcp_received, + m_rtcp_ntp_frac, + m_rtcp_ntp_sec, + m_rtcp_rtp_ts); + } +} + +void CPlayerMedia::syncronize_rtp_bytestreams (rtcp_sync_t *sync) +{ + if (!is_video()) { + player_error_message("Attempt to syncronize audio byte stream"); + return; + } + if (m_rtp_byte_stream != NULL) + m_rtp_byte_stream->syncronize(sync); +} + +void CPlayerMedia::start_decoding (void) +{ + m_decode_msg_queue.send_message(MSG_START_DECODING, + NULL, + 0, + m_decode_thread_sem); +} + +void CPlayerMedia::bytestream_primed (void) +{ + if (m_decode_thread_waiting != 0) { + m_decode_thread_waiting = 0; + SDL_SemPost(m_decode_thread_sem); + } +} + +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; +} + +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_video_sync != NULL) { + m_video_sync->flush_decode_buffers(); + } + if (m_audio_sync != NULL) { + m_audio_sync->flush_decode_buffers(); + } + break; + case MSG_START_DECODING: + if (m_video_sync != NULL) { + m_video_sync->flush_decode_buffers(); + } + if (m_audio_sync != NULL) { + m_audio_sync->flush_decode_buffers(); + } + decoding = 1; + break; + } + delete newmsg; + } +} + +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; + uint64_t ourtime; + // 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 bytes_decoded; + uint32_t frames_decoded_last_sec; + uint64_t bytes_decoded_last_sec; + uint64_t current_second; + uint32_t total_secs; + uint32_t last_div = 0; + + total_secs = 0; + frames_decoded = 0; + bytes_decoded = 0; + frames_decoded_last_sec = 0; + bytes_decoded_last_sec = 0; + current_second = 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", + is_video() ? "video" : "audio"); +#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 + if (is_video()) { + if (m_video_sync == NULL) { + m_video_sync = m_parent->set_up_video_sync(); + } + m_video_sync->set_wait_sem(m_decode_thread_sem); + } else { + if (m_audio_sync == NULL) { + m_audio_sync = m_parent->set_up_audio_sync(); + } + m_audio_sync->set_wait_sem(m_decode_thread_sem); + } + if (m_plugin == NULL) { + if (is_video()) { + m_plugin = check_for_video_codec(NULL, + m_media_fmt, + -1, + -1, + m_user_data, + m_user_data_size); + if (m_plugin != NULL) { + m_plugin_data = (m_plugin->vc_create)(NULL, // must figure from sdp + -1, + -1, + m_media_fmt, + m_video_info, + m_user_data, + m_user_data_size, + get_video_vft(), + m_video_sync); + if (m_plugin_data == NULL) { + m_plugin = NULL; + } else { + media_message(LOG_DEBUG, "Starting %s codec from decode thread", + m_plugin->c_name); + } + } + } else { + m_plugin = check_for_audio_codec(NULL, + m_media_fmt, + -1, + -1, + m_user_data, + m_user_data_size); + if (m_plugin != NULL) { + m_plugin_data = (m_plugin->ac_create)(NULL, + -1, + -1, + m_media_fmt, + m_audio_info, + m_user_data, + m_user_data_size, + get_audio_vft(), + m_audio_sync); + 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", + is_video() ? "video" : "audio"); +#endif + frames_decoded_last_sec = 0; + bytes_decoded_last_sec = 0; + current_second = 0; + 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); + continue; + } + if (m_byte_stream->eof()) { + media_message(LOG_INFO, "%s hit eof", m_is_video ? "video" : "audio"); + if (m_audio_sync) m_audio_sync->set_eof(); + if (m_video_sync) m_video_sync->set_eof(); + decoding = 0; + continue; + } + if (m_byte_stream->have_no_data()) { + // Indicate that we're waiting, and wait for a message from RTP + // task. + wait_on_bytestream(); + continue; + } + + frame_buffer = NULL; + ourtime = m_byte_stream->start_next_frame(&frame_buffer, + &frame_len, + &ud); + /* + * If we're decoding video, see if we're playing - if so, check + * if we've fallen significantly behind the audio + */ + if (is_video() && + (m_parent->get_session_state() == SESSION_PLAYING)) { + uint64_t current_time = m_parent->get_playing_time(); + if (current_time >= ourtime) { +#if 1 + media_message(LOG_INFO, "Candidate for skip decode "U64" our "U64, + current_time, ourtime); +#endif + // 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); + 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); + if (m_byte_stream->eof() || ret == 0) continue; +#if 1 + media_message(LOG_INFO, "Skipped ahead "U64 " to "U64, + current_time - 200, ourtime); +#endif + /* + * 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 - current_time; + if (diff > (2 * C_64)) { + 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); +#endif + } + } + } +#ifdef DEBUG_DECODE + media_message(LOG_DEBUG, "Decoding %c frame " U64, + m_is_video ? 'v' : 'a', ourtime); +#endif + if (frame_buffer != NULL && frame_len != 0) { + int sync_frame; + ret = m_plugin->c_decode_frame(m_plugin_data, + ourtime, + m_streaming != 0, + &sync_frame, + frame_buffer, + frame_len, + ud); +#ifdef DEBUG_DECODE + media_message(LOG_DEBUG, "Decoding %c frame return %d", + m_is_video ? 'v' : 'a', ret); +#endif + if (ret > 0) { + frames_decoded++; + m_byte_stream->used_bytes_for_frame(ret); + bytes_decoded += ret; + last_div = ourtime % 1000; + if ((ourtime / 1000) > current_second) { + if (frames_decoded_last_sec != 0) { +#if 0 + media_message(LOG_DEBUG, "%s - Second "U64", frames %d bytes "U64, + m_is_video ? "video" : "audio", + current_second, + frames_decoded_last_sec, + bytes_decoded_last_sec); +#endif + } + current_second = ourtime / 1000; + total_secs++; + frames_decoded_last_sec = 1; + bytes_decoded_last_sec = ret; + } else { + frames_decoded_last_sec++; + bytes_decoded_last_sec += ret; + } + } else { + m_byte_stream->used_bytes_for_frame(frame_len); + } + + } + } + // calculate frame rate for session + } + if (m_is_video) + media_message(LOG_NOTICE, "Video decoder skipped %u frames", + decode_skipped_frames); + if (total_secs != 0) { + double fps, bps; + double secs; + secs = last_div; + secs /= 1000.0; + secs += total_secs; + + 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 "U64, + m_is_video ? "video" : "audio", + bytes_decoded, secs, + fps, bytes_decoded * 8 / total_secs); + } + if (m_plugin) { + m_plugin->c_close(m_plugin_data); + m_plugin_data = NULL; + } + return (0); +} -- cgit v1.2.1