| 1 | package eu.etaxonomy.cdm.model.media; |
|---|
| 2 | |
|---|
| 3 | import java.util.ArrayList; |
|---|
| 4 | import java.util.Collection; |
|---|
| 5 | import java.util.Collections; |
|---|
| 6 | import java.util.HashSet; |
|---|
| 7 | import java.util.Iterator; |
|---|
| 8 | import java.util.List; |
|---|
| 9 | import java.util.NoSuchElementException; |
|---|
| 10 | import java.util.Set; |
|---|
| 11 | import java.util.SortedMap; |
|---|
| 12 | import java.util.TreeMap; |
|---|
| 13 | import java.util.regex.Matcher; |
|---|
| 14 | import java.util.regex.Pattern; |
|---|
| 15 | |
|---|
| 16 | import org.apache.log4j.Logger; |
|---|
| 17 | |
|---|
| 18 | public class MediaUtils { |
|---|
| 19 | |
|---|
| 20 | private static final Logger logger = Logger.getLogger(MediaUtils.class); |
|---|
| 21 | |
|---|
| 22 | /** |
|---|
| 23 | * @param representationPartType TODO |
|---|
| 24 | * @param size |
|---|
| 25 | * @param height |
|---|
| 26 | * @param widthOrDuration |
|---|
| 27 | * @param mimeTypeRegexes |
|---|
| 28 | * @return |
|---|
| 29 | * |
|---|
| 30 | * |
|---|
| 31 | */ |
|---|
| 32 | public static MediaRepresentation findBestMatchingRepresentation(Media media, Class<? extends MediaRepresentationPart> representationPartType, Integer size, Integer height, Integer widthOrDuration, String[] mimeTypes){ |
|---|
| 33 | // find best matching representations of each media |
|---|
| 34 | SortedMap<Integer, MediaRepresentation> prefRepresentations |
|---|
| 35 | = filterAndOrderMediaRepresentations(media.getRepresentations(), null, mimeTypes, size, widthOrDuration, height); |
|---|
| 36 | try { |
|---|
| 37 | // take first one and remove all other representations |
|---|
| 38 | MediaRepresentation prefOne = prefRepresentations.get(prefRepresentations.firstKey()); |
|---|
| 39 | |
|---|
| 40 | return prefOne; |
|---|
| 41 | |
|---|
| 42 | } catch (NoSuchElementException nse) { |
|---|
| 43 | /* IGNORE */ |
|---|
| 44 | } |
|---|
| 45 | return null; |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | /** |
|---|
| 49 | * |
|---|
| 50 | * @param mediaList |
|---|
| 51 | * @param representationPartType TODO |
|---|
| 52 | * @param mimeTypes |
|---|
| 53 | * @param sizeTokens |
|---|
| 54 | * @param widthOrDuration |
|---|
| 55 | * @param height |
|---|
| 56 | * @param size |
|---|
| 57 | * @return |
|---|
| 58 | */ |
|---|
| 59 | public static List<Media> findPreferredMedia(List<Media> mediaList, |
|---|
| 60 | Class<? extends MediaRepresentationPart> representationPartType, String[] mimeTypes, String[] sizeTokens, |
|---|
| 61 | Integer widthOrDuration, Integer height, Integer size) { |
|---|
| 62 | |
|---|
| 63 | if(mimeTypes != null) { |
|---|
| 64 | for(int i=0; i<mimeTypes.length; i++){ |
|---|
| 65 | mimeTypes[i] = mimeTypes[i].replace(':', '/'); |
|---|
| 66 | } |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | if(sizeTokens != null) { |
|---|
| 70 | if(sizeTokens.length > 0){ |
|---|
| 71 | try { |
|---|
| 72 | size = Integer.valueOf(sizeTokens[0]); |
|---|
| 73 | } catch (NumberFormatException nfe) { |
|---|
| 74 | /* IGNORE */ |
|---|
| 75 | } |
|---|
| 76 | } |
|---|
| 77 | if(sizeTokens.length > 1){ |
|---|
| 78 | try { |
|---|
| 79 | widthOrDuration = Integer.valueOf(sizeTokens[1]); |
|---|
| 80 | } catch (NumberFormatException nfe) { |
|---|
| 81 | /* IGNORE */ |
|---|
| 82 | } |
|---|
| 83 | } |
|---|
| 84 | if(sizeTokens.length > 2){ |
|---|
| 85 | try { |
|---|
| 86 | height = Integer.valueOf(sizeTokens[2]); |
|---|
| 87 | } catch (NumberFormatException nfe) { |
|---|
| 88 | /* IGNORE */ |
|---|
| 89 | } |
|---|
| 90 | } |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | List<Media> returnMediaList = new ArrayList<Media>(mediaList.size()); |
|---|
| 94 | if(mediaList != null){ |
|---|
| 95 | for(Media media : mediaList){ |
|---|
| 96 | |
|---|
| 97 | Set<MediaRepresentation> candidateRepresentations = new HashSet<MediaRepresentation>(); |
|---|
| 98 | candidateRepresentations.addAll(media.getRepresentations()); |
|---|
| 99 | |
|---|
| 100 | SortedMap<Integer, MediaRepresentation> prefRepresentations |
|---|
| 101 | = filterAndOrderMediaRepresentations(candidateRepresentations, representationPartType, mimeTypes, size, widthOrDuration, height); |
|---|
| 102 | try { |
|---|
| 103 | if(prefRepresentations.size() > 0){ |
|---|
| 104 | // Media.representations is a set |
|---|
| 105 | // so it cannot retain the sorting which has been found by filterAndOrderMediaRepresentations() |
|---|
| 106 | // thus we take first one and remove all other representations |
|---|
| 107 | |
|---|
| 108 | media.getRepresentations().clear(); |
|---|
| 109 | media.addRepresentation(prefRepresentations.get(prefRepresentations.firstKey())); |
|---|
| 110 | returnMediaList.add(media); |
|---|
| 111 | } |
|---|
| 112 | } catch (NoSuchElementException nse) { |
|---|
| 113 | logger.debug(nse); |
|---|
| 114 | /* IGNORE */ |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | return returnMediaList; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | /** |
|---|
| 123 | * @param media |
|---|
| 124 | * @param mimeTypeRegexes |
|---|
| 125 | * @param size |
|---|
| 126 | * @param widthOrDuration |
|---|
| 127 | * @param height |
|---|
| 128 | * @return |
|---|
| 129 | * |
|---|
| 130 | * TODO move into a media utils class |
|---|
| 131 | * TODO implement the quality filter |
|---|
| 132 | |
|---|
| 133 | public static SortedMap<String, MediaRepresentation> orderMediaRepresentations(Media media, String[] mimeTypeRegexes, |
|---|
| 134 | Integer size, Integer widthOrDuration, Integer height) { |
|---|
| 135 | SortedMap<String, MediaRepresentation> prefRepr = new TreeMap<String, MediaRepresentation>(); |
|---|
| 136 | for (String mimeTypeRegex : mimeTypeRegexes) { |
|---|
| 137 | // getRepresentationByMimeType |
|---|
| 138 | Pattern mimeTypePattern = Pattern.compile(mimeTypeRegex); |
|---|
| 139 | int representationCnt = 0; |
|---|
| 140 | for (MediaRepresentation representation : media.getRepresentations()) { |
|---|
| 141 | int dwa = 0; |
|---|
| 142 | if(representation.getMimeType() == null){ |
|---|
| 143 | prefRepr.put((dwa + representationCnt++) + "_NA", representation); |
|---|
| 144 | } else { |
|---|
| 145 | Matcher mather = mimeTypePattern.matcher(representation.getMimeType()); |
|---|
| 146 | if (mather.matches()) { |
|---|
| 147 | |
|---|
| 148 | /* TODO the quality filter part is being skipped |
|---|
| 149 | * // look for representation with the best matching parts |
|---|
| 150 | for (MediaRepresentationPart part : representation.getParts()) { |
|---|
| 151 | if (part instanceof ImageFile) { |
|---|
| 152 | ImageFile image = (ImageFile) part; |
|---|
| 153 | int dw = image.getWidth() * image.getHeight() - height * widthOrDuration; |
|---|
| 154 | if (dw < 0) { |
|---|
| 155 | dw *= -1; |
|---|
| 156 | } |
|---|
| 157 | dwa += dw; |
|---|
| 158 | } |
|---|
| 159 | dwa = (representation.getParts().size() > 0 ? dwa / representation.getParts().size() : 0); |
|---|
| 160 | } |
|---|
| 161 | prefRepr.put((dwa + representationCnt++) + '_' + representation.getMimeType(), representation); |
|---|
| 162 | |
|---|
| 163 | // preferred mime type found => end loop |
|---|
| 164 | break; |
|---|
| 165 | } |
|---|
| 166 | } |
|---|
| 167 | } |
|---|
| 168 | } |
|---|
| 169 | return prefRepr; |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | */ |
|---|
| 173 | /** |
|---|
| 174 | * @param mimeTypeRegexes |
|---|
| 175 | * @param size |
|---|
| 176 | * @param widthOrDuration |
|---|
| 177 | * @param height |
|---|
| 178 | * @return |
|---|
| 179 | * |
|---|
| 180 | * |
|---|
| 181 | */ |
|---|
| 182 | private static SortedMap<Integer, MediaRepresentation> filterAndOrderMediaRepresentations(Set<MediaRepresentation> mediaRepresentations, |
|---|
| 183 | Class<? extends MediaRepresentationPart> representationPartType, String[] mimeTypeRegexes, |
|---|
| 184 | Integer size, Integer widthOrDuration, Integer height) { |
|---|
| 185 | |
|---|
| 186 | SortedMap<Integer, MediaRepresentation> prefRepr = new TreeMap<Integer, MediaRepresentation>(); |
|---|
| 187 | |
|---|
| 188 | |
|---|
| 189 | size = (size == null ? new Integer(0) : size ); |
|---|
| 190 | widthOrDuration = (widthOrDuration == null ? new Integer(0) : widthOrDuration); |
|---|
| 191 | height = (height == null ? new Integer(0) : height); |
|---|
| 192 | mimeTypeRegexes = (mimeTypeRegexes == null ? new String[]{".*"} : mimeTypeRegexes); |
|---|
| 193 | |
|---|
| 194 | for (String mimeTypeRegex : mimeTypeRegexes) { |
|---|
| 195 | // getRepresentationByMimeType |
|---|
| 196 | Pattern mimeTypePattern = Pattern.compile(mimeTypeRegex); |
|---|
| 197 | int representationCnt = 0; |
|---|
| 198 | for (MediaRepresentation representation : mediaRepresentations) { |
|---|
| 199 | |
|---|
| 200 | List<MediaRepresentationPart> matchingParts = new ArrayList<MediaRepresentationPart>(); |
|---|
| 201 | |
|---|
| 202 | |
|---|
| 203 | // check MIME type |
|---|
| 204 | boolean mimeTypeOK = representation.getMimeType() == null || mimeTypePattern.matcher(representation.getMimeType()).matches(); |
|---|
| 205 | logger.debug("mimeTypeOK: " + Boolean.valueOf(mimeTypeOK).toString()); |
|---|
| 206 | |
|---|
| 207 | int dwa = 0; |
|---|
| 208 | |
|---|
| 209 | |
|---|
| 210 | //first the size is used for comparison |
|---|
| 211 | for (MediaRepresentationPart part : representation.getParts()) { |
|---|
| 212 | |
|---|
| 213 | // check representationPartType |
|---|
| 214 | boolean representationPartTypeOK = representationPartType == null || part.getClass().isAssignableFrom(representationPartType); |
|---|
| 215 | logger.debug("representationPartTypeOK: " + Boolean.valueOf(representationPartTypeOK).toString()); |
|---|
| 216 | |
|---|
| 217 | if ( !(representationPartTypeOK && mimeTypeOK) ) { |
|---|
| 218 | continue; |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | logger.debug(part + " matches"); |
|---|
| 222 | matchingParts.add(part); |
|---|
| 223 | |
|---|
| 224 | if (part.getSize()!= null){ |
|---|
| 225 | int sizeOfPart = part.getSize(); |
|---|
| 226 | int distance = sizeOfPart - size; |
|---|
| 227 | if (distance < 0) { |
|---|
| 228 | distance*= -1; |
|---|
| 229 | } |
|---|
| 230 | dwa += distance; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | //if height and width/duration is defined, add this information, too |
|---|
| 234 | if (height != 0 && widthOrDuration != 0){ |
|---|
| 235 | int durationWidthWeight = 0; |
|---|
| 236 | |
|---|
| 237 | if (part instanceof ImageFile) { |
|---|
| 238 | ImageFile image = (ImageFile) part; |
|---|
| 239 | durationWidthWeight = image.getWidth() * image.getHeight() - height * widthOrDuration; |
|---|
| 240 | } |
|---|
| 241 | else if (part instanceof MovieFile){ |
|---|
| 242 | MovieFile movie = (MovieFile) part; |
|---|
| 243 | durationWidthWeight = movie.getDuration() - widthOrDuration; |
|---|
| 244 | |
|---|
| 245 | }else if (part instanceof AudioFile){ |
|---|
| 246 | AudioFile audio = (AudioFile) part; |
|---|
| 247 | durationWidthWeight = audio.getDuration() - widthOrDuration; |
|---|
| 248 | |
|---|
| 249 | } |
|---|
| 250 | if (durationWidthWeight < 0) { |
|---|
| 251 | durationWidthWeight *= -1; |
|---|
| 252 | } |
|---|
| 253 | dwa += durationWidthWeight; |
|---|
| 254 | |
|---|
| 255 | } |
|---|
| 256 | } // loop parts |
|---|
| 257 | logger.debug("matchingParts.size():" + matchingParts.size()); |
|---|
| 258 | if(matchingParts.size() > 0 ){ |
|---|
| 259 | dwa = dwa / matchingParts.size(); |
|---|
| 260 | |
|---|
| 261 | representation.getParts().clear(); |
|---|
| 262 | representation.getParts().addAll(matchingParts); |
|---|
| 263 | |
|---|
| 264 | //keyString =(dwa + representationCnt++) + '_' + representation.getMimeType(); |
|---|
| 265 | |
|---|
| 266 | prefRepr.put((dwa + representationCnt++), representation); |
|---|
| 267 | } |
|---|
| 268 | } // loop representations |
|---|
| 269 | } // loop mime types |
|---|
| 270 | logger.debug(prefRepr.size() + " preferred representations found"); |
|---|
| 271 | return prefRepr; |
|---|
| 272 | } |
|---|
| 273 | } |
|---|