FFMPEG 4.0 版本 支持PSI设置
1. 支持 PSI 相关 PID 设置
1.1 给结构体 MpegTSWrite 添加如下相关PID成员
1.2 设置 PCR PID 和 PMT PID
1.3 设置 audio pid 和 video pid
1.4 注册 options 命令
2. PSI 的节目名和提供商名
3. 支持 PCR PID 和 VIDEO PID 不一致的情况
4. 将空包改为视频无效包
5. API 设置
1.1 给结构体 MpegTSWrite 添加如下相关PID成员
1.2 设置 PCR PID 和 PMT PID
1.3 设置 audio pid 和 video pid
1.4 注册 options 命令
2. PSI 的节目名和提供商名
3. 支持 PCR PID 和 VIDEO PID 不一致的情况
4. 将空包改为视频无效包
5. API 设置
1. 支持 PSI 相关 PID 设置
1.1 给结构体 MpegTSWrite 添加如下相关PID成员
文件:mpegtsenc.c
typedef struct MpegTSWrite {
...
int pmt_pid; //自定义pmt_pid
int pcr_pid; //自定义pcr_pid
int pcr_cc; //自定义pcr连续计算
int video_pid; //自定义video_pid
int audio_pid; //自定义audio_pid
int audio1_pid; //自定义audio1_pid
}MpegTSWrite;
1.2 设置 PCR PID 和 PMT PID
修改函数:mpegts_add_service
static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
const char *provider_name,
const char *name)
{
MpegTSService *service;
service = av_mallocz(sizeof(MpegTSService));
if (!service)
return NULL;
//设置pmt pid,自定义PID最小为32,小于32采用原来方案
//service->pmt.pid = ts->pmt_start_pid + ts->nb_services;
if (ts->pmt_pid >= MIN_PID_SET)
{
service->pmt.pid = ts->pmt_pid;
}else
{
service->pmt.pid = ts->pmt_start_pid + ts->nb_services;
}
//设置PCR PID
//service->pcr_pid = ts->pcr_pid;
service->sid = sid;
service->pcr_pid = 0x1fff;
service->provider_name = av_strdup(provider_name);
service->name = av_strdup(name);
if (!service->provider_name || !service->name)
goto fail;
if (av_dynarray_add_nofree(&ts->services, &ts->nb_services, service) < 0)
goto fail;
return service;
fail:
av_freep(&service->provider_name);
av_freep(&service->name);
av_free(service);
return NULL;
}
1.3 设置 audio pid 和 video pid
修改函数 :mpegts_init
static int mpegts_init(AVFormatContext *s)
{
...
//设置 video pid 和 audio pid
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
if ( ts->video_pid >= MIN_PID_SET)
{
ts_st->pid = ts->video_pid;
}
}
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
if ((ts->audio_pid >= MIN_PID_SET) && (!hasSetAudio0PID))
{
ts_st->pid = ts->audio_pid;
hasSetAudio0PID = 1;
}
else
{
if ((ts->audio1_pid >= MIN_PID_SET))
{
ts_st->pid = ts->audio1_pid;
}
}
}
pids[i] = ts_st->pid;
ts_st->payload_pts = AV_NOPTS_VALUE;
ts_st->payload_dts = AV_NOPTS_VALUE;
ts_st->first_pts_check = 1;
ts_st->cc = 15;
ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT;
...
}
1.4 注册 options 命令
在结构体AVOption 后添加如下命令
static const AVOption options[] = {
...
{ "mpegts_pmt_pid", "Set the first pid of the PMT.",
offsetof(MpegTSWrite, pmt_pid), AV_OPT_TYPE_INT,
{.i64 = 0x333 }, 0x10, 0x1fff, AV_OPT_FLAG_ENCODING_PARAM},
{ "mpegts_pcr_pid", "Set the pcr pid.",
offsetof(MpegTSWrite, pcr_pid), AV_OPT_TYPE_INT,
{.i64 = 0x1fff }, 0x0, 0x1fff, AV_OPT_FLAG_ENCODING_PARAM},
{ "mpegts_video_pid", "Set the video pid.",
offsetof(MpegTSWrite, video_pid), AV_OPT_TYPE_INT,
{.i64 = 0x1fff }, 0x10, 0x1fff, AV_OPT_FLAG_ENCODING_PARAM},
{ "mpegts_audio_pid", "Set the audio pid.",
offsetof(MpegTSWrite, audio_pid), AV_OPT_TYPE_INT,
{.i64 = 0x222 }, 0x10, 0x1fff, AV_OPT_FLAG_ENCODING_PARAM},
{ "mpegts_audio1_pid", "Set the audio1 pid.",
offsetof(MpegTSWrite, audio1_pid), AV_OPT_TYPE_INT,
{.i64 = 0x223 }, 0x10, 0x1fff, AV_OPT_FLAG_ENCODING_PARAM},
{ NULL },
}
2. PSI 的节目名和提供商名
直接调用 API 即可
3. 支持 PCR PID 和 VIDEO PID 不一致的情况
修改函数:mpegts_write_pes
while (payload_size > 0) {
...
write_pcr = 0;
//if (ts_st->pid == ts_st->service->pcr_pid) { //去掉视频pid和pcr pid必须一样的限制
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 >=
ts_st->service->pcr_packet_period) {
ts_st->service->pcr_packet_count = 0;
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 */
continue;
}
/* prepare packet header */
q = buf;
*q++ = 0x47;
val = ts_st->pid >> 8;
if (is_start)
val |= 0x40;
*q++ = val;
*q++ = ts_st->pid;
ts_st->cc = ts_st->cc + 1 & 0xf;
*q++ = 0x10 | ts_st->cc; // payload indicator + CC
if (ts_st->discontinuity) {
set_af_flag(buf, 0x80);
q = get_ts_payload_start(buf);
ts_st->discontinuity = 0;
}
if (key && is_start && pts != AV_NOPTS_VALUE) {
// set Random Access for key frames
//if (ts_st->pid == ts_st->service->pcr_pid) //去掉视频pid和pcr pid必须一样的限制
write_pcr = 1;
set_af_flag(buf, 0x40);
q = get_ts_payload_start(buf);
}
if (write_pcr) {
#if 0
#if 0
set_af_flag(buf, 0x10);
q = get_ts_payload_start(buf);
// add 11, pcr references the last byte of program clock reference base
if (ts->mux_rate > 1)
pcr = get_pcr(ts, s->pb);
else
pcr = (dts - delay) * 300;
if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
extend_af(buf, write_pcr_bits(q, pcr));
q = get_ts_payload_start(buf);
#else
mpegts_insert_pcr_only(s, st);
#endif
#endif
if (ts->video_pid == ts->pcr_pid)
{
//视频包附带PCR
set_af_flag(buf, 0x10);
q = get_ts_payload_start(buf);
// add 11, pcr references the last byte of program clock reference base
if (ts->mux_rate > 1)
pcr = get_pcr(ts, s->pb);
else
pcr = (dts - delay) * 300;
if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
extend_af(buf, write_pcr_bits(q, pcr));
q = get_ts_payload_start(buf);
}
else
{
//只插入PCR包
mpegts_insert_pcr_only(s, st);
}
}
...
}
修改函数:mpegts_insert_pcr_only
/* Write a single transport stream packet with a PCR and no payload */
static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
{
MpegTSWrite *ts = s->priv_data;
MpegTSWriteStream *ts_st = st->priv_data;
uint8_t *q;
uint8_t buf[TS_PACKET_SIZE];
q = buf;
*q++ = 0x47;
#if 0
#if 0
*q++ = ts_st->pid >> 8;
*q++ = ts_st->pid;
*q++ = 0x20 | ts_st->cc; /* Adaptation only */
#else
*q++ = ts->pcr_pid >> 8;
*q++ = ts->pcr_pid;
*q++ = 0x20 | (ts->pcr_cc & 0xF); /* Adaptation only */
#endif
#endif
if (ts->video_pid == ts->pcr_pid)
{
*q++ = ts_st->pid >> 8;
*q++ = ts_st->pid;
*q++ = 0x20 | ts_st->cc; /* Adaptation only */
}
else
{
*q++ = ts->pcr_pid >> 8;
*q++ = ts->pcr_pid;
*q++ = 0x20 | (ts->pcr_cc & 0xF); /* Adaptation only */
}
/* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */
*q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */
*q++ = 0x10; /* Adaptation flags: PCR present */
if (ts_st->discontinuity) {
q[-1] |= 0x80;
ts_st->discontinuity = 0;
}
/* PCR coded into 6 bytes */
q += write_pcr_bits(q, get_pcr(ts, s->pb));
/* stuffing bytes */
memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
4. 将空包改为视频无效包
空包接口函数
/* Write a single null transport stream packet */
static void mpegts_insert_null_packet(AVFormatContext *s)
{
uint8_t *q;
uint8_t buf[TS_PACKET_SIZE];
q = buf;
*q++ = 0x47;
*q++ = 0x00 | 0x1f;
*q++ = 0xff;
*q++ = 0x10;
memset(q, 0x0FF, TS_PACKET_SIZE - (q - buf));
mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
视频无效包函数
/* Write a single filled video packet */
static void mpegts_insert_filled_packet(AVFormatContext *s, AVStream *st)
{
MpegTSWrite *ts = s->priv_data;
MpegTSWriteStream *ts_st = st->priv_data;
uint8_t *q;
uint8_t buf[TS_PACKET_SIZE];
q = buf;
*q++ = 0x47;
*q++ = ts_st->pid >> 8;
*q++ = ts_st->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 */
*q++ = 0x00; /* Adaptation flags: PCR present */
///* PCR coded into 6 bytes */
//q += write_pcr_bits(q, get_pcr(ts, s->pb));
/* stuffing bytes */
memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}