1/*
2 * Copyright 2008 CoreMedia AG, Hamburg
3 *
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.coremedia.iso.boxes;
18
19
20import com.coremedia.iso.IsoFile;
21import com.coremedia.iso.IsoTypeReader;
22import com.coremedia.iso.IsoTypeWriter;
23import com.coremedia.iso.Utf8;
24import com.googlecode.mp4parser.AbstractFullBox;
25
26import java.nio.ByteBuffer;
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.Map;
30
31/**
32 * This box within a Media Box declares the process by which the media-data in the track is presented,
33 * and thus, the nature of the media in a track.
34 * This Box when present in a Meta Box, declares the structure or format of the 'meta' box contents.
35 * See ISO/IEC 14496-12 for details.
36 *
37 * @see MetaBox
38 * @see MediaBox
39 */
40public class HandlerBox extends AbstractFullBox {
41    public static final String TYPE = "hdlr";
42    public static final Map<String, String> readableTypes;
43
44    static {
45        HashMap<String, String> hm = new HashMap<String, String>();
46        hm.put("odsm", "ObjectDescriptorStream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
47        hm.put("crsm", "ClockReferenceStream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
48        hm.put("sdsm", "SceneDescriptionStream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
49        hm.put("m7sm", "MPEG7Stream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
50        hm.put("ocsm", "ObjectContentInfoStream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
51        hm.put("ipsm", "IPMP Stream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
52        hm.put("mjsm", "MPEG-J Stream - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
53        hm.put("mdir", "Apple Meta Data iTunes Reader");
54        hm.put("mp7b", "MPEG-7 binary XML");
55        hm.put("mp7t", "MPEG-7 XML");
56        hm.put("vide", "Video Track");
57        hm.put("soun", "Sound Track");
58        hm.put("hint", "Hint Track");
59        hm.put("appl", "Apple specific");
60        hm.put("meta", "Timed Metadata track - defined in ISO/IEC JTC1/SC29/WG11 - CODING OF MOVING PICTURES AND AUDIO");
61
62        readableTypes = Collections.unmodifiableMap(hm);
63
64    }
65
66    private String handlerType;
67    private String name = null;
68    private long a, b, c;
69    private boolean zeroTerm = true;
70
71    private long shouldBeZeroButAppleWritesHereSomeValue;
72
73    public HandlerBox() {
74        super(TYPE);
75    }
76
77    public String getHandlerType() {
78        return handlerType;
79    }
80
81    /**
82     * You are required to add a '\0' string termination by yourself.
83     *
84     * @param name the new human readable name
85     */
86    public void setName(String name) {
87        this.name = name;
88    }
89
90    public void setHandlerType(String handlerType) {
91        this.handlerType = handlerType;
92    }
93
94    public String getName() {
95        return name;
96    }
97
98    public String getHumanReadableTrackType() {
99        return readableTypes.get(handlerType) != null ? readableTypes.get(handlerType) : "Unknown Handler Type";
100    }
101
102    protected long getContentSize() {
103        if (zeroTerm) {
104            return 25 + Utf8.utf8StringLengthInBytes(name);
105        } else {
106            return 24 + Utf8.utf8StringLengthInBytes(name);
107        }
108
109    }
110
111    @Override
112    public void _parseDetails(ByteBuffer content) {
113        parseVersionAndFlags(content);
114        shouldBeZeroButAppleWritesHereSomeValue = IsoTypeReader.readUInt32(content);
115        handlerType = IsoTypeReader.read4cc(content);
116        a = IsoTypeReader.readUInt32(content);
117        b = IsoTypeReader.readUInt32(content);
118        c = IsoTypeReader.readUInt32(content);
119        if (content.remaining() > 0) {
120            name = IsoTypeReader.readString(content, content.remaining());
121            if (name.endsWith("\0")) {
122                name = name.substring(0, name.length() - 1);
123                zeroTerm = true;
124            } else {
125                zeroTerm = false;
126            }
127        } else {
128            zeroTerm = false; //No string at all, not even zero term char
129        }
130    }
131
132    @Override
133    protected void getContent(ByteBuffer byteBuffer) {
134        writeVersionAndFlags(byteBuffer);
135        IsoTypeWriter.writeUInt32(byteBuffer, shouldBeZeroButAppleWritesHereSomeValue);
136        byteBuffer.put(IsoFile.fourCCtoBytes(handlerType));
137        IsoTypeWriter.writeUInt32(byteBuffer, a);
138        IsoTypeWriter.writeUInt32(byteBuffer, b);
139        IsoTypeWriter.writeUInt32(byteBuffer, c);
140        if (name != null) {
141            byteBuffer.put(Utf8.convert(name));
142        }
143        if (zeroTerm) {
144            byteBuffer.put((byte) 0);
145        }
146    }
147
148    public String toString() {
149        return "HandlerBox[handlerType=" + getHandlerType() + ";name=" + getName() + "]";
150    }
151}
152