多协议、性能稳定、丰富API的流媒体服务器软件
如何控制HLS切片列表中显示节目日期和时间头信息(EXT-X-PROGRAM-DATE-TIME)?
element('share');?>
下面的Wowza扩展模块会为HLS切片列表中增加EXT-X-PROGRAM-DATE-TIME 标签。流的第一个切片将作为检索和显示后续切片的基础。

注意: 针对Wowza Streaming Engine™ 4.5.0 及以上版本。
package com.wowza.wms.plugin.test2.hlsprogramdatetime;

import java.util.*;

import org.apache.commons.lang.time.*;

import com.wowza.util.*;
import com.wowza.wms.amf.*;
import com.wowza.wms.application.*;
import com.wowza.wms.httpstreamer.cupertinostreaming.livestreampacketizer.*;
import com.wowza.wms.media.mp3.model.idtags.*;
import com.wowza.wms.module.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.stream.livepacketizer.*;

public class ModuleCupertinoProgramDateTime extends ModuleBase
{
	private static final Class<ModuleCupertinoProgramDateTime> CLASS = ModuleCupertinoProgramDateTime.class;
	private static final String CLASSNAME = "ModuleCupertinoProgramDateTime";
	
	public static final String PROPNAME_TRACKER = "ModuleCupertinoProgramDateTime.ProgramDateTimeTracker";
	public static final String DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'+00:00'"; // The date/time representation is ISO/IEC 8601:2004 - 2010-02-19T14:54:23.031+08:00
	
	private FastDateFormat fastDateFormat = FastDateFormat.getInstance(DATEFORMAT, SystemUtils.gmtTimeZone, Locale.US); //you can change it to Locale.CN

	private IApplicationInstance appInstance = null;
	
	class ProgramDateTimeTracker
	{
		long timeOffset = 0;
		IMediaStream stream = null;
		
		public ProgramDateTimeTracker(IMediaStream stream)
		{
			this.stream = stream;
		}
	}
	
	class LiveStreamPacketizerDataHandler implements IHTTPStreamerCupertinoLivePacketizerDataHandler2
	{
		private LiveStreamPacketizerCupertino liveStreamPacketizer = null;
		private int textId = 1;
		private String streamName = null;
		
		public LiveStreamPacketizerDataHandler(LiveStreamPacketizerCupertino liveStreamPacketizer, String streamName)
		{
			this.liveStreamPacketizer = liveStreamPacketizer;
			this.streamName = streamName;
		}
		
		public ProgramDateTimeTracker getTracker()
		{
			ProgramDateTimeTracker tracker = null;
			while(true)
			{
				IMediaStream stream = appInstance.getStreams().getStream(this.streamName);
				if (stream == null)
					break;
				
				WMSProperties props = stream.getProperties();
				
				synchronized(props)
				{
					tracker = (ProgramDateTimeTracker)props.getProperty(PROPNAME_TRACKER);
					if (tracker == null)
					{
						tracker = new ProgramDateTimeTracker(stream);
						props.put(PROPNAME_TRACKER, tracker);
					}
				}
				break;
			}
			
			return tracker;
		}

		@Override
		public void onFillChunkStart(LiveStreamPacketizerCupertinoChunk chunk)
		{
			ProgramDateTimeTracker tracker = getTracker();
			if (tracker != null)
			{
				ElapsedTimer elapsedTime = tracker.stream.getElapsedTime();
				
				long createTime = elapsedTime.getDate().getTime()+tracker.timeOffset;
								
				String programDateTimeStr = fastDateFormat.format(new Date(createTime));
				
				chunk.setProgramDateTime(programDateTimeStr);
				
				ID3Frames id3Header = liveStreamPacketizer.getID3FramesHeader(chunk.getRendition());
				if (id3Header != null)
				{
					ID3V2FrameTextInformationUserDefined comment = new ID3V2FrameTextInformationUserDefined();

					comment.setDescription("programDateTime");
					comment.setValue(programDateTimeStr);

					id3Header.clear();
					id3Header.putFrame(comment);
				}
			}
		}

		@Override
		public void onFillChunkEnd(LiveStreamPacketizerCupertinoChunk chunk, long timecode)
		{
			ProgramDateTimeTracker tracker = getTracker();
			if (tracker != null)
				tracker.timeOffset += chunk.getDuration();
		}

		@Override
		public void onFillChunkDataPacket(LiveStreamPacketizerCupertinoChunk chunk, CupertinoPacketHolder holder, AMFPacket packet, ID3Frames id3Frames)
		{
		}

		@Override
		public void onFillChunkMediaPacket(LiveStreamPacketizerCupertinoChunk chunk, CupertinoPacketHolder holder, AMFPacket packet)
		{
		}
	}
	
	class LiveStreamPacketizerListener extends LiveStreamPacketizerActionNotifyBase
	{
		public void onLiveStreamPacketizerCreate(ILiveStreamPacketizer liveStreamPacketizer, String streamName)
		{
			if (liveStreamPacketizer instanceof LiveStreamPacketizerCupertino)
			{
				getLogger().info(CLASSNAME+"#MyLiveListener.onLiveStreamPacketizerCreate["+((LiveStreamPacketizerCupertino)liveStreamPacketizer).getContextStr()+"]");
				((LiveStreamPacketizerCupertino)liveStreamPacketizer).setDataHandler(new LiveStreamPacketizerDataHandler((LiveStreamPacketizerCupertino)liveStreamPacketizer, streamName));
			}
		}
	}

	public void onAppStart(IApplicationInstance appInstance)
	{
		this.appInstance = appInstance;
		
		appInstance.addLiveStreamPacketizerListener(new LiveStreamPacketizerListener());

		getLogger().info(CLASSNAME+".onAppStart["+appInstance.getContextStr()+"]");
	}
}
要在HLS切片列表中增加EXT-X-PROGRAM-DATE-TIME标签,你需要为Wowza应用添加cupertinoEnableProgramDateTime属性,并将它设置为true

  1. 在Wowza Streaming Engine Manager 管理界面,点击Wowza应用的名字(例如live).

  2. 点击应用主界面的Propertiestab页,再点击Quick Links导航条的Custom链接.

  3. Custom部分,点击Edit.

  4. 点击Add Custom Property,然后按下面添加cupertinoEnableProgramDateTime属性:

    • Path: 选择 /Root/Application/HTTPStreamer.

    • Name: 输入 cupertinoEnableProgramDateTime.

    • Type: 选择 Boolean.

    • Value: 输入 true.

  5. 点击 Add, 再点击Save, 然后根据提示点击Restart