范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

iOS完整推流采集音视频数据编码同步合成流

  需求
  众所周知,原始的音视频数据无法直接在网络上传输,推流需要编码后的音视频数据以合成的视频流,如flv, mov, asf流等,根据接收方需要的格式进行合成并传输,这里以合成asf流为例,讲述一个完整推流过程:即音视频从采集到编码,同步合成asf视频流,然后可以将流进行传输,为了方便,本例将合成的视频流写入一个asf文件中,以供测试.
  注意: 测试需要使用终端通过: ffplay播放demo中录制好的文件,因为asf是windows才支持的格式,mac自带播放器无法播放. 1、实现原理采集: 采集视频帧使用AVCaptureSession,采集音频帧使用Audio Unit 编码: 编码视频数据使用VideoToolbox中vtCompresssion硬编,编码音频数据使用audio converter软编. 同步: 根据时间戳生成策略 合成: 使用FFmpeg mux编码的音视频数据以合成视频流 后续: 合成好的视频流可以通过网络传输或是录制成文件 2、阅读前提音视频基础知识 推荐必读:H264, H265硬件编解码基础及码流分析 iOS视频采集实战(AVCaptureSession) Audio Unit采集音频实战 视频编码实战 音频编码实战 iOS FFmpeg环境搭建
  代码地址  : iOS完整推流
  掘金地址  : iOS完整推流
  简书地址  : iOS完整推流
  博客地址  : iOS完整推流 3、总体架构
  1.mux
  对于iOS而言,我们可以通过底层API捕获视频帧与音频帧数据,捕获视频帧使用 AVFoundation  框架中的 AVCaptureSession  , 其实它同时也可以捕获音频数据,而因为我们想使用最低延时与最高音质的音频, 所以需要借助最底层的音频捕捉框架 Audio Unit  ,然后使用 VideoToolbox  框架中的 VTCompressionSessionRef  可以对视频数据进行编码,使用 AudioConverter  可以对音频数据进行编码,我们在采集时可以将第一帧I帧产生时的系统时间作为音视频时间戳的一个起点,往后的视频说都基于此,由此可扩展做音视频同步方案,最终,我们将比那编码好的音视频数据通过FFmpeg进行合成,这里以asf流为例进行合成,并将生成好的asf流写入文件,以供测试. 生成好的asf流可直接用于网络传输. 3.1 简易流程
  采集视频 创建 AVCaptureSession  对象 指定分辨率: sessionPreset/activeFormat  ,指定帧率 setActiveVideoMinFrameDuration/setActiveVideoMaxFrameDuration  指定摄像头位置: AVCaptureDevice  指定相机其他属性: 曝光,对焦,闪光灯,手电筒等等... 将摄像头数据源加入session 指定采集视频的格式:yuv,rgb.... kCVPixelBufferPixelFormatTypeKey  将输出源加入session 创建接收视频帧队列: - (void)setSampleBufferDelegate:(nullable id)sampleBufferDelegate queue:(nullable dispatch_queue_t)sampleBufferCallbackQueue  将采集视频数据渲染到屏幕: AVCaptureVideoPreviewLayer  在回调函数中获取视频帧数据:  CMSampleBufferRef
  采集音频 配置音频格式ASBD: 采样率,声道数,采样位数,数据精度,每个包中字节数等等... 设置采样时间:  setPreferredIOBufferDuration  创建audio unit对象,指定分类.  AudioComponentInstanceNew  设置audio unit属性: 打开输入,禁止输出... 为接收的音频数据分配大小 kAudioUnitProperty_ShouldAllocateBuffer  设置接收数据的回调 开始audio unit:  AudioOutputUnitStart  在回调函数中获取音频数据:  AudioUnitRender
  编码视频数据 指定编码器宽高类型回调并创建上下文对象:  VTCompressionSessionCreate  设置编码器属性:缓存帧数, 帧率, 平均码率, 最大码率, 实时编码, 是否重排序, 配置信息, 编码模式, I帧间隔时间等. 准备编码数据:  VTCompressionSessionPrepareToEncodeFrames  开始编码:  VTCompressionSessionEncodeFrame  回调函数中获取编码后的数据 CMBlockBufferRef  根据合成码流格式,这里是asf所以需要Annex B格式,自己组装sps,pps,start code.
  编码音频数据 提供原始数据类型与编码后数据类型的ASBD 指定编码器类型 kAudioEncoderComponentType  创建编码器 AudioConverterNewSpecific  设置编码器属性: 比特率, 编码质量等 将1024个采样点原始PCM数据传入编码器 开始编码:  AudioConverterFillComplexBuffer  获取编码后的AAC数据
  音视频同步
  以编码的第一帧视频的系统时间作为音视频数据的基准时间戳,随后将采集到音视频数据中的时间戳减去该基准时间戳作为各自的时间戳, 同步有两种策略,一种是以音频时间戳为准, 即当出现错误时,让视频时间戳去追音频时间戳,这样做即会造成看到画面会快进或快退,二是以视频时间戳为准,即当出现错误时,让音频时间戳去追视时间戳,即声音可能会刺耳,不推荐.所以一般使用第一种方案,通过估计下一帧视频时间戳看看如果超出同步范围则进行同步.
  FFmpeg合成数据流 初始化FFmpeg相关参数: AVFormatContext (管理合成上下文), AVOutputFormat(合成流格式), AVStream(音视频数据流)... 创建上下文对象 AVFormatContext  :  avformat_alloc_context  根据数据类型生成编码器 AVCodec  :  avcodec_find_encoder   视频: AV_CODEC_ID_H264/AV_CODEC_ID_HEVC  ,音频: AV_CODEC_ID_AAC  生成流 AVStream  :  avformat_new_stream  指定音视频流中各个参数信息, 如数据格式,视频宽高帧率,比特率,基准时间,extra data, 音频:采样率,声道数, 采样位数等等. 指定上下文及流格式中的音视频编码器id:  video_codec_id, audio_codec_id  生成视频流头数据: 当音视频编码器都填充到上下文对象后,即可生产该类型对应的头信息, 此头信息作为解码音视频数据的重要信息,一定需要正确合成. avformat_write_header  将音视频数据装入动态数组中. 合成音视频数据: 通过另一条线程取出动态数组中的音视频数据,通过比较时间戳的方式进行同步合成. 将音视频数据装入AVPacket中 产生合成的数据 av_write_frame
  C++音视频学习资料免费获取方法:关注音视频开发T哥  ,点击「链接」即可免费获取2023年最新 C++音视频开发进阶独家免费学习大礼包! 3.2 文件结构
  2.file3.3 快速使用初始化相关模块 - (void)viewDidLoad {     [super viewDidLoad];     // Do any additional setup after loading the view.          [self configureCamera];     [self configureAudioCapture];     [self configureAudioEncoder];     [self configurevideoEncoder];     [self configureAVMuxHandler];     [self configureAVRecorder]; }在相机回调中将原始yuv数据送去编码 - (void)xdxCaptureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {     if ([output isKindOfClass:[AVCaptureVideoDataOutput class]] == YES) {         if (self.videoEncoder) {             [self.videoEncoder startEncodeDataWithBuffer:sampleBuffer                                         isNeedFreeBuffer:NO];                      }              } }通过回调函数接收编码后的视频数据并将其送给合成流类. #pragma mark Video Encoder - (void)receiveVideoEncoderData:(XDXVideEncoderDataRef)dataRef {     [self.muxHandler addVideoData:dataRef->data size:(int)dataRef->size timestamp:dataRef->timestamp isKeyFrame:dataRef->isKeyFrame isExtraData:dataRef->isExtraData videoFormat:XDXMuxVideoFormatH264]; }在采集音频回调中接收音频数据并编码,最终将编码数据也送入合成流类 #pragma mark Audio Capture and Audio Encode - (void)receiveAudioDataByDevice:(XDXCaptureAudioDataRef)audioDataRef {     [self.audioEncoder encodeAudioWithSourceBuffer:audioDataRef->data                                   sourceBufferSize:audioDataRef->size                                                pts:audioDataRef->pts                                    completeHandler:^(XDXAudioEncderDataRef dataRef) {                                        if (dataRef->size > 10) {                                            [self.muxHandler addAudioData:(uint8_t *)dataRef->data                                                                     size:dataRef->size                                                               channelNum:1                                                               sampleRate:44100                                                                timestamp:dataRef->pts];                                                                                   }                                        free(dataRef->data);                                    }]; }先写文件后,随后接收合成后的数据并写入文件. #pragma mark Mux - (IBAction)startRecordBtnDidClicked:(id)sender {     int size = 0;     char *data = (char *)[self.muxHandler getAVStreamHeadWithSize:&size];     [self.recorder startRecordWithIsHead:YES data:data size:size];     self.isRecording = YES; }   - (void)receiveAVStreamWithIsHead:(BOOL)isHead data:(uint8_t *)data size:(int)size {     if (isHead) {         return;     }          if (self.isRecording) {         [self.recorder startRecordWithIsHead:NO data:(char *)data size:size];     } }4、具体实现
  本例中音视频采集编码模块在前面文章中已经详细介绍,这里不再重复,如需帮助请参考上文的阅读前提.下面仅介绍合成流. 4.1 初始化FFmpeg相关对象.AVFormatContext: 管理合成流上下文对象 AVOutputFormat: 合成流的格式,这里使用的asf数据流 AVStream: 音视频数据流具体信息 - (void)configureFFmpegWithFormat:(const char *)format {     if(m_outputContext != NULL) {         av_free(m_outputContext);         m_outputContext = NULL;     }          m_outputContext = avformat_alloc_context();     m_outputFormat  = av_guess_format(format, NULL, NULL);          m_outputContext->oformat    = m_outputFormat;     m_outputFormat->audio_codec = AV_CODEC_ID_NONE;     m_outputFormat->video_codec = AV_CODEC_ID_NONE;     m_outputContext->nb_streams = 0;          m_video_stream     = avformat_new_stream(m_outputContext, NULL);     m_video_stream->id = 0;     m_audio_stream     = avformat_new_stream(m_outputContext, NULL);     m_audio_stream->id = 1;          log4cplus_info(kModuleName, "configure ffmpeg finish."); }4.2 配置视频流的详细信息
  设置该编码的视频流中详细的信息, 如编码器类型,配置信息,原始视频数据格式,视频的宽高,比特率,帧率,基准时间戳,extra data等.
  这里最重要的就是extra data,注意,因为我们要根据extra data才能生成正确的头数据,而asf流需要的是annux b格式的数据,苹果采集的视频数据格式为avcc所以在编码模块中已经将其转为annux b格式的数据,并通过参数传入,这里可以直接使用,关于这两种格式区别也可以参考阅读前提中的码流介绍的文章. - (void)configureVideoStreamWithVideoFormat:(XDXMuxVideoFormat)videoFormat extraData:(uint8_t *)extraData extraDataSize:(int)extraDataSize {     if (m_outputContext == NULL) {         log4cplus_error(kModuleName, "%s: m_outputContext is null",__func__);         return;     }          if(m_outputFormat == NULL){         log4cplus_error(kModuleName, "%s: m_outputFormat is null",__func__);         return;     }      AVFormatContext *formatContext = avformat_alloc_context();     AVStream *stream = NULL;     if(XDXMuxVideoFormatH264 == videoFormat) {         AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);         stream = avformat_new_stream(formatContext, codec);         stream->codecpar->codec_id = AV_CODEC_ID_H264;     }else if(XDXMuxVideoFormatH265 == videoFormat) {         AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_HEVC);         stream = avformat_new_stream(formatContext, codec);         stream->codecpar->codec_tag      = MKTAG("h", "e", "v", "c");         stream->codecpar->profile        = FF_PROFILE_HEVC_MAIN;         stream->codecpar->format         = AV_PIX_FMT_YUV420P;         stream->codecpar->codec_id       = AV_CODEC_ID_HEVC;     }          stream->codecpar->format             = AV_PIX_FMT_YUVJ420P;     stream->codecpar->codec_type         = AVMEDIA_TYPE_VIDEO;     stream->codecpar->width              = 1280;     stream->codecpar->height             = 720;     stream->codecpar->bit_rate           = 1024*1024;     stream->time_base.den                = 1000;     stream->time_base.num                = 1;     stream->time_base                    = (AVRational){1, 1000};     stream->codec->flags                |= AV_CODEC_FLAG_GLOBAL_HEADER;          memcpy(m_video_stream, stream, sizeof(AVStream));          if(extraData) {         int newExtraDataSize = extraDataSize + AV_INPUT_BUFFER_PADDING_SIZE;         m_video_stream->codecpar->extradata_size = extraDataSize;         m_video_stream->codecpar->extradata      = (uint8_t *)av_mallocz(newExtraDataSize);         memcpy(m_video_stream->codecpar->extradata, extraData, extraDataSize);     }          av_free(stream);      m_outputContext->video_codec_id = m_video_stream->codecpar->codec_id;     m_outputFormat->video_codec     = m_video_stream->codecpar->codec_id;          self.isReadyForVideo = YES;          [self productStreamHead]; }4.3 配置音频流的详细信息
  首先根据编码音频的类型生成编码器并生成流对象,然后 配置音频流的详细信息,如压缩数据格式,采样率,声道数,比特率,extra data等等.这里要注意的是extra data是为了保存mp4文件时播放器能够正确解码播放准备的,可以参考这几篇文章:audio extra data1,audio extra data2 - (void)configureAudioStreamWithChannelNum:(int)channelNum sampleRate:(int)sampleRate {     AVFormatContext *formatContext  = avformat_alloc_context();     AVCodec         *codec          = avcodec_find_encoder(AV_CODEC_ID_AAC);     AVStream        *stream         = avformat_new_stream(formatContext, codec);          stream->index         = 1;     stream->id            = 1;     stream->duration      = 0;     stream->time_base.num = 1;     stream->time_base.den = 1000;     stream->start_time    = 0;     stream->priv_data     = NULL;          stream->codecpar->codec_type     = AVMEDIA_TYPE_AUDIO;     stream->codecpar->codec_id       = AV_CODEC_ID_AAC;     stream->codecpar->format         = AV_SAMPLE_FMT_S16;     stream->codecpar->sample_rate    = sampleRate;     stream->codecpar->channels       = channelNum;     stream->codecpar->bit_rate       = 0;     stream->codecpar->extradata_size = 2;     stream->codecpar->extradata      = (uint8_t *)malloc(2);     stream->time_base.den  = 25;     stream->time_base.num  = 1;          /*      * why we put extra data here for audio: when save to MP4 file, the player can not decode it correctly      * http://ffmpeg-users.933282.n4.nabble.com/AAC-decoder-td1013071.html      * http://ffmpeg.org/doxygen/trunk/mpeg4audio_8c.html#aa654ec3126f37f3b8faceae3b92df50e      * extra data have 16 bits:      * Audio object type - normally 5 bits, but 11 bits if AOT_ESCAPE      * Sampling index - 4 bits      * if (Sampling index == 15)      * Sample rate - 24 bits      * Channel configuration - 4 bits      * last reserved- 3 bits      * for exmpale:  "Low Complexity Sampling frequency 44100Hz, 1 channel mono":      * AOT_LC == 2 -> 00010      -              * 44.1kHz == 4 -> 0100      +              * 44.1kHz == 4 -> 0100  48kHz == 3 -> 0011      * mono == 1 -> 0001      * so extra data: 00010 0100 0001 000 ->0x12 0x8      +                  00010 0011 0001 000 ->0x11 0x88      +      */          if (stream->codecpar->sample_rate == 44100) {         stream->codecpar->extradata[0] = 0x12;         //iRig mic HD have two chanel 0x11         if(channelNum == 1)             stream->codecpar->extradata[1] = 0x8;         else             stream->codecpar->extradata[1] = 0x10;     }else if (stream->codecpar->sample_rate == 48000) {         stream->codecpar->extradata[0] = 0x11;         //iRig mic HD have two chanel 0x11         if(channelNum == 1)             stream->codecpar->extradata[1] = 0x88;         else             stream->codecpar->extradata[1] = 0x90;     }else if (stream->codecpar->sample_rate == 32000){         stream->codecpar->extradata[0] = 0x12;         if (channelNum == 1)             stream->codecpar->extradata[1] = 0x88;         else             stream->codecpar->extradata[1] = 0x90;     }     else if (stream->codecpar->sample_rate == 16000){         stream->codecpar->extradata[0] = 0x14;         if (channelNum == 1)             stream->codecpar->extradata[1] = 0x8;         else             stream->codecpar->extradata[1] = 0x10;     }else if(stream->codecpar->sample_rate == 8000){         stream->codecpar->extradata[0] = 0x15;         if (channelNum == 1)             stream->codecpar->extradata[1] = 0x88;         else             stream->codecpar->extradata[1] = 0x90;     }          stream->codec->flags|= AV_CODEC_FLAG_GLOBAL_HEADER;          memcpy(m_audio_stream, stream, sizeof(AVStream));          av_free(stream);          m_outputContext->audio_codec_id = stream->codecpar->codec_id;     m_outputFormat->audio_codec     = stream->codecpar->codec_id;          self.isReadyForAudio = YES;      [self productStreamHead]; }4.4 生成流头数据
  当前面2,3部都配置完成后,我们将音视频流注入上下文对象及对象中的流格式中,即可开始生成头数据. avformat_write_header  - (void)productStreamHead {     log4cplus_debug("record", "%s,line:%d",__func__,__LINE__);          if (m_outputFormat->video_codec == AV_CODEC_ID_NONE) {         log4cplus_error(kModuleName, "%s: video codec is NULL.",__func__);         return;     }          if(m_outputFormat->audio_codec == AV_CODEC_ID_NONE) {         log4cplus_error(kModuleName, "%s: audio codec is NULL.",__func__);         return;     }          /* prepare header and save header data in a stream */     if (avio_open_dyn_buf(&m_outputContext->pb) < 0) {         avio_close_dyn_buf(m_outputContext->pb, NULL);         log4cplus_error(kModuleName, "%s: AVFormat_HTTP_FF_OPEN_DYURL_ERROR.",__func__);         return;     }              /*      * HACK to avoid mpeg ps muxer to spit many underflow errors      * Default value from FFmpeg      * Try to set it use configuration option      */     m_outputContext->max_delay = (int)(0.7*AV_TIME_BASE);              int result = avformat_write_header(m_outputContext,NULL);     if (result < 0) {         log4cplus_error(kModuleName, "%s: Error writing output header, res:%d",__func__,result);         return;     }              uint8_t * output = NULL;     int len = avio_close_dyn_buf(m_outputContext->pb, (uint8_t **)(&output));     if(len > 0 && output != NULL) {         av_free(output);                  self.isReadyForHead = YES;                  if (m_avhead_data) {             free(m_avhead_data);         }         m_avhead_data_size = len;         m_avhead_data = (uint8_t *)malloc(len);         memcpy(m_avhead_data, output, len);                  if ([self.delegate respondsToSelector:@selector(receiveAVStreamWithIsHead:data:size:)]) {             [self.delegate receiveAVStreamWithIsHead:YES data:output size:len];         }                  log4cplus_error(kModuleName, "%s: create head length = %d",__func__, len);     }else{         self.isReadyForHead = NO;         log4cplus_error(kModuleName, "%s: product stream header failed.",__func__);     } }4.5 然后将传来的音视频数据装入数组中
  该数组通过封装C++中的vector实现一个轻量级数据结构以缓存数据. 4.6 合成音视频数据
  新建一条线程专门合成音视频数据,合成策略即取出音视频数据中时间戳较小的一帧先写,因为音视频数据总体偏差不大,所以理想情况应该是取一帧视频,一帧音频,当然因为音频采样较快,可能会相对多一两帧,而当音视频数据由于某种原因不同步时,则会等待,直至时间戳重新同步才能继续进行合成.     int err = pthread_create(&m_muxThread,NULL,MuxAVPacket,(__bridge_retained void *)self);     if(err != 0){         log4cplus_error(kModuleName, "%s: create thread failed: %s",__func__, strerror(err));     }          void * MuxAVPacket(void *arg) {     pthread_setname_np("XDX_MUX_THREAD");     XDXAVStreamMuxHandler *instance = (__bridge_transfer XDXAVStreamMuxHandler *)arg;     if(instance != nil) {         [instance dispatchAVData];     }          return NULL; }  #pragma mark Mux - (void)dispatchAVData {     XDXMuxMediaList audioPack;     XDXMuxMediaList videoPack;          memset(&audioPack, 0, sizeof(XDXMuxMediaList));     memset(&videoPack, 0, sizeof(XDXMuxMediaList));          [m_AudioListPack reset];     [m_VideoListPack reset];      while (true) {         int videoCount = [m_VideoListPack count];         int audioCount = [m_AudioListPack count];         if(videoCount == 0 || audioCount == 0) {             usleep(5*1000);             log4cplus_debug(kModuleName, "%s: Mux dispatch list: v:%d, a:%d",__func__,videoCount, audioCount);             continue;         }                  if(audioPack.timeStamp == 0) {             [m_AudioListPack popData:&audioPack];         }                  if(videoPack.timeStamp == 0) {             [m_VideoListPack popData:&videoPack];         }                  if(audioPack.timeStamp >= videoPack.timeStamp) {             log4cplus_debug(kModuleName, "%s: Mux dispatch input video time stamp = %llu",__func__,videoPack.timeStamp);                          if(videoPack.data != NULL && videoPack.data->data != NULL){                 [self addVideoPacket:videoPack.data                            timestamp:videoPack.timeStamp                  extraDataHasChanged:videoPack.extraDataHasChanged];                                  av_free(videoPack.data->data);                 av_free(videoPack.data);             }else{                 log4cplus_error(kModuleName, "%s: Mux Video AVPacket data abnormal",__func__);             }             videoPack.timeStamp = 0;         }else {             log4cplus_debug(kModuleName, "%s: Mux dispatch input audio time stamp = %llu",__func__,audioPack.timeStamp);                          if(audioPack.data != NULL && audioPack.data->data != NULL) {                 [self addAudioPacket:audioPack.data                            timestamp:audioPack.timeStamp];                 av_free(audioPack.data->data);                 av_free(audioPack.data);             }else {                 log4cplus_error(kModuleName, "%s: Mux audio AVPacket data abnormal",__func__);             }                          audioPack.timeStamp = 0;         }     } }4.7 获取合成好的视频流
  通过 av_write_frame  即可获取合成好的数据. - (void)productAVDataPacket:(AVPacket *)packet extraDataHasChanged:(BOOL)extraDataHasChanged {     BOOL    isVideoIFrame = NO;     uint8_t *output       = NULL;     int     len           = 0;          if (avio_open_dyn_buf(&m_outputContext->pb) < 0) {         return;     }          if(packet->stream_index == 0 && packet->flags != 0) {         isVideoIFrame = YES;     }          if (av_write_frame(m_outputContext, packet) < 0) {         avio_close_dyn_buf(m_outputContext->pb, (uint8_t **)(&output));         if(output != NULL)             free(output);                  log4cplus_error(kModuleName, "%s: Error writing output data",__func__);         return;     }               len = avio_close_dyn_buf(m_outputContext->pb, (uint8_t **)(&output));          if(len == 0 || output == NULL) {         log4cplus_debug(kModuleName, "%s: mux len:%d or data abnormal",__func__,len);         if(output != NULL)             av_free(output);         return;     }              if ([self.delegate respondsToSelector:@selector(receiveAVStreamWithIsHead:data:size:)]) {         [self.delegate receiveAVStreamWithIsHead:NO data:output size:len];     }          if(output != NULL)         av_free(output); }
  原文链接:iOS瀹屾暣鎺ㄦ祦閲囬泦闊宠 棰戞暟鎹 紪鐮佸悓姝ュ悎鎴愭祦 - 绠 涔

美团20年公布骑手有6万研究生17万本科生在如今互联网时代的发展,袋鼠小哥方便了我们的生活,美团也为我们解决了一日三餐的烦恼,方便了生活。美团发布的骑手就业报告在20年上半年美团拥有骑手超过300万,大专,本科,研究生占2G5勇士3点优势胜绿军NBA总决赛G5大战今天上午开打,勇士队水花不老尚能饭可,威金斯大发神威,展状元风采,其他上场球员三军用命多点开花,一场大胜,乾坤在握。本场比赛勇士有三点优势一战术安排出敌意料。凯5人得分上双,勇士拿下天王山夺取赛点比赛一开始绿凯策略就是攻击内线,而勇士偏偏把护框做得很好,从防守做起,抓了很多半转换让比赛的节奏属于自己,防三分则是打死不跳,让绿凯去选择更有难度的攻框,绿凯第一节攻击篮下的效率很大崩盘!NBA17冠王溃败,超巨95失误创耻辱纪录,库里狂笑庆祝北京时间6月14日,NBA总决赛G5勇士10494击败凯尔特人,32拿到冠军点!这场比赛,凯尔特人在最多落后16分的情况下,第三节一度反超。但关键的第四节,他们再度哑火失误不断。普龚翔宇,郎平最爱的徒弟之一,24岁的她疑似单身,你喜欢她吗?如果郎平没有参加东京奥运会,可能她会带着一束束鲜花退休。但是东京奥运会的惨败,一夜间郎平的名声陨落下来,郎平是能力突出品行完美,深受球员和球迷爱戴的教练。从80年代五连冠,到90年又一名过气老将被谢晖激活,本轮中超表现抢眼,差点收获进球大连队在本轮中超迎来了实力不俗的长春亚泰,这场比赛中球队主帅谢晖,再次展现了自己超强的临场指挥能力。谢晖激活了老将朱挺的能力,让他差点就上演进球的好戏。当时朱挺在边路获得了一次进攻昔日火箭弃将,如今荣膺MVP!获联盟东西部两强追逐,他只有25岁近20年来,最强的休斯顿火箭队应该是20172018赛季了,虽然在20082009赛季的休斯顿火箭队也不弱,甚至与当年的总冠军洛杉矶湖人队苦战7场最终34落败,但最顶尖的火箭队,还2年7100万!湖人组建超级阵容的好机会来了,NBA将回到紫金时代?20年湖人为何能做到夺冠?对于这个问题怕是每个球迷心中的答案都不一样,在我看来有那么两点不得不提其一,詹眉的高效发挥。詹姆斯场均27。6分10。8篮板8。8助攻,浓眉场均27。7分NBA现役第一人!字母哥东契奇约基奇戈贝尔恩比德上榜字母哥现役第一人希腊职业篮球运动员扬尼斯阿德托昆博(GiannisAntetokounmpo),因为名字太长被称为字母哥。字母哥作为13年首轮的第15号顺位,被雄鹿选中,仅用了短短30天窃取2000亿条数据,黑客帝国病入膏肓北京日报客户端评论员冬天30天窃取970亿条全球互联网数据和1240亿条电话记录,涉及世界各国大量公民的个人隐私近日,美国曝出网络窃密丑闻,再度揭开了这个黑客帝国背后的重重黑幕。据中英双语数据结构及算法入门教程此教程共10小时,中英双语字幕,画质清晰无水印,源码附件全课程英文名DataStructuresandAlgorithmsforBeginners下载地址百度网盘地址httpspa
2111淘汰泰国,中国女篮打进4强!27岁队长14中11,决战日本北京时间7月10日,2022年三人篮球亚洲杯进入淘汰赛阶段,8强赛上,中国三人篮球国家队两支队伍先后出战。女队这边对阵泰国女篮,在开局双方比分胶着的情况下,中国三人女篮依靠着张芷婷卡佩拉主动找火箭助教交流,斯通无意送走小波特,泰特损失50万面对天赋满满的雷霆,阵容不整的火箭凭借防守赢下了比赛。先后和班切罗以及切特交手的史密斯看起来有些紧张,在进攻端的表现让人失望,好在防守相当努力。史密斯打铁不断,一些球迷直言他是大水亏大了!曝皇马放走6300万先生,老佛爷约维奇,去佛罗伦萨吧皇马引进约维奇的这笔交易让俱乐部亏大了,现在佛罗伦蒂诺决定免费放走6300万先生,自从约维奇来到皇马后,他的表现堪称灾难,其他球队都没有意愿去引进这位皇马锋霸,老佛爷为了节约薪资开勇士老板拉科布谈某球星交易流言球队会评估短期和长期影响直播吧7月10日讯在近日做客记者Kawakami的播客节目TheTKShow时,勇士老板拉科布在没有提及杜兰特名字的情况下回应了外界流传的勇士对杜兰特的兴趣。Kawakami问理论只为总冠军!盘点NBA主动降薪的五大球星牺牲自己打造完美阵容NBA是一个名利场,每个球员进入NBA的目的都是挣更多的钱,赢得一些名气,但有些球员,他们不把金钱摆在一个不可撼动的位置,为了荣誉,为了球队,他们可以做出金钱上的牺牲,主动降薪,来汉密尔顿利索夫斯基遭受不公正批评他能赢下10个排名赛冠军安东尼汉密尔顿相信,杰克利索夫斯基能赢下10个排名赛冠军,并且能打进世界排名前三之列。而汉密尔顿感到,现下利索夫斯基收到的任何批评对他来说都是完全不公平的。现年31岁的利索夫斯基上明眸张开朗功夫嘉年华之战结果已出场上拼到无力,观众看的过瘾7月9日,万众瞩目的第六季功夫嘉年华正式开战,在新一季功夫嘉年华中,张开朗明眸两位主播将进行了一场属于他们的荣誉之战,这场比赛将解决两者在之前直播中发生的纷争。通过两者之间的身高体国际乒联官宣!刘国梁倾力打造赛事推迟,两大世界冠军无缘战国乒国际乒联正式宣布,原定于7月27日31日举办的刘国梁瓦尔德内尔杯乒乓球团体对抗赛确定推迟!国际乒联在官网宣布,在研究协商之后,做出了艰难的推迟决定,未来几周还会公布更多比赛的赛场安43岁罗志祥成功复出!在小巨蛋开演唱会座无虚席,狂瘦26斤状态好罗志祥小巨蛋开唱复出43岁的小猪罗志祥回来了。阔别2年4个多月之后,罗志祥终于成功复出,并且在台湾小巨蛋举行了个人演唱会。从罗志祥的演唱会上可以看出,演唱会全场爆满,座无虚席,相当高价买的iPhone13Pro是扩容机?网友不保值不稳定,亏大了iPhone13系列发布有一段时间了,所以市面上iPhone13系列翻新机越来越多。有换壳的有换屏的还有改数据换电池的。当然也有扩容机。今天这台iPhone13Pro就是扩容机,再AG惨遭GK零封被打回原形,实力差距太大了,网友表示准备好卡位赛AG被GK零封打回原形,三局比赛没有一局看到风暴龙王,实力差距太大了,就连解说李九都说他们其实失误并没有这么大,只是在硬实力方面被碾压了而已,AG还是在S组里吸取经验准备好打卡位赛古天乐的眼泪,唤不醒香港电影明日战记扑街了。扑得很惨。古天乐也哭得很惨。虽然古天乐后来澄清,他并非为了票房不佳而哭。但不可否认的是,这些年他为香港电影东奔西走。为香港电影流泪,也无可指摘。可惜,他的泪水也救不到中国旅游必去的6个城市有你的家乡吗?中国地大物博,历史悠久,拥有数百个城市。外国人想了解中国,可以先去哪些城市呢?下面让我们来看看中国最具特色的8个城市吧1北京想了解一个国家,当然首选就是去该国的首都了。北京作为中国布里吉特是施瓦辛格旧爱二婚嫁史泰龙,54岁为五婚39岁老公生女史泰龙靠着洛奇系列的电影,成为收入上千万的大导演时,施瓦辛格还在好莱坞片场跑龙套。可谁能想到,施瓦辛格用了一招美人计,就让史泰龙成了抛弃糟糠妻的渣男。时间拉回到1984年,导演迪诺迷宫般的千年古城菲斯摩洛哥文图老理行在摩洛哥四大皇城中,距离首都拉巴特190公里的菲斯,是其中最古老的一座。这座由伊德利斯二世修建于公元808年至今延续了千余年的古老城市,无论是外观,还是内涵,都是丰富多彩首发天玑9000!ROGPhone6D游戏手机跑分出炉CPU跑分超骁龙8下半年以来,在安卓顶级旗舰市场,可以说完全是高通骁龙8旗舰平台的天下,截至目前几乎各大品牌都已推出了搭载该芯片的当家旗舰。而在不久前,有不少爆料称上半年几乎与骁龙8平台评分秋色的联2022科隆游戏展首秀之夜游戏盘点,有你心仪的游戏吗?8月24日凌晨,在2022科隆游戏展的首秀之夜上,主持人GeoffKeighley在推特上放出了本次发布会的预热混剪视频,其中包括了微软BOX,万代南梦宫,育碧等上百家游戏厂商参展传奇世界小玩家之大快乐(土豪被虐卖号篇)大家好我是小风,今天聊一下认识的一个土豪合区后攻城被虐的事情。还记得当时印象很深,那是在13年的67区,当时基本是大哥遍地走的时代,能和我们区相抗衡的临边区是没有几个的。之所以这样改善衰老的10个秘诀全文1363字,阅读时长约5分钟我们知道,这不仅仅是为了活得更久,而是为了活得更健康快乐。遵循我们的10个建议,让你更好地变老。1。注意你的饮食2。照顾好你的牙齿3。保持活跃4。充鼻毛变白是因为衰老吗?或还有其他可能随着年龄的增长,身体各项机能会呈现下降趋势,人体也会随之出现衰老迹象,最明显的便是头发变白,皮肤变得松弛,长满皱纹,色斑,腿脚也不如以前利索。虽说现在人们都在关注养生,其目的便是预74岁退休奶奶逆袭成超模,不惧白发优雅到老,活成了最佳典范大众印象中的超模代名词在瘦削高挑之外,最大的特点就是年轻,但是中国却有一位退休奶奶成为了中国首位登上纽约时装周的高龄超模,打破了这个长期以来人们的固有思维。她是韩彬,t台上的她高贵赵丽颖白色眼线配脏辫,演绎叛逆感,身材苗条气质佳提到赵丽颖,你会想到什么形容词,乖巧安静温柔优雅?但一定和叛逆不沾边。在新作品当中,赵丽颖首次尝试叛逆感,最明显的特征就是白色眼线和脏辫搭配。众所周知,脏辫一般出现在rapper身