FFmpeg - 播放YUV旁舰,視頻幀格式轉(zhuǎn)換

播放YUV

  • 定時讀取YUV的視頻幀
- (void)play {
    //
    NSTimeInterval interval = 1.0 / _yuv.fps * 1.0;
    __weak typeof(self)weakSelf = self;
    self.timer = [NSTimer timerWithTimeInterval:interval repeats:true block:^(NSTimer * _Nonnull timer) {
        [weakSelf timerAction];
    }];
    [self.timer fire];
    [[NSRunLoop mainRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}
  • 將YUV轉(zhuǎn)換為RGB數(shù)據(jù)
  • 用RGB數(shù)據(jù)生成CGimage
  • 在view上繪制CGImage

- (void)play {
    //
    NSTimeInterval interval = 1.0 / _yuv.fps * 1.0;
    __weak typeof(self)weakSelf = self;
    self.timer = [NSTimer timerWithTimeInterval:interval repeats:true block:^(NSTimer * _Nonnull timer) {
        [weakSelf timerAction];
    }];
    [self.timer fire];
    [[NSRunLoop mainRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)setYUV:(YuvParam*)yuv {
    _yuv = yuv;
    NSInteger format = yuv.pixelFomat;
    self.file = [NSFileHandle fileHandleForReadingAtPath: _yuv.filename];
    // 一幀圖片的大小
    imageSize = av_image_get_buffer_size((AVPixelFormat)_yuv.pixelFomat, _yuv.width, _yuv.height, 1);
    // 當(dāng)前控件的大小
    CGFloat width = self.bounds.size.width;
    CGFloat height = self.bounds.size.height;
    // 計算Rect
    CGFloat dx = 0;
    CGFloat dy = 0;
    CGFloat dw = _yuv.width;
    CGFloat dh = _yuv.height;
    // 計算目標(biāo)尺寸
    if (dw > width || dh > height) {
        if (dw * height > width * dh) { // 視頻的寬高比 > 播放器的寬高比
            dh = width * dh / dw;
            dw = width;
        } else {
            dw = height * dw / dh;
            dh = height;
        }
    }
    dx = (width - dw) * 0.5;
    dy = (height - dh) * 0.5;
    playerRect = CGRectMake(dx, dy, dw, dh);
}

- (CGImageRef)generateImage:(const RawVideoFrame &)output {
    int width = output.width;
    int height = output.height;
    size_t bufferLength = width * height * 3;
    char * buffer = output.pixels;
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
    size_t bitsPerComponent = 8;
    size_t bitsPerPixel = 24;
    size_t bytesPerRow = 3 * width;
    
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    if(colorSpaceRef == NULL) {
        CGDataProviderRelease(provider);
    }
    
    CGBitmapInfo bitmapInfo = kCGImageAlphaNone;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    
    CGImageRef iref = CGImageCreate(width,
                                    height,
                                    bitsPerComponent,
                                    bitsPerPixel,
                                    bytesPerRow,
                                    colorSpaceRef,
                                    bitmapInfo,
                                    provider,   // data provider
                                    NULL,      // decode
                                    NO,          // should interpolate
                                    renderingIntent);
    return iref;
}

- (void)timerAction {
    NSData *imageData = [self.file readDataOfLength:imageSize];
    if (imageData.length > 0) {
        RawVideoFrame input = {
            (char*)imageData.bytes,
            static_cast<int>(_yuv.width),
            static_cast<int>(_yuv.height),
            (AVPixelFormat)_yuv.pixelFomat,
        };
        RawVideoFrame output = {
            nullptr,
            static_cast<int>(_yuv.width),
            static_cast<int>(_yuv.height),
            AV_PIX_FMT_RGB24
        };
        [FFMpegs convertRawVideo:&input output:&output];
        CGImageRef iref = [self generateImage:output];
        self.playLayer.contents = (__bridge id)iref;
        self.playLayer.frame = playerRect;
    } else {
        [self.timer invalidate];
        self.timer = nil;
    }
    
}

視頻幀格式轉(zhuǎn)換

  • 創(chuàng)建轉(zhuǎn)換上下文
  • 設(shè)置輸入輸出緩沖區(qū)
  • 計算輸出輸出幀的大小
  • 開始轉(zhuǎn)換
  • 獲取轉(zhuǎn)換后的數(shù)據(jù)

+ (void)convertRawVideo:(RawVideoFrame*)input
                  output:(RawVideoFrame*)output {
    // 上下文
    SwsContext *ctx = nullptr;
    // 輸入缩挑,輸出緩沖區(qū)(指向每一個平面的數(shù)據(jù))(Y U V  apha)
    uint8_t *inData[4], *outData[4];
    // 每一個平面的一行大小
    int inStrides[4], ouStrides[4];
    // 每一幀圖片的大小
    int inFrameSize, outFrameSize;
    int ret = 0;
    // 創(chuàng)建上下文
    ctx = sws_getContext(input->width, input->height, input->format,
                         output->width, output->height, output->format,
                         SWS_BILINEAR, nullptr, nullptr, nullptr);
    if(!ctx) {
        NSLog(@"sws_getContext error");
        goto end;
    }
    // 輸入緩沖區(qū)
    ret = av_image_alloc(inData, inStrides,
                         input->width,
                         input->height,
                         input->format, 1);
    END(av_image_alloc);
    // 輸出緩沖區(qū)
    ret = av_image_alloc(outData, ouStrides,
                         output->width,
                         output->height,
                         output->format, 1);
    END(av_image_alloc);
    
    // 計算每一幀的大小
    inFrameSize = av_image_get_buffer_size(input->format, input->width, input->height, 1);
    outFrameSize = av_image_get_buffer_size(output->format, output->width, output->height, 1);
    
    // 拷貝輸入數(shù)據(jù)
    memcpy(inData[0], input->pixels, inFrameSize);
    
    // 轉(zhuǎn)換
    sws_scale(ctx,
              inData, inStrides, 0, input->height,
              outData, ouStrides);
    // 寫到輸出文件去
    output->frameSize = outFrameSize;
    output->pixels = (char*)malloc(outFrameSize);
    memcpy(output->pixels, outData[0], outFrameSize);

end:
    NSLog(@"end");
    av_freep(&inData[0]);
    av_freep(&outData[0]);
    sws_freeContext(ctx);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鬓梅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谨湘,老刑警劉巖绽快,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異紧阔,居然都是意外死亡坊罢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門擅耽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來活孩,“玉大人,你說我怎么就攤上這事乖仇『度澹” “怎么了询兴?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長起趾。 經(jīng)常有香客問我诗舰,道長,這世上最難降的妖魔是什么训裆? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任眶根,我火速辦了婚禮,結(jié)果婚禮上边琉,老公的妹妹穿的比我還像新娘属百。我一直安慰自己,他們只是感情好变姨,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布族扰。 她就那樣靜靜地躺著,像睡著了一般钳恕。 火紅的嫁衣襯著肌膚如雪别伏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天忧额,我揣著相機與錄音厘肮,去河邊找鬼。 笑死睦番,一個胖子當(dāng)著我的面吹牛类茂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播托嚣,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼巩检,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了示启?” 一聲冷哼從身側(cè)響起兢哭,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夫嗓,沒想到半個月后迟螺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡舍咖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年矩父,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排霉。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡窍株,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情球订,我是刑警寧澤后裸,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站辙售,受9級特大地震影響轻抱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旦部,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一祈搜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧士八,春花似錦容燕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蝗茁,卻和暖如春醋虏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哮翘。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工颈嚼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人饭寺。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓阻课,卻偏偏與公主長得像,于是被迫代替她去往敵國和親艰匙。 傳聞我的和親對象是個殘疾皇子限煞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容