AVCODEC.avcodec_init(); // TODO: everything seems to be fine if we don't call this...
}
catch (Throwable t)
{
logger.log(Level.WARNING, "" + t, t);
throw new ResourceUnavailableException("avcodec_init or av_register_all failed");
}
// not sure what the consequences of such a mismatch are, but it is worth logging a warning:
if (AVCODEC.avcodec_version() != AVCodecLibrary.LIBAVCODEC_VERSION_INT)
logger.warning("ffmpeg-java and ffmpeg versions do not match: avcodec_version=" + AVCODEC.avcodec_version() + " LIBAVCODEC_VERSION_INT=" + AVCodecLibrary.LIBAVCODEC_VERSION_INT);
String urlStr;
final String protocol = source.getLocator().getProtocol();
// TODO: ffmpeg appears to support multiple file protocols, for example: file: pipe: udp: rtp: tcp: http:
// we should also allow those.
// TODO: would be best to query this dynamically from ffmpeg
// we don't really use the DataSource, we just grab its URL. So arbitrary data sources won't work.
// otherwise, we register a custom URLHandler with ffmpeg, which calls us back to get the data.
if (protocol.equals("file") || protocol.equals("http")) {
// just use the URL from the datasource
// FMJ supports relative file URLs, but FFMPEG does not. So we'll rewrite the URL here:
// TODO: perhaps we should only do this if FFMPEG has a problem (av_open_input_file returns nonzero).
if (protocol.equals("file"))
// ffmpeg has problems with windows file-URLs like file:///c:/
urlStr = URLUtils.extractValidPathFromFileUrl(source.getLocator().toExternalForm());
else
urlStr = source.getLocator().toExternalForm();
}
else
{ // use the real java datasource, via callbacks.
CallbackURLProtocolMgr.register(AVFORMAT);
// TODO: do this in start?
final String callbackURL = CallbackURLProtocolMgr.addCallbackURLProtocolHandler(new PullDataSourceCallbackURLProtocolHandler(source));
// TODO: we need to remove the handler when we are done.
urlStr = callbackURL;
}
final PointerByReference ppFormatCtx = new PointerByReference();
// Open video file
final int ret = AVFORMAT.av_open_input_file(ppFormatCtx, urlStr, null, 0, null);
if (ret != 0) {
throw new ResourceUnavailableException("av_open_input_file failed: " + ret); // Couldn't open file
}
formatCtx = new AVFormatContext(ppFormatCtx.getValue());
//System.out.println(new String(formatCtx.filename));
// Retrieve stream information
if (AVFORMAT.av_find_stream_info(formatCtx) < 0)
throw new ResourceUnavailableException("Couldn't find stream information"); // Couldn't find stream information
AVFORMAT.dump_format(formatCtx, 0, urlStr, 0);
VideoTrack videoTrack = null;
AudioTrack audioTrack = null;
for (int i = 0; i < formatCtx.nb_streams; i++)
{ final AVStream stream = new AVStream(formatCtx.getStreams()[i]);
final AVCodecContext codecCtx = new AVCodecContext(stream.codec);
if (codecCtx.codec_id == 0)
{ logger.info("Codec id is zero (no codec) - skipping stream " + i);
continue;
}
if (codecCtx.codec_type == AVCodecLibrary.CODEC_TYPE_VIDEO && videoTrack == null)
{
videoTrack = new VideoTrack(i, stream, codecCtx);
}
else if (codecCtx.codec_type == AVCodecLibrary.CODEC_TYPE_AUDIO && audioTrack == null)
{
try
{
audioTrack = new AudioTrack(i, stream, codecCtx);
}
catch (ResourceUnavailableException e)
{
if (!PROCEED_IF_NO_AUDIO_CODEC)
throw e;
logger.log(Level.WARNING, "Skipping audio track: " + e, e);
}
}
else
{ //throw new ResourceUnavailableException("Unknown track codec type " + codecCtx.codec_type + " for track " + i);
}
}
if (audioTrack == null && videoTrack == null)
throw new ResourceUnavailableException("No audio or video track found");
else if (audioTrack != null && videoTrack != null)
tracks = new PullSourceStreamTrack[] {videoTrack, audioTrack};
else if (audioTrack != null)
tracks = new PullSourceStreamTrack[] {audioTrack};
else