📄 pulldatasourcecallbackurlprotocolhandler.java
字号:
package net.sf.fmj.ffmpeg_java;import java.io.IOException;import java.util.logging.Level;import java.util.logging.Logger;import javax.media.protocol.PullDataSource;import javax.media.protocol.PullSourceStream;import javax.media.protocol.Seekable;import javax.media.protocol.SourceCloneable;import net.sf.ffmpeg_java.AVFormatLibrary;import net.sf.ffmpeg_java.AVFormatLibrary.URLContext;import net.sf.ffmpeg_java.custom_protocol.CallbackURLProtocolHandler;import net.sf.fmj.utility.LoggerSingleton;import com.sun.jna.Pointer;/** * Implements CallbackURLProtocolHandler by using a PullSourceStream. * * Because seeking functions have to be implemented, either the source has to be Cloneable, or the * streams provided by the source have to be Seekable. Otherwise, seeking will fail, and * ffmpeg does not really check for seek failures, it mostly just assumes they will work. * This was written with an eye on ffmpeg's http.c, which shows that when a seek is needed, they simply * open a new stream. http.c is not of course used when this is used, it just shows what kind of * behavior is expected from a URLProtocol. * * @author Ken Larson * */public class PullDataSourceCallbackURLProtocolHandler implements CallbackURLProtocolHandler{ private static final Logger logger = LoggerSingleton.logger; private PullDataSource source; private PullSourceStream pss; private long curpos; // for seeking public static final boolean TRACE = false; private boolean mustClone = false; private final boolean isSourceCloneable; public PullDataSourceCallbackURLProtocolHandler(PullDataSource source) { super(); this.source = source; isSourceCloneable = source instanceof SourceCloneable; } public int open(URLContext h, String filename, int flags) { if (TRACE) System.out.print("open: flags=" + flags); if ((flags & AVFormatLibrary.URL_RDWR) != 0) { if (TRACE) System.out.println(" return " + -1); logger.severe("PullSourceStreamCallbackURLProtocolHandler: only read-only open supported"); return -1; } else if ((flags & AVFormatLibrary.URL_WRONLY) != 0) { if (TRACE) System.out.println(" return " + -1); logger.severe("PullSourceStreamCallbackURLProtocolHandler: only read-only open supported"); return -1; } try { doOpen(); } catch (IOException e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.WARNING, "" + e, e); return -1; } catch (Throwable e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.SEVERE, "" + e, e); return -1; } if (TRACE) System.out.println(" return " + 0); return 0; } private boolean opened = false; private void doOpen() throws IOException { if (opened) return; if (mustClone) { if (!isSourceCloneable) { logger.severe("PullSourceStreamCallbackURLProtocolHandler: cannot reopen because source is not SourceCloneable"); throw new IOException("not SourceCloneable"); } source = (PullDataSource) ((SourceCloneable) source).createClone(); } source.connect(); // TODO: we don't need to connect if already connected source.start(); final PullSourceStream[] pullSourceStreams = source.getStreams(); if (pullSourceStreams.length < 1) { logger.warning("No streams"); throw new IOException("No streams"); } pss = pullSourceStreams[0]; curpos = 0; opened = true; } private void closeAndReopen() throws IOException { doClose(); doOpen(); } public int read(URLContext h, Pointer buf, int size) { if (TRACE) System.out.print("read: size=" + size); // TODO: is there a way to do this without having a copy of the byte array? final byte[] ba = new byte[size]; //buf.getByteArray(0, size); try { if (!opened) { logger.warning("Attempt to read with closed stream"); if (TRACE) System.out.println(" return " + -1); return -1; } final int ret = pss.read(ba, 0, size); buf.write(0, ba, 0, size); curpos += ret; if (TRACE) System.out.println(" return " + ret); return ret; } catch (IOException e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.WARNING, "" + e, e); return -1; } catch (Throwable e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.SEVERE, "" + e, e); return -1; } } public int write(URLContext h, Pointer buf, int size) { if (TRACE) System.out.print("write: size=" + size); logger.severe("write not supported"); return -1; } public long seek(URLContext h, long pos, int whence) { if (TRACE) System.out.print("seek: pos=" + pos + " whence=" + whence); // doesn't need to be seekable to query the size: if (whence == AVFormatLibrary.AVSEEK_SIZE) { final long ret = pss.getContentLength(); if (TRACE) System.out.println(" return " + ret); return ret; } if (whence == SEEK_END) { // cannot seek relative to end if no known content length. if (pss.getContentLength() < 0) { if (TRACE) System.out.println(" return " + -1); return -1; } } final boolean isSeekable = pss instanceof Seekable; try { if (!isSeekable) { // if the stream is not seekable, we have to seek by reading/ignoring and/or closing/reopening the stream to get to zero. if (whence == SEEK_END) { // seek relative to the end - translate this into an absolute seek whence = SEEK_SET; pos += pss.getContentLength(); } // at this point whence is either SEEK_SET or SEEK_CUR if (whence == SEEK_CUR) { // relative seek: translate to absolute seek whence = SEEK_SET; pos += curpos; } // at this point whence is definitely SEEK_SET (or an invalid value) if (whence == SEEK_SET) { if (pss.getContentLength() > 0 && pos >= pss.getContentLength()) { if (TRACE) System.out.println("Seek to end: closing..."); // seek to end or past. No need to read everything, just close stream and update pos. // subsequent seeks will cause us to reopen. doClose(); curpos = pos; return curpos; } else if (pos >= curpos) { // absolute seek to after our position: simply do a SEEK_CUR below. whence = SEEK_CUR; pos -= curpos; } else { // absolute seek to something before where we are: translate to relative seek to zero, and close and reopen stream, so that we are back at zero. whence = SEEK_CUR; if (TRACE) System.out.println("Closing and reopening..."); closeAndReopen(); } } if (whence == SEEK_CUR) { // implement seek by repeated read: final byte[] b = new byte[1]; // TODO: very inefficient // TODO: we don't actually have to do the read until later, if we don't want to. for (int i = 0; i < pos; ++i) { if (pss.read(b, 0, 1) < 0) { // end of stream - TODO: is this what we are supposed to do? logger.warning("attempt to seek past end of stream"); if (TRACE) System.out.println(" return " + -1); return -1; } curpos += 1; } return curpos; } } if (!(isSeekable)) { if (TRACE) System.out.println(" return " + -1); //System.err.println("pss not Seekable"); return -1; } final Seekable seekable = (Seekable) pss; final long seekTo; if (whence == SEEK_SET) seekTo = pos; else if (whence == SEEK_CUR) seekTo = curpos + pos; else if (whence == SEEK_END) { if (pss.getContentLength() < 0) return -1; seekTo = pss.getContentLength() + pos; } else { if (TRACE) System.out.println(" return " + -1); logger.warning("seek: Invalid whence value: " + whence); return -1; } final long ret = seekable.seek(seekTo); curpos = ret; if (TRACE) System.out.println(" return " + ret); return ret; } catch (IOException e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.WARNING, "" + e, e); return -1; } catch (Throwable e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.SEVERE, "" + e, e); return -1; } } public int close(URLContext h) { // TODO: it is not clear that we should really do the close here, if the source would be closed elsewhere. if (TRACE) System.out.print("close"); try { doClose(); } catch (IOException e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.WARNING, "" + e, e); return -1; } catch (Throwable e) { if (TRACE) System.out.println(" return " + -1); logger.log(Level.SEVERE, "" + e, e); return -1; } if (TRACE) System.out.println(" return " + 0); return 0; } private void doClose() throws IOException { if (!opened) return; try { source.stop(); source.disconnect(); } finally { mustClone = true; opened = false; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -