/* * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * J4Care. * Portions created by the Initial Developer are Copyright (C) 2015-2017 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * */ package org.dcm4che3.imageio.codec.mpeg; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.Tag; import org.dcm4che3.data.VR; import java.io.IOException; /** * @author Gunter Zeilinger <gunterze@gmail.com> * @since Apr 2017 */ public class MPEGHeader { private static final String[] ASPECT_RATIO_1_1 = { "1", "1" }; private static final String[] ASPECT_RATIO_4_3 = { "4", "3" }; private static final String[] ASPECT_RATIO_16_9 = { "16", "9" }; private static final String[] ASPECT_RATIO_221_100 = { "221", "100" }; private static final String[][] ASPECT_RATIOS = { ASPECT_RATIO_1_1, ASPECT_RATIO_4_3, ASPECT_RATIO_16_9, ASPECT_RATIO_221_100 }; private static int[] FPS = { 24, 1001, 24, 1000, 25, 1000, 30, 1001, 30, 1000, 50, 1000, 60, 1001, 60, 1000 }; private final byte[] data; private final int seqHeaderOffset; public MPEGHeader(byte[] data) throws IOException { this.data = data; int remaining = data.length; int i = 0; do { while (remaining-- > 0 && data[i++] != 0); if (remaining-- > 0 && data[i++] != 0) continue; } while (remaining > 8 && (data[i] != 1 || data[i+1] != (byte)0xb3)); seqHeaderOffset = remaining > 8 ? i+1 : -1; } /** * Return corresponding Image Pixel Description Macro Attributes * @param attrs target {@code Attributes} or {@code null} * @param length MPEG stream length * @return Image Pixel Description Macro Attributes */ public Attributes toAttributes(Attributes attrs, long length) { if (seqHeaderOffset == -1) return null; if (attrs == null) attrs = new Attributes(15); int off = seqHeaderOffset; int x = ((data[off + 1] & 0xFF) << 4) | ((data[off + 2] & 0xF0) >> 4); int y = ((data[off + 2] & 0x0F) << 8) | (data[off + 3] & 0xFF); int aspectRatio = (data[off + 4] >> 4) & 0x0F; int frameRate = data[off + 4] & 0x0F; int bitRate = ((data[off + 5] & 0xFF) << 10) | ((data[off + 6] & 0xFF) << 2) | ((data[off + 7] & 0xC0) >> 6); int numFrames = 9999; if (frameRate > 0 && frameRate < 9) { int frameRate2 = (frameRate - 1) << 1; attrs.setInt(Tag.CineRate, VR.IS, FPS[frameRate2]); attrs.setFloat(Tag.FrameTime, VR.DS, ((float) FPS[frameRate2 + 1]) / FPS[frameRate2]); if (bitRate > 0) numFrames = (int) (20 * length * FPS[frameRate2] / FPS[frameRate2 + 1] / bitRate); } attrs.setInt(Tag.SamplesPerPixel, VR.US, 3); attrs.setString(Tag.PhotometricInterpretation, VR.CS, "YBR_PARTIAL_420"); attrs.setInt(Tag.PlanarConfiguration, VR.US, 0); attrs.setInt(Tag.FrameIncrementPointer, VR.AT, Tag.FrameTime); attrs.setInt(Tag.NumberOfFrames, VR.IS, numFrames); attrs.setInt(Tag.Rows, VR.US, y); attrs.setInt(Tag.Columns, VR.US, x); if (aspectRatio > 0 && aspectRatio < 5) attrs.setString(Tag.PixelAspectRatio, VR.IS, ASPECT_RATIOS[aspectRatio-1]); attrs.setInt(Tag.BitsAllocated, VR.US, 8); attrs.setInt(Tag.BitsStored, VR.US, 8); attrs.setInt(Tag.HighBit, VR.US, 7); attrs.setInt(Tag.PixelRepresentation, VR.US, 0); attrs.setString(Tag.LossyImageCompression, VR.CS, "01"); return attrs; } }