static void set_16( filter_t *p_filter, void *p_buf, uint16_t i_val ) { if( p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB ) SetWBE( p_buf, i_val ); else SetWLE( p_buf, i_val ); }
/***************************************************************************** * DoWork: convert a buffer *****************************************************************************/ static block_t *DoWork( filter_t * p_filter, block_t *p_in_buf ) { /* AC3 is natively big endian. Most SPDIF devices have the native * endianness of the computer system. * On Mac OS X however, little endian devices are also common. */ static const uint8_t p_sync_le[6] = { 0x72, 0xF8, 0x1F, 0x4E, 0x01, 0x00 }; static const uint8_t p_sync_be[6] = { 0xF8, 0x72, 0x4E, 0x1F, 0x00, 0x01 }; uint16_t i_frame_size = p_in_buf->i_buffer / 2; uint8_t * p_in = p_in_buf->p_buffer; block_t *p_out_buf = filter_NewAudioBuffer( p_filter, AOUT_SPDIF_SIZE ); if( !p_out_buf ) goto out; uint8_t * p_out = p_out_buf->p_buffer; /* Copy the S/PDIF headers. */ if( p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB ) { vlc_memcpy( p_out, p_sync_be, 6 ); p_out[4] = p_in[5] & 0x7; /* bsmod */ SetWBE( p_out + 6, i_frame_size << 4 ); vlc_memcpy( &p_out[8], p_in, i_frame_size * 2 ); } else { vlc_memcpy( p_out, p_sync_le, 6 ); p_out[5] = p_in[5] & 0x7; /* bsmod */ SetWLE( p_out + 6, i_frame_size << 4 ); swab( p_in, &p_out[8], i_frame_size * 2 ); } vlc_memset( p_out + 8 + i_frame_size * 2, 0, AOUT_SPDIF_SIZE - i_frame_size * 2 - 8 ); p_out_buf->i_dts = p_in_buf->i_dts; p_out_buf->i_pts = p_in_buf->i_pts; p_out_buf->i_nb_samples = p_in_buf->i_nb_samples; p_out_buf->i_buffer = AOUT_SPDIF_SIZE; out: block_Release( p_in_buf ); return p_out_buf; }
void xiph_decode (demux_t *demux, void *data, block_t *block) { rtp_xiph_t *self = (rtp_xiph_t *)data; // sunqueen modify if (!data || block->i_buffer < 4) goto drop; /* 32-bits RTP header (§2.2) */ uint32_t ident = GetDWBE (block->p_buffer); block->i_buffer -= 4; block->p_buffer += 4; unsigned fragtype = (ident >> 6) & 3; unsigned datatype = (ident >> 4) & 3; unsigned pkts = (ident) & 15; ident >>= 8; /* RTP defragmentation */ if (self->block && (block->i_flags & BLOCK_FLAG_DISCONTINUITY)) { /* Screwed! discontinuity within a fragmented packet */ msg_Warn (demux, self->vorbis ? "discontinuity in fragmented Vorbis packet" : "discontinuity in fragmented Theora packet"); block_Release (self->block); self->block = NULL; } if (fragtype <= 1) { if (self->block) /* Invalid first fragment */ { block_Release (self->block); self->block = NULL; } } else { if (!self->block) goto drop; /* Invalid non-first fragment */ } if (fragtype > 0) { /* Fragment */ if (pkts > 0 || block->i_buffer < 2) goto drop; size_t fraglen = GetWBE (block->p_buffer); if (block->i_buffer < (fraglen + 2)) goto drop; /* Invalid payload length */ block->i_buffer = fraglen; if (fragtype == 1)/* Keep first fragment */ { block->i_buffer += 2; self->block = block; } else { /* Append non-first fragment */ size_t len = self->block->i_buffer; self->block = block_Realloc (self->block, 0, len + fraglen); if (!self->block) { block_Release (block); return; } memcpy (self->block->p_buffer + len, block->p_buffer + 2, fraglen); block_Release (block); } if (fragtype < 3) return; /* Non-last fragment */ /* Last fragment reached, process it */ block = self->block; self->block = NULL; SetWBE (block->p_buffer, block->i_buffer - 2); pkts = 1; } /* RTP payload packets processing */ while (pkts > 0) { if (block->i_buffer < 2) goto drop; size_t len = GetWBE (block->p_buffer); block->i_buffer -= 2; block->p_buffer += 2; if (block->i_buffer < len) goto drop; switch (datatype) { case 0: /* Raw payload */ { if (self->ident != ident) { msg_Warn (demux, self->vorbis ? "ignoring raw Vorbis payload without configuration" : "ignoring raw Theora payload without configuration"); break; } block_t *raw = block_Alloc (len); memcpy (raw->p_buffer, block->p_buffer, len); raw->i_pts = block->i_pts; /* FIXME: what about pkts > 1 */ codec_decode (demux, self->id, raw); break; } case 1: /* Packed configuration frame (§3.1.1) */ { if (self->ident == ident) break; /* Ignore config retransmission */ void *extv; ssize_t extc = xiph_header (&extv, block->p_buffer, len); if (extc < 0) break; es_format_t fmt; es_format_Init (&fmt, self->vorbis ? AUDIO_ES : VIDEO_ES, self->vorbis ? VLC_CODEC_VORBIS : VLC_CODEC_THEORA); fmt.p_extra = extv; fmt.i_extra = extc; codec_destroy (demux, self->id); msg_Dbg (demux, self->vorbis ? "Vorbis packed configuration received (%06"PRIx32")" : "Theora packed configuration received (%06"PRIx32")", ident); self->ident = ident; self->id = (es_out_id_t *)codec_init (demux, &fmt); // sunqueen modify break; } } block->i_buffer -= len; block->p_buffer += len; pkts--; } drop: block_Release (block); }
/** * Registers a new session with the announce handler, using a pregenerated SDP * * \param obj a VLC object * \param sdp the SDP to register * \param dst session address (needed for SAP address auto detection) * \return the new session descriptor structure */ session_descriptor_t * sout_AnnounceRegisterSDP (vlc_object_t *obj, const char *sdp, const char *dst) { int i; char psz_addr[NI_MAXNUMERICHOST]; union { struct sockaddr a; struct sockaddr_in in; struct sockaddr_in6 in6; } addr; socklen_t addrlen = 0; struct addrinfo *res; msg_Dbg (obj, "adding SAP session"); if (vlc_getaddrinfo (dst, 0, NULL, &res) == 0) { if (res->ai_addrlen <= sizeof (addr)) memcpy (&addr, res->ai_addr, res->ai_addrlen); addrlen = res->ai_addrlen; freeaddrinfo (res); } if (addrlen == 0 || addrlen > sizeof (addr)) { msg_Err (obj, "No/invalid address specified for SAP announce" ); return NULL; } /* Determine SAP multicast address automatically */ switch (addr.a.sa_family) { #if defined (HAVE_INET_PTON) || defined (_WIN32) case AF_INET6: { /* See RFC3513 for list of valid IPv6 scopes */ struct in6_addr *a6 = &addr.in6.sin6_addr; memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 ); if( IN6_IS_ADDR_MULTICAST( a6 ) ) /* force flags to zero, preserve scope */ a6->s6_addr[1] &= 0xf; else /* Unicast IPv6 - assume global scope */ memcpy( a6->s6_addr, "\xff\x0e", 2 ); break; } #endif case AF_INET: { /* See RFC2365 for IPv4 scopes */ uint32_t ipv4 = addr.in.sin_addr.s_addr; /* 224.0.0.0/24 => 224.0.0.255 */ if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000)) ipv4 = htonl (0xe00000ff); else /* 239.255.0.0/16 => 239.255.255.255 */ if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000)) ipv4 = htonl (0xefffffff); else /* 239.192.0.0/14 => 239.195.255.255 */ if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000)) ipv4 = htonl (0xefc3ffff); else if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000)) ipv4 = 0; else /* other addresses => 224.2.127.254 */ ipv4 = htonl (0xe0027ffe); if( ipv4 == 0 ) { msg_Err (obj, "Out-of-scope multicast address " "not supported by SAP"); return NULL; } addr.in.sin_addr.s_addr = ipv4; break; } default: msg_Err (obj, "Address family %d not supported by SAP", addr.a.sa_family); return NULL; } i = vlc_getnameinfo( &addr.a, addrlen, psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST ); if( i ) { msg_Err (obj, "%s", gai_strerror (i)); return NULL; } /* Find/create SAP address thread */ sap_address_t *sap_addr; msg_Dbg (obj, "using SAP address: %s", psz_addr); vlc_mutex_lock (&sap_mutex); for (sap_addr = sap_addrs; sap_addr; sap_addr = sap_addr->next) if (!strcmp (psz_addr, sap_addr->group)) break; if (sap_addr == NULL) { sap_addr = AddressCreate (obj, psz_addr); if (sap_addr == NULL) { vlc_mutex_unlock (&sap_mutex); return NULL; } sap_addr->next = sap_addrs; sap_addrs = sap_addr; } /* Switch locks. * NEVER take the global SAP lock when holding a SAP thread lock! */ vlc_mutex_lock (&sap_addr->lock); vlc_mutex_unlock (&sap_mutex); size_t length = 20; switch (sap_addr->orig.ss_family) { #ifdef AF_INET6 case AF_INET6: length += 16; break; #endif case AF_INET: length += 4; break; default: assert (0); } /* XXX: Check for dupes */ length += strlen (sdp); session_descriptor_t *session = malloc (sizeof (*session) + length); if (unlikely(session == NULL)) { vlc_mutex_unlock (&sap_addr->lock); return NULL; /* NOTE: we should destroy the thread if left unused */ } session->next = sap_addr->first; sap_addr->first = session; session->length = length; /* Build the SAP Headers */ uint8_t *buf = session->data; /* SAPv1, not encrypted, not compressed */ buf[0] = 0x20; buf[1] = 0x00; /* No authentication length */ SetWBE(buf + 2, mdate()); /* ID hash */ size_t offset = 4; switch (sap_addr->orig.ss_family) { #ifdef AF_INET6 case AF_INET6: { const struct in6_addr *a6 = &((const struct sockaddr_in6 *)&sap_addr->orig)->sin6_addr; memcpy (buf + offset, a6, 16); buf[0] |= 0x10; /* IPv6 flag */ offset += 16; break; } #endif case AF_INET: { const struct in_addr *a4 = &((const struct sockaddr_in *)&sap_addr->orig)->sin_addr; memcpy (buf + offset, a4, 4); offset += 4; break; } } memcpy (buf + offset, "application/sdp", 16); offset += 16; /* Build the final message */ strcpy((char *)buf + offset, sdp); sap_addr->session_count++; vlc_cond_signal (&sap_addr->wait); vlc_mutex_unlock (&sap_addr->lock); return session; }
/***************************************************************************** * DoWork: convert a buffer *****************************************************************************/ static block_t *DoWork( filter_t * p_filter, block_t * p_in_buf ) { uint32_t i_ac5_spdif_type = 0; uint16_t i_fz = p_in_buf->i_nb_samples * 4; uint16_t i_frame, i_length = p_in_buf->i_buffer; static const uint8_t p_sync_le[6] = { 0x72, 0xF8, 0x1F, 0x4E, 0x00, 0x00 }; static const uint8_t p_sync_be[6] = { 0xF8, 0x72, 0x4E, 0x1F, 0x00, 0x00 }; if( p_in_buf->i_buffer != p_filter->p_sys->i_frame_size ) { /* Frame size changed, reset everything */ msg_Warn( p_filter, "Frame size changed from %zu to %zu, " "resetting everything.", p_filter->p_sys->i_frame_size, p_in_buf->i_buffer ); p_filter->p_sys->i_frame_size = p_in_buf->i_buffer; p_filter->p_sys->p_buf = xrealloc( p_filter->p_sys->p_buf, p_in_buf->i_buffer * 3 ); p_filter->p_sys->i_frames = 0; } /* Backup frame */ /* TODO: keeping the blocks in a list would save one memcpy */ memcpy( p_filter->p_sys->p_buf + p_in_buf->i_buffer * p_filter->p_sys->i_frames, p_in_buf->p_buffer, p_in_buf->i_buffer ); p_filter->p_sys->i_frames++; if( p_filter->p_sys->i_frames < 3 ) { if( p_filter->p_sys->i_frames == 1 ) /* We'll need the starting date */ p_filter->p_sys->start_date = p_in_buf->i_pts; /* Not enough data */ block_Release( p_in_buf ); return NULL; } p_filter->p_sys->i_frames = 0; block_t *p_out_buf = filter_NewAudioBuffer( p_filter, 12 * p_in_buf->i_nb_samples ); if( !p_out_buf ) goto out; for( i_frame = 0; i_frame < 3; i_frame++ ) { uint16_t i_length_padded = i_length; uint8_t * p_out = p_out_buf->p_buffer + (i_frame * i_fz); uint8_t * p_in = p_filter->p_sys->p_buf + (i_frame * i_length); switch( p_in_buf->i_nb_samples ) { case 512: i_ac5_spdif_type = 0x0B; break; case 1024: i_ac5_spdif_type = 0x0C; break; case 2048: i_ac5_spdif_type = 0x0D; break; } /* Copy the S/PDIF headers. */ if( p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB ) { memcpy( p_out, p_sync_be, 6 ); p_out[5] = i_ac5_spdif_type; SetWBE( p_out + 6, i_length << 3 ); } else { memcpy( p_out, p_sync_le, 6 ); p_out[4] = i_ac5_spdif_type; SetWLE( p_out + 6, i_length << 3 ); } if( ( (p_in[0] == 0x1F || p_in[0] == 0x7F) && p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFL ) || ( (p_in[0] == 0xFF || p_in[0] == 0xFE) && p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB ) ) { /* We are dealing with a big endian bitstream and a little endian output * or a little endian bitstream and a big endian output. * Byteswap the stream */ swab( p_in, p_out + 8, i_length ); /* If i_length is odd, we have to adjust swapping a bit.. */ if( i_length & 1 ) { p_out[8+i_length-1] = 0; p_out[8+i_length] = p_in[i_length-1]; i_length_padded++; } } else { memcpy( p_out + 8, p_in, i_length ); } if( i_fz > i_length + 8 ) { memset( p_out + 8 + i_length_padded, 0, i_fz - i_length_padded - 8 ); } } p_out_buf->i_pts = p_filter->p_sys->start_date; p_out_buf->i_nb_samples = p_in_buf->i_nb_samples * 3; p_out_buf->i_buffer = p_out_buf->i_nb_samples * 4; out: block_Release( p_in_buf ); return p_out_buf; }