static pj_status_t opus_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { struct opus_private *opus; unsigned char toc; const unsigned char *raw_frames[48]; short size[48]; int payload_offset, samples_per_frame; unsigned i; PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); opus = (struct opus_private *)codec->codec_data; *frame_cnt = opus_packet_parse(pkt, pkt_size, &toc, raw_frames, size, &payload_offset); samples_per_frame = opus_packet_get_samples_per_frame(pkt, opus->externalFs); #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Pkt info : bw -> %d , spf -> %d, offset %d, packet_size %d", opus_packet_get_bandwidth(pkt), samples_per_frame, payload_offset, pkt_size)); #endif for (i = 0; i < *frame_cnt; i++) { frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[i].bit_info = (((unsigned)ts->u64 & 0xFFFF) << 16) | (((unsigned)pkt & 0xFF) << 8) | i; frames[i].buf = pkt; frames[i].size = pkt_size; frames[i].timestamp.u64 = ts->u64 * opus->externalFs / OPUS_CLOCK_RATE + i * samples_per_frame; #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "parsed %d of %d", frames[i].size, *frame_cnt)); #endif } return PJ_SUCCESS; }
/** * Output debug information regarding the state of a single Opus packet */ void DebugFrameInfoInternal(const uint8* PacketData, uint32 PacketLength, uint32 SampleRate, bool bEncode) { int32 NumFrames = opus_packet_get_nb_frames(PacketData, PacketLength); if (NumFrames == OPUS_BAD_ARG || NumFrames == OPUS_INVALID_PACKET) { UE_LOG(LogVoice, Warning, TEXT("opus_packet_get_nb_frames: Invalid voice packet data!")); } int32 NumSamples = opus_packet_get_nb_samples(PacketData, PacketLength, SampleRate); if (NumSamples == OPUS_BAD_ARG || NumSamples == OPUS_INVALID_PACKET) { UE_LOG(LogVoice, Warning, TEXT("opus_packet_get_nb_samples: Invalid voice packet data!")); } int32 NumSamplesPerFrame = opus_packet_get_samples_per_frame(PacketData, SampleRate); int32 Bandwidth = opus_packet_get_bandwidth(PacketData); const TCHAR* BandwidthStr = NULL; switch (Bandwidth) { case OPUS_BANDWIDTH_NARROWBAND: // Narrowband (4kHz bandpass) BandwidthStr = TEXT("NB"); break; case OPUS_BANDWIDTH_MEDIUMBAND: // Mediumband (6kHz bandpass) BandwidthStr = TEXT("MB"); break; case OPUS_BANDWIDTH_WIDEBAND: // Wideband (8kHz bandpass) BandwidthStr = TEXT("WB"); break; case OPUS_BANDWIDTH_SUPERWIDEBAND: // Superwideband (12kHz bandpass) BandwidthStr = TEXT("SWB"); break; case OPUS_BANDWIDTH_FULLBAND: // Fullband (20kHz bandpass) BandwidthStr = TEXT("FB"); break; case OPUS_INVALID_PACKET: default: BandwidthStr = TEXT("Invalid"); break; } /* * 0 * 0 1 2 3 4 5 6 7 * +-+-+-+-+-+-+-+-+ * | config |s| c | * +-+-+-+-+-+-+-+-+ */ uint8 TOC; // (max 48 x 2.5ms frames in a packet = 120ms) const uint8* frames[48]; int16 size[48]; int32 payload_offset = 0; int32 NumFramesParsed = opus_packet_parse(PacketData, PacketLength, &TOC, frames, size, &payload_offset); // Frame Encoding see http://tools.ietf.org/html/rfc6716#section-3.1 int32 TOCEncoding = (TOC & 0xf8) >> 3; // Number of channels bool TOCStereo = (TOC & 0x4) != 0 ? true : false; // Number of frames and their configuration // 0: 1 frame in the packet // 1: 2 frames in the packet, each with equal compressed size // 2: 2 frames in the packet, with different compressed sizes // 3: an arbitrary number of frames in the packet int32 TOCMode = TOC & 0x3; if (bEncode) { UE_LOG(LogVoiceEncode, Verbose, TEXT("PacketLength: %d NumFrames: %d NumSamples: %d Bandwidth: %s Encoding: %d Stereo: %d FrameDesc: %d"), PacketLength, NumFrames, NumSamples, BandwidthStr, TOCEncoding, TOCStereo, TOCMode); } else { UE_LOG(LogVoiceDecode, Verbose, TEXT("PacketLength: %d NumFrames: %d NumSamples: %d Bandwidth: %s Encoding: %d Stereo: %d FrameDesc: %d"), PacketLength, NumFrames, NumSamples, BandwidthStr, TOCEncoding, TOCStereo, TOCMode); } }
static GstFlowReturn gst_opus_parse_handle_frame (GstBaseParse * base, GstBaseParseFrame * frame, gint * skip) { GstOpusParse *parse; guint8 *data; gsize size; guint32 packet_size; int ret = FALSE; const unsigned char *frames[48]; unsigned char toc; short frame_sizes[48]; int payload_offset; int packet_offset = 0; gboolean is_header, is_idheader, is_commentheader; GstMapInfo map; parse = GST_OPUS_PARSE (base); *skip = -1; gst_buffer_map (frame->buffer, &map, GST_MAP_READ); data = map.data; size = map.size; GST_DEBUG_OBJECT (parse, "Checking for frame, %" G_GSIZE_FORMAT " bytes in buffer", size); /* check for headers */ is_idheader = gst_opus_header_is_id_header (frame->buffer); is_commentheader = gst_opus_header_is_comment_header (frame->buffer); is_header = is_idheader || is_commentheader; if (!is_header) { int nframes; /* Next, check if there's an Opus packet there */ nframes = opus_packet_parse (data, size, &toc, frames, frame_sizes, &payload_offset); if (nframes < 0) { /* Then, check for the test vector framing */ GST_DEBUG_OBJECT (parse, "No Opus packet found, trying test vector framing"); if (size < 4) { GST_DEBUG_OBJECT (parse, "Too small"); goto beach; } packet_size = GST_READ_UINT32_BE (data); GST_DEBUG_OBJECT (parse, "Packet size: %u bytes", packet_size); if (packet_size > MAX_PAYLOAD_BYTES) { GST_DEBUG_OBJECT (parse, "Too large"); goto beach; } if (packet_size > size - 4) { GST_DEBUG_OBJECT (parse, "Truncated"); goto beach; } nframes = opus_packet_parse (data + 8, packet_size, &toc, frames, frame_sizes, &payload_offset); if (nframes < 0) { GST_DEBUG_OBJECT (parse, "No test vector framing either"); goto beach; } packet_offset = 8; /* for ad hoc framing, heed the framing, so we eat any padding */ payload_offset = packet_size; } else { /* Add up all the frame sizes found */ int f; for (f = 0; f < nframes; ++f) payload_offset += frame_sizes[f]; } } if (is_header) { *skip = 0; } else { *skip = packet_offset; size = payload_offset; } GST_DEBUG_OBJECT (parse, "Got Opus packet at offset %d, %" G_GSIZE_FORMAT " bytes", *skip, size); ret = TRUE; beach: gst_buffer_unmap (frame->buffer, &map); /* convert old style result to new one */ if (!ret) { if (*skip < 0) *skip = 1; return GST_FLOW_OK; } /* always skip first if needed */ if (*skip > 0) return GST_FLOW_OK; /* normalize again */ if (*skip < 0) *skip = 0; /* not enough */ if (size > map.size) return GST_FLOW_OK; /* FIXME some day ... should not mess with buffer itself */ if (!parse->got_headers) { gst_buffer_replace (&frame->buffer, gst_buffer_copy_region (frame->buffer, GST_BUFFER_COPY_ALL, 0, size)); gst_buffer_unref (frame->buffer); } ret = gst_opus_parse_parse_frame (base, frame); if (ret == GST_BASE_PARSE_FLOW_DROPPED) { frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; ret = GST_FLOW_OK; } if (ret == GST_FLOW_OK) ret = gst_base_parse_finish_frame (base, frame, size); return ret; }