You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
469 lines
12 KiB
469 lines
12 KiB
4 years ago
|
/*
|
||
|
* export_mpeg2enc.c
|
||
|
*
|
||
|
* Copyright (C) Gerhard Monzel - January 2002
|
||
|
*
|
||
|
* This file is part of transcode, a video stream processing tool
|
||
|
*
|
||
|
* transcode is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||
|
* any later version.
|
||
|
*
|
||
|
* transcode is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with GNU Make; see the file COPYING. If not, write to
|
||
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#define MOD_NAME "export_mpeg2enc.so"
|
||
|
#define MOD_VERSION "v1.1.10 (2003-10-30)"
|
||
|
#define MOD_CODEC "(video) MPEG 1/2"
|
||
|
|
||
|
#include "transcode.h"
|
||
|
#include "libtcvideo/tcvideo.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
|
||
|
#if defined(HAVE_MJPEGTOOLS_INC)
|
||
|
#include "yuv4mpeg.h"
|
||
|
#include "mpegconsts.h"
|
||
|
#else
|
||
|
#include "mjpegtools/yuv4mpeg.h"
|
||
|
#include "mjpegtools/mpegconsts.h"
|
||
|
#endif
|
||
|
|
||
|
static int verbose_flag=TC_QUIET;
|
||
|
static int capability_flag=TC_CAP_YUV|TC_CAP_RGB;
|
||
|
|
||
|
#define MOD_PRE mpeg2enc
|
||
|
#include "export_def.h"
|
||
|
|
||
|
static y4m_stream_info_t y4mstream;
|
||
|
|
||
|
static FILE *sa_ip = NULL;
|
||
|
static int sa_width = 0;
|
||
|
static int sa_height = 0;
|
||
|
static int sa_size_l = 0;
|
||
|
static int sa_size_c = 0;
|
||
|
static TCVHandle tcvhandle = 0;
|
||
|
static ImageFormat srcfmt;
|
||
|
|
||
|
#define Y4M_LINE_MAX 256
|
||
|
#define Y4M_MAGIC "YUV4MPEG2"
|
||
|
#define Y4M_FRAME_MAGIC "FRAME"
|
||
|
|
||
|
|
||
|
static int y4m_snprint_xtags(char *s, int maxn, y4m_xtag_list_t *xtags)
|
||
|
{
|
||
|
int i, room;
|
||
|
|
||
|
for (i = 0, room = maxn - 1; i < y4m_xtag_count(xtags); i++) {
|
||
|
int n = tc_snprintf(s, room + 1, " %s", y4m_xtag_get(xtags, i));
|
||
|
if ((n < 0) || (n > room)) return Y4M_ERR_HEADER;
|
||
|
s += n;
|
||
|
room -= n;
|
||
|
}
|
||
|
s[0] = '\n'; /* finish off header with newline */
|
||
|
s[1] = '\0'; /* ...and end-of-string */
|
||
|
return Y4M_OK;
|
||
|
}
|
||
|
|
||
|
static int y4m_write_stream_header2(FILE *fd, y4m_stream_info_t *i)
|
||
|
{
|
||
|
char s[Y4M_LINE_MAX+1];
|
||
|
int n;
|
||
|
int err;
|
||
|
|
||
|
y4m_ratio_t tmpframerate = y4m_si_get_framerate(i);
|
||
|
y4m_ratio_t tmpsamplerate = y4m_si_get_sampleaspect(i);
|
||
|
y4m_ratio_reduce(&tmpframerate);
|
||
|
y4m_ratio_reduce(&tmpsamplerate);
|
||
|
n = tc_snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d",
|
||
|
Y4M_MAGIC,
|
||
|
y4m_si_get_width(i),
|
||
|
y4m_si_get_height(i),
|
||
|
y4m_si_get_framerate(i).n, y4m_si_get_framerate(i).d,
|
||
|
(y4m_si_get_interlace(i) == Y4M_ILACE_NONE) ? "p" :
|
||
|
(y4m_si_get_interlace(i) == Y4M_ILACE_TOP_FIRST) ? "t" :
|
||
|
(y4m_si_get_interlace(i) == Y4M_ILACE_BOTTOM_FIRST) ? "b" : "?",
|
||
|
y4m_si_get_sampleaspect(i).n, y4m_si_get_sampleaspect(i).d);
|
||
|
if (n < 0) return Y4M_ERR_HEADER;
|
||
|
if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, y4m_si_xtags(i)))
|
||
|
!= Y4M_OK)
|
||
|
return err;
|
||
|
/* zero on error */
|
||
|
return (fwrite(s, strlen(s), 1, fd) ? Y4M_OK : Y4M_ERR_SYSTEM);
|
||
|
|
||
|
}
|
||
|
|
||
|
static int y4m_write_frame_header2(FILE *fd, y4m_frame_info_t *i)
|
||
|
{
|
||
|
char s[Y4M_LINE_MAX+1];
|
||
|
int n;
|
||
|
int err;
|
||
|
|
||
|
n = snprintf(s, sizeof(s), "%s", Y4M_FRAME_MAGIC);
|
||
|
if (n < 0) return Y4M_ERR_HEADER;
|
||
|
if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, y4m_fi_xtags(i)))
|
||
|
!= Y4M_OK)
|
||
|
return err;
|
||
|
/* zero on error */
|
||
|
return (fwrite(s, strlen(s), 1, fd) ? Y4M_OK : Y4M_ERR_SYSTEM);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* open outputfile
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
MOD_open
|
||
|
{
|
||
|
|
||
|
int verb, prof=0;
|
||
|
const char *p1, *p2, *p3, *p4;
|
||
|
char bitrate[25];
|
||
|
//char dar_tag[20];
|
||
|
y4m_ratio_t framerate;
|
||
|
y4m_ratio_t dar;
|
||
|
int frc=0, asr=0;
|
||
|
char *tv_type="-n p";
|
||
|
char *pulldown="";
|
||
|
int fields = (vob->encode_fields == TC_ENCODE_FIELDS_TOP_FIRST
|
||
|
|| vob->encode_fields == TC_ENCODE_FIELDS_BOTTOM_FIRST);
|
||
|
|
||
|
/* check for mpeg2enc */
|
||
|
if (tc_test_program("mpeg2enc") != 0) return (TC_EXPORT_ERROR);
|
||
|
|
||
|
if(param->flag == TC_VIDEO)
|
||
|
{
|
||
|
char buf[PATH_MAX];
|
||
|
char buf2[16];
|
||
|
|
||
|
//note: this is the real framerate of the raw stream
|
||
|
framerate = (vob->ex_frc==0) ? mpeg_conform_framerate(vob->ex_fps):mpeg_framerate(vob->ex_frc);
|
||
|
asr = (vob->ex_asr<0) ? vob->im_asr:vob->ex_asr;
|
||
|
switch (asr) {
|
||
|
case 1: dar.n = 1; dar.d = 1; break;
|
||
|
case 2: dar = y4m_dar_4_3; break;
|
||
|
case 3: dar = y4m_dar_16_9; break;
|
||
|
case 4: dar = y4m_dar_221_100; break;
|
||
|
case 0: default: dar.n=0; dar.d=0; break;
|
||
|
}
|
||
|
|
||
|
y4m_init_stream_info(&y4mstream);
|
||
|
y4m_si_set_framerate(&y4mstream,framerate);
|
||
|
if (vob->encode_fields == TC_ENCODE_FIELDS_TOP_FIRST) {
|
||
|
y4m_si_set_interlace(&y4mstream, Y4M_ILACE_TOP_FIRST);
|
||
|
} else if (vob->encode_fields == TC_ENCODE_FIELDS_BOTTOM_FIRST) {
|
||
|
y4m_si_set_interlace(&y4mstream, Y4M_ILACE_BOTTOM_FIRST);
|
||
|
} else if (vob->encode_fields == TC_ENCODE_FIELDS_PROGRESSIVE) {
|
||
|
y4m_si_set_interlace(&y4mstream, Y4M_ILACE_NONE);
|
||
|
}
|
||
|
y4m_si_set_sampleaspect(&y4mstream, y4m_guess_sar(vob->ex_v_width, vob->ex_v_height, dar));
|
||
|
/*
|
||
|
tc_snprintf( dar_tag, 19, "XM2AR%03d", asr );
|
||
|
y4m_xtag_add( y4m_si_xtags(&y4mstream), dar_tag );
|
||
|
*/
|
||
|
y4m_si_set_height(&y4mstream, vob->ex_v_height);
|
||
|
y4m_si_set_width(&y4mstream, vob->ex_v_width);
|
||
|
|
||
|
verb = (verbose & TC_DEBUG) ? 2:0;
|
||
|
|
||
|
//base profile support and coustom setting
|
||
|
//-- -F "<base-profile>[,<options_string>]"
|
||
|
//-- parameter 1 (base profile) --
|
||
|
|
||
|
p1 = vob->ex_v_fcc;
|
||
|
p2 = vob->ex_a_fcc;
|
||
|
p3 = vob->ex_profile_name; //unsupported
|
||
|
|
||
|
if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "P1=%s, P2=%s, P3=%s", p1, p2, p3);
|
||
|
|
||
|
prof = (p1==NULL || strlen(p1) == 0) ? 0:atoi(p1);
|
||
|
|
||
|
|
||
|
//-- adjust frame rate stuff --
|
||
|
//-----------------------------
|
||
|
if (vob->ex_frc) { // use specified output frame rate code
|
||
|
frc = vob->ex_frc;
|
||
|
} else { // otherwise we guess based on the frame rate
|
||
|
if ((int)(vob->ex_fps*100.0 + 0.01) == (int)(29.97*100.0)) {
|
||
|
frc=4;
|
||
|
} else if ((int)(vob->ex_fps*100.0 + 0.01) == (int)(23.97*100.0)) {
|
||
|
frc=1;
|
||
|
} else if ((int)(vob->ex_fps*100.0 + 0.01) == (int)(24.00*100.0)) {
|
||
|
frc=2;
|
||
|
} else {
|
||
|
frc=3; // default is PAL framerate code
|
||
|
}
|
||
|
}
|
||
|
// now set the stream type to either NTSC or PAL based on the
|
||
|
// frame rate code
|
||
|
if ((frc == 4) || (frc == 1) || (frc == 2)) {
|
||
|
tv_type = "-n n";
|
||
|
} else {
|
||
|
tv_type = "-n p"; // default is PAL
|
||
|
}
|
||
|
|
||
|
//ThOe pulldown?
|
||
|
if(vob->pulldown) pulldown="-p";
|
||
|
|
||
|
//ThOe collect additional parameter
|
||
|
if(asr>0)
|
||
|
tc_snprintf(buf2, sizeof(buf2), "%s %s -a %d", tv_type, pulldown, asr);
|
||
|
else
|
||
|
tc_snprintf(buf2, sizeof(buf2), "%s %s", tv_type, pulldown);
|
||
|
|
||
|
if (p2==NULL) p2="";
|
||
|
|
||
|
// additional commandline arguments
|
||
|
if (vob->ex_v_string==NULL) p4="";
|
||
|
else p4=vob->ex_v_string;
|
||
|
|
||
|
// constant quantizer encoding?
|
||
|
if (vob->divxmultipass == 3) {
|
||
|
if (vob->video_max_bitrate != 0) {
|
||
|
tc_snprintf(bitrate, sizeof(bitrate), "-q %d -b %d", vob->divxbitrate, vob->video_max_bitrate);
|
||
|
} else {
|
||
|
tc_snprintf(bitrate, sizeof(bitrate), "-q %d", vob->divxbitrate);
|
||
|
}
|
||
|
} else {
|
||
|
tc_snprintf(bitrate, sizeof(bitrate), "-b %d", vob->divxbitrate);
|
||
|
}
|
||
|
|
||
|
|
||
|
switch(prof) {
|
||
|
|
||
|
case 1:
|
||
|
|
||
|
//Standard VCD. An MPEG1 profile
|
||
|
//exactly to the VCD2.0 specification.
|
||
|
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 1 -F %d %s %s -o \"%s\" %s", verb, fields, frc, buf2, p4, vob->video_out_file, p2);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
|
||
|
//User VCD
|
||
|
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 2 -4 2 -2 3 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
|
||
|
//Generic MPEG2
|
||
|
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 3 -4 2 -2 3 %s -s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
|
||
|
//Standard SVCD. An MPEG-2 profile
|
||
|
//exactly to the SVCD2.0 specification
|
||
|
|
||
|
if ( !(vob->export_attributes & TC_EXPORT_ATTRIBUTE_VBITRATE) )
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 4 -F %d %s -o \"%s\" %s %s", verb, fields, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
else
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 4 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
break;
|
||
|
|
||
|
case 5:
|
||
|
|
||
|
//User SVCD
|
||
|
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 5 -4 2 -2 3 %s -F %d %s -V 230 -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
break;
|
||
|
|
||
|
case 6:
|
||
|
|
||
|
// Manual parameter mode.
|
||
|
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d %s -o \"%s\" %s %s", verb, fields, bitrate, vob->video_out_file, p2?p2:"", p4);
|
||
|
break;
|
||
|
|
||
|
case 8:
|
||
|
|
||
|
//DVD
|
||
|
|
||
|
if ( !(vob->export_attributes & TC_EXPORT_ATTRIBUTE_VBITRATE) )
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 8 -F %d %s -o \"%s\" %s %s", verb, fields, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
else
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 8 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
|
||
|
break;
|
||
|
|
||
|
|
||
|
case 0:
|
||
|
default:
|
||
|
|
||
|
//Generic MPEG1
|
||
|
|
||
|
tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 0 -4 2 -2 3 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
tc_log_info(MOD_NAME, "%s", buf);
|
||
|
|
||
|
sa_ip = popen(buf, "w");
|
||
|
if (!sa_ip) return(TC_EXPORT_ERROR);
|
||
|
|
||
|
if( y4m_write_stream_header2( sa_ip, &y4mstream ) != Y4M_OK ){
|
||
|
tc_log_perror(MOD_NAME, "write stream header");
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
// tc_snprintf(buf, sizeof(buf), MENC_HDR, sa_width, sa_height);
|
||
|
//fwrite(buf, strlen(buf), 1, sa_ip);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
if(param->flag == TC_AUDIO) return(0);
|
||
|
|
||
|
// invalid flag
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* init codec
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
MOD_init
|
||
|
{
|
||
|
|
||
|
if(param->flag == TC_VIDEO)
|
||
|
{
|
||
|
int prof = 0;
|
||
|
|
||
|
sa_width = vob->ex_v_width;
|
||
|
sa_height = vob->ex_v_height;
|
||
|
sa_size_l = sa_width * sa_height;
|
||
|
sa_size_c = sa_size_l/4;
|
||
|
|
||
|
if (vob->im_v_codec == CODEC_YUV) {
|
||
|
srcfmt = IMG_YUV_DEFAULT;
|
||
|
} else if (vob->im_v_codec == CODEC_YUV422) {
|
||
|
srcfmt = IMG_YUV422P;
|
||
|
} else if (vob->im_v_codec == CODEC_RGB) {
|
||
|
srcfmt = IMG_RGB_DEFAULT;
|
||
|
} else {
|
||
|
tc_log_warn(MOD_NAME, "unsupported video format %d",
|
||
|
vob->im_v_codec);
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
if (!(tcvhandle = tcv_init())) {
|
||
|
tc_log_warn(MOD_NAME, "image conversion init failed");
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
if (vob->ex_v_fcc) prof = atoi(vob->ex_v_fcc);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
if(param->flag == TC_AUDIO) return(0);
|
||
|
|
||
|
// invalid flag
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* encode and export frame
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
MOD_encode
|
||
|
{
|
||
|
y4m_frame_info_t info;
|
||
|
|
||
|
if(param->flag == TC_VIDEO)
|
||
|
{
|
||
|
vob_t *vob = tc_get_vob();
|
||
|
|
||
|
if (!tcv_convert(tcvhandle, param->buffer, param->buffer,
|
||
|
vob->ex_v_width, vob->ex_v_height,
|
||
|
srcfmt, IMG_YUV420P)) {
|
||
|
tc_log_warn(MOD_NAME, "image format conversion failed");
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
y4m_init_frame_info(&info);
|
||
|
|
||
|
if( y4m_write_frame_header2( sa_ip, &info ) != Y4M_OK ){
|
||
|
tc_log_perror(MOD_NAME, "write stream header");
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
fwrite(param->buffer, sa_size_l, 1, sa_ip);
|
||
|
fwrite(param->buffer + sa_size_l, sa_size_c, 1, sa_ip);
|
||
|
fwrite(param->buffer + sa_size_l + sa_size_c, sa_size_c, 1, sa_ip);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
if(param->flag == TC_AUDIO) return(0);
|
||
|
|
||
|
// invalid flag
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* stop encoder
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
MOD_stop
|
||
|
{
|
||
|
if(param->flag == TC_VIDEO) {
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
if(param->flag == TC_AUDIO) return (0);
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* close codec
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
MOD_close
|
||
|
{
|
||
|
|
||
|
if(param->flag == TC_AUDIO) return (0);
|
||
|
|
||
|
if(param->flag == TC_VIDEO)
|
||
|
{
|
||
|
if (sa_ip) pclose(sa_ip);
|
||
|
sa_ip = NULL;
|
||
|
|
||
|
tcv_free(tcvhandle);
|
||
|
tcvhandle = 0;
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
return(TC_EXPORT_ERROR);
|
||
|
}
|
||
|
|