//
// LiveTelecastController.m
// FFmpegDemo
//
// Created by huoliquankai on 2017/7/20.
// Copyright ? 2017年 深圳機(jī)械行業(yè)協(xié)會. All rights reserved.
//
#import "LiveTelecastController.h"
#import <AVFoundation/AVFoundation.h>
#import "avcodec.h"
#import "imgutils.h"
#import "avdevice.h"
#import "swscale.h"
#import "x264.h"
#include "time.h"
#import "GPUImage.h"
@interface LiveTelecastController () <AVCaptureVideoDataOutputSampleBufferDelegate>
@property (nonatomic, strong)AVCaptureSession *session;
@property (nonatomic, strong)AVCaptureDeviceInput *videoInput;
@property (nonatomic, strong)AVCaptureVideoDataOutput *videoDataOutput;
@property (nonatomic, strong)UIView *cameraShowView;
@property (nonatomic, strong)AVCaptureVideoPreviewLayer *previewLayer;
@end
@implementation LiveTelecastController
{
AVOutputFormat *oFmt;
AVFormatContext *pFormatCtx;
AVStream *video_st;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame;
AVPacket pkt;
int y_size;
int framecnt;
int encoder_h264_frame_width;
int encoder_h264_frame_height;
unsigned char *picture_buf;
int picture_size;
//
AVStream *out_stream;
int frame_count;
int y_length;
int uv_length;
AVFormatContext *ofmt_ctx;
// AVFrame *yuv_frame;
int src_height;
int src_width;
int64_t start_time;
}
- (instancetype)init
{
self = [super init];
if (self) {
[self initialSession];
[self initialCameraShowView];
}
return self;
}
- (void)initialSession {
self.session = [[AVCaptureSession alloc] init];
self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backCamera] error:nil];
self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
// 表示設(shè)置攝像頭返回的數(shù)據(jù)類型為YUV420SP類型
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange], kCVPixelBufferPixelFormatTypeKey,
[NSNumber numberWithInt: 640], (id)kCVPixelBufferWidthKey,
[NSNumber numberWithInt: 480], (id)kCVPixelBufferHeightKey,
nil];
[self.videoDataOutput setVideoSettings:outputSettings];
dispatch_queue_t queue = dispatch_queue_create("linlinqi", NULL);
[self.videoDataOutput setSampleBufferDelegate:self queue:queue];
if ([self.session canAddInput:self.videoInput]) {
[self.session addInput:self.videoInput];
}
if ([self.session canAddOutput:self.videoDataOutput]) {
[self.session addOutput:self.videoDataOutput];
} else {
NSLog(@"failed get output");
}
}
- (void)initialCameraShowView {
self.cameraShowView = [[UIView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.cameraShowView];
}
- (AVCaptureDevice *)backCamera {
return [self cameraWithPosition:AVCaptureDevicePositionBack];
}
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
AVCaptureDeviceDiscoverySession *devicesIOS10 = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
NSArray *devicesIOS = devicesIOS10.devices;
for (AVCaptureDevice *device in devicesIOS) {
if (device.position == position) {
return device;
}
}
return nil;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self setUpCameraLayer];
}
//啟動攝像頭捕獲數(shù)據(jù)
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)setUpCameraLayer {
if (self.previewLayer == nil) {
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
UIView *view = self.cameraShowView;
CALayer *viewLayer = [view layer];
[viewLayer setMasksToBounds:YES];
CGRect bounds = [view bounds];
[self.previewLayer setFrame:bounds];
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
[viewLayer addSublayer:self.previewLayer];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
if (self.session) {
[self streamerInit];
[self.session startRunning];
}
}
#pragma AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo]) {
//視頻數(shù)據(jù)
//
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
//獲取Y分量
UInt8 *bufferPrt = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
//獲取UV分量
UInt8 *bufferPrt1 = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
size_t width = CVPixelBufferGetWidth(pixelBuffer);
size_t height = CVPixelBufferGetHeight(pixelBuffer);
size_t bytesrow0 = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
size_t bytesrow1 = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
UInt8 *yuv420_data = (UInt8 *)malloc(width * height * 3/2);
/*convert NV21 data to YUV420*/
UInt8 *pY = bufferPrt;
UInt8 *pUV = bufferPrt1;
UInt8 *pU = yuv420_data + width * height;
UInt8 *pV = pU + width * height / 4;
for (int i = 0; i < height; i ++) {
memcpy(yuv420_data + i * width, pY + i * bytesrow0, width);
}
for (int j = 0; j < height/2; j ++) {
for (int i = 0; i < width/2; i ++) {
*(pU++) = pUV[i<<1];
*(pV++) = pUV[(i<<1) + 1];
//*pU = pUV[2*i];
//pU++;
}
pUV += bytesrow1;
}
//這里可以開始使用yuv420_data去編碼為視頻了
[self yuv420ToH264:yuv420_data];
//編碼完成后
free(yuv420_data);
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}
}
- (void)streamerInit {
int ret = 0;
const char *address = "rtmp://192.168.0.111/live/livestream";
encoder_h264_frame_width = 1920;
encoder_h264_frame_height = 1080;
src_width = 1920;
src_height = 1080;
y_length = encoder_h264_frame_width * encoder_h264_frame_height;
uv_length = y_length/4;
//
// av_log_set_callback(void (*callback)(void *, int, const char *, va_list))
av_register_all();
avformat_network_init();
avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", address);
if (!ofmt_ctx) {
printf("不能打開輸出");
return;
}
//尋找編碼器
pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!pCodec) {
printf("找不到編碼器");
return;
}
//初始化編碼器操作者
pCodecCtx = avcodec_alloc_context3(pCodec);
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; //指定編碼格式
pCodecCtx->width = encoder_h264_frame_width;
pCodecCtx->height = encoder_h264_frame_height;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 30;
pCodecCtx->bit_rate = 400000;
// pCodecCtx->rc_max_rate = 400000;
// pCodecCtx->rc_max_rate = 400000;
// pCodecCtx->rc_buffer_size = 200000;
pCodecCtx->gop_size = 250;
if(ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
pCodecCtx->max_b_frames = 0;
AVDictionary *dicParams = NULL;
av_dict_set(&dicParams, "preset", "slow", 0);
av_dict_set(&dicParams, "tune", "zerolatency", 0);
//打開編碼器
if(avcodec_open2(pCodecCtx, pCodec, &dicParams) < 0) {
printf("Failed to open encoder!\n");
return;
}
//新建輸出流
out_stream = avformat_new_stream(ofmt_ctx, pCodec);
if(!out_stream) {
printf("Failed allocation output stream\n");
return;
}
out_stream->time_base.num = 1;
out_stream->time_base.den = 30;
//復(fù)制一份編碼器的配置給輸出流
// avcodec_copy_context(out_stream->codec, pCodecCtx);
avcodec_parameters_from_context(out_stream->codecpar, pCodecCtx);
ret = avio_open(&ofmt_ctx->pb, address, AVIO_FLAG_WRITE);
if(ret < 0) {
printf("Could not open output URL %s", address);
return;
}
ret = avformat_write_header(ofmt_ctx, NULL);
if(ret < 0) {
printf("Error occurred when open output URL\n");
return;
}
//初始化一個幀的數(shù)據(jù)結(jié)構(gòu),用于編碼用
//指定AV_PIX_FMT_YUV420P這種格式的
pFrame = av_frame_alloc();
uint8_t *out_buffer = (uint8_t *) av_malloc(av_image_get_buffer_size(pCodecCtx->pix_fmt, src_width, src_height, 1));
av_image_fill_arrays(pFrame->data, pFrame->linesize, out_buffer, pCodecCtx->pix_fmt, src_width, src_height, 1);
start_time = av_gettime();
}
int encode(AVCodecContext *pCodecCtx, AVPacket* pPkt, AVFrame *pFrame, int *got_packet) {
int ret;
*got_packet = 0;
ret = avcodec_send_frame(pCodecCtx, pFrame);
if(ret <0 && ret != AVERROR_EOF) {
return ret;
}
ret = avcodec_receive_packet(pCodecCtx, pPkt);
if(ret < 0 && ret != AVERROR(EAGAIN)) {
return ret;
}
if(ret >= 0) {
*got_packet = 1;
}
return 0;
}
- (void)yuv420ToH264:(UInt8 *)yuv420_data {
//
picture_buf = yuv420_data;
pFrame->data[0] = picture_buf;//y分量占一份所以 // Y
pFrame->data[1] = picture_buf+ y_length; // U
pFrame->data[2] = picture_buf+ y_length*5/4;
pFrame->pts = (1.0 / 30) * 90 * frame_count;
int got_picture = 0;
// Encode
pFrame->width = encoder_h264_frame_width;
pFrame->height = encoder_h264_frame_height;
pFrame->format = AV_PIX_FMT_YUV420P;
int ret = encode(pCodecCtx, &pkt, pFrame, &got_picture);
// encode(pCodecCtx, &pkt, pFrame, &got_picture)
if(ret < 0) {
printf("Failed to encode! \n");
return;
}
//ofmt_ctx
if (got_picture == 1) {
printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
framecnt++;
pkt.stream_index = out_stream->index;
//寫PTS/DTS
AVRational time_base = ofmt_ctx->streams[0]->time_base;
AVRational r_frame_ratel = {30, 2};
AVRational time_base_q = {1, AV_TIME_BASE};
int64_t calc_duration = (double)(AV_TIME_BASE) * (1/av_q2d(r_frame_ratel));
// int64_t calc_duration = (double)AV_TIME_BASE/av_q2d(ofmt_ctx->streams[0]->r_frame_rate);
pkt.pts = av_rescale_q(frame_count * calc_duration, time_base_q, time_base);
pkt.dts = pkt.pts;
pkt.pos = -1;
frame_count ++;
ofmt_ctx->duration = pkt.duration * frame_count;
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
printf("-====");
return;
}
av_packet_unref(&pkt);
}
// av_write_trailer(pFormatCtx);
}
@end
[木木方文技術(shù)分享之音視頻五]FFmpeg+x264攝像頭直播推流
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 上篇文章介紹了FFmpeg+x264的編譯也可以去下載我已經(jīng)編譯好的,star一個么么噠丷丷https://git...
- 先下載好所有資源 編譯x264:1、復(fù)制gas-preprocessor.pl到/usr/local/bin 2、...
- 前幾天看到一篇文章中寫到,現(xiàn)實(shí)生活中“歡樂頌”式的五姐妹不可能存在。理由是,五個人生活層次相差太大,如安迪,華爾街...