summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/import/nuv/import_nuv.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/import/nuv/import_nuv.c')
-rw-r--r--debian/transcode/transcode-1.1.7/import/nuv/import_nuv.c686
1 files changed, 686 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/import/nuv/import_nuv.c b/debian/transcode/transcode-1.1.7/import/nuv/import_nuv.c
new file mode 100644
index 00000000..b314a85d
--- /dev/null
+++ b/debian/transcode/transcode-1.1.7/import/nuv/import_nuv.c
@@ -0,0 +1,686 @@
+/*
+ * import_nuv.c -- NuppelVideo import module
+ * Written by Andrew Church <achurch@achurch.org>
+ *
+ * This file is part of transcode, a video stream processing tool.
+ * transcode is free software, distributable under the terms of the GNU
+ * General Public License (version 2 or later). See the file COPYING
+ * for details.
+ */
+
+#include "transcode.h"
+#include "libtc/libtc.h"
+#include "libtc/optstr.h"
+#include "libtc/tcmodule-plugin.h"
+#include "aclib/ac.h"
+#include "nuppelvideo.h"
+#include "RTjpegN.h"
+#include "libtc/tc_lzo.h"
+
+#define MOD_NAME "import_nuv.so"
+#define MOD_VERSION "v0.9 (2006-06-03)"
+#define MOD_CAP "Imports NuppelVideo streams"
+#define MOD_AUTHOR "Andrew Church"
+
+#define MOD_FEATURES \
+ TC_MODULE_FEATURE_DEMULTIPLEX|TC_MODULE_FEATURE_DECODE|TC_MODULE_FEATURE_VIDEO
+
+#define MOD_FLAGS \
+ TC_MODULE_FLAG_RECONFIGURABLE
+
+
+/*************************************************************************/
+
+/* Private data used by this module. */
+typedef struct {
+ int fd; // File descriptor to read from
+ int width, height; // Video dimensions
+ double fps; // Nominal frames per second
+ double tsoffset; // Timestamp offset for multi-part capture files
+ int framenum; // Frame number of loaded frame
+ int have_vframe; // Do we have a video frame stored?
+ double audiorate; // Actual audio rate (from SA frame)
+ double audiofrac; // Saved fractional position (for resampling)
+ uint32_t cdata[128]; // Compressor data (from DR frame)
+ int dec_initted; // Decompressor initted?
+
+ // Previous video frame, for frame cloning
+ uint8_t saved_vframe[TC_MAX_V_FRAME_WIDTH*TC_MAX_V_FRAME_HEIGHT*3];
+ int saved_vframelen;
+ uint8_t saved_vcomptype;
+ struct rtframeheader framehdr; // Next video frame header
+} PrivateData;
+
+/* NuppelVideo always uses 44100 sps */
+#define NUV_ARATE 44100
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Module interface routines and data. */
+
+/*************************************************************************/
+
+/**
+ * nuv_init: Initialize this instance of the module. See tcmodule-data.h
+ * for function details.
+ */
+
+static int nuv_init(TCModuleInstance *self, uint32_t features)
+{
+ PrivateData *pd;
+
+ TC_MODULE_SELF_CHECK(self, "init");
+ TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
+
+ self->userdata = pd = tc_malloc(sizeof(PrivateData));
+ if (!pd) {
+ tc_log_error(MOD_NAME, "init: out of memory!");
+ return TC_ERROR;
+ }
+ pd->fd = -1;
+ pd->dec_initted = 0;
+
+ if (verbose) {
+ tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
+ }
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * nuv_fini: Clean up after this instance of the module. See
+ * tcmodule-data.h for function details.
+ */
+
+static int nuv_fini(TCModuleInstance *self)
+{
+ PrivateData *pd;
+
+ TC_MODULE_SELF_CHECK(self, "fini");
+
+ pd = self->userdata;
+
+ if (pd->fd) {
+ close(pd->fd);
+ pd->fd = -1;
+ }
+
+ tc_free(self->userdata);
+ self->userdata = NULL;
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * nuv_configure: Configure this instance of the module. See
+ * tcmodule-data.h for function details.
+ */
+
+static int nuv_configure(TCModuleInstance *self,
+ const char *options, vob_t *vob)
+{
+ PrivateData *pd;
+ struct rtfileheader hdr;
+ const char *filename = vob->video_in_file;
+
+ TC_MODULE_SELF_CHECK(self, "configure");
+
+ pd = self->userdata;
+
+ // FIXME: is this a good place for open()? And how do we know which
+ // file (video or audio) to open?
+ pd->fd = open(filename, O_RDONLY);
+ if (pd->fd < 0) {
+ tc_log_error(MOD_NAME, "Unable to open %s: %s", filename,
+ strerror(errno));
+ return TC_OK;
+ }
+ if (read(pd->fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ tc_log_error(MOD_NAME, "Unable to read file header from %s", filename);
+ close(pd->fd);
+ pd->fd = -1;
+ return TC_OK;
+ }
+ if (strcmp(hdr.finfo, "NuppelVideo") != 0) {
+ tc_log_error(MOD_NAME, "Bad file header in %s", filename);
+ close(pd->fd);
+ pd->fd = -1;
+ return TC_OK;
+ }
+ if (strcmp(hdr.version, "0.05") != 0) {
+ tc_log_error(MOD_NAME, "Bad format version in %s", filename);
+ close(pd->fd);
+ pd->fd = -1;
+ return TC_OK;
+ }
+ pd->width = hdr.width;
+ pd->height = hdr.height;
+ pd->fps = hdr.fps;
+ pd->tsoffset = 0;
+ pd->framenum = 0;
+ pd->have_vframe = 0;
+ pd->audiorate = NUV_ARATE;
+ pd->audiofrac = 0;
+ memset(pd->cdata, 0, sizeof(pd->cdata));
+ pd->saved_vframelen = 0;
+ pd->saved_vcomptype = 'N'; // black frame
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * nuv_stop: Reset this instance of the module. See tcmodule-data.h for
+ * function details.
+ */
+
+static int nuv_stop(TCModuleInstance *self)
+{
+ PrivateData *pd;
+
+ TC_MODULE_SELF_CHECK(self, "stop");
+
+ pd = self->userdata;
+
+ if (pd->fd >= 0) {
+ close(pd->fd);
+ pd->fd = -1;
+ }
+ pd->dec_initted = 0;
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * nuv_inspect: Return the value of an option in this instance of the
+ * module. See tcmodule-data.h for function details.
+ */
+
+static int nuv_inspect(TCModuleInstance *self,
+ const char *param, const char **value)
+{
+ PrivateData *pd;
+ static char buf[TC_BUF_MAX];
+
+ TC_MODULE_SELF_CHECK(self, "inspect");
+ TC_MODULE_SELF_CHECK(param, "inspect");
+ TC_MODULE_SELF_CHECK(value, "inspect");
+
+ pd = self->userdata;
+
+ if (optstr_lookup(param, "help")) {
+ tc_snprintf(buf, sizeof(buf),
+ "Overview:\n"
+ " Decodes NuppelVideo streams.\n"
+ "Options available: None.\n");
+ *value = buf;
+ }
+ return TC_IMPORT_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * nuv_demultiplex: Demultiplex a frame of data. See tcmodule-data.h for
+ * function details.
+ */
+
+static int nuv_demultiplex(TCModuleInstance *self,
+ vframe_list_t *vframe, aframe_list_t *aframe)
+{
+ PrivateData *pd;
+ double timestamp;
+ uint8_t *audiobuf = NULL;
+ int audiolen = 0;
+
+ TC_MODULE_SELF_CHECK(self, "demultiplex");
+
+ pd = self->userdata;
+ if (pd->fd < 0) {
+ tc_log_error(MOD_NAME, "demultiplex: no file opened!");
+ return TC_ERROR;
+ }
+
+ /* Loop reading packets until we have a video frame. */
+
+ while (!pd->have_vframe) {
+ struct rtframeheader hdr;
+
+ if (read(pd->fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ if (verbose & TC_DEBUG)
+ tc_log_info(MOD_NAME, "End of file reached");
+ tc_free(audiobuf);
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+
+ /* Check for a compressor data (DR) packet */
+ if (hdr.frametype == 'D' && hdr.comptype == 'R') {
+ if (hdr.packetlength < sizeof(pd->cdata)) {
+ tc_log_warn(MOD_NAME, "Short compressor data packet");
+ tc_free(audiobuf);
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ if (read(pd->fd, pd->cdata, sizeof(pd->cdata))
+ != sizeof(pd->cdata)
+ ) {
+ tc_log_warn(MOD_NAME,
+ "File truncated in compressor data packet");
+ tc_free(audiobuf);
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ hdr.packetlength -= sizeof(pd->cdata);
+ }
+
+ /* Check for an audio sync (SA) packet */
+ if (hdr.frametype == 'S' && hdr.comptype == 'A') {
+ pd->audiorate = (double)hdr.timecode / 100;
+ }
+
+ /* Check for a seekpoint (R) packet, and set its length to zero
+ * (seekpoint packets don't have a valid length field) */
+ if (hdr.frametype == 'R') {
+ hdr.packetlength = 0;
+ }
+
+ /* Check for and read an audio (A) packet */
+ if (hdr.frametype == 'A' && hdr.packetlength > 0) {
+ if (hdr.comptype != '0') {
+ tc_log_warn(MOD_NAME, "Unsupported audio compression %c",
+ hdr.comptype);
+ tc_free(audiobuf);
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ audiobuf = tc_realloc(audiobuf, audiolen + hdr.packetlength);
+ if (!audiobuf) {
+ tc_log_error(MOD_NAME, "No memory for audio!");
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ if (read(pd->fd, audiobuf+audiolen, hdr.packetlength)
+ != hdr.packetlength
+ ) {
+ tc_log_warn(MOD_NAME, "File truncated in audio packet");
+ tc_free(audiobuf);
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ audiolen += hdr.packetlength;
+ hdr.packetlength = 0;
+ }
+
+ /* Check for a video (V) packet */
+ if (hdr.frametype == 'V') {
+ memcpy(&pd->framehdr, &hdr, sizeof(hdr));
+ pd->have_vframe = 1;
+ /* We read the data later, so avoid skipping it now */
+ hdr.packetlength = 0;
+ }
+
+ /* Skip anything that's not processed above */
+ while (hdr.packetlength > 0) {
+ char buf[0x1000];
+ int toread = hdr.packetlength;
+ if (toread > sizeof(buf))
+ toread = sizeof(buf);
+ if (read(pd->fd, buf, toread) != toread) {
+ tc_log_warn(MOD_NAME, "File truncated in skipped packet");
+ tc_free(audiobuf);
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ hdr.packetlength -= toread;
+ }
+
+ } // while (!pd->have_vframe)
+
+ /* Now we have a video packet. First take care of audio processing. */
+ if (aframe) {
+ if (pd->audiorate == NUV_ARATE) {
+ /* No resampling needed, just copy */
+ ac_memcpy(aframe->audio_buf, audiobuf, audiolen);
+ aframe->audio_size = audiolen;
+ } else {
+ /* Resample data to NUV_ARATE samples per second */
+ int16_t *in = (int16_t *)audiobuf;
+ int16_t *out = (int16_t *)aframe->audio_buf;
+ int inpos = 0, outpos = 0;
+
+ while (pd->audiofrac >= 1 && inpos < audiolen/2) {
+ inpos += 2;
+ pd->audiofrac -= 1;
+ }
+ while (inpos < audiolen/2) {
+ out[outpos ] = in[ inpos ] * (1-pd->audiofrac)
+ + in[(inpos+2) ] * pd->audiofrac;
+ out[outpos+1] = in[ inpos +1] * (1-pd->audiofrac)
+ + in[(inpos+2)+1] * pd->audiofrac;
+ pd->audiofrac += pd->audiorate / NUV_ARATE;
+ while (pd->audiofrac >= 1 && inpos < audiolen/2) {
+ inpos += 2;
+ pd->audiofrac -= 1;
+ }
+ outpos += 2;
+ }
+ aframe->audio_size = outpos*2;
+ }
+ aframe->a_rate = NUV_ARATE;
+ aframe->a_bits = 16;
+ aframe->a_chan = 2;
+ tc_free(audiobuf);
+ audiobuf = NULL;
+ }
+
+ /* Check the timecode on the video frame and read or clone as needed. */
+ timestamp = (double)pd->framehdr.timecode / 1000;
+ if (pd->framenum == 0) {
+ /* This is the first frame we've seen; treat the timestamp as zero,
+ * and offset subsequent timestamps by the same amount. This is
+ * needed to handle multi-part capture files, where the timestamp
+ * may not start at zero. (From S. Hosgood, January 2007) */
+ pd->tsoffset = timestamp;
+ }
+ if (verbose & TC_DEBUG) {
+ tc_log_msg(MOD_NAME,"<<< frame=%d[%.3f] timestamp=%.3f-%.3f >>>",
+ pd->framenum, pd->framenum/pd->fps, timestamp, pd->tsoffset);
+ }
+ if ((timestamp - pd->tsoffset) < (pd->framenum+0.5)/pd->fps) {
+ if (pd->framehdr.comptype != 'L') { // 'L'ast frame: keep saved data
+ if (pd->framehdr.packetlength > 0) {
+ if (read(pd->fd, pd->saved_vframe, pd->framehdr.packetlength)
+ != pd->framehdr.packetlength
+ ) {
+ tc_log_warn(MOD_NAME, "File truncated in video packet");
+ nuv_stop(self);
+ return TC_ERROR;
+ }
+ }
+ pd->saved_vframelen = pd->framehdr.packetlength;
+ pd->saved_vcomptype = pd->framehdr.comptype;
+ }
+ pd->have_vframe = 0;
+ } else {
+ if (verbose & TC_DEBUG) {
+ tc_log_warn(MOD_NAME, "(frame %d) Dropped frame(s) or bad A/V"
+ " sync, cloning last frame", pd->framenum);
+ }
+ }
+
+ /* Copy the video frame to the destination buffer and return. */
+ if (vframe) {
+ vframe->video_buf[0] = pd->width>>8;
+ vframe->video_buf[1] = pd->width;
+ vframe->video_buf[2] = pd->height>>8;
+ vframe->video_buf[3] = pd->height;
+ vframe->video_buf[4] = pd->saved_vcomptype;
+ ac_memcpy(vframe->video_buf+5, pd->cdata, sizeof(pd->cdata));
+ ac_memcpy(vframe->video_buf+5+sizeof(pd->cdata), pd->saved_vframe,
+ pd->saved_vframelen);
+ vframe->video_size = pd->saved_vframelen+5+sizeof(pd->cdata);
+ vframe->v_codec = TC_CODEC_NUV;
+ }
+ pd->framenum++;
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * nuv_decode_video: Decode a frame of data. See tcmodule-data.h for
+ * function details.
+ */
+
+static int nuv_decode_video(TCModuleInstance *self,
+ vframe_list_t *inframe, vframe_list_t *outframe)
+{
+ PrivateData *pd;
+ uint8_t comptype, *encoded_frame;
+ int in_framesize, out_framesize;
+ int free_frame = 0; // set to 1 if we need to free the decompress buffer
+
+ TC_MODULE_SELF_CHECK(self, "decode_video");
+ TC_MODULE_SELF_CHECK(inframe, "decode_video");
+ TC_MODULE_SELF_CHECK(outframe, "decode_video");
+
+ pd = self->userdata;
+
+ if (!pd->dec_initted) {
+ pd->width = inframe->video_buf[0]<<8 | inframe->video_buf[1];
+ pd->height = inframe->video_buf[2]<<8 | inframe->video_buf[3];
+ RTjpeg_init_decompress((uint32_t *)(inframe->video_buf+5),
+ pd->width, pd->height);
+ pd->dec_initted = 1;
+ }
+
+ comptype = inframe->video_buf[4];
+ encoded_frame = inframe->video_buf+5+sizeof(pd->cdata);
+ in_framesize = inframe->video_size-5-sizeof(pd->cdata);
+ out_framesize = pd->width*pd->height + (pd->width/2)*(pd->height/2)*2;
+
+ if (comptype == '2' || comptype == '3') {
+ /* Undo LZO compression */
+ uint8_t *decompressed_frame;
+ lzo_uint len;
+ decompressed_frame = tc_malloc(out_framesize);
+ if (!decompressed_frame) {
+ tc_log_error(MOD_NAME, "No memory for decompressed frame!");
+ return TC_ERROR;
+ }
+ if (lzo1x_decompress(encoded_frame, in_framesize,
+ decompressed_frame, &len, NULL) == LZO_E_OK) {
+ encoded_frame = decompressed_frame;
+ in_framesize = len;
+ free_frame = 1;
+ } else {
+ tc_log_warn(MOD_NAME, "Unable to decompress video frame");
+ tc_free(decompressed_frame);
+ /* And try it as raw, just like rtjpeg_vid_plugin */
+ }
+ /* Convert 2 -> 1, 3 -> 0 */
+ comptype ^= 3;
+ }
+
+ switch (comptype) {
+
+ case '0': // Uncompressed YUV
+ if (in_framesize > out_framesize)
+ in_framesize = out_framesize;
+ ac_memcpy(outframe->video_buf, encoded_frame, in_framesize);
+ break;
+
+ case '1': // RTjpeg-compressed data
+ RTjpeg_decompressYUV420((int8_t *)encoded_frame, outframe->video_buf);
+ break;
+
+ case 'N': // Black frame
+ memset(outframe->video_buf, 0, pd->width*pd->height);
+ memset(outframe->video_buf + pd->width*pd->height, 128,
+ (pd->width/2)*(pd->height/2)*2);
+ break;
+
+ case 'L': // Repeat last frame--leave videobuf alone
+ // Should have been handled by demux!
+ tc_log_warn(MOD_NAME, "BUG: 'L' frame not handled!");
+ break;
+
+ default:
+ tc_log_warn(MOD_NAME, "Unknown video compression type %c (%02X)",
+ comptype >= ' ' && comptype <= '=' ? comptype : '?',
+ comptype);
+ break;
+
+ } // switch (comptype)
+
+ if (free_frame)
+ tc_free(encoded_frame);
+ outframe->video_size = out_framesize;
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+static const TCCodecID nuv_codecs_in[] = { TC_CODEC_NUV, TC_CODEC_ERROR };
+static const TCCodecID nuv_codecs_out[] = { TC_CODEC_YUV420P, TC_CODEC_ERROR };
+
+static const TCFormatID nuv_formats_in[] = { TC_FORMAT_NUV, TC_FORMAT_ERROR };
+static const TCFormatID nuv_formats_out[] = { TC_FORMAT_ERROR };
+
+static const TCModuleInfo nuv_info = {
+ .features = MOD_FEATURES,
+ .flags = MOD_FLAGS,
+ .name = MOD_NAME,
+ .version = MOD_VERSION,
+ .description = MOD_CAP,
+ .codecs_in = nuv_codecs_in,
+ .codecs_out = nuv_codecs_out,
+ .formats_in = nuv_formats_in,
+ .formats_out = nuv_formats_out
+};
+
+static const TCModuleClass nuv_class = {
+ TC_MODULE_CLASS_HEAD(nuv),
+
+ .init = nuv_init,
+ .fini = nuv_fini,
+ .configure = nuv_configure,
+ .stop = nuv_stop,
+ .inspect = nuv_inspect,
+
+ .decode_video = nuv_decode_video,
+ .demultiplex = nuv_demultiplex,
+};
+
+TC_MODULE_ENTRY_POINT(nuv)
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Old-fashioned module interface. */
+
+#define MOD_CODEC "(video) YUV | (audio) PCM"
+
+static int verbose_flag = TC_QUIET;
+static int capability_flag = TC_CAP_YUV | TC_CAP_PCM;
+
+#define MOD_PRE nuv
+#include "import_def.h"
+
+static TCModuleInstance mod_video, mod_audio;
+
+/*************************************************************************/
+
+/* Open stream. */
+
+MOD_open
+{
+ TCModuleInstance *mod = NULL;
+
+ if (param->flag == TC_VIDEO) {
+ mod = &mod_video;
+ } else if (param->flag == TC_AUDIO) {
+ mod = &mod_audio;
+ } else {
+ return TC_ERROR;
+ }
+
+ /* XXX */
+ if (nuv_init(mod, TC_MODULE_FEATURE_VIDEO) < 0)
+ return TC_ERROR;
+ if (nuv_configure(mod, "", vob) < 0) {
+ nuv_fini(mod);
+ return TC_ERROR;
+ }
+
+ param->fd = NULL; /* we handle the reading ourselves */
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/* Close stream. */
+
+MOD_close
+{
+ TCModuleInstance *mod = NULL;
+
+ if (param->flag == TC_VIDEO) {
+ mod = &mod_video;
+ } else if (param->flag == TC_AUDIO) {
+ mod = &mod_audio;
+ } else {
+ return TC_ERROR;
+ }
+ return nuv_fini(mod);
+}
+
+/*************************************************************************/
+
+/* Decode stream. */
+
+MOD_decode
+{
+ TCModuleInstance *mod = NULL;
+ PrivateData *pd = NULL;
+
+ if (param->flag == TC_VIDEO) {
+ mod = &mod_video;
+ } else if (param->flag == TC_AUDIO) {
+ mod = &mod_audio;
+ } else {
+ return TC_ERROR;
+ }
+ pd = mod->userdata;
+
+ if (pd->fd < 0) {
+ tc_log_error(MOD_NAME, "No file open in decode!");
+ return TC_ERROR;
+ }
+
+ if (param->flag == TC_VIDEO) {
+ vframe_list_t vframe1, vframe2;
+ static uint8_t tempvbuf[TC_MAX_V_FRAME_WIDTH*TC_MAX_V_FRAME_HEIGHT*3];
+ vframe1.video_buf = tempvbuf;
+ vframe2.video_buf = param->buffer;
+ if (param->attributes & TC_FRAME_IS_OUT_OF_RANGE) {
+ if (nuv_demultiplex(mod, &vframe2, NULL) < 0)
+ return TC_ERROR;
+ } else {
+ if (nuv_demultiplex(mod, &vframe1, NULL) < 0)
+ return TC_ERROR;
+ if (nuv_decode_video(mod, &vframe1, &vframe2) < 0)
+ return TC_ERROR;
+ }
+ param->size = vframe2.video_size;
+ } else if (param->flag == TC_AUDIO) {
+ aframe_list_t aframe;
+ aframe.audio_buf = param->buffer;
+ if (nuv_demultiplex(mod, NULL, &aframe) < 0)
+ return TC_ERROR;
+ param->size = aframe.audio_size;
+ }
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/*
+ * Local variables:
+ * c-file-style: "stroustrup"
+ * c-file-offsets: ((case-label . *) (statement-case-intro . *))
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+ */