Package limn.video

Class VideoFrame.Writer

java.lang.Object
limn.video.VideoFrame.Writer
Enclosing class:
VideoFrame

public static final class VideoFrame.Writer extends Object
A producer's handle on one pooled frame: the only thing that can point a frame at memory or publish it. A pool builds one writer per slot at startup and keeps it; a consumer is handed the frame alone and therefore cannot retarget a frame it does not own, which is a property of the type rather than a comment asking nicely.

A nested class rather than package-private methods on purpose: producers live in other packages and other modules, and package-private access across a module boundary requires a split package, which fails as soon as anything declares a module descriptor. A nested class reaches the frame's private state from anywhere.

One thread per slot: only the thread that owns the pool calls these. The steady-state path is setPtsMicros(long) then publish(): two primitive writes, nothing created. configure(int, int, limn.video.PixelFormat, limn.video.VideoColor) and setPlane(int, java.nio.ByteBuffer, int) are for pool construction and for a format change; calling them per picture is the mistake that reintroduces per-frame allocation.

  • Method Details

    • allocate

      public static VideoFrame.Writer allocate(int slot, VideoFrame.Recycler recycler)
      Builds one pooled frame and the writer that publishes it. Pool construction only.
      Parameters:
      slot - this frame's index in the pool, at least 0
      recycler - where VideoFrame.release() returns the memory
      Throws:
      IllegalArgumentException - if slot is negative
      NullPointerException - if recycler is null
    • frame

      public VideoFrame frame()
      Returns:
      the frame this writer publishes, the same instance for the pool's lifetime
    • configure

      public void configure(int width, int height, PixelFormat format, VideoColor color)
      Sets geometry and interpretation and invalidates every plane binding, so setPlane(int, java.nio.ByteBuffer, int) must be called for all of the format's planes before the next publish().
      Parameters:
      width - visible width in pixels, in [1..PixelFormat.MAX_DIMENSION]
      height - visible height in pixels, in the same range
      Throws:
      IllegalArgumentException - if either dimension is outside that range
      IllegalStateException - if the frame is currently published
      NullPointerException - if format or color is null
    • setPlane

      public void setPlane(int plane, ByteBuffer buffer, int strideBytes)
      Points plane at buffer, whose rows are strideBytes apart. The buffer is stored as a read-only view; its capacity must be at least the plane's minimum byte count for the configured size and this stride, and may be more; a frame cropped out of a larger coded picture is expressed exactly this way, by slicing each plane so byte 0 is the first visible sample and leaving the stride at the coded width.

      Direct or heap: the toolkit reads either. A consumer that uploads to a device may require direct, which is that consumer's precondition and not this one's.

      Parameters:
      strideBytes - bytes between the starts of consecutive rows; rows run top-down, so a producer holding bottom-up rows flips on its own side
      Throws:
      IllegalArgumentException - if the stride is below the plane's byte width, or the buffer is smaller than the plane needs
      IllegalStateException - if the frame is currently published (except inside a VideoFrame.Downloader, which is the one moment a producer may re-point a held frame), or if no size has been configured yet
      IndexOutOfBoundsException - if plane is not a plane of the configured format
      NullPointerException - if buffer is null
    • setPtsMicros

      public void setPtsMicros(long ptsMicros)
      Sets the presentation time in microseconds from the start of the stream, or VideoFrame.PTS_UNKNOWN when the producer has no timing at all. Survives VideoFrame.release(), so a producer that does not set it per picture republishes the previous one.
    • setHandle

      public void setHandle(VideoFrame.Kind kind, long handle)
      Makes the next publish() a device picture rather than a planar one: this frame will carry handle, VideoFrame.plane(int) will refuse, and every plane binding is dropped.

      The geometry and the PixelFormat still describe the picture (a VideoToolbox NV12 surface is NV12, and the layout is what a consumer needs to bind or convert it), but the samples are not in memory this side can address.

      Set per picture, unlike setPlane(int, java.nio.ByteBuffer, int): a decoder's pool hands out a different allocation each time, and a handle is one field rather than a read-only view, so writing it per picture allocates nothing.

      Parameters:
      kind - which family of handle this is; not VideoFrame.Kind.PLANAR
      handle - the device allocation, not 0
      Throws:
      IllegalArgumentException - if kind is VideoFrame.Kind.PLANAR or handle is 0
      IllegalStateException - if the frame is currently published
      NullPointerException - if kind is null
    • setDownloader

      public void setDownloader(VideoFrame.Downloader downloader)
      Installs the producer's way of reading one of its device pictures back into memory, for the consumers that cannot use a handle. Pool construction; a producer that publishes only planar pictures never calls it, and VideoFrame.toPlanar() then refuses rather than pretending.
      Parameters:
      downloader - what VideoFrame.toPlanar() calls; null removes it
    • downloaded

      public void downloaded()
      Ends a VideoFrame.Downloader's work: the planes it has just bound become the picture, and this frame stops being handle-backed for the rest of the lease the consumer is holding.
      Throws:
      IllegalStateException - if called outside a download, or a plane is still unbound
    • publish

      public VideoFrame publish()
      Rewinds every plane view, marks the frame held and returns it for handoff. The generation counter is written last and is written volatile, so a consumer that reads VideoFrame.generation() before anything else sees a fully initialized frame even when the handoff itself carries no ordering of its own.
      Returns:
      the frame, now held by whoever receives it
      Throws:
      IllegalStateException - if the frame is already published, or carries neither every plane of its format nor a device handle