001/*******************************************************************************
002 * Copyright (c) 2024, 2026, Olivier Ayache.  All rights reserved.
003 *
004 * This file is part of AVPKit.
005 *
006 * AVPKit is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Lesser General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * AVPKit is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with AVPKit.  If not, see <http://www.gnu.org/licenses/>.
018 *******************************************************************************/
019
020package com.avpkit.mediatool;
021
022import java.awt.image.BufferedImage;
023
024import com.avpkit.mediatool.event.IVideoPictureEvent;
025import com.avpkit.core.IAudioSamples;
026import com.avpkit.core.IContainer;
027import com.avpkit.core.IContainerFormat;
028import com.avpkit.core.IError;
029import com.avpkit.core.IVideoPicture;
030
031/**
032 * An {@link IMediaCoder} that reads and decodes media from an
033 * {@link IContainer}.
034 * 
035 * <p>
036 * An {@link IMediaReader} opens up a media container,
037 * reads packets from it, decodes the data, and then dispatches
038 * information about the data to any registered
039 * {@link IMediaListener} objects.  The main method of
040 * interest is {@link #readPacket()}.
041 * 
042 * </p>
043 * <p>
044 * 
045 * Here's an example of a very simple program that prints out
046 * a line when the {@link IMediaReader} decides to open a container.
047 * 
048 *  </p>
049 *  <pre>
050 *  IMediaDebugListener myListener = new MediaListenerAdapter(){
051 *    public void onOpen(IMediaGenerator pipe) {
052 *      System.out.println("opened: " + ((IMediaReader)pipe).getUrl());
053 *    }
054 *  };
055 *  IMediaReader reader = ToolFactory.makeReader("myinputfile.flv");
056 *  reader.addListener(myListener);
057 *  while(reader.readPacket() == null)
058 *    ;
059 *  </pre>
060 *  <p>
061 *  
062 *  And here's a slightly more involved example where we read
063 *  a file and display it on screen in real-time:
064 *  
065 *  </p>
066 *  <pre>
067 *  IMediaReader reader = ToolFactory.makeReader("myinputfile.flv");
068 *  reader.addListener(ToolFactory.makeViewer());
069 *  while(reader.readPacket() == null)
070 *    ;
071 *  </pre>
072 *  <p>
073 *  For examples of this class in action, see the
074 *  com.avpkit.mediatool.demos package.
075 *  </p>
076 * @author trebor
077 * @author aclarke
078 *
079 */
080
081public interface IMediaReader extends IMediaCoder
082{
083
084  /**
085   * Set if the underlying media container supports adding dynamic streams. See
086   * {@link IContainer#open(String, IContainer.Type, IContainerFormat, boolean, boolean)}
087   * . The default value for this is false.
088   * 
089   * <p>
090   * 
091   * If set to false, the {@link IMediaReader} can assume no new streams will be
092   * added after {@link #open()} has been called, and may decide to query the
093   * entire media file to find all meta data. If true then {@link IMediaReader}
094   * will not read ahead; instead it will only query meta data for a stream when
095   * a {@link #readPacket()} returns the first packet in a new stream. Note that
096   * a {@link IMediaWriter} can only initialize itself from a
097   * {@link IMediaReader} that has this parameter set to false.
098   * 
099   * </p>
100   * <p>
101   * 
102   * To have an effect, the MediaReader must not have been created with an
103   * already open {@link IContainer}, and this method must be called before the
104   * first call to {@link #readPacket}.
105   * 
106   * </p>
107   * 
108   * @param streamsCanBeAddedDynamically true if new streams may appear at any
109   *        time during a {@link #readPacket} call
110   * 
111   * @throws RuntimeException if the media container is already open
112   */
113
114  public abstract void setAddDynamicStreams(boolean streamsCanBeAddedDynamically);
115
116  /** 
117   * Report if the underlying media container supports adding dynamic
118   * streams.  See {@link IContainer#open(String, IContainer.Type,
119   * IContainerFormat, boolean, boolean)}.
120   * 
121   * @return true if new streams can may appear at any time during a
122   *         {@link #readPacket} call
123   * @see #setAddDynamicStreams(boolean)
124   */
125
126  public abstract boolean canAddDynamicStreams();
127
128  /** 
129   * Set if the underlying media container will attempt to establish all
130   * meta data when the container is opened, which will potentially
131   * block until it has ready enough data to find all streams in a
132   * container.  If false, it will only block to read a minimal header
133   * for this container format.  See {@link IContainer#open(String,
134   * IContainer.Type, IContainerFormat, boolean, boolean)}.  The default
135   * value for this is true.
136   *
137   * <p>
138   * 
139   * To have an effect, the MediaReader must not have been created with
140   * an already open {@link IContainer}, and this method must be called
141   * before the first call to {@link #readPacket}.
142   *
143   * </p>
144   * 
145   * @param queryStreamMetaData true if meta data is to be queried
146   * 
147   * @throws RuntimeException if the media container is already open
148   */
149
150  public abstract void setQueryMetaData(boolean queryStreamMetaData);
151
152  /** 
153   * Report if the underlying media container will attempt to establish
154   * all meta data when the container is opened, which will potentially
155   * block until it has ready enough data to find all streams in a
156   * container.  If false, it will only block to read a minimal header
157   * for this container format.  See {@link IContainer#open(String,
158   * IContainer.Type, IContainerFormat, boolean, boolean)}.
159   * 
160   * @return true meta data will be queried
161   * @see #setQueryMetaData(boolean)
162   */
163
164  public abstract boolean willQueryMetaData();
165
166  /**
167   * Should {@link IMediaReader} automatically call {@link #close()}, only if
168   * ERROR_EOF is returned from {@link #readPacket}. Otherwise {@link #close()}
169   * is automatically called when any error value is returned. The default value
170   * for this is false.
171   * 
172   * @param closeOnEofOnly true if meta data is to be queried
173   * 
174   * @throws RuntimeException if the media container is already open
175   */
176
177  public abstract void setCloseOnEofOnly(boolean closeOnEofOnly);
178
179  /** 
180   * Report if close will called only if ERROR_EOF is returned from
181   * {@link #readPacket}.  Otherwise close is called when any error
182   * value is returned.  The default value for this is false.
183   * 
184   * @return true if will close on ERROR_EOF only
185   * @see #setCloseOnEofOnly(boolean)
186   */
187
188  public abstract boolean willCloseOnEofOnly();
189
190  /**
191   * Decodes the next packet and calls all registered {@link IMediaListener}
192   * objects.
193   * 
194   * <p>
195   * 
196   * If a complete {@link IVideoPicture} or {@link IAudioSamples} set are
197   * decoded, it will be dispatched to the listeners added to the media reader.
198   * 
199   * </p>
200   * 
201   * <p>
202   * 
203   * This method will automatically call {@link #open()} if it has not
204   * already been called, and will automatically call {@link #close()} when
205   * it reads an error or end of file from the file.  The default
206   * close behavior can be changed with {@link #setCloseOnEofOnly(boolean)}.
207   * 
208   * </p>
209   * 
210   * @return null if there are more packets to read, otherwise return an IError
211   *         instance. If {@link IError#getType()} ==
212   *         {@link com.avpkit.core.IError.Type#ERROR_EOF} then end of file
213   *         has been reached.
214   */
215
216  public abstract IError readPacket();
217
218  /**
219   * Asks the {@link IMediaReader} to generate {@link BufferedImage} images when
220   * calling
221   * {@link IMediaListener#onVideoPicture(IVideoPictureEvent)}
222   * .
223   * 
224   * <p>
225   * NOTE: Only {@link BufferedImage#TYPE_3BYTE_BGR} is supported today.
226   * </p>
227   * 
228   * <p>
229   * If set to a non-negative value, {@link IMediaReader} will resample any
230   * video data it has decoded into the right colorspace for the
231   * {@link BufferedImage}, and generate a new {@link BufferedImage} to pass in
232   * on each
233   * {@link IMediaListener#onVideoPicture(IVideoPictureEvent)
234   * }
235   * call.
236   * </p>
237   * 
238   * @param bufferedImageType The buffered image type (e.g.
239   *        {@link BufferedImage#TYPE_3BYTE_BGR}) you want {@link IMediaReader}
240   *        to generate. Set to -1 to disable this feature.
241   * 
242   * @see BufferedImage
243   */
244
245  public abstract void setBufferedImageTypeToGenerate(int bufferedImageType);
246
247  /**
248   * Get the {@link BufferedImage} type this {@link IMediaReader} will
249   * generate.
250   * @return the type, or -1 if disabled.
251   * 
252   * @see #getBufferedImageTypeToGenerate()
253   */
254
255  public abstract int getBufferedImageTypeToGenerate();
256  
257  /**
258   * {@inheritDoc}
259   */
260
261  public abstract IContainer getContainer();
262
263  /**
264   * {@inheritDoc}
265   */
266
267  public abstract String getUrl();
268
269  /**
270   * {@inheritDoc}
271   */
272
273  public abstract void open();
274
275  /**
276   * {@inheritDoc}
277   */
278
279  public abstract void close();
280
281}