1e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber/*
2e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * Copyright (C) 2010 The Android Open Source Project
3e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber *
4e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License");
5e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * you may not use this file except in compliance with the License.
6e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * You may obtain a copy of the License at
7e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber *
8e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber *      http://www.apache.org/licenses/LICENSE-2.0
9e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber *
10e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * Unless required by applicable law or agreed to in writing, software
11e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS,
12e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * See the License for the specific language governing permissions and
14e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber * limitations under the License.
15e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber */
16e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
17ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber//#define LOG_NDEBUG 0
18ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber#define LOG_TAG "M3UParser"
19ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber#include <utils/Log.h>
20ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber
2114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber#include "M3UParser.h"
22dcb89b3b505522efde173c105a851c412f947178Chong Zhang#include <binder/Parcel.h>
2314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber#include <cutils/properties.h>
240a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber#include <media/stagefright/foundation/ADebug.h>
25e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber#include <media/stagefright/foundation/AMessage.h>
26404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang#include <media/stagefright/MediaDefs.h>
27e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber#include <media/stagefright/MediaErrors.h>
28ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih#include <media/stagefright/Utils.h>
29dcb89b3b505522efde173c105a851c412f947178Chong Zhang#include <media/mediaplayer.h>
30e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
31e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Hubernamespace android {
32e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
3314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberstruct M3UParser::MediaGroup : public RefBase {
3414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    enum Type {
3514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        TYPE_AUDIO,
3614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        TYPE_VIDEO,
3714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        TYPE_SUBS,
3899cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang        TYPE_CC,
3914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    };
4014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
4114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    enum FlagBits {
4214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        FLAG_AUTOSELECT         = 1,
4314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        FLAG_DEFAULT            = 2,
4414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        FLAG_FORCED             = 4,
4514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        FLAG_HAS_LANGUAGE       = 8,
4614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        FLAG_HAS_URI            = 16,
4714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    };
4814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
4914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    MediaGroup(Type type);
5014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
5114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    Type type() const;
5214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
5314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    status_t addMedia(
5414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            const char *name,
5514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            const char *uri,
5614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            const char *language,
5714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            uint32_t flags);
5814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
5914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool getActiveURI(AString *uri) const;
6014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
6114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    void pickRandomMediaItems();
62dcb89b3b505522efde173c105a851c412f947178Chong Zhang    status_t selectTrack(size_t index, bool select);
63dcb89b3b505522efde173c105a851c412f947178Chong Zhang    size_t countTracks() const;
64404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    sp<AMessage> getTrackInfo(size_t index) const;
6514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
6614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberprotected:
6714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    virtual ~MediaGroup();
6814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
6914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberprivate:
7089bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
7189bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    friend struct M3UParser;
7289bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
7314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    struct Media {
7414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        AString mName;
7514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        AString mURI;
7614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        AString mLanguage;
7714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        uint32_t mFlags;
7814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    };
7914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
8014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    Type mType;
8114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    Vector<Media> mMediaItems;
8214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
8314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    ssize_t mSelectedIndex;
8414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
8514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    DISALLOW_EVIL_CONSTRUCTORS(MediaGroup);
8614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber};
8714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
8814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas HuberM3UParser::MediaGroup::MediaGroup(Type type)
8914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    : mType(type),
9014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber      mSelectedIndex(-1) {
9114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
9214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
9314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas HuberM3UParser::MediaGroup::~MediaGroup() {
9414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
9514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
9614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas HuberM3UParser::MediaGroup::Type M3UParser::MediaGroup::type() const {
9714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return mType;
9814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
9914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
10014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberstatus_t M3UParser::MediaGroup::addMedia(
10114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        const char *name,
10214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        const char *uri,
10314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        const char *language,
10414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        uint32_t flags) {
10514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    mMediaItems.push();
10614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    Media &item = mMediaItems.editItemAt(mMediaItems.size() - 1);
10714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
10814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    item.mName = name;
10914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
11014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (uri) {
11114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        item.mURI = uri;
11214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
11314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
11414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (language) {
11514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        item.mLanguage = language;
11614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
11714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
11814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    item.mFlags = flags;
11914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
12014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return OK;
12114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
12214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
12314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Hubervoid M3UParser::MediaGroup::pickRandomMediaItems() {
12414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber#if 1
12514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    switch (mType) {
12614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        case TYPE_AUDIO:
12714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        {
12814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            char value[PROPERTY_VALUE_MAX];
12914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (property_get("media.httplive.audio-index", value, NULL)) {
13014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                char *end;
13114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                mSelectedIndex = strtoul(value, &end, 10);
13214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                CHECK(end > value && *end == '\0');
13314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
13484333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber                if (mSelectedIndex >= (ssize_t)mMediaItems.size()) {
13514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    mSelectedIndex = mMediaItems.size() - 1;
13614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                }
13714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else {
13814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                mSelectedIndex = 0;
13914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
14014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            break;
14114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
14214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
14314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        case TYPE_VIDEO:
14414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        {
14514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            mSelectedIndex = 0;
14614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            break;
14714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
14814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
14914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        case TYPE_SUBS:
15014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        {
15114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            mSelectedIndex = -1;
15214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            break;
15314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
15414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
15514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        default:
15614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            TRESPASS();
15714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
15814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber#else
15914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    mSelectedIndex = (rand() * mMediaItems.size()) / RAND_MAX;
16014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber#endif
16114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
16214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
163dcb89b3b505522efde173c105a851c412f947178Chong Zhangstatus_t M3UParser::MediaGroup::selectTrack(size_t index, bool select) {
164309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    if (mType != TYPE_SUBS && mType != TYPE_AUDIO) {
165309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih        ALOGE("only select subtitile/audio tracks for now!");
166dcb89b3b505522efde173c105a851c412f947178Chong Zhang        return INVALID_OPERATION;
167dcb89b3b505522efde173c105a851c412f947178Chong Zhang    }
168dcb89b3b505522efde173c105a851c412f947178Chong Zhang
169dcb89b3b505522efde173c105a851c412f947178Chong Zhang    if (select) {
170dcb89b3b505522efde173c105a851c412f947178Chong Zhang        if (index >= mMediaItems.size()) {
171b4a7a2df4c28c3f32b5d877b54831d2cc5d78f81Colin Cross            ALOGE("track %zu does not exist", index);
172dcb89b3b505522efde173c105a851c412f947178Chong Zhang            return INVALID_OPERATION;
173dcb89b3b505522efde173c105a851c412f947178Chong Zhang        }
17484333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber        if (mSelectedIndex == (ssize_t)index) {
175b4a7a2df4c28c3f32b5d877b54831d2cc5d78f81Colin Cross            ALOGE("track %zu already selected", index);
176dcb89b3b505522efde173c105a851c412f947178Chong Zhang            return BAD_VALUE;
177dcb89b3b505522efde173c105a851c412f947178Chong Zhang        }
178b4a7a2df4c28c3f32b5d877b54831d2cc5d78f81Colin Cross        ALOGV("selected track %zu", index);
179dcb89b3b505522efde173c105a851c412f947178Chong Zhang        mSelectedIndex = index;
180dcb89b3b505522efde173c105a851c412f947178Chong Zhang    } else {
18184333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber        if (mSelectedIndex != (ssize_t)index) {
182b4a7a2df4c28c3f32b5d877b54831d2cc5d78f81Colin Cross            ALOGE("track %zu is not selected", index);
183dcb89b3b505522efde173c105a851c412f947178Chong Zhang            return BAD_VALUE;
184dcb89b3b505522efde173c105a851c412f947178Chong Zhang        }
185b4a7a2df4c28c3f32b5d877b54831d2cc5d78f81Colin Cross        ALOGV("unselected track %zu", index);
186dcb89b3b505522efde173c105a851c412f947178Chong Zhang        mSelectedIndex = -1;
187dcb89b3b505522efde173c105a851c412f947178Chong Zhang    }
188dcb89b3b505522efde173c105a851c412f947178Chong Zhang
189dcb89b3b505522efde173c105a851c412f947178Chong Zhang    return OK;
190dcb89b3b505522efde173c105a851c412f947178Chong Zhang}
191dcb89b3b505522efde173c105a851c412f947178Chong Zhang
192404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhangsize_t M3UParser::MediaGroup::countTracks() const {
193404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    return mMediaItems.size();
194404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang}
195dcb89b3b505522efde173c105a851c412f947178Chong Zhang
196404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhangsp<AMessage> M3UParser::MediaGroup::getTrackInfo(size_t index) const {
197404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    if (index >= mMediaItems.size()) {
198404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        return NULL;
199404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    }
200a911f51c21430ac92f1d796b2338878fd98382e9Chong Zhang
201404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    sp<AMessage> format = new AMessage();
202404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang
203404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    int32_t trackType;
204404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    if (mType == TYPE_AUDIO) {
205404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        trackType = MEDIA_TRACK_TYPE_AUDIO;
206404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    } else if (mType == TYPE_VIDEO) {
207404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        trackType = MEDIA_TRACK_TYPE_VIDEO;
208404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    } else if (mType == TYPE_SUBS) {
209404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        trackType = MEDIA_TRACK_TYPE_SUBTITLE;
210404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    } else {
211404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        trackType = MEDIA_TRACK_TYPE_UNKNOWN;
212404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    }
213404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    format->setInt32("type", trackType);
214404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang
215404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    const Media &item = mMediaItems.itemAt(index);
216404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    const char *lang = item.mLanguage.empty() ? "und" : item.mLanguage.c_str();
217404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    format->setString("language", lang);
218404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang
219404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    if (mType == TYPE_SUBS) {
220404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        // TO-DO: pass in a MediaFormat instead
221404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        format->setString("mime", MEDIA_MIMETYPE_TEXT_VTT);
222404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        format->setInt32("auto", !!(item.mFlags & MediaGroup::FLAG_AUTOSELECT));
223404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        format->setInt32("default", !!(item.mFlags & MediaGroup::FLAG_DEFAULT));
224404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        format->setInt32("forced", !!(item.mFlags & MediaGroup::FLAG_FORCED));
225dcb89b3b505522efde173c105a851c412f947178Chong Zhang    }
226dcb89b3b505522efde173c105a851c412f947178Chong Zhang
227404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    return format;
228dcb89b3b505522efde173c105a851c412f947178Chong Zhang}
229dcb89b3b505522efde173c105a851c412f947178Chong Zhang
23014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberbool M3UParser::MediaGroup::getActiveURI(AString *uri) const {
23114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    for (size_t i = 0; i < mMediaItems.size(); ++i) {
23214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) {
23314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            const Media &item = mMediaItems.itemAt(i);
23414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
23514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            *uri = item.mURI;
23614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            return true;
23714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
23814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
23914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
24014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return false;
24114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
24214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
24314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber////////////////////////////////////////////////////////////////////////////////
24414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
245e71d10e7ad55ccbcb0756c007caef1c959090384Andreas HuberM3UParser::M3UParser(
246e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        const char *baseURI, const void *data, size_t size)
247e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    : mInitCheck(NO_INIT),
248e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber      mBaseURI(baseURI),
249e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber      mIsExtM3U(false),
250bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huber      mIsVariantPlaylist(false),
251b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huber      mIsComplete(false),
252dcb89b3b505522efde173c105a851c412f947178Chong Zhang      mIsEvent(false),
253309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih      mDiscontinuitySeq(0),
254dcb89b3b505522efde173c105a851c412f947178Chong Zhang      mSelectedIndex(-1) {
255e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    mInitCheck = parse(data, size);
256e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
257e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
258e71d10e7ad55ccbcb0756c007caef1c959090384Andreas HuberM3UParser::~M3UParser() {
259e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
260e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
261e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberstatus_t M3UParser::initCheck() const {
262e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return mInitCheck;
263e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
264e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
265e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberbool M3UParser::isExtM3U() const {
266e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return mIsExtM3U;
267e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
268e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
269e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberbool M3UParser::isVariantPlaylist() const {
270e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return mIsVariantPlaylist;
271e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
272e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
273bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huberbool M3UParser::isComplete() const {
274bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huber    return mIsComplete;
275bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huber}
276bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huber
277b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huberbool M3UParser::isEvent() const {
278b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huber    return mIsEvent;
279b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huber}
280b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huber
281309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shihsize_t M3UParser::getDiscontinuitySeq() const {
282309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    return mDiscontinuitySeq;
283309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih}
284309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih
285e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Hubersp<AMessage> M3UParser::meta() {
286e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return mMeta;
287e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
288e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
289e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Hubersize_t M3UParser::size() {
290e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return mItems.size();
291e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
292e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
293e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberbool M3UParser::itemAt(size_t index, AString *uri, sp<AMessage> *meta) {
2940f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber    if (uri) {
2950f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber        uri->clear();
2960f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber    }
2970f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber
2980f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber    if (meta) {
2990f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber        *meta = NULL;
3000f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber    }
301e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
302e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    if (index >= mItems.size()) {
303e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        return false;
304e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
305e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
3060f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber    if (uri) {
3070f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber        *uri = mItems.itemAt(index).mURI;
3080f30bd90272c818aa37c0bb22d22eaa7d3689879Andreas Huber    }
309e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
310e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    if (meta) {
311e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        *meta = mItems.itemAt(index).mMeta;
312e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
313e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
314e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return true;
315e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
316e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
31714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Hubervoid M3UParser::pickRandomMediaItems() {
31814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    for (size_t i = 0; i < mMediaGroups.size(); ++i) {
31914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        mMediaGroups.valueAt(i)->pickRandomMediaItems();
32014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
32114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
32214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
323dcb89b3b505522efde173c105a851c412f947178Chong Zhangstatus_t M3UParser::selectTrack(size_t index, bool select) {
324dcb89b3b505522efde173c105a851c412f947178Chong Zhang    for (size_t i = 0, ii = index; i < mMediaGroups.size(); ++i) {
325dcb89b3b505522efde173c105a851c412f947178Chong Zhang        sp<MediaGroup> group = mMediaGroups.valueAt(i);
326dcb89b3b505522efde173c105a851c412f947178Chong Zhang        size_t tracks = group->countTracks();
327dcb89b3b505522efde173c105a851c412f947178Chong Zhang        if (ii < tracks) {
328dcb89b3b505522efde173c105a851c412f947178Chong Zhang            status_t err = group->selectTrack(ii, select);
329dcb89b3b505522efde173c105a851c412f947178Chong Zhang            if (err == OK) {
330dcb89b3b505522efde173c105a851c412f947178Chong Zhang                mSelectedIndex = select ? index : -1;
331dcb89b3b505522efde173c105a851c412f947178Chong Zhang            }
332dcb89b3b505522efde173c105a851c412f947178Chong Zhang            return err;
333dcb89b3b505522efde173c105a851c412f947178Chong Zhang        }
334dcb89b3b505522efde173c105a851c412f947178Chong Zhang        ii -= tracks;
335dcb89b3b505522efde173c105a851c412f947178Chong Zhang    }
336dcb89b3b505522efde173c105a851c412f947178Chong Zhang    return INVALID_OPERATION;
337dcb89b3b505522efde173c105a851c412f947178Chong Zhang}
338dcb89b3b505522efde173c105a851c412f947178Chong Zhang
339404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhangsize_t M3UParser::getTrackCount() const {
340dcb89b3b505522efde173c105a851c412f947178Chong Zhang    size_t trackCount = 0;
341dcb89b3b505522efde173c105a851c412f947178Chong Zhang    for (size_t i = 0; i < mMediaGroups.size(); ++i) {
342dcb89b3b505522efde173c105a851c412f947178Chong Zhang        trackCount += mMediaGroups.valueAt(i)->countTracks();
343dcb89b3b505522efde173c105a851c412f947178Chong Zhang    }
344404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    return trackCount;
345404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang}
346dcb89b3b505522efde173c105a851c412f947178Chong Zhang
347404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhangsp<AMessage> M3UParser::getTrackInfo(size_t index) const {
348404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    for (size_t i = 0, ii = index; i < mMediaGroups.size(); ++i) {
349404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        sp<MediaGroup> group = mMediaGroups.valueAt(i);
350404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        size_t tracks = group->countTracks();
351404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        if (ii < tracks) {
352404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang            return group->getTrackInfo(ii);
353404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        }
354404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang        ii -= tracks;
355dcb89b3b505522efde173c105a851c412f947178Chong Zhang    }
356404fced9bfa8fa423ee210a271ca051ffd1bec13Chong Zhang    return NULL;
357dcb89b3b505522efde173c105a851c412f947178Chong Zhang}
358dcb89b3b505522efde173c105a851c412f947178Chong Zhang
359dcb89b3b505522efde173c105a851c412f947178Chong Zhangssize_t M3UParser::getSelectedIndex() const {
360dcb89b3b505522efde173c105a851c412f947178Chong Zhang    return mSelectedIndex;
361dcb89b3b505522efde173c105a851c412f947178Chong Zhang}
362dcb89b3b505522efde173c105a851c412f947178Chong Zhang
36389bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shihssize_t M3UParser::getSelectedTrack(media_track_type type) const {
36489bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    MediaGroup::Type groupType;
36589bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    switch (type) {
36689bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        case MEDIA_TRACK_TYPE_VIDEO:
36789bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            groupType = MediaGroup::TYPE_VIDEO;
36889bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            break;
36989bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
37089bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        case MEDIA_TRACK_TYPE_AUDIO:
37189bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            groupType = MediaGroup::TYPE_AUDIO;
37289bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            break;
37389bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
37489bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        case MEDIA_TRACK_TYPE_SUBTITLE:
37589bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            groupType = MediaGroup::TYPE_SUBS;
37689bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            break;
37789bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
37889bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        default:
37989bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            return -1;
38089bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    }
38189bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
38289bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    for (size_t i = 0, ii = 0; i < mMediaGroups.size(); ++i) {
38389bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        sp<MediaGroup> group = mMediaGroups.valueAt(i);
38489bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        size_t tracks = group->countTracks();
38589bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        if (groupType != group->mType) {
38689bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            ii += tracks;
38789bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        } else if (group->mSelectedIndex >= 0) {
38889bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih            return ii + group->mSelectedIndex;
38989bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih        }
39089bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    }
39189bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
39289bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih    return -1;
39389bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih}
39489bf2525c5b57f17260de5b00c5f3f78ac4b881eRobert Shih
39514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberbool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const {
39614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (!mIsVariantPlaylist) {
39714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        *uri = mBaseURI;
39814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
39914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        // Assume media without any more specific attribute contains
40014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        // audio and video, but no subtitles.
40114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        return !strcmp("audio", key) || !strcmp("video", key);
40214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
40314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
40414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    CHECK_LT(index, mItems.size());
40514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
40614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    sp<AMessage> meta = mItems.itemAt(index).mMeta;
40714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
40814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    AString groupID;
40914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (!meta->findString(key, &groupID)) {
41014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        *uri = mItems.itemAt(index).mURI;
41114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
412ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        AString codecs;
413ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        if (!meta->findString("codecs", &codecs)) {
414ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            // Assume media without any more specific attribute contains
415ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            // audio and video, but no subtitles.
416ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            return !strcmp("audio", key) || !strcmp("video", key);
417ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        } else {
418ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            // Split the comma separated list of codecs.
419ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            size_t offset = 0;
420ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            ssize_t commaPos = -1;
421ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            codecs.append(',');
422ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            while ((commaPos = codecs.find(",", offset)) >= 0) {
423ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                AString codec(codecs, offset, commaPos - offset);
4241ac1638077277de52d1dd4c54db71e67753f1960Robert Shih                codec.trim();
425ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                // return true only if a codec of type `key` ("audio"/"video")
426ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                // is found.
427ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                if (codecIsType(codec, key)) {
428ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                    return true;
429ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                }
430ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                offset = commaPos + 1;
431ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            }
432ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            return false;
433ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        }
43414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
43514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
43614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    sp<MediaGroup> group = mMediaGroups.valueFor(groupID);
43714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (!group->getActiveURI(uri)) {
43814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        return false;
43914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
44014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
44114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if ((*uri).empty()) {
44214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        *uri = mItems.itemAt(index).mURI;
44314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
44414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
44514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return true;
44614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
44714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
448e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberstatic bool MakeURL(const char *baseURL, const char *url, AString *out) {
449e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    out->clear();
450e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
4512a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber    if (strncasecmp("http://", baseURL, 7)
452df42f949c8bd05b81d94633767514fff88f52062Andreas Huber            && strncasecmp("https://", baseURL, 8)
4532a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            && strncasecmp("file://", baseURL, 7)) {
454e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        // Base URL must be absolute
455e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        return false;
456e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
457a8454c29b15748964b5eb556d1e23fbb1ee19cebLajos Molnar    const size_t schemeEnd = (strstr(baseURL, "//") - baseURL) + 2;
458a8454c29b15748964b5eb556d1e23fbb1ee19cebLajos Molnar    CHECK(schemeEnd == 7 || schemeEnd == 8);
459e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
460df42f949c8bd05b81d94633767514fff88f52062Andreas Huber    if (!strncasecmp("http://", url, 7) || !strncasecmp("https://", url, 8)) {
461e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        // "url" is already an absolute URL, ignore base URL.
462e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        out->setTo(url);
463ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber
4643856b090cd04ba5dd4a59a12430ed724d5995909Steve Block        ALOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
465ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber
466e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        return true;
467e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
468e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
469d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber    if (url[0] == '/') {
470d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        // URL is an absolute path.
471d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber
472d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        char *protocolEnd = strstr(baseURL, "//") + 2;
473d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        char *pathStart = strchr(protocolEnd, '/');
474e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
475d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        if (pathStart != NULL) {
476d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber            out->setTo(baseURL, pathStart - baseURL);
477e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        } else {
478e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            out->setTo(baseURL);
479e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        }
480e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
481e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        out->append(url);
482d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber    } else {
483d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        // URL is a relative path
484d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber
4858883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        // Check for a possible query string
4868883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        const char *qsPos = strchr(baseURL, '?');
4878883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        size_t end;
4888883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        if (qsPos != NULL) {
4898883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo            end = qsPos - baseURL;
490d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        } else {
4918883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo            end = strlen(baseURL);
4928883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        }
4938883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        // Check for the last slash before a potential query string
494e175d77bb610675ba2f70f24449189995ddeaf5bMarco Nelissen        for (ssize_t pos = end - 1; pos >= 0; pos--) {
4958883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo            if (baseURL[pos] == '/') {
4968883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo                end = pos;
4978883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo                break;
498d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber            }
4998883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        }
500d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber
5018883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        // Check whether the found slash actually is part of the path
5028883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        // and not part of the "http://".
503a8454c29b15748964b5eb556d1e23fbb1ee19cebLajos Molnar        if (end >= schemeEnd) {
5048883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo            out->setTo(baseURL, end);
5058883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        } else {
5068883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo            out->setTo(baseURL);
507d6a46a6bda23667e06ee5ccca3781e407c7d1204Andreas Huber        }
5088883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo
5098883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        out->append("/");
5108883a38a308fb02a653c990118ec7931a47b749bMartin Storsjo        out->append(url);
511e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
512e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
5133856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    ALOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
514ac13b81c590386b3a42e381b31d7614ca53b79c5Andreas Huber
515e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return true;
516e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
517e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
518e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberstatus_t M3UParser::parse(const void *_data, size_t size) {
519e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    int32_t lineNo = 0;
520e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
521e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    sp<AMessage> itemMeta;
522e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
523e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    const char *data = (const char *)_data;
524e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    size_t offset = 0;
5252aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    uint64_t segmentRangeOffset = 0;
526e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    while (offset < size) {
527e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        size_t offsetLF = offset;
528e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        while (offsetLF < size && data[offsetLF] != '\n') {
529e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            ++offsetLF;
530e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        }
531e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
532e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        AString line;
533e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        if (offsetLF > offset && data[offsetLF - 1] == '\r') {
534e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            line.setTo(&data[offset], offsetLF - offset - 1);
535e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        } else {
536e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            line.setTo(&data[offset], offsetLF - offset);
537e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        }
538e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
539df64d15042bbd5e0e4933ac49bf3c177dd94752cSteve Block        // ALOGI("#%s#", line.c_str());
5402a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
5412a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        if (line.empty()) {
5422a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            offset = offsetLF + 1;
5432a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            continue;
5442a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        }
545e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
546e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        if (lineNo == 0 && line == "#EXTM3U") {
547e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            mIsExtM3U = true;
548e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        }
549e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
550e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        if (mIsExtM3U) {
551e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            status_t err = OK;
552e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
553e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            if (line.startsWith("#EXT-X-TARGETDURATION")) {
554e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                if (mIsVariantPlaylist) {
555e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                    return ERROR_MALFORMED;
556e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                }
557e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                err = parseMetaData(line, &mMeta, "target-duration");
558e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            } else if (line.startsWith("#EXT-X-MEDIA-SEQUENCE")) {
559e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                if (mIsVariantPlaylist) {
560e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                    return ERROR_MALFORMED;
561e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                }
562e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                err = parseMetaData(line, &mMeta, "media-sequence");
563e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            } else if (line.startsWith("#EXT-X-KEY")) {
564e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber                if (mIsVariantPlaylist) {
565e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber                    return ERROR_MALFORMED;
566e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber                }
567decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                err = parseCipherInfo(line, &itemMeta, mBaseURI);
568bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huber            } else if (line.startsWith("#EXT-X-ENDLIST")) {
569bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0Andreas Huber                mIsComplete = true;
570b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huber            } else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:EVENT")) {
571b7c8e91880463ff4981e3e53e98e45d68e2fe374Andreas Huber                mIsEvent = true;
572e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            } else if (line.startsWith("#EXTINF")) {
573e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                if (mIsVariantPlaylist) {
574e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                    return ERROR_MALFORMED;
575e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                }
5769067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber                err = parseMetaDataDuration(line, &itemMeta, "durationUs");
5772a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            } else if (line.startsWith("#EXT-X-DISCONTINUITY")) {
5782a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                if (mIsVariantPlaylist) {
5792a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                    return ERROR_MALFORMED;
5802a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                }
5812a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                if (itemMeta == NULL) {
5822a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                    itemMeta = new AMessage;
5832a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                }
5842a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                itemMeta->setInt32("discontinuity", true);
585e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            } else if (line.startsWith("#EXT-X-STREAM-INF")) {
586e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                if (mMeta != NULL) {
587e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                    return ERROR_MALFORMED;
588e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                }
589e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                mIsVariantPlaylist = true;
5902a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                err = parseStreamInf(line, &itemMeta);
5912aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber            } else if (line.startsWith("#EXT-X-BYTERANGE")) {
5922aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                if (mIsVariantPlaylist) {
5932aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                    return ERROR_MALFORMED;
5942aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                }
5952aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
5962aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                uint64_t length, offset;
5972aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                err = parseByteRange(line, segmentRangeOffset, &length, &offset);
5982aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
5992aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                if (err == OK) {
6002aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                    if (itemMeta == NULL) {
6012aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                        itemMeta = new AMessage;
6022aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                    }
6032aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
6042aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                    itemMeta->setInt64("range-offset", offset);
6052aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                    itemMeta->setInt64("range-length", length);
6062aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
6072aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                    segmentRangeOffset = offset + length;
6082aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber                }
60914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else if (line.startsWith("#EXT-X-MEDIA")) {
61014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                err = parseMedia(line);
611309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih            } else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) {
612309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih                size_t seq;
613309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih                err = parseDiscontinuitySequence(line, &seq);
614309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih                if (err == OK) {
615309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih                    mDiscontinuitySeq = seq;
616309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih                }
617e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            }
618e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
619e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            if (err != OK) {
620e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                return err;
621e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            }
622e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        }
623e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
624e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        if (!line.startsWith("#")) {
625e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            if (!mIsVariantPlaylist) {
6269067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber                int64_t durationUs;
627e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                if (itemMeta == NULL
6289067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber                        || !itemMeta->findInt64("durationUs", &durationUs)) {
629e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                    return ERROR_MALFORMED;
630e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber                }
631e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            }
632e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
633e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            mItems.push();
634e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            Item *item = &mItems.editItemAt(mItems.size() - 1);
635e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
636e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            CHECK(MakeURL(mBaseURI.c_str(), line.c_str(), &item->mURI));
637e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
638e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            item->mMeta = itemMeta;
639e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
640e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber            itemMeta.clear();
641e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        }
642e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
643e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        offset = offsetLF + 1;
644e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        ++lineNo;
645e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
646e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
647e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return OK;
648e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
649e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
650e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber// static
651e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberstatus_t M3UParser::parseMetaData(
652e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        const AString &line, sp<AMessage> *meta, const char *key) {
653e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    ssize_t colonPos = line.find(":");
654e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
655e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    if (colonPos < 0) {
656e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        return ERROR_MALFORMED;
657e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
658e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
659e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    int32_t x;
660e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    status_t err = ParseInt32(line.c_str() + colonPos + 1, &x);
661e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
662e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    if (err != OK) {
663e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        return err;
664e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
665e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
666e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    if (meta->get() == NULL) {
667e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        *meta = new AMessage;
668e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
669e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    (*meta)->setInt32(key, x);
670e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
671e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return OK;
672e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
673e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
674e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber// static
6759067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huberstatus_t M3UParser::parseMetaDataDuration(
6769067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber        const AString &line, sp<AMessage> *meta, const char *key) {
6779067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    ssize_t colonPos = line.find(":");
6789067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
6799067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    if (colonPos < 0) {
6809067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber        return ERROR_MALFORMED;
6819067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    }
6829067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
6839067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    double x;
6849067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    status_t err = ParseDouble(line.c_str() + colonPos + 1, &x);
6859067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
6869067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    if (err != OK) {
6879067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber        return err;
6889067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    }
6899067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
6909067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    if (meta->get() == NULL) {
6919067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber        *meta = new AMessage;
6929067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    }
693abd5feeea499ee7337125223df4ea22cae19cd20Oscar Rydhé    (*meta)->setInt64(key, (int64_t)(x * 1E6));
6949067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
6959067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    return OK;
6969067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber}
6979067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
69814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber// Find the next occurence of the character "what" at or after "offset",
69914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber// but ignore occurences between quotation marks.
70014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber// Return the index of the occurrence or -1 if not found.
70114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberstatic ssize_t FindNextUnquoted(
70214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        const AString &line, char what, size_t offset) {
70314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    CHECK_NE((int)what, (int)'"');
70414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
70514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool quoted = false;
70614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    while (offset < line.size()) {
70714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        char c = line.c_str()[offset];
70814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
70914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (c == '"') {
71014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            quoted = !quoted;
71114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (c == what && !quoted) {
71214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            return offset;
71314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
71414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
71514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        ++offset;
71614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
71714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
71814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return -1;
71914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
72014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
7212a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huberstatus_t M3UParser::parseStreamInf(
72214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        const AString &line, sp<AMessage> *meta) const {
7232a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber    ssize_t colonPos = line.find(":");
7242a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7252a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber    if (colonPos < 0) {
7262a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        return ERROR_MALFORMED;
7272a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber    }
7282a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7292a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber    size_t offset = colonPos + 1;
7302a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7312a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber    while (offset < line.size()) {
73214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        ssize_t end = FindNextUnquoted(line, ',', offset);
7332a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        if (end < 0) {
7342a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            end = line.size();
7352a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        }
7362a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7372a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        AString attr(line, offset, end - offset);
7382a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        attr.trim();
7392a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7402a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        offset = end + 1;
7412a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7422a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        ssize_t equalPos = attr.find("=");
7432a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        if (equalPos < 0) {
7442a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            continue;
7452a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        }
7462a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7472a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        AString key(attr, 0, equalPos);
7482a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        key.trim();
7492a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7502a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        AString val(attr, equalPos + 1, attr.size() - equalPos - 1);
7512a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        val.trim();
7522a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7533856b090cd04ba5dd4a59a12430ed724d5995909Steve Block        ALOGV("key=%s value=%s", key.c_str(), val.c_str());
7542a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7552a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber        if (!strcasecmp("bandwidth", key.c_str())) {
7562a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            const char *s = val.c_str();
7572a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            char *end;
7582a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            unsigned long x = strtoul(s, &end, 10);
7592a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7602a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            if (end == s || *end != '\0') {
7612a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                // malformed
7622a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                continue;
7632a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            }
7642a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
7652a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            if (meta->get() == NULL) {
7662a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber                *meta = new AMessage;
7672a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            }
7682a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber            (*meta)->setInt32("bandwidth", x);
769ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        } else if (!strcasecmp("codecs", key.c_str())) {
770ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            if (!isQuotedString(val)) {
771ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                ALOGE("Expected quoted string for %s attribute, "
772ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                      "got '%s' instead.",
773ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                      key.c_str(), val.c_str());;
774ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
775ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih                return ERROR_MALFORMED;
776ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            }
777ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
778ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            key.tolower();
779ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            const AString &codecs = unquoteString(val);
7805154f98277922aba7103ac19529ecc00b1889c1eRobert Shih            if (meta->get() == NULL) {
7815154f98277922aba7103ac19529ecc00b1889c1eRobert Shih                *meta = new AMessage;
7825154f98277922aba7103ac19529ecc00b1889c1eRobert Shih            }
783ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            (*meta)->setString(key.c_str(), codecs.c_str());
78414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("audio", key.c_str())
78514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                || !strcasecmp("video", key.c_str())
78614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                || !strcasecmp("subtitles", key.c_str())) {
787ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            if (!isQuotedString(val)) {
78814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected quoted string for %s attribute, "
78914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      "got '%s' instead.",
79014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      key.c_str(), val.c_str());
79114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
79214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
79314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
7942a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
795ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            const AString &groupID = unquoteString(val);
79614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            ssize_t groupIndex = mMediaGroups.indexOfKey(groupID);
7972a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber
79814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (groupIndex < 0) {
79914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Undefined media group '%s' referenced in stream info.",
80014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      groupID.c_str());
8010a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber
80214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
80314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
8040a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber
80514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            key.tolower();
8065154f98277922aba7103ac19529ecc00b1889c1eRobert Shih            if (meta->get() == NULL) {
8075154f98277922aba7103ac19529ecc00b1889c1eRobert Shih                *meta = new AMessage;
8085154f98277922aba7103ac19529ecc00b1889c1eRobert Shih            }
80914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            (*meta)->setString(key.c_str(), groupID.c_str());
8100a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber        }
8110a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber    }
8120a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber
81314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return OK;
8140a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber}
8150a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber
8162a4d22d79e927f2245537921e10fc5fda1c47a29Andreas Huber// static
817e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huberstatus_t M3UParser::parseCipherInfo(
818decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber        const AString &line, sp<AMessage> *meta, const AString &baseURI) {
819e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    ssize_t colonPos = line.find(":");
820e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
821e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    if (colonPos < 0) {
822e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        return ERROR_MALFORMED;
823e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    }
824e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
825e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    size_t offset = colonPos + 1;
826e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
827e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    while (offset < line.size()) {
8280a73d81f213fb2d6f2fdd59c4dda047bf453bb1cAndreas Huber        ssize_t end = FindNextUnquoted(line, ',', offset);
829e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        if (end < 0) {
830e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            end = line.size();
831e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        }
832e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
833e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        AString attr(line, offset, end - offset);
834e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        attr.trim();
835e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
836e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        offset = end + 1;
837e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
838e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        ssize_t equalPos = attr.find("=");
839e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        if (equalPos < 0) {
840e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            continue;
841e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        }
842e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
843e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        AString key(attr, 0, equalPos);
844e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        key.trim();
845e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
846e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        AString val(attr, equalPos + 1, attr.size() - equalPos - 1);
847e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        val.trim();
848e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
8493856b090cd04ba5dd4a59a12430ed724d5995909Steve Block        ALOGV("key=%s value=%s", key.c_str(), val.c_str());
850e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
851e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        key.tolower();
852e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
853e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        if (key == "method" || key == "uri" || key == "iv") {
854e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            if (meta->get() == NULL) {
855e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber                *meta = new AMessage;
856e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            }
857e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
858decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber            if (key == "uri") {
859decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                if (val.size() >= 2
860decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                        && val.c_str()[0] == '"'
861decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                        && val.c_str()[val.size() - 1] == '"') {
862decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                    // Remove surrounding quotes.
863decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                    AString tmp(val, 1, val.size() - 2);
864decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                    val = tmp;
865decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                }
866decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber
867decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                AString absURI;
868decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                if (MakeURL(baseURI.c_str(), val.c_str(), &absURI)) {
869decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                    val = absURI;
870decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                } else {
871a1df816c0677185534babba6ffc29970b048e52eLajos Molnar                    ALOGE("failed to make absolute url for %s.",
872a1df816c0677185534babba6ffc29970b048e52eLajos Molnar                            uriDebugString(baseURI).c_str());
873decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber                }
874decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber            }
875decd96988e495133e4a1728f612d4c9fdb4d218eAndreas Huber
876e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            key.insert(AString("cipher-"), 0);
877e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
878e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber            (*meta)->setString(key.c_str(), val.c_str(), val.size());
879e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber        }
880e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    }
881e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
882e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber    return OK;
883e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber}
884e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber
885e332a9181cf6a3155ed1a0fd2afc212ccb1f2753Andreas Huber// static
8862aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huberstatus_t M3UParser::parseByteRange(
8872aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        const AString &line, uint64_t curOffset,
8882aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        uint64_t *length, uint64_t *offset) {
8892aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    ssize_t colonPos = line.find(":");
8902aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
8912aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    if (colonPos < 0) {
8922aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        return ERROR_MALFORMED;
8932aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    }
8942aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
8952aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    ssize_t atPos = line.find("@", colonPos + 1);
8962aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
8972aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    AString lenStr;
8982aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    if (atPos < 0) {
8992aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        lenStr = AString(line, colonPos + 1, line.size() - colonPos - 1);
9002aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    } else {
9012aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        lenStr = AString(line, colonPos + 1, atPos - colonPos - 1);
9022aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    }
9032aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9042aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    lenStr.trim();
9052aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9062aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    const char *s = lenStr.c_str();
9072aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    char *end;
9082aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    *length = strtoull(s, &end, 10);
9092aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9102aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    if (s == end || *end != '\0') {
9112aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        return ERROR_MALFORMED;
9122aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    }
9132aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9142aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    if (atPos >= 0) {
9152aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        AString offStr = AString(line, atPos + 1, line.size() - atPos - 1);
9162aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        offStr.trim();
9172aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9182aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        const char *s = offStr.c_str();
9192aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        *offset = strtoull(s, &end, 10);
9202aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9212aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        if (s == end || *end != '\0') {
9222aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber            return ERROR_MALFORMED;
9232aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        }
9242aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    } else {
9252aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber        *offset = curOffset;
9262aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    }
9272aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
9282aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber    return OK;
9292aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber}
9302aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber
93114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huberstatus_t M3UParser::parseMedia(const AString &line) {
93214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    ssize_t colonPos = line.find(":");
93314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
93414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (colonPos < 0) {
93514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        return ERROR_MALFORMED;
93614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
93714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
93814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupType = false;
93914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    MediaGroup::Type groupType = MediaGroup::TYPE_AUDIO;
94014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
94114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupID = false;
94214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    AString groupID;
94314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
94414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupLanguage = false;
94514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    AString groupLanguage;
94614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
94714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupName = false;
94814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    AString groupName;
94914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
95014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupAutoselect = false;
95114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool groupAutoselect = false;
95214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
95314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupDefault = false;
95414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool groupDefault = false;
95514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
95614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupForced = false;
95714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool groupForced = false;
95814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
95914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    bool haveGroupURI = false;
96014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    AString groupURI;
96114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
96214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    size_t offset = colonPos + 1;
96314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
96414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    while (offset < line.size()) {
96514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        ssize_t end = FindNextUnquoted(line, ',', offset);
96614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (end < 0) {
96714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            end = line.size();
96814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
96914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
97014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        AString attr(line, offset, end - offset);
97114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        attr.trim();
97214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
97314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        offset = end + 1;
97414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
97514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        ssize_t equalPos = attr.find("=");
97614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (equalPos < 0) {
97714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            continue;
97814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
97914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
98014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        AString key(attr, 0, equalPos);
98114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        key.trim();
98214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
98314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        AString val(attr, equalPos + 1, attr.size() - equalPos - 1);
98414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        val.trim();
98514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
98614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        ALOGV("key=%s value=%s", key.c_str(), val.c_str());
98714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
98814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (!strcasecmp("type", key.c_str())) {
98914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (!strcasecmp("subtitles", val.c_str())) {
99014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupType = MediaGroup::TYPE_SUBS;
99114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else if (!strcasecmp("audio", val.c_str())) {
99214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupType = MediaGroup::TYPE_AUDIO;
99314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else if (!strcasecmp("video", val.c_str())) {
99414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupType = MediaGroup::TYPE_VIDEO;
99599cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang            } else if (!strcasecmp("closed-captions", val.c_str())){
99699cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang                groupType = MediaGroup::TYPE_CC;
99714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else {
99814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Invalid media group type '%s'", val.c_str());
99914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
100014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
100114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
100214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupType = true;
100314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("group-id", key.c_str())) {
100414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (val.size() < 2
100514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[0] != '"'
100614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[val.size() - 1] != '"') {
100714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected quoted string for GROUP-ID, got '%s' instead.",
100814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
100914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
101014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
101114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
101214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
101314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupID.setTo(val, 1, val.size() - 2);
101414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupID = true;
101514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("language", key.c_str())) {
101614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (val.size() < 2
101714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[0] != '"'
101814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[val.size() - 1] != '"') {
101914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected quoted string for LANGUAGE, got '%s' instead.",
102014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
102114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
102214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
102314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
102414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
102514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupLanguage.setTo(val, 1, val.size() - 2);
102614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupLanguage = true;
102714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("name", key.c_str())) {
102814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (val.size() < 2
102914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[0] != '"'
103014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[val.size() - 1] != '"') {
103114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected quoted string for NAME, got '%s' instead.",
103214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
103314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
103414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
103514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
103614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
103714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupName.setTo(val, 1, val.size() - 2);
103814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupName = true;
103914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("autoselect", key.c_str())) {
104014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupAutoselect = false;
104114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (!strcasecmp("YES", val.c_str())) {
104214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupAutoselect = true;
104314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else if (!strcasecmp("NO", val.c_str())) {
104414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupAutoselect = false;
104514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else {
104614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected YES or NO for AUTOSELECT attribute, "
104714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      "got '%s' instead.",
104814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
104914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
105014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
105114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
105214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
105314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupAutoselect = true;
105414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("default", key.c_str())) {
105514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupDefault = false;
105614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (!strcasecmp("YES", val.c_str())) {
105714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupDefault = true;
105814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else if (!strcasecmp("NO", val.c_str())) {
105914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupDefault = false;
106014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else {
106114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected YES or NO for DEFAULT attribute, "
106214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      "got '%s' instead.",
106314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
106414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
106514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
106614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
106714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
106814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupDefault = true;
106914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("forced", key.c_str())) {
107014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupForced = false;
107114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (!strcasecmp("YES", val.c_str())) {
107214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupForced = true;
107314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else if (!strcasecmp("NO", val.c_str())) {
107414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                groupForced = false;
107514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            } else {
107614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected YES or NO for FORCED attribute, "
107714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      "got '%s' instead.",
107814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
107914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
108014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
108114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
108214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
108314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupForced = true;
108414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        } else if (!strcasecmp("uri", key.c_str())) {
108514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (val.size() < 2
108614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[0] != '"'
108714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                    || val.c_str()[val.size() - 1] != '"') {
108814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGE("Expected quoted string for URI, got '%s' instead.",
108914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                      val.c_str());
109014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
109114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                return ERROR_MALFORMED;
109214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
109314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
109414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            AString tmp(val, 1, val.size() - 2);
109514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
109614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            if (!MakeURL(mBaseURI.c_str(), tmp.c_str(), &groupURI)) {
109714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                ALOGI("Failed to make absolute URI from '%s'.", tmp.c_str());
109814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            }
109914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
110014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupURI = true;
110114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
110214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
110314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
110414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (!haveGroupType || !haveGroupID || !haveGroupName) {
110514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        ALOGE("Incomplete EXT-X-MEDIA element.");
110614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        return ERROR_MALFORMED;
110714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
110814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
110999cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang    if (groupType == MediaGroup::TYPE_CC) {
111099cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang        // TODO: ignore this for now.
111199cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang        // CC track will be detected by CCDecoder. But we still need to
111299cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang        // pass the CC track flags (lang, auto) to the app in the future.
111399cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang        return OK;
111499cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang    }
111599cef1ef1cf1232966fabf3793ce7964c01474d7Chong Zhang
111614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    uint32_t flags = 0;
111714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (haveGroupAutoselect && groupAutoselect) {
111814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        flags |= MediaGroup::FLAG_AUTOSELECT;
111914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
112014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (haveGroupDefault && groupDefault) {
112114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        flags |= MediaGroup::FLAG_DEFAULT;
112214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
112314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (haveGroupForced) {
112414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (groupType != MediaGroup::TYPE_SUBS) {
112514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            ALOGE("The FORCED attribute MUST not be present on anything "
112614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                  "but SUBS media.");
112714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
112814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            return ERROR_MALFORMED;
112914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
113014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
113114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (groupForced) {
113214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            flags |= MediaGroup::FLAG_FORCED;
113314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
113414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
113514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (haveGroupLanguage) {
113614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        flags |= MediaGroup::FLAG_HAS_LANGUAGE;
113714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
113814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (haveGroupURI) {
113914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        flags |= MediaGroup::FLAG_HAS_URI;
114014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
114114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
114214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    ssize_t groupIndex = mMediaGroups.indexOfKey(groupID);
114314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    sp<MediaGroup> group;
114414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
114514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    if (groupIndex < 0) {
114614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        group = new MediaGroup(groupType);
114714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        mMediaGroups.add(groupID, group);
114814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    } else {
114914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        group = mMediaGroups.valueAt(groupIndex);
115014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
115114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        if (group->type() != groupType) {
115214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            ALOGE("Attempt to put media item under group of different type "
115314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                  "(groupType = %d, item type = %d",
115414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                  group->type(),
115514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber                  groupType);
115614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
115714f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            return ERROR_MALFORMED;
115814f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber        }
115914f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    }
116014f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
116114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber    return group->addMedia(
116214f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            groupName.c_str(),
116314f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupURI ? groupURI.c_str() : NULL,
116414f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            haveGroupLanguage ? groupLanguage.c_str() : NULL,
116514f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber            flags);
116614f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber}
1167309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih
1168309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih// static
1169309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shihstatus_t M3UParser::parseDiscontinuitySequence(const AString &line, size_t *seq) {
1170309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    ssize_t colonPos = line.find(":");
1171309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih
1172309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    if (colonPos < 0) {
1173309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih        return ERROR_MALFORMED;
1174309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    }
1175309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih
1176309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    int32_t x;
1177309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    status_t err = ParseInt32(line.c_str() + colonPos + 1, &x);
1178309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    if (err != OK) {
1179309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih        return err;
1180309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    }
1181309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih
1182309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    if (x < 0) {
1183309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih        return ERROR_MALFORMED;
1184309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    }
1185309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih
1186309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    if (seq) {
1187309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih        *seq = x;
1188309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    }
1189309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih    return OK;
1190309aa8bf5e4cd66fe988adf2654cac3fadc2a1c3Robert Shih}
119114f7672b5d450ed26a06fd3bb3ce045ea78b11b2Andreas Huber
11922aa4cc04154d82f6658fb86f394f13bb488b0468Andreas Huber// static
1193e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huberstatus_t M3UParser::ParseInt32(const char *s, int32_t *x) {
1194e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    char *end;
1195e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    long lval = strtol(s, &end, 10);
1196e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
1197e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    if (end == s || (*end != '\0' && *end != ',')) {
1198e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber        return ERROR_MALFORMED;
1199e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    }
1200e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
1201e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    *x = (int32_t)lval;
1202e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
1203e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber    return OK;
1204e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}
1205e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber
12069067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber// static
12079067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huberstatus_t M3UParser::ParseDouble(const char *s, double *x) {
12089067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    char *end;
12099067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    double dval = strtod(s, &end);
12109067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
12119067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    if (end == s || (*end != '\0' && *end != ',')) {
12129067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber        return ERROR_MALFORMED;
12139067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    }
12149067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
12159067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    *x = dval;
12169067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
12179067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber    return OK;
12189067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber}
12199067e30b3ccb3a07e41b61af22c036378053a9a3Andreas Huber
1220ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih// static
1221ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shihbool M3UParser::isQuotedString(const AString &str) {
1222ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    if (str.size() < 2
1223ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            || str.c_str()[0] != '"'
1224ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            || str.c_str()[str.size() - 1] != '"') {
1225ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        return false;
1226ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    }
1227ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    return true;
1228ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih}
1229ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
1230ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih// static
1231ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert ShihAString M3UParser::unquoteString(const AString &str) {
1232ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih     if (!isQuotedString(str)) {
1233ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih         return str;
1234ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih     }
1235ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih     return AString(str, 1, str.size() - 2);
1236ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih}
1237ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
1238ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih// static
1239ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shihbool M3UParser::codecIsType(const AString &codec, const char *type) {
1240ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    if (codec.size() < 4) {
1241ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        return false;
1242ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    }
1243ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    const char *c = codec.c_str();
1244ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    switch (FOURCC(c[0], c[1], c[2], c[3])) {
1245ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        // List extracted from http://www.mp4ra.org/codecs.html
1246ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'ac-3':
1247ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'alac':
1248ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dra1':
1249ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dtsc':
1250ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dtse':
1251ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dtsh':
1252ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dtsl':
1253ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'ec-3':
1254ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'enca':
1255ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'g719':
1256ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'g726':
1257ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'm4ae':
1258ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mlpa':
1259ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mp4a':
1260ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'raw ':
1261ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'samr':
1262ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'sawb':
1263ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'sawp':
1264ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'sevc':
1265ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'sqcp':
1266ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'ssmv':
1267ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'twos':
1268ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'agsm':
1269ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'alaw':
1270ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dvi ':
1271ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'fl32':
1272ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'fl64':
1273ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'ima4':
1274ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'in24':
1275ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'in32':
1276ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'lpcm':
1277ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'Qclp':
1278ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'QDM2':
1279ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'QDMC':
1280ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'ulaw':
1281ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'vdva':
1282ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            return !strcmp("audio", type);
1283ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
1284ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'avc1':
1285ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'avc2':
1286ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'avcp':
1287ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'drac':
1288ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'encv':
1289ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mjp2':
1290ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mp4v':
1291ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mvc1':
1292ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mvc2':
1293ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'resv':
1294ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 's263':
1295ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'svc1':
1296ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'vc-1':
1297ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'CFHD':
1298ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'civd':
1299ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'DV10':
1300ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dvh5':
1301ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dvh6':
1302ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'dvhp':
1303ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'DVOO':
1304ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'DVOR':
1305ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'DVTV':
1306ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'DVVT':
1307ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'flic':
1308ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'gif ':
1309ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'h261':
1310ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'h263':
1311ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'HD10':
1312ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'jpeg':
1313ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'M105':
1314ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mjpa':
1315ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'mjpb':
1316ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'png ':
1317ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'PNTG':
1318ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'rle ':
1319ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'rpza':
1320ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'Shr0':
1321ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'Shr1':
1322ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'Shr2':
1323ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'Shr3':
1324ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'Shr4':
1325ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'SVQ1':
1326ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'SVQ3':
1327ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'tga ':
1328ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'tiff':
1329ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        case 'WRLE':
1330ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            return !strcmp("video", type);
1331ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
1332ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih        default:
1333ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih            return false;
1334ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih    }
1335ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih}
1336ef8adf8ce4ece039a839f42a22b436d8ae077f37Robert Shih
1337e71d10e7ad55ccbcb0756c007caef1c959090384Andreas Huber}  // namespace android
1338