Search in sources :

Example 6 with UrlMatchDef

use of com.att.aro.core.videoanalysis.pojo.UrlMatchDef in project VideoOptimzer by attdevsupport.

the class ManifestBuilderHLS method parseManifestData.

public void parseManifestData(Manifest newManifest, byte[] data) {
    String segmentUriName;
    if (data == null || data.length == 0) {
        LOG.debug("Manifest file is invalid, it has no content");
        return;
    }
    String strData = (new String(data)).trim();
    String[] sData = strData.split("\r\n");
    if (sData == null || sData.length == 1) {
        sData = strData.split("[\n\r]");
    }
    if (sData.length < 2) {
        LOG.debug("Invalid Playlist: " + strData);
        return;
    }
    int scanLength = strData.length() > 500 ? 20 : strData.length();
    if (strData.substring(0, scanLength).contains("#EXTM3U")) {
        newManifest.setVideoType(VideoType.HLS);
        newManifest.setVideoFormat(VideoFormat.UNKNOWN);
    } else {
        LOG.debug(String.format("Unrecognized Manifest:%s \ndata:%s", newManifest.getRequest().getObjNameWithoutParams(), strData));
        return;
    }
    // for #EXT-X-BYTERANGE usage only
    int[] byteRangeOffest = new int[] { 0 };
    adjustDurationNeeded = false;
    String[] flag;
    for (int itr = 0; itr < sData.length; itr++) {
        if (!sData[itr].startsWith("#") || (flag = stringParse.parse(sData[itr], pattern)) == null) {
            continue;
        }
        ContentType contentType;
        switch(flag[0]) {
            // Master & Child -------------------------------
            case // FirstLine of a manifest
            "#EXTM3U":
                // VID-TODO revisit this, want to know the proper key structure
                String key = StringUtils.substringBefore(newManifest.getRequest().getObjNameWithoutParams(), ";");
                if (strData.contains("#EXT-X-STREAM-INF:")) {
                    // master manifest
                    // dealing with a master manifest here, hence need a new ManifestCollection
                    LOG.debug("******* Parsing new Master Manifest (Video Stream)");
                    switchManifestCollection(newManifest, key, manifest.getRequestTime());
                } else if (strData.contains("#EXTINF:")) {
                    // child manifest
                    LOG.debug(" ****** Parsing new Child Manifest");
                    if (manifestCollection == null) {
                        // special handling of childManifest when there is no parent manifest
                        switchManifestCollection(newManifest, key, manifest.getRequestTime());
                    }
                    newManifest.setManifestType(ManifestType.CHILD);
                    newManifest.setMasterManifest(masterManifest);
                    childManifest = locateChildManifest(newManifest);
                    if (childManifest.getManifest() != null && childManifest.getManifest().getChecksumCRC32() == newManifest.getChecksumCRC32()) {
                        LOG.debug("Identical VOD child manifest found, skipping..." + newManifest.getVideoName());
                        return;
                    }
                    if (childManifest.getManifest() == null) {
                        childManifest.setManifest(newManifest);
                    }
                    // set name on (child)manifest
                    childManifest.getManifest().setVideoName(StringUtils.substringBefore(buildUriNameKey(childManifest.getManifest().getRequest()), ";"));
                    if (childManifest.isVideo()) {
                        if (childManifest.getCodecs().contains(",")) {
                            childManifest.setContentType(ContentType.MUXED);
                        } else {
                            childManifest.setContentType(ContentType.VIDEO);
                        }
                    }
                } else {
                    LOG.debug("Unknown HLS manifest:\n" + strData);
                    return;
                }
                break;
            case // Indicates the compatibility version of the playlist file
            "#EXT-X-VERSION":
                break;
            // Master only -------------------------------
            case // Alternate MediaPlaylist
            "#EXT-X-MEDIA":
                // #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="eng",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="eng",URI="06.m3u8"
                // #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="caption_1",DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="ENG",URI="114.m3u8"
                childUriName = StringParse.findLabeledDataFromString("URI=", "\"", sData[itr]);
                childUriName = Util.decodeUrlEncoding(childUriName);
                if (StringUtils.isNotEmpty(childUriName)) {
                    LOG.debug("MEDIA childUriName :" + childUriName);
                    UrlMatchDef urlMatchDef = defineUrlMatching(childUriName);
                    manifest.getSegUrlMatchDef().add(urlMatchDef);
                    LOG.debug("EXT-X-MEDIA defineUrlMatching(childUriName): " + urlMatchDef);
                    childManifest = createChildManifest(null, "", childUriName);
                    childManifest.setVideo(false);
                    switch(StringParse.findLabeledDataFromString("TYPE=", ",", sData[itr])) {
                        case "AUDIO":
                            contentType = ContentType.AUDIO;
                            String channels = StringParse.findLabeledDataFromString("CHANNELS=", ",", sData[itr]);
                            if (StringUtils.isNotEmpty(channels)) {
                                childManifest.setChannels(channels.startsWith("") ? channels.substring(1, channels.length() - 1) : channels);
                            }
                            break;
                        case "CLOSED-CAPTIONS":
                        case "SUBTITLES":
                            contentType = ContentType.SUBTITLES;
                            break;
                        default:
                            contentType = ContentType.UNKNOWN;
                            break;
                    }
                    childManifest.setContentType(contentType);
                    manifest.setContentType(contentType);
                }
                break;
            case // ChildManifest Map itr:metadata-Info, ++itr:childManifestName
            "#EXT-X-STREAM-INF":
                String childParameters = sData[itr];
                childUriName = Util.decodeUrlEncoding(sData[++itr]);
                UrlMatchDef urlMatchDef = defineUrlMatching(childUriName);
                manifest.getSegUrlMatchDef().add(urlMatchDef);
                LOG.debug("EXT-X-STREAM-INF defineUrlMatching(childUriName): " + urlMatchDef);
                childManifest = manifestCollection.getChildManifest(childUriName);
                if (childManifest == null) {
                    // stored in childMap
                    childManifest = createChildManifest(null, childParameters, childUriName);
                }
                break;
            case // "keyframes" I_FrameInfo for fast-forward
            "#EXT-X-I-FRAME-STREAM-INF":
                break;
            // child manifest only -------------------------------
            case "#EXT-X-PLAYLIST-TYPE":
                if ("VOD".equals(StringParse.findLabeledDataFromString("#EXT-X-PLAYLIST-TYPE", ":", sData[itr]))) {
                    newManifest.setPlayListType(Manifest.StreamType.VOD);
                }
                break;
            case "#EXT-X-INDEPENDENT-SEGMENTS":
                break;
            case "#EXT-X-MAP":
                if (manifest.getVideoFormat().equals(VideoFormat.UNKNOWN)) {
                    if (sData[itr].contains(".mp4")) {
                        manifest.setVideoFormat(VideoFormat.MPEG4);
                        manifest.getMasterManifest().setVideoFormat(VideoFormat.MPEG4);
                    } else {
                        manifest.setVideoFormat(VideoFormat.TS);
                        manifest.getMasterManifest().setVideoFormat(VideoFormat.TS);
                    }
                }
                segmentUriName = StringParse.findLabeledDataFromString("URI=", "\"", sData[itr]);
                if (segmentUriName.contains("BUMPER") || segmentUriName.contains("DUB_CARD")) {
                    // Excluding DISCONTINUITY sections for BUMPER & DUB_CARD
                    // Suspect BUMPER is some sort of 1 segment preroll, skipping for now
                    // Suspect DUB_CARDs are advertisements, skipping for now
                    {
                        int skipCount = itr;
                        LOG.debug("Ignoring BUMPER/DUB_SUB:" + segmentUriName + "\nSkip through DISCONTINUITY");
                        for (; itr < sData.length - 1; itr++) {
                            if (sData[itr].contains("#EXT-X-DISCONTINUITY")) {
                                LOG.debug("skipped: " + (itr - skipCount + 1) + " lines");
                                break;
                            }
                        }
                    }
                    break;
                }
                String byteRangeSegmentKey = StringParse.findLabeledDataFromString("BYTERANGE=", "\"", sData[itr]);
                segmentUriName = cleanUriName(segmentUriName);
                urlMatchDef = defineUrlMatching(segmentUriName);
                segmentUriName = prefixParentUrlToSegmentUrl(segmentUriName, urlMatchDef, childManifest);
                masterManifest.getSegUrlMatchDef().add(urlMatchDef);
                newManifest.getSegUrlMatchDef().add(urlMatchDef);
                SegmentInfo segmentInfo = new SegmentInfo();
                segmentInfo.setVideo(false);
                segmentInfo.setDuration(0);
                segmentInfo.setSegmentID(0);
                segmentInfo.setQuality(String.format("%.0f", (double) childManifest.getQuality()));
                byteRangeSegmentKey = brKeyBuilder(byteRangeSegmentKey, segmentUriName, byteRangeOffest);
                childManifest.addSegment(!byteRangeSegmentKey.isEmpty() ? byteRangeSegmentKey : segmentUriName, segmentInfo);
                manifestCollection.addToSegmentTrie(segmentUriName, segmentInfo);
                if (!manifestCollection.getSegmentChildManifestTrie().containsKey(segmentUriName)) {
                    manifestCollection.addToSegmentChildManifestTrie(segmentUriName, childManifest);
                    addToSegmentManifestCollectionMap(segmentUriName);
                }
                break;
            case // Segment duration + media-file
            "#EXTINF":
                if (childManifest == null) {
                    LOG.debug("failed to locate child manifest " + sData[++itr]);
                } else {
                    String parameters = sData[itr];
                    byteRangeSegmentKey = "";
                    String byteRangeTagName = "#EXT-X-BYTERANGE";
                    int byteRangeIdx = sData[itr + 1].indexOf(byteRangeTagName);
                    if (byteRangeIdx != -1) {
                        byteRangeSegmentKey = sData[++itr].substring(byteRangeIdx + 1 + byteRangeTagName.length());
                        segmentUriName = sData[++itr];
                    } else {
                        segmentUriName = sData[++itr];
                    }
                    if (manifest.getVideoFormat().equals(VideoFormat.UNKNOWN)) {
                        if (segmentUriName.contains(".mp4")) {
                            manifest.setVideoFormat(VideoFormat.MPEG4);
                            manifest.getMasterManifest().setVideoFormat(VideoFormat.MPEG4);
                        } else {
                            manifest.setVideoFormat(VideoFormat.TS);
                            manifest.getMasterManifest().setVideoFormat(VideoFormat.TS);
                        }
                    }
                    segmentUriName = cleanUriName(segmentUriName);
                    urlMatchDef = defineUrlMatching(segmentUriName);
                    segmentUriName = prefixParentUrlToSegmentUrl(segmentUriName, urlMatchDef, childManifest);
                    masterManifest.getSegUrlMatchDef().add(urlMatchDef);
                    newManifest.getSegUrlMatchDef().add(urlMatchDef);
                    LOG.debug("EXTINF defineUrlMatching(childUriName): " + urlMatchDef);
                    segmentInfo = createSegmentInfo(childManifest, parameters, segmentUriName);
                    String[] segmentMatch = stringParse.parse(segmentUriName, "-([0-9]*T[0-9]*)-([0-9]*)-");
                    if (segmentMatch != null) {
                        newManifest.getTimeScale();
                        segmentInfo.setStartTime(0);
                    }
                    segmentUriName = Util.decodeUrlEncoding(segmentUriName);
                    segmentUriName = StringUtils.substringBefore(segmentUriName, "?");
                    byteRangeSegmentKey = brKeyBuilder(byteRangeSegmentKey, segmentUriName, byteRangeOffest);
                    if (childManifest.addSegment(!byteRangeSegmentKey.isEmpty() ? byteRangeSegmentKey : segmentUriName, segmentInfo).equals(segmentInfo)) {
                        manifestCollection.addToSegmentTrie(segmentUriName, segmentInfo);
                        manifestCollection.addToTimestampChildManifestMap(manifest.getRequest().getTimeStamp(), childManifest);
                        if (!manifestCollection.getSegmentChildManifestTrie().containsKey(segmentUriName)) {
                            manifestCollection.addToSegmentChildManifestTrie(segmentUriName, childManifest);
                            addToSegmentManifestCollectionMap(segmentUriName);
                        }
                    }
                }
                break;
            case // Segment ENDLIST for VOD (STATIC)
            "#EXT-X-ENDLIST":
                break;
            case // Segment is encrypted using [METHOD=____]
            "#EXT-X-KEY":
                break;
            case // Indicates the sequence number of the first URL that appears in a playlist file
            "#EXT-X-MEDIA-SEQUENCE":
                mediaSequence = StringParse.findLabeledDoubleFromString("#EXT-X-MEDIA-SEQUENCE", ":", sData[itr]);
                if (mediaSequence != null) {
                    if (childManifest.getSequenceStart() < 0) {
                        childManifest.setSequenceStart(mediaSequence.intValue());
                    }
                    manifestCollection.setMinimumSequenceStart(mediaSequence.intValue());
                    if (!sData[sData.length - 1].startsWith("#EXT-X-ENDLIST")) {
                        Integer pointer;
                        if ((pointer = findIndex(sData, childManifest)) != null) {
                            itr = pointer;
                        }
                    }
                }
                break;
            case // Specifies the maximum media-file duration.
            "#EXT-X-TARGETDURATION":
                // processExtXtargetDuration(line);
                break;
            case // YYYY-MM-DDTHH:MM:sss
            "#EXT-X-PROGRAM-DATE-TIME":
                String dateString = StringParse.findLabeledDataFromString("#EXT-X-PROGRAM-DATE-TIME:", "\\$", sData[itr]);
                dateString = dateString.replaceAll("\\+00\\:00", "000Z");
                long programDateTime = Util.parseForUTC(dateString);
                if (masterManifest.updateStreamProgramDateTime(programDateTime)) {
                    // VID-TODO need to resync segments across ManifestCollection
                    LOG.debug("Program time changed, need to resync segments across ManifestCollection");
                }
                if (childManifest.getSegmentStartTime() == 0) {
                    childManifest.setStreamProgramDateTime(programDateTime);
                }
                break;
            case "#USP-X-MEDIA":
                double bandwidth = StringParse.findLabeledDoubleFromString("BANDWIDTH=", ",", sData[itr]);
                childManifest.setBandwidth(bandwidth);
                childManifest.setCodecs(StringParse.findLabeledDataFromString("CODECS=", "\"", sData[itr]));
                String content = StringParse.findLabeledDataFromString("TYPE=", ",", sData[itr]);
                if (StringUtils.isNotEmpty(content)) {
                    if ("AUDIO".equals(content)) {
                        childManifest.setContentType(ContentType.AUDIO);
                        String val = StringParse.findLabeledDataFromString("CHANNELS=", "\"", sData[itr]);
                        if (val.contentEquals("")) {
                            val = "NA ";
                        }
                        LOG.debug("Parsing audio channels values: " + val);
                        childManifest.setChannels(val);
                    } else if ("VIDEO".equals(content)) {
                        childManifest.setContentType(ContentType.VIDEO);
                    }
                }
                break;
            case // #USP-X-TIMESTAMP-MAP:MPEGTS=900000,LOCAL=1970-01-01T00:00:00Z
            "#USP-X-TIMESTAMP-MAP":
                break;
            default:
                break;
        }
    }
    if (manifest.getManifestType().equals(Manifest.ManifestType.MASTER)) {
        assignQuality(manifestCollection);
    }
    if (adjustDurationNeeded) {
        adjustDurations(childManifest);
    }
}
Also used : UrlMatchDef(com.att.aro.core.videoanalysis.pojo.UrlMatchDef) ContentType(com.att.aro.core.videoanalysis.pojo.Manifest.ContentType)

Example 7 with UrlMatchDef

use of com.att.aro.core.videoanalysis.pojo.UrlMatchDef in project VideoOptimzer by attdevsupport.

the class VideoStreamConstructor method extractManifestHLS.

/**
 * <pre>Extract a HLS manifest from traffic data
 *
 * Types: movie, livetv
 * </pre>
 * @param streamingVideoData
 * @param request
 * @return Manifest
 */
public Manifest extractManifestHLS(StreamingVideoData streamingVideoData, HttpRequestResponseInfo request) {
    manifestBuilder = manifestBuilderHLS;
    LOG.debug("\nHLS request:\n" + request.getObjUri());
    this.streamingVideoData = streamingVideoData;
    byte[] content = extractContent(request, StreamContentType.MANIFEST);
    if (content == null) {
        return null;
    }
    Manifest manifest = manifestBuilderHLS.create(request, content, "blank");
    String fileName = manifest.getVideoName();
    if (!manifest.getManifestType().equals(ManifestType.MASTER)) {
        SortedSet<UrlMatchDef> segUrlMatchDefSortedSet = manifest.getMasterManifest().getSegUrlMatchDef();
        if (segUrlMatchDefSortedSet.size() > 0) {
            fileName = manifestBuilder.buildUriNameKey(segUrlMatchDefSortedSet.first(), request);
        } else {
            fileName = manifestBuilder.buildUriNameKey(request);
        }
    }
    request.getSession().getSessionStartTime();
    fileName = StringUtils.substringBefore(fileName, ";");
    if (StringUtils.countMatches(fileName, "/") > 3) {
        fileName = shortenNameByParts(fileName, "/", 2);
    }
    savePayload(content, buildPath(streamingVideoData, request, -1, "m", fileName));
    return manifest;
}
Also used : UrlMatchDef(com.att.aro.core.videoanalysis.pojo.UrlMatchDef) ChildManifest(com.att.aro.core.videoanalysis.pojo.ChildManifest) Manifest(com.att.aro.core.videoanalysis.pojo.Manifest)

Aggregations

UrlMatchDef (com.att.aro.core.videoanalysis.pojo.UrlMatchDef)7 ChildManifest (com.att.aro.core.videoanalysis.pojo.ChildManifest)3 Manifest (com.att.aro.core.videoanalysis.pojo.Manifest)3 HttpRequestResponseInfo (com.att.aro.core.packetanalysis.pojo.HttpRequestResponseInfo)2 IStringParse (com.att.aro.core.util.IStringParse)2 StringParse (com.att.aro.core.util.StringParse)2 Util (com.att.aro.core.util.Util)2 ContentType (com.att.aro.core.videoanalysis.pojo.Manifest.ContentType)2 ManifestType (com.att.aro.core.videoanalysis.pojo.Manifest.ManifestType)2 ManifestCollection (com.att.aro.core.videoanalysis.pojo.ManifestCollection)2 VideoFormat (com.att.aro.core.videoanalysis.pojo.VideoFormat)2 HashMap (java.util.HashMap)2 Map (java.util.Map)2 Pattern (java.util.regex.Pattern)2 CRC32 (java.util.zip.CRC32)2 NonNull (lombok.NonNull)2 StringUtils (org.apache.commons.lang.StringUtils)2 LogManager (org.apache.log4j.LogManager)2 Logger (org.apache.log4j.Logger)2 AROConfig (com.att.aro.core.AROConfig)1