Ejemplo n.º 1
0
    def datagramReceived(self, encoded_frame, addr):
        #print("Received data from", addr)

        # TODO: Once we've started receiving data from a given
        # address, should we close down this port to other addresses?

        # Create a buffer to hold the PCM data
        pcm_buf = PCMBufType()

        # Get pointer to first element of the PCM buffer
        pcm_buf_ptr = ctypes.cast(pcm_buf, ctypes.POINTER(opus.opus_int16))

        # Get pointer to encoded frame
        encoded_frame_ptr = ctypes.cast(encoded_frame, ctypes.POINTER(ctypes.c_ubyte))
        encoded_frame_bytes = len(encoded_frame)
        
        # Decode the frame
        num_samples = opus.opus_decode(
            decoder_ptr,
            encoded_frame_ptr,
            encoded_frame_bytes,
            pcm_buf_ptr,
            samples_per_channel_in_buf,
            0 # FIXME: What's this about?
        )
        
        # Check for any errors during decoding
        if num_samples < 0:
            raise Exception("Decoder error detected: "+
                            opus.opus_strerror(numSamples).decode("utf"))

        # Create a numpy array to hold the decoded PCM data.  Note
        # that the numpy array has the correct shape (whereas the
        # original PCM buffer had sufficient space allocated for the
        # largest possible frame.
        np_pcm_buf = numpy.ctypeslib.as_array(
            pcm_buf,
            (num_samples//channels, channels)
        )
        np_pcm_buf = np_pcm_buf[0:num_samples]
        
        # Put the samples on the queue to play
        q.put_nowait(np_pcm_buf)
Ejemplo n.º 2
0
    def datagramReceived(self, data, addr):
        print("Received data from", addr)

        print("len(data):", len(data))
        #print(data)

        # Create a buffer to hold the PCM data
        buf = TypeBuf()

        # Get pointer to first element of buf
        bufPtr = ctypes.cast(buf, ctypes.POINTER(opus.opus_int16))

        # Get pointer to encoded frame
        encodedFramePtr = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte))
        numBytes = len(data)

        # Decode the frame
        numSamples = opus.opus_decode(
            decoderPtr,
            encodedFramePtr,
            numBytes,
            bufPtr,
            samples_per_channel_in_buf,
            0  # FIXME: What's this about?
        )
        print("numSamples: ", numSamples)

        # Check for any errors during decoding
        if numSamples < 0:
            raise Exception("Decoder error detected: " +
                            opus.opus_strerror(numSamples).decode("utf"))

        # Put the samples on the queue to play
        np_buf = numpy.ctypeslib.as_array(buf,
                                          (numSamples // channels, channels))

        np_buf = np_buf[0:numSamples]

        print("data placed on queue")
        #print("channels:", channels)
        print("shape:", np_buf.shape)
        q.put_nowait(np_buf)
Ejemplo n.º 3
0
def encodeThenDecode(npBufSource, npBufTarget, freq):
    # Encoding
    # ========

    # Extract the number of channels in the source
    sourceChannels = npBufSource.shape[1]

    # Create an encoder
    encoder = createEncoder(npBufSource, freq)

    # Frame sizes are measured in number of samples.  There are only a
    # specified number of possible valid frame durations for Opus,
    # which (assuming a frequency of 48kHz) gives the following valid
    # sizes.
    frameSizes = [120, 240, 480, 960, 1920, 2880]

    # Specify the desired frame size.  This will be used for the vast
    # majority of the encoding, except possibly at the end of the
    # buffer (as there may not be sufficient data left to fill a
    # frame.)
    frameSizeIndex = 5
    frameSize = frameSizes[frameSizeIndex]

    # Function to calculate the size of a frame in bytes
    def frameSizeBytes(frameSize):
        global bytesPerSample
        return frameSize * sourceChannels * bytesPerSample

    # Allocate storage space for the encoded frame.  4,000 bytes is
    # the recommended maximum buffer size for the encoded frame.
    maxEncodedFrameBytes = opus.opus_int32(4000)
    encodedFrameType = ctypes.c_ubyte * maxEncodedFrameBytes.value
    encodedFrame = encodedFrameType()

    # Create a pointer to the first byte of the buffer for the encoded
    # frame.
    encodedFramePtr = ctypes.cast(ctypes.pointer(encodedFrame),
                                  ctypes.POINTER(ctypes.c_ubyte))

    # Number of bytes to process in buffer
    bytesPerSample = 2
    lengthBytes = buf.shape[0] * buf.shape[1] * bytesPerSample

    # Saving
    # ======

    # Create a new stream state with a random serial number
    stream_state = createStreamState()

    # Create a packet (reused for each pass)
    ogg_packet = ogg.ogg_packet()

    # Flag to indicate the start of stream
    start_of_stream = 1

    # Packet counter
    count_packets = 0

    # PCM samples counter
    count_samples = 0

    # Allocate memory for a page
    ogg_page = ogg.ogg_page()

    # Open file for writing
    output_filename = "test.opus"
    f = open(output_filename, "wb")

    # Specify the identification header
    id_header = opus.make_identification_header(pre_skip=312)

    # Specify the packet containing the identification header
    ogg_packet.packet = ctypes.cast(id_header, ogg.c_uchar_p)
    ogg_packet.bytes = len(id_header)
    ogg_packet.b_o_s = start_of_stream
    ogg_packet.e_o_s = 0
    ogg_packet.granulepos = 0
    ogg_packet.packetno = count_packets
    start_of_stream = 0
    count_packets += 1

    # Write the header
    result = ogg.ogg_stream_packetin(stream_state, ogg_packet)

    if result != 0:
        raise Exception("Failed to write Opus identification header")

    # Specify the comment header
    comment_header = opus.make_comment_header()

    # Specify the packet containing the identification header
    ogg_packet.packet = ctypes.cast(comment_header, ogg.c_uchar_p)
    ogg_packet.bytes = len(comment_header)
    ogg_packet.b_o_s = start_of_stream
    ogg_packet.e_o_s = 0
    ogg_packet.granulepos = 0
    ogg_packet.packetno = count_packets
    count_packets += 1

    # Write the header
    result = ogg.ogg_stream_packetin(stream_state, ogg_packet)

    if result != 0:
        raise Exception("Failed to write Opus comment header")

    # Write out pages to file
    while ogg.ogg_stream_flush(ctypes.pointer(stream_state),
                               ctypes.pointer(ogg_page)) != 0:
        # Write page
        print("Writing page")
        f.write(bytes(ogg_page.header[0:ogg_page.header_len]))
        f.write(bytes(ogg_page.body[0:ogg_page.body_len]))

    # Decoding
    # ========

    # Extract the number of channels for the target
    targetChannels = npBufTarget.shape[1]

    # Create a decoder
    decoderFreq = 48000  # TODO: Test changes to this
    decoderPtr = createDecoder(decoderFreq, targetChannels)

    # Encode and re-decode the audio
    # ==============================

    # Pointer to a location in the source buffer.  We will increment
    # this as we progress through the encoding of the buffer.  It
    # starts pointing to the first byte.
    sourcePtr = npBufSource.ctypes.data_as(ctypes.c_void_p)
    sourcePtr_init = sourcePtr

    # The number of bytes processed will be the difference between the
    # pointer's current location and the address of the first byte.
    bytesProcessed = sourcePtr.value - sourcePtr_init.value

    # Pointer to a location in the target buffer.  We will increment
    # this as we progress through re-decoding each encoded frame.
    targetPtr = npBufTarget.ctypes.data_as(ctypes.c_void_p)

    # Loop through the source buffer
    while bytesProcessed < lengthBytes:
        print("Processing frame at sourcePtr ", sourcePtr.value)

        # Check if we have enough source data remaining to process at
        # the current frame size
        print("lengthBytes: ", lengthBytes)
        print("bytesProcessed: ", bytesProcessed)
        print("bytes remaining (lengthBytes - bytesProcessed):",
              lengthBytes - bytesProcessed)
        print("frameSizeBytes(frameSize):", frameSizeBytes(frameSize))
        while lengthBytes - bytesProcessed < frameSizeBytes(frameSize):
            print("Warning! Not enough data for frame.")
            frameSizeIndex -= 1
            if frameSizeIndex < 0:
                # The data is less than the smallest number of samples
                # in a frame.  Either we ignore the remaining samples
                # and shorten the audio, or we pad the frame with
                # zeros and lengthen the audio.  We'll take the easy
                # option and shorten the audio.
                break
            frameSize = frameSizes[frameSizeIndex]
            print("Decreased frame size to ", frameSize)

        if frameSizeIndex < 0:
            print("Warning! Ignoring samples at the end of the audio\n" +
                  "as they do not fit into even the smallest frame.")
            break

        # Encode the audio
        print("Encoding audio")
        numBytes = opus.opus_encode(
            encoder, ctypes.cast(sourcePtr, ctypes.POINTER(opus.opus_int16)),
            frameSize, encodedFramePtr, maxEncodedFrameBytes)
        print("numBytes: ", numBytes)

        # Check for any errors during encoding
        if numBytes < 0:
            raise Exception("Encoder error detected: " +
                            opus.opus_strerror(numBytes).decode("utf"))

        # Move to next position in the buffer: encoder
        oldAddress = sourcePtr.value
        #print("oldAddress:",oldAddress)
        deltaBytes = frameSize * sourceChannels * 2
        newAddress = oldAddress + deltaBytes
        #print("newAddress:",newAddress)
        sourcePtr = ctypes.c_void_p(newAddress)

        bytesProcessed = sourcePtr.value - sourcePtr_init.value

        # Writing OggOpus
        # ===============

        # Increase the number of samples
        count_samples += frameSize

        # Place data into the packet
        ogg_packet.packet = encodedFramePtr
        ogg_packet.bytes = numBytes
        ogg_packet.b_o_s = start_of_stream
        ogg_packet.e_o_s = 0  # FIXME: It needs to end!
        ogg_packet.granulepos = count_samples
        ogg_packet.packetno = count_packets

        # No longer the start of stream
        start_of_stream = 0

        # Increase the number of packets
        count_packets += 1

        # Place the packet in to the stream
        result = ogg.ogg_stream_packetin(stream_state, ogg_packet)

        # Check for errors
        if result != 0:
            raise Exception("Error while placing packet in Ogg stream")

        # Write out pages to file
        while ogg.ogg_stream_pageout(ctypes.pointer(stream_state),
                                     ctypes.pointer(ogg_page)) != 0:
            # Write page
            print("Writing page")
            f.write(bytes(ogg_page.header[0:ogg_page.header_len]))
            f.write(bytes(ogg_page.body[0:ogg_page.body_len]))

        # Decode the audio
        if True:
            print("Decoding audio")
            numSamples = opus.opus_decode(
                decoderPtr,
                encodedFramePtr,
                numBytes,
                ctypes.cast(targetPtr, ctypes.POINTER(ctypes.c_short)),
                5760,  # Max space required in PCM
                0  # What's this about?
            )
            print("numSamples: ", numSamples)

            # Check for any errors during decoding
            if numSamples < 0:
                raise Exception("Decoder error detected: " +
                                opus.opus_strerror(numSamples).decode("utf"))

            # Move to next position in the buffer: decoder
            targetPtr.value += numSamples * targetChannels * 2

    # Write a packet saying we're at the end of the stream
    # Place data into the packet
    ogg_packet.packet = None
    ogg_packet.bytes = 0
    ogg_packet.b_o_s = start_of_stream
    ogg_packet.e_o_s = 1
    ogg_packet.granulepos = count_samples
    ogg_packet.packetno = count_packets

    # Increase the number of packets
    count_packets += 1

    # Place the packet in to the stream
    #result = ogg.ogg_stream_packetin(
    #    stream_state,
    #    ogg_packet
    #)

    # Check for errors
    if result != 0:
        raise Exception("Error while placing packet in Ogg stream")

    # Write out pages to file
    while ogg.ogg_stream_pageout(ctypes.pointer(stream_state),
                                 ctypes.pointer(ogg_page)) != 0:
        # Write page
        print("Writing page")
        f.write(bytes(ogg_page.header[0:ogg_page.header_len]))
        f.write(bytes(ogg_page.body[0:ogg_page.body_len]))

    # Close file
    f.close()
Ejemplo n.º 4
0
    def process_frame(self, encoded_frame, seq_no):
        # Aquire the lock to ensure that we're threadsafe
        with self.__lock:
            # Mark the fact that we've started
            self.__started = True

            # Is the sequence number the one we're expecting?  If not,
            # consider storing it for later processing.
            print("Expecting seq no", self.__expected_seq_no)
            if encoded_frame is None:
                print("Did not get seq no", seq_no)
            else:
                print("Got seq no", seq_no)

            if seq_no != self.__expected_seq_no:
                print("Found out of order frame")
                # Calculate the new frame's distance from the
                # currently expected sequence number.  Because the
                # sequence numbers may roll over, we need to account
                # for that.
                distance = JitterBuffer.__calc_distance(
                    seq_no, self.__expected_seq_no, JitterBuffer.seq_rollover)

                # Check if the frame is too late
                if distance > 0:
                    print("Frame is ahead of what we were expecting; storing")
                    self.__out_of_order_frames[seq_no] = encoded_frame
                else:
                    print("Frame is behind what we were expecting; discarding")
                    pass

                # Are there frames still in the queue?  If not, then we
                # have a problem.  The current frame isn't what we need,
                # and we're in immediate need of the one we're missing.
                # It's time to give up on the currently expected frame.
                if q.empty():
                    self.give_up_on_expected_sequence()

                return

            # The encoded frame is the next in the sequence, so process it
            # now.

            # Create a buffer to hold the PCM data
            pcm_buf = self.__PCMFrameBufferType()

            # Get pointer to first element of the PCM buffer
            pcm_buf_ptr = ctypes.cast(pcm_buf, ctypes.POINTER(opus.opus_int16))

            # Get pointer to encoded frame
            if encoded_frame is None:
                encoded_frame_ptr = None
                encoded_frame_bytes = 0
            else:
                encoded_frame_ptr = ctypes.cast(encoded_frame,
                                                ctypes.POINTER(ctypes.c_ubyte))
                encoded_frame_bytes = len(encoded_frame)

            # Decode the frame
            num_samples = opus.opus_decode(
                self.__decoder_ptr,
                encoded_frame_ptr,
                encoded_frame_bytes,
                pcm_buf_ptr,
                self.__samples_per_channel_in_buf,
                0  # FIXME: What's this about?
            )

            # Check for any errors during decoding
            if num_samples < 0:
                raise Exception("Decoder error detected: " +
                                opus.opus_strerror(numSamples).decode("utf"))

            # Create a numpy array to hold the decoded PCM data.  Note
            # that the numpy array has the correct shape (whereas the
            # original PCM buffer had sufficient space allocated for the
            # largest possible frame.
            np_pcm_buf = numpy.ctypeslib.as_array(
                pcm_buf, (num_samples // channels, channels))
            np_pcm_buf = np_pcm_buf[0:num_samples]

            # Put the samples on the queue to play
            q.put_nowait(np_pcm_buf)

            # We can now expect the next sequence number
            self.__expected_seq_no += 1

            # If the next frame is already in the out-of-order dictionary,
            # process it now
            seq_no = self.__expected_seq_no
            if (seq_no in self.__out_of_order_frames):
                print("Processing previously stored, out-of-order, frame")
                self.process_frame(self.__out_of_order_frames[seq_no], seq_no)
                del self.__out_of_order_frames[seq_no]
Ejemplo n.º 5
0
def encodeThenDecode(npBufSource, npBufTarget, freq):
    # Encoding
    # ========

    # Extract the number of channels in the source
    sourceChannels = npBufSource.shape[1]

    # Create an encoder
    encoder = createEncoder(npBufSource, freq)

    # Frame sizes are measured in number of samples.  There are only a
    # specified number of possible valid frame durations for Opus,
    # which (assuming a frequency of 48kHz) gives the following valid
    # sizes.
    frameSizes = [120, 240, 480, 960, 1920, 2880]

    # Specify the desired frame size.  This will be used for the vast
    # majority of the encoding, except possibly at the end of the
    # buffer (as there may not be sufficient data left to fill a
    # frame.)
    frameSizeIndex = 5
    frameSize = frameSizes[frameSizeIndex]

    # Function to calculate the size of a frame in bytes
    def frameSizeBytes(frameSize):
        global bytesPerSample
        return frameSize * sourceChannels * bytesPerSample

    # Allocate storage space for the encoded frame.  4,000 bytes is
    # the recommended maximum buffer size for the encoded frame.
    maxEncodedFrameBytes = opus.opus_int32(4000)
    encodedFrameType = ctypes.c_ubyte * maxEncodedFrameBytes.value
    encodedFrame = encodedFrameType()

    # Create a pointer to the first byte of the buffer for the encoded
    # frame.
    encodedFramePtr = ctypes.cast(ctypes.pointer(encodedFrame),
                                  ctypes.POINTER(ctypes.c_ubyte))

    # Number of bytes to process in buffer
    bytesPerSample = 2
    lengthBytes = buf.shape[0] * buf.shape[1] * bytesPerSample

    # Decoding
    # ========

    # Extract the number of channels for the target
    targetChannels = npBufTarget.shape[1]

    # Create a decoder
    decoderFreq = 48000  # TODO: Test changes to this
    decoderPtr = createDecoder(decoderFreq, targetChannels)

    # Encode and re-decode the audio
    # ==============================

    # Pointer to a location in the source buffer.  We will increment
    # this as we progress through the encoding of the buffer.  It
    # starts pointing to the first byte.
    sourcePtr = npBufSource.ctypes.data_as(ctypes.c_void_p)
    sourcePtr_init = sourcePtr

    # The number of bytes processed will be the difference between the
    # pointer's current location and the address of the first byte.
    bytesProcessed = sourcePtr.value - sourcePtr_init.value

    # Pointer to a location in the target buffer.  We will increment
    # this as we progress through re-decoding each encoded frame.
    targetPtr = npBufTarget.ctypes.data_as(ctypes.c_void_p)

    # Loop through the source buffer
    count = 0  # FIXME: debugging only
    while bytesProcessed < lengthBytes:
        print("processing frame: ", count)
        count += 1
        print("Processing frame at sourcePtr ", sourcePtr.value)

        # Check if we have enough source data remaining to process at
        # the current frame size
        print("lengthBytes: ", lengthBytes)
        print("bytesProcessed: ", bytesProcessed)
        print("bytes remaining (lengthBytes - bytesProcessed):",
              lengthBytes - bytesProcessed)
        print("frameSizeBytes(frameSize):", frameSizeBytes(frameSize))
        while lengthBytes - bytesProcessed < frameSizeBytes(frameSize):
            print("Warning! Not enough data for frame.")
            frameSizeIndex -= 1
            if frameSizeIndex < 0:
                # The data is less than the smallest number of samples
                # in a frame.  Either we ignore the remaining samples
                # and shorten the audio, or we pad the frame with
                # zeros and lengthen the audio.  We'll take the easy
                # option and shorten the audio.
                break
            frameSize = frameSizes[frameSizeIndex]
            print("Decreased frame size to ", frameSize)

        if frameSizeIndex < 0:
            print("Warning! Ignoring samples at the end of the audio\n" +
                  "as they do not fit into even the smallest frame.")
            break

        # Encode the audio
        if True:
            # Print out the PCM data to see that it's readable
            #p = sourcePtr
            #d = 0
            #while d < frameSize*2*2:
            #    p2 = ctypes.c_void_p(ctypes.cast(p,ctypes.c_void_p).value + d)
            #    print("p[",p2.value,"]:",ctypes.cast(p2, opus.opus_int16_p).contents.value)
            #    d += 2

            print("Encoding audio")
            numBytes = opus.opus_encode(
                encoder, ctypes.cast(sourcePtr,
                                     ctypes.POINTER(opus.opus_int16)),
                frameSize, encodedFramePtr, maxEncodedFrameBytes)
            print("numBytes: ", numBytes)

            # Check for any errors during encoding
            if numBytes < 0:
                raise Exception("Encoder error detected: " +
                                opus.opus_strerror(numBytes).decode("utf"))

            # Move to next position in the buffer: encoder
            oldAddress = sourcePtr.value
            #print("oldAddress:",oldAddress)
            deltaBytes = frameSize * sourceChannels * 2
            newAddress = oldAddress + deltaBytes
            #print("newAddress:",newAddress)
            sourcePtr = ctypes.c_void_p(newAddress)

            bytesProcessed = sourcePtr.value - sourcePtr_init.value

        # Decode the audio
        if True:
            print("Decoding audio")
            numSamples = opus.opus_decode(
                decoderPtr,
                encodedFramePtr,
                numBytes,
                ctypes.cast(targetPtr, ctypes.POINTER(ctypes.c_short)),
                5760,  # Max space required in PCM
                0  # What's this about?
            )
            print("numSamples: ", numSamples)

            # Check for any errors during decoding
            if numSamples < 0:
                raise Exception("Decoder error detected: " +
                                opus.opus_strerror(numSamples).decode("utf"))

            # Move to next position in the buffer: decoder
            targetPtr.value += numSamples * targetChannels * 2