[FFmpeg-devel] [PATCH v2] avformat/dashdec: Differentiate unassigned and zero attributes

Steven Liu lingjiujianke at gmail.com
Thu Oct 20 12:34:49 EEST 2022


Gregor Riepl <onitake at gmail.com> 于2022年10月19日周三 02:24写道:
>
> This fixes an issue where a timestamp attribute may have a valid zero
> value (the UNIX epoch 1970-01-01T00:00:00), but is misinterpreted by
> dashdec as being unassigned. This changes the logic that calculates
> segment numbers and makes the stream undecodable by dashdec.
>
> The fix originally posted to the issue tracker was incorrect and
> changed other parts of the segment calculation logic. With this
> patch, only the interpretation of the attributes is changed.
> Some warnings are added to account for potential errors in manifests.
>
> v2 change: Use int, 0 and 1 instead of C99 stdbool.
> This similar to what's done in fftools/ffmpeg_opt.c.
>
> Fixes: #8522
> Signed-off-by: Gregor Riepl <onitake at gmail.com>
> ---
>   libavformat/dashdec.c | 209 ++++++++++++++++++++++++++++++++----------
>   1 file changed, 161 insertions(+), 48 deletions(-)
>
> diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
> index 29d4680c68..df5d453c5a 100644
> --- a/libavformat/dashdec.c
> +++ b/libavformat/dashdec.c
> @@ -129,21 +129,34 @@ typedef struct DASHContext {
>       struct representation **subtitles;
>
>       /* MediaPresentationDescription Attribute */
> -    uint64_t media_presentation_duration;
> -    uint64_t suggested_presentation_delay;
> -    uint64_t availability_start_time;
> -    uint64_t availability_end_time;
> -    uint64_t publish_time;
> -    uint64_t minimum_update_period;
> -    uint64_t time_shift_buffer_depth;
> -    uint64_t min_buffer_time;
> +    uint64_t media_presentation_duration_value;
> +    uint64_t suggested_presentation_delay_value;
> +    uint64_t availability_start_time_value;
> +    uint64_t availability_end_time_value;
> +    uint64_t publish_time_value;
> +    uint64_t minimum_update_period_value;
> +    uint64_t time_shift_buffer_depth_value;
> +    uint64_t min_buffer_time_value;
>
>       /* Period Attribute */
> -    uint64_t period_duration;
> -    uint64_t period_start;
> +    uint64_t period_duration_value;
> +    uint64_t period_start_value;
>
>       /* AdaptationSet Attribute */
> -    char *adaptionset_lang;
> +    char *adaptionset_lang_value;
> +
> +    /* Attribute valid flags (true if the attribute exists in the XML manifest) */
> +    int media_presentation_duration_assigned;
> +    int suggested_presentation_delay_assigned;
> +    int availability_start_time_assigned;
> +    int availability_end_time_assigned;
> +    int publish_time_assigned;
> +    int minimum_update_period_assigned;
> +    int time_shift_buffer_depth_assigned;
> +    int min_buffer_time_assigned;
> +    int period_duration_assigned;
> +    int period_start_assigned;
> +    int adaptionset_lang_assigned;
>
>       int is_live;
>       AVIOInterruptCB *interrupt_callback;
> @@ -867,8 +880,8 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url,
>       rep = av_mallocz(sizeof(struct representation));
>       if (!rep)
>           return AVERROR(ENOMEM);
> -    if (c->adaptionset_lang) {
> -        rep->lang = av_strdup(c->adaptionset_lang);
> +    if (c->adaptionset_lang_assigned) {
> +        rep->lang = av_strdup(c->adaptionset_lang_value);
>           if (!rep->lang) {
>               av_log(s, AV_LOG_ERROR, "alloc language memory failure\n");
>               av_freep(&rep);
> @@ -1106,7 +1119,10 @@ static int parse_manifest_adaptationset_attr(AVFormatContext *s, xmlNodePtr adap
>           av_log(s, AV_LOG_WARNING, "Cannot get AdaptionSet\n");
>           return AVERROR(EINVAL);
>       }
> -    c->adaptionset_lang = xmlGetProp(adaptionset_node, "lang");
> +    c->adaptionset_lang_value = xmlGetProp(adaptionset_node, "lang");
> +    if (c->adaptionset_lang_value) {
> +        c->adaptionset_lang_assigned = 1;
> +    }
>
>       return 0;
>   }
> @@ -1162,8 +1178,9 @@ static int parse_manifest_adaptationset(AVFormatContext *s, const char *url,
>       }
>
>   err:
> -    xmlFree(c->adaptionset_lang);
> -    c->adaptionset_lang = NULL;
> +    xmlFree(c->adaptionset_lang_value);
> +    c->adaptionset_lang_value = NULL;
> +    c->adaptionset_lang_assigned = 0;
>       return ret;
>   }
>
> @@ -1273,29 +1290,37 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
>               val = xmlGetProp(node, attr->name);
>
>               if (!av_strcasecmp(attr->name, "availabilityStartTime")) {
> -                c->availability_start_time = get_utc_date_time_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->availability_start_time = [%"PRId64"]\n", c->availability_start_time);
> +                c->availability_start_time_value = get_utc_date_time_insec(s, val);
> +                c->availability_start_time_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->availability_start_time = [%"PRId64"]\n", c->availability_start_time_value);
>               } else if (!av_strcasecmp(attr->name, "availabilityEndTime")) {
> -                c->availability_end_time = get_utc_date_time_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->availability_end_time = [%"PRId64"]\n", c->availability_end_time);
> +                c->availability_end_time_value = get_utc_date_time_insec(s, val);
> +                c->availability_end_time_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->availability_end_time = [%"PRId64"]\n", c->availability_end_time_value);
>               } else if (!av_strcasecmp(attr->name, "publishTime")) {
> -                c->publish_time = get_utc_date_time_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->publish_time = [%"PRId64"]\n", c->publish_time);
> +                c->publish_time_value = get_utc_date_time_insec(s, val);
> +                c->publish_time_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->publish_time = [%"PRId64"]\n", c->publish_time_value);
>               } else if (!av_strcasecmp(attr->name, "minimumUpdatePeriod")) {
> -                c->minimum_update_period = get_duration_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->minimum_update_period = [%"PRId64"]\n", c->minimum_update_period);
> +                c->minimum_update_period_value = get_duration_insec(s, val);
> +                c->minimum_update_period_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->minimum_update_period = [%"PRId64"]\n", c->minimum_update_period_value);
>               } else if (!av_strcasecmp(attr->name, "timeShiftBufferDepth")) {
> -                c->time_shift_buffer_depth = get_duration_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->time_shift_buffer_depth = [%"PRId64"]\n", c->time_shift_buffer_depth);
> +                c->time_shift_buffer_depth_value = get_duration_insec(s, val);
> +                c->time_shift_buffer_depth_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->time_shift_buffer_depth = [%"PRId64"]\n", c->time_shift_buffer_depth_value);
>               } else if (!av_strcasecmp(attr->name, "minBufferTime")) {
> -                c->min_buffer_time = get_duration_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->min_buffer_time = [%"PRId64"]\n", c->min_buffer_time);
> +                c->min_buffer_time_value = get_duration_insec(s, val);
> +                c->min_buffer_time_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->min_buffer_time = [%"PRId64"]\n", c->min_buffer_time_value);
>               } else if (!av_strcasecmp(attr->name, "suggestedPresentationDelay")) {
> -                c->suggested_presentation_delay = get_duration_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->suggested_presentation_delay = [%"PRId64"]\n", c->suggested_presentation_delay);
> +                c->suggested_presentation_delay_value = get_duration_insec(s, val);
> +                c->suggested_presentation_delay_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->suggested_presentation_delay = [%"PRId64"]\n", c->suggested_presentation_delay_value);
>               } else if (!av_strcasecmp(attr->name, "mediaPresentationDuration")) {
> -                c->media_presentation_duration = get_duration_insec(s, val);
> -                av_log(s, AV_LOG_TRACE, "c->media_presentation_duration = [%"PRId64"]\n", c->media_presentation_duration);
> +                c->media_presentation_duration_value = get_duration_insec(s, val);
> +                c->media_presentation_duration_assigned = 1;
> +                av_log(s, AV_LOG_TRACE, "c->media_presentation_duration = [%"PRId64"]\n", c->media_presentation_duration_value);
>               }
>               attr = attr->next;
>               xmlFree(val);
> @@ -1325,12 +1350,30 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
>                       attr = attr->next;
>                       xmlFree(val);
>                   }
> -                if ((period_duration_sec) >= (c->period_duration)) {
> +                if (c->period_duration_assigned) {
> +                    if ((period_duration_sec) >= (c->period_duration_value)) {
> +                        period_node = node;
> +                        c->period_duration_value = period_duration_sec;
> +                        c->period_start_value = period_start_sec;
> +                        c->period_start_assigned = 1;
> +                        if (c->period_start_value > 0) {
> +                            c->media_presentation_duration_value = c->period_duration_value;
> +                            c->media_presentation_duration_assigned = 1;
> +                        }
> +                    } else {
> +                        av_log(s, AV_LOG_VERBOSE, "previous period_duration is larger than new value. ignoring.\n");
> +                    }
> +                } else {
> +                    av_log(s, AV_LOG_VERBOSE, "period_duration attribute unset - updating from calculated value.\n");
>                       period_node = node;
> -                    c->period_duration = period_duration_sec;
> -                    c->period_start = period_start_sec;
> -                    if (c->period_start > 0)
> -                        c->media_presentation_duration = c->period_duration;
> +                    c->period_duration_value = period_duration_sec;
> +                    c->period_duration_assigned = 1;
> +                    c->period_start_value = period_start_sec;
> +                    c->period_start_assigned = 1;
> +                    if (c->period_start_value > 0) {
> +                        c->media_presentation_duration_value = c->period_duration_value;
> +                        c->media_presentation_duration_assigned = 1;
> +                    }
>                   }
>               } else if (!av_strcasecmp(node->name, "ProgramInformation")) {
>                   parse_programinformation(s, node);
> @@ -1391,15 +1434,54 @@ static int64_t calc_cur_seg_no(AVFormatContext *s, struct representation *pls)
>           } else if (pls->fragment_duration){
>               av_log(s, AV_LOG_TRACE, "in fragment_duration mode fragment_timescale = %"PRId64", presentation_timeoffset = %"PRId64"\n", pls->fragment_timescale, pls->presentation_timeoffset);
>               if (pls->presentation_timeoffset) {
> -                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) * pls->fragment_timescale)-pls->presentation_timeoffset) / pls->fragment_duration - c->min_buffer_time;
> -            } else if (c->publish_time > 0 && !c->availability_start_time) {
> -                if (c->min_buffer_time) {
> -                    num = pls->first_seq_no + (((c->publish_time + pls->fragment_duration) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration - c->min_buffer_time;
> +                if (c->availability_start_time_assigned && c->min_buffer_time_assigned) {
> +                    num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_value) * pls->fragment_timescale)-pls->presentation_timeoffset) / pls->fragment_duration - c->min_buffer_time_assigned;
>                   } else {
> -                    num = pls->first_seq_no + (((c->publish_time - c->time_shift_buffer_depth + pls->fragment_duration) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
> +                    av_log(s, AV_LOG_WARNING, "availability_start_time and/or min_buffer_time attributes unset - using zero values. segment numbers may be incorrect.\n");
> +                    if (c->availability_start_time_assigned) {
> +                        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_value) * pls->fragment_timescale)-pls->presentation_timeoffset) / pls->fragment_duration;
> +                    } else if (c->min_buffer_time_assigned) {
> +                        num = pls->first_seq_no + (((get_current_time_in_sec()) * pls->fragment_timescale)-pls->presentation_timeoffset) / pls->fragment_duration - c->min_buffer_time_value;
> +                    } else {
> +                        num = pls->first_seq_no + (((get_current_time_in_sec()) * pls->fragment_timescale)-pls->presentation_timeoffset) / pls->fragment_duration;
> +                    }
> +                }
> +            } else if (c->publish_time_assigned && c->publish_time_value > 0 && !c->availability_start_time_assigned) {
> +                // FIXME is publish_time_value > 0 a required condition, or are we only checking for existence of the attribute?
> +                if (c->min_buffer_time_assigned) {
> +                    if (c->suggested_presentation_delay_assigned) {
> +                        num = pls->first_seq_no + (((c->publish_time_value + pls->fragment_duration) - c->suggested_presentation_delay_value) * pls->fragment_timescale) / pls->fragment_duration - c->min_buffer_time_value;
> +                    } else {
> +                        av_log(s, AV_LOG_WARNING, "suggested_presentation_delay attribute unset - using zero value. segment numbers may be incorrect.\n");
> +                        num = pls->first_seq_no + ((c->publish_time_value + pls->fragment_duration) * pls->fragment_timescale) / pls->fragment_duration - c->min_buffer_time_value;
> +                    }
> +                } else {
> +                    if (c->time_shift_buffer_depth_assigned && c->suggested_presentation_delay_assigned) {
> +                        num = pls->first_seq_no + (((c->publish_time_value - c->time_shift_buffer_depth_value + pls->fragment_duration) - c->suggested_presentation_delay_value) * pls->fragment_timescale) / pls->fragment_duration;
> +                    } else {
> +                        av_log(s, AV_LOG_WARNING, "time_shift_buffer_depth and/or suggested_presentation_delay attributes unset - using zero values. segment numbers may be incorrect.\n");
> +                        if (c->time_shift_buffer_depth_assigned) {
> +                            num = pls->first_seq_no + ((c->publish_time_value - c->time_shift_buffer_depth_value + pls->fragment_duration) * pls->fragment_timescale) / pls->fragment_duration;
> +                        } else if (c->suggested_presentation_delay_assigned) {
> +                            num = pls->first_seq_no + (((c->publish_time_value + pls->fragment_duration) - c->suggested_presentation_delay_value) * pls->fragment_timescale) / pls->fragment_duration;
> +                        } else {
> +                            num = pls->first_seq_no + ((c->publish_time_value + pls->fragment_duration) * pls->fragment_timescale) / pls->fragment_duration;
> +                        }
> +                    }
>                   }
>               } else {
> -                num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
> +                if (c->availability_start_time_assigned && c->suggested_presentation_delay_assigned) {
> +                    num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_value) - c->suggested_presentation_delay_value) * pls->fragment_timescale) / pls->fragment_duration;
> +                } else {
> +                    av_log(s, AV_LOG_WARNING, "availability_start_time and/or suggested_presentation_delay attributes unset - using zero values. segment numbers may be incorrect.\n");
> +                    if (c->availability_start_time_assigned) {
> +                        num = pls->first_seq_no + ((get_current_time_in_sec() - c->availability_start_time_value) * pls->fragment_timescale) / pls->fragment_duration;
> +                    } else if (c->suggested_presentation_delay_assigned) {
> +                        num = pls->first_seq_no + ((get_current_time_in_sec() - c->suggested_presentation_delay_value) * pls->fragment_timescale) / pls->fragment_duration;
> +                    } else {
> +                        num = pls->first_seq_no + (get_current_time_in_sec() * pls->fragment_timescale) / pls->fragment_duration;
> +                    }
> +                }
>               }
>           }
>       } else {
> @@ -1415,7 +1497,18 @@ static int64_t calc_min_seg_no(AVFormatContext *s, struct representation *pls)
>
>       if (c->is_live && pls->fragment_duration) {
>           av_log(s, AV_LOG_TRACE, "in live mode\n");
> -        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
> +        if (c->availability_start_time_assigned && c->time_shift_buffer_depth_assigned) {
> +            num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_value) - c->time_shift_buffer_depth_value) * pls->fragment_timescale) / pls->fragment_duration;
> +        } else {
> +            av_log(s, AV_LOG_WARNING, "availability_start_time and/or time_shift_buffer_depth attributes unset - using zero values. segment numbers may be incorrect.\n");
> +            if (c->availability_start_time_assigned) {
> +                num = pls->first_seq_no + ((get_current_time_in_sec() - c->availability_start_time_value) * pls->fragment_timescale) / pls->fragment_duration;
> +            } else if (c->time_shift_buffer_depth_assigned) {
> +                num = pls->first_seq_no + ((get_current_time_in_sec() - c->time_shift_buffer_depth_value) * pls->fragment_timescale) / pls->fragment_duration;
> +            } else {
> +                num = pls->first_seq_no + (get_current_time_in_sec() * pls->fragment_timescale) / pls->fragment_duration;
> +            }
> +        }
>       } else {
>           num = pls->first_seq_no;
>       }
> @@ -1434,15 +1527,30 @@ static int64_t calc_max_seg_no(struct representation *pls, DASHContext *c)
>           for (i = 0; i < pls->n_timelines; i++) {
>               if (pls->timelines[i]->repeat == -1) {
>                   int length_of_each_segment = pls->timelines[i]->duration / pls->fragment_timescale;
> -                num =  c->period_duration / length_of_each_segment;
> +                if (c->period_duration_assigned) {
> +                    num = c->period_duration_value / length_of_each_segment;
> +                } else {
> +                    av_log(NULL, AV_LOG_WARNING, "period_duration attribute unset - using zero value. segment numbers may be incorrect.\n");
> +                    num = 0;
> +                }
>               } else {
>                   num += pls->timelines[i]->repeat;
>               }
>           }
>       } else if (c->is_live && pls->fragment_duration) {
> -        num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale)  / pls->fragment_duration;
> +        if (c->availability_start_time_assigned) {
> +            num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time_value)) * pls->fragment_timescale)  / pls->fragment_duration;
> +        } else {
> +            av_log(NULL, AV_LOG_WARNING, "availability_start_time attribute unset - using zero value. segment numbers may be incorrect.\n");
> +            num = pls->first_seq_no + (get_current_time_in_sec() * pls->fragment_timescale)  / pls->fragment_duration;
> +        }
>       } else if (pls->fragment_duration) {
> -        num = pls->first_seq_no + av_rescale_rnd(1, c->media_presentation_duration * pls->fragment_timescale, pls->fragment_duration, AV_ROUND_UP);
> +        if (c->media_presentation_duration_assigned) {
> +            num = pls->first_seq_no + av_rescale_rnd(1, c->media_presentation_duration_value * pls->fragment_timescale, pls->fragment_duration, AV_ROUND_UP);
> +        } else {
> +            av_log(NULL, AV_LOG_WARNING, "media_presentation_duration attribute unset - using zero value. segment numbers may be incorrect.\n");
> +            num = pls->first_seq_no + av_rescale_rnd(1, 0, pls->fragment_duration, AV_ROUND_UP);
> +        }
>       }
>
>       return num;
> @@ -2040,7 +2148,12 @@ static int dash_read_header(AVFormatContext *s)
>       /* If this isn't a live stream, fill the total duration of the
>        * stream. */
>       if (!c->is_live) {
> -        s->duration = (int64_t) c->media_presentation_duration * AV_TIME_BASE;
> +        if (c->media_presentation_duration_assigned) {
> +            s->duration = (int64_t) c->media_presentation_duration_value * AV_TIME_BASE;
> +        } else {
> +            av_log(NULL, AV_LOG_WARNING, "media_presentation_duration attribute unset - using zero value. segment numbers may be incorrect.\n");
> +            s->duration = 0;
> +        }
>       } else {
>           av_dict_set(&c->avio_opts, "seekable", "0", 0);
>       }
> --
> 2.35.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".

lgtm

btw, i cannot sure if we use c99 stdbool.h, if no body objection, i
will push this version of patch.

Thanks
Steven


More information about the ffmpeg-devel mailing list