MediaInfoServiceImpl.java 17.5 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
package com.wondertek.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wondertek.service.MediaInfoService;
import com.wondertek.util.MediaAnalysisHelper;
import com.wondertek.util.MediaInfoUtil;
import com.wondertek.vo.media.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * @description 媒体分析
 * @author W5669
 * @date 2025/8/19 17:57
 * @param null

 */

@Service
@Slf4j
public class MediaInfoServiceImpl implements MediaInfoService {

    @Autowired
    private MediaInfoUtil mediaInfoUtil;

    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Override
    public Media getMediaInfo(String mediaPath, String logId) throws Exception {
        log.info("[Media Analysis] getMediaInfo mediaPath:{},logId:{}", mediaPath, logId);
        long startTime = System.currentTimeMillis();
        // 工具绝对路径
        String mediaInfoPath = mediaInfoUtil.mediaInfoPath();
        log.info("[Media Analysis] mediaInfo tool path:{} ", mediaInfoPath);

        String mediaInfoJson;
        try {
            mediaInfoJson = MediaAnalysisHelper.analysis(mediaInfoPath, mediaPath, logId);
            log.info("[Media Analysis] getMediaInfo mediaInfoJson:{}", mediaInfoJson);
        } catch (Exception e) {
            log.error("[Media Analysis] getMediaInfo Exception:{}", e);
            throw e;
        }

        if (StringUtils.isBlank(mediaInfoJson)) {
            return null;
        }

        Media media;
        // 解析工具返回JOSN数据
        try {
            String mediaType = getMediaType(mediaInfoJson);
            log.info("[Media Analysis] getMediaInfo mediaType:{}", mediaType);

            switch (mediaType) {
            case MediaType.IMAGE_TYPE:
                media = parseImageMedia(mediaInfoJson);
                break;
            case MediaType.AUDIO_TYPE:
                media = parseAudioMedia(mediaInfoJson);
                break;
            case MediaType.VIDEO_TYPE:
                media = parseVideoMedia(mediaInfoJson);
                break;
            default:
                throw new Exception("MediaInfo Cannot analysis media format");
            }
        } catch (Exception e) {
            log.error("[Media Analysis] getMediaInfo parse json error:{}", e);
            throw e;
        }
        log.info("[Media Analysis] getMediaInfo,mediaPath={}logId={},spendTime={}", mediaPath, logId,
                System.currentTimeMillis() - startTime);

        return media;
    }

    /**
     * 判断媒体类型
     * 
     * @param mediaInfoJson
     * @return MediaType类定义字符串,无法解析返回空字符串
     * @throws Exception
     */
    private String getMediaType(String mediaInfoJson) throws Exception {
        JsonNode root = MAPPER.readTree(mediaInfoJson);
        JsonNode trackNode = root.get("media").get("track");
        Iterator<JsonNode> elements = trackNode.elements();

        List<String> typeList = new ArrayList<>();
        while (elements.hasNext()) {
            JsonNode node = elements.next();
            String type = node.get("@type").asText();
            typeList.add(type);
        }

        if (!CollectionUtils.isEmpty(typeList)) {
            if (typeList.contains(MediaType.VIDEO_TYPE)) {
                return MediaType.VIDEO_TYPE;
            }
            if (typeList.contains(MediaType.AUDIO_TYPE)) {
                return MediaType.AUDIO_TYPE;
            }
            if (typeList.contains(MediaType.IMAGE_TYPE)) {
                return MediaType.IMAGE_TYPE;
            }
        }

        return "";
    }

    /**
     * 图片JOSN数据的解析
     * 
     * @param mediaInfoJson
     * @return
     */
    private Media parseImageMedia(String mediaInfoJson) throws Exception {
        log.info("[Media Analysis] parseImageMedia");
        ImageMedia imageMedia = new ImageMedia();

        JsonNode root = MAPPER.readTree(mediaInfoJson);
        JsonNode mediaNode = root.get("media");

        // Media Type
        imageMedia.setType(MediaType.IMAGE_TYPE);

        // Media fileName
        String fileName = mediaNode.get("@ref").asText();
        imageMedia.setFileName(fileName);

        JsonNode trackNode = mediaNode.get("track");
        Iterator<JsonNode> elemIterator = trackNode.elements();
        while (elemIterator.hasNext()) {
            JsonNode node = elemIterator.next();
            String elementType = node.path("@type").asText();
            if (elementType.equals("General")) {
                // Media fileExtension
                String fileExtension = paserUncertainNode(node, "FileExtension");
                imageMedia.setFileExtension(fileExtension);
                // Media format
                String format = paserUncertainNode(node, "Format");
                imageMedia.setFormat(format);
                // Media size
                String fileSize = paserUncertainNode(node, "FileSize");
                imageMedia.setSize(fileSize);
            } else if (elementType.equals(MediaType.IMAGE_TYPE)) {
                // ImageMedia fileExtension
                String width = parseNumberValueNode(node, "Width");
                imageMedia.setWidth(width);
                // ImageMedia Height
                String height = parseNumberValueNode(node, "Height");
                imageMedia.setHeight(height);
                // ImageMedia colorSpace
                String colorSpace = paserUncertainNode(node, "ColorSpace");
                imageMedia.setColorSpace(colorSpace);
                // ImageMedia chromaSubsampling
                String chromaSubsampling = paserUncertainNode(node, "ChromaSubsampling");
                imageMedia.setChromaSubsampling(chromaSubsampling);
                // ImageMedia bitDepth
                String bitDepth = paserUncertainNode(node, "BitDepth");
                imageMedia.setBitDepth(bitDepth);
            }
        }

        log.info("[Media Analysis] parseImageMedia over:{}", imageMedia);
        return imageMedia;
    }

    /**
     * 音频JOSN数据的解析
     * 
     * @param mediaInfoJson
     * @return
     */
    private Media parseAudioMedia(String mediaInfoJson) throws Exception {
        log.info("[Media Analysis] parseAudioMedia");
        AudioMedia audioMedia = new AudioMedia();

        JsonNode root = MAPPER.readTree(mediaInfoJson);
        JsonNode mediaNode = root.get("media");

        // Media Type
        audioMedia.setType(MediaType.AUDIO_TYPE);

        // Media fileName
        String fileName = mediaNode.get("@ref").asText();
        audioMedia.setFileName(fileName);

        JsonNode trackNode = mediaNode.get("track");
        Iterator<JsonNode> elemIterator = trackNode.elements();

        List<Stream> streamList = new ArrayList<>();
        while (elemIterator.hasNext()) {
            JsonNode node = elemIterator.next();
            String elementType = node.path("@type").asText();
            if (elementType.equals("General")) {
                // Media fileExtension
                String fileExtension = paserUncertainNode(node, "FileExtension");
                audioMedia.setFileExtension(fileExtension);
                // Media format
                String format = paserUncertainNode(node, "Format");
                audioMedia.setFormat(format);
                // Media size
                String fileSize = paserUncertainNode(node, "FileSize");
                audioMedia.setSize(fileSize);
                // StreamMedia audioCount
                String audioCount = paserUncertainNode(node, "AudioCount");
                audioMedia.setAudioCount(audioCount);
                // StreamMedia duration
                String duration = parseNumberValueNode(node, "Duration");
                audioMedia.setDuration(duration);
                // StreamMedia overallBitRate
                String overallBitRate = paserUncertainNode(node, "OverallBitRate");
                audioMedia.setOverallBitRate(overallBitRate);
                // StreamMedia overallBitRateMode
                String overallBitRateMode = paserUncertainNode(node, "OverallBitRate_Mode");
                audioMedia.setOverallBitRateMode(overallBitRateMode);
            } else if (elementType.equals(MediaType.AUDIO_TYPE)) {
                Stream stream = parseStream(node, MediaType.AUDIO_TYPE);
                streamList.add(stream);
            }
        }
        audioMedia.setSteams(streamList);

        log.info("[Media Analysis] parseAudioMedia over:{}", audioMedia);
        return audioMedia;
    }

    /**
     * 视频JOSN数据的解析
     * 
     * @param mediaInfoJson
     * @return
     */
    private Media parseVideoMedia(String mediaInfoJson) throws Exception {
        log.info("[Media Analysis] parseVideoMedia");
        VideoMedia videoMedia = new VideoMedia();

        JsonNode root = MAPPER.readTree(mediaInfoJson);
        JsonNode mediaNode = root.get("media");

        videoMedia.setOriginalInfo(JSONUtil.parseObj(mediaInfoJson).toString());
        // Media Type
        videoMedia.setType(MediaType.VIDEO_TYPE);

        // Media fileName
        String fileName = mediaNode.get("@ref").asText();
        videoMedia.setFileName(fileName);

        JsonNode trackNode = mediaNode.get("track");
        Iterator<JsonNode> elemIterator = trackNode.elements();

        List<Stream> streamList = new ArrayList<>();
        while (elemIterator.hasNext()) {
            JsonNode node = elemIterator.next();
            String elementType = node.path("@type").asText();
            if (elementType.equals("General")) {
                // Media fileExtension
                String fileExtension = paserUncertainNode(node, "FileExtension");
                videoMedia.setFileExtension(fileExtension);
                // Media format
                String format = paserUncertainNode(node, "Format");
                videoMedia.setFormat(format);
                // Media size
                String fileSize = paserUncertainNode(node, "FileSize");
                videoMedia.setSize(fileSize);
                // StreamMedia audioCount
                String audioCount = paserUncertainNode(node, "AudioCount");
                videoMedia.setAudioCount(audioCount);
                // StreamMedia duration
                String duration = parseNumberValueNode(node, "Duration");
                videoMedia.setDuration(duration);
                // StreamMedia overallBitRate
                String overallBitRate = paserUncertainNode(node, "OverallBitRate");
                videoMedia.setOverallBitRate(overallBitRate);
                // StreamMedia overallBitRateMode
                String overallBitRateMode = paserUncertainNode(node, "OverallBitRate_Mode");
                videoMedia.setOverallBitRateMode(overallBitRateMode);
                // VideoMedia videoCount
                String videoCount = paserUncertainNode(node, "VideoCount");
                videoMedia.setVideoCount(videoCount);
                // VideoMedia formatProfile
                String formatProfile = paserUncertainNode(node, "Format_Profile");
                videoMedia.setFormatProfile(formatProfile);
            } else if (elementType.equals(MediaType.AUDIO_TYPE)) {
                Stream stream = parseStream(node, MediaType.AUDIO_TYPE);
                streamList.add(stream);
            } else if (elementType.equals(MediaType.VIDEO_TYPE)) {
                Stream stream = parseStream(node, MediaType.VIDEO_TYPE);
                streamList.add(stream);
            }
        }
        videoMedia.setSteams(streamList);

        log.info("[Media Analysis] parseVideoMedia over:{}", videoMedia);
        return videoMedia;
    }

    /**
     * 处理一些不确定的节点
     * 
     * @param parentNode
     *            父节点
     * @param name
     *            子节点名称
     * @return
     */
    private String paserUncertainNode(JsonNode parentNode, String name) {
        JsonNode node = parentNode.get(name);
        if (node != null) {
            return node.asText();
        }
        return "";
    }

    /**
     * 解析数值字段
     * 
     * @param parentNode
     * @param name
     * @return
     */
    private String parseNumberValueNode(JsonNode parentNode, String name) {
        String parseResult = null;
        try {
            parseResult = paserUncertainNode(parentNode, name);
            if (StringUtils.isNotBlank(parseResult)) {
                String valueString = parseResult;
                // 处理MediaInfo异常返回,比如:分辨率返回"1920 / 1920"这种情形
                if (parseResult.contains("/")) {
                    List<String> splitResult = Arrays.asList(StringUtils.split(parseResult, "/"));
                    if (CollectionUtil.isNotEmpty(splitResult)) {
                        valueString = splitResult.get(0).trim();
                    }
                }
                Double valueDouble = Double.parseDouble(valueString); // 目的做个校验
                log.info("[Media Analysis] parseNumberValueNode check number value:{}", valueDouble);
                return valueString;
            }
        } catch (Exception e) {
            log.error("[Media Analysis] parseNumberValueNode parser value:{} error:{}", parseResult, e);
            return "0";
        }

        return "0";
    }

    /**
     * 解析音视频流JSON数据
     * 
     * @param parentNode
     *            节点
     * @param streamType
     *            音频或者视频 MediaType取值
     * @return
     */
    private Stream parseStream(JsonNode parentNode, String streamType) {
        Stream stream = null;
        if (streamType.equals(MediaType.AUDIO_TYPE)) {
            stream = new AudioSteam();
        } else if (streamType.equals(MediaType.VIDEO_TYPE)) {
            stream = new VideoStream();
        }

        if (stream == null) {
            return null;
        }

        // 共同的属性
        String steamType = paserUncertainNode(parentNode, "@type");
        stream.setStreamType(steamType);

        String id = paserUncertainNode(parentNode, "ID");
        stream.setId(id);

        String streamOrder = paserUncertainNode(parentNode, "StreamOrder");
        stream.setStreamOrder(streamOrder);

        String format = paserUncertainNode(parentNode, "Format");
        stream.setFormat(format);

        String formatProfile = paserUncertainNode(parentNode, "Format_Profile");
        stream.setFormatProfile(formatProfile);

        String duration = parseNumberValueNode(parentNode, "Duration");
        stream.setDuration(duration);

        String bitRateMode = paserUncertainNode(parentNode, "BitRate_Mode");
        stream.setBitRateMode(bitRateMode);

        String bitRate = paserUncertainNode(parentNode, "BitRate");
        stream.setBitRate(bitRate);

        String bitRateMinimum = paserUncertainNode(parentNode, "BitRate_Minimum");
        stream.setBitRateMinimum(bitRateMinimum);

        String bitRateMaximum = paserUncertainNode(parentNode, "BitRate_Maximum");
        stream.setBitRateMaximum(bitRateMaximum);

        String frameRate = paserUncertainNode(parentNode, "FrameRate");
        stream.setFrameRate(frameRate);

        String frameCount = paserUncertainNode(parentNode, "FrameCount");
        stream.setFrameCount(frameCount);

        String bitDepth = paserUncertainNode(parentNode, "BitDepth");
        stream.setBitDepth(bitDepth);

        String streamSize = paserUncertainNode(parentNode, "StreamSize");
        stream.setStreamSize(streamSize);

        // 处理音视频独特的属性
        if (streamType.equals(MediaType.AUDIO_TYPE)) {
            AudioSteam audioSteam = (AudioSteam) stream;
            String samplingRate = paserUncertainNode(parentNode, "SamplingRate");
            audioSteam.setSamplingRate(samplingRate);

            String samplingCount = paserUncertainNode(parentNode, "SamplingCount");
            audioSteam.setSamplingCount(samplingCount);

            String channels = paserUncertainNode(parentNode, "Channels");
            audioSteam.setChannels(channels);

        } else if (streamType.equals(MediaType.VIDEO_TYPE)) {
            VideoStream videoStream = (VideoStream) stream;
            String formatLevel = paserUncertainNode(parentNode, "Format_Level");
            videoStream.setFormatLevel(formatLevel);

            String formatSettingsGOP = paserUncertainNode(parentNode, "Format_Settings_GOP");
            videoStream.setFormatSettingsGOP(formatSettingsGOP);

            String frameRateMode = paserUncertainNode(parentNode, "FrameRate_Mode");
            videoStream.setFrameRateMode(frameRateMode);

            String width = parseNumberValueNode(parentNode, "Width");
            videoStream.setWidth(width);

            String height = parseNumberValueNode(parentNode, "Height");
            videoStream.setHeight(height);

            String displayAspectRatio = paserUncertainNode(parentNode, "DisplayAspectRatio");
            videoStream.setDisplayAspectRatio(displayAspectRatio);

            String colorSpace = paserUncertainNode(parentNode, "ColorSpace");
            videoStream.setColorSpace(colorSpace);

            String chromaSubsampling = paserUncertainNode(parentNode, "ChromaSubsampling");
            videoStream.setChromaSubsampling(chromaSubsampling);

            String scanType = paserUncertainNode(parentNode, "ScanType");
            videoStream.setScanType(scanType);

        }

        return stream;

    }
}