/* mkvmerge -- utility for splicing together matroska files from component media subtypes p_vorbis.h Written by Moritz Bunkus Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html */ /*! \file \version \$Id: p_vorbis.cpp,v 1.13 2003/04/21 08:29:50 mosu Exp $ \brief Vorbis packetizer \author Moritz Bunkus */ #include "config.h" #ifdef HAVE_OGGVORBIS #include #include #include #include #include #include #include #include "common.h" #include "pr_generic.h" #include "p_vorbis.h" #include "matroska.h" #ifdef DMALLOC #include #endif vorbis_packetizer_c::vorbis_packetizer_c(unsigned char *d_header, int l_header, unsigned char *d_comments, int l_comments, unsigned char *d_codecsetup, int l_codecsetup, track_info_t *nti) throw (error_c): generic_packetizer_c(nti) { int i; packetno = 0; last_bs = 0; samples = 0; memset(headers, 0, 3 * sizeof(ogg_packet)); headers[0].packet = (unsigned char *)malloc(l_header); headers[1].packet = (unsigned char *)malloc(l_comments); headers[2].packet = (unsigned char *)malloc(l_codecsetup); if ((headers[0].packet == NULL) || (headers[1].packet == NULL) || (headers[2].packet == NULL)) die("malloc"); memcpy(headers[0].packet, d_header, l_header); memcpy(headers[1].packet, d_comments, l_comments); memcpy(headers[2].packet, d_codecsetup, l_codecsetup); headers[0].bytes = l_header; headers[1].bytes = l_comments; headers[2].bytes = l_codecsetup; headers[0].b_o_s = 1; headers[1].packetno = 1; headers[2].packetno = 2; vorbis_info_init(&vi); vorbis_comment_init(&vc); for (i = 0; i < 3; i++) if (vorbis_synthesis_headerin(&vi, &vc, &headers[i]) < 0) throw error_c("Error: vorbis_packetizer: Could not extract the " "stream's parameters from the first packets.\n"); set_header(); } vorbis_packetizer_c::~vorbis_packetizer_c() { int i; for (i = 0; i < 3; i++) if (headers[i].packet != NULL) free(headers[i].packet); } #define AVORBIS "A_VORBIS" void vorbis_packetizer_c::set_header() { using namespace LIBMATROSKA_NAMESPACE; unsigned char *buffer; int n, offset, i, lsize; set_serial(-1); set_track_type(track_audio); set_codec_id(MKV_A_VORBIS); // We use lacing for the blocks. The first bytes is the number of // packets being laced which is one less than the number of blocks that // are actually stored. For each packet in the lace there's the length // coded like this: // length = 0 // while (next byte == 255) { length += 255 } // length += this byte which is < 255 // The last packet's length can be calculated by the length of // the KaxCodecPrivate and all prior packets, so there's no length for it - // and that's why the first byte is (num_packets - 1). lsize = 1 + (headers[0].bytes / 255) + 1 + (headers[1].bytes / 255) + 1 + headers[0].bytes + headers[1].bytes + headers[2].bytes; buffer = (unsigned char *)malloc(lsize); if (buffer == NULL) die("malloc"); buffer[0] = 2; // The number of packets less one. offset = 1; for (i = 0; i < 2; i++) { for (n = headers[i].bytes; n >= 255; n -= 255) { buffer[offset] = 255; offset++; } buffer[offset] = n; offset++; memcpy(&buffer[offset], headers[i].packet, headers[i].bytes); offset += headers[i].bytes; } memcpy(&buffer[offset], headers[2].packet, headers[2].bytes); set_codec_private(buffer, lsize); free(buffer); set_audio_sampling_freq((float)vi.rate); set_audio_channels(vi.channels); if (ti->default_track) set_as_default_track('a'); generic_packetizer_c::set_header(); } /* * Some notes - processing is straight-forward if no AV synchronization * is needed - the packet is simply stored in the Matroska file. * Unfortunately things are not that easy if AV sync is done. For a * negative displacement packets are simply discarded if their timecode * is set before the displacement. For positive displacements the packetizer * has to generate silence packets and put them into the Matroska file first. */ int vorbis_packetizer_c::process(unsigned char *data, int size, int64_t timecode, int64_t, int64_t, int64_t) { unsigned char zero[2]; ogg_packet op; int64_t this_bs, samples_here, samples_needed; // Recalculate the timecode if needed. if (timecode == -1) timecode = samples * 1000 / vi.rate; // Positive displacement, first packet? Well then lets create silence. if ((packetno == 0) && (ti->async.displacement > 0)) { // Create a fake packet so we can use vorbis_packet_blocksize(). zero[0] = 0; zero[1] = 0; memset(&op, 0, sizeof(ogg_packet)); op.packet = zero; op.bytes = 2; // Calculate how many samples we have to create. samples_needed = vi.rate * 1000 / ti->async.displacement; this_bs = vorbis_packet_blocksize(&vi, &op); samples_here = (this_bs + last_bs) / 4; while ((samples + samples_here) < samples_needed) { samples += samples_here; last_bs = this_bs; samples_here = (this_bs + last_bs) / 4; add_packet(zero, 2, samples * 1000 / vi.rate); } ti->async.displacement = 0; } // Update the number of samples we have processed so that we can // calculate the timecode on the next call. op.packet = data; op.bytes = size; this_bs = vorbis_packet_blocksize(&vi, &op); samples_here = (this_bs + last_bs) / 4; samples += samples_here; last_bs = this_bs; // fprintf(stdout, "samples in this Vorbis packet: %llu\n", samples_here); // Handle the displacement. timecode += ti->async.displacement; // Handle the linear sync - simply multiply with the given factor. timecode = (int64_t)((double)timecode * ti->async.linear); // If a negative sync value was used we may have to skip this packet. if (timecode < 0) return EMOREDATA; add_packet(data, size, (int64_t)timecode); return EMOREDATA; } #endif // HAVE_OGGVORBIS