001/* ----------------------------------------------------------------------------
002 * This file was automatically generated by SWIG (http://www.swig.org).
003 * Version 4.0.2
004 *
005 * Do not make changes to this file unless you know what you are doing--modify
006 * the SWIG interface file instead.
007 * ----------------------------------------------------------------------------- */
008
009package com.avpkit.core;
010import com.avpkit.ferry.*;
011/**
012 * Represents a stream of similar data (eg video) in a {IContainer}.<br>
013 * <p><br>
014 * Streams are really virtual concepts; {IContainer}s really just contain<br>
015 * a bunch of {IPacket}s.  But each {IPacket} usually has a stream<br>
016 * id associated with it, and all {IPacket}s with that stream id represent<br>
017 * the same type of (usually time-based) data.  For example in many FLV<br>
018 * video files, there is a stream with id "0" that contains all video data, and<br>
019 * a stream with id "1" that contains all audio data.<br>
020 * </p><p><br>
021 * You use an {IStream} object to get properly configured {IStreamCoder}<br>
022 * for decoding, and to tell {IStreamCoder}s how to encode {IPacket}s when<br>
023 * decoding.<br>
024 * </p>
025 */
026public class IStream extends RefCounted {
027  // JNIHelper.swg: Start generated code
028  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
029  /**
030   * This method is only here to use some references and remove
031   * a Eclipse compiler warning.
032   */
033  @SuppressWarnings("unused")
034  private void noop()
035  {
036    IBuffer.make(null, 1);
037  }
038   
039  private volatile long swigCPtr;
040
041  /**
042   * Internal Only.
043   */
044  protected IStream(long cPtr, boolean cMemoryOwn) {
045    super(AVPKitJNI.IStream_SWIGUpcast(cPtr), cMemoryOwn);
046    swigCPtr = cPtr;
047  }
048  
049  /**
050   * Internal Only.
051   */
052  protected IStream(long cPtr, boolean cMemoryOwn,
053      java.util.concurrent.atomic.AtomicLong ref)
054  {
055    super(AVPKitJNI.IStream_SWIGUpcast(cPtr),
056     cMemoryOwn, ref);
057    swigCPtr = cPtr;
058  }
059    
060  /**
061   * Internal Only.  Not part of public API.
062   *
063   * Get the raw value of the native object that obj is proxying for.
064   *   
065   * @param obj The java proxy object for a native object.
066   * @return The raw pointer obj is proxying for.
067   */
068  public static long getCPtr(IStream obj) {
069    if (obj == null) return 0;
070    return obj.getMyCPtr();
071  }
072
073  /**
074   * Internal Only.  Not part of public API.
075   *
076   * Get the raw value of the native object that we're proxying for.
077   *   
078   * @return The raw pointer we're proxying for.
079   */  
080  public long getMyCPtr() {
081    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
082    return swigCPtr;
083  }
084  
085  /**
086   * Create a new IStream object that is actually referring to the
087   * exact same underlying native object.
088   *
089   * @return the new Java object.
090   */
091  @Override
092  public IStream copyReference() {
093    if (swigCPtr == 0)
094      return null;
095    else
096      return new IStream(swigCPtr, swigCMemOwn, getJavaRefCount());
097  }
098
099  /**
100   * Compares two values, returning true if the underlying objects in native code are the same object.
101   *
102   * That means you can have two different Java objects, but when you do a comparison, you'll find out
103   * they are the EXACT same object.
104   *
105   * @return True if the underlying native object is the same.  False otherwise.
106   */
107  public boolean equals(Object obj) {
108    boolean equal = false;
109    if (obj instanceof IStream)
110      equal = (((IStream)obj).swigCPtr == this.swigCPtr);
111    return equal;
112  }
113  
114  /**
115   * Get a hashable value for this object.
116   *
117   * @return the hashable value.
118   */
119  public int hashCode() {
120     return (int)swigCPtr;
121  }
122  
123  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
124  // JNIHelper.swg: End generated code
125  
126
127  /**
128   * info about this stream
129   * @return information about this stream
130   */
131   
132  @Override
133  public String toString()
134  {
135    StringBuilder result = new StringBuilder();
136    
137    result.append(this.getClass().getName()+"@"+hashCode()+"[");
138    result.append("index:"+getIndex()+";");
139    result.append("id:"+getId()+";");
140    result.append("streamcoder:"+getStreamCoder()+";");
141    result.append("framerate:"+getFrameRate()+";");
142    result.append("timebase:"+getTimeBase()+";");
143    result.append("direction:"+getDirection()+";");
144    result.append("]");
145    return result.toString();
146  }
147
148  /**
149   * Get an ordered sequence of index entries in this {@link IStream}.
150   * 
151   * @return A list of entries.  Will always return a non-null
152   *   list, but if there are no entries the list size will be zero.
153   */
154  public java.util.List<IIndexEntry> getIndexEntries()
155  {
156    final int numEntries = getNumIndexEntries();
157    java.util.List<IIndexEntry> retval = new java.util.ArrayList<IIndexEntry>(Math.max(numEntries, 10));
158    for(int i = 0; i < numEntries; i++) {
159      final IIndexEntry entry = getIndexEntry(i);
160      if (entry != null) {
161       retval.add(entry); 
162      }
163    }
164    
165    return retval;
166  }
167
168
169  /**
170   * Get the {Direction} this stream is pointing in.<br>
171   * @return The direction of this stream.
172   */
173  public IStream.Direction getDirection() {
174    return IStream.Direction.swigToEnum(AVPKitJNI.IStream_getDirection(swigCPtr, this));
175  }
176
177  /**
178   * Get the relative position this stream has in the hosting<br>
179   * {IContainer} object.<br>
180   * @return The Index within the Container of this stream.
181   */
182  public int getIndex() {
183    return AVPKitJNI.IStream_getIndex(swigCPtr, this);
184  }
185
186  /**
187   * Return a container format specific id for this stream.<br>
188   * @return The (container format specific) id of this stream.
189   */
190  public int getId() {
191    return AVPKitJNI.IStream_getId(swigCPtr, this);
192  }
193
194  /**
195   * Get the StreamCoder than can manipulate this stream.<br>
196   * If the stream is an INBOUND stream, then the StreamCoder can<br>
197   * do a IStreamCoder::DECODE.  IF this stream is an OUTBOUND stream,<br>
198   * then the StreamCoder can do all IStreamCoder::ENCODE methods.<br>
199   * <br>
200   * @return The StreamCoder assigned to this object.
201   */
202  public IStreamCoder getStreamCoder() {
203    long cPtr = AVPKitJNI.IStream_getStreamCoder(swigCPtr, this);
204    return (cPtr == 0) ? null : new IStreamCoder(cPtr, false);
205  }
206
207  /**
208   * Get the (sometimes estimated) frame rate of this container.<br>
209   * For variable frame-rate containers (they do exist) this is just<br>
210   * an approimation.  Better to use getTimeBase().<br>
211   * <br>
212   * For contant frame-rate containers, this will be 1 / ( getTimeBase() )<br>
213   * <br>
214   * @return The frame-rate of this container.
215   */
216  public IRational getFrameRate() {
217    long cPtr = AVPKitJNI.IStream_getFrameRate(swigCPtr, this);
218    return (cPtr == 0) ? null : new IRational(cPtr, false);
219  }
220
221  /**
222   * The time base in which all timestamps (e.g. Presentation Time Stamp (PTS)<br>
223   * and Decompression Time Stamp (DTS)) are represented.  For example<br>
224   * if the time base is 1/1000, then the difference between a PTS of 1 and<br>
225   * a PTS of 2 is 1 millisecond.  If the timebase is 1/1, then the difference<br>
226   * between a PTS of 1 and a PTS of 2 is 1 second.<br>
227   * <br>
228   * @return The time base of this stream.
229   */
230  public IRational getTimeBase() {
231    long cPtr = AVPKitJNI.IStream_getTimeBase(swigCPtr, this);
232    return (cPtr == 0) ? null : new IRational(cPtr, false);
233  }
234
235  /**
236   * Return the start time, in {#getTimeBase()} units, when this stream<br>
237   * started.<br>
238   * @return The start time.
239   */
240  public long getStartTime() {
241    return AVPKitJNI.IStream_getStartTime(swigCPtr, this);
242  }
243
244  /**
245   * Return the duration, in {#getTimeBase()} units, of this stream,<br>
246   * or {Global#NO_PTS} if unknown.<br>
247   * @return The duration (in getTimeBase units) of this stream, if known.
248   */
249  public long getDuration() {
250    return AVPKitJNI.IStream_getDuration(swigCPtr, this);
251  }
252
253  /**
254   * The current Decompression Time Stamp that will be used on this stream,<br>
255   * in {#getTimeBase()} units.<br>
256   * @return The current Decompression Time Stamp that will be used on this stream.
257   */
258  public long getCurrentDts() {
259    return AVPKitJNI.IStream_getCurrentDts(swigCPtr, this);
260  }
261
262  /**
263   * Get the number of index entries in this stream.<br>
264   * @return The number of index entries in this stream.<br>
265   * @see #getIndexEntry(int)
266   */
267  public int getNumIndexEntries() {
268    return AVPKitJNI.IStream_getNumIndexEntries(swigCPtr, this);
269  }
270
271  /**
272   * Returns the number of encoded frames if known.  Note that frames here means<br>
273   * encoded frames, which can consist of many encoded audio samples, or<br>
274   * an encoded video frame.<br>
275   * <br>
276   * @return The number of frames (encoded) in this stream.
277   */
278  public long getNumFrames() {
279    return AVPKitJNI.IStream_getNumFrames(swigCPtr, this);
280  }
281
282  /**
283   * Gets the sample aspect ratio.<br>
284   * <br>
285   * @return The sample aspect ratio.
286   */
287  public IRational getSampleAspectRatio() {
288    long cPtr = AVPKitJNI.IStream_getSampleAspectRatio(swigCPtr, this);
289    return (cPtr == 0) ? null : new IRational(cPtr, false);
290  }
291
292  /**
293   * Sets the sample aspect ratio.<br>
294   * <br>
295   * @param newRatio The new ratio.
296   */
297  public void setSampleAspectRatio(IRational newRatio) {
298    AVPKitJNI.IStream_setSampleAspectRatio(swigCPtr, this, IRational.getCPtr(newRatio), newRatio);
299  }
300
301  /**
302   * Get the 4-character language setting for this stream.<br>
303   * <br>
304   * This will return null if no setting.  When calling<br>
305   * from C++, callers must ensure that the IStream outlives the<br>
306   * value returned.
307   */
308  public String getLanguage() {
309    return AVPKitJNI.IStream_getLanguage(swigCPtr, this);
310  }
311
312  /**
313   * Set the 4-character language setting for this stream.<br>
314   * <br>
315   * If a string longer than 4 characters is passed in, only the<br>
316   * first 4 characters is copied.<br>
317   * <br>
318   * @param language The new language setting.  null is equivalent to the<br>
319   *   empty string.  strings longer than 4 characters will be truncated<br>
320   *   to first 4 characters.
321   */
322  public void setLanguage(String language) {
323    AVPKitJNI.IStream_setLanguage(swigCPtr, this, language);
324  }
325
326  /**
327   * Get the underlying container for this stream, or null if AVPKit<br>
328   * doesn't know.<br>
329   * <br>
330   * @return the container, or null if we don't know.
331   */
332  public IContainer getContainer() {
333    long cPtr = AVPKitJNI.IStream_getContainer(swigCPtr, this);
334    return (cPtr == 0) ? null : new IContainer(cPtr, false);
335  }
336
337  /**
338   * Sets the stream coder to use for this stream.<br>
339   * <br>
340   * This method will only cause a change if the IStreamCoder currently set on this<br>
341   * IStream is not open.  Otherwise the call is ignore and an error is returned.<br>
342   * <br>
343   * @param newCoder The new stream coder to use.<br>
344   * @return &gt;= 0 on success; &lt; 0 on error.
345   */
346  public int setStreamCoder(IStreamCoder newCoder) {
347    return AVPKitJNI.IStream_setStreamCoder__SWIG_0(swigCPtr, this, IStreamCoder.getCPtr(newCoder), newCoder);
348  }
349
350  /**
351   * Get how the decoding codec should parse data from this stream.<br>
352   * @return the parse type.<br>
353   * @since 3.0
354   */
355  public IStream.ParseType getParseType() {
356    return IStream.ParseType.swigToEnum(AVPKitJNI.IStream_getParseType(swigCPtr, this));
357  }
358
359  /**
360   * Set the parse type the decoding codec should use.  Set to<br>
361   * {ParseType#PARSE_NONE} if you don't want any parsing<br>
362   * to be done.<br>
363   * <p><br>
364   * Warning: do not set this flag unless you know what you're doing,<br>
365   * and do not set after you've started decoding.<br>
366   * </p><br>
367   * <br>
368   * @param type The type to set.<br>
369   * @since 3.0
370   */
371  public void setParseType(IStream.ParseType type) {
372    AVPKitJNI.IStream_setParseType(swigCPtr, this, type.swigValue());
373  }
374
375  /**
376   * Set a bitstream filter on this stream<br>
377   * <br>
378   * @param name The name of bitstream filter<br>
379   * @since 6.0
380   */
381  public int setBitstreamFilter(String name) {
382    return AVPKitJNI.IStream_setBitstreamFilter(swigCPtr, this, name);
383  }
384
385  /**
386   * Get the {IMetaData} for this object,<br>
387   * or null if none.<br>
388   * <p><br>
389   * If the {IContainer} or {IStream} object<br>
390   * that this {IMetaData} came from was opened<br>
391   * for reading, then changes via {IMetaData#setValue(String, String)}<br>
392   * will have no effect on the underlying media.<br>
393   * </p><br>
394   * <p><br>
395   * If the {IContainer} or {IStream} object<br>
396   * that this {IMetaData} came from was opened<br>
397   * for writing, then changes via {IMetaData#setValue(String, String)}<br>
398   * will have no effect after {IContainer#writeHeader()}<br>
399   * is called.<br>
400   * </p><br>
401   * @return the {IMetaData}.<br>
402   * @since 3.1
403   */
404  public IMetaData getMetaData() {
405    long cPtr = AVPKitJNI.IStream_getMetaData(swigCPtr, this);
406    return (cPtr == 0) ? null : new IMetaData(cPtr, false);
407  }
408
409  /**
410   * Set the {IMetaData} on this object, overriding<br>
411   * any previous meta data.  You should call this<br>
412   * method on writable containers and<br>
413   * before you call {IContainer#writeHeader}, as<br>
414   * it probably won't do anything after that.<br>
415   * <br>
416   * @see #getMetaData()<br>
417   * @since 3.1
418   */
419  public void setMetaData(IMetaData data) {
420    AVPKitJNI.IStream_setMetaData(swigCPtr, this, IMetaData.getCPtr(data), data);
421  }
422
423  /**
424   * Takes a packet destined for this stream, and stamps<br>
425   * the stream index, and converts the time stamp to the<br>
426   * correct units (adjusting for rounding errors between<br>
427   * stream conversions).<br>
428   * <br>
429   * @param packet to stamp<br>
430   * @return &gt;= 0 on success; &lt;0 on failure.<br>
431   * @since 3.2
432   */
433  public int stampOutputPacket(IPacket packet) {
434    return AVPKitJNI.IStream_stampOutputPacket(swigCPtr, this, IPacket.getCPtr(packet), packet);
435  }
436
437  /**
438   * Sets the stream coder to use for this stream.<br>
439   * <br>
440   * This method will only cause a change if the IStreamCoder currently set on this<br>
441   * IStream is not open.  Otherwise the call is ignored and an error is returned.<br>
442   * <br>
443   * @param newCoder The new stream coder to use.<br>
444   * @param assumeOnlyStream If true then this {IStream} will notify the {IStreamCoder} that it is the only stream and the {IStreamCoder} may use it to determine time stamps to output packets with.<br>
445   *   If false then the {IStreamCoder}<br>
446   *   does not support automatic stamping of packets with stream index IDs<br>
447   *   and users must call {#stampOutputPacket(IPacket)} themselves.<br>
448   * @return &gt;= 0 on success; &lt; 0 on error.<br>
449   * @since 3.2
450   */
451  public int setStreamCoder(IStreamCoder newCoder, boolean assumeOnlyStream) {
452    return AVPKitJNI.IStream_setStreamCoder__SWIG_1(swigCPtr, this, IStreamCoder.getCPtr(newCoder), newCoder, assumeOnlyStream);
453  }
454
455  /**
456   * Search for the given time stamp in the key-frame index for this {IStream}.<br>
457   * <p><br>
458   * Not all {IContainerFormat} implementations<br>
459   * maintain key frame indexes, but if they have one,<br>
460   * then this method searches in the {IStream} index<br>
461   * to quickly find the byte-offset of the nearest key-frame to<br>
462   * the given time stamp.<br>
463   * </p><br>
464   * @param wantedTimeStamp the time stamp wanted, in the stream's<br>
465   *                        time base units.<br>
466   * @param flags A bitmask of the <code>SEEK_FLAG_*</code> flags, or 0 to turn<br>
467   *              all flags off.  If {IContainer#SEEK_FLAG_BACKWARDS} then the returned<br>
468   *              index will correspond to the time stamp which is &lt;=<br>
469   *              the requested one (not supported by all demuxers).<br>
470   *              If {IContainer#SEEK_FLAG_BACKWARDS} is not set then it will be &gt;=.<br>
471   *              if {IContainer#SEEK_FLAG_ANY} seek to any frame, only<br>
472   *              keyframes otherwise (not supported by all demuxers).<br>
473   * @return The {IIndexEntry} for the nearest appropriate timestamp<br>
474   *   in the index, or null if it can't be found.<br>
475   * @since 3.4
476   */
477  public IIndexEntry findTimeStampEntryInIndex(long wantedTimeStamp, int flags) {
478    long cPtr = AVPKitJNI.IStream_findTimeStampEntryInIndex(swigCPtr, this, wantedTimeStamp, flags);
479    return (cPtr == 0) ? null : new IIndexEntry(cPtr, false);
480  }
481
482  /**
483   * Search for the given time stamp in the key-frame index for this {IStream}.<br>
484   * <p><br>
485   * Not all {IContainerFormat} implementations<br>
486   * maintain key frame indexes, but if they have one,<br>
487   * then this method searches in the {IStream} index<br>
488   * to quickly find the index entry position of the nearest key-frame to<br>
489   * the given time stamp.<br>
490   * </p><br>
491   * @param wantedTimeStamp the time stamp wanted, in the stream's<br>
492   *                        time base units.<br>
493   * @param flags A bitmask of the <code>SEEK_FLAG_*</code> flags, or 0 to turn<br>
494   *              all flags off.  If {IContainer#SEEK_FLAG_BACKWARDS} then the returned<br>
495   *              index will correspond to the time stamp which is &lt;=<br>
496   *              the requested one (not supported by all demuxers).<br>
497   *              If {IContainer#SEEK_FLAG_BACKWARDS} is not set then it will be &gt;=.<br>
498   *              if {IContainer#SEEK_FLAG_ANY} seek to any frame, only<br>
499   *              keyframes otherwise (not supported by all demuxers).<br>
500   * @return The position in this {IStream} index, or -1 if it cannot<br>
501   *   be found or an index is not maintained.<br>
502   * @see #getIndexEntry(int)<br>
503   * @since 3.4
504   */
505  public int findTimeStampPositionInIndex(long wantedTimeStamp, int flags) {
506    return AVPKitJNI.IStream_findTimeStampPositionInIndex(swigCPtr, this, wantedTimeStamp, flags);
507  }
508
509  /**
510   * Get the {IIndexEntry} at the given position in this<br>
511   * {IStream} object's index.<br>
512   * <p><br>
513   * Not all {IContainerFormat} types maintain<br>
514   * {IStream} indexes, but if they do,<br>
515   * this method can return those entries.<br>
516   * </p><br>
517   * <p><br>
518   * Do not modify the {IContainer} this stream<br>
519   * is from between calls to this method and<br>
520   * {#getNumIndexEntries()} as indexes may<br>
521   * be compacted while processing.<br>
522   * </p><br>
523   * @param position The position in the index table.<br>
524   * @since 3.4
525   */
526  public IIndexEntry getIndexEntry(int position) {
527    long cPtr = AVPKitJNI.IStream_getIndexEntry(swigCPtr, this, position);
528    return (cPtr == 0) ? null : new IIndexEntry(cPtr, false);
529  }
530
531  /**
532   * Adds an index entry into the stream's sorted index list.<br>
533   * Updates the entry if the list<br>
534   * already contains it.<br>
535   * <br>
536   * @param entry The entry to add.<br>
537   * @return &gt;=0 on success; &lt;0 on error.<br>
538   * @since 3.4
539   */
540  public int addIndexEntry(IIndexEntry entry) {
541    return AVPKitJNI.IStream_addIndexEntry(swigCPtr, this, IIndexEntry.getCPtr(entry), entry);
542  }
543
544  /**
545   * Set the format-specific stream id.<br>
546   * <br>
547   * @param id The id to set.<br>
548   * @see #getId()<br>
549   * @since 5.0
550   */
551  public void setId(int id) {
552    AVPKitJNI.IStream_setId(swigCPtr, this, id);
553  }
554
555  /**
556   * The direction this stream is going (based on the container).<br>
557   * <br>
558   * If the container Container is opened in Container::READ mode<br>
559   * then this will be INBOUND.  If it's opened in Container::WRITE<br>
560   * mode, then this will be OUTBOUND.
561   */
562  public enum Direction {
563    INBOUND,
564    OUTBOUND;
565
566    public final int swigValue() {
567      return swigValue;
568    }
569
570    public static Direction swigToEnum(int swigValue) {
571      Direction[] swigValues = Direction.class.getEnumConstants();
572      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
573        return swigValues[swigValue];
574      for (Direction swigEnum : swigValues)
575        if (swigEnum.swigValue == swigValue)
576          return swigEnum;
577      throw new IllegalArgumentException("No enum " + Direction.class + " with value " + swigValue);
578    }
579
580    @SuppressWarnings("unused")
581    private Direction() {
582      this.swigValue = SwigNext.next++;
583    }
584
585    @SuppressWarnings("unused")
586    private Direction(int swigValue) {
587      this.swigValue = swigValue;
588      SwigNext.next = swigValue+1;
589    }
590
591    @SuppressWarnings("unused")
592    private Direction(Direction swigEnum) {
593      this.swigValue = swigEnum.swigValue;
594      SwigNext.next = this.swigValue+1;
595    }
596
597    private final int swigValue;
598
599    private static class SwigNext {
600      private static int next = 0;
601    }
602  }
603
604  /**
605   * What types of parsing can we do on a call to<br>
606   * {IContainer#readNextPacket(IPacket)}
607   */
608  public enum ParseType {
609    PARSE_NONE,
610    /**
611     *  full parsing and repack 
612     */
613    PARSE_FULL,
614    /**
615     *  Only parse headers, do not repack. 
616     */
617    PARSE_HEADERS,
618    /**
619     *  full parsing and interpolation of timestamps for frames not starting on a packet boundary 
620     */
621    PARSE_TIMESTAMPS,
622    /**
623     *  full parsing and repack of the first frame only, only implemented for H.264 currently 
624     */
625    PARSE_FULL_ONCE,
626    /**
627     *  full parsing and repack with timestamp and position generation by parser for raw<br>
628     *                                                              this assumes that each packet in the file contains no demuxer level headers and<br>
629     *                                                              just codec level data, otherwise position generation would fail 
630     */
631    PARSE_FULL_RAW;
632
633    public final int swigValue() {
634      return swigValue;
635    }
636
637    public static ParseType swigToEnum(int swigValue) {
638      ParseType[] swigValues = ParseType.class.getEnumConstants();
639      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
640        return swigValues[swigValue];
641      for (ParseType swigEnum : swigValues)
642        if (swigEnum.swigValue == swigValue)
643          return swigEnum;
644      throw new IllegalArgumentException("No enum " + ParseType.class + " with value " + swigValue);
645    }
646
647    @SuppressWarnings("unused")
648    private ParseType() {
649      this.swigValue = SwigNext.next++;
650    }
651
652    @SuppressWarnings("unused")
653    private ParseType(int swigValue) {
654      this.swigValue = swigValue;
655      SwigNext.next = swigValue+1;
656    }
657
658    @SuppressWarnings("unused")
659    private ParseType(ParseType swigEnum) {
660      this.swigValue = swigEnum.swigValue;
661      SwigNext.next = this.swigValue+1;
662    }
663
664    private final int swigValue;
665
666    private static class SwigNext {
667      private static int next = 0;
668    }
669  }
670
671}