예제 #1
0
int main(int argc, char *argv[]) {
    /*perform simple round-trip using page reader and writer*/

    BitstreamReader *reader = br_open(stdin, BS_LITTLE_ENDIAN);
    BitstreamWriter *writer = bw_open(stdout, BS_LITTLE_ENDIAN);
    struct ogg_page page;

    do {
        ogg_status result;
        if ((result = read_ogg_page(reader, &page)) == OGG_OK) {
            write_ogg_page(writer, &page);
        } else {
            fprintf(stderr, "*** Error: %s", ogg_strerror(result));
            goto error;
        }
    } while (!page.header.stream_end);

    reader->close(reader);
    writer->close(writer);
    return 0;

error:
    reader->close(reader);
    writer->close(writer);
    return 1;
}
예제 #2
0
PyObject*
encoders_encode_shn(PyObject *dummy,
                    PyObject *args, PyObject *keywds)
{
    static char *kwlist[] = {"filename",
                             "pcmreader",
                             "is_big_endian",
                             "signed_samples",
                             "header_data",
                             "footer_data",
                             "block_size",
                             NULL};
    char *filename;
    FILE *output_file;
    BitstreamWriter* writer;
    pcmreader* pcmreader;
    int is_big_endian = 0;
    int signed_samples = 0;
    char* header_data;
#ifdef PY_SSIZE_T_CLEAN
    Py_ssize_t header_size;
#else
    int header_size;
#endif
    char* footer_data = NULL;
#ifdef PY_SSIZE_T_CLEAN
    Py_ssize_t footer_size = 0;
#else
    int footer_size = 0;
#endif
    unsigned block_size = 256;
    unsigned bytes_written = 0;
    unsigned i;

    /*fetch arguments*/
    if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO&iis#|s#I",
                                     kwlist,
                                     &filename,
                                     pcmreader_converter,
                                     &pcmreader,
                                     &is_big_endian,
                                     &signed_samples,
                                     &header_data,
                                     &header_size,

                                     &footer_data,
                                     &footer_size,
                                     &block_size))
        return NULL;

    /*ensure PCMReader is compatible with Shorten*/
    if ((pcmreader->bits_per_sample != 8) &&
        (pcmreader->bits_per_sample != 16)) {
        pcmreader->del(pcmreader);
        PyErr_SetString(PyExc_ValueError, "unsupported bits per sample");
        return NULL;
    }

    /*open given filename for writing*/
    if ((output_file = fopen(filename, "wb")) == NULL) {
        PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
        pcmreader->del(pcmreader);
        return NULL;
    } else {
        writer = bw_open(output_file, BS_BIG_ENDIAN);
    }

    /*write magic number and version*/
    writer->build(writer, "4b 8u", "ajkg", 2);

    bw_add_callback(writer, byte_counter, &bytes_written);

    /*write Shorten header*/
    write_header(writer,
                 pcmreader->bits_per_sample,
                 is_big_endian,
                 signed_samples,
                 pcmreader->channels,
                 block_size);

    /*issue initial VERBATIM command with header data*/
    write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM);
    write_unsigned(writer, VERBATIM_SIZE, header_size);
    for (i = 0; i < header_size; i++)
        write_unsigned(writer, VERBATIM_BYTE_SIZE, (uint8_t)header_data[i]);

    /*process PCM frames*/
    if (encode_audio(writer, pcmreader, signed_samples, block_size))
        goto error;

    /*if there's footer data, issue a VERBATIM command for it*/
    if ((footer_data != NULL) && (footer_size > 0)) {
        write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM);
        write_unsigned(writer, VERBATIM_SIZE, footer_size);
        for (i = 0; i < footer_size; i++)
            write_unsigned(writer, VERBATIM_BYTE_SIZE, (uint8_t)footer_data[i]);
    }

    /*issue QUIT command*/
    write_unsigned(writer, COMMAND_SIZE, FN_QUIT);

    /*pad output (not including header) to a multiple of 4 bytes*/
    writer->byte_align(writer);
    while ((bytes_written % 4) != 0) {
        writer->write(writer, 8, 0);
    }

    /*deallocate temporary buffers and close files*/
    pcmreader->del(pcmreader);
    writer->close(writer);

    Py_INCREF(Py_None);
    return Py_None;
 error:
    pcmreader->del(pcmreader);
    writer->close(writer);

    return NULL;
}
예제 #3
0
static result_t
encode_opus_file(char *filename, pcmreader *pcmreader,
                 int quality, unsigned original_sample_rate)
{
    const int multichannel = (pcmreader->channels > 2);
    const unsigned channel_mapping = (pcmreader->channels > 8 ? 255 :
                                      pcmreader->channels > 2);
    int stream_count;
    int coupled_stream_count;
    unsigned char stream_map[255];

    result_t result = ENCODE_OK;
    FILE *output_file = NULL;
    ogg_stream_state ogg_stream;
    ogg_page ogg_page;
    OpusEncoder *opus_encoder = NULL;
    OpusMSEncoder *opus_ms_encoder = NULL;
    int error;
    aa_int *samples = NULL;
    opus_int16 *opus_samples = NULL;
    unsigned char opus_frame[OPUS_FRAME_LEN];
    ogg_int64_t granulepos = 0;
    ogg_int64_t packetno = 0;
    opus_int32 preskip;

    /*open output file for writing*/
    if ((output_file = fopen(filename, "w+b")) == NULL) {
        return ERR_IOERROR;
    }

    if (!multichannel) {
        if ((opus_encoder = opus_encoder_create(48000,
                                                pcmreader->channels,
                                                OPUS_APPLICATION_AUDIO,
                                                &error)) == NULL) {
            fclose(output_file);
            return ERR_ENCODER_INIT;
        }

        opus_encoder_ctl(opus_encoder, OPUS_SET_COMPLEXITY(quality));
        opus_encoder_ctl(opus_encoder, OPUS_GET_LOOKAHEAD(&preskip));
    } else {

        if ((opus_ms_encoder =
             opus_multistream_surround_encoder_create(
                 48000,
                 pcmreader->channels,
                 channel_mapping,
                 &stream_count,
                 &coupled_stream_count,
                 stream_map,
                 OPUS_APPLICATION_AUDIO,
                 &error)) == NULL) {
            fclose(output_file);
            return ERR_ENCODER_INIT;
        }

        opus_multistream_encoder_ctl(opus_ms_encoder,
                                     OPUS_SET_COMPLEXITY(quality));
        opus_multistream_encoder_ctl(opus_ms_encoder,
                                     OPUS_GET_LOOKAHEAD(&preskip));
    }


    srand((unsigned)time(NULL));
    ogg_stream_init(&ogg_stream, rand());

    /*write header and comment packets to Ogg stream*/
    {
        BitstreamWriter *header = bw_open_recorder(BS_LITTLE_ENDIAN);
        BitstreamWriter *comment = bw_open_recorder(BS_LITTLE_ENDIAN);
        int i;

        /*write header packet to Ogg stream*/
        const char opushead[] = "OpusHead";
        const char opuscomment[] = "OpusTags";
        const char *vendor_string = opus_get_version_string();
        const size_t vendor_string_len = strlen(vendor_string);
        ogg_packet packet_head;
        ogg_packet packet_tags;

        header->write_bytes(header,
                            (uint8_t*)opushead,
                            (unsigned)strlen(opushead));
        header->write(header, 8, 1);       /*version*/
        header->write(header, 8, pcmreader->channels);
        header->write(header, 16, preskip);
        header->write(header, 32, original_sample_rate);
        header->write(header, 16, 0);      /*output gain*/
        header->write(header, 8, channel_mapping);
        if (channel_mapping != 0) {
            header->write(header, 8, stream_count);
            header->write(header, 8, coupled_stream_count);
            for (i = 0; i < pcmreader->channels; i++) {
                header->write(header, 8, stream_map[i]);
            }
        }

        packet_head.packet = BUF_WINDOW_START(header->output.buffer);
        packet_head.bytes = BUF_WINDOW_SIZE(header->output.buffer);
        packet_head.b_o_s = 1;
        packet_head.e_o_s = 0;
        packet_head.granulepos = 0;
        packet_head.packetno = packetno++;

        ogg_stream_packetin(&ogg_stream, &packet_head);

        for (i = ogg_stream_flush(&ogg_stream, &ogg_page);
             i != 0;
             i = ogg_stream_flush(&ogg_stream, &ogg_page)) {
            fwrite(ogg_page.header, 1, ogg_page.header_len, output_file);
            fwrite(ogg_page.body, 1, ogg_page.body_len, output_file);
        }

        /*write comment packet to Ogg stream*/
        comment->write_bytes(comment,
                             (uint8_t*)opuscomment,
                             (unsigned)strlen(opuscomment));
        comment->write(comment, 32, (unsigned)vendor_string_len);
        comment->write_bytes(comment,
                             (uint8_t*)vendor_string,
                             (unsigned)vendor_string_len);
        comment->write(comment, 32, 0);

        packet_tags.packet = BUF_WINDOW_START(comment->output.buffer);
        packet_tags.bytes = BUF_WINDOW_SIZE(comment->output.buffer);
        packet_tags.b_o_s = 0;
        packet_tags.e_o_s = 0;
        packet_tags.granulepos = 0;
        packet_tags.packetno = packetno++;

        ogg_stream_packetin(&ogg_stream, &packet_tags);

        for (i = ogg_stream_flush(&ogg_stream, &ogg_page);
             i != 0;
             i = ogg_stream_flush(&ogg_stream, &ogg_page)) {
            fwrite(ogg_page.header, 1, ogg_page.header_len, output_file);
            fwrite(ogg_page.body, 1, ogg_page.body_len, output_file);
        }

        header->close(header);
        comment->close(comment);
    }

    samples = aa_int_new();
    opus_samples = malloc(sizeof(opus_int16) *
                          pcmreader->channels *
                          BLOCK_SIZE);

    /*for each non-empty FrameList from PCMReader, encode Opus frame*/
    if (pcmreader->read(pcmreader, BLOCK_SIZE, samples)) {
        result = ERR_PCMREADER;
        goto cleanup;
    } else if (samples->_[0]->len > BLOCK_SIZE) {
        result = ERR_BLOCK_SIZE;
        goto cleanup;
    }

    while (samples->_[0]->len > 0) {
        const int short_framelist = (samples->_[0]->len < BLOCK_SIZE);
        unsigned c;
        opus_int32 encoded_size;
        ogg_packet packet;

        granulepos += samples->_[0]->len;

        /*pad FrameList with additional null samples if necessary*/
        for (c = 0; c < samples->len; c++) {
            a_int *channel = samples->_[c];
            channel->mappend(channel, BLOCK_SIZE - samples->_[0]->len, 0);
        }

        /*rearrange channels to Vorbis order if necessary*/
        reorder_channels(pcmreader->channel_mask, samples);

        /*place samples in interleaved buffer*/
        for (c = 0; c < samples->len; c++) {
            unsigned i;
            a_int *channel = samples->_[c];

            for (i = 0; i < channel->len; i++) {
                opus_samples[c + (i * samples->len)] =
                    (opus_int16)channel->_[i];
            }
        }

        /*call opus_encode on interleaved buffer to get next packet*/
        if (!multichannel) {
            encoded_size = opus_encode(opus_encoder,
                                       opus_samples,
                                       samples->_[0]->len,
                                       opus_frame,
                                       OPUS_FRAME_LEN);
        } else {
            encoded_size = opus_multistream_encode(opus_ms_encoder,
                                                   opus_samples,
                                                   samples->_[0]->len,
                                                   opus_frame,
                                                   OPUS_FRAME_LEN);
        }

        /*get next FrameList to encode*/
        if (pcmreader->read(pcmreader, BLOCK_SIZE, samples)) {
            result = ERR_PCMREADER;
            goto cleanup;
        } else if (samples->_[0]->len > BLOCK_SIZE) {
            result = ERR_BLOCK_SIZE;
            goto cleanup;
        }

        /*dump Opus packet to Ogg stream*/
        /*do this *after* reading the next FrameList in order to detect
          the end of stream properly based on whether the FrameList
          has no frames*/
        packet.packet = (unsigned char *)opus_frame;
        packet.bytes = encoded_size;
        packet.b_o_s = 0;
        packet.e_o_s = (short_framelist || (samples->_[0]->len == 0));
        packet.granulepos = granulepos;
        packet.packetno = packetno;

        ogg_stream_packetin(&ogg_stream, &packet);
        while (ogg_stream_pageout(&ogg_stream, &ogg_page)) {
            fwrite(ogg_page.header, 1, ogg_page.header_len, output_file);
            fwrite(ogg_page.body, 1, ogg_page.body_len, output_file);
        }
    }

    /*flush any remaining Ogg pages to disk*/
    while (ogg_stream_flush(&ogg_stream, &ogg_page)) {
        fwrite(ogg_page.header, 1, ogg_page.header_len, output_file);
        fwrite(ogg_page.body, 1, ogg_page.body_len, output_file);
    }

cleanup:
    fclose(output_file);
    ogg_stream_clear(&ogg_stream);
    if (!multichannel) {
        opus_encoder_destroy(opus_encoder);
    } else {
        opus_multistream_encoder_destroy(opus_ms_encoder);
    }
    samples->del(samples);
    free(opus_samples);
    return result;
}