{ "1920x1080 24p", 1920,1080, 24., 1, ILACE_MODE_NOTINTERLACED },
{ "1920x1080 25i", 1920,1080, 25., 1, ILACE_MODE_TOP_FIRST },
{ "1920x1080 23.976p", 1920,1080, 23.976, 1, ILACE_MODE_NOTINTERLACED },
- { "1440x1080 29.97i", 1440,1080, 29.97, 1, ILACE_MODE_TOP_FIRST },
- { "1440x1080 25i", 1440,1080, 25., 1, ILACE_MODE_TOP_FIRST },
- { "1440x1080 24p", 1440,1080, 24., 1, ILACE_MODE_NOTINTERLACED },
- { "1440x1080 23.976p", 1440,1080, 23.976, 1, ILACE_MODE_NOTINTERLACED },
+ { "1440x1080 29.97i", 1440,1080, 29.97, -1, ILACE_MODE_TOP_FIRST },
+ { "1440x1080 25i", 1440,1080, 25., -1, ILACE_MODE_TOP_FIRST },
+ { "1440x1080 24p", 1440,1080, 24., -1, ILACE_MODE_NOTINTERLACED },
+ { "1440x1080 23.976p", 1440,1080, 23.976,-1, ILACE_MODE_NOTINTERLACED },
{ "1280x720 59.94p", 1280,720, 59.94, 1, ILACE_MODE_NOTINTERLACED },
{ "1280x720 50p", 1280,720, 50., 1, ILACE_MODE_NOTINTERLACED },
{ "1280x720 24p", 1280,720, 24., 1, ILACE_MODE_NOTINTERLACED },
const double CreateBD_Thread::BD_ASPECT_WIDTH = 4.;
const double CreateBD_Thread::BD_ASPECT_HEIGHT = 3.;
const double CreateBD_Thread::BD_FRAMERATE = 24000. / 1001.;
-const int CreateBD_Thread::BD_MAX_BITRATE = 40000000;
-//const int CreateBD_Thread::BD_MAX_BITRATE = 8000000;
+//const int CreateBD_Thread::BD_MAX_BITRATE = 40000000;
+const int CreateBD_Thread::BD_MAX_BITRATE = 10000000;
const int CreateBD_Thread::BD_CHANNELS = 2;
const int CreateBD_Thread::BD_WIDE_CHANNELS = 6;
const double CreateBD_Thread::BD_SAMPLERATE = 48000;
edit->startproject, edit->length,
PLUGIN_STANDALONE, 0, &keyframe, 0);
}
+ vtrk->optimize();
}
}
if( use_resize_tracks )
edit->startproject, edit->length,
PLUGIN_STANDALONE, 0, default_keyframe, 0);
}
+ vtrk->optimize();
}
return 0;
}
bd_width = bd_formats[use_standard].w;
bd_height = bd_formats[use_standard].h;
bd_framerate = bd_formats[use_standard].framerate;
- bd_aspect_width = bd_formats[use_standard].wide ?
- BD_WIDE_ASPECT_WIDTH : BD_ASPECT_WIDTH;
- bd_aspect_height = bd_formats[use_standard].wide ?
- BD_WIDE_ASPECT_HEIGHT : BD_ASPECT_HEIGHT;
+ int wide = bd_formats[use_standard].wide;
+ bd_aspect_width = wide < 0 ? 1. :
+ wide > 0 ? BD_WIDE_ASPECT_WIDTH : BD_ASPECT_WIDTH;
+ bd_aspect_height = wide < 0 ? 1. :
+ wide > 0 ? BD_WIDE_ASPECT_HEIGHT : BD_ASPECT_HEIGHT;
bd_interlace_mode = bd_formats[use_standard].interlaced;
double bd_aspect = bd_aspect_width / bd_aspect_height;
float aw, ah;
MWindow::create_aspect_ratio(aw, ah, w, h);
double aspect = ah > 0 ? aw / ah : 1;
- if( !EQUIV(aspect, bd_aspect) ) use_scale = Rescale::scaled;
+ if( wide >= 0 && !EQUIV(aspect, bd_aspect) )
+ use_scale = Rescale::scaled;
}
for( int i=0; i<trk->plugin_set.size(); ++i ) {
for(Plugin *plugin = (Plugin*)trk->plugin_set[i]->first;
#define BCTEXTLEN 1024
#define BLURAY_TS_PKTSZ 192L
+static const int bd_sig = 2;
extern "C" {
#include "libavfilter/buffersrc.h"
int64_t fpos, len;
public:
bs_length() { fpos = len = 0; }
+ int64_t bs_posb(bs_file &bs) { return bs.posb() - fpos; }
void bs_len(bs_file &bs, int n) {
bs.write(len, n); fpos = bs.posb();
}
void bs_end(bs_file &bs) {
- len = bs.posb() - fpos;
+ len = bs_posb(bs);
}
void bs_ofs(bs_file &bs, int n) {
bs.write(fpos-n/8, n);
}
+ void bs_zofs(bs_file &bs, int n) {
+ bs.write(!len ? 0 : fpos-n/8, n);
+ }
};
class _bd_stream_info {
~clpi_cpi() { remove_all_objects(); }
};
+class clpi_cmrk : public bs_length {
+public:
+ int write();
+
+ clpi_cmrk() {}
+ ~clpi_cmrk() {}
+};
+
+
class bd_uo_mask {
public:
unsigned int menu_call : 1;
int write_pip_metadata_extension();
int write();
- mpls_pl() { sig = 1; }
+ mpls_pl() { sig = bd_sig; }
~mpls_pl() {
play_item.remove_all_objects();
sub_path.remove_all_objects();
clpi_extents extents;
clpi_programs programs_ss;
clpi_cpi cpi_ss;
+ clpi_cmrk cmrk;
int write_header();
int write();
int write_clpi_extension(int id1, int id2, void *handle);
int write_mpls_extension(int id1, int id2, void *handle);
- clpi_cl() { sig = 1; }
+ clpi_cl() { sig = bd_sig; }
~clpi_cl() {}
};
int sig;
ArrayList<movie_obj *> movies;
- movie_file() { sig = 1; }
+ movie_file() { sig = bd_sig; }
~movie_file() {
movies.remove_all_objects();
}
int write();
index_file() {
- sig = 1;
+ sig = bd_sig;
memset(user_data, 0, sizeof(user_data));
}
~index_file() {
int sig;
int write();
- bdid() { sig = 1; }
+ bdid() { sig = bd_sig; }
~bdid() {}
};
ArrayList<clpi_cl *> cl;
ArrayList<mpls_pl *> pl;
+ void add_movie(uint32_t *ops, int n);
int compose();
int write(char *fn);
bs.writeb("INDX", 4);
bs.writeb(sig == 1 ? "0100" : "0200", 4);
bs_ofs(bs, 32);
- exten.bs_ofs(bs, 32);
+ exten.bs_zofs(bs, 32);
int appinfo_start = 0x28;
bs.posb(appinfo_start);
appinf.bs_len(bs, 32);
return 1;
};
+ bs.padb(0x15 - bs_posb(bs));
bs_end(bs);
return 0;
}
return 0;
}
+int
+clpi_cmrk::write()
+{
+ bs_len(bs, 32);
+ bs_end(bs);
+ return 0;
+}
+
int
clpi_extents::write()
{
if( programs.write() ) return 1;
cpi_start_addr = bs.posb();
if( cpi.write() ) return 1;
+ clip_mark_start_addr = bs.posb();
+ if( cmrk.write() ) return 1;
//if( has_ext_data ) {
// ext_data_start_addr = bs.pos();
// bdmv_write_extension_data(write_clpi_extension, this);
fprintf(stderr, "unrecognized stream type %02x\n", stream_type);
break;
};
+ bs.padb(9 - strm.bs_posb(bs));
strm.bs_end(bs);
code.bs_len(bs, 8);
fprintf(stderr, "mpls_stream: unrecognized coding type %02x\n", coding_type);
break;
};
+ bs.padb(5 - code.bs_posb(bs));
code.bs_end(bs);
return 0;
}
char bdmv_path[BCTEXTLEN];
sprintf(bdmv_path, "%s/BDMV", path);
if( mk_bdmv_dir(bdmv_path) ) return 1;
+ char cert_path[BCTEXTLEN];
+ sprintf(cert_path, "%s/CERTIFICATE", path);
+ if( mk_bdmv_dir(cert_path) ) return 1;
+ char cert_backup[BCTEXTLEN];
+ sprintf(cert_backup, "%s/BACKUP", cert_path);
+ if( mk_bdmv_dir(cert_backup) ) return 1;
char stream_path[BCTEXTLEN];
- sprintf(stream_path, "%s/BDMV/STREAM", path);
+ sprintf(stream_path, "%s/STREAM", bdmv_path);
if( mk_dir(stream_path) ) return 1;
char auxdata_path[BCTEXTLEN];
- sprintf(auxdata_path, "%s/BDMV/AUXDATA", path);
+ sprintf(auxdata_path, "%s/AUXDATA", bdmv_path);
if( mk_dir(auxdata_path) ) return 1;
char meta_path[BCTEXTLEN];
- sprintf(meta_path, "%s/BDMV/META", path);
+ sprintf(meta_path, "%s/META", bdmv_path);
if( mk_dir(meta_path) ) return 1;
char backup_path[BCTEXTLEN];
sprintf(backup_path, "%s/BACKUP", bdmv_path);
uint32_t pkt = mp->pos / BLURAY_TS_PKTSZ;
if( last_pkt >= pkt ) continue;
last_pkt = pkt;
- int64_t coarse_pts = (pts >> 18) & ~0x01;
+ int64_t coarse_pts = (pts >> 18); // & ~0x01;
int64_t fine_pts = (pts & 0x7ffff) >> 8;
uint32_t mpkt = pkt & ~0x1ffff;
- if( !cp || cp->pts_ep != coarse_pts || cp->spn_ep != mpkt ) {
+ if( !cp || cp->pts_ep != coarse_pts || mpkt > cp->spn_ep ) {
cp = new clpi_ep_coarse();
map->coarse.append(cp);
cp->ref_ep_fine_id = map->fine.size();
cp->pts_ep = coarse_pts;
- cp->spn_ep = mpkt;
+ cp->spn_ep = pkt;
}
clpi_ep_fine *fp = new clpi_ep_fine();
map->fine.append(fp);
int stream_type = 0;
switch (codec_id) {
case AV_CODEC_ID_MPEG1VIDEO:
+ stream_type = BLURAY_STREAM_TYPE_VIDEO_MPEG1;
+ break;
case AV_CODEC_ID_MPEG2VIDEO:
stream_type = BLURAY_STREAM_TYPE_VIDEO_MPEG2;
break;
stream_type = BLURAY_STREAM_TYPE_VIDEO_H264;
break;
case AV_CODEC_ID_MP2:
- case AV_CODEC_ID_MP3:
stream_type = BLURAY_STREAM_TYPE_AUDIO_MPEG1;
break;
+ case AV_CODEC_ID_MP3:
+ stream_type = BLURAY_STREAM_TYPE_AUDIO_MPEG2;
+ break;
case AV_CODEC_ID_AC3:
stream_type = BLURAY_STREAM_TYPE_AUDIO_AC3;
break;
+ case AV_CODEC_ID_EAC3:
+ stream_type = BLURAY_STREAM_TYPE_AUDIO_AC3PLUS;
+ break;
case AV_CODEC_ID_DTS:
stream_type = BLURAY_STREAM_TYPE_AUDIO_DTS;
break;
case AV_CODEC_ID_TRUEHD:
stream_type = BLURAY_STREAM_TYPE_AUDIO_TRUHD;
break;
+ case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
+ stream_type = BLURAY_STREAM_TYPE_SUB_PG;
+ break;
default:
fprintf(stderr, "unknown bluray stream type %s\n", avcodec_get_name(codec_id));
exit(1);
switch( type ) {
case AVMEDIA_TYPE_VIDEO: break;
case AVMEDIA_TYPE_AUDIO: break;
+ case AVMEDIA_TYPE_SUBTITLE: break;
default: continue;
}
stream *s = new stream(type, i);
s->coding_type = bd_stream_type(codec_id);
s->format = bd_audio_format(st->codec->channels);
s->rate = bd_audio_rate(st->codec->sample_rate);
- strcpy((char*)s->lang, "und");
+ strcpy((char*)s->lang, "eng");
+ break; }
+ case AVMEDIA_TYPE_SUBTITLE: {
+ s->coding_type = bd_stream_type(codec_id);
+ AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", 0, 0);
+ strncpy((char*)s->lang, lang ? lang->value : "und", sizeof(s->lang));
break; }
default:
break;
program *pgm = new program(-1, 1);
pgm->ep_pid = ep_pid;
pgm->pmt_pid = 0x1000;
- pgm->pcr_pid = 0x100;
+ pgm->pcr_pid = 0x1001;
pgm->duration = 0;
for( int jj=0; jj<streams.size(); ++jj ) {
AVStream *st = fmt_ctx->streams[jj];
if( ep_pid < 0 ) ep_pid = st->id;
break;
case AVMEDIA_TYPE_AUDIO:
+ case AVMEDIA_TYPE_SUBTITLE:
break;
default:
continue;
int ret = 0;
AVPacket ipkt;
av_init_packet(&ipkt);
-#if 0
+#if 1
// zero pts at pos zero
for( int i=0; i<programs.size(); ++i ) {
program *p = programs[i];
return ret != AVERROR_EOF ? -1 : 0;
}
+void
+Media::add_movie(uint32_t *ops, int n)
+{
+ movie_obj *mp = new movie_obj();
+ mp->resume_intention_flag = 1;
+ uint32_t *eop = ops + n/sizeof(*ops);
+ while( ops < eop ) {
+ command_obj *cmd = new command_obj();
+ cmd->cmd = htobe32(*ops++);
+ cmd->dst = *ops++;
+ cmd->src = *ops++;
+ mp->cmds.append(cmd);
+ }
+ mov.movies.append(mp);
+}
+
int
Media::compose()
{
-// index
- bs.init();
- idx.sig = 1;
- idx.first_play.set_hdmv(0, pb_typ_movie);
- idx.top_menu.set_hdmv(0xffff, pb_typ_iactv);
-
// movie
bs.init();
- mov.sig = 1;
+
+// top menu
+ int top_menu_obj = mov.movies.size();
movie_obj *mp = new movie_obj();
mp->resume_intention_flag = 1;
command_obj *cmd = new command_obj();
cmd->cmd = htobe32(0x21810000); cmd->dst = 1; cmd->src = 0;
mp->cmds.append(cmd); // JUMP_TITLE 1
- cmd = new command_obj();
- cmd->cmd = htobe32(0x00020000); cmd->dst = 0; cmd->src = 0;
- mp->cmds.append(cmd);
- mov.movies.append(mp); // BREAK
-
+ mov.movies.append(mp);
+// titles
for( int ii=0; ii<size(); ++ii ) {
mp = new movie_obj();
mp->resume_intention_flag = 1;
mov.movies.append(mp); // BREAK
}
+// first play
+ int first_play_obj = mov.movies.size();
mp = new movie_obj();
mp->resume_intention_flag = 1;
cmd = new command_obj();
- cmd->cmd = htobe32(0x21810000); cmd->dst = 1; cmd->src = 0;
- mp->cmds.append(cmd); // JUMP_TITLE 1
- cmd = new command_obj();
- cmd->cmd = htobe32(0x00020000); cmd->dst = 0; cmd->src = 0;
- mp->cmds.append(cmd);
- mov.movies.append(mp); // BREAK
+ cmd->cmd = htobe32(0x21810000); cmd->dst = 0; cmd->src = 0;
+ mp->cmds.append(cmd); // JUMP_TITLE 0 ; top menu
+ mov.movies.append(mp);
+
+// index
+ bs.init();
+ idx.first_play.set_hdmv(first_play_obj, pb_typ_iactv);
+ idx.top_menu.set_hdmv(top_menu_obj, pb_typ_iactv);
title_obj *tp = 0;
// clips
cp->clip.clip_stream_type = 1;
cp->clip.application_type = BLURAY_APP_TYPE_MAIN_MOVIE;
cp->clip.ts_recording_rate = ip->bit_rate;
- uint32_t ts_pkt_count = ip->file_size / BLURAY_TS_PKTSZ + 1;
+ uint32_t ts_pkt_count = ip->file_size / BLURAY_TS_PKTSZ;
cp->clip.num_source_packets = ts_pkt_count;
cp->clip.ts_type_info.validity = 0x80;
strcpy(cp->clip.ts_type_info.format_id, "HDMV");
s->aspect = sp->aspect;
break;
case AVMEDIA_TYPE_AUDIO:
+ case AVMEDIA_TYPE_SUBTITLE:
memcpy(s->lang,sp->lang,sizeof(s->lang));
break;
default:
p->streams.append(s);
}
clpi_ep_map_entry *map = new clpi_ep_map_entry(pgm->ep_pid);
+ map->ep_stream_type = 1;
pgm->build_toc(map);
cp->cpi.append(map);
cp->programs.append(p);
pgm = ip->prog();
mpls_pi *pi = new mpls_pi();
pi->connection_condition = 1; // seamless
+// pi->uo_mask.xxx = 1;
pi->in_time = pgm->start_time;
pi->out_time = pgm->end_time;
if( ip->still )
switch( sp->type ) {
case AVMEDIA_TYPE_VIDEO: break;
case AVMEDIA_TYPE_AUDIO: break;
+ case AVMEDIA_TYPE_SUBTITLE: break;
default: continue;
}
mpls_stream *ps = new mpls_stream();
memcpy(ps->lang, sp->lang, sizeof(ps->lang));
pi->stn.audio.append(ps);
break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ memcpy(ps->lang, sp->lang, sizeof(ps->lang));
+ pi->stn.pg.append(ps);
+ break;
default:
break;
}
pp->play_item.append(pi);
}
// chapter marks every ch_duration ticks
- int64_t ch_duration = 45000 * 60*10;
+ int64_t ch_duration = 45000 * 60*5;
int64_t mrktm = ch_duration;
int64_t plytm = 0;
int pmark = 0, pitem = 0;
#include "file.h"
#include "ffmpeg.h"
#include "indexfile.h"
+#include "interlacemodes.h"
#include "libdv.h"
#include "libmjpeg.h"
#include "mainerror.h"
frame_rate = 0;
aspect_ratio = 0;
length = 0;
+ interlaced = 0;
+ top_field_first = 0;
}
FFVideoStream::~FFVideoStream()
int FFVideoStream::encode_frame(AVPacket *pkt, AVFrame *frame, int &got_packet)
{
+ if( frame ) {
+ frame->interlaced_frame = interlaced;
+ frame->top_field_first = top_field_first;
+ }
int ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet);
if( ret < 0 ) {
ff_err(ret, "FFVideoStream::encode_frame: encode video failed\n");
int width = 1000000, height = width * sample_aspect + 0.5;
float w, h;
MWindow::create_aspect_ratio(w, h, width, height);
- return (AVRational){(int)h, (int)w};
+ return (AVRational){(int)w, (int)h};
#else
// square pixels
return (AVRational){1, 1};
int FFMPEG::get_file_format()
{
- int ret = 0;
+ char audio_muxer[BCSTRLEN], video_muxer[BCSTRLEN];
char audio_format[BCSTRLEN], video_format[BCSTRLEN];
- file_format[0] = audio_format[0] = video_format[0] = 0;
+ audio_muxer[0] = audio_format[0] = 0;
+ video_muxer[0] = video_format[0] = 0;
Asset *asset = file_base->asset;
- if( !ret && asset->audio_data )
- ret = get_format(audio_format, "audio", asset->acodec);
- if( !ret && asset->video_data )
- ret = get_format(video_format, "video", asset->vcodec);
- if( !ret && !audio_format[0] && !video_format[0] )
+ int ret = asset ? 0 : 1;
+ if( !ret && asset->audio_data ) {
+ if( !(ret=get_format(audio_format, "audio", asset->acodec)) ) {
+ if( get_format(audio_muxer, "format", audio_format) ) {
+ strcpy(audio_muxer, audio_format);
+ audio_format[0] = 0;
+ }
+ }
+ }
+ if( !ret && asset->video_data ) {
+ if( !(ret=get_format(video_format, "video", asset->vcodec)) ) {
+ if( get_format(video_muxer, "format", video_format) ) {
+ strcpy(video_muxer, video_format);
+ video_format[0] = 0;
+ }
+ }
+ }
+ if( !ret && !audio_muxer[0] && !video_muxer[0] )
ret = 1;
+ if( !ret && audio_muxer[0] && video_muxer[0] &&
+ strcmp(audio_muxer, video_muxer) ) ret = -1;
if( !ret && audio_format[0] && video_format[0] &&
strcmp(audio_format, video_format) ) ret = -1;
if( !ret )
- strcpy(file_format, audio_format[0] ? audio_format : video_format);
+ strcpy(file_format, !audio_format[0] && !video_format[0] ?
+ (audio_muxer[0] ? audio_muxer : video_muxer) :
+ (audio_format[0] ? audio_format : video_format));
return ret;
}
{
while( *cp == ' ' || *cp == '\t' ) ++cp;
char *bp = cp;
- while( *cp && *cp != ' ' && *cp != '\t' && *cp != '=' ) ++cp;
+ while( *cp && *cp != ' ' && *cp != '\t' && *cp != '=' && *cp != '\n' ) ++cp;
int len = cp - bp;
if( !len || len > BCSTRLEN-1 ) return 1;
while( bp < cp ) *tag++ = *bp++;
char *codec, char *codec_options, int len)
{
char default_file[BCTEXTLEN];
- FFMPEG::set_option_path(default_file, "%s/%s.dfl", path, type);
+ set_option_path(default_file, "%s/%s.dfl", path, type);
FILE *fp = fopen(default_file,"r");
if( !fp ) return 1;
fgets(codec, BCSTRLEN, fp);
codec_options += n; len -= n;
}
fclose(fp);
- FFMPEG::set_option_path(default_file, "%s/%s", path, codec);
- return FFMPEG::load_options(default_file, codec_options, len);
+ set_option_path(default_file, "%s/%s", path, codec);
+ return load_options(default_file, codec_options, len);
}
void FFMPEG::set_asset_format(Asset *asset, const char *text)
{
if( asset->format != FILE_FFMPEG ) return;
- strcpy(asset->fformat, text);
+ if( text != asset->fformat )
+ strcpy(asset->fformat, text);
if( !asset->ff_audio_options[0] ) {
asset->audio_data = !load_defaults("audio", text, asset->acodec,
asset->ff_audio_options, sizeof(asset->ff_audio_options));
return 0;
}
-int FFMPEG::read_options(const char *options, AVDictionary *&opts)
+int FFMPEG::read_options(const char *options, AVDictionary *&opts, int skip)
{
FILE *fp = fopen(options,"r");
if( !fp ) return 1;
- int ret = read_options(fp, options, opts);
+ int ret = 0;
+ while( !ret && --skip >= 0 ) {
+ int ch = getc(fp);
+ while( ch >= 0 && ch != '\n' ) ch = getc(fp);
+ if( ch < 0 ) ret = 1;
+ }
+ if( !ret )
+ ret = read_options(fp, options, opts);
fclose(fp);
return ret;
}
char line[BCTEXTLEN];
while( !ret && fgets(line, sizeof(line), fp) ) {
line[sizeof(line)-1] = 0;
- ++no;
if( line[0] == '#' ) continue;
if( line[0] == '\n' ) continue;
char key[BCSTRLEN], val[BCTEXTLEN];
}
ff_lock("FFMPEG::init_encoder");
av_register_all();
- avformat_alloc_output_context2(&fmt_ctx, 0, file_format, filename);
+ char format[BCSTRLEN];
+ if( get_format(format, "format", file_format) )
+ strcpy(format, file_format);
+ avformat_alloc_output_context2(&fmt_ctx, 0, format, filename);
if( !fmt_ctx ) {
eprintf(_("failed: %s\n"), filename);
ret = 1;
ctx->time_base = (AVRational) { frame_rate.den, frame_rate.num };
st->time_base = ctx->time_base;
vid->writing = -1;
+ vid->interlaced = asset->interlace_mode == ILACE_MODE_TOP_FIRST ||
+ asset->interlace_mode == ILACE_MODE_BOTTOM_FIRST ? 1 : 0;
+ vid->top_field_first = asset->interlace_mode == ILACE_MODE_TOP_FIRST ? 1 : 0;
break; }
default:
eprintf(_("not audio/video, %s:%s\n"), codec_name, filename);
return -1;
}
+ int prog_id = 1;
+ AVProgram *prog = av_new_program(fmt_ctx, prog_id);
+ for( int i=0; i< ffvideo.size(); ++i )
+ av_program_add_stream_index(fmt_ctx, prog_id, ffvideo[i]->fidx);
+ for( int i=0; i< ffaudio.size(); ++i )
+ av_program_add_stream_index(fmt_ctx, prog_id, ffaudio[i]->fidx);
+ int pi = fmt_ctx->nb_programs;
+ while( --pi >= 0 && fmt_ctx->programs[pi]->id != prog_id );
+ AVDictionary **meta = &prog->metadata;
+ av_dict_set(meta, "service_provider", "cin5", 0);
+ const char *path = fmt_ctx->filename, *bp = strrchr(path,'/');
+ if( bp ) path = bp + 1;
+ av_dict_set(meta, "title", path, 0);
+
+ if( ffaudio.size() ) {
+ const char *ep = getenv("CIN_AUDIO_LANG"), *lp = 0;
+ if( !ep && (lp=getenv("LANG")) ) { // some are guesses
+ static struct { const char lc[3], lng[4]; } lcode[] = {
+ { "en", "eng" }, { "de", "ger" }, { "es", "spa" },
+ { "eu", "bas" }, { "fr", "fre" }, { "el", "gre" },
+ { "hi", "hin" }, { "it", "ita" }, { "ja", "jap" },
+ { "ko", "kor" }, { "du", "dut" }, { "pl", "pol" },
+ { "pt", "por" }, { "ru", "rus" }, { "sl", "slv" },
+ { "uk", "ukr" }, { "vi", "vie" }, { "zh", "chi" },
+ };
+ for( int i=sizeof(lcode)/sizeof(lcode[0]); --i>=0 && !ep; )
+ if( !strncmp(lcode[i].lc,lp,2) ) ep = lcode[i].lng;
+ }
+ char lang[4];
+ strncpy(lang,ep,3); lang[3] = 0;
+ AVStream *st = ffaudio[0]->st;
+ av_dict_set(&st->metadata,"language",lang,0);
+ }
+
AVDictionary *fopts = 0;
char option_path[BCTEXTLEN];
set_option_path(option_path, "format/%s", file_format);
- read_options(option_path, fopts);
+ read_options(option_path, fopts, 1);
ret = avformat_write_header(fmt_ctx, &fopts);
- av_dict_free(&fopts);
if( ret < 0 ) {
ff_err(ret, "FFMPEG::encode_activate: write header failed %s\n",
fmt_ctx->filename);
return -1;
}
+ av_dict_free(&fopts);
encoding = 1;
}
return encoding;
int64_t length;
float aspect_ratio;
- uint8_t *pkt_bfr;
- int pkt_bfr_sz;
- int64_t start_pts;
+ int interlaced;
+ int top_field_first;
};
class FFMPEG : public Thread {
char *format, char *codec, char *bsfilter, char *bsargs);
int get_encoder(FILE *fp,
char *format, char *codec, char *bsfilter, char *bsargs);
- int read_options(const char *options, AVDictionary *&opts);
+ int read_options(const char *options, AVDictionary *&opts, int skip=0);
int scan_options(const char *options, AVDictionary *&opts, AVStream *st);
int read_options(FILE *fp, const char *options, AVDictionary *&opts);
int load_options(const char *options, AVDictionary *&opts);
FFMPEG::load_options(option_path, asset->ff_audio_options,
sizeof(asset->ff_audio_options));
popup->audio_options->update(asset->ff_audio_options);
+ popup->audio_options->set_text_row(0);
char value[BCTEXTLEN];
if( !FileFFMPEG::get_ff_option("cin_bitrate", asset->ff_audio_options, value) )
FFMPEG::load_options(option_path, asset->ff_video_options,
sizeof(asset->ff_video_options));
popup->video_options->update(asset->ff_video_options);
+ popup->video_options->set_text_row(0);
char value[BCTEXTLEN];
if( !FileFFMPEG::get_ff_option("cin_quality", asset->ff_video_options, value) ) {
#include <string.h>
-#define WIDTH 600
-#define HEIGHT 400
-
+#define WIDTH 640
+#define HEIGHT 425
New::New(MWindow *mwindow)
: BC_MenuItem(_("New Project..."), "n", 'n')
load_defaults();
mwindow->gui->lock_window("NewThread::new_gui");
- int x = mwindow->gui->get_abs_cursor_x(0) - WIDTH / 2;
- int y = mwindow->gui->get_abs_cursor_y(0) - HEIGHT / 2;
+ int x = mwindow->gui->get_pop_cursor_x(0);
+ int y = mwindow->gui->get_pop_cursor_y(0);
nwindow = new NewWindow(mwindow, this, x, y);
nwindow->create_objects();
void NewThread::handle_close_event(int result)
{
+ if( !new_project->new_edl ) return;
new_project->new_edl->save_defaults(mwindow->defaults);
mwindow->defaults->save();
NewWindow::NewWindow(MWindow *mwindow, NewThread *new_thread, int x, int y)
- : BC_Window(_(_(PROGRAM_NAME ": New Project")),
- x,
- y,
- WIDTH,
- HEIGHT,
- -1,
- -1,
- 0,
- 0,
- 1)
+ : BC_Window(_(PROGRAM_NAME ": New Project"), x, y, WIDTH, HEIGHT,
+ -1, -1, 0, 0, 1)
{
this->mwindow = mwindow;
this->new_thread = new_thread;
y += 30;
x1 = x;
add_subwindow(new BC_Title(x1, y, _("Tracks:")));
- x1 += 100;
+ x1 += 115;
add_subwindow(vtracks = new NewVTracks(this, "", x1, y));
x1 += vtracks->get_w();
add_subwindow(new NewVTracksTumbler(this, x1, y));
// y += vchannels->get_h() + 5;
x1 = x;
add_subwindow(new BC_Title(x1, y, _("Framerate:")));
- x1 += 100;
+ x1 += 115;
add_subwindow(frame_rate = new NewFrameRate(this, "", x1, y));
x1 += frame_rate->get_w();
add_subwindow(new FrameRatePulldown(mwindow, frame_rate, x1, y));
x1 = x;
add_subwindow(new BC_Title(x1, y, _("Canvas size:")));
- x1 += 100;
+ x1 += 115;
add_subwindow(output_w_text = new NewOutputW(this, x1, y));
x1 += output_w_text->get_w() + 2;
add_subwindow(new BC_Title(x1, y, "x"));
output_h_text,
x1,
y));
- x1 += pulldown->get_w() + 5;
+ x1 += pulldown->get_w() + 10;
add_subwindow(new NewSwapExtents(mwindow, this, x1, y));
y += output_h_text->get_h() + 5;
x1 = x;
add_subwindow(new BC_Title(x1, y, _("Aspect ratio:")));
- x1 += 100;
+ x1 += 115;
add_subwindow(aspect_w_text = new NewAspectW(this, "", x1, y));
x1 += aspect_w_text->get_w() + 2;
add_subwindow(new BC_Title(x1, y, ":"));
y += aspect_w_text->get_h() + 5;
add_subwindow(new NewAspectAuto(this, x1, y));
y += 40;
- add_subwindow(new BC_Title(x, y, _("Color model:")));
- add_subwindow(textbox = new BC_TextBox(x + 100, y, 200, 1, ""));
+ BC_Title *title;
+ add_subwindow(title = new BC_Title(x, y, _("Color model:")));
+ x1 = x + title->get_w();
+ y1 = y; y += title->get_h() + 10;
+ add_subwindow(title = new BC_Title(x, y, _("Interlace mode:")));
+ int x2 = x + title->get_w();
+ int y2 = y; y += title->get_h() + 10;
+ if( x1 < x2 ) x1 = x2;
+ x1 += 20;
+ add_subwindow(textbox = new BC_TextBox(x1, y1, 150, 1, ""));
add_subwindow(color_model = new ColormodelPulldown(mwindow,
- textbox,
- &new_edl->session->color_model,
- x + 100 + textbox->get_w(),
- y));
- y += textbox->get_h() + 5;
-
- // --------------------
- add_subwindow(new BC_Title(x, y, _("Interlace mode:")));
- add_subwindow(textbox = new BC_TextBox(x + 100, y, 140, 1, ""));
+ textbox, &new_edl->session->color_model, x1+textbox->get_w(), y1));
+ add_subwindow(textbox = new BC_TextBox(x1, y2, 150, 1, ""));
add_subwindow(interlace_pulldown = new InterlacemodePulldown(mwindow,
- textbox,
- &new_edl->session->interlace_mode,
+ textbox, &new_edl->session->interlace_mode,
(ArrayList<BC_ListBoxItem*>*)&mwindow->interlace_project_modes,
- x + 100 + textbox->get_w(),
- y));
- y += textbox->get_h() + 5;
+ x1+textbox->get_w(), y2));
add_subwindow(new BC_OKButton(this,
mwindow->theme->get_image_set("new_ok_images")));
if(!asset) asset = new Asset;
load_defaults(asset);
check_asset(mwindow->edl, *asset);
-
+ int px = mwindow->gui->get_pop_cursor_x(1);
+ int py = mwindow->gui->get_pop_cursor_y(1);
// Get format from user
- render_window = new RenderWindow(mwindow,
- this,
- asset,
- mwindow->gui->get_abs_cursor_x(1),
- mwindow->gui->get_abs_cursor_y(1));
+ render_window = new RenderWindow(mwindow, this, asset, px, py);
render_window->create_objects();
}
Asset *asset,
int x,
int y)
- : BC_Window(_(PROGRAM_NAME ": Render"),
- x - WIDTH / 2, y - HEIGHT / 2,
- WIDTH, HEIGHT, WIDTH, HEIGHT,
- 0, 0, 1)
+ : BC_Window(_(PROGRAM_NAME ": Render"), x, y,
+ WIDTH, HEIGHT, WIDTH, HEIGHT, 0, 0, 1)
{
this->mwindow = mwindow;
this->render = render;
echo "x264.cfg_params := --enable-static --bit-depth=10"
fi
if test $WANT_X265_HIDEPTH = "yes" ; then
- echo "x265.cfg_params := -DENABLE_SHARED=no -DHIGH_BIT_DEPTH:BOOL=ON"
+ echo "x265.cfg_params := -DENABLE_SHARED=no -DHIGH_BIT_DEPTH:BOOL=ON -DMAIN12:BOOL=ON"
fi
for pkg in $STATIC_PKGS; do echo "static_pkgs += $pkg"; done
-mpegts ac3
+bluray ac3
id 0x1100
maxrate 9000000
minrate 0
--- /dev/null
+mpegts
+mpegts_m2ts_mode=2
+mpegts_pmt_start_pid=256
+sdt_period=-1
+packetsize 2048
+muxrate 10080000
+preload 500000
#tblend
#telecine
#testsrc
+#testsrc2
#thumbnail
#tile
#tinterlace
-mpegts libx264
+bluray libx264
+bluray-compat=1
id=0x1011
+level=41
+bf=2
+refs=3
+color_range=mpeg
+colorspace=bt709
+color_trc=bt709
+color_primaries=bt709
profile=high422
-pixel_format=yuv422p
-level=3.0
preset=medium
-bluray-compat=1
+pixel_format=yuv422p
+flags=+cgop
x264opts keyint=25:min-keyint=4:qpmin=3:qpmax=33:qp_step=4:merange=8
-mpegts libx264
+bluray libx264
+bluray-compat=1
id=0x1011
+level=41
+bf=2
+refs=3
+color_range=mpeg
+colorspace=bt709
+color_trc=bt709
+color_primaries=bt709
profile=high
-level=3.0
preset=medium
-bluray-compat=1
+flags=+cgop
# must be last for bdcreate.C
x264opts keyint=25:min-keyint=4:qpmin=3:qpmax=33:qp_step=4:merange=8
-mpegts libx264
+bluray libx264
+bluray-compat=1
id=0x1011
+level=41
+bf=2
+refs=3
+color_range=mpeg
+colorspace=bt709
+color_trc=bt709
+color_primaries=bt709
profile=baseline
-level=3.0
preset=medium
-bluray-compat=1
+flags=+cgop
x264opts keyint=25:min-keyint=4:qpmin=3:qpmax=33:qp_step=4:merange=8
--- /dev/null
+mp4 libx265
+# only works in build cinx hi-depth build
+strict=-2
+colorspace=bt2020_cl
+color_primaries=bt2020
+color_trc=bt2020_12bit
+pixel_format=yuv422p12
+x265-params=output-depth=12
+
--- /dev/null
+mp4 libx265
+# lossy but faster
+crf=25
--- /dev/null
+mp4 libx265
+# visually lossless
+crf=18
-mpegts libx265
-loglevel=debug
+bluray libx265
id=0x1011
#preset=ultrafast,superfast,veryfast,faster,fast,
#preset=medium,slow,slower,veryslow,placebo
preset=medium
#tune=psnr,ssim,grain,zerolatency,fastdecode
pixel_format=yuv422p10
-x265_opts=output-depth=10
+x265_opts=profile=main10:output-depth=10
mpegts libx265
-loglevel=debug
id=0x1011
#preset=ultrafast,superfast,veryfast,faster,fast,
#preset=medium,slow,slower,veryslow,placebo
preset=medium
#tune=psnr,ssim,grain,zerolatency,fastdecode
pixel_format=yuv422p
-x265_opts=output-depth=8
+x265_opts=profile=main:output-depth=8
mpegts libx264
+cin_bitrate=0
+cin_quality=0
qp=0
--- /dev/null
+bluray libx264
+bluray-compat=1
+id=0x1011
+crf=18
+maxrate=20000000
+bufsize=20000000
+cin_bitrate=20000000
+cin_quality=0
+preset=medium
+level=41
+bf=2
+refs=3
+color_range=mpeg
+colorspace=bt709
+color_trc=bt709
+color_primaries=bt709
+flags=+cgop
+# must be last for bdcreate.C
+x264opts keyint=25:min-keyint=4:qpmin=3:qpmax=33:qp_step=4:merange=8
while( *cp ) ++cp;
if( cp > path && *--cp != '/' ) return 0;
char *file_path = FileSystem::basepath(path);
- char *dir_path = FileSystem::basepath(filebox->fs->get_current_dir());
+ char *dir_path = FileSystem::basepath(filebox->directory);
int ret = !strcmp(file_path, dir_path) ? 0 : 1;
if( ret ) {
strcpy(filebox->directory, file_path);
this->want_directory = want_directory;
if(show_all_files) fs->set_show_all();
fs->complete_path(this->current_path);
- fs->complete_path(this->submitted_path);
+ strcpy(this->submitted_path, this->current_path);
fs->extract_dir(directory, this->current_path);
fs->extract_name(filename, this->current_path);
// Test if current directory exists
if(!fs->is_dir(directory))
{
- sprintf(this->current_path, "~");
- fs->complete_path(this->current_path);
- fs->set_current_dir(this->current_path);
-// fs->update(this->current_path);
- strcpy(directory, fs->get_current_dir());
+ sprintf(directory, "~");
+ fs->complete_path(directory);
+ strcpy(current_path,directory);
filename[0] = 0;
}
- else
- fs->set_current_dir(this->directory);
-
+ fs->set_current_dir(directory);
if(h_padding == -1)
{
fs->set_filter(get_resources()->filebox_filter);
}
- fs->set_sort_order(sort_order);
- fs->set_sort_field(column_type[sort_column]);
- fs->update(directory);
-
create_icons();
create_tables();
if( newest >= 0 ) {
strcpy(directory, resources->filebox_history[newest].path);
fs->change_dir(directory, 0);
+ strcpy(directory, fs->get_current_dir());
directory_title->update(fs->get_current_dir());
}
+ fs->set_sort_order(sort_order);
+ fs->set_sort_field(column_type[sort_column]);
+ fs->update(directory);
// Create recent dir list
create_history();
rename_thread = new BC_RenameThread(this);
-
+ refresh();
show_window();
}
draw_wtext(text_x, k + text_ascent, wtext_row, len,
0, &positions[wtext_row - wtext]);
}
+ else
+ positions[wtext_row - wtext] = 0;
// Get ibeam location
if(ibeam_letter >= row_begin && ibeam_letter <= row_end) {
//printf("BC_TextBox::draw 3 %d\n", ibeam_y);
if(need_ibeam) {
- if( wtext_len == 0 ) {
- ibeam_x = 0;
- ibeam_y = 0;
- }
- else {
- ibeam_x = -1;
- ibeam_y = -1;
- }
+// ibeam_x = ibeam_y = !wtext_len ? 0 : -1;
+ ibeam_x = 0; ibeam_y = k - text_y;
}
//printf("BC_TextBox::draw 4 %d\n", ibeam_y);
{
int i, row_begin, row_end;
int wtext_len = wtext_update();
+ x = y = 0;
- y = 0;
- x = 0;
for( i=0; i<wtext_len; ) {
row_begin = i;
for(; i<wtext_len && wtext[i]!='\n'; i++);
parent_window->add_subwindow(yscroll = new BC_ScrollTextBoxYScroll(this));
text->yscroll = yscroll;
yscroll->bound_to = text;
-
+ set_text_row(0);
}
int BC_ScrollTextBox::handle_event()
int BC_WindowBase::get_text_width(int font, const wchar_t *text, int length)
{
- int i, j, w = 0, line_w = 0;
-
- if(length < 0) length = wcslen(text);
-
- for(i = 0, j = 0; i <= length; i++)
- {
- line_w = 0;
- if(text[i] == '\n')
- {
- line_w = get_single_text_width(font, &text[j], i - j);
- j = i + 1;
+ int i, j, w = 0;
+ if( length < 0 ) length = wcslen(text);
+
+ for( i=j=0; i<length && text[i]; ++i ) {
+ if( text[i] != '\n' ) continue;
+ if( i > j ) {
+ int lw = get_single_text_width(font, &text[j], i-j);
+ if( w < lw ) w = lw;
}
- else
- if(text[i] == 0)
- line_w = get_single_text_width(font, &text[j], length - j);
-
- if(line_w > w) w = line_w;
+ j = i + 1;
+ }
+ if( j < length ) {
+ int lw = get_single_text_width(font, &text[j], length-j);
+ if( w < lw ) w = lw;
}
-
- if(i > length && w == 0)
- w = get_single_text_width(font, text, length);
return w;
}
return abs_y;
}
+int BC_WindowBase::get_pop_cursor_x(int lock_window)
+{
+ int margin = 100;
+ int px = get_abs_cursor_x(lock_window);
+ if( px < margin ) px = margin;
+ int wd = get_screen_w(lock_window,-1) - margin;
+ if( px > wd ) px = wd;
+ return px;
+}
+
+int BC_WindowBase::get_pop_cursor_y(int lock_window)
+{
+ int margin = 100;
+ int py = get_abs_cursor_y(lock_window);
+ if( py < margin ) py = margin;
+ int ht = get_screen_h(lock_window,-1) - margin;
+ if( py > ht ) py = ht;
+ return py;
+}
+
int BC_WindowBase::match_window(Window win)
{
if (this->win == win) return 1;
// Get current position
int get_abs_cursor_x(int lock_window);
int get_abs_cursor_y(int lock_window);
+ int get_pop_cursor_x(int lock_window);
+ int get_pop_cursor_y(int lock_window);
int get_relative_cursor_x();
int get_relative_cursor_y();
void get_root_coordinates(int x, int y, int *abs_x, int *abs_y);
--- /dev/null
+--- a/libavformat/mpegtsenc.c 2016-09-30 19:12:42.000000000 -0600
++++ b/libavformat/mpegtsenc.c 2017-01-25 17:25:58.720017593 -0700
+@@ -55,7 +55,7 @@
+ int sid; /* service ID */
+ char *name;
+ char *provider_name;
+- int pcr_pid;
++ int pcr_sid, pcr_pid;
+ int pcr_packet_count;
+ int pcr_packet_period;
+ AVProgram *program;
+@@ -94,8 +94,10 @@
+ int service_type;
+
+ int pmt_start_pid;
++ int pcr_start_pid;
+ int start_pid;
+ int m2ts_mode;
++ int64_t pcr_offset;
+
+ int reemit_pat_pmt; // backward compatibility
+
+@@ -704,6 +706,7 @@
+ service->pmt.pid = ts->pmt_start_pid + ts->nb_services;
+ service->sid = sid;
+ service->pcr_pid = 0x1fff;
++ service->pcr_sid = 0x1fff;
+ service->provider_name = av_strdup(provider_name);
+ service->name = av_strdup(name);
+ if (!service->provider_name || !service->name)
+@@ -722,7 +725,7 @@
+ static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb)
+ {
+ return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) +
+- ts->first_pcr;
++ ts->first_pcr + ts->pcr_offset;
+ }
+
+ static void mpegts_prefix_m2ts_header(AVFormatContext *s)
+@@ -760,6 +763,14 @@
+ if (s->max_delay < 0) /* Not set by the caller */
+ s->max_delay = 0;
+
++ if (ts->m2ts_mode == -1) {
++ if (av_match_ext(s->filename, "m2ts")) {
++ ts->m2ts_mode = 1;
++ } else {
++ ts->m2ts_mode = 0;
++ }
++ }
++
+ // round up to a whole number of TS packets
+ ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14;
+
+@@ -803,6 +814,8 @@
+ service->program = program;
+ }
+ }
++ if (ts->m2ts_mode > 1)
++ service->pmt.pid = 0x00ff + ts->service_id;
+
+ ts->pat.pid = PAT_PID;
+ /* Initialize at 15 so that it wraps and is equal to 0 for the
+@@ -885,10 +898,9 @@
+ ts_st->cc = 15;
+ /* update PCR pid by using the first video stream */
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+- service->pcr_pid == 0x1fff) {
+- service->pcr_pid = ts_st->pid;
++ service->pcr_sid == 0x1fff)
+ pcr_st = st;
+- }
++
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
+ st->codecpar->extradata_size > 0) {
+ AVStream *ast;
+@@ -924,12 +936,24 @@
+ av_freep(&pids);
+
+ /* if no video stream, use the first stream as PCR */
+- if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
+- pcr_st = s->streams[0];
+- ts_st = pcr_st->priv_data;
+- service->pcr_pid = ts_st->pid;
+- } else
+- ts_st = pcr_st->priv_data;
++ if (!pcr_st && s->nb_streams > 0)
++ pcr_st = s->streams[0];
++ if (!pcr_st) {
++ av_log(s, AV_LOG_ERROR, "no streams\n");
++ ret = AVERROR(EINVAL);
++ goto fail;
++ }
++ ts_st = pcr_st->priv_data;
++ if (service->pcr_sid == 0x1fff)
++ service->pcr_sid = ts_st->pid;
++ if (service->pcr_pid == 0x1fff)
++ service->pcr_pid = ts->m2ts_mode > 1 ?
++ 0x1000 + ts->service_id : service->pcr_sid ;
++ if (service->pmt.pid == service->pcr_pid) {
++ av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", service->pcr_pid);
++ ret = AVERROR(EINVAL);
++ goto fail;
++ }
+
+ if (ts->mux_rate > 1) {
+ service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
+@@ -989,14 +1013,6 @@
+ service->pcr_packet_period,
+ ts->sdt_packet_period, ts->pat_packet_period);
+
+- if (ts->m2ts_mode == -1) {
+- if (av_match_ext(s->filename, "m2ts")) {
+- ts->m2ts_mode = 1;
+- } else {
+- ts->m2ts_mode = 0;
+- }
+- }
+-
+ return 0;
+
+ fail:
+@@ -1010,9 +1026,9 @@
+ MpegTSWrite *ts = s->priv_data;
+ int i;
+
+- if (++ts->sdt_packet_count == ts->sdt_packet_period ||
++ if ( ts->sdt_period >= 0 && (++ts->sdt_packet_count == ts->sdt_packet_period ||
+ (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
+- (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0)
++ (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0))
+ ) {
+ ts->sdt_packet_count = 0;
+ if (dts != AV_NOPTS_VALUE)
+@@ -1067,13 +1083,14 @@
+ {
+ MpegTSWrite *ts = s->priv_data;
+ MpegTSWriteStream *ts_st = st->priv_data;
++ uint32_t pcr_pid = ts_st->service->pcr_pid;
+ uint8_t *q;
+ uint8_t buf[TS_PACKET_SIZE];
+
+ q = buf;
+ *q++ = 0x47;
+- *q++ = ts_st->pid >> 8;
+- *q++ = ts_st->pid;
++ *q++ = pcr_pid >> 8;
++ *q++ = pcr_pid;
+ *q++ = 0x20 | ts_st->cc; /* Adaptation only */
+ /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */
+ *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */
+@@ -1148,7 +1165,7 @@
+ uint8_t buf[TS_PACKET_SIZE];
+ uint8_t *q;
+ int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags;
+- int afc_len, stuffing_len;
++ int afc_len, stuffing_len, write_null;
+ int64_t pcr = -1; /* avoid warning */
+ int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+ int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
+@@ -1163,8 +1180,8 @@
+ retransmit_si_info(s, force_pat, dts);
+ force_pat = 0;
+
+- write_pcr = 0;
+- if (ts_st->pid == ts_st->service->pcr_pid) {
++ write_pcr = write_null = 0;
++ if (ts_st->pid == ts_st->service->pcr_sid) {
+ if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
+ ts_st->service->pcr_packet_count++;
+ if (ts_st->service->pcr_packet_count >=
+@@ -1173,15 +1190,17 @@
+ write_pcr = 1;
+ }
+ }
+-
+ if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
+- (dts - get_pcr(ts, s->pb) / 300) > delay) {
+- /* pcr insert gets priority over null packet insert */
+- if (write_pcr)
+- mpegts_insert_pcr_only(s, st);
+- else
+- mpegts_insert_null_packet(s);
+- /* recalculate write_pcr and possibly retransmit si_info */
++ (dts - get_pcr(ts, s->pb) / 300) > delay) {
++ write_null = 1;
++ }
++ /* pcr insert gets priority over null packet insert */
++ if (write_pcr && ts_st->service->pcr_sid != ts_st->service->pcr_pid) {
++ mpegts_insert_pcr_only(s, st);
++ continue;
++ }
++ if (write_null) {
++ mpegts_insert_null_packet(s);
+ continue;
+ }
+
+@@ -1191,13 +1210,17 @@
+ val = ts_st->pid >> 8;
+ if (is_start)
+ val |= 0x40;
++ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
++ st->codecpar->codec_id == AV_CODEC_ID_AC3 &&
++ ts->m2ts_mode > 1)
++ val |= 0x20;
+ *q++ = val;
+ *q++ = ts_st->pid;
+ ts_st->cc = ts_st->cc + 1 & 0xf;
+ *q++ = 0x10 | ts_st->cc; // payload indicator + CC
+ if (key && is_start && pts != AV_NOPTS_VALUE) {
+ // set Random Access for key frames
+- if (ts_st->pid == ts_st->service->pcr_pid)
++ if (ts_st->pid == ts_st->service->pcr_sid)
+ write_pcr = 1;
+ set_af_flag(buf, 0x40);
+ q = get_ts_payload_start(buf);
+@@ -1310,11 +1333,13 @@
+ *q++ = flags;
+ *q++ = header_len;
+ if (pts != AV_NOPTS_VALUE) {
+- write_pts(q, flags >> 6, pts);
++ int64_t ts_pts = pts + ts->pcr_offset;
++ write_pts(q, flags >> 6, ts_pts);
+ q += 5;
+ }
+ if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) {
+- write_pts(q, 1, dts);
++ int64_t ts_dts = dts + ts->pcr_offset;
++ write_pts(q, 1, ts_dts);
+ q += 5;
+ }
+ if (pes_extension && st->codecpar->codec_id == AV_CODEC_ID_DIRAC) {
+@@ -1838,12 +1863,18 @@
+ { "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
+ offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT,
+ { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM },
++ { "mpegts_pcr_start_pid", "Set the first pid of the PCR.",
++ offsetof(MpegTSWrite, pcr_start_pid), AV_OPT_TYPE_INT,
++ { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM },
+ { "mpegts_start_pid", "Set the first pid.",
+ offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT,
+ { .i64 = 0x0100 }, 0x0020, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM },
+ { "mpegts_m2ts_mode", "Enable m2ts mode.",
+ offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL,
+- { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM },
++ { .i64 = -1 }, -1, 2, AV_OPT_FLAG_ENCODING_PARAM },
++ { "mpegts_pcr_offset", "clock offset.",
++ offsetof(MpegTSWrite, pcr_offset), AV_OPT_TYPE_BOOL,
++ { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "muxrate", NULL,
+ offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT,
+ { .i64 = 1 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+@@ -1886,7 +1917,7 @@
+ { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "sdt_period", "SDT retransmission time limit in seconds",
+ offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE,
+- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
++ { .dbl = INT_MAX }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+ };
+