/* * Flazr <http://flazr.com> Copyright (C) 2009 Peter Thomas. * * This file is part of Flazr. * * Flazr is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Flazr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Flazr. If not, see <http://www.gnu.org/licenses/>. */ package com.flazr.rtmp.message; import com.flazr.amf.Amf0Object; import com.flazr.io.f4v.MovieInfo; import com.flazr.io.f4v.TrackInfo; import com.flazr.io.f4v.box.STSD.VideoSD; import com.flazr.rtmp.RtmpHeader; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.jboss.netty.buffer.ChannelBuffer; public abstract class Metadata extends AbstractMessage { protected String name; protected Object[] data; public Metadata(String name, Object... data) { this.name = name; this.data = data; header.setSize(encode().readableBytes()); } public Metadata(RtmpHeader header, ChannelBuffer in) { super(header, in); } public Object getData(int index) { if(data == null || data.length < index + 1) { return null; } return data[index]; } private Object getValue(String key) { final Map<String, Object> map = getMap(0); if(map == null) { return null; } return map.get(key); } public void setValue(String key, Object value) { if(data == null || data.length == 0) { data = new Object[]{new LinkedHashMap<String, Object>()}; } if(data[0] == null) { data[0] = new LinkedHashMap<String, Object>(); } final Map<String, Object> map = (Map) data[0]; map.put(key, value); } public Map<String, Object> getMap(int index) { return (Map<String, Object>) getData(index); } public String getString(String key) { return (String) getValue(key); } public Boolean getBoolean(String key) { return (Boolean) getValue(key); } public Double getDouble(String key) { return (Double) getValue(key); } public double getDuration() { if(data == null || data.length == 0) { return -1; } final Map<String, Object> map = getMap(0); if(map == null) { return -1; } final Object o = map.get("duration"); if(o == null) { return -1; } return ((Double) o).longValue(); } public void setDuration(final double duration) { if(data == null || data.length == 0) { data = new Object[] {map(pair("duration", duration))}; } final Object meta = data[0]; final Map<String, Object> map = (Map) meta; if(map == null) { data[0] = map(pair("duration", duration)); return; } map.put("duration", duration); } //========================================================================== public static Metadata onPlayStatus(double duration, double bytes) { Map<String, Object> map = Command.onStatus(Command.OnStatus.STATUS, "NetStream.Play.Complete", pair("duration", duration), pair("bytes", bytes)); return new MetadataAmf0("onPlayStatus", map); } public static Metadata rtmpSampleAccess() { return new MetadataAmf0("|RtmpSampleAccess", false, false); } public static Metadata dataStart() { return new MetadataAmf0("onStatus", object(pair("code", "NetStream.Data.Start"))); } //========================================================================== /** [ (map){ duration=112.384, moovPosition=28.0, width=640.0, height=352.0, videocodecid=avc1, audiocodecid=mp4a, avcprofile=100.0, avclevel=30.0, aacaot=2.0, videoframerate=29.97002997002997, audiosamplerate=24000.0, audiochannels=2.0, trackinfo= [ (object){length=3369366.0, timescale=30000.0, language=eng, sampledescription=[(object){sampletype=avc1}]}, (object){length=2697216.0, timescale=24000.0, language=eng, sampledescription=[(object){sampletype=mp4a}]} ]}] */ public static Metadata onMetaDataTest(MovieInfo movie) { Amf0Object track1 = object( pair("length", 3369366.0), pair("timescale", 30000.0), pair("language", "eng"), pair("sampledescription", new Amf0Object[]{object(pair("sampletype", "avc1"))}) ); Amf0Object track2 = object( pair("length", 2697216.0), pair("timescale", 24000.0), pair("language", "eng"), pair("sampledescription", new Amf0Object[]{object(pair("sampletype", "mp4a"))}) ); Map<String, Object> map = map( pair("duration", movie.getDuration()), pair("moovPosition", movie.getMoovPosition()), pair("width", 640.0), pair("height", 352.0), pair("videocodecid", "avc1"), pair("audiocodecid", "mp4a"), pair("avcprofile", 100.0), pair("avclevel", 30.0), pair("aacaot", 2.0), pair("videoframerate", 29.97002997002997), pair("audiosamplerate", 24000.0), pair("audiochannels", 2.0), pair("trackinfo", new Amf0Object[]{track1, track2}) ); return new MetadataAmf0("onMetaData", map); } public static Metadata onMetaData(MovieInfo movie) { Map<String, Object> map = map( pair("duration", movie.getDuration()), pair("moovPosition", movie.getMoovPosition()) ); TrackInfo track1 = movie.getVideoTrack(); Amf0Object t1 = null; if(track1 != null) { String sampleType = track1.getStsd().getSampleTypeString(1); t1 = object( pair("length", track1.getMdhd().getDuration()), pair("timescale", track1.getMdhd().getTimeScale()), pair("sampledescription", new Amf0Object[]{object(pair("sampletype", sampleType))}) ); VideoSD video = movie.getVideoSampleDescription(); map(map, pair("width", (double) video.getWidth()), pair("height", (double) video.getHeight()), pair("videocodecid", sampleType) ); } TrackInfo track2 = movie.getAudioTrack(); Amf0Object t2 = null; if(track2 != null) { String sampleType = track2.getStsd().getSampleTypeString(1); t2 = object( pair("length", track2.getMdhd().getDuration()), pair("timescale", track2.getMdhd().getTimeScale()), pair("sampledescription", new Amf0Object[]{object(pair("sampletype", sampleType))}) ); map(map, pair("audiocodecid", sampleType) ); } List<Amf0Object> trackList = new ArrayList<Amf0Object>(); if(t1 != null) { trackList.add(t1); } if(t2 != null) { trackList.add(t2); } map(map, pair("trackinfo", trackList.toArray())); return new MetadataAmf0("onMetaData", map); } //========================================================================== public String getName() { return name; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(super.toString()); sb.append("name: ").append(name); sb.append(" data: ").append(Arrays.toString(data)); return sb.toString(); } }