AV1代码学习6:函数av1_encode和 av1_first_pass

av1_encode没什么特别好说的,会把在av1_encode_strategy的参数(EncodeFrameInput和EncodeFrameParams)赋给结构体AV1_COMP和AV1_COMMON,对当前帧的数据进行初始化

  AV1_COMP *const cpi
  AV1_COMMON *const cm = &cpi->common;
  CurrentFrame *const current_frame = &cm->current_frame;

这里可以看出,对于第一次编码和第二次编码,其所对应的下个函数是不同的。

  if (cpi->oxcf.pass == 1) {
#if !CONFIG_REALTIME_ONLY
    av1_first_pass(cpi, frame_input->ts_duration);
#endif
  } else if (cpi->oxcf.pass == 0 || cpi->oxcf.pass == 2) {

    if (encode_frame_to_data_rate(cpi, &frame_results->size, dest) !=
        AOM_CODEC_OK) {
      return AOM_CODEC_ERROR;
    }

对于第一次编码,进入的函数av1_first_pass, 第一次编码的参考帧只有LAST和GOLDEN两种类型,为的是减少复杂度

  const YV12_BUFFER_CONFIG *const lst_yv12 =
      get_ref_frame_yv12_buf(cm, LAST_FRAME);
  const YV12_BUFFER_CONFIG *gld_yv12 = get_ref_frame_yv12_buf(cm, GOLDEN_FRAME);
  const YV12_BUFFER_CONFIG *alt_yv12 = NULL;

在第一次编码中,对于QP, 通过函数av1_find_qindex用二叉树的方法找到符合第一次编码的QP,块的是大小16×16

函数av1_set_quantizer:quantizer has to be reinitialized with av1_init_quantizer() if any delta_q changes.

av1_frame_init_quantizer初始化帧的量化器

在for (mb_row = 0; mb_row < cm->mb_rows; ++mb_row)开始循环

在循环内:把刚编码好的作为last frame,第0帧还要作为黄金帧,对块进行帧内和帧间的预测。

在函数末尾有可以保存第一次编码重建值的代码和对应的开关,方便对数据进行分析。

min_err是为了保证码率分配,这里跟块的数量成正比。

一系列运算的结果都存到 FIRSTPASS_STATS fps里, 并在完成一次编码后,赋给&twopass->total_stats

 

void av1_first_pass(AV1_COMP *cpi, const int64_t ts_duration) {
  int mb_row, mb_col;
  MACROBLOCK *const x = &cpi->td.mb;
  AV1_COMMON *const cm = &cpi->common;
  CurrentFrame *const current_frame = &cm->current_frame;
  const SequenceHeader *const seq_params = &cm->seq_params;
  const int num_planes = av1_num_planes(cm);
  MACROBLOCKD *const xd = &x->e_mbd;
  TileInfo tile;
  struct macroblock_plane *const p = x->plane;
  struct macroblockd_plane *const pd = xd->plane;
  const PICK_MODE_CONTEXT *ctx =
      &cpi->td.pc_root[MAX_MIB_SIZE_LOG2 - MIN_MIB_SIZE_LOG2]->none;
  int i;

  int recon_yoffset, src_yoffset, recon_uvoffset;
  int64_t intra_error = 0;
  int64_t frame_avg_wavelet_energy = 0;
  int64_t coded_error = 0;
  int64_t sr_coded_error = 0;
  int64_t tr_coded_error = 0;

  int sum_mvr = 0, sum_mvc = 0;
  int sum_mvr_abs = 0, sum_mvc_abs = 0;
  int64_t sum_mvrs = 0, sum_mvcs = 0;
  int mvcount = 0;
  int intercount = 0;
  int second_ref_count = 0;
  int third_ref_count = 0;
  const int intrapenalty = INTRA_MODE_PENALTY;
  double neutral_count;
  int intra_skip_count = 0;
  int image_data_start_row = INVALID_ROW;
  int new_mv_count = 0;
  int sum_in_vectors = 0;
  MV lastmv = kZeroMv;
  TWO_PASS *twopass = &cpi->twopass;
  int recon_y_stride, src_y_stride, recon_uv_stride, uv_mb_height;
  //重新定义了BUFFER,为了和第二次编码的区分开来,主要是LAST和GOLDEN
  const YV12_BUFFER_CONFIG *const lst_yv12 =
      get_ref_frame_yv12_buf(cm, LAST_FRAME);
  const YV12_BUFFER_CONFIG *gld_yv12 = get_ref_frame_yv12_buf(cm, GOLDEN_FRAME);
  const YV12_BUFFER_CONFIG *alt_yv12 = NULL;
  const int alt_offset = 16 - (current_frame->frame_number % 16);
  if (alt_offset < 16) {
    const struct lookahead_entry *const alt_buf =
        av1_lookahead_peek(cpi->lookahead, alt_offset);
    if (alt_buf != NULL) {
      alt_yv12 = &alt_buf->img;
    }
  }
  YV12_BUFFER_CONFIG *const new_yv12 = &cm->cur_frame->buf;
  double intra_factor;
  double brightness_factor;
  const int qindex = find_fp_qindex(seq_params->bit_depth);
  const int mb_scale = mi_size_wide[BLOCK_16X16];

  int *raw_motion_err_list;
  int raw_motion_err_counts = 0;
  CHECK_MEM_ERROR(
      cm, raw_motion_err_list,
      aom_calloc(cm->mb_rows * cm->mb_cols, sizeof(*raw_motion_err_list)));
  // First pass code requires valid last and new frame buffers.
  assert(new_yv12 != NULL);
  assert(frame_is_intra_only(cm) || (lst_yv12 != NULL));

  av1_setup_frame_size(cpi);
  aom_clear_system_state();

  xd->mi = cm->mi_grid_visible;
  xd->mi[0] = cm->mi;
  x->e_mbd.mi[0]->sb_type = BLOCK_16X16;

  intra_factor = 0.0;
  brightness_factor = 0.0;
  neutral_count = 0.0;

  // Do not use periodic key frames.
  cpi->rc.frames_to_key = INT_MAX;
  //QP的初始化,buffer的初始化等
  av1_set_quantizer(cm, qindex);

  av1_setup_block_planes(&x->e_mbd, seq_params->subsampling_x,
                         seq_params->subsampling_y, num_planes);

  av1_setup_src_planes(x, cpi->source, 0, 0, num_planes,
                       x->e_mbd.mi[0]->sb_type);
  av1_setup_dst_planes(xd->plane, seq_params->sb_size, new_yv12, 0, 0, 0,
                       num_planes);
  //如果不是intra,就要从last_frame的buffer里读取数据来做预测,通常是原始帧
  if (!frame_is_intra_only(cm)) {
    av1_setup_pre_planes(xd, 0, lst_yv12, 0, 0, NULL, num_planes);
  }

  xd->mi = cm->mi_grid_visible;
  xd->mi[0] = cm->mi;

  // Don't store luma o
一直以来, 在多媒体播放器这块, 即使目前有许多开源的播放器项目, 但要写一个播放器仍然是件非常困难的事, 如果在windows上你有可能需要熟悉DShow, 另外的话, 你需要学习一堆开源项目(比如FFmpeg, MPC, VLC, Mplayer), 而且多数都是基于linux, 在windows上学习起来很不容易, 然而这些开源项目对于一些希望快速实现自己播放器, 就显得很困难. 因此, 我创建了这个项目, 致力于以最简单的方法实现自己的播放器, 并提供一个可以很方便使用的接口. 目前, 在这个代码中, 主要链接到FFmpeg来进行解码, 并将其改造成一个通用的播放器框架. 在这个框架中能够接受各种数据的读入, 可以很方便的封装自己的数据读取模块, 也可以很方便的定制自己的视频渲染模块和音频播放模块, 你只需要参考其中的实现即可. 另外在当前的实现中, 因为个人精力实在有限, 所以借鉴了一些开源项目的代码(如Mplayer), 并且该代码主要基于windows平台, 有移植到linux等其它平台的打算. 所以, 我希望有朋友能参与到这个项目中一起研究和学习, 并完成这个目标. 在 https://github.com/Jackarain/avplayer/downloads 中有已经编译好的exe可供测试, 测试命令如下: avplayer.exe test.mp4 或者 avplayer.exe http://edstream.googlecode.com/files/stream.m3u8 或者 avplayer.exe gq.flv.torrent gq.flv.torrent 是从优酷找的一个超清视频文件做的种子. 整个项目分为几个模块: 1. avcore 这只是一个包装类, 实现windows上的窗口创建以及消息响应, 比如: 右击暂停/开始, 单击按屏幕宽百分比seek, F2全屏切换. 在这个模块中, avplayer类是外部接口类, 具体实现在player_impl类中. 在player_impl类中, 初始化各模块结构指针由下面几个函数实现, void init_file_source(media_source *ms); void init_audio(audio_render *ao); void init_video(video_render *vo); 你可以根据自己的需求来修改这些函数实现. media_source是一个提供数据访问的结构, 包含一些函数指针, 如果你需要重新从其它地方读取数据进来提供给播放器, 那么你可以参照这个结构体, 实现这些函数并指向它, 就可以获得从你指定的地方读取数据来进行播放, 这里实现了一个从文件读取数据播放的dll. audio_render是用于播放音频的结构, 同样包含了一些函数指针, 只要实现这些函数指针, 就可以改变音频输出, 比如把音频数据写到文件, 这里实现了2种方式输出音频, 一种是使用dsound来输出音频, 另一种是使用waveout来输出音频. video_render是用于渲染视频的结构, 原理同上, 需要注意的是render_one_frame的data是YUV420格式. 2. libav 这是一个基于ffmpeg的播放框架, 该框架不包含视频渲染和音频播放以及数据读取, 若需要可以实现上述结构体各函数即可, 这个框架中主要完成了使用ffmpeg解码, 以及音视频同步, seek操作处理, 并提供一些基本的控制播放器的函数(在avplay.h中定义). 3. audio 是一个音频播放输出模块实现, 主要实现了audio_render中那几个函数指针. 在这个模块中实现了2套音频输出dsound和waveout. 4. video 是一个视频渲染模块的实现, 实现了video_render中的那几个函数指针. 在这个模块中, 实现了3套视频渲染输出d3d, ddraw, opengl这些方式(还有gdi方式渲染没有添加到video_out.h中). 5. avplayer 是一个使用avcore实现一个简单播放器的示范, 如果要创建一个播放器, 只需要avplayer.h, 然后使用创建一个avplayer对象, 就可以创建一个播放器, 关于如何使用avcore的具体细节可以参考avplayer/main.cpp. 最后, 任何方式(包括商业或非商业)修改或引用该项目的全部或部分源码(或dll), 请在你的软件中或产品文档中必须清楚的声明引用了该项目, 并注明url(https://github.com/Jackarain/avplayer), 你可以偷偷的不这样做, 但这样做是令人欣赏的做法. 最后本程序作者不承担使用该程序所带来的任何问题并拥有一切解释权. mail: jack.wgm@gmail.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值