2035 lines · 80.4 KB · C++
RawDownload
#include <gtest/gtest.h>#include "codec_def.h"#include "utils/BufferedData.h"#include "utils/FileInputStream.h"#include "BaseDecoderTest.h"#include "BaseEncoderTest.h"#include "wels_common_defs.h"#include <string>#include <vector>using namespace WelsCommon; #define TRY_TIME_RANGE           (10)#define ENCODE_FRAME_NUM         (30)#define LEVEL_ID_RANGE           (18)#define MAX_WIDTH                (4096)#define MAX_HEIGHT               (2304)#define MAX_FRAME_RATE           (30)#define MIN_FRAME_RATE           (1)#define FRAME_RATE_RANGE         (2*MAX_FRAME_RATE)#define RC_MODE_RANGE            (4)#define BIT_RATE_RANGE           (10000)#define MAX_QP                   (51)#define MIN_QP                   (0)#define QP_RANGE                 (2*MAX_QP)#define SPATIAL_LAYER_NUM_RANGE  (2*MAX_SPATIAL_LAYER_NUM)#define TEMPORAL_LAYER_NUM_RANGE (2*MAX_TEMPORAL_LAYER_NUM)#define SAVED_NALUNIT_NUM        ( (MAX_SPATIAL_LAYER_NUM*MAX_QUALITY_LAYER_NUM) + 1 + MAX_SPATIAL_LAYER_NUM )#define MAX_SLICES_NUM           ( ( MAX_NAL_UNITS_IN_LAYER - SAVED_NALUNIT_NUM ) / 3 )#define SLICE_MODE_NUM           (6)#define LOOP_FILTER_IDC_NUM      (3)#define LOOF_FILTER_OFFSET_RANGE (6)#define MAX_REF_PIC_COUNT        (16)#define MIN_REF_PIC_COUNT        (1)#define LONG_TERM_REF_NUM        (2)#define LONG_TERM_REF_NUM_SCREEN (4)#define MAX_REFERENCE_PICTURE_COUNT_NUM_CAMERA (6)#define MAX_REFERENCE_PICTURE_COUNT_NUM_SCREEN (8)  typedef struct SLost_Sim {  WelsCommon::EWelsNalUnitType eNalType;  bool isLost;} SLostSim;  struct EncodeDecodeFileParamBase {  int numframes;  int width;  int height;  float frameRate;  int slicenum;  bool bLostPara;  const char* pLossSequence;}; static void welsStderrTraceOrigin (void* ctx, int level, const char* string) {  fprintf (stderr, "%s\n", string);} typedef struct STrace_Unit {  int iTarLevel;} STraceUnit; static void TestOutPutTrace (void* ctx, int level, const char* string) {  STraceUnit* pTraceUnit = (STraceUnit*) ctx;  EXPECT_LE (level, pTraceUnit->iTarLevel);} class EncodeDecodeTestBase : public ::testing::TestWithParam<EncodeDecodeFileParamBase>,  public BaseEncoderTest, public BaseDecoderTest { public:  virtual void SetUp() {    BaseEncoderTest::SetUp();    BaseDecoderTest::SetUp();    pFunc = welsStderrTraceOrigin;    pTraceInfo = NULL;    encoder_->SetOption (ENCODER_OPTION_TRACE_CALLBACK, &pFunc);    encoder_->SetOption (ENCODER_OPTION_TRACE_CALLBACK_CONTEXT, &pTraceInfo);    decoder_->SetOption (DECODER_OPTION_TRACE_CALLBACK, &pFunc);    decoder_->SetOption (DECODER_OPTION_TRACE_CALLBACK_CONTEXT, &pTraceInfo);  }   virtual void TearDown() {    BaseEncoderTest::TearDown();    BaseDecoderTest::TearDown();  }   virtual void prepareParam (int iLayers, int iSlices, int width, int height, float framerate) {    memset (&param_, 0, sizeof (SEncParamExt));    param_.iUsageType = CAMERA_VIDEO_REAL_TIME;    param_.iPicWidth = width;    param_.iPicHeight = height;    param_.fMaxFrameRate = framerate;    param_.iRCMode = RC_OFF_MODE; //rc off    param_.iMultipleThreadIdc = 1; //single thread    for (int i = 0; i < iLayers; i++) {      param_.sSpatialLayers[i].iVideoWidth = width >> (iLayers - i - 1);      param_.sSpatialLayers[i].iVideoHeight = height >> (iLayers - i - 1);      param_.sSpatialLayers[i].fFrameRate = framerate;      param_.sSpatialLayers[i].sSliceCfg.uiSliceMode = SM_FIXEDSLCNUM_SLICE;      param_.sSpatialLayers[i].sSliceCfg.sSliceArgument.uiSliceNum = iSlices;    }    }   virtual void encToDecData (const SFrameBSInfo& info, int& len) {    len = 0;    for (int i = 0; i < info.iLayerNum; ++i) {      const SLayerBSInfo& layerInfo = info.sLayerInfo[i];      for (int j = 0; j < layerInfo.iNalCount; ++j) {        len += layerInfo.pNalLengthInByte[j];      }    }  }   virtual void encToDecSliceData (const int iLayerNum, const int iSliceNum, const SFrameBSInfo& info, int& len) {    ASSERT_TRUE (iLayerNum < MAX_LAYER_NUM_OF_FRAME);    len = 0;    const SLayerBSInfo& layerInfo = info.sLayerInfo[iLayerNum];    if (iSliceNum < layerInfo.iNalCount)      len = layerInfo.pNalLengthInByte[iSliceNum];  }  protected:  SEncParamExt   param_;  BufferedData   buf_;  SSourcePicture EncPic;  SFrameBSInfo   info;  SBufferInfo    dstBufInfo_;  std::vector<SLostSim> m_SLostSim;  WelsTraceCallback pFunc;  STraceUnit sTrace;  STraceUnit* pTraceInfo;}; class EncodeDecodeTestAPI : public EncodeDecodeTestBase { public:  uint8_t iRandValue; public:  void SetUp() {    EncodeDecodeTestBase::SetUp();  }   void TearDown() {    EncodeDecodeTestBase::TearDown();  }   void prepareParam (int iLayers, int iSlices, int width, int height, float framerate) {    EncodeDecodeTestBase::prepareParam (iLayers, iSlices, width, height, framerate);  }   void InitialEncDec (int iWidth, int iHeight);  void RandomParamExtCombination();  void ValidateParamExtCombination();  void SliceParamValidationForMode2 (int iSpatialIdx);  void SliceParamValidationForMode3 (int iSpatialIdx);  void SliceParamValidationForMode4();   void EncodeOneFrame (int iCheckTypeIndex) {    int frameSize = EncPic.iPicWidth * EncPic.iPicHeight * 3 / 2;    memset (buf_.data(), iRandValue, (frameSize >> 2));    memset (buf_.data() + (frameSize >> 2), rand() % 256, (frameSize - (frameSize >> 2)));    int rv = encoder_->EncodeFrame (&EncPic, &info);    if (0 == iCheckTypeIndex)      ASSERT_TRUE (rv == cmResultSuccess);    else if (1 == iCheckTypeIndex)      ASSERT_TRUE (rv == cmResultSuccess || rv == cmUnkonwReason);  }}; void EncodeDecodeTestAPI::InitialEncDec (int iWidth, int iHeight) {   // for encoder  // I420: 1(Y) + 1/4(U) + 1/4(V)  int frameSize = iWidth * iHeight * 3 / 2;   buf_.SetLength (frameSize);  ASSERT_TRUE (buf_.Length() == (size_t)frameSize);   memset (&EncPic, 0, sizeof (SSourcePicture));  EncPic.iPicWidth    = iWidth;  EncPic.iPicHeight   =  iHeight;  EncPic.iColorFormat = videoFormatI420;  EncPic.iStride[0]   = EncPic.iPicWidth;  EncPic.iStride[1]   = EncPic.iStride[2] = EncPic.iPicWidth >> 1;  EncPic.pData[0]     = buf_.data();  EncPic.pData[1]     = EncPic.pData[0] + iWidth * iHeight;  EncPic.pData[2]     = EncPic.pData[1] + (iWidth * iHeight >> 2);   //for decoder  memset (&info, 0, sizeof (SFrameBSInfo));   //set a fixed random value  iRandValue = rand() % 256;} void EncodeDecodeTestAPI::RandomParamExtCombination() {   param_.iPicWidth  = WELS_CLIP3 ((((rand() % MAX_WIDTH) >> 1)  + 1) << 1, 2, MAX_WIDTH);  param_.iPicHeight = WELS_CLIP3 ((((rand() % MAX_HEIGHT) >> 1) + 1) << 1, 2, MAX_HEIGHT);   param_.fMaxFrameRate      = rand() % FRAME_RATE_RANGE + 0.5f;  param_.iUsageType         = static_cast<EUsageType> (rand() % 2);  param_.iTemporalLayerNum  = rand() % TEMPORAL_LAYER_NUM_RANGE;  param_.iSpatialLayerNum   = rand() % SPATIAL_LAYER_NUM_RANGE;   param_.uiIntraPeriod      = rand() - 1;  param_.iNumRefFrame       = rand();  param_.iMultipleThreadIdc = rand();   param_.bEnableSpsPpsIdAddition   = (rand() % 2 == 0) ? false : true;  param_.bPrefixNalAddingCtrl      = (rand() % 2 == 0) ? false : true;  param_.bEnableSSEI               = (rand() % 2 == 0) ? false : true;  param_.iPaddingFlag              = rand() % 2;   //LTR  param_.bEnableLongTermReference  = (rand() % 2 == 0) ? false : true;  param_.bIsLosslessLink           = (rand() % 2 == 0) ? false : true;  param_.iLTRRefNum                = rand();  param_.iLtrMarkPeriod            = rand();   //loop filter  param_.iLoopFilterDisableIdc     = rand();  param_.iLoopFilterAlphaC0Offset  = rand();  param_.iLoopFilterBetaOffset     = rand();   param_.bEnableDenoise             = (rand() % 2 == 0) ? false : true;  param_.bEnableBackgroundDetection = (rand() % 2 == 0) ? false : true;  param_.bEnableAdaptiveQuant       = (rand() % 2 == 0) ? false : true;  param_.bEnableFrameCroppingFlag   = (rand() % 2 == 0) ? false : true;  param_.bEnableSceneChangeDetect   = (rand() % 2 == 0) ? false : true;   //for rc  param_.iRCMode            = static_cast<RC_MODES> (rand() % RC_MODE_RANGE - 1);  param_.iMaxBitrate        = rand() % BIT_RATE_RANGE;  param_.iTargetBitrate     = rand() % BIT_RATE_RANGE;  param_.iMaxQp             = rand() % QP_RANGE;  param_.iMinQp             = rand() % QP_RANGE;  param_.uiMaxNalSize       = rand();  param_.bEnableFrameSkip   = (rand() % 2 == 0) ? false : true;   for (int iSpatialIdx = 0; iSpatialIdx < param_.iSpatialLayerNum; iSpatialIdx++) {    if (iSpatialIdx < MAX_SPATIAL_LAYER_NUM) {      SSpatialLayerConfig* pSpatialLayer = &param_.sSpatialLayers[iSpatialIdx];       //to do: profile and level id      //pSpatialLayer->uiProfileIdc        = 0;      //pSpatialLayer->uiLevelIdc          = 0;      pSpatialLayer->iVideoWidth         = WELS_CLIP3 ((((rand() % MAX_WIDTH) >> 1)  + 1) << 1, 2, MAX_WIDTH);      pSpatialLayer->iVideoHeight        = WELS_CLIP3 ((((rand() % MAX_HEIGHT) >> 1) + 1) << 1, 2, MAX_HEIGHT);      pSpatialLayer->fFrameRate          = rand() % FRAME_RATE_RANGE + 0.5f;      pSpatialLayer->iMaxSpatialBitrate  = rand() % BIT_RATE_RANGE;      pSpatialLayer->iSpatialBitrate     = rand() % BIT_RATE_RANGE;        pSpatialLayer->sSliceCfg.uiSliceMode = static_cast<SliceModeEnum> (rand() % SLICE_MODE_NUM);      pSpatialLayer->sSliceCfg.sSliceArgument.uiSliceNum = rand();      pSpatialLayer->sSliceCfg.sSliceArgument.uiSliceSizeConstraint = rand();    }  }} void EncodeDecodeTestAPI::ValidateParamExtCombination() {   bool bDynSliceModeFlag   = false;  unsigned int uiGOPSize   = 0;  unsigned int uiSliceNum  = 0;  int iTotalBitRate        = 0;  int iMinQP               = 0;   param_.iPicWidth          = WELS_CLIP3 (param_.iPicWidth,  2, MAX_WIDTH);  param_.iPicHeight         = WELS_CLIP3 (param_.iPicHeight, 2, MAX_HEIGHT);  param_.fMaxFrameRate      = WELS_CLIP3 (param_.fMaxFrameRate, MIN_FRAME_RATE, MAX_FRAME_RATE);  param_.iTemporalLayerNum  = WELS_CLIP3 (param_.iTemporalLayerNum, 1, MAX_TEMPORAL_LAYER_NUM);   if (CAMERA_VIDEO_REAL_TIME == param_.iUsageType)    param_.iSpatialLayerNum   = WELS_CLIP3 (param_.iSpatialLayerNum, 1, MAX_SPATIAL_LAYER_NUM);  else    param_.iSpatialLayerNum   = 1;   //IntraPeriod  uiGOPSize = 1 << (param_.iTemporalLayerNum - 1);  param_.uiIntraPeriod -= param_.uiIntraPeriod % uiGOPSize;  if (param_.uiIntraPeriod < 0) {    param_.uiIntraPeriod = 0;  }   //RefNum  int32_t iRefUpperBound    = (param_.iUsageType == CAMERA_VIDEO_REAL_TIME) ?                              MAX_REFERENCE_PICTURE_COUNT_NUM_CAMERA : MAX_REFERENCE_PICTURE_COUNT_NUM_SCREEN;  param_.iNumRefFrame       = WELS_CLIP3 (param_.iNumRefFrame, MIN_REF_PIC_COUNT, iRefUpperBound);   //to do: will add more validate logic for thread number  param_.iMultipleThreadIdc = 1;   //LTR  //iLTRRefNum: not supported to set it arbitrary yet  if (true == param_.bEnableLongTermReference) {    param_.iLTRRefNum     = (SCREEN_CONTENT_REAL_TIME == param_.iUsageType) ? LONG_TERM_REF_NUM_SCREEN : LONG_TERM_REF_NUM;    param_.iLtrMarkPeriod = (0 == param_.iLtrMarkPeriod) ? 1 : param_.iLtrMarkPeriod;   } else {    param_.iLTRRefNum = 0;  }   //loop filter  param_.iLoopFilterDisableIdc    = param_.iLoopFilterDisableIdc    % LOOP_FILTER_IDC_NUM;  param_.iLoopFilterAlphaC0Offset = param_.iLoopFilterAlphaC0Offset % (2 * LOOF_FILTER_OFFSET_RANGE + 1) -                                    LOOF_FILTER_OFFSET_RANGE;  param_.iLoopFilterBetaOffset    = param_.iLoopFilterBetaOffset    % (2 * LOOF_FILTER_OFFSET_RANGE + 1) -                                    LOOF_FILTER_OFFSET_RANGE;   for (int iSpatialIdx = 0; iSpatialIdx < param_.iSpatialLayerNum; iSpatialIdx++) {    SSpatialLayerConfig* pSpatialLayer = &param_.sSpatialLayers[iSpatialIdx];    pSpatialLayer->iVideoWidth  = param_.iPicWidth >> (param_.iSpatialLayerNum - 1 - iSpatialIdx);    pSpatialLayer->iVideoHeight = param_.iPicHeight >> (param_.iSpatialLayerNum - 1 - iSpatialIdx);    pSpatialLayer->fFrameRate   = param_.fMaxFrameRate;     pSpatialLayer->iMaxSpatialBitrate  = WELS_CLIP3 (pSpatialLayer->iMaxSpatialBitrate, 1, BIT_RATE_RANGE);    pSpatialLayer->iSpatialBitrate     = WELS_CLIP3 (pSpatialLayer->iSpatialBitrate, 1, pSpatialLayer->iMaxSpatialBitrate);    iTotalBitRate += pSpatialLayer->iSpatialBitrate;     uiSliceNum  = pSpatialLayer->sSliceCfg.sSliceArgument.uiSliceNum;    pSpatialLayer->sSliceCfg.sSliceArgument.uiSliceNum = WELS_CLIP3 (uiSliceNum, 1, MAX_SLICES_NUM);    pSpatialLayer->sSliceCfg.sSliceArgument.uiSliceSizeConstraint = 0;      //for SM_FIXEDSLCNUM_SLICE    // to do will add this when GOM bug fixed    if (SM_FIXEDSLCNUM_SLICE == pSpatialLayer->sSliceCfg.uiSliceMode) {      pSpatialLayer->sSliceCfg.uiSliceMode = SM_SINGLE_SLICE;    }     //for slice mode = SM_DYN_SLICE    if (SM_DYN_SLICE == pSpatialLayer->sSliceCfg.uiSliceMode) {      bDynSliceModeFlag = true;    }     //for slice mode = SM_RASTER_SLICE    if (SM_RASTER_SLICE == pSpatialLayer->sSliceCfg.uiSliceMode) {      SliceParamValidationForMode2 (iSpatialIdx);    }    //for slice mode = SM_ROWMB_SLICE    if (SM_ROWMB_SLICE == pSpatialLayer->sSliceCfg.uiSliceMode) {      SliceParamValidationForMode3 (iSpatialIdx);    }   }   //for RC  if ((RC_QUALITY_MODE == param_.iRCMode) || (RC_BITRATE_MODE == param_.iRCMode)) {    param_.bEnableFrameSkip = true;  }  if (param_.iTargetBitrate < iTotalBitRate) {    param_.iTargetBitrate = iTotalBitRate;  }  if (param_.iMaxBitrate < param_.iTargetBitrate) {    param_.iMaxBitrate = param_.iTargetBitrate;  }  param_.iMaxQp       = WELS_CLIP3 (param_.iMaxQp, MIN_QP, MAX_QP);  param_.iMinQp       = WELS_CLIP3 (param_.iMinQp, MIN_QP, MAX_QP);  iMinQP              = (param_.iMaxQp < param_.iMinQp) ? param_.iMaxQp : param_.iMinQp;  param_.iMaxQp       = (param_.iMaxQp > param_.iMinQp) ? param_.iMaxQp : param_.iMinQp;  param_.iMinQp       = iMinQP;  param_.uiMaxNalSize = 0;   //for slice mode = SM_DYN_SLICE  if (true == bDynSliceModeFlag) {    SliceParamValidationForMode4();  } }  void EncodeDecodeTestAPI::SliceParamValidationForMode2 (int iSpatialIdx) {   unsigned int uiMbWidth          = 0;  unsigned int uiMbHeight         = 0;  unsigned int uiMbNumInFrame     = 0;  unsigned int uiCountMb          = 0;  unsigned int uiSliceIdx         = 0;  unsigned int uiActualSliceCount = 0;   uiMbWidth	     = (param_.iPicWidth + 15) >> 4;  uiMbHeight	 = (param_.iPicHeight + 15) >> 4;  uiMbNumInFrame = uiMbWidth * uiMbHeight;   uiSliceIdx = 0;  while (uiSliceIdx < MAX_SLICES_NUM) {    param_.sSpatialLayers[iSpatialIdx].sSliceCfg.sSliceArgument.uiSliceMbNum[uiSliceIdx] = rand() % uiMbNumInFrame;    uiCountMb			+= param_.sSpatialLayers[iSpatialIdx].sSliceCfg.sSliceArgument.uiSliceMbNum[uiSliceIdx];    uiActualSliceCount	 =  uiSliceIdx + 1;     if (uiCountMb >= uiMbNumInFrame) {      break;    }     ++ uiSliceIdx;  }   if (uiCountMb >= uiMbNumInFrame) {    param_.sSpatialLayers[iSpatialIdx].sSliceCfg.sSliceArgument.uiSliceMbNum[uiActualSliceCount - 1] -=      (uiCountMb - uiMbNumInFrame);   } else {    param_.sSpatialLayers[iSpatialIdx].sSliceCfg.sSliceArgument.uiSliceMbNum[uiActualSliceCount - 1 ] +=      (uiMbNumInFrame - uiCountMb);  }  param_.sSpatialLayers[iSpatialIdx].sSliceCfg.sSliceArgument.uiSliceNum = uiActualSliceCount; }void EncodeDecodeTestAPI::SliceParamValidationForMode3 (int iSpatialIdx) {   unsigned int uiMbWidth          = 0;  unsigned int uiMbHeight         = 0;  unsigned int uiMbNumInFrame     = 0;   uiMbWidth	     = (param_.iPicWidth + 15) >> 4;  uiMbHeight	 = (param_.iPicHeight + 15) >> 4;   //change slice mode to SM_SINGLE_SLICE  if (uiMbHeight >  MAX_SLICES_NUM) {    param_.sSpatialLayers[iSpatialIdx].sSliceCfg.uiSliceMode = SM_SINGLE_SLICE;  } } void EncodeDecodeTestAPI::SliceParamValidationForMode4() {  //slice mode of all spatial layer should be set as SM_DYN_SLICE  for (int iSpatialIdx = 0; iSpatialIdx < param_.iSpatialLayerNum; iSpatialIdx++) {    param_.sSpatialLayers[iSpatialIdx].sSliceCfg.sSliceArgument.uiSliceSizeConstraint = 600;    param_.sSpatialLayers[iSpatialIdx].sSliceCfg.uiSliceMode = SM_DYN_SLICE;  }  param_.uiMaxNalSize = 1500;} static const EncodeDecodeFileParamBase kFileParamArray[] = {  {300, 160, 96, 6.0f, 2, 1, "000000000000001010101010101010101010101001101010100000010101000011"},  {300, 140, 96, 6.0f, 4, 1, "000000000000001010101010101010101010101001101010100000010101000011"},  {300, 140, 96, 6.0f, 4, 1, "000000000000001010111010101011101010101001101010100000010101110011010101"},}; TEST_P (EncodeDecodeTestAPI, DecoderVclNal) {  EncodeDecodeFileParamBase p = GetParam();  prepareParam (1, p.slicenum, p.width, p.height, p.frameRate);  encoder_->Uninitialize();  int rv = encoder_->InitializeExt (&param_);  ASSERT_TRUE (rv == cmResultSuccess);   int32_t iTraceLevel = WELS_LOG_QUIET;  encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &iTraceLevel);  decoder_->SetOption (DECODER_OPTION_TRACE_LEVEL, &iTraceLevel);   InitialEncDec (p.width, p.height);   int iIdx = 0;  while (iIdx <= p.numframes) {     EncodeOneFrame (0);     //decoding after each encoding frame    int vclNal, len = 0;    encToDecData (info, len);    unsigned char* pData[3] = { NULL };    memset (&dstBufInfo_, 0, sizeof (SBufferInfo));    rv = decoder_->DecodeFrame2 (info.sLayerInfo[0].pBsBuf, len, pData, &dstBufInfo_);    ASSERT_TRUE (rv == cmResultSuccess);    rv = decoder_->GetOption (DECODER_OPTION_VCL_NAL, &vclNal);    EXPECT_EQ (vclNal, FEEDBACK_UNKNOWN_NAL); //no reconstruction, unknown return    rv = decoder_->DecodeFrame2 (NULL, 0, pData, &dstBufInfo_); //reconstruction    ASSERT_TRUE (rv == cmResultSuccess);    rv = decoder_->GetOption (DECODER_OPTION_VCL_NAL, &vclNal);    EXPECT_EQ (vclNal, FEEDBACK_VCL_NAL);    iIdx++;  } //while  //ignore last frame} TEST_P (EncodeDecodeTestAPI, GetOptionFramenum) {  EncodeDecodeFileParamBase p = GetParam();  prepareParam (1, p.slicenum,  p.width, p.height, p.frameRate);  encoder_->Uninitialize();  int rv = encoder_->InitializeExt (&param_);  ASSERT_TRUE (rv == cmResultSuccess);   int32_t iTraceLevel = WELS_LOG_QUIET;  encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &iTraceLevel);  decoder_->SetOption (DECODER_OPTION_TRACE_LEVEL, &iTraceLevel);   InitialEncDec (p.width, p.height);   int32_t iEncFrameNum = -1;  int32_t iDecFrameNum;  int iIdx = 0;  while (iIdx <= p.numframes) {    EncodeOneFrame (0);    //decoding after each encoding frame    int len = 0;    encToDecData (info, len);

Lines 1–500 of 2035.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t