diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index e7c2abce57..2f7d7e0f3a 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -552,6 +552,10 @@ Show the detected raw packet size, cannot be set by the user.
 Scan and combine all PMTs. The value is an integer with value from -1
 to 1 (-1 means automatic setting, 1 means enabled, 0 means
 disabled). Default value is -1.
+
+@item merge_pmt_versions
+Re-use existing streams when a PMT's version is updated and elementary
+streams move to different PIDs. Default value is 0.
 @end table
 
 @section mpjpeg
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index 27b1c30a44..c42e564a7e 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -84,6 +84,8 @@ typedef struct MpegTSSectionFilter {
     unsigned int end_of_section_reached : 1;
     SectionCallback *section_cb;
     void *opaque;
+    int orig_pcr_pid; /* pmt specific */
+    int last_pcr_pid;
 } MpegTSSectionFilter;
 
 struct MpegTSFilter {
@@ -147,6 +149,7 @@ struct MpegTSContext {
     int scan_all_pmts;
 
     int resync_size;
+    int merge_pmt_versions;
 
     /******************************************/
     /* private mpegts data */
@@ -172,6 +175,8 @@ static const AVOption options[] = {
      {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
     {"scan_all_pmts", "scan and combine all PMTs", offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL,
      {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
+    {"merge_pmt_versions", "re-use streams when PMT's version/pids change", offsetof(MpegTSContext, merge_pmt_versions), AV_OPT_TYPE_BOOL,
+     {.i64 = 0}, 0, 1,  AV_OPT_FLAG_DECODING_PARAM },
     {"skip_changes", "skip changing / adding streams / programs", offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL,
      {.i64 = 0}, 0, 1, 0 },
     {"skip_clear", "skip clearing programs", offsetof(MpegTSContext, skip_clear), AV_OPT_TYPE_BOOL,
@@ -1073,7 +1078,8 @@ static int mpegts_push_data(MpegTSFilter *filter,
                         if (ts->skip_changes)
                             goto skip;
 
-                        pes->st = avformat_new_stream(ts->stream, NULL);
+                        if (!pes->st)
+                            pes->st = avformat_new_stream(ts->stream, NULL);
                         if (!pes->st)
                             return AVERROR(ENOMEM);
                         pes->st->id = pes->pid;
@@ -1982,6 +1988,68 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
     *pp = desc_end;
     return 0;
 }
+static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
+                                      int stream_id,
+                                      int pcr_pid, int orig_pcr_pid)
+{
+    AVFormatContext *s = ts->stream;
+    int i, orig_pid = orig_pcr_pid + (pid - pcr_pid);
+    AVStream *found = NULL;
+
+    for (i = 0; i < s->nb_streams; i++) {
+        AVStream *st = s->streams[i];
+        if (stream_id != -1) {
+            if (st->stream_identifier == stream_id+1) {
+                found = st;
+                break;
+            }
+        } else if (pcr_pid != orig_pcr_pid && st->id == orig_pid) {
+            found = st;
+            break;
+        }
+    }
+
+    if (found) {
+        av_log(ts->stream, AV_LOG_DEBUG, "re-using existing %s stream %d (pid=0x%x) for new pid=0x%x\n",
+               av_get_media_type_string(found->codecpar->codec_type), i, found->id, pid);
+    }
+
+    return found;
+}
+
+static int parse_stream_identifier_desc(uint8_t *p, uint8_t *p_end)
+{
+    const uint8_t **pp = &p;
+    const uint8_t *desc_list_end;
+    const uint8_t *desc_end;
+    int desc_list_len;
+    int desc_len, desc_tag;
+
+    desc_list_len = get16(&p, p_end);
+    if (desc_list_len < 0)
+        return -1;
+    desc_list_len &= 0xfff;
+    desc_list_end  = p + desc_list_len;
+    if (desc_list_end > p_end)
+        return -1;
+
+    while (1) {
+        desc_tag = get8(pp, desc_list_end);
+        if (desc_tag < 0)
+            return -1;
+        desc_len = get8(pp, desc_list_end);
+        if (desc_len < 0)
+            return -1;
+        desc_end = *pp + desc_len;
+        if (desc_end > desc_list_end)
+            return -1;
+
+        if (desc_tag == 0x52) {
+            return get8(pp, desc_end);
+        }
+        *pp = desc_end;
+    }
+}
 
 static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
 {
@@ -2000,6 +2068,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
     int program_info_length, pcr_pid, pid, stream_type;
     int desc_list_len;
     uint32_t prog_reg_desc = 0; /* registration descriptor */
+    int last_ver = tssf->last_ver;
+    int stream_id = -1;
 
     int mp4_descr_count = 0;
     Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } };
@@ -2032,6 +2102,14 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
     pcr_pid &= 0x1fff;
     add_pid_to_pmt(ts, h->id, pcr_pid);
     set_pcr_pid(ts->stream, h->id, pcr_pid);
+    if (!tssf->orig_pcr_pid)
+        tssf->orig_pcr_pid = pcr_pid;
+
+    if (last_ver != -1 && h->version != last_ver) {
+        av_log(ts->stream, AV_LOG_DEBUG, "detected PMT change (version=%d/%d, pcr_pid=0x%x/0x%x)\n",
+               last_ver, h->version, tssf->last_pcr_pid, pcr_pid);
+    }
+    tssf->last_pcr_pid = pcr_pid;
 
     av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid);
 
@@ -2083,14 +2161,22 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
         if (pid < 0)
             goto out;
         pid &= 0x1fff;
+        stream_id = parse_stream_identifier_desc(p, p_end);
         if (pid == ts->current_pid)
             goto out;
 
         /* now create stream */
         if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
             pes = ts->pids[pid]->u.pes_filter.opaque;
+            if (ts->merge_pmt_versions && !pes->st) {
+                st = find_matching_stream(ts, pid, stream_id, pcr_pid, tssf->orig_pcr_pid);
+                if (st) {
+                    pes->st = st;
+                    pes->stream_type = stream_type;
+                }
+            }
             if (!pes->st) {
-                pes->st     = avformat_new_stream(pes->stream, NULL);
+                pes->st = avformat_new_stream(pes->stream, NULL);
                 if (!pes->st)
                     goto out;
                 pes->st->id = pes->pid;
@@ -2100,7 +2186,14 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
             if (ts->pids[pid])
                 mpegts_close_filter(ts, ts->pids[pid]); // wrongly added sdt filter probably
             pes = add_pes_stream(ts, pid, pcr_pid);
-            if (pes) {
+            if (ts->merge_pmt_versions && pes && !pes->st) {
+                st = find_matching_stream(ts, pid, stream_id, pcr_pid, tssf->orig_pcr_pid);
+                if (st) {
+                    pes->st = st;
+                    pes->stream_type = stream_type;
+                }
+            }
+            if (pes && !pes->st) {
                 st = avformat_new_stream(pes->stream, NULL);
                 if (!st)
                     goto out;
@@ -2110,7 +2203,11 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
             int idx = ff_find_stream_index(ts->stream, pid);
             if (idx >= 0) {
                 st = ts->stream->streams[idx];
-            } else {
+            }
+            if (ts->merge_pmt_versions && !st) {
+                st = find_matching_stream(ts, pid, stream_id, pcr_pid, tssf->orig_pcr_pid);
+            }
+            if (!st) {
                 st = avformat_new_stream(ts->stream, NULL);
                 if (!st)
                     goto out;
