//
//  CaptureView.m
//  AVCapture2
//
//  Created by magewell on 2025/8/20.
//  Copyright © 2025 magewell. All rights reserved.
//

#import "CaptureView.h"
#import "CaptureUtil.h"
#import "CaptureFormatInfo.h"

static void onVideoCaptureCallback(BYTE *pBuffer, long bufferLen, long stride, void* pParam) {
    CaptureView *viewCtrl = (__bridge CaptureView*)pParam;
    [viewCtrl onVideoCaptureFrame:pBuffer bufferLen:bufferLen stride:stride];
}

static void onAudioCaptureCallback(const BYTE * pbFrame, int cbFrame, uint64_t u64TimeStamp, void* pParam) {
    CaptureView *viewCtrl = (__bridge CaptureView*)pParam;
    [viewCtrl onAudioCaptureFrame:(BYTE *)pbFrame cbFrame:cbFrame u64TimeStamp:u64TimeStamp];
}

@implementation CaptureView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    
    // Drawing code here.
}

- (id)initWithFrame:(NSRect)frameRect lineNumber:(int)lineNumber {
    if (self = [super initWithFrame:frameRect]) {
        self.lineNumber = lineNumber;
        MWCaptureInitInstance();
        MWRefreshDevice();
        
        //base UI
        self.statusLabel = [[NSTextField alloc] initWithFrame:CGRectMake(0, 0, frameRect.size.width, 20)];
        self.statusLabel.textColor = [NSColor blackColor];
        self.statusLabel.layer.backgroundColor = [NSColor whiteColor].CGColor;
        [self addSubview:self.statusLabel];
        self.statusLabel.enabled = NO;
        self.statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
        NSLayoutConstraint *constraintWidth = [NSLayoutConstraint constraintWithItem:self.statusLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
        [self addConstraint:constraintWidth];
        
        [self performSelector:@selector(createPreviewLayer) withObject:nil afterDelay:0];
        
        NSMenu *mainMenu = [NSApp mainMenu];
        
        NSMenuItem *mainMenuDeviceItem = [[NSMenuItem alloc] init];
        [mainMenu addItem:mainMenuDeviceItem];
        self.deviceMenu = [[NSMenu alloc] initWithTitle:[NSString stringWithFormat:@"Device(%d)",lineNumber]];
        [mainMenuDeviceItem setSubmenu:self.deviceMenu];
        
        NSMenuItem *mainMenuResolutionsItem = [[NSMenuItem alloc] init];
        [mainMenu addItem:mainMenuResolutionsItem];
        self.resolutionsMenu = [[NSMenu alloc] initWithTitle:@"Resolution"];
        [mainMenuResolutionsItem setSubmenu:self.resolutionsMenu];
        
        NSMenuItem *mainFPSItem = [[NSMenuItem alloc] init];
        [mainMenu addItem:mainFPSItem];
        self.fpsMenu = [[NSMenu alloc] initWithTitle:@"FPS"];
        [mainFPSItem setSubmenu:self.fpsMenu];
        
        NSMenuItem *mainFourccItem = [[NSMenuItem alloc] init];
        [mainMenu addItem:mainFourccItem];
        self.fourccMenu = [[NSMenu alloc] initWithTitle:@"Color Format"];
        [mainFourccItem setSubmenu:self.fourccMenu];
        
        NSMenuItem *mainMenuAudioItem = [[NSMenuItem alloc] init];
        [mainMenu addItem:mainMenuAudioItem];
        self.audioMenu = [[NSMenu alloc] initWithTitle:@"Audio"];
        [mainMenuAudioItem setSubmenu:self.audioMenu];

        [self initParameters];
        [self initDeviceMenu];
        
        if (self.deviceMenu.numberOfItems > 0) {
            [self performSelector:@selector(deviceItemAction:) withObject:[self.deviceMenu itemAtIndex:0] afterDelay:0];
        }
        
        self.statusTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target: self selector:@selector(statusTimerProc:) userInfo:nil repeats:YES];
        
        
    }
    return self;
}

- (void)initParameters {
    self.width         = 1920;
    self.height        = 1080;
    self.frameDuration = 166667;// 333334
    self.fourcc        = MWFOURCC_NV12; //such as MWFOURCC_BGRA、MWFOURCC_ARGB、MWFOURCC_YUYV、MWFOURCC_UYVY、 MWFOURCC_NV12、 MWFOURCC_I420
    
    self.samplerate    = 48000;
    self.audioChannels = 2;
    self.bitsPerSample = 16;
    
    self.audioRender   = NULL;
    self.audioCapture  = NULL;
    self.videoCapture  = NULL;
    
    self.captureFps      = 0.0;
    self.lastTimestamp   = 0;
    self.videoFrameCount = 0;
    
    self.pixelFormat = fourcc_to_CVPixelFormat(self.fourcc);
}

- (void)initDeviceMenu {
    int count = MWGetChannelCount();
    for (int i = 0; i < count; i++) {
        NSString *itemTitle = nil;
        
        MWCAP_CHANNEL_INFO mci = { 0 };
        MWGetChannelInfoByIndex(i, &mci);
        if (strcmp(mci.szFamilyName, "USB Capture") == 0) {
            itemTitle = [NSString stringWithFormat:@"%s(%s)", mci.szProductName, mci.szBoardSerialNo];
        } else {
            itemTitle = [NSString stringWithFormat:@"%02d-%d %s", mci.byBoardIndex, mci.byChannelIndex, mci.szProductName];
        }
        
        NSMenuItem *deviceItem = [self.deviceMenu addItemWithTitle:itemTitle action:@selector(deviceItemAction:) keyEquivalent:@""];
        [deviceItem setTag:i];
        [deviceItem setTarget:self];
    }
}

- (void)initResolutionsMenu:(HCHANNEL) hChannel
{
    [self.resolutionsMenu removeAllItems];
    self.suportCaptureFormatList = [[NSMutableArray alloc] init];
    if (!hChannel) {
        return;
    }
    bool resolutionFound = false;
    MWCAP_VIDEO_RESOLUTION resolutions[256] = { 0 };
    MWCAP_VIDEO_RESOLUTION_LIST resList = { sizeof(resolutions)/sizeof(resolutions[0]), resolutions};
    MWGetVideoCaptureSupportListResolution(hChannel, &resList);
    
    NSMenuItem *autoItem = [self.resolutionsMenu addItemWithTitle:@"continuousAutoSwitch" action:@selector(switchCaptureFormat:) keyEquivalent:@""];
    [autoItem setTarget:self];
    [autoItem setTag:-1];
    
    for(int i=0; i<resList.nListSize; i++) {
        printf("INFO: Line%d support resolution[%d x %d]\n", self.lineNumber, resolutions[i].cx, resolutions[i].cy);
        CaptureFormatInfo *info = [[CaptureFormatInfo alloc] initWithWidth:resolutions[i].cx height:resolutions[i].cy];
        [self.suportCaptureFormatList addObject:info];
        NSString *itemTitle = [NSString stringWithFormat:@"%d * %d", resolutions[i].cx, resolutions[i].cy];
        NSMenuItem *resolutionItem = [self.resolutionsMenu addItemWithTitle:itemTitle action:@selector(switchCaptureFormat:) keyEquivalent:@""];
        [resolutionItem setTarget:self];
        [resolutionItem setTag:i];
        if (resolutions[i].cx == self.width && resolutions[i].cy == self.height) {
            [resolutionItem setState:NSControlStateValueOn];
            self.selectedResolutionItem = resolutionItem;
        } else {
            [resolutionItem setState:NSControlStateValueOff];
        }
    }
}

- (void)initFpsMenu
{
    [self.fpsMenu removeAllItems];
    for(int i=0; i<3; i++) {
        int fps = 15*pow(2, i);
        NSString *itemTitle = [NSString stringWithFormat:@"%d", fps];
        NSMenuItem *fpsItem = [self.fpsMenu addItemWithTitle:itemTitle action:@selector(switchFP:) keyEquivalent:@""];
        [fpsItem setTarget:self];
        int frameDuration = (int32_t)(round)(10000000/(float)fps);
        [fpsItem setTag:frameDuration];

        if (self.frameDuration == frameDuration) {
            [fpsItem setState:NSControlStateValueOn];
            self.selectedFpsItem = fpsItem;
        } else {
            [fpsItem setState:NSControlStateValueOff];
        }
        printf("INFO: Line %d support FPS[%d]\n", self.lineNumber, fps);

    }
}

- (void)initColorFormatMenu:(HCHANNEL)hChannel
{
    if (!hChannel) {
        return;
    }
    [self.fourccMenu removeAllItems];
    DWORD *pColorFourcc = malloc(sizeof(DWORD)*10);
    int nCount=10;
    int ret = MWGetVideoCaptureSupportColorFormat(hChannel, pColorFourcc, &nCount);
    for (int i=0; i <nCount && i<10 ; i++) {
        DWORD colorFourcc = pColorFourcc[i];
        char strFourcc[5] = { 0 };
        *(uint32_t*)strFourcc = colorFourcc;
        NSString *itemTitle = [NSString stringWithFormat:@"%s", strFourcc];
        NSMenuItem *fourccItem = [self.fourccMenu addItemWithTitle:itemTitle action:@selector(switchFourcc:) keyEquivalent:@""];
        [fourccItem setTarget:self];
        [fourccItem setTag:colorFourcc];

        if (self.fourcc == colorFourcc) {
            [fourccItem setState:NSControlStateValueOn];
            self.selectedFourccItem= fourccItem;
        } else {
            [fourccItem setState:NSControlStateValueOff];
        }
        printf("INFO: Line %d support fourcc[%s]\n", self.lineNumber, strFourcc);
    }
    free(pColorFourcc);
}

- (void)initAudioMenu:(HCHANNEL) hChannel {
    [self.audioMenu removeAllItems];
    
    MWCAP_CHANNEL_INFO mci = { 0 };
    MWGetChannelInfo(hChannel, &mci);
    if (0 == strcmp(mci.szFamilyName, "Pro Capture")) {
        NSString *itemTitle = [NSString stringWithFormat:@"%02d-%d %s", mci.byBoardIndex, mci.byChannelIndex, mci.szProductName];
        NSMenuItem *audioItem = [self.audioMenu addItemWithTitle:itemTitle action:@selector(switchAudioNode:) keyEquivalent:@""];
        [audioItem setTag:MWCAP_AUDIO_CAPTURE_NODE_DEFAULT];
        [audioItem setTarget:self];
        [audioItem setState:NSControlStateValueOn];
        self.selectedAudioItem = audioItem;
    } else if (strcmp(mci.szFamilyName, "USB Capture") == 0) {
        MWCAP_AUDIO_CAPS t_Caps;
        MWGetAudioCaps(hChannel, &t_Caps);
        if (t_Caps.dwCaps & MWCAP_USB_AUDIO_CAP_EMBEDDED_CAPTURE) {
            NSString *itemTitle = [NSString stringWithFormat:@"Digital (%s(%s))", mci.szProductName, mci.szBoardSerialNo];
            NSMenuItem *audioItem = [self.audioMenu addItemWithTitle:itemTitle action:@selector(switchAudioNode:) keyEquivalent:@""];
            [audioItem setTag:MWCAP_AUDIO_CAPTURE_NODE_EMBEDDED_CAPTURE];
            [audioItem setTarget:self];
            [audioItem setState:NSControlStateValueOn];
            self.selectedAudioItem = audioItem;
        }
        
        if (t_Caps.dwCaps & MWCAP_USB_AUDIO_CAP_MICROPHONE) {
            NSString *itemTitle = [NSString stringWithFormat:@"Mic (%s(%s))", mci.szProductName, mci.szBoardSerialNo];
            NSMenuItem *audioItem = [self.audioMenu addItemWithTitle:itemTitle action:@selector(switchAudioNode:) keyEquivalent:@""];
            [audioItem setTag:MWCAP_AUDIO_CAPTURE_NODE_MICROPHONE];
            [audioItem setTarget:self];
        }
        
        if (t_Caps.dwCaps & MWCAP_USB_AUDIO_CAP_USB_CAPTURE) {
            NSString *itemTitle = [NSString stringWithFormat:@"Computer (%s(%s))", mci.szProductName, mci.szBoardSerialNo];
            NSMenuItem *audioItem = [self.audioMenu addItemWithTitle:itemTitle action:@selector(switchAudioNode:) keyEquivalent:@""];
            [audioItem setTag:MWCAP_AUDIO_CAPTURE_NODE_USB_CAPTURE];
            [audioItem setTarget:self];
        }
        
        if (t_Caps.dwCaps & MWCAP_USB_AUDIO_CAP_LINE_IN)  {
            NSString *itemTitle = [NSString stringWithFormat:@"Analog (%s(%s))", mci.szProductName, mci.szBoardSerialNo];
            NSMenuItem *audioItem = [self.audioMenu addItemWithTitle:itemTitle action:@selector(switchAudioNode:) keyEquivalent:@""];
            [audioItem setTag:MWCAP_AUDIO_CAPTURE_NODE_LINE_IN];
            [audioItem setTarget:self];
        }
    }
}

- (void)createPreviewLayer {
    if (!self.videoLayer) {
        self.videoLayer = [[AVSampleBufferDisplayLayer alloc] init];
        self.videoLayer.frame = CGRectMake(0, self.statusLabel.frame.size.height,
                                           self.frame.size.width, self.frame.size.height - self.statusLabel.frame.size.height);
        self.videoLayer.bounds = CGRectMake(0, self.statusLabel.frame.size.height,
                                            self.bounds.size.width, self.bounds.size.height - self.statusLabel.frame.size.height);
        self.videoLayer.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
        self.videoLayer.videoGravity = AVLayerVideoGravityResizeAspect;
        self.videoLayer.backgroundColor = [NSColor blackColor].CGColor;
        [[self layer] addSublayer:self.videoLayer];
        
        self.window.delegate = self;
    }
}

- (void)startCapture:(NSMenuItem *)deviceItem  {
    int index = (int)[deviceItem tag];

    char wPath[256] = {0};
    MWGetDevicePath(index, wPath);
    HCHANNEL hChannel = MWOpenChannelByPath(wPath);
    if (hChannel == NULL) {
        printf("ERROR: Open channel failed !\n");
        return;
    }
    self.inUseDevicePath = [NSString stringWithCString:wPath encoding:NSUTF8StringEncoding];
    {
        bool resolutionFound = false;
        MWCAP_VIDEO_RESOLUTION resolutions[256] = { 0 };
        MWCAP_VIDEO_RESOLUTION_LIST resList = { sizeof(resolutions)/sizeof(resolutions[0]), resolutions};
        MWGetVideoCaptureSupportListResolution(hChannel, &resList);
        for(int i=0; i<resList.nListSize; i++) {
            printf("INFO: support resolution[%d x %d]\n", resolutions[i].cx, resolutions[i].cy);
            if (resolutions[i].cx == self.width &&
                resolutions[i].cy == self.height) {
                resolutionFound = true;
                //break;
            }
        }
        
        if (!resolutionFound) {
            printf("ERROR: resolution[%d x %d] not found !\n", self.width, self.height);
            MWCloseChannel(hChannel);
            return;
        }
    }
    
    [self stopCapture];
    if (!self.selectedDeviceItem || self.selectedDeviceItem != deviceItem) {
        //Audio Node menu
        [self initAudioMenu:hChannel];
        [self initResolutionsMenu:hChannel];
        [self initFpsMenu];
        [self initColorFormatMenu:hChannel];
    }
    self.viewEnable = 1;
    //Audio render
    {
        MWAudioRenderCreate(self.samplerate, self.audioChannels, self.bitsPerSample, &(_audioRender));
        MWAudioRenderStart(self.audioRender);
    }

    //AV Capture
    {
        self.audioCapture = MWCreateAudioCapture(hChannel, MWCAP_AUDIO_CAPTURE_NODE_DEFAULT, self.samplerate, self.bitsPerSample, self.audioChannels,
                                              onAudioCaptureCallback, (__bridge void *)(self));
        
        char szVideoPath[512] = { 0 };
        if(MW_SUCCEEDED == MWGetVideoDevicePathByChannel(hChannel, szVideoPath)) {
            //close channel first before video capture
            MWCloseChannel(hChannel);
            self.videoCapture = MWCreateVideoCapture(szVideoPath, self.width, self.height, self.fourcc, self.frameDuration,
                                                  onVideoCaptureCallback, (__bridge void *)(self));
        } else {
            MWCloseChannel(hChannel);
        }
    }
    
}

- (void)stopCapture {
    self.viewEnable = 0;
    if (self.audioCapture) {
        MWDestoryAudioCapture(self.audioCapture);
        self.audioCapture = NULL;
    }
    
    if (self.audioRender) {
        MWAudioRenderStop(self.audioRender);
        MWAudioRenderDestroy(self.audioRender);
        self.audioRender = NULL;
    }
    
    if (self.videoCapture) {
        MWDestoryVideoCapture(self.videoCapture);
        self.videoCapture = NULL;
    }
    self.captureFps      = 0.0;
    self.lastTimestamp   = 0;
    self.videoFrameCount = 0;
    self.previousPts = 0;
    self.delayVideoFrameCount = 0;
    self.dropVideoFrameCount = 0;
}

- (void)destory {
    [self stopContinuousAutoSwitch];
    [self stopCapture];
    if (self.statusTimer) {
        [self.statusTimer invalidate];
    }
    
    MWCaptureExitInstance();
}

- (void) statusTimerProc: (NSTimer *) timer {
    char strFourcc[5] = { 0 };
    *(uint32_t*)strFourcc = self.fourcc;
    [self.statusLabel setStringValue:[NSString stringWithFormat:@" %d x %d %s %.2f FPS  Drop:%lld Stutter:%lld", self.width, self.height, strFourcc, self.captureFps, self.dropVideoFrameCount, self.delayVideoFrameCount]];
}

#pragma mark -
- (void)deviceItemAction:(NSMenuItem *)deviceItem {
    
    int index = (int)[deviceItem tag];
    char wPath[256] = {0};
    MWGetDevicePath(index, wPath);
    NSString *devicePath = [NSString stringWithCString:wPath encoding:NSUTF8StringEncoding];
    BOOL inUse = NO;
    if (self.delegate && [self.delegate respondsToSelector:@selector(isDeviceInUse:)])
    {
        inUse = [self.delegate isDeviceInUse:devicePath];
    }
    
    if (inUse && self.inUseDevicePath) {
        NSLog(@"Prohibit using the same channel multiple times in the same process.");
        // 创建 NSAlert 实例
        NSAlert *alert = [[NSAlert alloc] init];
        // 设置标题和信息文本
        [alert setMessageText:@"Warning"];
        [alert setInformativeText:@"Prohibit using the same channel multiple times in the same process."];
        // 添加按钮
        [alert addButtonWithTitle:@"Done"];
        // 显示警告框
        [alert runModal];
        return;
    }
    
    while (inUse && !self.inUseDevicePath && self.deviceMenu.numberOfItems > 1) {
        //first load, find the next device
        index++;
        if (index >= self.deviceMenu.numberOfItems) {
            index = 0;
        }
        char wNextPath[256] = {0};
        MWGetDevicePath(index, wNextPath);
        NSString *nextDevicePath = [NSString stringWithCString:wNextPath encoding:NSUTF8StringEncoding];
        BOOL nextInUse = NO;
        if (self.delegate && [self.delegate respondsToSelector:@selector(isDeviceInUse:)])
        {
            nextInUse = [self.delegate isDeviceInUse:nextDevicePath];
        }
        if (!nextInUse) {
            deviceItem = [self.deviceMenu itemAtIndex:index];
            break;
        }
    }
    
    [self stopContinuousAutoSwitch];
    
    self.width         = 1920;
    self.height        = 1080;
    self.frameDuration = 166667;// 333334
    self.fourcc        = MWFOURCC_NV12;
    self.captureFps      = 0.0;
    self.lastTimestamp   = 0;
    self.videoFrameCount = 0;
    self.pixelFormat = fourcc_to_CVPixelFormat(self.fourcc);
    
    if (self.selectedDeviceItem) {
        [self.selectedDeviceItem setState:NSControlStateValueOff];
        self.selectedDeviceItem = nil;
    }
    
    if (self.selectedAudioItem) {
        [self.selectedAudioItem setState:NSControlStateValueOff];
        self.selectedAudioItem = nil;
    }
    [self stopContinuousAutoSwitch];
    
    [self startCapture:deviceItem];
    
    [deviceItem setState:NSControlStateValueOn];
    self.selectedDeviceItem = deviceItem;
}

- (void)switchAudioNode:(NSMenuItem *)audioItem {
    if (self.audioCapture) {
        MWDestoryAudioCapture(self.audioCapture);
        self.audioCapture = NULL;
    }
    
    if (self.selectedAudioItem) {
        [self.selectedAudioItem setState:NSControlStateValueOff];
        self.selectedAudioItem = NULL;
    }
    
    int deviceIndex = (int)[self.selectedDeviceItem tag];
    char wPath[256] = {0};
    MWGetDevicePath(deviceIndex, wPath);
    HCHANNEL hChannel = MWOpenChannelByPath(wPath);
    if (hChannel == NULL) {
        printf("ERROR: Open channel failed !\n");
        return;
    }
    
    MWCAP_AUDIO_CAPTURE_NODE audioNode = (MWCAP_AUDIO_CAPTURE_NODE)[audioItem tag];
    self.audioCapture = MWCreateAudioCapture(hChannel, audioNode, self.samplerate, self.bitsPerSample, self.audioChannels, onAudioCaptureCallback, (__bridge void *)(self));
    
    MWCloseChannel(hChannel);
    hChannel = NULL;
    
    [audioItem setState:NSControlStateValueOn];
    self.selectedAudioItem = audioItem;
}

- (void)switchCaptureFormat:(NSMenuItem *)menuItem
{
    int index = (int)[menuItem tag];
    if (index == -1) {
        [self startContinuousAutoSwitch];
    } else {
        [self stopContinuousAutoSwitch];
        if (index >= [self.suportCaptureFormatList count]) {
            return;
        }
        [self stopCapture];
        CaptureFormatInfo *info = [self.suportCaptureFormatList objectAtIndex:index];
        self.width = info.width;
        self.height = info.height;
        self.currentCaptrueFormatIndex = index;
        [self startCapture:self.selectedDeviceItem];
    }
    if (self.selectedResolutionItem) {
        [self.selectedResolutionItem setState:NSControlStateValueOff];
        self.selectedResolutionItem = NULL;
    }
    
    [menuItem setState:NSControlStateValueOn];
    self.selectedResolutionItem = menuItem;
}

- (void)switchFP:(NSMenuItem *)menuItem
{
    int tag = (int)[menuItem tag];
    [self stopCapture];
    self.frameDuration = tag;
    [self startCapture:self.selectedDeviceItem];

    if (self.selectedFpsItem) {
        [self.selectedFpsItem setState:NSControlStateValueOff];
        self.selectedFpsItem = NULL;
    }
    
    [menuItem setState:NSControlStateValueOn];
    self.selectedFpsItem = menuItem;
}

- (void)switchFourcc:(NSMenuItem *)menuItem
{
    int fourcc = (int)[menuItem tag];
    [self stopCapture];
    self.fourcc = fourcc;
    self.pixelFormat = fourcc_to_CVPixelFormat(self.fourcc);
    [self startCapture:self.selectedDeviceItem];
    if (self.selectedFourccItem) {
        [self.selectedFourccItem setState:NSControlStateValueOff];
        self.selectedFourccItem = NULL;
    }
    
    [menuItem setState:NSControlStateValueOn];
    self.selectedFourccItem = menuItem;
}

#pragma mark -
- (void)startContinuousAutoSwitch
{
    if (self.continuousAutoSwitch) {
        return;
    }
    self.continuousAutoSwitch = YES;
    self.currentCaptrueFormatIndex = 0;
    _timer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:@selector(autoSwitch) userInfo:nil repeats:YES];
}

- (void)stopContinuousAutoSwitch
{
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
    self.continuousAutoSwitch = NO;
}

- (void)autoSwitch
{
    if (!self.continuousAutoSwitch) {
        return;
    }
    if (!self.suportCaptureFormatList) {
        return;
    }
    if (self.currentCaptrueFormatIndex >= [self.suportCaptureFormatList count]) {
        self.currentCaptrueFormatIndex = 0;
    }
    
    [self stopCapture];
    CaptureFormatInfo *info = [self.suportCaptureFormatList objectAtIndex:self.currentCaptrueFormatIndex];
    self.width = info.width;
    self.height = info.height;
    [self startCapture:self.selectedDeviceItem];
    
    self.currentCaptrueFormatIndex++;
    
    
}
#pragma mark -
- (void)onVideoCaptureFrame:(BYTE *)pBuffer bufferLen:(long)BufferLen stride:(long)stride {
    
    {
        uint64_t timestamp = getTimestamp();
        if (self.previousPts != 0) {
            uint64_t t = timestamp - self.previousPts;
            float delta = t - self.frameDuration/10000.0;
            if (fabsf(delta) >= 5) {
                self.delayVideoFrameCount++;
            }
            if (delta > self.frameDuration/10000.0) {
                self.dropVideoFrameCount++;
            }
//            NSLog(@"-onVideoCaptureFrame- timestamp:%lld delta:%0.f",timestamp,delta);
        }
        self.previousPts = timestamp;
    }
    
    //render
    if (self.viewEnable) {
        CVPixelBufferRef pixelBuffer = NULL;
        NSDictionary *pixelAttributes = @{(NSString*)kCVPixelBufferIOSurfacePropertiesKey:@{}};
        int height = self.height;
        if (self.fourcc == MWFOURCC_NV12) {
            height =  ((self.height+7)/8)*8;
        }
        CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,
                                         self.width,
                                         self.height,
                                         self.pixelFormat,
                                         (__bridge CFDictionaryRef)(pixelAttributes),
                                         &pixelBuffer);
        CVPixelBufferLockBaseAddress(pixelBuffer,0);
        size_t pixelStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
        unsigned char *yDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
        unsigned char *uvDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
        unsigned char *y_ch0 = pBuffer;
        unsigned char *y_ch1 = pBuffer + stride * height;
//        NSLog(@"===",stride * self.height)
        if (pixelStride == stride) {
            memcpy(yDestPlane, y_ch0, stride *height);
            if (uvDestPlane != NULL) {
                memcpy(uvDestPlane, y_ch1, stride * height / 2);
            }
        } else {
            for (int i = 0; i < height; i++) {
                memcpy(yDestPlane + i*pixelStride, y_ch0 + i*stride, self.width);
            }
            if (uvDestPlane != NULL) {
                for (int i = 0; i < height/2; i++) {
                    memcpy(uvDestPlane + i*pixelStride, y_ch1+ i*stride, self.width);
                }
            }
        }


        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

        CMSampleTimingInfo timing = {kCMTimeInvalid, kCMTimeInvalid, kCMTimeInvalid};
        CMVideoFormatDescriptionRef videoInfo = NULL;
        result = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo);

        CMSampleBufferRef sampleBuffer = NULL;
        result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, true, NULL, NULL, videoInfo, &timing, &sampleBuffer);
        CFRelease(videoInfo);

        CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, YES);
        CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
        CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);

        if (self.videoLayer) {
            [self.videoLayer enqueueSampleBuffer:(CMSampleBufferRef)sampleBuffer];
        }

        CFRelease(sampleBuffer);
        CFRelease(pixelBuffer);
    }

    //fps
    {
        uint64_t now = getTimestamp();
        if (self.lastTimestamp == 0) {
            self.lastTimestamp = now;
            self.videoFrameCount = 0;
        } else {
            self.videoFrameCount++;
        }

        if (now - self.lastTimestamp >= 1000) {
            self.captureFps = self.videoFrameCount * 1000.0 / (now - self.lastTimestamp);
            self.lastTimestamp = now;
            self.videoFrameCount = 0;
            
            //NSLog(@"video capture fps: %f ", self.captureFps);
        }
    }
}

- (void)onAudioCaptureFrame:(BYTE *)pbFrame cbFrame:(int)cbFrame u64TimeStamp:(uint64_t)u64TimeStamp {
    MWAudioRenderPut(self.audioRender, (uint8_t*)pbFrame, cbFrame);

    //static FILE *fd = fopen("capture.pcm", "wb");
    //fwrite(pbFrame, cbFrame, 1, fd);
}

@end
