mpv/libmpdemux/muxer_mpeg.c

2690 lines
70 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "config.h"
#include "version.h"
#include "mp_msg.h"
#include "help_mp.h"
#include "aviheader.h"
#include "ms_hdr.h"
#include "stream.h"
#include "muxer.h"
#include "demuxer.h"
#include "stheader.h"
#include "m_option.h"
#include "mpeg_hdr.h"
#include "mp3_hdr.h"
#include "liba52/a52.h"
#define PACK_HEADER_START_CODE 0x01ba
#define SYSTEM_HEADER_START_CODE 0x01bb
#define PSM_START_CODE 0x01bc
#define PES_PRIVATE1 0x01bd
#define PES_PRIVATE2 0x01bf
#define MUX_MPEG1 1
#define MUX_MPEG2 2
#define VIDEO_MPEG1 0x10000001
#define VIDEO_MPEG2 0x10000002
#define AUDIO_MP2 0x50
#define AUDIO_MP3 0x55
#define AUDIO_A52 0x2000
#define AUDIO_LPCM 0x10001 /* only a placeholder at the moment */
#define AUDIO_AAC1 0x706D
//#define AUDIO_AAC2 (short) mmioFOURCC('m','p','4','a')
#define AUDIO_AAC2 0x504D
#define ASPECT_1_1 1
#define ASPECT_4_3 2
#define ASPECT_16_9 3
#define ASPECT_2_21_1 4
#define FRAMERATE_23976 1
#define FRAMERATE_24 2
#define FRAMERATE_25 3
#define FRAMERATE_2997 4
#define FRAMERATE_30 5
#define FRAMERATE_50 6
#define FRAMERATE_5994 7
#define FRAMERATE_60 8
static char ftypes[] = {'?', 'I', 'P', 'B'};
#define FTYPE(x) (ftypes[(x)])
#define MAX_PATTERN_LENGTH 2000000
static unsigned char bff_mask[MAX_PATTERN_LENGTH]; //2 million frames are enough
static const char *framerates[] = {
"unchanged", "23.976", "24", "25", "29.97", "30", "50", "59.94", "60"
};
static const char *aspect_ratios[] = {
"unchanged", "1/1", "4/3", "16/9", "2.21/1"
};
static char *conf_mux = "mpeg2";
static uint16_t conf_packet_size = 0; //dvd
static uint32_t conf_muxrate = 0; //kb/s
static float conf_vaspect = 0;
static float conf_vframerate = 0;
static uint32_t conf_vwidth = 0, conf_vheight = 0, conf_panscan_width = 0, conf_panscan_height = 0;
static uint32_t conf_vbitrate = 0;
static int conf_init_vpts = 200, conf_init_apts = 200;
static int conf_ts_allframes = 0;
static int conf_init_adelay = 0;
static int conf_abuf_size = 0;
static int conf_vbuf_size = 0;
static int conf_drop = 0;
static int conf_telecine = 0;
static float conf_telecine_src = 0;
static float conf_telecine_dest = 0;
enum FRAME_TYPE {
I_FRAME = 1,
P_FRAME = 2,
B_FRAME = 3
};
typedef struct {
uint8_t *buffer;
size_t size;
size_t alloc_size;
uint8_t type;
uint64_t pts, dts, idur;
uint32_t pos; //start offset for the frame
} mpeg_frame_t;
typedef struct {
uint8_t cnt; // how many entries we use
struct {
uint8_t id, type;
uint32_t bufsize;
uint32_t format;
} streams[50]; //16 video + 16 audio mpa + 16 audio private + bd/bf for dvd
} sys_info_t;
typedef struct {
uint8_t cnt; // how many entries we use
struct {
uint8_t id;
uint8_t type;
uint32_t format;
} streams[50]; //16 video + 16 audio mpa + 16 audio private + bd/bf for dvd
} psm_info_t;
typedef struct {
int size;
uint64_t dts;
} buffer_track_t;
typedef struct {
uint64_t dts, pts;
uint64_t frame_dts, frame_pts;
int len, stflen;
} pack_stats_t;
typedef struct {
int mux;
sys_info_t sys_info;
psm_info_t psm_info;
uint16_t packet_size;
int is_dvd, is_xvcd, is_xsvcd, is_genmpeg1, is_genmpeg2, rawpes, ts_allframes, has_video, has_audio;
int update_system_header, use_psm;
off_t headers_size, data_size;
uint64_t scr;
uint64_t delta_scr;
uint32_t muxrate;
uint8_t *buff;
uint32_t headers_cnt;
double init_adelay;
int drop;
//video patching parameters
uint8_t vaspect, vframerate;
uint16_t vwidth, vheight, panscan_width, panscan_height;
uint32_t vbitrate;
int patch_seq, patch_sde;
int psm_streams_cnt;
} muxer_priv_t;
typedef struct {
int has_pts, has_dts, pes_is_aligned, type, min_pes_hlen, psm_fixed;
int delay_rff;
uint64_t pts, last_pts, last_dts, dts, size, frame_duration, delta_pts, nom_delta_pts, last_saved_pts;
uint32_t buffer_size;
double delta_clock, timer;
int drop_delayed_frames;
mpeg_frame_t *framebuf;
uint16_t framebuf_cnt;
uint16_t framebuf_used;
int32_t last_tr;
int max_tr;
uint8_t id, is_mpeg12, telecine;
uint64_t vframes;
int64_t display_frame;
mp_mpeg_header_t picture;
int max_buffer_size;
buffer_track_t *buffer_track;
int track_pos, track_len, track_bufsize; //pos and len control the array, bufsize is the size of the buffer
unsigned char *pack;
int pack_offset, pes_offset, pes_set, payload_offset;
int frames;
int last_frame_rest; //the rest of the previous frame
int is_ready;
int mpa_layer;
} muxer_headers_t;
#define PULLDOWN32 1
#define TELECINE_FILM2PAL 2
#define TELECINE_DGPULLDOWN 3
m_option_t mpegopts_conf[] = {
{"format", &(conf_mux), CONF_TYPE_STRING, M_OPT_GLOBAL, 0 ,0, NULL},
{"size", &(conf_packet_size), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 0, 65535, NULL},
{"muxrate", &(conf_muxrate), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 0, 12000000, NULL}, //12 Mb/s
{"vaspect", &(conf_vaspect), CONF_TYPE_FLOAT, M_OPT_GLOBAL, 0, 0, NULL},
{"vframerate", &(conf_vframerate), CONF_TYPE_FLOAT, M_OPT_GLOBAL, 0, 0, NULL},
{"vwidth", &(conf_vwidth), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 1, 4095, NULL},
{"vheight", &(conf_vheight), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 1, 4095, NULL},
{"vpswidth", &(conf_panscan_width), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 1, 16383, NULL},
{"vpsheight", &(conf_panscan_height), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 1, 16383, NULL},
{"vbitrate", &(conf_vbitrate), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 1, 104857599, NULL},
{"init_vpts", &(conf_init_vpts), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 100, 700, NULL}, //2*frametime at 60fps
{"init_apts", &(conf_init_apts), CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 100, 700, NULL},
{"vdelay", &conf_init_adelay, CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 1, 32760, NULL},
{"vbuf_size", &conf_vbuf_size, CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 40, 1194, NULL},
{"abuf_size", &conf_abuf_size, CONF_TYPE_INT, M_OPT_GLOBAL|M_OPT_RANGE, 4, 64, NULL},
{"drop", &conf_drop, CONF_TYPE_FLAG, M_OPT_GLOBAL, 0, 1, NULL},
{"tsaf", &conf_ts_allframes, CONF_TYPE_FLAG, M_OPT_GLOBAL, 0, 1, NULL},
{"telecine", &conf_telecine, CONF_TYPE_FLAG, M_OPT_GLOBAL, 0, PULLDOWN32, NULL},
{"film2pal", &conf_telecine, CONF_TYPE_FLAG, M_OPT_GLOBAL, 0, TELECINE_FILM2PAL, NULL},
{"tele_src", &(conf_telecine_src), CONF_TYPE_FLOAT, M_OPT_GLOBAL, 0, 0, NULL},
{"tele_dest", &(conf_telecine_dest), CONF_TYPE_FLOAT, M_OPT_GLOBAL, 0, 0, NULL},
{NULL, NULL, 0, 0, 0, 0, NULL}
};
static void fix_audio_sys_header(muxer_priv_t *priv, uint8_t id, uint8_t newid, uint32_t size)
{
uint8_t i;
for(i = 0; i < priv->sys_info.cnt; i++)
{
if(priv->sys_info.streams[i].id == id)
{
priv->sys_info.streams[i].id = newid;
priv->sys_info.streams[i].type = 1;
priv->sys_info.streams[i].bufsize = size;
}
}
}
static inline int is_mpeg1(uint32_t x)
{
return (
(x == 0x10000001) ||
(x == mmioFOURCC('m','p','g','1')) ||
(x == mmioFOURCC('M','P','G','1'))
);
}
static inline int is_mpeg2(uint32_t x)
{
return (
(x == 0x10000002) ||
(x == mmioFOURCC('m','p','g','2')) ||
(x == mmioFOURCC('M','P','G','2')) ||
(x == mmioFOURCC('m','p','e','g')) ||
(x == mmioFOURCC('M','P','E','G'))
);
}
static inline int is_mpeg4(uint32_t x)
{
return (
(x == 0x10000004) ||
(x == mmioFOURCC('d','i','v','x')) ||
(x == mmioFOURCC('D','I','V','X')) ||
(x == mmioFOURCC('x','v','i','d')) ||
(x == mmioFOURCC('X','V','I','D')) ||
(x == mmioFOURCC('X','v','i','D')) ||
(x == mmioFOURCC('x','v','i','x')) ||
(x == mmioFOURCC('X','V','I','X')) ||
(x == mmioFOURCC('m','p','4','v')) ||
(x == mmioFOURCC('M','P','4','V')) ||
(x == mmioFOURCC('F', 'M','P','4')) ||
(x == mmioFOURCC('f', 'm','p','4')) ||
(x == mmioFOURCC('D', 'X','5','0')) ||
(x == mmioFOURCC('d', 'x','5','0'))
);
}
//from unrarlib.c
static uint32_t CalcCRC32(uint8_t *buff, uint32_t size)
{
uint32_t i, j, CRCTab[256], crc;
for(i = 0;i < 256; i++)
{
for(crc = i, j = 0; j < 8; j++)
crc= (crc & 1) ? (crc >> 1)^0xEDB88320L : (crc >> 1);
CRCTab[i] = crc;
}
crc = 0xffffffff;
for(i = 0; i < size; i++)
crc = (crc << 8) ^ CRCTab[((crc >> 24) ^ buff[i]) & 0xff];
return crc;
}
static void add_to_psm(muxer_priv_t *priv, uint8_t id, uint32_t format)
{
uint8_t i;
i = priv->psm_info.cnt;
priv->psm_info.streams[i].id = id;
priv->psm_info.streams[i].format = format;
if(is_mpeg1(format))
priv->psm_info.streams[i].type = 0x01;
else if(is_mpeg2(format))
priv->psm_info.streams[i].type = 0x02;
else if(is_mpeg4(format))
priv->psm_info.streams[i].type = 0x10;
else if(format == AUDIO_MP2 || format == AUDIO_MP3)
priv->psm_info.streams[i].type = 0x03;
else if(format == AUDIO_AAC1 || format == AUDIO_AAC2)
priv->psm_info.streams[i].type = 0x0f;
else
priv->psm_info.streams[i].type = 0x81;
if(format == AUDIO_A52)
memcpy((char*) &(priv->psm_info.streams[i].format), "AC-3", 4);
priv->psm_info.cnt++;
}
static mpeg_frame_t *init_frames(uint16_t num, size_t size)
{
mpeg_frame_t *tmp;
uint16_t i;
tmp = (mpeg_frame_t *) calloc(num, sizeof(mpeg_frame_t));
if(tmp == NULL)
return NULL;
for(i=0; i < num; i++)
{
tmp[i].buffer = (uint8_t *) calloc(1, size);
if(tmp[i].buffer == NULL)
return NULL;
tmp[i].size = 0;
tmp[i].pos = 0;
tmp[i].alloc_size = size;
tmp[i].pts = 0;
}
return tmp;
}
static int add_frame(muxer_headers_t *spriv, uint64_t idur, uint8_t *ptr, int len, uint8_t pt, uint64_t dts, uint64_t pts);
static muxer_stream_t* mpegfile_new_stream(muxer_t *muxer,int type){
muxer_priv_t *priv = (muxer_priv_t*) muxer->priv;
muxer_stream_t *s;
muxer_headers_t *spriv;
if (!muxer) return NULL;
if(muxer->avih.dwStreams>=MUXER_MAX_STREAMS){
mp_msg(MSGT_MUXER, MSGL_ERR, "Too many streams! increase MUXER_MAX_STREAMS !\n");
return NULL;
}
switch (type) {
case MUXER_TYPE_VIDEO:
if (muxer->num_videos >= 16) {
mp_msg(MSGT_MUXER, MSGL_ERR, "MPEG files can't contain more than 16 video streams!\n");
return NULL;
}
break;
case MUXER_TYPE_AUDIO:
if (muxer->num_audios >= 16) {
mp_msg(MSGT_MUXER, MSGL_ERR, "MPEG files can't contain more than 16 audio streams!\n");
return NULL;
}
break;
default:
mp_msg(MSGT_MUXER, MSGL_ERR, "Unknown stream type!\n");
return NULL;
}
s = (muxer_stream_t*) calloc(1, sizeof(muxer_stream_t));
if(!s) return NULL; // no mem!?
if (!(s->b_buffer = malloc(priv->packet_size)))
goto init_fail;
s->b_buffer_size = priv->packet_size;
s->b_buffer_ptr = 0;
s->b_buffer_len = 0;
s->priv = (muxer_headers_t*) calloc(1, sizeof(muxer_headers_t));
if(s->priv == NULL)
goto init_fail;
spriv = (muxer_headers_t *) s->priv;
spriv->pack = malloc(priv->packet_size);
if(! spriv->pack)
goto init_fail;
spriv->buffer_track = calloc(1, 4096*sizeof(buffer_track_t));
if(!spriv->buffer_track)
goto init_fail;
spriv->track_pos = 0;
spriv->track_len = 4096;
muxer->streams[muxer->avih.dwStreams]=s;
s->type=type;
s->id=muxer->avih.dwStreams;
s->muxer=muxer;
if (type == MUXER_TYPE_VIDEO) {
spriv->type = 1;
spriv->last_pts = conf_init_vpts * 90 * 300;
spriv->id = 0xe0 + muxer->num_videos;
s->ckid = be2me_32 (0x100 + spriv->id);
if(priv->is_genmpeg1 || priv->is_genmpeg2) {
int v = (conf_vbuf_size ? conf_vbuf_size*1024 :
(s->h.dwSuggestedBufferSize ? s->h.dwSuggestedBufferSize : 46*1024));
int n = priv->sys_info.cnt;
priv->sys_info.streams[n].id = spriv->id;
priv->sys_info.streams[n].type = 1;
priv->sys_info.streams[n].bufsize = v;
priv->sys_info.cnt++;
}
muxer->num_videos++;
priv->has_video++;
s->h.fccType=streamtypeVIDEO;
if(!muxer->def_v) muxer->def_v=s;
spriv->framebuf_cnt = 30;
spriv->framebuf_used = 0;
spriv->framebuf = init_frames(spriv->framebuf_cnt, (size_t) 5000);
if(spriv->framebuf == NULL) {
mp_msg(MSGT_MUXER, MSGL_FATAL, "Couldn't allocate initial frames structure, abort!\n");
goto init_fail;
}
memset(&(spriv->picture), 0, sizeof(spriv->picture));
if(priv->is_xvcd)
spriv->min_pes_hlen = 18;
else if(priv->is_xsvcd)
spriv->min_pes_hlen = 22;
spriv->telecine = conf_telecine;
mp_msg (MSGT_MUXER, MSGL_DBG2, "Added video stream %d, ckid=%X\n", muxer->num_videos, s->ckid);
} else { // MUXER_TYPE_AUDIO
spriv->type = 0;
spriv->drop_delayed_frames = conf_drop;
spriv->last_pts = conf_init_apts * 90 * 300;
if(conf_init_adelay < 0 && ! spriv->drop_delayed_frames)
spriv->last_pts += (-conf_init_adelay) * 90 * 300;
spriv->pts = spriv->last_pts;
spriv->id = 0xc0 + muxer->num_audios;
s->ckid = be2me_32 (0x100 + spriv->id);
if(priv->is_genmpeg1 || priv->is_genmpeg2) {
int a1 = (conf_abuf_size ? conf_abuf_size*1024 :
(s->h.dwSuggestedBufferSize ? s->h.dwSuggestedBufferSize : 4*1024));
int n = priv->sys_info.cnt;
priv->sys_info.streams[n].id = spriv->id;
priv->sys_info.streams[n].type = 0;
priv->sys_info.streams[n].bufsize = a1;
priv->sys_info.cnt++;
}
if(priv->is_xvcd)
spriv->min_pes_hlen = 13;
else if(priv->is_xsvcd)
spriv->min_pes_hlen = 17;
muxer->num_audios++;
priv->has_audio++;
s->h.fccType=streamtypeAUDIO;
spriv->framebuf_cnt = 30;
spriv->framebuf_used = 0;
spriv->framebuf = init_frames(spriv->framebuf_cnt, (size_t) 2048);
if(spriv->framebuf == NULL) {
mp_msg(MSGT_MUXER, MSGL_FATAL, "Couldn't allocate initial frames structure, abort!\n");
goto init_fail;
}
mp_msg (MSGT_MUXER, MSGL_DBG2, "Added audio stream %d, ckid=%X\n", s->id - muxer->num_videos + 1, s->ckid);
}
muxer->avih.dwStreams++;
return s;
init_fail:
if(s)
{
if(s->priv)
{
spriv = s->priv;
if(spriv->pack)
free(spriv->pack);
if(spriv->buffer_track)
free(spriv->buffer_track);
free(s->priv);
}
if(s->b_buffer)
free(s->b_buffer);
free(s);
}
return NULL;
}
static void write_mpeg_ts(unsigned char *b, uint64_t ts, uint8_t mod) {
ts /= 300;
b[0] = mod | ((ts >> 29) & 0xf) | 1;
b[1] = (ts >> 22) & 0xff;
b[2] = ((ts >> 14) & 0xff) | 1;
b[3] = (ts >> 7) & 0xff;
b[4] = ((ts << 1) & 0xff) | 1;
}
static void write_mpeg_rate(int type, unsigned char *b, unsigned int rate)
{
rate = (rate+49) / 50;
if(type == MUX_MPEG1)
{
b[0] = ((rate >> 15) & 0x7f) | 0x80;
b[1] = (rate >> 7) & 0xff;
b[2] = ((rate << 1) & 0xff) | 1;
}
else
{
b[0] = (rate >> 14);
b[1] = (rate >> 6) & 0xff;
b[2] = ((rate & 0x3f) << 2) | 0x03;
}
}
static void write_mpeg_std(unsigned char *b, unsigned int size, unsigned int type, uint8_t mod)
{
//type = 0:mpeg audio/128, 1:video and pes private streams (including ac3/dts/lpcm)/1024
if(type == 0) //audio
size = (size + 127) / 128;
else //video or other
size = ((size + 1023) / 1024);
if(! size)
size++;
b[0] = ((size >> 8) & 0x3f) | (type==1 ? 0x60 : 0x40) | mod;
b[1] = size & 0xff;
}
static void write_mpeg2_scr(unsigned char *b, uint64_t ts)
{
uint16_t t1, t2, t3, scr_ext;
scr_ext = ts % 300ULL;
ts /= 300ULL;
ts &= 0x1FFFFFFFFULL; //33 bits
t1 = (ts >> 30) & 0x7;;
t2 = (ts >> 15) & 0x7fff;
t3 = ts & 0x7fff;
b[0] = (t1 << 3 ) | 0x44 | ((t2 >> 13) & 0x3);
b[1] = (t2 >> 5);
b[2] = (t2 & 0x1f) << 3 | 0x4 | ((t3 >> 13) & 0x3);
b[3] = (t3 >> 5);
b[4] = (t3 & 0x1f) << 3 | ((scr_ext >> 7) & 0x03) | 0x4;
b[5] = ((scr_ext << 1) & 0xFF) | 1;
}
static int write_mpeg_pack_header(muxer_t *muxer, char *buff)
{
int len;
muxer_priv_t *priv;
priv = (muxer_priv_t *) muxer->priv;
*(uint32_t *)buff = be2me_32(PACK_HEADER_START_CODE);
if(priv->mux==MUX_MPEG1)
{
write_mpeg_ts(&buff[4], priv->scr, 0x20); // 0010 and SCR
write_mpeg_rate(priv->mux, &buff[9], muxer->sysrate);
len = 12;
}
else
{
write_mpeg2_scr(&buff[4], priv->scr); // 0010 and SCR
write_mpeg_rate(priv->mux, &buff[10], muxer->sysrate);
buff[13] = 0xf8; //5 bits reserved + 3 set to 0 to indicate 0 stuffing bytes
len = 14;
}
return len;
}
static int write_mpeg_system_header(muxer_t *muxer, char *buff)
{
int len;
uint8_t i;
muxer_priv_t *priv;
priv = (muxer_priv_t *) muxer->priv;
len = 0;
*(uint32_t *)(&buff[len]) = be2me_32(SYSTEM_HEADER_START_CODE);
len += 4;
*(uint16_t *)(&buff[len]) = 0; //fake length, we'll fix it later
len += 2;
write_mpeg_rate(MUX_MPEG1, &buff[len], muxer->sysrate);
len += 3;
buff[len++] = 0x4 | (priv->is_xvcd ? 1 : 0); //1 audio stream bound, no fixed, CSPS only for xvcd
buff[len++] = 0xe1; //system_audio_lock, system_video_lock, marker, 1 video stream bound
buff[len++] = ((priv->mux == MUX_MPEG1) ? 0xff : 0x7f); //in mpeg2 there's the packet rate restriction
for(i = 0; i < priv->sys_info.cnt; i++)
{
buff[len++] = priv->sys_info.streams[i].id;
write_mpeg_std(&buff[len], priv->sys_info.streams[i].bufsize, priv->sys_info.streams[i].type,
(priv->sys_info.streams[i].type == 1 ? 0xe0: 0xc0));
len += 2;
}
*(uint16_t *)(&buff[4]) = be2me_16(len - 6); // length field fixed
return len;
}
static int write_mpeg_psm(muxer_t *muxer, char *buff)
{
int len;
uint8_t i;
uint16_t dlen;
muxer_priv_t *priv;
priv = (muxer_priv_t *) muxer->priv;
len = 0;
*(uint32_t *)(&buff[len]) = be2me_32(PSM_START_CODE);
len += 4;
*(uint16_t *)(&buff[len]) = 0; //fake length, we'll fix it later
len += 2;
buff[len++] = 0xe0; //1 current, 2 bits reserved, 5 version 0
buff[len++] = 0xff; //7 reserved, 1 marker
buff[len] = buff[len+1] = 0; //length of the program descriptors (unused)
len += 2;
*(uint16_t *)(&buff[len]) = 0; //length of the es descriptors
len += 2;
dlen = 0;
for(i = 0; i < priv->psm_info.cnt; i++)
{
if(
(priv->psm_info.streams[i].id == 0xbd) ||
(priv->psm_info.streams[i].id >= 0xe0 && priv->psm_info.streams[i].id <= 0xef) ||
(priv->psm_info.streams[i].id >= 0xc0 && priv->psm_info.streams[i].id <= 0xcf)
)
{
buff[len++] = priv->psm_info.streams[i].type;
buff[len++] = priv->psm_info.streams[i].id;
buff[len++] = 0; //len of descriptor upper ...
buff[len++] = 6; //... lower
//registration descriptor
buff[len++] = 0x5; //tag
buff[len++] = 4; //length: 4 bytes
memcpy(&(buff[len]), (char*) &(priv->psm_info.streams[i].format), 4);
len += 4;
dlen += 10;
}
}
*(uint16_t *)(&buff[10]) = be2me_16(dlen); //length of the es descriptors
*(uint16_t *)(&buff[4]) = be2me_16(len - 6 + 4); // length field fixed, including size of CRC32
*(uint32_t *)(&buff[len]) = be2me_32(CalcCRC32(buff, len));
len += 4; //for crc
return len;
}
static int write_mpeg_pes_header(muxer_headers_t *h, uint8_t *pes_id, uint8_t *buff, uint16_t plen, int stuffing_len, int mux_type)
{
int len;
len = 0;
memcpy(&buff[len], pes_id, 4);
len += 4;
buff[len] = buff[len+1] = 0; //fake len
len += 2;
if(mux_type == MUX_MPEG1)
{
if(stuffing_len > 0)
{
memset(&buff[len], 0xff, stuffing_len);
len += stuffing_len;
}
if(h->buffer_size > 0)
{
write_mpeg_std(&buff[len], h->buffer_size, h->type, 0x40); // 01 is pes1 format
len += 2;
}
}
else //MPEG2
{
buff[len] = (h->pes_is_aligned ? 0x84 : 0x80); //0x10...
len++;
buff[len] = ((h->buffer_size > 0) ? 1 : 0) | (h->pts ? (h->dts ? 0xC0 : 0x80) : 0); //pes extension + pts/dts flags
len++;
buff[len] = (h->pts ? (h->dts ? 10 : 5) : 0) + ((h->buffer_size > 0) ? 3 : 0) + stuffing_len;//pts + std + stuffing
len++;
}
if(h->pts)
{
write_mpeg_ts(&buff[len], h->pts, (h->dts ? 0x30 : 0x20)); // 001x and both PTS/DTS
len += 5;
if(h->dts)
{
write_mpeg_ts(&buff[len], h->dts, 0x10); // 0001 before DTS
len += 5;
}
}
else
{
if(mux_type == MUX_MPEG1)
{
buff[len] = 0x0f;
len += 1;
}
}
if(mux_type == MUX_MPEG2)
{
if(h->buffer_size > 0)
{
buff[len] = 0x1e; //std flag
len++;
write_mpeg_std(&buff[len], h->buffer_size, h->type, 0x40);
len += 2;
}
if(stuffing_len > 0)
{
memset(&buff[len], 0xff, stuffing_len);
len += stuffing_len;
}
}
*((uint16_t*) &buff[4]) = be2me_16(len + plen - 6); //fix pes packet size
return len;
}
static void write_pes_padding(uint8_t *buff, uint16_t len)
{
//6 header bytes + len-6 0xff chars
buff[0] = buff[1] = 0;
buff[2] = 1;
buff[3] = 0xbe;
*((uint16_t*) &buff[4]) = be2me_16(len - 6);
memset(&buff[6], 0xff, len - 6);
}
static void write_psm_block(muxer_t *muxer, stream_t *stream)
{
uint16_t offset, stuffing_len;
muxer_priv_t *priv = (muxer_priv_t *) muxer->priv;
uint8_t *buff = priv->buff;
offset = write_mpeg_pack_header(muxer, buff);
offset += write_mpeg_psm(muxer, &buff[offset]);
stuffing_len = priv->packet_size - offset;
if(stuffing_len > 0)
{
//insert a PES padding packet
write_pes_padding(&buff[offset], stuffing_len);
offset += stuffing_len;
}
stream_write_buffer(stream, buff, offset);
priv->headers_size += offset;
}
static int write_nav_pack(uint8_t *buff)
{
// concatenation of pes_private2 + 03d4 x 0 and pes_private2 + 03fa x 0
int len;
mp_msg(MSGT_MUXER, MSGL_DBG3, "NAV\n");
len = 0;
*(uint32_t *)(&buff[len]) = be2me_32(PES_PRIVATE2);
len += 4;
buff[len++] = 0x3;
buff[len++] = 0xd4;
memset(&buff[len], 0, 0x03d4);
len += 0x03d4;
*(uint32_t *)(&buff[len]) = be2me_32(PES_PRIVATE2);
len += 4;
buff[len++] = 0x3;
buff[len++] = 0xfa;
memset(&buff[len], 0, 0x03fa);
len += 0x03fa;
return len;
}
static uint32_t calc_pes_hlen(int format, muxer_headers_t *h, muxer_priv_t *priv)
{
uint32_t len;
if(format == MUX_MPEG1)
len = 6;
else
len = 9;
if(h->pts)
{
len += 5;
if(h->dts)
len += 5;
}
else if(format == MUX_MPEG1)
len += 1;
if(h->buffer_size > 0)
{
if(format == MUX_MPEG2)
len += 3;
else
len += 2;
}
//len = max(h->min_pes_hlen, len);
return len;
}
static int write_mpeg_pack(muxer_t *muxer, muxer_stream_t *s, stream_t *stream, int isoend)
{
size_t tot, offset;
muxer_priv_t *priv;
unsigned char *buff;
int stuffing_len;
priv = (muxer_priv_t *) muxer->priv;
buff = priv->buff;
if(isoend)
{
offset = priv->packet_size - 4;
write_pes_padding(buff, offset);
buff[offset + 0] = buff[offset + 1] = 0;
buff[offset + 2] = 1;
buff[offset + 3] = 0xb9;
stream_write_buffer(stream, buff, priv->packet_size);
return 1;
}
else //FAKE DVD NAV PACK
{
offset = write_mpeg_pack_header(muxer, buff);
offset += write_mpeg_system_header(muxer, &buff[offset]);
//priv->update_system_header = 0;
if(priv->is_dvd)
offset += write_nav_pack(&buff[offset]);
stuffing_len = priv->packet_size - offset;
if(stuffing_len > 0)
{
//insert a PES padding packet
write_pes_padding(&buff[offset], stuffing_len);
offset += stuffing_len;
}
stream_write_buffer(stream, buff, offset);
priv->headers_size += offset;
tot = offset;
muxer->movi_end += tot;
return tot;
}
}
static void update_demux_bufsize(muxer_headers_t *spriv, uint64_t dts, int framelen, int type)
{
int dim = (spriv->track_len+16)*sizeof(buffer_track_t);
if(spriv->track_pos+1 >= spriv->track_len)
{
buffer_track_t *tmp = realloc(spriv->buffer_track, dim);
if(!tmp)
{
mp_msg(MSGT_MUXER, MSGL_ERR, "\r\nERROR, couldn't realloc %d bytes for tracking buffer\r\n", dim);
return;
}
spriv->buffer_track = tmp;
memset(&(spriv->buffer_track[spriv->track_pos+1]), 0, 16*sizeof(buffer_track_t));
spriv->track_len += 16;
}
spriv->buffer_track[spriv->track_pos].size = framelen;
spriv->buffer_track[spriv->track_pos].dts = dts; //must be dts
spriv->track_pos++;
}
static void fix_a52_headers(muxer_stream_t *s)
{
muxer_headers_t *spriv = s->priv;
int x = spriv->payload_offset;
spriv->pack[x+0] = 0x80;
spriv->pack[x+1] = spriv->frames;
if(spriv->frames)
{
spriv->pack[x+2] = ((spriv->last_frame_rest+1) >> 8) & 0xff; //256 * 0 ...
spriv->pack[x+3] = (spriv->last_frame_rest+1) & 0xff; // + 1 byte(s) to skip
}
else
spriv->pack[x+2] = spriv->pack[x+3] = 0;
}
static inline void remove_frames(muxer_headers_t *spriv, int n)
{
mpeg_frame_t tmp;
int i;
for(i = n; i < spriv->framebuf_used; i++)
{
tmp = spriv->framebuf[i - n];
spriv->framebuf[i - n] = spriv->framebuf[i];
spriv->framebuf[i] = tmp;
}
spriv->framebuf_used -= n;
}
static int calc_packet_len(muxer_stream_t *s, int psize, int finalize)
{
muxer_headers_t *spriv = s->priv;
int n, len, frpos, m;
n = len = 0;
frpos = spriv->framebuf[0].pos;
while(len < psize && n < spriv->framebuf_used)
{
if(!frpos && len>0 && s->type == MUXER_TYPE_VIDEO && spriv->framebuf[n].type==I_FRAME)
return len;
m = FFMIN(spriv->framebuf[n].size - frpos, psize - len);
len += m;
frpos += m;
if(frpos == spriv->framebuf[n].size)
{
frpos = 0;
n++;
}
}
if(len < psize && !finalize)
return 0;
return len;
}
static int find_packet_timestamps(muxer_priv_t *priv, muxer_stream_t *s, unsigned int start, uint64_t *dts, uint64_t *pts)
{
muxer_headers_t *spriv = s->priv;
int i, m, pes_hlen, ret, threshold;
uint64_t spts, sdts, dpts;
if(!spriv->framebuf_used)
return 0;
spts = spriv->pts;
sdts = spriv->dts;
spriv->dts = spriv->pts = 0;
ret = 0;
if(spriv->framebuf[0].pos == 0) // start of frame
i = 0;
else
{
pes_hlen = calc_pes_hlen(priv->mux, spriv, priv);
if(pes_hlen < spriv->min_pes_hlen)
pes_hlen = spriv->min_pes_hlen;
m = spriv->framebuf[0].size - spriv->framebuf[0].pos;
if(start + pes_hlen + m >= priv->packet_size) //spriv->pack_offset
i = -1; //this pack won't have a pts: no space available
else
{
if(spriv->framebuf_used < 2)
goto fail;
if(spriv->framebuf[1].pts == spriv->framebuf[1].dts)
threshold = 5;
else
threshold = 10;
//headers+frame 0 < space available including timestamps
if(start + pes_hlen + m < priv->packet_size - threshold)
i = 1;
else
i = -1;
}
}
if(i > -1)
{
dpts = FFMAX(spriv->last_saved_pts, spriv->framebuf[i].pts) -
FFMIN(spriv->last_saved_pts, spriv->framebuf[i].pts) +
spriv->framebuf[0].idur;
if(s->type != MUXER_TYPE_VIDEO)
ret = 1;
else if((spriv->framebuf[i].type == I_FRAME || priv->ts_allframes || dpts >= 36000*300)) //0.4 seconds
ret = 1;
if(ret)
{
*pts = spriv->framebuf[i].pts;
*dts = spriv->framebuf[i].dts;
if(*dts == *pts)
*dts = 0;
}
}
fail:
spriv->pts = spts;
spriv->dts = sdts;
return ret;
}
static int get_packet_stats(muxer_priv_t *priv, muxer_stream_t *s, pack_stats_t *p, int finalize)
{
muxer_headers_t *spriv = s->priv;
int len, len2, pack_hlen, pes_hlen, hlen, target, stflen, stuffing_len;
uint64_t pts, dts;
spriv->pts = spriv->dts = 0;
p->dts = p->pts = p->frame_pts = p->frame_dts = 0;
p->len = 0;
if(priv->rawpes)
pack_hlen = 0;
else if(priv->mux == MUX_MPEG1)
pack_hlen = 12;
else
pack_hlen = 14;
if(find_packet_timestamps(priv, s, pack_hlen, &dts, &pts))
{
p->pts = p->frame_pts = pts;
p->dts = p->frame_dts = dts;
spriv->pts = pts;
spriv->dts = dts;
}
pes_hlen = calc_pes_hlen(priv->mux, spriv, priv);
p->stflen = stflen = (spriv->min_pes_hlen > pes_hlen ? spriv->min_pes_hlen - pes_hlen : 0);
target = len = priv->packet_size - pack_hlen - pes_hlen - stflen; //max space available
if(s->type == MUXER_TYPE_AUDIO && s->wf->wFormatTag == AUDIO_A52)
hlen = 4;
else
hlen = 0;
len -= hlen;
target -= hlen;
len2 = calc_packet_len(s, target, finalize);
if(!len2 || (len2 < target && s->type == MUXER_TYPE_AUDIO && !finalize))
{
//p->len = 0;
//p->dts = p->pts = 0;
spriv->pts = spriv->dts = 0;
//fprintf(stderr, "\r\nLEN2: %d, target: %d, type: %d\r\n", len2, target, s->type);
return 0;
}
len = len2;
stuffing_len = 0;
if(len < target)
{
if(s->type == MUXER_TYPE_VIDEO)
{
if(spriv->pts)
target += 5;
if(spriv->dts)
target += 5;
spriv->pts = spriv->dts = 0;
p->pts = p->dts = 0;
}
stuffing_len = target - len;
if(stuffing_len > 0 && stuffing_len < 7)
{
if(stflen + stuffing_len > 16)
{
int x = 7 - stuffing_len;
stflen -= x;
stuffing_len += x;
}
else
{
stflen += stuffing_len;
stuffing_len = 0;
}
}
}
len += hlen;
p->len = len;
p->stflen = stflen;
return p->len;
}
static int fill_packet(muxer_t *muxer, muxer_stream_t *s, int finalize)
{
//try to fill a packet as much as possible
//spriv->pack_offset is the start position inited to 0
//data is taken from spriv->framebuf
//if audio and a52 insert the headers
muxer_priv_t *priv = (muxer_priv_t *) muxer->priv;
muxer_headers_t *spriv = (muxer_headers_t *) s->priv;
int len, m, n, dvd_pack = 0;
mpeg_frame_t *frm;
pack_stats_t p;
spriv->dts = spriv->pts = 0;
if(! spriv->framebuf_used)
{
spriv->pack_offset = 0;
return 0;
}
if(!spriv->pack_offset)
{
if(priv->rawpes)
spriv->pack_offset = 0;
else
spriv->pack_offset = write_mpeg_pack_header(muxer, spriv->pack);
if(priv->update_system_header && (priv->is_genmpeg1 || priv->is_genmpeg2))
{
spriv->pack_offset += write_mpeg_system_header(muxer, &spriv->pack[spriv->pack_offset]);
priv->update_system_header = 0;
}
spriv->pes_set = 0;
spriv->pes_offset = spriv->pack_offset;
spriv->payload_offset = 0;
spriv->frames = 0;
spriv->last_frame_rest = 0;
}
if(!spriv->pes_set)
{
int bufsize = 0;
//search the pts. yes if either it's video && (I-frame or priv->ts_allframes) && framebuf[i].pos == 0
//or it's audio && framebuf[i].pos == 0
//NB pts and dts can only be relative to the first frame beginning in this pack
if((priv->is_xsvcd || priv->is_xvcd || priv->rawpes) && spriv->size == 0)
{
if(s->type == MUXER_TYPE_VIDEO)
bufsize = (conf_vbuf_size ? conf_vbuf_size : (priv->is_xvcd ? 46 : 232));
else
bufsize = (conf_abuf_size ? conf_abuf_size : 4);
spriv->buffer_size = bufsize*1024;
}
if(priv->is_dvd && s->type == MUXER_TYPE_VIDEO
&& spriv->framebuf[0].type==I_FRAME && spriv->framebuf[0].pos==0)
dvd_pack = 1;
if(! get_packet_stats(priv, s, &p, finalize))
{
spriv->pack_offset = 0;
return 0;
}
spriv->dts = p.dts;
spriv->pts = p.pts;
if(spriv->pts)
spriv->last_saved_pts = p.pts;
spriv->pack_offset += write_mpeg_pes_header(spriv, (uint8_t *) &s->ckid, &(spriv->pack[spriv->pack_offset]),
p.len, p.stflen, priv->mux);
if(s->type == MUXER_TYPE_AUDIO && s->wf->wFormatTag == AUDIO_A52)
{
spriv->payload_offset = spriv->pack_offset;
spriv->pack_offset += 4; //for the 4 bytes of header
if(!spriv->framebuf[0].pos)
spriv->last_frame_rest = 0;
else
spriv->last_frame_rest = spriv->framebuf[0].size - spriv->framebuf[0].pos;
}
spriv->pes_set = 1;
}
if(spriv->dts || spriv->pts)
{
if((spriv->dts && priv->scr >= spriv->dts) || priv->scr >= spriv->pts)
mp_msg(MSGT_MUXER, MSGL_ERR, "\r\nERROR: scr %.3lf, dts %.3lf, pts %.3lf\r\n", (double) priv->scr/27000000.0, (double) spriv->dts/27000000.0, (double) spriv->pts/27000000.0);
else if(priv->scr + 63000*300 < spriv->dts)
mp_msg(MSGT_MUXER, MSGL_INFO, "\r\nWARNING>: scr %.3lf, dts %.3lf, pts %.3lf, diff %.3lf, piff %.3lf\r\n", (double) priv->scr/27000000.0, (double) spriv->dts/27000000.0, (double) spriv->pts/27000000.0, (double)(spriv->dts - priv->scr)/27000000.0, (double)(spriv->pts - priv->scr)/27000000.0);
}
n = 0;
len = 0;
frm = spriv->framebuf;
while(spriv->pack_offset < priv->packet_size && n < spriv->framebuf_used)
{
if(!frm->pos)
{
//since iframes must always be aligned at block boundaries exit when we find the
//beginning of one in the middle of the flush
if(len > 0 && s->type == MUXER_TYPE_VIDEO && frm->type == I_FRAME)
{
break;
}
spriv->frames++;
update_demux_bufsize(spriv, frm->dts, frm->size, s->type);
}
m = FFMIN(frm->size - frm->pos, priv->packet_size - spriv->pack_offset);
memcpy(&(spriv->pack[spriv->pack_offset]), &(frm->buffer[frm->pos]), m);
len += m;
spriv->pack_offset += m;
frm->pos += m;
if(frm->pos == frm->size) //end of frame
{
frm->pos = frm->size = 0;
frm->pts = frm->dts = 0;
n++;
frm++;
}
}
if((priv->is_xsvcd || priv->is_xvcd || priv->rawpes) && spriv->size == 0)
spriv->buffer_size = 0;
spriv->size += len;
if(dvd_pack && (spriv->pack_offset == priv->packet_size))
write_mpeg_pack(muxer, NULL, muxer->stream, 0); //insert fake Nav Packet
if(n > 0)
remove_frames(spriv, n);
spriv->track_bufsize += len;
if(spriv->track_bufsize > spriv->max_buffer_size)
mp_msg(MSGT_MUXER, MSGL_ERR, "\r\nBUFFER OVERFLOW: %d > %d, pts: %llu\r\n", spriv->track_bufsize, spriv->max_buffer_size, spriv->pts);
if(s->type == MUXER_TYPE_AUDIO && s->wf->wFormatTag == AUDIO_A52)
fix_a52_headers(s);
if(spriv->pack_offset < priv->packet_size && !priv->rawpes) //here finalize is set
{
int diff = priv->packet_size - spriv->pack_offset;
write_pes_padding(&(spriv->pack[spriv->pack_offset]), diff);
spriv->pack_offset += diff;
}
stream_write_buffer(muxer->stream, spriv->pack, spriv->pack_offset);
priv->headers_size += spriv->pack_offset - len;
priv->data_size += len;
muxer->movi_end += spriv->pack_offset;
spriv->pack_offset = 0;
spriv->pes_set = 0;
spriv->frames = 0;
return len;
}
static inline int find_best_stream(muxer_t *muxer)
{
int i, ndts;
uint64_t dts = -1;
muxer_priv_t *priv = muxer->priv;
muxer_headers_t *spriv;
pack_stats_t p;
ndts = -1;
//THIS RULE MUST ALWAYS apply: dts <= SCR + 0.7 seconds
for(i = 0; i < muxer->avih.dwStreams; i++)
{
spriv = muxer->streams[i]->priv;
p.len = 0;
get_packet_stats(priv, muxer->streams[i], &p, 0);
if(spriv->track_bufsize + p.len > spriv->max_buffer_size)
continue;
if(p.frame_pts && p.frame_dts > priv->scr + 63000*300)
continue;
if(spriv->framebuf[0].dts <= dts)
{
dts = spriv->framebuf[0].dts;
ndts = i;
}
}
return ndts;
}
static void patch_seq(muxer_priv_t *priv, unsigned char *buf)
{
if(priv->vwidth > 0)
{
buf[4] = (priv->vwidth >> 4) & 0xff;
buf[5] &= 0x0f;
buf[5] |= (priv->vwidth & 0x0f) << 4;
}
if(priv->vheight > 0)
{
buf[5] &= 0xf0;
buf[5] |= (priv->vheight >> 8) & 0x0f;
buf[6] = priv->vheight & 0xff;
}
if(priv->vaspect > 0)
buf[7] = (buf[7] & 0x0f) | (priv->vaspect << 4);
if(priv->vframerate > 0)
buf[7] = (buf[7] & 0xf0) | priv->vframerate;
if(priv->vbitrate > 0)
{
buf[8] = (priv->vbitrate >> 10);
buf[9] = (priv->vbitrate >> 2);
buf[10] = (buf[10] & 0x3f) | (unsigned char) ((priv->vbitrate & 0x4) << 2);
}
}
static void patch_panscan(muxer_priv_t *priv, unsigned char *buf)
{ //patches sequence display extension (display_horizontal_size and display_vertical_size)
//1:
int offset = 1;
if(buf[0] & 0x01)
offset += 3;
if(priv->panscan_width > 0)
{
buf[offset] = (priv->panscan_width >> 6);
buf[offset+1] = ((priv->panscan_width & 0x3F) << 2) | (buf[offset + 1] & 0x03);
}
offset++;
if(priv->panscan_height > 0)
{
buf[offset] = (priv->panscan_height >> 13) << 7;
buf[offset+1] = (priv->panscan_height >> 5) & 0xFF;
buf[offset+2] = ((priv->panscan_height & 0x1F) << 3) | (buf[offset+2] & 0x07);
}
}
static void update_scr(muxer_t *muxer)
{
muxer_priv_t *priv = muxer->priv;
muxer_stream_t *stream;
muxer_headers_t *spriv;
int i, j;
uint64_t mindts = (uint64_t) -1;
priv->scr += priv->delta_scr;
for(i = 0; i < muxer->avih.dwStreams; i++)
{
stream = muxer->streams[i];
spriv = stream->priv;
if(spriv->framebuf_used && spriv->framebuf[0].dts < mindts)
mindts = spriv->framebuf[0].dts;
}
mp_msg(MSGT_MUXER, MSGL_DBG2, "UPDATE SCR TO %"PRIu64" (%.3lf)\n", priv->scr, (double) (priv->scr/27000000.0));
for(i = 0; i < muxer->avih.dwStreams; i++)
{
stream = muxer->streams[i];
spriv = stream->priv;
j = 0;
while(j < spriv->track_pos && priv->scr >= spriv->buffer_track[j].dts)
{
spriv->track_bufsize -= spriv->buffer_track[j].size;
j++;
}
if(spriv->track_bufsize < 0)
{
double d;
muxer->sysrate = (muxer->sysrate * 11) / 10; //raise by 10%
d = (double) priv->packet_size / (double)muxer->sysrate;
priv->delta_scr = (uint64_t) (d * 27000000.0f);
mp_msg(MSGT_MUXER, MSGL_INFO, "\r\nBUFFER UNDEFLOW at stream %d, raising muxrate to %d kb/s, delta_scr: %llu\r\n", i, muxer->sysrate/125, priv->delta_scr);
spriv->track_bufsize = 0;
}
if(j > 0)
{
memmove(spriv->buffer_track, &(spriv->buffer_track[j]), (spriv->track_len - j) * sizeof(buffer_track_t));
spriv->track_pos -= j;
for(j = spriv->track_pos; j < spriv->track_len; j++)
spriv->buffer_track[j].size = 0;
}
if(spriv->framebuf_used && spriv->framebuf[0].dts < mindts)
mindts = spriv->framebuf[0].dts;
}
}
static int calc_frames_to_flush(muxer_headers_t *vpriv)
{
int n, found = 0;
if(vpriv->framebuf_used > 0)
{
n = 0;
//let's count how many frames we'll store in the next pack sequence
mp_msg(MSGT_MUXER, MSGL_DBG2, "\n");
while(n < vpriv->framebuf_used)
{
mp_msg(MSGT_MUXER, MSGL_DBG2, "CALC_FRAMES, n=%d, type=%c, pts=%.3lf\n", n, FTYPE(vpriv->framebuf[n].type), (double)vpriv->framebuf[n].pts/27000000.0f);
if(n+1 < vpriv->framebuf_used)
mp_msg(MSGT_MUXER, MSGL_DBG2, "n+1=%d, type=%c, pts=%.3lf\n", n+1, FTYPE(vpriv->framebuf[n+1].type), (double)vpriv->framebuf[n+1].pts/27000000.0f);
if(vpriv->framebuf[n].type == I_FRAME)
{
if(n > 0)
{
found = 1;
break;
}
}
n++;
}
}
if(found && (n < vpriv->framebuf_used+1))
return n;
else
return 0;
}
static int flush_buffers(muxer_t *muxer, int finalize)
{
int i, n, found;
int skip_cnt;
uint64_t init_delay = 0;
muxer_stream_t *s, *vs, *as;
muxer_headers_t *vpriv = NULL, *apriv = NULL;
muxer_priv_t *priv = (muxer_priv_t *) muxer->priv;
double duration;
uint64_t iduration, iaduration;
/*
analyzes all streams and decides what to flush
trying to respect an interleaving distribution
equal to the v_bitrate/a_bitrate proportion
*/
n = 0;
vs = as = NULL;
found = 0;
for(i = 0; i < muxer->avih.dwStreams; i++)
{
s = muxer->streams[i];
if(s->type == MUXER_TYPE_VIDEO)
{
vs = muxer->streams[i];
vpriv = (muxer_headers_t*) vs->priv;
if(!vpriv->is_ready)
return 0;
n = found = calc_frames_to_flush(vpriv);
}
else if(s->type == MUXER_TYPE_AUDIO)
as = s;
}
if((! found) && finalize)
{
if(vpriv != NULL)
found = n = vpriv->framebuf_used;
}
if(found)
{
mp_msg(MSGT_MUXER, MSGL_DBG2, "\nVIDEO, FLUSH %d frames (of %d), 0 to %d\n", n, vpriv->framebuf_used, n-1);
vpriv = (muxer_headers_t*) vs->priv;
duration = 0;
iduration = 0;
for(i = 0; i < n; i++)
iduration += vpriv->framebuf[i].idur;
duration = (double) (iduration / 27000000.0);
if(as != NULL)
{
apriv = (muxer_headers_t*) as->priv;
iaduration = 0;
for(i = 0; i < apriv->framebuf_used; i++)
{
iaduration += apriv->framebuf[i].idur;
}
if(iaduration < iduration)
{
mp_msg(MSGT_MUXER, MSGL_DBG2, "Not enough audio data exit\n");
return 0;
}
}
if(as != NULL && (apriv->size == 0))
{
init_delay = vpriv->framebuf[0].pts - vpriv->framebuf[0].dts;
for(i = 0; i < apriv->framebuf_cnt; i++)
{
apriv->framebuf[i].pts += init_delay;
apriv->framebuf[i].dts += init_delay;
}
apriv->last_pts += init_delay;
mp_msg(MSGT_MUXER, MSGL_DBG2, "\r\nINITIAL VIDEO DELAY: %.3lf, currAPTS: %.3lf\r\n", (double) init_delay/27000000.0f, (double) apriv->last_pts/27000000.0f);
}
if((priv->is_xvcd || priv->is_xsvcd) && (vpriv->size == 0))
vpriv->buffer_size = (conf_vbuf_size ? conf_vbuf_size : (priv->is_xvcd ? 46 : 230))*1024;
i = 0;
skip_cnt = 0;
while(1)
{
update_scr(muxer);
i = find_best_stream(muxer);
if(i < 0)
continue;
if(!fill_packet(muxer, muxer->streams[i], finalize))
skip_cnt++;
if(skip_cnt == muxer->avih.dwStreams)
{
found = 0;
break;
}
}
}
muxer->file_end = priv->scr;
return found;
}
static inline uint64_t parse_fps(float fps)
{
// 90000 * 300 * 1001 / d , there's no rounding error with any of the admitted framerates
int d = (int)(fps*1001+0.5);
return 27027000000ULL / d;
}
static int soft_telecine(muxer_priv_t *priv, muxer_headers_t *vpriv, uint8_t *fps_ptr, uint8_t *se_ptr, uint8_t *pce_ptr, int n)
{
if(! pce_ptr)
return 0;
if(fps_ptr != NULL)
{
*fps_ptr = (*fps_ptr & 0xf0) | priv->vframerate;
vpriv->nom_delta_pts = parse_fps(conf_vframerate);
}
//in pce_ptr starting from bit 0 bit 24 is tff, bit 30 is rff,
if(pce_ptr[3] & 0x2)
{
mp_msg(MSGT_MUXER, MSGL_ERR, "\nERROR! RFF bit is already set, disabling telecining\n");
vpriv->telecine = 0;
return 0;
}
vpriv->picture.progressive_sequence = 0;
vpriv->picture.progressive_frame = 1;
if(se_ptr)
se_ptr[1] &= 0xf7;
pce_ptr[3] = (pce_ptr[3] & 0xfd) | bff_mask[vpriv->display_frame % MAX_PATTERN_LENGTH];
pce_ptr[4] |= 0x80; //sets progressive frame
vpriv->display_frame += n;
if(! vpriv->vframes)
mp_msg(MSGT_MUXER, MSGL_INFO, "\nENABLED SOFT TELECINING, FPS=%.3f\n",conf_vframerate);
return 1;
}
static size_t parse_mpeg12_video(muxer_stream_t *s, muxer_priv_t *priv, muxer_headers_t *spriv, float fps, size_t len)
{
uint8_t *fps_ptr = NULL; //pointer to the fps byte in the sequence header
uint8_t *se_ptr = NULL; //pointer to sequence extension
uint8_t *pce_ptr = NULL; //pointer to picture coding extension
int frames_diff, d1, gop_reset = 0; //how any frames we advanced respect to the last one
int ret;
int i, err;
uint32_t temp_ref;
int pt;
mp_msg(MSGT_MUXER, MSGL_DBG2,"parse_mpeg12_video, len=%u\n", (uint32_t) len);
if(s->buffer[0] != 0 || s->buffer[1] != 0 || s->buffer[2] != 1 || len<6)
{
mp_msg(MSGT_MUXER, MSGL_ERR,"Unknown video format, possibly non-MPEG1/2 stream, len=%d!\n", len);
return 0;
}
temp_ref = 0;
pt = 0;
err = 0;
i = 0;
while(i + 4 < len)
{ // Video (0) Sequence header (b3) or GOP (b8)
if((s->buffer[i] == 0) && (s->buffer[i+1] == 0) && (s->buffer[i+2] == 1))
{
switch(s->buffer[i+3])
{
case 0xb3: //sequence
{
if(i + 11 > len)
{
err=1;
break;
}
fps_ptr = &(s->buffer[i+7]);
mp_header_process_sequence_header(&(spriv->picture), &(s->buffer[i+4]));
spriv->delta_pts = spriv->nom_delta_pts = parse_fps(spriv->picture.fps);
spriv->delta_clock = (double) 1/fps;
//the 2 lines below are needed to handle non-standard frame rates (such as 18)
if(! spriv->delta_pts)
spriv->delta_pts = spriv->nom_delta_pts = (uint64_t) ((double)27000000.0 * spriv->delta_clock );
mp_msg(MSGT_MUXER, MSGL_DBG2, "\nFPS: %.3f, FRAMETIME: %.3lf\n", fps, (double)1/fps);
if(priv->patch_seq)
patch_seq(priv, &(s->buffer[i]));
}
break;
case 0xb5:
if(i + 9 > len)
{
err = 1;
break;
}
mp_header_process_extension(&(spriv->picture), &(s->buffer[i+4]));
if(((s->buffer[i+4] & 0xf0) == 0x20))
{
se_ptr = &(s->buffer[i+4]);
if(priv->patch_sde)
patch_panscan(priv, se_ptr);
}
if((s->buffer[i+4] & 0xf0) == 0x80)
{
pce_ptr = &(s->buffer[i+4]);
}
break;
case 0xb8:
gop_reset = 1;
break;
case 0x00:
if(i + 5 > len)
{
err = 1;
break;
}
pt = (s->buffer[i+5] & 0x1c) >> 3;
temp_ref = (s->buffer[i+4]<<2)+(s->buffer[i+5]>>6);
break;
}
if(err) break; //something went wrong
if(s->buffer[i+3] >= 0x01 && s->buffer[i+3] <= 0xAF) break; //slice, we have already analized what we need
}
i++;
}
if(err)
mp_msg(MSGT_MUXER, MSGL_ERR,"Warning: picture too short or broken!\n");
//following 2 lines are workaround: lavf doesn't sync to sequence headers before passing demux_packets
if(!spriv->nom_delta_pts)
spriv->delta_pts = spriv->nom_delta_pts = parse_fps(fps);
if(!spriv->vframes)
spriv->last_tr = spriv->max_tr = temp_ref;
d1 = temp_ref - spriv->last_tr;
if(gop_reset)
frames_diff = spriv->max_tr + 1 + temp_ref - spriv->last_tr;
else
{
if(d1 < -6) //there's a wraparound
frames_diff = spriv->max_tr + 1 + temp_ref - spriv->last_tr;
else if(d1 > 6) //there's a wraparound
frames_diff = spriv->max_tr + 1 + spriv->last_tr - temp_ref;
else if(!d1) //pre-emptive fix against broken sequences
frames_diff = 1;
else
frames_diff = d1;
}
mp_msg(MSGT_MUXER, MSGL_DBG2, "\nLAST: %d, TR: %d, GOP: %d, DIFF: %d, MAX: %d, d1: %d\n",
spriv->last_tr, temp_ref, gop_reset, frames_diff, spriv->max_tr, d1);
if(temp_ref > spriv->max_tr || gop_reset)
spriv->max_tr = temp_ref;
spriv->last_tr = temp_ref;
if(spriv->picture.mpeg1 == 0)
{
if(spriv->telecine && pce_ptr)
{
soft_telecine(priv, spriv, fps_ptr, se_ptr, pce_ptr, frames_diff);
spriv->picture.display_time = 100;
mp_header_process_extension(&(spriv->picture), pce_ptr);
if(spriv->picture.display_time >= 50 && spriv->picture.display_time <= 300)
spriv->delta_pts = (spriv->nom_delta_pts * spriv->picture.display_time) / 100;
}
}
if(! spriv->vframes)
frames_diff = 1;
spriv->last_dts += spriv->delta_pts;
spriv->last_pts += spriv->nom_delta_pts*(frames_diff-1) + spriv->delta_pts;
ret = add_frame(spriv, spriv->delta_pts, s->buffer, len, pt, spriv->last_dts, spriv->last_pts);
if(ret < 0)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "\r\nPARSE_MPEG12: add_frames(%d) failed, exit\r\n", len);
return 0;
}
mp_msg(MSGT_MUXER, MSGL_DBG2, "\r\nVIDEO FRAME, PT: %C, tr: %d, diff: %d, dts: %.3lf, pts: %.3lf, pdt: %u, gop_reset: %d\r\n",
ftypes[pt], temp_ref, frames_diff, ((double) spriv->last_dts/27000000.0f),
((double) spriv->last_pts/27000000.0f), spriv->picture.display_time, gop_reset);
if(pt == B_FRAME)
{
int j, n, adj = 0;
int64_t diff = spriv->last_dts - spriv->last_pts;
if(diff != 0)
{
n = spriv->framebuf_used - 1;
for(j = n; j >= 0; j--)
{
if(spriv->framebuf[j].pts >= spriv->last_pts)
{
spriv->framebuf[j].pts += diff;
adj++;
}
}
mp_msg(MSGT_MUXER, MSGL_V, "\r\nResynced B-frame by %d units, DIFF: %lld (%.3lf),[pd]ts=%.3lf\r\n",
n, diff, (double) diff/27000000.0f, (double) spriv->last_pts/27000000.0f);
spriv->last_pts = spriv->last_dts;
}
}
spriv->vframes++;
mp_msg(MSGT_MUXER, MSGL_DBG2,"parse_mpeg12_video, return %u\n", (uint32_t) len);
return len;
}
static uint64_t fix_mp4_frame_duration(muxer_headers_t *vpriv)
{
uint64_t mn, md, mx, diff;
uint32_t i;
mn = mx = vpriv->framebuf[0].pts;
for(i = 0; i < 3; i++)
{
mp_msg(MSGT_DECVIDEO,MSGL_DBG2, "PTS: %"PRIu64"\n", vpriv->framebuf[i].pts);
if(vpriv->framebuf[i].pts < mn)
mn = vpriv->framebuf[i].pts;
if(vpriv->framebuf[i].pts > mx)
mx = vpriv->framebuf[i].pts;
}
md = mn;
for(i=0; i<3; i++)
{
if((vpriv->framebuf[i].pts > mn) && (vpriv->framebuf[i].pts < mx))
md = vpriv->framebuf[i].pts;
}
if(mx - md > md - mn)
diff = md - mn;
else
diff = mx - md;
mp_msg(MSGT_DECVIDEO,MSGL_DBG2, "MIN: %"PRIu64", mid: %"PRIu64", max: %"PRIu64", diff: %"PRIu64"\n", mn, md, mx, diff);
if(diff > 0)
{
for(i=0; i<3; i++)
{
vpriv->framebuf[i].pts += diff;
vpriv->framebuf[i].dts += i * diff;
mp_msg(MSGT_MUXER, MSGL_DBG2, "FIXED_PTS: %.3lf, FIXED_DTS: %.3lf\n",
(double) (vpriv->framebuf[i].pts/27000000.0), (double) (vpriv->framebuf[i].dts/27000000.0));
}
return diff;
}
else
return 0;
}
static size_t parse_mpeg4_video(muxer_stream_t *s, muxer_priv_t *priv, muxer_headers_t *vpriv, float fps, size_t len)
{
size_t ptr = 0;
int64_t delta_pts=0;
uint8_t pt;
int ret;
mp_msg(MSGT_MUXER, MSGL_DBG2,"parse_mpeg4_video, len=%u\n", (uint32_t) len);
if(len<6)
{
mp_msg(MSGT_MUXER, MSGL_ERR,"Frame too short: %d, exit!\n", len);
return 0;
}
pt = 0;
while(ptr < len - 5)
{
if(s->buffer[ptr] != 0 || s->buffer[ptr+1] != 0 || s->buffer[ptr+2] != 1)
{
ptr++;
continue;
}
if(s->buffer[ptr+3] >= 0x20 && s->buffer[ptr+3] <= 0x2f) //VOL
{
mp4_header_process_vol(&(vpriv->picture), &(s->buffer[ptr+4]));
}
else if(s->buffer[ptr+3] == 0xb3) //gov
{
//fprintf(stderr, "\nGOV\n");
}
else if(s->buffer[ptr+3] == 0xb6) //vop
{
int32_t delta;
mp4_header_process_vop(&(vpriv->picture), &(s->buffer[ptr+4]));
delta = vpriv->picture.timeinc_unit - vpriv->last_tr;
if((delta > 0) && (delta > (vpriv->picture.timeinc_resolution/2)))
delta -= vpriv->picture.timeinc_resolution;
else if((delta < 0) && (delta < (-(vpriv->picture.timeinc_resolution/2))))
delta += vpriv->picture.timeinc_resolution;
delta_pts = (27000000 * (int64_t) delta) / vpriv->picture.timeinc_resolution;
//warning, it seems that packed bops can lead to delta == 0
pt = vpriv->picture.picture_type + 1;
mp_msg(MSGT_MUXER, MSGL_DBG2, "\nTYPE: %c, RESOLUTION: %d, TEMP: %d, delta: %d, delta_pts: %"PRId64" = %.3lf, delta2: %.3lf\n",
FTYPE(pt), vpriv->picture.timeinc_resolution, vpriv->picture.timeinc_unit, delta, delta_pts, (double) (delta_pts/27000000.0),
(double) delta / (double) vpriv->picture.timeinc_resolution);
vpriv->last_tr = vpriv->picture.timeinc_unit;
break;
}
ptr++;
}
if(vpriv->vframes)
{
vpriv->last_dts += vpriv->frame_duration;
vpriv->last_pts += delta_pts;
}
ret = add_frame(vpriv, delta_pts, s->buffer, len, pt, vpriv->last_dts, vpriv->last_pts);
if(ret < 0)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "\r\nPARSE_MPEG4: add_frames(%d) failed, exit\r\n", len);
return 0;
}
if(!vpriv->frame_duration && vpriv->framebuf_used == 3)
{
vpriv->frame_duration = fix_mp4_frame_duration(vpriv);
if(vpriv->frame_duration)
{
vpriv->last_pts += vpriv->frame_duration;
vpriv->last_dts = vpriv->framebuf[vpriv->framebuf_used-1].dts;
vpriv->delta_clock = ((double) vpriv->frame_duration)/27000000.0;
mp_msg(MSGT_MUXER, MSGL_INFO, "FRAME DURATION: %"PRIu64" %.3lf\n",
vpriv->frame_duration, (double) (vpriv->frame_duration/27000000.0));
vpriv->is_ready = 1;
}
}
mp_msg(MSGT_MUXER, MSGL_DBG2, "LAST_PTS: %.3lf, LAST_DTS: %.3lf\n",
(double) (vpriv->last_pts/27000000.0), (double) (vpriv->last_dts/27000000.0));
vpriv->vframes++;
return len;
}
static int fill_last_frame(muxer_headers_t *spriv, uint8_t *ptr, int len)
{
int idx;
if(!len)
return 0;
if(spriv->framebuf_used == 0)
idx = spriv->framebuf_used;
else
idx = spriv->framebuf_used - 1;
if(spriv->framebuf[idx].alloc_size < spriv->framebuf[idx].size + len)
{
if(spriv->framebuf[idx].size > SIZE_MAX - (size_t)len)
return 0;
spriv->framebuf[idx].buffer = (uint8_t*) realloc(spriv->framebuf[idx].buffer, spriv->framebuf[idx].size + len);
if(! spriv->framebuf[idx].buffer)
return 0;
spriv->framebuf[idx].alloc_size = spriv->framebuf[idx].size + len;
}
memcpy(&(spriv->framebuf[idx].buffer[spriv->framebuf[idx].size]), ptr, len);
spriv->framebuf[idx].size += len;
return len;
}
static int add_frame(muxer_headers_t *spriv, uint64_t idur, uint8_t *ptr, int len, uint8_t pt, uint64_t dts, uint64_t pts)
{
int idx;
idx = spriv->framebuf_used;
if(idx >= spriv->framebuf_cnt)
{
spriv->framebuf = (mpeg_frame_t*) realloc_struct(spriv->framebuf, (spriv->framebuf_cnt+1), sizeof(mpeg_frame_t));
if(spriv->framebuf == NULL)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "Couldn't realloc frame buffer(idx), abort\n");
return -1;
}
spriv->framebuf[spriv->framebuf_cnt].size = 0;
spriv->framebuf[spriv->framebuf_cnt].alloc_size = 0;
spriv->framebuf[spriv->framebuf_cnt].pos = 0;
spriv->framebuf[spriv->framebuf_cnt].buffer = (uint8_t*) malloc(len);
if(spriv->framebuf[spriv->framebuf_cnt].buffer == NULL)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "Couldn't realloc frame buffer(frame), abort\n");
return -1;
}
spriv->framebuf[spriv->framebuf_cnt].alloc_size = len;
spriv->framebuf_cnt++;
}
if(spriv->framebuf[idx].alloc_size < spriv->framebuf[idx].size + len)
{
if(spriv->framebuf[idx].size > SIZE_MAX - (size_t)len)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "Size overflow, couldn't realloc frame buffer(frame), abort\n");
return -1;
}
spriv->framebuf[idx].buffer = realloc(spriv->framebuf[idx].buffer, spriv->framebuf[idx].size + len);
if(spriv->framebuf[idx].buffer == NULL)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "Couldn't realloc frame buffer(frame), abort\n");
return -1;
}
spriv->framebuf[idx].alloc_size = spriv->framebuf[idx].size + len;
}
memcpy(&(spriv->framebuf[idx].buffer[spriv->framebuf[idx].size]), ptr, len);
spriv->framebuf[idx].size += len;
spriv->framebuf[idx].pos = 0;
spriv->framebuf[idx].type = pt;
spriv->framebuf[idx].idur = idur;
spriv->framebuf[idx].dts = dts;
spriv->framebuf[idx].pts = pts;
spriv->framebuf_used++;
mp_msg(MSGT_MUXER, MSGL_DBG2, "\r\nAdded frame, size: %u, idur: %llu, dts: %llu, pts: %llu, used: %u\r\n", len, idur, dts, pts, spriv->framebuf_used);
return idx;
}
static int analyze_mpa(muxer_stream_t *s)
{
int i = 0, len, max, chans, srate, spf, layer;
int score[4] = {0, 0, 0, 0};
while(i < s->b_buffer_len + 3)
{
if(s->b_buffer[i] == 0xFF && ((s->b_buffer[i+1] & 0xE0) == 0xE0))
{
len = mp_get_mp3_header(&(s->b_buffer[i]), &chans, &srate, &spf, &layer, NULL);
if(len > 0 && (srate == s->wf->nSamplesPerSec) && (i + len <= s->b_buffer_len))
{
score[layer]++;
i += len;
}
}
i++;
}
max = 0;
layer = 2;
for(i = 1; i <= 3; i++)
{
if(score[i] >= max)
{
max = score[i];
layer = i;
}
}
return layer; //actual layer with the highest score
}
extern int aac_parse_frame(uint8_t *buf, int *srate, int *num);
static int parse_audio(muxer_stream_t *s, int finalize, unsigned int *nf, double *timer, double delay, int drop)
{
int i, j, len, chans, srate, spf, layer, dummy, tot, num, frm_idx;
int finished;
unsigned int frames;
uint64_t idur;
double dur;
muxer_headers_t *spriv = (muxer_headers_t *) s->priv;
i = tot = frames = 0;
finished = 0;
while(1)
{
len = 0;
switch(s->wf->wFormatTag)
{
case AUDIO_MP2:
case AUDIO_MP3:
{
if(i + 3 >= s->b_buffer_len)
{
finished = 1;
break;
}
if(s->b_buffer[i] == 0xFF && ((s->b_buffer[i+1] & 0xE0) == 0xE0))
{
len = mp_get_mp3_header(&(s->b_buffer[i]), &chans, &srate, &spf, &layer, NULL);
if(len > 0 && (srate == s->wf->nSamplesPerSec) && (i + len <= s->b_buffer_len)
&& layer == spriv->mpa_layer)
{
dur = (double) spf / (double) srate;
idur = (27000000ULL * spf) / srate;
}
else
len = 0;
}
}
break;
case AUDIO_A52:
{
if(i + 6 >= s->b_buffer_len)
{
finished = 1;
break;
}
if(s->b_buffer[i] == 0x0B && s->b_buffer[i+1] == 0x77)
{
srate = 0;
#ifdef USE_LIBA52
len = a52_syncinfo(&(s->b_buffer[i]), &dummy, &srate, &dummy);
#else
len = mp_a52_framesize(&(s->b_buffer[i]), &srate);
#endif
if((len > 0) && (srate == s->wf->nSamplesPerSec) && (i + len <= s->b_buffer_len))
{
dur = (double) 1536 / (double) srate;
idur = (27000000ULL * 1536) / srate;
}
else
len = 0;
}
}
break;
case AUDIO_AAC1:
case AUDIO_AAC2:
{
if(i + 7 >= s->b_buffer_len)
{
finished = 1;
break;
}
if(s->b_buffer[i] == 0xFF && ((s->b_buffer[i+1] & 0xF6) == 0xF0))
{
len = aac_parse_frame(&(s->b_buffer[i]), &srate, &num);
if((len > 0) && (srate == s->wf->nSamplesPerSec) && (i + len <= s->b_buffer_len))
{
dur = (double) 1024 / (double) srate;
idur = (27000000ULL * 1024 * num) / srate;
}
else
len = 0;
}
}
}
if(finished)
break;
if(!len)
{
i++;
continue;
}
spriv->timer += dur;
if(spriv->drop_delayed_frames && delay < 0 && spriv->timer <= -delay)
{
i += len;
tot = i;
continue;
}
frames++;
fill_last_frame(spriv, &(s->b_buffer[tot]), i - tot);
frm_idx = add_frame(spriv, idur, &(s->b_buffer[i]), len, 0, spriv->last_pts, spriv->last_pts);
if(frm_idx < 0)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "Couldn't add audio frame buffer(frame), abort\n");
goto audio_exit;
}
for(j = frm_idx; j < spriv->framebuf_cnt; j++)
spriv->framebuf[j].pts = spriv->last_pts;
spriv->last_pts += idur;
i += len;
tot = i;
}
audio_exit:
if(tot)
{
memmove(s->b_buffer, &(s->b_buffer[tot]), s->b_buffer_len - tot);
s->b_buffer_len -= tot;
s->b_buffer_ptr += tot;
if(s->b_buffer_len > 0)
memmove(s->b_buffer, &(s->b_buffer[s->b_buffer_ptr]), s->b_buffer_len);
s->b_buffer_ptr = 0;
}
if(finalize)
{
frm_idx = add_frame(spriv, 0, s->b_buffer, s->b_buffer_len, 0, spriv->last_pts, spriv->last_pts);
if(frm_idx >= 0)
{
for(j = frm_idx; j < spriv->framebuf_cnt; j++)
spriv->framebuf[j].pts = spriv->last_pts;
}
}
*nf = frames;
*timer = spriv->timer;
return tot;
}
static void fix_parameters(muxer_stream_t *stream)
{
muxer_headers_t *spriv = stream->priv;
muxer_t *muxer = stream->muxer;
muxer_priv_t *priv = muxer->priv;
if(stream->type == MUXER_TYPE_AUDIO)
{
spriv->is_ready = 1;
if(conf_abuf_size)
spriv->max_buffer_size = conf_abuf_size*1024;
else
spriv->max_buffer_size = 4*1024;
if(stream->wf->wFormatTag == AUDIO_A52)
{
stream->ckid = be2me_32 (0x1bd);
if(priv->is_genmpeg1 || priv->is_genmpeg2)
fix_audio_sys_header(priv, spriv->id, 0xbd, FFMAX(conf_abuf_size, 58)*1024); //only one audio at the moment
spriv->id = 0xbd;
if(!conf_abuf_size)
spriv->max_buffer_size = 16*1024;
}
else if(stream->wf->wFormatTag == AUDIO_AAC1 || stream->wf->wFormatTag == AUDIO_AAC2)
{
priv->use_psm = 1;
}
else if(stream->wf->wFormatTag == AUDIO_MP2 || stream->wf->wFormatTag == AUDIO_MP3)
spriv->is_ready = 0;
}
else //video
{
if(conf_vbuf_size)
spriv->max_buffer_size = conf_vbuf_size*1024;
else
{
if(priv->is_dvd)
spriv->max_buffer_size = 232*1024;
else if(priv->is_xsvcd)
spriv->max_buffer_size = 230*1024;
else if(priv->is_xvcd)
spriv->max_buffer_size = 46*1024;
else
spriv->max_buffer_size = 232*1024; //no profile => unconstrained :) FIXME!!!
}
if(is_mpeg4(stream->bih->biCompression))
spriv->is_ready = 0;
else
spriv->is_ready = 1;
}
}
static void mpegfile_write_chunk(muxer_stream_t *s,size_t len,unsigned int flags, double dts_arg, double pts_arg){
size_t sz = 0;
uint64_t tmp;
muxer_t *muxer = s->muxer;
muxer_priv_t *priv = (muxer_priv_t *)muxer->priv;
muxer_headers_t *spriv = (muxer_headers_t*) s->priv;
float fps;
uint32_t stream_format, nf;
if(s->buffer == NULL)
return;
if(len == -1)
return;
if (s->type == MUXER_TYPE_VIDEO) { // try to recognize frame type...
fps = (float) s->h.dwRate/ (float) s->h.dwScale;
spriv->type = 1;
stream_format = s->bih->biCompression;
if(! spriv->vframes)
{
spriv->last_dts = spriv->last_pts - (uint64_t)(27000000.0f/fps);
mp_msg(MSGT_MUXER, MSGL_INFO,"INITV: %.3lf, %.3lf, fps: %.3f\r\n", (double) spriv->last_pts/27000000.0f, (double) spriv->last_dts/27000000.0f, fps);
}
if(is_mpeg1(stream_format) || is_mpeg2(stream_format))
{
spriv->is_mpeg12 = 1;
spriv->is_ready = 1;
if(len)
sz = parse_mpeg12_video(s, priv, spriv, fps, len);
else {
tmp = (uint64_t) (27000000.0f / fps);
spriv->last_pts += tmp;
spriv->last_dts += tmp;
}
}
else if(is_mpeg4(stream_format))
{
spriv->is_mpeg12 = 0;
spriv->telecine = 0;
if(spriv->size == 0)
priv->use_psm = 1;
if(len)
sz = parse_mpeg4_video(s, priv, spriv, fps, len);
else {
tmp = (uint64_t) (27000000.0f / fps);
spriv->last_pts += tmp;
spriv->last_dts += tmp;
}
}
mp_msg(MSGT_MUXER, MSGL_DBG2,"mpegfile_write_chunk, Video codec=%x, len=%u, mpeg12 returned %u\n", stream_format, (uint32_t) len, (uint32_t) sz);
} else { // MUXER_TYPE_AUDIO
double fake_timer;
spriv->type = 0;
stream_format = s->wf->wFormatTag;
if(s->b_buffer_size - s->b_buffer_len < len)
{
if(s->b_buffer_len > SIZE_MAX - len)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "\nFATAL! couldn't realloc, integer overflow\n");
return;
}
s->b_buffer = realloc(s->b_buffer, len + s->b_buffer_len);
if(s->b_buffer == NULL)
{
mp_msg(MSGT_MUXER, MSGL_FATAL, "\nFATAL! couldn't realloc %d bytes\n", len + s->b_buffer_len);
return;
}
s->b_buffer_size = len + s->b_buffer_len;
mp_msg(MSGT_MUXER, MSGL_DBG2, "REALLOC(%d) bytes to AUDIO backbuffer\n", s->b_buffer_size);
}
memcpy(&(s->b_buffer[s->b_buffer_ptr + s->b_buffer_len]), s->buffer, len);
s->b_buffer_len += len;
if(!spriv->is_ready)
{
if(s->b_buffer_len >= 32*1024)
{
spriv->mpa_layer = analyze_mpa(s);
spriv->is_ready = 1;
}
}
else
{
parse_audio(s, 0, &nf, &fake_timer, priv->init_adelay, priv->drop);
spriv->vframes += nf;
if(! spriv->vframes)
mp_msg(MSGT_MUXER, MSGL_INFO, "AINIT: %.3lf\r\n", (double) spriv->last_pts/27000000.0f);
}
}
if(spriv->psm_fixed == 0) {
add_to_psm(priv, spriv->id, stream_format);
spriv->psm_fixed = 1;
priv->psm_streams_cnt++;
if((priv->psm_streams_cnt == muxer->num_videos + muxer->num_audios) && priv->use_psm)
write_psm_block(muxer, muxer->stream);
}
flush_buffers(muxer, 0);
}
static void mpegfile_write_index(muxer_t *muxer)
{
int i, nf;
double fake_timer;
muxer_priv_t *priv = (muxer_priv_t *) muxer->priv;
mp_msg(MSGT_MUXER, MSGL_INFO, MSGTR_WritingTrailer);
for(i = 0; i < muxer->avih.dwStreams; i++)
{
if(muxer->streams[i]->type == MUXER_TYPE_AUDIO)
parse_audio(muxer->streams[i], 1, &nf, &fake_timer, priv->init_adelay, priv->drop);
}
while(flush_buffers(muxer, 0) > 0);
flush_buffers(muxer, 1);
if(priv->is_genmpeg1 || priv->is_genmpeg2)
write_mpeg_pack(muxer, NULL, muxer->stream, 1); //insert fake Nav Packet
mp_msg(MSGT_MUXER, MSGL_INFO, "\nOverhead: %.3lf%% (%"PRIu64" / %"PRIu64")\n", 100.0 * (double)priv->headers_size / (double)priv->data_size, priv->headers_size, priv->data_size);
}
static void mpegfile_write_header(muxer_t *muxer)
{
muxer_priv_t *priv = (muxer_priv_t*) muxer->priv;
mp_msg(MSGT_MUXER, MSGL_INFO, MSGTR_WritingHeader);
priv->headers_cnt++;
if((priv->is_genmpeg1 || priv->is_genmpeg2) && (priv->headers_cnt == muxer->avih.dwStreams))
{
int i;
for(i = 0; i < muxer->avih.dwStreams; i++)
{
priv->sys_info.streams[i].bufsize = muxer->streams[i]->h.dwSuggestedBufferSize;
mp_msg(MSGT_MUXER, MSGL_DBG2, "IDX: %d, BUFSIZE: %u\n", i, priv->sys_info.streams[i].bufsize);
}
}
//write the first system header only for generic mpeg1/2 muxes, and only when we have collected all necessary infos
if(priv->is_genmpeg1 || priv->is_genmpeg2 || ((priv->is_xvcd || priv->is_xsvcd) && (priv->headers_cnt == 1)))
{
write_mpeg_pack(muxer, NULL, muxer->stream, 0);
priv->update_system_header = 0;
}
return;
}
static void setup_sys_params(muxer_priv_t *priv)
{
if(priv->is_dvd)
{
int v = (conf_vbuf_size ? conf_vbuf_size : 232);
int a1 = (conf_abuf_size ? conf_abuf_size : 4);
int a2 = (conf_abuf_size>58 ? conf_abuf_size : 58);
priv->sys_info.cnt = 4;
priv->sys_info.streams[0].id = 0xb9;
priv->sys_info.streams[0].type = 1;
priv->sys_info.streams[0].bufsize = v*1024;
priv->sys_info.streams[1].id = 0xb8;
priv->sys_info.streams[1].type = 0;
priv->sys_info.streams[1].bufsize = a1*1024;
priv->sys_info.streams[2].id = 0xbd;
priv->sys_info.streams[2].type = 1;
priv->sys_info.streams[2].bufsize = a2*1024;
priv->sys_info.streams[3].id = 0xbf;
priv->sys_info.streams[3].type = 1;
priv->sys_info.streams[3].bufsize = 2*1024;
}
else if(priv->is_xvcd || priv->is_xsvcd)
{
int v = (conf_vbuf_size ? conf_vbuf_size : (priv->is_xvcd ? 46: 230));
int a1 = (conf_abuf_size ? conf_abuf_size : 4);
priv->sys_info.cnt = 2;
priv->sys_info.streams[0].id = 0xe0;
priv->sys_info.streams[0].type = 1;
priv->sys_info.streams[0].bufsize = v*1024;
priv->sys_info.streams[1].id = 0xc0;
priv->sys_info.streams[1].type = 0;
priv->sys_info.streams[1].bufsize = a1*1024;
}
else
priv->sys_info.cnt = 0;
}
/* excerpt from DGPulldown Copyright (C) 2005-2006, Donald Graft */
static void generate_flags(int source, int target)
{
unsigned int i, trfp;
uint64_t dfl,tfl;
unsigned char ormask[4] = {0x0, 0x2, 0x80, 0x82};
dfl = (target - source) << 1;
tfl = source >> 1;
trfp = 0;
for(i = 0; i < MAX_PATTERN_LENGTH; i++)
{
tfl += dfl;
if(tfl >= source)
{
tfl -= source;
bff_mask[i] = ormask[trfp + 1];
trfp ^= 2;
}
else
bff_mask[i] = ormask[trfp];
}
}
int muxer_init_muxer_mpeg(muxer_t *muxer){
muxer_priv_t *priv;
priv = (muxer_priv_t *) calloc(1, sizeof(muxer_priv_t));
if(priv == NULL)
return 0;
priv->update_system_header = 1;
//calloc() already zero-ed all flags, so we assign only the ones we need
if(conf_mux != NULL) {
if(! strcasecmp(conf_mux, "mpeg1"))
{
priv->mux = MUX_MPEG1;
priv->packet_size = 2048;
priv->is_genmpeg1 = 1;
priv->muxrate = 1800 * 125; //Constrained parameters
}
else if(! strcasecmp(conf_mux, "dvd"))
{
priv->mux = MUX_MPEG2;
priv->is_dvd = 1;
priv->packet_size = 2048;
priv->muxrate = 10080 * 125;
}
else if(! strcasecmp(conf_mux, "xsvcd"))
{
priv->mux = MUX_MPEG2;
priv->is_xsvcd = 1;
priv->packet_size = 2324;
priv->muxrate = 150*2324;
priv->ts_allframes = 1;
}
else if(! strcasecmp(conf_mux, "xvcd"))
{
priv->mux = MUX_MPEG1;
priv->is_xvcd = 1;
priv->packet_size = 2324;
priv->muxrate = 75*2352;
priv->ts_allframes = 1;
}
else if(! strcasecmp(conf_mux, "pes1"))
{
priv->mux = MUX_MPEG1;
priv->rawpes = 1;
priv->packet_size = 2048;
priv->muxrate = 10080 * 125;
priv->ts_allframes = 1;
}
else if(! strcasecmp(conf_mux, "pes2"))
{
priv->mux = MUX_MPEG2;
priv->rawpes = 1;
priv->packet_size = 2048;
priv->muxrate = 10080 * 125;
priv->ts_allframes = 1;
}
else
{
if(strcasecmp(conf_mux, "mpeg2"))
mp_msg(MSGT_MUXER, MSGL_ERR, "Unknown format %s, default to mpeg2\n", conf_mux);
priv->mux = MUX_MPEG2;
priv->is_genmpeg2 = 1;
priv->packet_size = 2048;
priv->muxrate = 1800 * 125; //Constrained parameters
}
}
if(conf_ts_allframes)
priv->ts_allframes = 1;
if(conf_muxrate > 0)
priv->muxrate = conf_muxrate * 125; // * 1000 / 8
if(conf_packet_size)
priv->packet_size = conf_packet_size;
priv->delta_scr = (uint64_t) (90000.0f*300.0f*(double)priv->packet_size/(double)priv->muxrate);
mp_msg(MSGT_MUXER, MSGL_INFO, "PACKET SIZE: %u bytes, deltascr: %llu\n", priv->packet_size, priv->delta_scr);
setup_sys_params(priv);
if(conf_vaspect > 0)
{
int asp = (int) (conf_vaspect * 1000.0f);
if(asp >= 1332 && asp <= 1334)
priv->vaspect = ASPECT_4_3;
else if(asp >= 1776 && asp <= 1778)
priv->vaspect = ASPECT_16_9;
else if(asp >= 2209 && asp <= 2211)
priv->vaspect = ASPECT_2_21_1;
else if(asp == 1000)
priv->vaspect = ASPECT_1_1;
else
mp_msg(MSGT_MUXER, MSGL_ERR, "ERROR: unrecognized aspect %.3f\n", conf_vaspect);
}
priv->vframerate = 0; // no change
if(conf_telecine && conf_vframerate > 0)
{
mp_msg(MSGT_MUXER, MSGL_ERR, "ERROR: options 'telecine' and 'vframerate' are mutually exclusive, vframerate disabled\n");
conf_vframerate = 0;
}
if(conf_telecine == TELECINE_FILM2PAL)
{
if(conf_telecine_src==0.0f) conf_telecine_src = 24000.0/1001.0;
conf_telecine_dest = 25;
conf_telecine = TELECINE_DGPULLDOWN;
}
else if(conf_telecine == PULLDOWN32)
{
if(conf_telecine_src==0.0f) conf_telecine_src = 24000.0/1001.0;
conf_telecine_dest = 30000.0/1001.0;
conf_telecine = TELECINE_DGPULLDOWN;
}
if(conf_telecine_src>0 && conf_telecine_dest>0 && conf_telecine_src < conf_telecine_dest)
{
int sfps, tfps;
sfps = (int) (conf_telecine_src * 1001 + 0.5);
tfps = (int) (conf_telecine_dest * 1001 + 0.5);
if(sfps % 2 || tfps % 2)
{
sfps *= 2;
tfps *= 2;
}
if(((tfps - sfps)>>1) > sfps)
{
mp_msg(MSGT_MUXER, MSGL_ERR, "ERROR! Framerate increment must be <= 1.5, telecining disabled\n");
conf_telecine = 0;
}
else
{
generate_flags(sfps, tfps);
conf_telecine = TELECINE_DGPULLDOWN;
conf_vframerate = conf_telecine_dest;
}
}
if(conf_vframerate)
{
int fps;
fps = (int) (conf_vframerate * 1001 + 0.5);
switch(fps)
{
case 24000:
priv->vframerate = FRAMERATE_23976;
break;
case 24024:
priv->vframerate = FRAMERATE_24;
break;
case 25025:
priv->vframerate = FRAMERATE_25;
break;
case 30000:
priv->vframerate = FRAMERATE_2997;
break;
case 30030:
priv->vframerate = FRAMERATE_30;
break;
case 50050:
priv->vframerate = FRAMERATE_50;
break;
case 60000:
priv->vframerate = FRAMERATE_5994;
break;
case 60060:
priv->vframerate = FRAMERATE_60;
break;
default:
{
mp_msg(MSGT_MUXER, MSGL_ERR, "WRONG FPS: %d/1000, ignoring\n", fps);
if(conf_telecine)
mp_msg(MSGT_MUXER, MSGL_ERR, "DISABLED TELECINING\n");
conf_telecine = 0;
}
}
}
priv->vwidth = (uint16_t) conf_vwidth;
priv->vheight = (uint16_t) conf_vheight;
priv->panscan_width = (uint16_t) conf_panscan_width;
priv->panscan_height = (uint16_t) conf_panscan_height;
priv->vbitrate = ((conf_vbitrate) * 10) >> 2; //*1000 / 400
if(priv->vaspect || priv->vframerate || priv->vwidth || priv->vheight || priv->vbitrate || priv->panscan_width || priv->panscan_height)
{
priv->patch_seq = priv->vaspect || priv->vframerate || priv->vwidth || priv->vheight || priv->vbitrate;
priv->patch_sde = priv->panscan_width || priv->panscan_height;
mp_msg(MSGT_MUXER, MSGL_INFO, "MPEG MUXER, patching");
if(priv->vwidth || priv->vheight)
mp_msg(MSGT_MUXER, MSGL_INFO, " resolution to %dx%d", priv->vwidth, priv->vheight);
if(priv->panscan_width || priv->panscan_height)
mp_msg(MSGT_MUXER, MSGL_INFO, " panscan to to %dx%d", priv->panscan_width, priv->panscan_height);
if(priv->vframerate)
mp_msg(MSGT_MUXER, MSGL_INFO, " framerate to %s fps", framerates[priv->vframerate]);
if(priv->vaspect)
mp_msg(MSGT_MUXER, MSGL_INFO, " aspect ratio to %s", aspect_ratios[priv->vaspect]);
if(priv->vbitrate)
mp_msg(MSGT_MUXER, MSGL_INFO, " bitrate to %u", conf_vbitrate);
mp_msg(MSGT_MUXER, MSGL_INFO, "\n");
}
priv->has_video = priv->has_audio = 0;
muxer->sysrate = priv->muxrate; // initial muxrate = constrained stream parameter
priv->scr = muxer->file_end = 0;
if(conf_init_adelay)
priv->init_adelay = - (double) conf_init_adelay / (double) 1000.0;
priv->drop = conf_drop;
priv->buff = (uint8_t *) malloc(priv->packet_size);
if((priv->buff == NULL))
{
mp_msg(MSGT_MUXER, MSGL_ERR, "\nCouldn't allocate %d bytes, exit\n", priv->packet_size);
return 0;
}
muxer->priv = (void *) priv;
muxer->cont_new_stream = &mpegfile_new_stream;
muxer->cont_write_chunk = &mpegfile_write_chunk;
muxer->cont_write_header = &mpegfile_write_header;
muxer->cont_write_index = &mpegfile_write_index;
muxer->fix_stream_parameters = &fix_parameters;
return 1;
}