mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2025-01-13 05:31:44 +00:00
366 lines
12 KiB
C++
366 lines
12 KiB
C++
/*
|
|
mkvmerge -- utility for splicing together matroska files
|
|
from component media subtypes
|
|
|
|
p_vorbis.h
|
|
|
|
Written by Moritz Bunkus <moritz@bunkus.org>
|
|
|
|
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.3 2003/03/04 09:27:05 mosu Exp $
|
|
\brief Vorbis packetizer
|
|
\author Moritz Bunkus <moritz @ bunkus.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_OGGVORBIS
|
|
|
|
#include <malloc.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <ogg/ogg.h>
|
|
#include <vorbis/codec.h>
|
|
|
|
#include "common.h"
|
|
#include "pr_generic.h"
|
|
#include "p_vorbis.h"
|
|
// #include "vorbis_header_utils.h"
|
|
|
|
#include "KaxTracks.h"
|
|
#include "KaxTrackAudio.h"
|
|
|
|
#ifdef DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif
|
|
|
|
vorbis_packetizer_c::vorbis_packetizer_c(audio_sync_t *nasync,
|
|
void *d_header, int l_header,
|
|
void *d_comments, int l_comments,
|
|
void *d_codecsetup, int l_codecsetup)
|
|
throw (error_c) : q_c() {
|
|
int i;
|
|
|
|
packetno = 0;
|
|
last_bs = 0;
|
|
samples = 0;
|
|
memcpy(&async, nasync, sizeof(audio_sync_t));
|
|
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);
|
|
}
|
|
|
|
int vorbis_packetizer_c::encode_silence(int fd) {
|
|
// vorbis_dsp_state lvds;
|
|
// vorbis_block lvb;
|
|
// vorbis_info lvi;
|
|
// vorbis_comment lvc;
|
|
// ogg_stream_state loss;
|
|
// ogg_page log;
|
|
// ogg_packet lop;
|
|
// ogg_packet h_main, h_comments, h_codebook;
|
|
// int samples, i, j, k, eos;
|
|
// float **buffer;
|
|
|
|
// samples = vi.rate * async.displacement / 1000;
|
|
// vorbis_info_init(&lvi);
|
|
// if (vorbis_encode_setup_vbr(&lvi, vi.channels, vi.rate, 1))
|
|
// return EOTHER;
|
|
// vorbis_encode_setup_init(&lvi);
|
|
// vorbis_analysis_init(&lvds, &lvi);
|
|
// vorbis_block_init(&lvds, &lvb);
|
|
// vorbis_comment_init(&lvc);
|
|
// lvc.vendor = strdup(VERSIONINFO);
|
|
// lvc.user_comments = (char **)mmalloc(4);
|
|
// lvc.comment_lengths = (int *)mmalloc(4);
|
|
// lvc.comments = 0;
|
|
// ogg_stream_init(&loss, serialno);
|
|
// vorbis_analysis_headerout(&lvds, &lvc, &h_main, &h_comments, &h_codebook);
|
|
// ogg_stream_packetin(&loss, &h_main);
|
|
// ogg_stream_packetin(&loss, &h_comments);
|
|
// ogg_stream_packetin(&loss, &h_codebook);
|
|
// while (ogg_stream_flush(&loss, &log)) {
|
|
// write(fd, log.header, log.header_len);
|
|
// write(fd, log.body, log.body_len);
|
|
// }
|
|
// eos = 0;
|
|
// for (i = 0; i <= 1; i++) {
|
|
// if (i == 0) {
|
|
// buffer = vorbis_analysis_buffer(&lvds, samples);
|
|
// for (j = 0; j < samples; j++)
|
|
// for (k = 0; k < vi.channels; k++)
|
|
// buffer[k][j] = 0.0f;
|
|
// vorbis_analysis_wrote(&lvds, samples);
|
|
// } else
|
|
// vorbis_analysis_wrote(&lvds, 0);
|
|
// while (vorbis_analysis_blockout(&lvds, &lvb) == 1) {
|
|
// vorbis_analysis(&lvb, NULL);
|
|
// vorbis_bitrate_addblock(&lvb);
|
|
// while (vorbis_bitrate_flushpacket(&lvds, &lop)) {
|
|
// ogg_stream_packetin(&loss, &lop);
|
|
// while (!eos) {
|
|
// if (!ogg_stream_pageout(&loss, &log))
|
|
// break;
|
|
// write(fd, log.header, log.header_len);
|
|
// write(fd, log.body, log.body_len);
|
|
// if (ogg_page_eos(&log))
|
|
// eos = 1;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// ogg_stream_clear(&loss);
|
|
// vorbis_block_clear(&lvb);
|
|
// vorbis_dsp_clear(&lvds);
|
|
// vorbis_info_clear(&lvi);
|
|
// vorbis_comment_clear(&lvc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void vorbis_packetizer_c::setup_displacement() {
|
|
// char tmpname[30];
|
|
// int fd, old_verbose, status;
|
|
// ogm_reader_c *ogm_reader;
|
|
// audio_sync_t nosync;
|
|
// range_t norange;
|
|
// generic_packetizer_c *old_packetizer;
|
|
// ogmmerge_page_t *mpage;
|
|
|
|
// if (async.displacement <= 0)
|
|
// return;
|
|
|
|
// strcpy(tmpname, "/tmp/ogmmerge-XXXXXX");
|
|
// if ((fd = mkstemp(tmpname)) == -1) {
|
|
// fprintf(stderr, "FATAL: vorbis_packetizer: mkstemp() failed.\n");
|
|
// exit(1);
|
|
// }
|
|
// if (encode_silence(fd) < 0) {
|
|
// fprintf(stderr, "FATAL: Could not encode silence.\n");
|
|
// exit(1);
|
|
// }
|
|
// close(fd);
|
|
|
|
// old_verbose = verbose;
|
|
// memset(&norange, 0, sizeof(range_t));
|
|
// verbose = 0;
|
|
// nosync.displacement = 0;
|
|
// nosync.linear = 1.0;
|
|
// try {
|
|
// ogm_reader = new ogm_reader_c(tmpname, NULL, NULL, NULL, &nosync,
|
|
// &norange, NULL, NULL);
|
|
// } catch (error_c error) {
|
|
// fprintf(stderr, "FATAL: vorbis_packetizer: Could not create an " \
|
|
// "ogm_reader for the temporary file.\n%s\n", error.get_error());
|
|
// exit(1);
|
|
// }
|
|
// ogm_reader->overwrite_eos(1);
|
|
// memcpy(&nosync, &async, sizeof(audio_sync_t));
|
|
// async.displacement = 0;
|
|
// async.linear = 1.0;
|
|
// status = ogm_reader->read();
|
|
// mpage = ogm_reader->get_header_page();
|
|
// free(mpage->og->header);
|
|
// free(mpage->og->body);
|
|
// free(mpage->og);
|
|
// free(mpage);
|
|
// skip_packets = 2;
|
|
// old_packetizer = ogm_reader->set_packetizer(this);
|
|
// while (status == EMOREDATA)
|
|
// status = ogm_reader->read();
|
|
// ogm_reader->set_packetizer(old_packetizer);
|
|
// delete ogm_reader;
|
|
// verbose = old_verbose;
|
|
// unlink(tmpname);
|
|
// memcpy(&async, &nosync, sizeof(audio_sync_t));
|
|
}
|
|
|
|
#define AVORBIS "A_VORBIS"
|
|
|
|
void vorbis_packetizer_c::set_header() {
|
|
using namespace LIBMATROSKA_NAMESPACE;
|
|
unsigned char *buffer;
|
|
int n, offset, i, lsize;
|
|
|
|
if (kax_last_entry == NULL)
|
|
track_entry =
|
|
&GetChild<KaxTrackEntry>(static_cast<KaxTracks &>(*kax_tracks));
|
|
else
|
|
track_entry =
|
|
&GetNextChild<KaxTrackEntry>(static_cast<KaxTracks &>(*kax_tracks),
|
|
static_cast<KaxTrackEntry &>(*kax_last_entry));
|
|
kax_last_entry = track_entry;
|
|
|
|
if (serialno == -1)
|
|
serialno = track_number++;
|
|
KaxTrackNumber &tnumber =
|
|
GetChild<KaxTrackNumber>(static_cast<KaxTrackEntry &>(*track_entry));
|
|
*(static_cast<EbmlUInteger *>(&tnumber)) = serialno;
|
|
|
|
*(static_cast<EbmlUInteger *>
|
|
(&GetChild<KaxTrackType>(static_cast<KaxTrackEntry &>(*track_entry)))) =
|
|
track_audio;
|
|
|
|
KaxCodecID &codec_id =
|
|
GetChild<KaxCodecID>(static_cast<KaxTrackEntry &>(*track_entry));
|
|
codec_id.CopyBuffer((binary *)AVORBIS, countof(AVORBIS));
|
|
|
|
KaxTrackAudio &track_audio =
|
|
GetChild<KaxTrackAudio>(static_cast<KaxTrackEntry &>(*track_entry));
|
|
|
|
KaxAudioSamplingFreq &kax_freq = GetChild<KaxAudioSamplingFreq>(track_audio);
|
|
*(static_cast<EbmlFloat *>(&kax_freq)) = (float)vi.rate;
|
|
|
|
KaxAudioChannels &kax_chans = GetChild<KaxAudioChannels>(track_audio);
|
|
*(static_cast<EbmlUInteger *>(&kax_chans)) = vi.channels;
|
|
|
|
// We use lacing for the blocks. The first bytes is the number of
|
|
// packets being laced. 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.
|
|
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] = 3; // The number of packets.
|
|
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);
|
|
|
|
KaxCodecPrivate &codec_private =
|
|
GetChild<KaxCodecPrivate>(static_cast<KaxTrackEntry &>(*track_entry));
|
|
codec_private.CopyBuffer((binary *)buffer, lsize);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
/*
|
|
* Some notes - processing is straight-forward if no AV synchronization
|
|
* is needed - the packet is simply handed over to ogg_stream_packetin.
|
|
* Unfortunately things are not that easy if AV sync is done. For a
|
|
* negative displacement packets are simply discarded if their granulepos
|
|
* is set before the displacement. For positive displacements the packetizer
|
|
* has to generate silence and insert this silence just after the first
|
|
* three stream packets - the Vorbis header, the comment and the
|
|
* setup packets.
|
|
* The creation of the silence is done by encoding 0 samples with
|
|
* libvorbis. This produces an OGG file that is read by a separate
|
|
* ogm_reader_c. We set this very packetizer as the reader's packetizer
|
|
* so that we get the packets we want from the 'silenced' file. This means
|
|
* skipping the very first three packets, hence the 'skip_packets' variable.
|
|
*/
|
|
int vorbis_packetizer_c::process(char *data, int size, int64_t timecode) {
|
|
ogg_packet op;
|
|
u_int64_t this_bs, samples_here;
|
|
|
|
/*
|
|
* We might want to skip a certain amount of packets if this packetizer
|
|
* is used by a second ogm_reader_c, one that reads from a 'silence'
|
|
* file. skip_packets is then intially set to 3 in order to skip all
|
|
* header packets.
|
|
*/
|
|
// if (skip_packets > 0) {
|
|
// skip_packets--;
|
|
// return EMOREDATA;
|
|
// }
|
|
|
|
// Recalculate the timecode if needed.
|
|
if (timecode == -1)
|
|
timecode = samples * 1000 / vi.rate;
|
|
|
|
op.packet = (unsigned char *)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;
|
|
|
|
// Handle the displacement.
|
|
timecode += async.displacement;
|
|
|
|
// Handle the linear sync - simply multiply with the given factor.
|
|
timecode = (int64_t)((double)timecode * async.linear);
|
|
|
|
// // range checking
|
|
// if ((timecode >= range.start) &&
|
|
// (last_granulepos_seen >= range.start) &&
|
|
// ((range.end == 0) || (op->granulepos <= range.end))) {
|
|
// // Adjust the granulepos
|
|
// op->granulepos = (u_int64_t)(op->granulepos - range.start);
|
|
// // If no or a positive displacement is set the packet has to be output.
|
|
// if (async.displacement >= 0) {
|
|
// ogg_stream_packetin(&os, op);
|
|
// packetno++;
|
|
// }
|
|
// /*
|
|
// * Only output the current packet if its granulepos ( = position in
|
|
// * samples) is bigger than the displacement.
|
|
// */
|
|
// else if (op->granulepos > 0) {
|
|
// ogg_stream_packetin(&os, op);
|
|
// packetno++;
|
|
// }
|
|
// queue_pages();
|
|
// last_granulepos = op->granulepos;
|
|
// } else if (op->e_o_s) {
|
|
|
|
add_packet(data, size, (u_int64_t)timecode);
|
|
|
|
return EMOREDATA;
|
|
}
|
|
|
|
#endif // HAVE_OGGVORBIS
|