1 /* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mms.model; 19 20 import com.android.mms.ContentRestrictionException; 21 import com.android.mms.LogTag; 22 import com.android.mms.dom.events.EventImpl; 23 import com.android.mms.dom.smil.SmilMediaElementImpl; 24 import com.android.mms.drm.DrmWrapper; 25 import com.google.android.mms.MmsException; 26 import android.database.sqlite.SqliteWrapper; 27 28 import org.w3c.dom.events.Event; 29 import org.w3c.dom.smil.ElementTime; 30 31 import android.content.ContentResolver; 32 import android.content.Context; 33 import android.database.Cursor; 34 import android.net.Uri; 35 import android.provider.MediaStore.Images; 36 import android.text.TextUtils; 37 import android.util.Log; 38 import android.webkit.MimeTypeMap; 39 40 import com.google.android.mms.ContentType; 41 import java.io.IOException; 42 43 public class VideoModel extends RegionMediaModel { 44 private static final String TAG = MediaModel.TAG; 45 private static final boolean DEBUG = true; 46 private static final boolean LOCAL_LOGV = false; 47 48 public VideoModel(Context context, Uri uri, RegionModel region) 49 throws MmsException { 50 this(context, null, null, uri, region); 51 initModelFromUri(uri); 52 checkContentRestriction(); 53 } 54 55 public VideoModel(Context context, String contentType, String src, 56 Uri uri, RegionModel region) throws MmsException { 57 super(context, SmilHelper.ELEMENT_TAG_VIDEO, contentType, src, uri, region); 58 } 59 60 public VideoModel(Context context, String contentType, String src, 61 DrmWrapper wrapper, RegionModel regionModel) throws IOException { 62 super(context, SmilHelper.ELEMENT_TAG_VIDEO, contentType, src, wrapper, regionModel); 63 } 64 65 private void initModelFromUri(Uri uri) throws MmsException { 66 String scheme = uri.getScheme(); 67 if (scheme.equals("content")) { 68 initFromContentUri(uri); 69 } else if (uri.getScheme().equals("file")) { 70 initFromFile(uri); 71 } 72 initMediaDuration(); 73 } 74 75 private void initFromFile(Uri uri) { 76 mSrc = uri.getPath(); 77 MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); 78 String extension = MimeTypeMap.getFileExtensionFromUrl(mSrc); 79 if (TextUtils.isEmpty(extension)) { 80 // getMimeTypeFromExtension() doesn't handle spaces in filenames nor can it handle 81 // urlEncoded strings. Let's try one last time at finding the extension. 82 int dotPos = mSrc.lastIndexOf('.'); 83 if (0 <= dotPos) { 84 extension = mSrc.substring(dotPos + 1); 85 } 86 } 87 mContentType = mimeTypeMap.getMimeTypeFromExtension(extension); 88 // It's ok if mContentType is null. Eventually we'll show a toast telling the 89 // user the video couldn't be attached. 90 91 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 92 Log.v(TAG, "New VideoModel initFromFile created:" 93 + " mSrc=" + mSrc 94 + " mContentType=" + mContentType 95 + " mUri=" + uri); 96 } 97 } 98 99 private void initFromContentUri(Uri uri) throws MmsException { 100 ContentResolver cr = mContext.getContentResolver(); 101 Cursor c = SqliteWrapper.query(mContext, cr, uri, null, null, null, null); 102 103 if (c != null) { 104 try { 105 if (c.moveToFirst()) { 106 String path; 107 try { 108 // Local videos will have a data column 109 path = c.getString(c.getColumnIndexOrThrow(Images.Media.DATA)); 110 } catch (IllegalArgumentException e) { 111 // For non-local videos, the path is the uri 112 path = uri.toString(); 113 } 114 mSrc = path.substring(path.lastIndexOf('/') + 1); 115 mContentType = c.getString(c.getColumnIndexOrThrow( 116 Images.Media.MIME_TYPE)); 117 if (TextUtils.isEmpty(mContentType)) { 118 throw new MmsException("Type of media is unknown."); 119 } 120 121 if (mContentType.equals(ContentType.VIDEO_MP4) && !(TextUtils.isEmpty(mSrc))) { 122 int index = mSrc.lastIndexOf("."); 123 if (index != -1) { 124 try { 125 String extension = mSrc.substring(index + 1); 126 if (!(TextUtils.isEmpty(extension)) && 127 (extension.equalsIgnoreCase("3gp") || 128 extension.equalsIgnoreCase("3gpp") || 129 extension.equalsIgnoreCase("3g2"))) { 130 mContentType = ContentType.VIDEO_3GPP; 131 } 132 } catch(IndexOutOfBoundsException ex) { 133 if (LOCAL_LOGV) { 134 Log.v(TAG, "Media extension is unknown."); 135 } 136 } 137 } 138 } 139 140 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 141 Log.v(TAG, "New VideoModel initFromContentUri created:" 142 + " mSrc=" + mSrc 143 + " mContentType=" + mContentType 144 + " mUri=" + uri); 145 } 146 } else { 147 throw new MmsException("Nothing found: " + uri); 148 } 149 } finally { 150 c.close(); 151 } 152 } else { 153 throw new MmsException("Bad URI: " + uri); 154 } 155 } 156 157 // EventListener Interface 158 public void handleEvent(Event evt) { 159 String evtType = evt.getType(); 160 if (LOCAL_LOGV || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 161 Log.v(TAG, "[VideoModel] handleEvent " + evt.getType() + " on " + this); 162 } 163 164 MediaAction action = MediaAction.NO_ACTIVE_ACTION; 165 if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_START_EVENT)) { 166 action = MediaAction.START; 167 168 // if the Music player app is playing audio, we should pause that so it won't 169 // interfere with us playing video here. 170 pauseMusicPlayer(); 171 172 mVisible = true; 173 } else if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_END_EVENT)) { 174 action = MediaAction.STOP; 175 if (mFill != ElementTime.FILL_FREEZE) { 176 mVisible = false; 177 } 178 } else if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_PAUSE_EVENT)) { 179 action = MediaAction.PAUSE; 180 mVisible = true; 181 } else if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_SEEK_EVENT)) { 182 action = MediaAction.SEEK; 183 mSeekTo = ((EventImpl) evt).getSeekTo(); 184 mVisible = true; 185 } 186 187 appendAction(action); 188 notifyModelChanged(false); 189 } 190 191 protected void checkContentRestriction() throws ContentRestrictionException { 192 ContentRestriction cr = ContentRestrictionFactory.getContentRestriction(); 193 cr.checkVideoContentType(mContentType); 194 } 195 196 @Override 197 protected boolean isPlayable() { 198 return true; 199 } 200 } 201