private Representation readRepresentation(MPD mpd, Period period, AdaptationSet adaptationSet,
                                              Uri baseUrl, XmlPullParser parser,
                                              SegmentTemplate segmentTemplate)
            throws XmlPullParserException, IOException, DashParserException {
        Representation representation = new Representation();

        representation.id = getAttributeValue(parser, "id");
        representation.codec = getAttributeValue(parser, "codecs");
        representation.mimeType = getAttributeValue(parser, "mimeType", adaptationSet.mimeType);
        if(representation.mimeType.startsWith("video/")) {
            representation.width = getAttributeValueInt(parser, "width");
            representation.height = getAttributeValueInt(parser, "height");
            representation.sar = getAttributeValueRatio(parser, "sar");
        }
        representation.bandwidth = getAttributeValueInt(parser, "bandwidth");

        int type = 0;
        while((type = parser.next()) >= 0) {
            String tagName = parser.getName();

            if(type == XmlPullParser.START_TAG) {
                if (tagName.equals("Initialization")) {
                    String sourceURL = getAttributeValue(parser, "sourceURL");
                    String range = getAttributeValue(parser, "range");

                    sourceURL = sourceURL != null ? extendUrl(baseUrl, sourceURL).toString() : baseUrl.toString();

                    representation.initSegment = new Segment(sourceURL, range);
                    Log.d(TAG, "Initialization: " + representation.initSegment.toString());
                } else if(tagName.equals("SegmentList")) {
                    long timescale = getAttributeValueLong(parser, "timescale", 1);
                    long duration = getAttributeValueLong(parser, "duration");
                    representation.segmentDurationUs = (long)(((double)duration / timescale) * 1000000d);
                } else if(tagName.equals("SegmentURL")) {
                    String media = getAttributeValue(parser, "media");
                    String mediaRange = getAttributeValue(parser, "mediaRange");
                    String indexRange = getAttributeValue(parser, "indexRange");

                    media = media != null ? extendUrl(baseUrl, media).toString() : baseUrl.toString();

                    representation.segments.add(new Segment(media, mediaRange));

                    if(indexRange != null) {
                        Log.v(TAG, "skipping unsupported indexRange in SegmentURL");
                    }
                } else if(tagName.equals("SegmentBase")) {
                    String indexRange = getAttributeValue(parser, "indexRange");
                    if(indexRange != null) {
                        throw new DashParserException("single segment / indexRange is not supported yet");
                    }
                } else if(tagName.equals("SegmentTemplate")) {
                    // Overwrite passed template with newly parsed one
                    segmentTemplate = readSegmentTemplate(parser, baseUrl, segmentTemplate);
                } else if(tagName.equals("BaseURL")) {
                    baseUrl = extendUrl(baseUrl, parser.nextText());
                    Log.d(TAG, "new base url: " + baseUrl);
                } else if(tagName.equals("RepresentationIndex")) {
                    throw new DashParserException("RepresentationIndex is not supported yet");
                }
            } else if(type == XmlPullParser.END_TAG) {
                if(tagName.equals("Representation")) {
                    if(!representation.segments.isEmpty()) {
                        // a SegmentList has been parsed, nothing to do here
                    }
                    else if(segmentTemplate != null) {
                        // We have a SegmentTemplate, expand it to a list of segments

                        if(segmentTemplate.hasTimeline()) {
                            if(segmentTemplate.timeline.size() > 1) {
                                /* TODO Add support for individual segment lengths
                                 * To support multiple timeline entries, the segmentDurationUs
                                 * must be moved from the representation to the individual segments,
                                 * because their length is not necessarily constant and can change
                                 * over time.
                                 */
                                throw new DashParserException("timeline with multiple entries is not supported yet");
                            }

                            SegmentTemplate.SegmentTimelineEntry current, previous, next;
                            for(int i = 0; i < segmentTemplate.timeline.size(); i++) {
                                current = segmentTemplate.timeline.get(i);
                                //previous = i > 0 ? segmentTemplate.timeline.get(i - 1) : null;
                                next = i < segmentTemplate.timeline.size() - 1 ? segmentTemplate.timeline.get(i + 1) : null;

                                int repeat = current.r;
                                if(repeat < 0) {
                                    long duration = next != null ? next.t - current.t :
                                            calculateTimescaleTime(mpd.mediaPresentationDurationUs, segmentTemplate.timescale) - current.t;
                                    repeat = (int)(duration / current.d) - 1;
                                }

                                representation.segmentDurationUs = calculateUs(current.d, segmentTemplate.timescale);

                                // init segment
                                String processedInitUrl = processMediaUrl(
                                        segmentTemplate.init, representation.id, null, representation.bandwidth, null);
                                representation.initSegment = new Segment(processedInitUrl);

                                // media segments
                                long time = current.t;
                                for (int number = segmentTemplate.startNumber; number < repeat + 1; number++) {
                                    String processedMediaUrl = processMediaUrl(
                                            segmentTemplate.media, representation.id, number, representation.bandwidth, time);
                                    representation.segments.add(new Segment(processedMediaUrl));
                                    time += current.d;
                                }
                            }
                        }
                        else {
                            representation.segmentDurationUs = segmentTemplate.calculateDurationUs();
                            int numSegments = (int) Math.ceil((double) mpd.mediaPresentationDurationUs / representation.segmentDurationUs);
                            int dynamicStartNumberOffset = 0;

                            if(mpd.isDynamic) {
                                // Simulate availabilityStartTime support by converting it to a startNumber
                                Date now = new Date();
                                Calendar calendar = Calendar.getInstance();

                                calendar.setTime(now);
                                calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
                                now = calendar.getTime();

                                // sync local time with server time (from http date header)
                                if(serverDate != null) {
                                    now = serverDate;
                                }

                                /* Calculate the time delta between the availability start time
                                 * and the current time, for that we know at which position we
                                 * currently are in the live stream. */
                                long availabilityDeltaTimeUs = (now.getTime() - mpd.availabilityStartTime.getTime()) * 1000;

                                // shift by the period start
                                availabilityDeltaTimeUs -= period.startUs;

                                // shift by the presentationTimeOffset
                                availabilityDeltaTimeUs -= segmentTemplate.presentationTimeOffsetUs;

                                // go back in time by the buffering period (else the segments to be buffered are not available yet)
                                availabilityDeltaTimeUs -= Math.max(mpd.minBufferTimeUs, 10 * 1000000L);

                                // go back in time by the suggested presentation delay
                                availabilityDeltaTimeUs -= mpd.suggestedPresentationDelayUs;

                                // convert the delta time to the number of corresponding segments
                                // add it to the start number (which by default is 0 if not specified)
                                dynamicStartNumberOffset = (int)(availabilityDeltaTimeUs / representation.segmentDurationUs);
                            }

                            // init segment
                            String processedInitUrl = processMediaUrl(
                                    segmentTemplate.init, representation.id, null, representation.bandwidth, null);
                            representation.initSegment = new Segment(processedInitUrl);

                            // media segments
                            for (int i = segmentTemplate.startNumber + dynamicStartNumberOffset; i < segmentTemplate.startNumber + dynamicStartNumberOffset + numSegments; i++) {
                                String processedMediaUrl = processMediaUrl(
                                        segmentTemplate.media, representation.id, i, representation.bandwidth, null);
                                representation.segments.add(new Segment(processedMediaUrl));
                            }
                        }
                    }
                    else {
                        /* When there is no SegmentList or SegmentTemplate, the only option left is
                         * a single file/segment representation. */

                        // Subtitle are not supported yet and can be ignored
                        if(representation.mimeType != null && representation.mimeType.startsWith("text/")) {
                            Log.i(TAG, "unsupported subtitle representation");
                        }
                        // Video and audio representations are vital for the player and cannot be ignored
                        else {
                            throw new DashParserException("single-segment representations are not supported yet");
                            // TODO implement single-file/single-segment support
                            // TODO add SegmentBase and sidx downloading and parsing
                        }
                    }

                    Log.d(TAG, representation.toString());

                    return representation;
                }
            }
        }

        throw new DashParserException("invalid state");
    }
Parses an MPD XML file. This needs to be executed off the main thread, else a
NetworkOnMainThreadException gets thrown.
@param source the URl of an MPD XML file
@return a MPD object
@throws android.os.NetworkOnMainThreadException if executed on the main thread
MediaPlayer-DASH/src/main/java/net/protyposis/android/mediaplayer/dash/DashParser.java