org.jitsi.impl.neomedia.protocol.TranscodingDataSource Maven / Gradle / Ivy
/*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.impl.neomedia.protocol;
import java.io.*;
import java.lang.reflect.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;
import javax.media.protocol.*;
import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.control.*;
/**
* Represents a DataSource which transcodes the tracks of a specific
* input DataSource into a specific output Format. The
* transcoding is attempted only for tracks which actually support it for the
* specified output Format.
*
* @author Lubomir Marinov
*/
public class TranscodingDataSource
extends DataSource
{
/**
* The DataSource which has its tracks transcoded by this instance.
*/
private final DataSource inputDataSource;
/**
* The DataSource which contains the transcoded tracks of
* inputDataSource and which is wrapped by this instance. It is the
* output of transcodingProcessor.
*/
private DataSource outputDataSource;
/**
* The Format in which the tracks of inputDataSource are
* transcoded.
*/
private final Format outputFormat;
/**
* The Processor which carries out the actual transcoding of the
* tracks of inputDataSource.
*/
private Processor transcodingProcessor;
/**
* Initializes a new TranscodingDataSource instance to transcode
* the tracks of a specific DataSource into a specific output
* Format.
*
* @param inputDataSource the DataSource which is to have its
* tracks transcoded in a specific output Format
* @param outputFormat the Format in which the new instance is to
* transcode the tracks of inputDataSource
*/
public TranscodingDataSource(
DataSource inputDataSource,
Format outputFormat)
{
super(inputDataSource.getLocator());
this.inputDataSource = inputDataSource;
this.outputFormat = outputFormat;
}
/**
* Implements {@link DataSource#connect()}. Sets up the very transcoding
* process and just does not start it i.e. creates a Processor on
* the inputDataSource, sets outputFormat on its tracks
* (which support a Format compatible with outputFormat)
* and connects to its output DataSource.
*
* @throws IOException if creating the transcoding Processor,
* setting its Format or connecting to it fails
*/
@Override
public synchronized void connect()
throws IOException
{
if (outputDataSource != null)
return;
/*
* Manager#createProcessor(DataSource) requires the specified DataSource
* to be connected.
*/
inputDataSource.connect();
Processor processor;
try
{
processor = Manager.createProcessor(inputDataSource);
}
catch (NoProcessorException npex)
{
IOException ioex = new IOException();
ioex.initCause(npex);
throw ioex;
}
ProcessorUtility processorUtility = new ProcessorUtility();
if (!processorUtility.waitForState(processor, Processor.Configured))
throw new IOException("Couldn't configure transcoding processor.");
TrackControl[] trackControls = processor.getTrackControls();
if (trackControls != null)
for (TrackControl trackControl : trackControls)
{
Format trackFormat = trackControl.getFormat();
/*
* XXX We only care about AudioFormat here and we assume
* outputFormat is of such type because it is in our current and
* only use case of TranscodingDataSource
*/
if ((trackFormat instanceof AudioFormat)
&& !trackFormat.matches(outputFormat))
{
Format[] supportedTrackFormats
= trackControl.getSupportedFormats();
if (supportedTrackFormats != null)
for (Format supportedTrackFormat
: supportedTrackFormats)
if (supportedTrackFormat.matches(outputFormat))
{
Format intersectionFormat
= supportedTrackFormat.intersects(
outputFormat);
if (intersectionFormat != null)
{
trackControl.setFormat(intersectionFormat);
break;
}
}
}
}
if (!processorUtility.waitForState(processor, Processor.Realized))
throw new IOException("Couldn't realize transcoding processor.");
DataSource outputDataSource = processor.getDataOutput();
outputDataSource.connect();
transcodingProcessor = processor;
this.outputDataSource = outputDataSource;
}
/**
* Implements {@link DataSource#disconnect()}. Stops and undoes the whole
* setup of the very transcoding process i.e. disconnects from the output
* DataSource of the transcodingProcessor and disposes of the
* transcodingProcessor.
*/
@Override
public synchronized void disconnect()
{
if (outputDataSource == null)
return;
try
{
stop();
}
catch (IOException ioex)
{
throw new UndeclaredThrowableException(ioex);
}
outputDataSource.disconnect();
transcodingProcessor.deallocate();
transcodingProcessor.close();
transcodingProcessor = null;
outputDataSource = null;
}
/**
* Implements {@link DataSource#getContentType()}. Delegates to the actual
* output of the transcoding.
*
* @return a String value which describes the type of the content
* made available by this DataSource
*/
@Override
public synchronized String getContentType()
{
return
(outputDataSource == null)
? null
: outputDataSource.getContentType();
}
/**
* Implements {@link DataSource#getControl(String)}. Delegates to the actual
* output of the transcoding.
*
* @param controlType a String value which names the type of the
* control to be retrieved
* @return an Object which represents the control of this instance
* with the specified type if such a control is available; otherwise,
* null
*/
@Override
public synchronized Object getControl(String controlType)
{
/*
* The Javadoc of DataSource#getControl(String) says it's an error to
* call the method without being connected and by that time we should
* have the outputDataSource.
*/
return outputDataSource.getControl(controlType);
}
/**
* Implements {@link DataSource#getControls()}. Delegates to the actual
* output of the transcoding.
*
* @return an array of Objects which represent the controls
* available for this instance
*/
@Override
public synchronized Object[] getControls()
{
return
(outputDataSource == null)
? ControlsAdapter.EMPTY_CONTROLS
: outputDataSource.getControls();
}
/**
* Implements {@link DataSource#getDuration()}. Delegates to the actual
* output of the transcoding.
*
* @return a Time value which describes the duration of the content
* made available by this instance
*/
@Override
public synchronized Time getDuration()
{
return
(outputDataSource == null)
? DURATION_UNKNOWN
: outputDataSource.getDuration();
}
/**
* Gets the output streams that this instance provides. Some of them may be
* the result of transcoding the tracks of the input DataSource of
* this instance in the output Format of this instance.
*
* @return an array of SourceStreams which represents the
* collection of output streams that this instance provides
*/
public synchronized SourceStream[] getStreams()
{
if (outputDataSource instanceof PushBufferDataSource)
return ((PushBufferDataSource) outputDataSource).getStreams();
if (outputDataSource instanceof PullBufferDataSource)
return ((PullBufferDataSource) outputDataSource).getStreams();
if (outputDataSource instanceof PushDataSource)
return ((PushDataSource) outputDataSource).getStreams();
if (outputDataSource instanceof PullDataSource)
return ((PullDataSource) outputDataSource).getStreams();
return new SourceStream[0];
}
/**
* Implements {@link DataSource#start()}. Starts the actual transcoding
* process already set up with {@link #connect()}.
*
* @throws IOException if starting the transcoding fails
*/
@Override
public synchronized void start()
throws IOException
{
/*
* The Javadoc of DataSource#start() says it's an error to call the
* method without being connected and by that time we should have the
* outputDataSource.
*/
outputDataSource.start();
transcodingProcessor.start();
}
/**
* Implements {@link DataSource#stop()}. Stops the actual transcoding
* process if it has already been set up with {@link #connect()}.
*
* @throws IOException if stopping the transcoding fails
*/
@Override
public synchronized void stop()
throws IOException
{
if (outputDataSource != null)
{
transcodingProcessor.stop();
outputDataSource.stop();
}
}
/**
* Returns this instance's Processor object
*
* @return this instance's Processor object
*/
public Processor getTranscodingProcessor()
{
return transcodingProcessor;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy