ใช้สตรีมจากกล้องหลายตัวพร้อมกัน

หมายเหตุ: หน้านี้อ้างอิงถึงแพ็กเกจ Camera2 เราขอแนะนำให้ใช้ CameraX เว้นแต่แอปของคุณจะต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป

แอปพลิเคชันกล้องสามารถใช้สตรีมเฟรมมากกว่า 1 รายการพร้อมกันได้ ในบางกรณี สตรีมที่แตกต่างกันอาจต้องใช้ความละเอียดของเฟรมหรือรูปแบบพิกเซลที่แตกต่างกันด้วย กรณีการใช้งานทั่วไปบางส่วนมีดังนี้

  • การบันทึกวิดีโอ: สตรีมหนึ่งสำหรับการแสดงตัวอย่าง อีกสตรีมหนึ่งจะได้รับการเข้ารหัสและบันทึก ลงในไฟล์
  • การสแกนบาร์โค้ด: สตรีมหนึ่งสำหรับแสดงตัวอย่าง อีกสตรีมหนึ่งสำหรับการตรวจหาบาร์โค้ด
  • การถ่ายภาพคอมพิวเตอร์: สตรีมหนึ่งสำหรับแสดงตัวอย่าง อีกสตรีมหนึ่งสำหรับการตรวจหาใบหน้า/ฉาก

การประมวลผลเฟรมมีต้นทุนด้านประสิทธิภาพที่สำคัญ และต้นทุนจะเพิ่มขึ้นเมื่อประมวลผลสตรีมหรือไปป์ไลน์แบบขนาน

ทรัพยากรต่างๆ เช่น CPU, GPU และ DSP อาจใช้ประโยชน์จากความสามารถในการประมวลผลซ้ำของเฟรมเวิร์กได้ แต่ทรัพยากรอย่างหน่วยความจำจะเพิ่มขึ้นแบบเชิงเส้น

เป้าหมายหลายรายการต่อคำขอ

คุณรวมสตรีมกล้องหลายรายการเป็นCameraCaptureRequestเดียวได้ ข้อมูลโค้ดต่อไปนี้แสดงวิธีตั้งค่าเซสชันกล้องที่มีสตรีม 1 รายการสำหรับตัวอย่างกล้องและอีกสตรีมหนึ่งสำหรับการประมวลผลรูปภาพ

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback

// You will use the preview capture template for the combined streams
// because it is optimized for low latency; for high-quality images, use
// TEMPLATE_STILL_CAPTURE, and for a steady frame rate use TEMPLATE_RECORD
val requestTemplate = CameraDevice.TEMPLATE_PREVIEW
val combinedRequest = session.device.createCaptureRequest(requestTemplate)

// Link the Surface targets with the combined request
combinedRequest.addTarget(previewSurface)
combinedRequest.addTarget(imReaderSurface)

// In this simple case, the SurfaceView gets updated automatically. ImageReader
// has its own callback that you have to listen to in order to retrieve the
// frames so there is no need to set up a callback for the capture request
session.setRepeatingRequest(combinedRequest.build(), null, null)

Java

CameraCaptureSession session = ;  // from CameraCaptureSession.StateCallback

// You will use the preview capture template for the combined streams
// because it is optimized for low latency; for high-quality images, use
// TEMPLATE_STILL_CAPTURE, and for a steady frame rate use TEMPLATE_RECORD
        CaptureRequest.Builder combinedRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

// Link the Surface targets with the combined request
        combinedRequest.addTarget(previewSurface);
        combinedRequest.addTarget(imReaderSurface);

// In this simple case, the SurfaceView gets updated automatically. ImageReader
// has its own callback that you have to listen to in order to retrieve the
// frames so there is no need to set up a callback for the capture request
        session.setRepeatingRequest(combinedRequest.build(), null, null);

หากกำหนดค่าแพลตฟอร์มเป้าหมายอย่างถูกต้อง โค้ดนี้จะสร้างเฉพาะสตรีมที่ตรงตาม FPS ขั้นต่ำที่กำหนดโดย StreamComfigurationMap.GetOutputMinFrameDuration(int, Size) และ StreamComfigurationMap.GetOutputStallDuration(int, Size) ประสิทธิภาพจริงจะแตกต่างกันไปในแต่ละอุปกรณ์ แต่ Android มีการรับประกันบางอย่าง สำหรับการรองรับการผสมผสานที่เฉพาะเจาะจงโดยขึ้นอยู่กับ 3 ตัวแปร ได้แก่ ประเภทเอาต์พุต ขนาดเอาต์พุต และระดับฮาร์ดแวร์

การใช้ชุดตัวแปรที่ไม่รองรับอาจทำงานที่อัตราเฟรมต่ำ หากไม่ทำงาน ระบบจะทริกเกอร์หนึ่งในโค้ดเรียกกลับที่ล้มเหลว เอกสารประกอบสำหรับ createCaptureSession อธิบายสิ่งที่รับประกันว่าจะใช้งานได้

ประเภทเอาต์พุต

ประเภทเอาต์พุตหมายถึงรูปแบบที่เข้ารหัสเฟรม ค่าที่เป็นไปได้คือ PRIV, YUV, JPEG และ RAW เอกสารประกอบสำหรับ createCaptureSession จะอธิบายถึงฟีเจอร์เหล่านี้

เมื่อเลือกประเภทเอาต์พุตของแอปพลิเคชัน หากเป้าหมายคือการเพิ่มความ เข้ากันได้ ให้ใช้ ImageFormat.YUV_420_888 สําหรับการวิเคราะห์เฟรมและ ImageFormat.JPEGสําหรับภาพ นิ่ง สำหรับสถานการณ์การแสดงตัวอย่างและการบันทึก คุณอาจใช้ SurfaceView TextureView MediaRecorder MediaCodec หรือ RenderScript.Allocation ใน กรณีดังกล่าว ให้ไม่ต้องระบุรูปแบบรูปภาพ เพื่อความเข้ากันได้ ระบบจะนับเป็น ImageFormat.PRIVATE ไม่ว่ารูปแบบจริงที่ใช้ภายในจะเป็นอย่างไร หากต้องการค้นหารูปแบบที่อุปกรณ์รองรับ เมื่อระบุCameraCharacteristics ให้ใช้โค้ดต่อไปนี้

Kotlin

val characteristics: CameraCharacteristics = ...
val supportedFormats = characteristics.get(
    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).outputFormats

Java

CameraCharacteristics characteristics = ;
        int[] supportedFormats = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputFormats();

ขนาดเอาต์พุต

ขนาดเอาต์พุตทั้งหมดที่มีจะแสดงตาม StreamConfigurationMap.getOutputSizes() แต่มีเพียง 2 ขนาดเท่านั้นที่เกี่ยวข้องกับความเข้ากันได้ ได้แก่ PREVIEW และ MAXIMUM ขนาด ทำหน้าที่เป็นขอบเขตบน หากมีขนาด PREVIEW ใช้งานได้ แสดงว่ารายการที่มีขนาดเล็กกว่า PREVIEW ก็จะใช้งานได้เช่นกัน MAXIMUM ก็เช่นกัน เอกสารประกอบสำหรับ CameraDevice อธิบายขนาดเหล่านี้

ขนาดเอาต์พุตที่ใช้ได้จะขึ้นอยู่กับรูปแบบที่เลือก เมื่อระบุ CameraCharacteristics และรูปแบบ คุณจะค้นหาขนาดเอาต์พุตที่ใช้ได้ดังนี้

Kotlin

val characteristics: CameraCharacteristics = ...
val outputFormat: Int = ...  // such as ImageFormat.JPEG
val sizes = characteristics.get(
    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
    .getOutputSizes(outputFormat)

Java

CameraCharacteristics characteristics = ;
        int outputFormat = ;  // such as ImageFormat.JPEG
Size[] sizes = characteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                .getOutputSizes(outputFormat);

ในกรณีการใช้งานการแสดงตัวอย่างและการบันทึกของกล้อง ให้ใช้คลาสเป้าหมายเพื่อกำหนดขนาดที่รองรับ เฟรมเวิร์กของกล้องจะจัดการรูปแบบเอง

Kotlin

val characteristics: CameraCharacteristics = ...
val targetClass: Class <T> = ...  // such as SurfaceView::class.java
val sizes = characteristics.get(
    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
    .getOutputSizes(targetClass)

Java

CameraCharacteristics characteristics = ;
   int outputFormat = ;  // such as ImageFormat.JPEG
   Size[] sizes = characteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                .getOutputSizes(outputFormat);

หากต้องการรับขนาด MAXIMUM ให้จัดเรียงขนาดเอาต์พุตตามพื้นที่และส่งคืนขนาดที่ใหญ่ที่สุด

Kotlin

fun <T>getMaximumOutputSize(
    characteristics: CameraCharacteristics, targetClass: Class <T>, format: Int? = null):
    Size {
  val config = characteristics.get(
      CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)

  // If image format is provided, use it to determine supported sizes; or else use target class
  val allSizes = if (format == null)
    config.getOutputSizes(targetClass) else config.getOutputSizes(format)
  return allSizes.maxBy { it.height * it.width }
}

Java

 @RequiresApi(api = Build.VERSION_CODES.N)
    <T> Size getMaximumOutputSize(CameraCharacteristics characteristics,
                                            Class <T> targetClass,
                                            Integer format) {
        StreamConfigurationMap config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

        // If image format is provided, use it to determine supported sizes; else use target class
        Size[] allSizes;
        if (format == null) {
            allSizes = config.getOutputSizes(targetClass);
        } else {
            allSizes = config.getOutputSizes(format);
        }
        return Arrays.stream(allSizes).max(Comparator.comparing(s -> s.getHeight() * s.getWidth())).get();
    }

PREVIEW หมายถึงขนาดที่ตรงกับความละเอียดหน้าจอของอุปกรณ์หรือ 1080p (1920x1080) มากที่สุด ไม่ว่าขนาดใดจะเล็กกว่า สัดส่วนภาพอาจไม่ตรงกับสัดส่วนภาพของหน้าจอพอดี คุณจึงอาจต้องใช้การแสดงภาพแบบจอกว้างหรือการครอบตัดกับสตรีมเพื่อแสดงในโหมดเต็มหน้าจอ หากต้องการดูตัวอย่างขนาดที่ถูกต้อง ให้เปรียบเทียบขนาดเอาต์พุตที่มีกับขนาดการแสดงผลโดย คำนึงถึงว่าอาจมีการหมุนจอแสดงผล

โค้ดต่อไปนี้กำหนดคลาสตัวช่วย SmartSize ซึ่งจะช่วยให้การเปรียบเทียบขนาด ง่ายขึ้นเล็กน้อย

Kotlin

/** Helper class used to pre-compute shortest and longest sides of a [Size] */
class SmartSize(width: Int, height: Int) {
    var size = Size(width, height)
    var long = max(size.width, size.height)
    var short = min(size.width, size.height)
    override fun toString() = "SmartSize(${long}x${short})"
}

/** Standard High Definition size for pictures and video */
val SIZE_1080P: SmartSize = SmartSize(1920, 1080)

/** Returns a [SmartSize] object for the given [Display] */
fun getDisplaySmartSize(display: Display): SmartSize {
    val outPoint = Point()
    display.getRealSize(outPoint)
    return SmartSize(outPoint.x, outPoint.y)
}

/**
 * Returns the largest available PREVIEW size. For more information, see:
 * https://d.android.com/reference/android/hardware/camera2/CameraDevice
 */
fun <T>getPreviewOutputSize(
        display: Display,
        characteristics: CameraCharacteristics,
        targetClass: Class <T>,
        format: Int? = null
): Size {

    // Find which is smaller: screen or 1080p
    val screenSize = getDisplaySmartSize(display)
    val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
    val maxSize = if (hdScreen) SIZE_1080P else screenSize

    // If image format is provided, use it to determine supported sizes; else use target class
    val config = characteristics.get(
            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
    if (format == null)
        assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
    else
        assert(config.isOutputSupportedFor(format))
    val allSizes = if (format == null)
        config.getOutputSizes(targetClass) else config.getOutputSizes(format)

    // Get available sizes and sort them by area from largest to smallest
    val validSizes = allSizes
            .sortedWith(compareBy { it.height * it.width })
            .map { SmartSize(it.width, it.height) }.reversed()

    // Then, get the largest output size that is smaller or equal than our max size
    return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
}

Java

/** Helper class used to pre-compute shortest and longest sides of a [Size] */
    class SmartSize {
        Size size;
        double longSize;
        double shortSize;

        public SmartSize(Integer width, Integer height) {
            size = new Size(width, height);
            longSize = max(size.getWidth(), size.getHeight());
            shortSize = min(size.getWidth(), size.getHeight());
        }

        @Override
        public String toString() {
            return String.format("SmartSize(%sx%s)", longSize, shortSize);
        }
    }

    /** Standard High Definition size for pictures and video */
    SmartSize SIZE_1080P = new SmartSize(1920, 1080);

    /** Returns a [SmartSize] object for the given [Display] */
    SmartSize getDisplaySmartSize(Display display) {
        Point outPoint = new Point();
        display.getRealSize(outPoint);
        return new SmartSize(outPoint.x, outPoint.y);
    }

    /**
     * Returns the largest available PREVIEW size. For more information, see:
     * https://d.android.com/reference/android/hardware/camera2/CameraDevice
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    <T> Size getPreviewOutputSize(
            Display display,
            CameraCharacteristics characteristics,
            Class <T> targetClass,
            Integer format
    ){

        // Find which is smaller: screen or 1080p
        SmartSize screenSize = getDisplaySmartSize(display);
        boolean hdScreen = screenSize.longSize >= SIZE_1080P.longSize || screenSize.shortSize >= SIZE_1080P.shortSize;
        SmartSize maxSize;
        if (hdScreen) {
            maxSize = SIZE_1080P;
        } else {
            maxSize = screenSize;
        }

        // If image format is provided, use it to determine supported sizes; else use target class
        StreamConfigurationMap config = characteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        if (format == null)
            assert(StreamConfigurationMap.isOutputSupportedFor(targetClass));
        else
            assert(config.isOutputSupportedFor(format));
        Size[] allSizes;
        if (format == null) {
            allSizes = config.getOutputSizes(targetClass);
        } else {
            allSizes = config.getOutputSizes(format);
        }

        // Get available sizes and sort them by area from largest to smallest
        List <Size> sortedSizes = Arrays.asList(allSizes);
        List <SmartSize> validSizes =
                sortedSizes.stream()
                        .sorted(Comparator.comparing(s -> s.getHeight() * s.getWidth()))
                        .map(s -> new SmartSize(s.getWidth(), s.getHeight()))
                        .sorted(Collections.reverseOrder()).collect(Collectors.toList());

        // Then, get the largest output size that is smaller or equal than our max size
        return validSizes.stream()
                .filter(s -> s.longSize <= maxSize.longSize && s.shortSize <= maxSize.shortSize)
                .findFirst().get().size;
    }

ตรวจสอบระดับฮาร์ดแวร์ที่รองรับ

หากต้องการตรวจสอบความสามารถที่พร้อมใช้งานขณะรันไทม์ ให้ตรวจสอบระดับฮาร์ดแวร์ที่รองรับโดยใช้ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL

ออบเจ็กต์ CameraCharacteristics ช่วยให้คุณดึงข้อมูลระดับฮาร์ดแวร์ได้ด้วยคำสั่งเดียว

Kotlin

val characteristics: CameraCharacteristics = ...

// Hardware level will be one of:
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
val hardwareLevel = characteristics.get(
        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)

Java

CameraCharacteristics characteristics = ...;

// Hardware level will be one of:
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
// - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
Integer hardwareLevel = characteristics.get(
                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);

การรวมทุกอย่างเข้าด้วยกัน

ประเภทเอาต์พุต ขนาดเอาต์พุต และระดับฮาร์ดแวร์จะช่วยให้คุณระบุได้ว่าการ ผสมสตรีมใดที่ใช้ได้ แผนภูมิต่อไปนี้เป็นภาพรวมของ การกำหนดค่าที่ CameraDevice รองรับที่ระดับฮาร์ดแวร์ LEGACY

เป้าหมาย 1 เป้าหมาย 2 เป้าหมาย 3 กรณีการใช้งานตัวอย่าง
ประเภท ขนาดสูงสุด ประเภท ขนาดสูงสุด ประเภท ขนาดสูงสุด
PRIV MAXIMUM การแสดงตัวอย่างแบบง่าย การประมวลผลวิดีโอด้วย GPU หรือการบันทึกวิดีโอแบบไม่มีการแสดงตัวอย่าง
JPEG MAXIMUM การจับภาพนิ่งโดยไม่มีช่องมองภาพ
YUV MAXIMUM การประมวลผลวิดีโอ/รูปภาพในแอปพลิเคชัน
PRIV PREVIEW JPEG MAXIMUM การถ่ายภาพนิ่งมาตรฐาน
YUV PREVIEW JPEG MAXIMUM การประมวลผลในแอปและการจับภาพยังคงทำงาน
PRIV PREVIEW PRIV PREVIEW การบันทึกมาตรฐาน
PRIV PREVIEW YUV PREVIEW ดูตัวอย่างและประมวลผลในแอป
PRIV PREVIEW YUV PREVIEW JPEG MAXIMUM ยังคงจับภาพและประมวลผลในแอป

LEGACY คือระดับฮาร์ดแวร์ที่ต่ำที่สุดที่เป็นไปได้ ตารางนี้แสดงให้เห็นว่าอุปกรณ์ทุกเครื่องที่รองรับ Camera2 (API ระดับ 21 ขึ้นไป) สามารถส่งออกสตรีมพร้อมกันได้สูงสุด 3 รายการโดยใช้การกำหนดค่าที่เหมาะสม และหากไม่มีค่าใช้จ่ายมากเกินไปที่จำกัดประสิทธิภาพ เช่น ข้อจำกัดด้านหน่วยความจำ, CPU หรือความร้อน

นอกจากนี้ แอปยังต้องกำหนดค่าบัฟเฟอร์เอาต์พุตการกำหนดเป้าหมายด้วย เช่น หากต้องการ กำหนดเป้าหมายอุปกรณ์ที่มีฮาร์ดแวร์ระดับ LEGACY คุณสามารถตั้งค่าเอาต์พุตเป้าหมาย 2 รายการ โดยรายการหนึ่งใช้ ImageFormat.PRIVATE และอีกรายการใช้ ImageFormat.YUV_420_888 นี่คือการผสมผสานที่รองรับขณะใช้ PREVIEW ขนาด การใช้ฟังก์ชันที่กำหนดไว้ก่อนหน้านี้ในหัวข้อนี้ การรับ ขนาดตัวอย่างที่จำเป็นสำหรับรหัสกล้องต้องใช้โค้ดต่อไปนี้

Kotlin

val characteristics: CameraCharacteristics = ...
val context = this as Context  // assuming you are inside of an activity

val surfaceViewSize = getPreviewOutputSize(
    context, characteristics, SurfaceView::class.java)
val imageReaderSize = getPreviewOutputSize(
    context, characteristics, ImageReader::class.java, format = ImageFormat.YUV_420_888)

Java

CameraCharacteristics characteristics = ...;
        Context context = this; // assuming you are inside of an activity

        Size surfaceViewSize = getPreviewOutputSize(
                context, characteristics, SurfaceView.class);
        Size imageReaderSize = getPreviewOutputSize(
                context, characteristics, ImageReader.class, format = ImageFormat.YUV_420_888);

โดยต้องรอจนกว่า SurfaceView จะพร้อมใช้งานโดยใช้การเรียกกลับที่ระบุ

Kotlin

val surfaceView = findViewById <SurfaceView>(...)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
  override fun surfaceCreated(holder: SurfaceHolder) {
    // You do not need to specify image format, and it will be considered of type PRIV
    // Surface is now ready and you could use it as an output target for CameraSession
  }
  ...
})

Java

SurfaceView surfaceView = findViewById <SurfaceView>(...);

surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
                // You do not need to specify image format, and it will be considered of type PRIV
                // Surface is now ready and you could use it as an output target for CameraSession
            }
            ...
        });

คุณสามารถบังคับให้ SurfaceView ตรงกับขนาดเอาต์พุตของกล้องได้โดยเรียกใช้ SurfaceHolder.setFixedSize() หรือจะใช้วิธีการที่คล้ายกับ AutoFitSurfaceView จากโมดูล Common ของตัวอย่างกล้องใน GitHub ก็ได้ ซึ่งจะกำหนดขนาดที่แน่นอนโดยคำนึงถึงทั้งสัดส่วนภาพและพื้นที่ว่าง พร้อมทั้งปรับโดยอัตโนมัติเมื่อมีการเปลี่ยนแปลงกิจกรรม

การตั้งค่าแพลตฟอร์มอื่นๆ จาก ImageReader ด้วยรูปแบบที่ต้องการจะ ง่ายกว่า เนื่องจากไม่ต้องรอการเรียกกลับ

Kotlin

val frameBufferCount = 3  // just an example, depends on your usage of ImageReader
val imageReader = ImageReader.newInstance(
    imageReaderSize.width, imageReaderSize.height, ImageFormat.YUV_420_888,
    frameBufferCount)

Java

int frameBufferCount = 3;  // just an example, depends on your usage of ImageReader
ImageReader imageReader = ImageReader.newInstance(
                imageReaderSize.width, imageReaderSize.height, ImageFormat.YUV_420_888,
                frameBufferCount);

เมื่อใช้บัฟเฟอร์เป้าหมายการบล็อก เช่น ImageReader ให้ทิ้งเฟรมหลังจากใช้แล้ว

Kotlin

imageReader.setOnImageAvailableListener({
  val frame =  it.acquireNextImage()
  // Do something with "frame" here
  it.close()
}, null)

Java

imageReader.setOnImageAvailableListener(listener -> {
            Image frame = listener.acquireNextImage();
            // Do something with "frame" here
            listener.close();
        }, null);

LEGACYระดับฮาร์ดแวร์จะกำหนดเป้าหมายไปยังอุปกรณ์ที่มีข้อกำหนดจำนวนหารร่วมน้อย คุณสามารถ เพิ่มการแตกแขนงแบบมีเงื่อนไขและใช้ขนาด RECORD สำหรับพื้นผิวเป้าหมายเอาต์พุตรายการใดรายการหนึ่ง ในอุปกรณ์ที่มีระดับฮาร์ดแวร์ LIMITED หรือจะเพิ่มเป็นขนาด MAXIMUM สำหรับอุปกรณ์ที่มีระดับฮาร์ดแวร์ FULL ก็ได้