激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

香港云服务器
服務(wù)器之家 - 編程語(yǔ)言 - IOS - iOS利用手機(jī)攝像頭測(cè)心率

iOS利用手機(jī)攝像頭測(cè)心率

2021-01-30 17:25YvanLiu IOS

這篇文章主要為大家詳細(xì)介紹了iOS利用手機(jī)攝像頭測(cè)心率的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

原理

簡(jiǎn)單介紹一下,網(wǎng)上可以查到很多關(guān)于手機(jī)測(cè)心率的這種項(xiàng)目,大概就是: 把手指放在攝像頭和閃光燈上,通過手指處脈搏跳動(dòng)充血導(dǎo)致的細(xì)微顏色變化來(lái)確定心跳波動(dòng),確定波峰波谷,根據(jù)兩個(gè)波峰之間的時(shí)間差來(lái)確定瞬時(shí)心率。

思路

首先,采集視頻流,根據(jù)拿到的RGB顏色轉(zhuǎn)成HSV顏色集,其實(shí)我們只用到了HSV的H。
對(duì)拿到的H進(jìn)行一些處理,看跟人喜好或者具體情況,主要是用于后面的折線圖和計(jì)算瞬時(shí)心率,如果有能力的話可以處理一下噪音數(shù)據(jù),因?yàn)榭赡軠y(cè)的時(shí)候手指輕微抖動(dòng)會(huì)造成一些不穩(wěn)定的數(shù)據(jù)。
根據(jù)處理后的H就可以進(jìn)行畫折線圖了,我是把處理后的H和時(shí)間戳進(jìn)行了綁定,用來(lái)后面的計(jì)算心率。
根據(jù)處理后的H來(lái)確定波峰波谷,利用兩個(gè)波谷之間的時(shí)間差計(jì)算心率。

實(shí)現(xiàn)

大致思路就是上面這樣,下面來(lái)看一下代碼具體實(shí)現(xiàn)以下。

1.首先我先初始化了一些數(shù)據(jù),方便后面使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 設(shè)備
@property (strong, nonatomic) AVCaptureDevice      *device;
// 結(jié)合輸入輸出
@property (strong, nonatomic) AVCaptureSession     *session;
// 輸入設(shè)備
@property (strong, nonatomic) AVCaptureDeviceInput   *input;
// 輸出設(shè)備
@property (strong, nonatomic) AVCaptureVideoDataOutput *output;
// 輸出的所有點(diǎn)
@property (strong, nonatomic) NSMutableArray      *points;
 
// 記錄浮點(diǎn)變化的前一次的值
static float lastH = 0;
// 用于判斷是否是第一個(gè)福點(diǎn)值
static int  count = 0;
 
// 初始化
self.device   = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
self.session  = [[AVCaptureSession alloc]init];
self.input   = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
self.output   = [[AVCaptureVideoDataOutput alloc]init];
self.points   = [[NSMutableArray alloc]init];

2.設(shè)置視頻采集流,為了節(jié)省內(nèi)存,我沒有輸出視頻畫面

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 開啟閃光燈
 if ([self.device isTorchModeSupported:AVCaptureTorchModeOn]) {
   [self.device lockForConfiguration:nil];
    // 開啟閃光燈
    self.device.torchMode=AVCaptureTorchModeOn;
    // 調(diào)低閃光燈亮度(為了減少內(nèi)存占用和避免時(shí)間長(zhǎng)手機(jī)發(fā)燙)
    [self.device setTorchModeOnWithLevel:0.01 error:nil];
    [self.device unlockForConfiguration];
  }
 
  // 開始配置input output
  [self.session beginConfiguration];
 
  // 設(shè)置像素輸出格式
  NSNumber *BGRA32Format = [NSNumber numberWithInt:kCVPixelFormatType_32BGRA];
  NSDictionary *setting =@{(id)kCVPixelBufferPixelFormatTypeKey:BGRA32Format};
  [self.output setVideoSettings:setting];
 
  // 拋棄延遲的幀
  [self.output setAlwaysDiscardsLateVideoFrames:YES];
  //開啟攝像頭采集圖像輸出的子線程
  dispatch_queue_t outputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
  // 設(shè)置子線程執(zhí)行代理方法
  [self.output setSampleBufferDelegate:self queue:outputQueue];
 
  // 向session添加
  if ([self.session canAddInput:self.input])  [self.session addInput:self.input];
  if ([self.session canAddOutput:self.output]) [self.session addOutput:self.output];
 
  // 降低分辨率,減少采樣率(為了減少內(nèi)存占用)
  self.session.sessionPreset = AVCaptureSessionPreset1280x720;
  // 設(shè)置最小的視頻幀輸出間隔
  self.device.activeVideoMinFrameDuration = CMTimeMake(1, 10);
 
  // 用當(dāng)前的output 初始化connection
  AVCaptureConnection *connection =[self.output connectionWithMediaType:AVMediaTypeVideo];
  [connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
 
  // 完成編輯
  [self.session commitConfiguration];
  // 開始運(yùn)行
  [self.session startRunning];

這里我降低了閃光燈亮度,降低了分辨率,減少了每秒鐘輸出的幀。主要就是為了減少內(nèi)存的占用。(我手里只有一臺(tái)6,沒有測(cè)其他設(shè)備可不可以)

3.在output的代理方法中采集視頻流

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// captureOutput->當(dāng)前output  sampleBuffer->樣本緩沖  connection->捕獲連接
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
 
  //獲取圖層緩沖
  CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  CVPixelBufferLockBaseAddress(imageBuffer, 0);
  uint8_t*buf = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
  size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
  size_t width = CVPixelBufferGetWidth(imageBuffer);
  size_t height = CVPixelBufferGetHeight(imageBuffer);
 
  float r = 0, g = 0,b = 0;
  float h,s,v;
  // 計(jì)算RGB
  TORGB(buf, width, height, bytesPerRow, &r, &g, &b);
  // RGB轉(zhuǎn)HSV
  RGBtoHSV(r, g, b, &h, &s, &v);
  // 獲取當(dāng)前時(shí)間戳(精確到毫秒)
  double t = [[NSDate date] timeIntervalSince1970]*1000;
  // 返回處理后的浮點(diǎn)值
  float p = HeartRate(h);
  // 綁定浮點(diǎn)和時(shí)間戳
  NSDictionary *point = @{[NSNumber numberWithDouble:t]:[NSNumber numberWithFloat:p]};
  //下面按個(gè)人情況可以進(jìn)行計(jì)算心率或者畫心率圖
}

到這里數(shù)據(jù)已經(jīng)處理好了,后面可以根據(jù)數(shù)據(jù)畫折線圖,或者計(jì)算心率

計(jì)算RGB

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void TORGB (uint8_t *buf, float ww, float hh, size_t pr, float *r, float *g, float *b) {
  float wh = (float)(ww * hh );
  for(int y = 0; y < hh; y++) {
    for(int x = 0; x < ww * 4; x += 4) {
      *b += buf[x];
      *g += buf[x+1];
      *r += buf[x+2];
    }
    buf += pr;
  }
  *r /= 255 * wh;
  *g /= 255 * wh;
  *b /= 255 * wh;
}

RGB轉(zhuǎn)HSV

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v ) {
  float min, max, delta;
  min = MIN( r, MIN(g, b ));
  max = MAX( r, MAX(g, b ));
  *v = max;
  delta = max - min;
  if( max != 0 )
    *s = delta / max;
  else {
    *s = 0;
    *h = -1;
    return;
  }
  if( r == max )
    *h = ( g - b ) / delta;
  else if( g == max )
    *h = 2 + (b - r) / delta;
  else
    *h = 4 + (r - g) / delta;
  *h *= 60;
  if( *h < 0 )
    *h += 360;
}

根據(jù)h處理浮點(diǎn)

?
1
2
3
4
5
6
7
8
float HeartRate (float h) {
  float low = 0;
  count++;
  lastH = (count==1)?h:lastH;
  low = (h-lastH);
  lastH = h;
  return low;
}

4.分析數(shù)據(jù),計(jì)算心率

這里我糾結(jié)了好長(zhǎng)時(shí)間,試了幾種不同的方法,都沒有一個(gè)比較理想的結(jié)果,計(jì)算出來(lái)的特別不準(zhǔn)。后來(lái)看了 http://ios.jobbole.com/88158/ 這篇文章,后面優(yōu)化的部分有一個(gè) 基音算法 ,雖不明,但覺厲,對(duì)此表示非常感謝。吼吼吼。

原理:就是說(shuō)劃一個(gè)時(shí)間段,在這個(gè)時(shí)間段里面找到一個(gè) 最低峰值 ,然后確定一個(gè) 周期 ,然后分別在 這個(gè)峰值 前間隔 0.5個(gè)周期 的 1周期里 和 這個(gè)峰值 后間隔 0.5個(gè)周期 的 1周期 里找到一個(gè)最低峰值。 然后根據(jù)這幾個(gè)值來(lái)確定瞬時(shí)心率。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
- (void)analysisPointsWith:(NSDictionary *)point {
 
  [self.points addObject:point];
  if (self.points.count<=30) return;
  int count = (int)self.points.count;
 
  if (self.points.count%10 == 0) {
 
    int d_i_c = 0;     //最低峰值的位置 姑且算在中間位置 c->center
    int d_i_l = 0;     //最低峰值左面的最低峰值位置 l->left
    int d_i_r = 0;     //最低峰值右面的最低峰值位置 r->right
 
 
    float trough_c = 0;   //最低峰值的浮點(diǎn)值
    float trough_l = 0;   //最低峰值左面的最低峰值浮點(diǎn)值
    float trough_r = 0;   //最低峰值右面的最低峰值浮點(diǎn)值
 
    // 1.先確定數(shù)據(jù)中的最低峰值
    for (int i = 0; i < count; i++) {
      float trough = [[[self.points[i] allObjects] firstObject] floatValue];
      if (trough < trough_c) {
        trough_c = trough;
        d_i_c = i;
      }
    }
 
    // 2.找到最低峰值以后 以最低峰值為中心 找到前0.5-1.5周期中的最低峰值 和后0.5-1.5周期的最低峰值
 
    if (d_i_c >= 1.5*T) {
 
      // a.如果最低峰值處在中心位置, 即距離前后都至少有1.5個(gè)周期
      if (d_i_c <= count-1.5*T) {
        // 左面最低峰值
        for (int j = d_i_c - 0.5*T; j > d_i_c - 1.5*T; j--) {
          float trough = [[[self.points[j] allObjects] firstObject] floatValue];
          if (trough < trough_l) {
            trough_l = trough;
            d_i_l = j;
          }
        }
        // 右面最低峰值
        for (int k = d_i_c + 0.5*T; k < d_i_c + 1.5*T; k++) {
          float trough = [[[self.points[k] allObjects] firstObject] floatValue];
          if (trough < trough_r) {
            trough_r = trough;
            d_i_r = k;
          }
        }
 
      }
      // b.如果最低峰值右面不夠1.5個(gè)周期 分兩種情況 不夠0.5個(gè)周期和夠0.5個(gè)周期
      else {
        // b.1 夠0.5個(gè)周期
        if (d_i_c <count-0.5*T) {
          // 左面最低峰值
          for (int j = d_i_c - 0.5*T; j > d_i_c - 1.5*T; j--) {
            float trough = [[[self.points[j] allObjects] firstObject] floatValue];
            if (trough < trough_l) {
              trough_l = trough;
              d_i_l = j;
            }
          }
          // 右面最低峰值
          for (int k = d_i_c + 0.5*T; k < count; k++) {
            float trough = [[[self.points[k] allObjects] firstObject] floatValue];
            if (trough < trough_r) {
              trough_r = trough;
              d_i_r = k;
            }
          }
        }
        // b.2 不夠0.5個(gè)周期
        else {
          // 左面最低峰值
          for (int j = d_i_c - 0.5*T; j > d_i_c - 1.5*T; j--) {
            float trough = [[[self.points[j] allObjects] firstObject] floatValue];
            if (trough < trough_l) {
              trough_l = trough;
              d_i_l = j;
            }
          }
        }
      }
 
    }
    // c. 如果左面不夠1.5個(gè)周期 一樣分兩種情況 夠0.5個(gè)周期 不夠0.5個(gè)周期
    else {
      // c.1 夠0.5個(gè)周期
      if (d_i_c>0.5*T) {
        // 左面最低峰值
        for (int j = d_i_c - 0.5*T; j > 0; j--) {
          float trough = [[[self.points[j] allObjects] firstObject] floatValue];
          if (trough < trough_l) {
            trough_l = trough;
            d_i_l = j;
          }
        }
        // 右面最低峰值
        for (int k = d_i_c + 0.5*T; k < d_i_c + 1.5*T; k++) {
          float trough = [[[self.points[k] allObjects] firstObject] floatValue];
          if (trough < trough_r) {
            trough_r = trough;
            d_i_r = k;
          }
        }
 
      }
      // c.2 不夠0.5個(gè)周期
      else {
        // 右面最低峰值
        for (int k = d_i_c + 0.5*T; k < d_i_c + 1.5*T; k++) {
          float trough = [[[self.points[k] allObjects] firstObject] floatValue];
          if (trough < trough_r) {
            trough_r = trough;
            d_i_r = k;
          }
        }
      }
 
    }
 
    // 3. 確定哪一個(gè)與最低峰值更接近 用最接近的一個(gè)最低峰值測(cè)出瞬時(shí)心率 60*1000兩個(gè)峰值的時(shí)間差
    if (trough_l-trough_c < trough_r-trough_c) {
 
      NSDictionary *point_c = self.points[d_i_c];
      NSDictionary *point_l = self.points[d_i_l];
      double t_c = [[[point_c allKeys] firstObject] doubleValue];
      double t_l = [[[point_l allKeys] firstObject] doubleValue];
      NSInteger fre = (NSInteger)(60*1000)/(t_c - t_l);
      if (self.frequency)
        self.frequency(fre);
      if ([self.delegate respondsToSelector:@selector(startHeartDelegateRateFrequency:)])
        [self.delegate startHeartDelegateRateFrequency:fre];
    } else {
      NSDictionary *point_c = self.points[d_i_c];
      NSDictionary *point_r = self.points[d_i_r];
      double t_c = [[[point_c allKeys] firstObject] doubleValue];
      double t_r = [[[point_r allKeys] firstObject] doubleValue];
      NSInteger fre = (NSInteger)(60*1000)/(t_r - t_c);
      if (self.frequency)
        self.frequency(fre);
      if ([self.delegate respondsToSelector:@selector(startHeartDelegateRateFrequency:)])
        [self.delegate startHeartDelegateRateFrequency:fre];
    }
    // 4.刪除過期數(shù)據(jù)
    for (int i = 0; i< 10; i++) {
      [self.points removeObjectAtIndex:0];
    }
  }
}

我目前是這樣處理的,后面是用的前后兩個(gè)峰值與 最低峰值 最接近的那個(gè)峰值的時(shí)間差,測(cè)了幾次又和別的app比較了一下,基本都是正確的,最多也就是上下差1-2次每分鐘。(在數(shù)據(jù)比較穩(wěn)定的情況下,如果有更好的方法請(qǐng)推薦,謝謝)

5.畫折線圖 這里用到了 CoreGraphics

PS:首先,使用這個(gè)CoreGraphics要在View里面,并且要在View的 drawRect: 方法中使用,不然拿不到畫布。我是為了封裝單獨(dú)建立了一個(gè)UIView的類。

a.首先還是數(shù)據(jù),沒有數(shù)據(jù)怎么畫

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@property (strong, nonatomic) NSMutableArray *points;
// 在init中初始化數(shù)組
self.points = [[NSMutableArray alloc]init];
// 這個(gè)可以翻譯過來(lái),也是在init中
self.clearsContextBeforeDrawing = YES;
 
// 外部調(diào)用方法
- (void)drawRateWithPoint:(NSNumber *)point {
  // 倒敘插入數(shù)組
  [self.points insertObject:point atIndex:0];
 
  // 刪除溢出屏幕數(shù)據(jù)
  if (self.points.count > self.frame.size.width/6) {
    [self.points removeLastObject];
  }
 
  dispatch_async(dispatch_get_main_queue(), ^{
    // 這個(gè)方法自動(dòng)調(diào)取 drawRect:方法
    [self setNeedsDisplay];
  });
}

之前調(diào) setNeedsDisplay ,一直沒有走 drawRect: 方法,或者就直走了一次,然后去百度是說(shuō) setNeedsDisplay 會(huì)在系統(tǒng)空閑的時(shí)候執(zhí)行 drawRect: ,然后我嘗試著回歸到主線程中調(diào)用,就好了。具體原因不是很清楚,也可能是因?yàn)橐谥骶€程中修改View。

b.畫折線的方法,具體怎么調(diào)整看個(gè)人心情了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CGFloat ww = self.frame.size.width;
  CGFloat hh = self.frame.size.height;
  CGFloat pos_x = ww;
  CGFloat pos_y = hh/2;
  // 獲取當(dāng)前畫布
  CGContextRef context = UIGraphicsGetCurrentContext();
  // 折線寬度
  CGContextSetLineWidth(context, 1.0);
  //消除鋸齒
  //CGContextSetAllowsAntialiasing(context,false);
  // 折線顏色
  CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
  CGContextMoveToPoint(context, pos_x, pos_y);
  for (int i = 0; i < self.points.count; i++) {
    float h = [self.points[i] floatValue];
    pos_y = hh/2 + (h * hh/2) ;
    CGContextAddLineToPoint(context, pos_x, pos_y);
    pos_x -=6;
  }
  CGContextStrokePath(context);

c.為了看起來(lái)好看,我還加了網(wǎng)格,當(dāng)然也是在 drawRect: 中調(diào)用的

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
static CGFloat grid_w = 30.0f;
- (void)buildGrid {
 
  CGFloat wight = self.frame.size.width;
  CGFloat height = self.frame.size.height;
 
  // 獲取當(dāng)前畫布
  CGContextRef context = UIGraphicsGetCurrentContext();
 
  CGFloat pos_x = 0.0f;
  CGFloat pos_y = 0.0f;
 
  // 在wight范圍內(nèi)畫豎線
  while (pos_x < wight) {
    // 設(shè)置網(wǎng)格線寬度
    CGContextSetLineWidth(context, 0.2);
    // 設(shè)置網(wǎng)格線顏色
    CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
    // 起點(diǎn)
    CGContextMoveToPoint(context, pos_x, 1.0f);
    // 終點(diǎn)
    CGContextAddLineToPoint(context, pos_x, height);
    pos_x +=grid_w;
    //開始劃線
    CGContextStrokePath(context);
  }
 
  // 在height范圍內(nèi)畫橫線
  while (pos_y < height) {
 
    CGContextSetLineWidth(context, 0.2);
    CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextMoveToPoint(context, 1.0f, pos_y);
    CGContextAddLineToPoint(context, wight, pos_y);
    pos_y +=grid_w;
    CGContextStrokePath(context);
  }
  pos_x = 0.0f; pos_y = 0.0f;
 
  // 在wight范圍內(nèi)畫豎線
  while (pos_x < wight) {
    CGContextSetLineWidth(context, 0.1);
    CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextMoveToPoint(context, pos_x, 1.0f);
    CGContextAddLineToPoint(context, pos_x, height);
    pos_x +=grid_w/5;
    CGContextStrokePath(context);
  }
 
  // 在height范圍內(nèi)畫橫線
  while (pos_y < height) {
    CGContextSetLineWidth(context, 0.1);
    CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextMoveToPoint(context, 1.0f, pos_y);
    CGContextAddLineToPoint(context, wight, pos_y);
    pos_y +=grid_w/5;
    CGContextStrokePath(context);
  }
 
}

總結(jié)

寫這個(gè)功能的時(shí)候,自己有很多思考,也參考了很多其他人的博客、代碼還有別人的畢業(yè)論文,呵呵呵,還問了幾個(gè)學(xué)醫(yī)的同學(xué),代碼不難,數(shù)據(jù)處理的部分可能不太好弄,但是寫完還是有點(diǎn)成就感的。

代碼里還存在很多問題,后期有時(shí)間我會(huì)慢慢優(yōu)化,歡迎指正。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

延伸 · 閱讀

精彩推薦
518
主站蜘蛛池模板: 制服丝袜成人动漫 | 国产欧美日韩在线播放 | 欧美18—19sex性hd | 日韩黄色一级视频 | 国产精品刺激对白麻豆99 | 欧美视频在线观看一区 | 4480午夜 | 色操网| 日朝毛片 | 草久视频在线观看 | 在线一级片 | 精品久久久久久久久中文字幕 | 亚洲精品欧美二区三区中文字幕 | 性 毛片 | 素人视频在线观看免费 | 娇喘在线| 一级做a爰片性色毛片2021 | 久久久久国产精品久久久久 | 久久综合一区二区 | 午夜精品老牛av一区二区三区 | 亚洲午夜精选 | 久久亚洲精品视频 | 欧美日韩后| 中文字幕在线资源 | 久久国产精品二国产精品中国洋人 | 在线成人免费网站 | 日韩黄色在线播放 | 毛片一区二区三区四区 | 一级大黄毛片 | 国产免费专区 | 在线看免费观看av | 一区二区三区四区国产精品视频 | 成人毛片免费播放 | 91短视频在线观看视频 | 精品一区二区在线观看视频 | 今井夏帆av一区二区 | 久久国产免费 | 激情小说激情电影 | 国产精品久久久久久久久久 | 国产一区二区三区精品在线观看 | 成人毛片免费播放 |