av1_video_encoder.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/video/av1_video_encoder.h"
  5. #include <cmath>
  6. #include "base/cxx17_backports.h"
  7. #include "base/logging.h"
  8. #include "base/numerics/checked_math.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/system/sys_info.h"
  11. #include "base/time/time.h"
  12. #include "base/trace_event/trace_event.h"
  13. #include "media/base/bind_to_current_loop.h"
  14. #include "media/base/svc_scalability_mode.h"
  15. #include "media/base/timestamp_constants.h"
  16. #include "media/base/video_frame.h"
  17. #include "media/base/video_util.h"
  18. #include "third_party/libaom/source/libaom/aom/aomcx.h"
  19. #include "third_party/libyuv/include/libyuv/convert.h"
  20. namespace media {
  21. namespace {
  22. void FreeCodecCtx(aom_codec_ctx_t* codec_ctx) {
  23. if (codec_ctx->name) {
  24. // Codec has been initialized, we need to destroy it.
  25. auto error = aom_codec_destroy(codec_ctx);
  26. DCHECK_EQ(error, AOM_CODEC_OK);
  27. }
  28. delete codec_ctx;
  29. }
  30. int GetNumberOfThreads(int width) {
  31. // Default to 1 thread for less than VGA.
  32. int desired_threads = 1;
  33. if (width >= 3840)
  34. desired_threads = 16;
  35. else if (width >= 2560)
  36. desired_threads = 8;
  37. else if (width >= 1280)
  38. desired_threads = 4;
  39. else if (width >= 640)
  40. desired_threads = 2;
  41. // Clamp to the number of available logical processors/cores.
  42. desired_threads =
  43. std::min(desired_threads, base::SysInfo::NumberOfProcessors());
  44. return desired_threads;
  45. }
  46. EncoderStatus SetUpAomConfig(const VideoEncoder::Options& opts,
  47. aom_codec_enc_cfg_t& config,
  48. aom_svc_params_t& svc_params) {
  49. if (opts.frame_size.width() <= 0 || opts.frame_size.height() <= 0)
  50. return EncoderStatus(EncoderStatus::Codes::kEncoderUnsupportedConfig,
  51. "Negative width or height values.");
  52. if (!opts.frame_size.GetCheckedArea().IsValid())
  53. return EncoderStatus(EncoderStatus::Codes::kEncoderUnsupportedConfig,
  54. "Frame is too large.");
  55. // Set up general config
  56. config.g_profile = 0; // main
  57. config.g_input_bit_depth = 8;
  58. config.g_pass = AOM_RC_ONE_PASS;
  59. config.g_lag_in_frames = 0;
  60. config.rc_max_quantizer = 56;
  61. config.rc_min_quantizer = 10;
  62. config.rc_dropframe_thresh = 0; // Don't drop frames
  63. config.rc_undershoot_pct = 50;
  64. config.rc_overshoot_pct = 50;
  65. config.rc_buf_initial_sz = 600;
  66. config.rc_buf_optimal_sz = 600;
  67. config.rc_buf_sz = 1000;
  68. config.g_error_resilient = 0;
  69. config.g_timebase.num = 1;
  70. config.g_timebase.den = base::Time::kMicrosecondsPerSecond;
  71. // Set the number of threads based on the image width and num of cores.
  72. config.g_threads = GetNumberOfThreads(opts.frame_size.width());
  73. // Insert keyframes at will with a given max interval
  74. if (opts.keyframe_interval.has_value()) {
  75. config.kf_mode = AOM_KF_AUTO;
  76. config.kf_min_dist = 0;
  77. config.kf_max_dist = opts.keyframe_interval.value();
  78. }
  79. if (opts.bitrate.has_value()) {
  80. auto& bitrate = opts.bitrate.value();
  81. config.rc_target_bitrate = bitrate.target_bps() / 1000;
  82. switch (bitrate.mode()) {
  83. case Bitrate::Mode::kVariable:
  84. config.rc_end_usage = AOM_VBR;
  85. break;
  86. case Bitrate::Mode::kConstant:
  87. config.rc_end_usage = AOM_CBR;
  88. break;
  89. }
  90. } else {
  91. config.rc_end_usage = AOM_VBR;
  92. config.rc_target_bitrate = GetDefaultVideoEncodeBitrate(
  93. opts.frame_size, opts.framerate.value_or(30));
  94. }
  95. config.g_w = opts.frame_size.width();
  96. config.g_h = opts.frame_size.height();
  97. // Setting up SVC parameters
  98. svc_params = {};
  99. svc_params.framerate_factor[0] = 1;
  100. svc_params.number_spatial_layers = 1;
  101. if (opts.scalability_mode.has_value()) {
  102. switch (opts.scalability_mode.value()) {
  103. case SVCScalabilityMode::kL1T2:
  104. svc_params.framerate_factor[0] = 2;
  105. svc_params.framerate_factor[1] = 1;
  106. svc_params.number_temporal_layers = 2;
  107. // Bitrate allocation L0: 60% L1: 40%
  108. svc_params.layer_target_bitrate[0] =
  109. 60 * config.rc_target_bitrate / 100;
  110. svc_params.layer_target_bitrate[1] = config.rc_target_bitrate;
  111. break;
  112. case SVCScalabilityMode::kL1T3:
  113. svc_params.framerate_factor[0] = 4;
  114. svc_params.framerate_factor[1] = 2;
  115. svc_params.framerate_factor[2] = 1;
  116. svc_params.number_temporal_layers = 3;
  117. // Bitrate allocation L0: 50% L1: 20% L2: 30%
  118. svc_params.layer_target_bitrate[0] =
  119. 50 * config.rc_target_bitrate / 100;
  120. svc_params.layer_target_bitrate[1] =
  121. 70 * config.rc_target_bitrate / 100;
  122. svc_params.layer_target_bitrate[2] = config.rc_target_bitrate;
  123. break;
  124. default:
  125. return EncoderStatus(
  126. EncoderStatus::Codes::kEncoderUnsupportedConfig,
  127. "Unsupported configuration of scalability layers.");
  128. }
  129. }
  130. for (int i = 0; i < svc_params.number_temporal_layers; ++i) {
  131. svc_params.scaling_factor_num[i] = 1;
  132. svc_params.scaling_factor_den[i] = 1;
  133. svc_params.max_quantizers[i] = config.rc_max_quantizer;
  134. svc_params.min_quantizers[i] = config.rc_min_quantizer;
  135. }
  136. return EncoderStatus::Codes::kOk;
  137. }
  138. } // namespace
  139. Av1VideoEncoder::Av1VideoEncoder() : codec_(nullptr, FreeCodecCtx) {}
  140. void Av1VideoEncoder::Initialize(VideoCodecProfile profile,
  141. const Options& options,
  142. OutputCB output_cb,
  143. EncoderStatusCB done_cb) {
  144. done_cb = BindToCurrentLoop(std::move(done_cb));
  145. if (codec_) {
  146. std::move(done_cb).Run(EncoderStatus::Codes::kEncoderInitializeTwice);
  147. return;
  148. }
  149. profile_ = profile;
  150. if (profile != AV1PROFILE_PROFILE_MAIN) {
  151. std::move(done_cb).Run(
  152. EncoderStatus(EncoderStatus::Codes::kEncoderUnsupportedProfile)
  153. .WithData("profile", profile));
  154. return;
  155. }
  156. // libaom is compiled with CONFIG_REALTIME_ONLY, so we can't use anything
  157. // but AOM_USAGE_REALTIME.
  158. auto error = aom_codec_enc_config_default(aom_codec_av1_cx(), &config_,
  159. AOM_USAGE_REALTIME);
  160. if (error != AOM_CODEC_OK) {
  161. std::move(done_cb).Run(
  162. EncoderStatus(EncoderStatus::Codes::kEncoderInitializationError,
  163. "Failed to get default AOM config.")
  164. .WithData("error_code", error));
  165. return;
  166. }
  167. if (auto status = SetUpAomConfig(options, config_, svc_params_);
  168. !status.is_ok()) {
  169. std::move(done_cb).Run(std::move(status));
  170. return;
  171. }
  172. // Initialize an encoder instance.
  173. aom_codec_unique_ptr codec(new aom_codec_ctx_t, FreeCodecCtx);
  174. codec->name = nullptr;
  175. aom_codec_flags_t flags = 0;
  176. error = aom_codec_enc_init(codec.get(), aom_codec_av1_cx(), &config_, flags);
  177. if (error != AOM_CODEC_OK) {
  178. std::move(done_cb).Run(
  179. EncoderStatus(EncoderStatus::Codes::kEncoderInitializationError,
  180. "aom_codec_enc_init() failed.")
  181. .WithData("error_code", error)
  182. .WithData("error_message", aom_codec_err_to_string(error)));
  183. return;
  184. }
  185. DCHECK_NE(codec->name, nullptr);
  186. #define CALL_AOM_CONTROL(key, value) \
  187. do { \
  188. error = aom_codec_control(codec.get(), (key), (value)); \
  189. if (error != AOM_CODEC_OK) { \
  190. std::move(done_cb).Run( \
  191. EncoderStatus(EncoderStatus::Codes::kEncoderInitializationError, \
  192. "Setting " #key " failed.") \
  193. .WithData("error_code", error) \
  194. .WithData("error_message", aom_codec_err_to_string(error))); \
  195. return; \
  196. } \
  197. } while (false)
  198. CALL_AOM_CONTROL(AV1E_SET_ROW_MT, 1);
  199. CALL_AOM_CONTROL(AV1E_SET_COEFF_COST_UPD_FREQ, 3);
  200. CALL_AOM_CONTROL(AV1E_SET_MODE_COST_UPD_FREQ, 3);
  201. CALL_AOM_CONTROL(AV1E_SET_MV_COST_UPD_FREQ, 3);
  202. CALL_AOM_CONTROL(AV1E_SET_ENABLE_TPL_MODEL, 0);
  203. CALL_AOM_CONTROL(AV1E_SET_DELTAQ_MODE, 0);
  204. CALL_AOM_CONTROL(AV1E_SET_ENABLE_ORDER_HINT, 0);
  205. CALL_AOM_CONTROL(AV1E_SET_ENABLE_OBMC, 0);
  206. CALL_AOM_CONTROL(AV1E_SET_ENABLE_WARPED_MOTION, 0);
  207. CALL_AOM_CONTROL(AV1E_SET_ENABLE_GLOBAL_MOTION, 0);
  208. CALL_AOM_CONTROL(AV1E_SET_ENABLE_REF_FRAME_MVS, 0);
  209. CALL_AOM_CONTROL(AV1E_SET_ENABLE_CFL_INTRA, 0);
  210. CALL_AOM_CONTROL(AV1E_SET_ENABLE_SMOOTH_INTRA, 0);
  211. CALL_AOM_CONTROL(AV1E_SET_ENABLE_ANGLE_DELTA, 0);
  212. CALL_AOM_CONTROL(AV1E_SET_ENABLE_FILTER_INTRA, 0);
  213. CALL_AOM_CONTROL(AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1);
  214. CALL_AOM_CONTROL(AV1E_SET_SVC_PARAMS, &svc_params_);
  215. if (config_.rc_end_usage == AOM_CBR)
  216. CALL_AOM_CONTROL(AV1E_SET_AQ_MODE, 3);
  217. CALL_AOM_CONTROL(AV1E_SET_TILE_COLUMNS,
  218. static_cast<int>(std::log2(config_.g_threads)));
  219. // AOME_SET_CPUUSED determines tradeoff between video quality and compression
  220. // time. Valid range: 0..10. 0 runs the slowest, and 10 runs the fastest.
  221. // Values 6 to 9 are usually used for realtime applications. Here we choose
  222. // two sides of realtime range for our 'realtime' and 'quality' modes
  223. // because we don't want encoding speed to drop into single digit fps
  224. // even in quality mode.
  225. const int cpu_speed =
  226. (options.latency_mode == VideoEncoder::LatencyMode::Realtime) ? 9 : 7;
  227. CALL_AOM_CONTROL(AOME_SET_CPUUSED, cpu_speed);
  228. #undef CALL_AOM_CONTROL
  229. options_ = options;
  230. originally_configured_size_ = options.frame_size;
  231. output_cb_ = BindToCurrentLoop(std::move(output_cb));
  232. codec_ = std::move(codec);
  233. std::move(done_cb).Run(EncoderStatus::Codes::kOk);
  234. }
  235. void Av1VideoEncoder::Encode(scoped_refptr<VideoFrame> frame,
  236. bool key_frame,
  237. EncoderStatusCB done_cb) {
  238. done_cb = BindToCurrentLoop(std::move(done_cb));
  239. if (!codec_) {
  240. std::move(done_cb).Run(
  241. EncoderStatus::Codes::kEncoderInitializeNeverCompleted);
  242. return;
  243. }
  244. if (!frame) {
  245. std::move(done_cb).Run(
  246. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode,
  247. "No frame provided for encoding."));
  248. return;
  249. }
  250. bool supported_format = frame->format() == PIXEL_FORMAT_NV12 ||
  251. frame->format() == PIXEL_FORMAT_I420 ||
  252. frame->format() == PIXEL_FORMAT_XBGR ||
  253. frame->format() == PIXEL_FORMAT_XRGB ||
  254. frame->format() == PIXEL_FORMAT_ABGR ||
  255. frame->format() == PIXEL_FORMAT_ARGB;
  256. if ((!frame->IsMappable() && !frame->HasGpuMemoryBuffer()) ||
  257. !supported_format) {
  258. std::move(done_cb).Run(
  259. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode,
  260. "Unexpected frame format.")
  261. .WithData("IsMappable", frame->IsMappable())
  262. .WithData("HasGpuMemoryBuffer", frame->HasGpuMemoryBuffer())
  263. .WithData("format", frame->format()));
  264. return;
  265. }
  266. if (frame->HasGpuMemoryBuffer()) {
  267. frame = ConvertToMemoryMappedFrame(frame);
  268. if (!frame) {
  269. std::move(done_cb).Run(
  270. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode,
  271. "Convert GMB frame to MemoryMappedFrame failed."));
  272. return;
  273. }
  274. }
  275. const bool is_yuv = IsYuvPlanar(frame->format());
  276. if (frame->visible_rect().size() != options_.frame_size || !is_yuv) {
  277. auto resized_frame = frame_pool_.CreateFrame(
  278. PIXEL_FORMAT_I420, options_.frame_size, gfx::Rect(options_.frame_size),
  279. options_.frame_size, frame->timestamp());
  280. if (resized_frame) {
  281. auto conv_status =
  282. ConvertAndScaleFrame(*frame, *resized_frame, resize_buf_);
  283. if (!conv_status.is_ok()) {
  284. std::move(done_cb).Run(
  285. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode)
  286. .AddCause(std::move(conv_status)));
  287. return;
  288. }
  289. } else {
  290. std::move(done_cb).Run(
  291. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode,
  292. "Can't allocate a resized frame."));
  293. return;
  294. }
  295. frame = std::move(resized_frame);
  296. }
  297. aom_img_fmt fmt = frame->format() == PIXEL_FORMAT_NV12 ? AOM_IMG_FMT_NV12
  298. : AOM_IMG_FMT_I420;
  299. aom_image_t* image = aom_img_wrap(&image_, fmt, options_.frame_size.width(),
  300. options_.frame_size.height(), 1,
  301. frame->data(VideoFrame::kYPlane));
  302. DCHECK_EQ(image, &image_);
  303. switch (frame->format()) {
  304. case PIXEL_FORMAT_I420:
  305. image->planes[AOM_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane);
  306. image->planes[AOM_PLANE_U] = frame->visible_data(VideoFrame::kUPlane);
  307. image->planes[AOM_PLANE_V] = frame->visible_data(VideoFrame::kVPlane);
  308. image->stride[AOM_PLANE_Y] = frame->stride(VideoFrame::kYPlane);
  309. image->stride[AOM_PLANE_U] = frame->stride(VideoFrame::kUPlane);
  310. image->stride[AOM_PLANE_V] = frame->stride(VideoFrame::kVPlane);
  311. break;
  312. case PIXEL_FORMAT_NV12:
  313. image->planes[AOM_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane);
  314. image->planes[AOM_PLANE_U] = frame->visible_data(VideoFrame::kUVPlane);
  315. image->planes[AOM_PLANE_V] = nullptr;
  316. image->stride[AOM_PLANE_Y] = frame->stride(VideoFrame::kYPlane);
  317. image->stride[AOM_PLANE_U] = frame->stride(VideoFrame::kUVPlane);
  318. image->stride[AOM_PLANE_V] = 0;
  319. break;
  320. default:
  321. NOTREACHED();
  322. }
  323. auto duration_us = GetFrameDuration(*frame).InMicroseconds();
  324. last_frame_timestamp_ = frame->timestamp();
  325. if (last_frame_color_space_ != frame->ColorSpace()) {
  326. last_frame_color_space_ = frame->ColorSpace();
  327. key_frame = true;
  328. }
  329. auto temporal_id_status = AssignNextTemporalId(key_frame);
  330. if (temporal_id_status.has_error()) {
  331. std::move(done_cb).Run(std::move(temporal_id_status).error());
  332. return;
  333. }
  334. TRACE_EVENT1("media", "aom_codec_encode", "timestamp", frame->timestamp());
  335. // Use artificial timestamps, so the encoder will not be misled by frame's
  336. // fickle timestamps when doing rate control.
  337. auto error =
  338. aom_codec_encode(codec_.get(), image, artificial_timestamp_, duration_us,
  339. key_frame ? AOM_EFLAG_FORCE_KF : 0);
  340. artificial_timestamp_ += duration_us;
  341. if (error != AOM_CODEC_OK) {
  342. auto msg =
  343. base::StringPrintf("AOM encoding error: %s (%d)",
  344. aom_codec_error_detail(codec_.get()), codec_->err);
  345. DLOG(ERROR) << msg;
  346. std::move(done_cb).Run(
  347. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode, msg));
  348. return;
  349. }
  350. DrainOutputs(std::move(temporal_id_status).value(), frame->timestamp(),
  351. frame->ColorSpace());
  352. std::move(done_cb).Run(EncoderStatus::Codes::kOk);
  353. }
  354. void Av1VideoEncoder::ChangeOptions(const Options& options,
  355. OutputCB output_cb,
  356. EncoderStatusCB done_cb) {
  357. done_cb = BindToCurrentLoop(std::move(done_cb));
  358. if (!codec_) {
  359. std::move(done_cb).Run(
  360. EncoderStatus::Codes::kEncoderInitializeNeverCompleted);
  361. return;
  362. }
  363. if (options.frame_size.width() > originally_configured_size_.width() ||
  364. options.frame_size.height() > originally_configured_size_.height()) {
  365. auto status = EncoderStatus(
  366. EncoderStatus::Codes::kEncoderUnsupportedConfig,
  367. "libaom doesn't support dynamically increasing frame dimensions");
  368. std::move(done_cb).Run(std::move(status));
  369. return;
  370. }
  371. aom_codec_enc_cfg_t new_config = config_;
  372. aom_svc_params_t new_svc_params;
  373. if (auto status = SetUpAomConfig(options, new_config, new_svc_params);
  374. !status.is_ok()) {
  375. std::move(done_cb).Run(status);
  376. return;
  377. }
  378. auto error = aom_codec_enc_config_set(codec_.get(), &new_config);
  379. if (error != AOM_CODEC_OK) {
  380. std::move(done_cb).Run(
  381. EncoderStatus(EncoderStatus::Codes::kEncoderUnsupportedConfig,
  382. "Failed to set a new AOM config")
  383. .WithData("error_code", error)
  384. .WithData("error_message", aom_codec_err_to_string(error)));
  385. return;
  386. }
  387. error = aom_codec_control(codec_.get(), AV1E_SET_SVC_PARAMS, &new_svc_params);
  388. if (error != AOM_CODEC_OK) {
  389. std::move(done_cb).Run(
  390. EncoderStatus(EncoderStatus::Codes::kEncoderInitializationError,
  391. "Setting AV1E_SET_SVC_PARAMS failed.")
  392. .WithData("error_code", error)
  393. .WithData("error_message", aom_codec_err_to_string(error)));
  394. return;
  395. }
  396. config_ = new_config;
  397. svc_params_ = new_svc_params;
  398. options_ = options;
  399. if (!output_cb.is_null())
  400. output_cb_ = BindToCurrentLoop(std::move(output_cb));
  401. std::move(done_cb).Run(EncoderStatus::Codes::kOk);
  402. }
  403. base::TimeDelta Av1VideoEncoder::GetFrameDuration(const VideoFrame& frame) {
  404. // Frame has duration in metadata, use it.
  405. if (frame.metadata().frame_duration.has_value())
  406. return frame.metadata().frame_duration.value();
  407. // Options have framerate specified, use it.
  408. if (options_.framerate.has_value())
  409. return base::Seconds(1.0 / options_.framerate.value());
  410. // No real way to figure out duration, use time passed since the last frame
  411. // as an educated guess, but clamp it within reasonable limits.
  412. constexpr auto min_duration = base::Seconds(1.0 / 60.0);
  413. constexpr auto max_duration = base::Seconds(1.0 / 24.0);
  414. auto duration = frame.timestamp() - last_frame_timestamp_;
  415. return base::clamp(duration, min_duration, max_duration);
  416. }
  417. void Av1VideoEncoder::DrainOutputs(int temporal_id,
  418. base::TimeDelta ts,
  419. gfx::ColorSpace color_space) {
  420. const aom_codec_cx_pkt_t* pkt = nullptr;
  421. aom_codec_iter_t iter = nullptr;
  422. while ((pkt = aom_codec_get_cx_data(codec_.get(), &iter)) != nullptr) {
  423. if (pkt->kind != AOM_CODEC_CX_FRAME_PKT)
  424. continue;
  425. VideoEncoderOutput result;
  426. result.key_frame = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
  427. result.timestamp = ts;
  428. result.color_space = color_space;
  429. result.size = pkt->data.frame.sz;
  430. if (result.key_frame) {
  431. // If we got an unexpected key frame, temporal_svc_frame_index needs to
  432. // be adjusted, because the next frame should have index 1.
  433. temporal_svc_frame_index_ = 1;
  434. result.temporal_id = 0;
  435. } else {
  436. result.temporal_id = temporal_id;
  437. }
  438. result.data = std::make_unique<uint8_t[]>(result.size);
  439. memcpy(result.data.get(), pkt->data.frame.buf, result.size);
  440. output_cb_.Run(std::move(result), {});
  441. }
  442. }
  443. EncoderStatus::Or<int> Av1VideoEncoder::AssignNextTemporalId(bool key_frame) {
  444. if (svc_params_.number_temporal_layers < 2)
  445. return 0;
  446. int temporal_id = 0;
  447. if (key_frame)
  448. temporal_svc_frame_index_ = 0;
  449. switch (svc_params_.number_temporal_layers) {
  450. case 2: {
  451. const static std::array<int, 2> kTwoTemporalLayers = {0, 1};
  452. temporal_id = kTwoTemporalLayers[temporal_svc_frame_index_ %
  453. kTwoTemporalLayers.size()];
  454. break;
  455. }
  456. case 3: {
  457. const static std::array<int, 4> kThreeTemporalLayers = {0, 2, 1, 2};
  458. temporal_id = kThreeTemporalLayers[temporal_svc_frame_index_ %
  459. kThreeTemporalLayers.size()];
  460. break;
  461. }
  462. }
  463. temporal_svc_frame_index_++;
  464. aom_svc_layer_id_t layer_id = {};
  465. layer_id.temporal_layer_id = temporal_id;
  466. auto error =
  467. aom_codec_control(codec_.get(), AV1E_SET_SVC_LAYER_ID, &layer_id);
  468. if (error == AOM_CODEC_OK)
  469. aom_codec_control(codec_.get(), AV1E_SET_ERROR_RESILIENT_MODE,
  470. temporal_id > 0 ? 1 : 0);
  471. if (error != AOM_CODEC_OK) {
  472. auto msg =
  473. base::StringPrintf("Set AV1E_SET_SVC_LAYER_ID error: %s (%d)",
  474. aom_codec_error_detail(codec_.get()), codec_->err);
  475. return EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode, msg);
  476. }
  477. return temporal_id;
  478. }
  479. Av1VideoEncoder::~Av1VideoEncoder() = default;
  480. void Av1VideoEncoder::Flush(EncoderStatusCB done_cb) {
  481. done_cb = BindToCurrentLoop(std::move(done_cb));
  482. if (!codec_) {
  483. std::move(done_cb).Run(
  484. EncoderStatus::Codes::kEncoderInitializeNeverCompleted);
  485. return;
  486. }
  487. auto error = aom_codec_encode(codec_.get(), nullptr, 0, 0, 0);
  488. if (error != AOM_CODEC_OK) {
  489. auto msg =
  490. base::StringPrintf("AOM encoding error: %s (%d)",
  491. aom_codec_error_detail(codec_.get()), codec_->err);
  492. DLOG(ERROR) << msg;
  493. std::move(done_cb).Run(
  494. EncoderStatus(EncoderStatus::Codes::kEncoderFailedEncode, msg));
  495. return;
  496. }
  497. // We don't call DrainOutputs() because Flush() is not expected to produce any
  498. // outputs. The encoder configured with g_lag_in_frames = 0 and all outputs
  499. // are drained after each Encode(). We might want to change this in the
  500. // future, see: crbug.com/1280404
  501. std::move(done_cb).Run(EncoderStatus::Codes::kOk);
  502. }
  503. } // namespace media