wowza java api 插入字幕
wowza java api inject captioning
我们有一堆 .mp4 文件及其关联的 .ttml 开放式字幕文件。我们正在使用 Wowza 根据动态计划动态加载和流式传输这些文件,而不会中断流。
我能够按照此处的教程 (https://www.wowza.com/docs/how-to-use-ipublishingprovider-api-to-publish-server-side-live-streams) 在指定时间流式传输单个 mp4 文件。
但是,我找不到 Java API 命令来注入关联的字幕文件。我找到了这个页面( https://www.wowza.com/docs/how-to-configure-closed-captioning-for-live-streaming ),它没有谈论 Java API 而是一个配置设置。页面末尾是一个显示一些 API 调用的示例包,但该部分被描述为 "legacy"。我可以扩展它,但由于它是 "legacy",我担心模块所依赖的任何 API 可能无法在更高版本的 WMS 中使用。
是否有低级方法(除了 "legacy" 方法)告诉系统在 PublishingProviderMediaReader 调用后立即加载 ttml 文件(或只是手动注入文本)?
下面是从上面link粘贴的代码
package com.wowza.wms.plugin.test.integration;
import com.wowza.wms.stream.publish.*;
import com.wowza.wms.vhost.*;
import com.wowza.wms.logging.*;
public class ServerPublisherWorker extends Thread
{
private long sleepTime = 75;
private boolean running = true;
private Object lock = new Object();
private String applicationName = "live";
private String vodStreamName = "mp4:sample.mp4";
private String liveStreamName = "myStream";
private String publishStreamName = "publishstream";
private int cycleTime = 10000;
public synchronized void quit()
{
synchronized(lock)
{
running = false;
}
}
public void run()
{
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: START");
long startTime = System.currentTimeMillis();
long playStartTime = startTime;
try
{
IVHost vhost = VHostSingleton.getInstance(VHost.VHOST_DEFAULT);
Publisher publisher = Publisher.createInstance(vhost, applicationName);
publisher.publish(publishStreamName);
long nextSwitch = playStartTime + cycleTime;
long nextType = 0;
IPublishingProvider provider = new PublishingProviderMediaReader(publisher, playStartTime, vodStreamName);
//provider.seek(20000);
provider.setRealTimeStartTime(startTime);
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: Start with vod stream: "+vodStreamName);
while(true)
{
boolean moreInFile = provider!=null?provider.play(publisher):false;
long currentTime = System.currentTimeMillis();
if (!moreInFile || currentTime > nextSwitch)
{
if (provider != null)
provider.close();
provider = null;
if ((nextType % 2) == 0)
{
provider = new PublishingProviderLive(publisher, publisher.getMaxTimecode(), liveStreamName);
//((PublishingProviderLive)provider).setStartOnPreviousKeyFrame(false);
provider.setRealTimeStartTime(currentTime);
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: Switch to live stream: "+liveStreamName);
}
else
{
provider = new PublishingProviderMediaReader(publisher, publisher.getMaxTimecode(), vodStreamName);
//provider.seek(20000);
provider.setRealTimeStartTime(currentTime);
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: Switch to vod stream: "+vodStreamName);
}
nextSwitch = currentTime + cycleTime;
nextType++;
if (nextType == 100)
break;
}
else
sleep(sleepTime);
synchronized(lock)
{
if (!running)
break;
}
}
provider.close();
publisher.publish(null);
synchronized(lock)
{
running = false;
}
}
catch (Exception e)
{
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).error("ServerPublisherWorker.run: "+e.toString());
e.printStackTrace();
}
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: STOP");
}
}
以及调用上述代码的基础监听器:
package com.wowza.wms.plugin.test.integration;
import com.wowza.wms.server.*;
public class ServerPublisherServerListener implements IServerNotify
{
ServerPublisherWorker worker = null;
public void onServerCreate(IServer server)
{
}
public void onServerInit(IServer server)
{
worker = new ServerPublisherWorker();
worker.start();
}
public void onServerShutdownComplete(IServer server)
{
}
public void onServerShutdownStart(IServer server)
{
if (worker != null)
worker.quit();
worker = null;
}
}
非常感谢任何帮助或意见。提前谢谢你。
您可以使用以下方法从流中插入提示点或元数据:
package com.wowza.example.module;
import com.wowza.wms.amf.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;
import com.wowza.wms.stream.*;
public class ModuleInjectData extends ModuleBase {
public void setCaption (IClient client, RequestFunction function, AMFDataList params)
{
String streamname = params.getString(PARAM1);
String text = params.getString(PARAM2);
String language = params.getString(PARAM3);
String trackid = params.getString(PARAM4);
IMediaStream stream = client.getAppInstance().getStreams().getStream(streamname);
//essential code
AMFDataMixedArray data = new AMFDataMixedArray();
data.put("text", new AMFDataItem(text));
data.put("language", new AMFDataItem(language));
data.put("trackid", new AMFDataItem(trackid));
stream.sendDirect("onTextData", data);
((MediaStream)stream).processSendDirectMessages();
getLogger().info("Caption: " + text);
}
public void injectMetaData(IClient client, RequestFunction function, AMFDataList params)
{
String streamName = params.getString(PARAM1);
String data = params.getString(PARAM2);
IMediaStream stream = client.getAppInstance().getStreams().getStream(streamName);
if (stream != null)
{
AMFDataList amfList = new AMFDataList();
amfList.add(new AMFDataItem("@setDataFrame"));
amfList.add(new AMFDataItem("onMetaData"));
AMFDataMixedArray metaData = new AMFDataMixedArray();
metaData.put("param1", data);
metaData.put("param2", new AMFDataItem("data2"));
amfList.add(metaData);
synchronized(stream)
{
byte[] dataData = amfList.serialize();
int size = dataData.length;
long timecode = Math.max(stream.getAudioTC(), stream.getVideoTC());
stream.setDataTC(timecode);
stream.setDataSize(size);
stream.startDataPacket();
stream.addDataData(dataData, 0, size);
}
}
}
}
您将需要创建一个 BufferedReader 对象来读取您的文件并使用上述方法注入数据。
我们有一堆 .mp4 文件及其关联的 .ttml 开放式字幕文件。我们正在使用 Wowza 根据动态计划动态加载和流式传输这些文件,而不会中断流。
我能够按照此处的教程 (https://www.wowza.com/docs/how-to-use-ipublishingprovider-api-to-publish-server-side-live-streams) 在指定时间流式传输单个 mp4 文件。
但是,我找不到 Java API 命令来注入关联的字幕文件。我找到了这个页面( https://www.wowza.com/docs/how-to-configure-closed-captioning-for-live-streaming ),它没有谈论 Java API 而是一个配置设置。页面末尾是一个显示一些 API 调用的示例包,但该部分被描述为 "legacy"。我可以扩展它,但由于它是 "legacy",我担心模块所依赖的任何 API 可能无法在更高版本的 WMS 中使用。
是否有低级方法(除了 "legacy" 方法)告诉系统在 PublishingProviderMediaReader 调用后立即加载 ttml 文件(或只是手动注入文本)?
下面是从上面link粘贴的代码
package com.wowza.wms.plugin.test.integration;
import com.wowza.wms.stream.publish.*;
import com.wowza.wms.vhost.*;
import com.wowza.wms.logging.*;
public class ServerPublisherWorker extends Thread
{
private long sleepTime = 75;
private boolean running = true;
private Object lock = new Object();
private String applicationName = "live";
private String vodStreamName = "mp4:sample.mp4";
private String liveStreamName = "myStream";
private String publishStreamName = "publishstream";
private int cycleTime = 10000;
public synchronized void quit()
{
synchronized(lock)
{
running = false;
}
}
public void run()
{
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: START");
long startTime = System.currentTimeMillis();
long playStartTime = startTime;
try
{
IVHost vhost = VHostSingleton.getInstance(VHost.VHOST_DEFAULT);
Publisher publisher = Publisher.createInstance(vhost, applicationName);
publisher.publish(publishStreamName);
long nextSwitch = playStartTime + cycleTime;
long nextType = 0;
IPublishingProvider provider = new PublishingProviderMediaReader(publisher, playStartTime, vodStreamName);
//provider.seek(20000);
provider.setRealTimeStartTime(startTime);
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: Start with vod stream: "+vodStreamName);
while(true)
{
boolean moreInFile = provider!=null?provider.play(publisher):false;
long currentTime = System.currentTimeMillis();
if (!moreInFile || currentTime > nextSwitch)
{
if (provider != null)
provider.close();
provider = null;
if ((nextType % 2) == 0)
{
provider = new PublishingProviderLive(publisher, publisher.getMaxTimecode(), liveStreamName);
//((PublishingProviderLive)provider).setStartOnPreviousKeyFrame(false);
provider.setRealTimeStartTime(currentTime);
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: Switch to live stream: "+liveStreamName);
}
else
{
provider = new PublishingProviderMediaReader(publisher, publisher.getMaxTimecode(), vodStreamName);
//provider.seek(20000);
provider.setRealTimeStartTime(currentTime);
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: Switch to vod stream: "+vodStreamName);
}
nextSwitch = currentTime + cycleTime;
nextType++;
if (nextType == 100)
break;
}
else
sleep(sleepTime);
synchronized(lock)
{
if (!running)
break;
}
}
provider.close();
publisher.publish(null);
synchronized(lock)
{
running = false;
}
}
catch (Exception e)
{
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).error("ServerPublisherWorker.run: "+e.toString());
e.printStackTrace();
}
WMSLoggerFactory.getLogger(ServerPublisherWorker.class).info("ServerPublisherWorker.run: STOP");
}
}
以及调用上述代码的基础监听器:
package com.wowza.wms.plugin.test.integration;
import com.wowza.wms.server.*;
public class ServerPublisherServerListener implements IServerNotify
{
ServerPublisherWorker worker = null;
public void onServerCreate(IServer server)
{
}
public void onServerInit(IServer server)
{
worker = new ServerPublisherWorker();
worker.start();
}
public void onServerShutdownComplete(IServer server)
{
}
public void onServerShutdownStart(IServer server)
{
if (worker != null)
worker.quit();
worker = null;
}
}
非常感谢任何帮助或意见。提前谢谢你。
您可以使用以下方法从流中插入提示点或元数据:
package com.wowza.example.module;
import com.wowza.wms.amf.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;
import com.wowza.wms.stream.*;
public class ModuleInjectData extends ModuleBase {
public void setCaption (IClient client, RequestFunction function, AMFDataList params)
{
String streamname = params.getString(PARAM1);
String text = params.getString(PARAM2);
String language = params.getString(PARAM3);
String trackid = params.getString(PARAM4);
IMediaStream stream = client.getAppInstance().getStreams().getStream(streamname);
//essential code
AMFDataMixedArray data = new AMFDataMixedArray();
data.put("text", new AMFDataItem(text));
data.put("language", new AMFDataItem(language));
data.put("trackid", new AMFDataItem(trackid));
stream.sendDirect("onTextData", data);
((MediaStream)stream).processSendDirectMessages();
getLogger().info("Caption: " + text);
}
public void injectMetaData(IClient client, RequestFunction function, AMFDataList params)
{
String streamName = params.getString(PARAM1);
String data = params.getString(PARAM2);
IMediaStream stream = client.getAppInstance().getStreams().getStream(streamName);
if (stream != null)
{
AMFDataList amfList = new AMFDataList();
amfList.add(new AMFDataItem("@setDataFrame"));
amfList.add(new AMFDataItem("onMetaData"));
AMFDataMixedArray metaData = new AMFDataMixedArray();
metaData.put("param1", data);
metaData.put("param2", new AMFDataItem("data2"));
amfList.add(metaData);
synchronized(stream)
{
byte[] dataData = amfList.serialize();
int size = dataData.length;
long timecode = Math.max(stream.getAudioTC(), stream.getVideoTC());
stream.setDataTC(timecode);
stream.setDataSize(size);
stream.startDataPacket();
stream.addDataData(dataData, 0, size);
}
}
}
}
您将需要创建一个 BufferedReader 对象来读取您的文件并使用上述方法注入数据。