/**
   * Generates a test video file, saving it as VideoChunks. We generate frames with GL to avoid
   * having to deal with multiple YUV formats.
   *
   * @return true on success, false on "soft" failure
   */
  private boolean generateVideoFile(VideoChunks output) {
    if (VERBOSE) Log.d(TAG, "generateVideoFile " + mWidth + "x" + mHeight);
    MediaCodec encoder = null;
    InputSurface inputSurface = null;

    try {
      MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
      if (codecInfo == null) {
        // Don't fail CTS if they don't have an AVC codec (not here, anyway).
        Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
        return false;
      }
      if (VERBOSE) Log.d(TAG, "found codec: " + codecInfo.getName());

      // We avoid the device-specific limitations on width and height by using values that
      // are multiples of 16, which all tested devices seem to be able to handle.
      MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);

      // Set some properties.  Failing to specify some of these can cause the MediaCodec
      // configure() call to throw an unhelpful exception.
      format.setInteger(
          MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
      format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
      format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
      format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
      if (VERBOSE) Log.d(TAG, "format: " + format);
      output.setMediaFormat(format);

      // Create a MediaCodec for the desired codec, then configure it as an encoder with
      // our desired properties.
      encoder = MediaCodec.createByCodecName(codecInfo.getName());
      encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
      inputSurface = new InputSurface(encoder.createInputSurface());
      inputSurface.makeCurrent();
      encoder.start();

      generateVideoData(encoder, inputSurface, output);
    } finally {
      if (encoder != null) {
        if (VERBOSE) Log.d(TAG, "releasing encoder");
        encoder.stop();
        encoder.release();
        if (VERBOSE) Log.d(TAG, "released encoder");
      }
      if (inputSurface != null) {
        inputSurface.release();
      }
    }

    return true;
  }
  /**
   * select the first codec that match a specific MIME type
   *
   * @param mimeType
   * @return
   */
  private static final MediaCodecInfo selectAudioCodec(final String mimeType) {
    if (DEBUG) Log.v(TAG, "selectAudioCodec:");

    MediaCodecInfo result = null;
    // get the list of available codecs
    final int numCodecs = MediaCodecList.getCodecCount();
    LOOP:
    for (int i = 0; i < numCodecs; i++) {
      final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
      if (!codecInfo.isEncoder()) { // skipp decoder
        continue;
      }
      final String[] types = codecInfo.getSupportedTypes();
      for (int j = 0; j < types.length; j++) {
        if (DEBUG) Log.i(TAG, "supportedType:" + codecInfo.getName() + ",MIME=" + types[j]);
        if (types[j].equalsIgnoreCase(mimeType)) {
          if (result == null) {
            result = codecInfo;
            break LOOP;
          }
        }
      }
    }
    return result;
  }
  @Override
  protected void prepare() throws IOException {
    if (DEBUG) Log.v(TAG, "prepare:");
    mTrackIndex = -1;
    mMuxerStarted = mIsEOS = false;
    // prepare MediaCodec for AAC encoding of audio data from inernal mic.
    final MediaCodecInfo audioCodecInfo = selectAudioCodec(MIME_TYPE);
    if (audioCodecInfo == null) {
      Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
      return;
    }
    if (DEBUG) Log.i(TAG, "selected codec: " + audioCodecInfo.getName());

    final MediaFormat audioFormat = MediaFormat.createAudioFormat(MIME_TYPE, SAMPLE_RATE, 1);
    audioFormat.setInteger(
        MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
    audioFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO);
    audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
    audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
    //		audioFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, inputFile.length());
    //      audioFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs );
    if (DEBUG) Log.i(TAG, "format: " + audioFormat);
    mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
    mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mMediaCodec.start();
    if (DEBUG) Log.i(TAG, "prepare finishing");
    if (mListener != null) {
      try {
        mListener.onPrepared(this);
      } catch (final Exception e) {
        Log.e(TAG, "prepare:", e);
      }
    }
  }
  private static DecoderProperties findDecoder(String mime, String[] supportedCodecPrefixes) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
      return null; // MediaCodec.setParameters is missing.
    }
    for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
      MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
      if (info.isEncoder()) {
        continue;
      }
      String name = null;
      for (String mimeType : info.getSupportedTypes()) {
        if (mimeType.equals(mime)) {
          name = info.getName();
          break;
        }
      }
      if (name == null) {
        continue; // No HW support in this codec; try the next one.
      }
      Logging.v(TAG, "Found candidate decoder " + name);

      // Check if this is supported decoder.
      boolean supportedCodec = false;
      for (String codecPrefix : supportedCodecPrefixes) {
        if (name.startsWith(codecPrefix)) {
          supportedCodec = true;
          break;
        }
      }
      if (!supportedCodec) {
        continue;
      }

      // Check if codec supports either yuv420 or nv12.
      CodecCapabilities capabilities = info.getCapabilitiesForType(mime);
      for (int colorFormat : capabilities.colorFormats) {
        Logging.v(TAG, "   Color: 0x" + Integer.toHexString(colorFormat));
      }
      for (int supportedColorFormat : supportedColorList) {
        for (int codecColorFormat : capabilities.colorFormats) {
          if (codecColorFormat == supportedColorFormat) {
            // Found supported HW decoder.
            Logging.d(
                TAG,
                "Found target decoder "
                    + name
                    + ". Color: 0x"
                    + Integer.toHexString(codecColorFormat));
            return new DecoderProperties(name, codecColorFormat);
          }
        }
      }
    }
    return null; // No HW decoder.
  }
예제 #5
0
  // We declare this method as explicitly throwing Exception
  // since some bad decoders can throw IllegalArgumentExceptions unexpectedly
  // and we want to be sure all callers are handling this possibility
  @SuppressWarnings("RedundantThrows")
  private static MediaCodecInfo findKnownSafeDecoder(String mimeType, int requiredProfile)
      throws Exception {
    for (MediaCodecInfo codecInfo : getMediaCodecList()) {
      // Skip encoders
      if (codecInfo.isEncoder()) {
        continue;
      }

      // Check for explicitly blacklisted decoders
      if (isDecoderInList(blacklistedDecoderPrefixes, codecInfo.getName())) {
        LimeLog.info("Skipping blacklisted decoder: " + codecInfo.getName());
        continue;
      }

      // Find a decoder that supports the requested video format
      for (String mime : codecInfo.getSupportedTypes()) {
        if (mime.equalsIgnoreCase(mimeType)) {
          LimeLog.info("Examining decoder capabilities of " + codecInfo.getName());

          CodecCapabilities caps = codecInfo.getCapabilitiesForType(mime);

          if (requiredProfile != -1) {
            for (CodecProfileLevel profile : caps.profileLevels) {
              if (profile.profile == requiredProfile) {
                LimeLog.info("Decoder " + codecInfo.getName() + " supports required profile");
                return codecInfo;
              }
            }

            LimeLog.info("Decoder " + codecInfo.getName() + " does NOT support required profile");
          } else {
            return codecInfo;
          }
        }
      }
    }

    return null;
  }
예제 #6
0
  private static MediaCodecInfo findPreferredDecoder() {
    // This is a different algorithm than the other findXXXDecoder functions,
    // because we want to evaluate the decoders in our list's order
    // rather than MediaCodecList's order

    for (String preferredDecoder : preferredDecoders) {
      for (MediaCodecInfo codecInfo : getMediaCodecList()) {
        // Skip encoders
        if (codecInfo.isEncoder()) {
          continue;
        }

        // Check for preferred decoders
        if (preferredDecoder.equalsIgnoreCase(codecInfo.getName())) {
          LimeLog.info("Preferred decoder choice is " + codecInfo.getName());
          return codecInfo;
        }
      }
    }

    return null;
  }
예제 #7
0
  @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  public static IjkMediaCodecInfo setupCandidate(MediaCodecInfo codecInfo, String mimeType) {
    if (codecInfo == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) return null;

    String name = codecInfo.getName();
    if (TextUtils.isEmpty(name)) return null;

    name = name.toLowerCase(Locale.US);
    int rank = RANK_NO_SENSE;
    if (!name.startsWith("omx.")) {
      rank = RANK_NON_STANDARD;
    } else if (name.startsWith("omx.pv")) {
      rank = RANK_SOFTWARE;
    } else if (name.startsWith("omx.google.")) {
      rank = RANK_SOFTWARE;
    } else if (name.startsWith("omx.ffmpeg.")) {
      rank = RANK_SOFTWARE;
    } else if (name.startsWith("omx.k3.ffmpeg.")) {
      rank = RANK_SOFTWARE;
    } else if (name.startsWith("omx.avcodec.")) {
      rank = RANK_SOFTWARE;
    } else if (name.startsWith("omx.ittiam.")) {
      // unknown codec in qualcomm SoC
      rank = RANK_NO_SENSE;
    } else if (name.startsWith("omx.mtk.")) {
      // 1. MTK only works on 4.3 and above
      // 2. MTK works on MIUI 6 (4.2.1)
      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) rank = RANK_NO_SENSE;
      else rank = RANK_TESTED;
    } else {
      Integer knownRank = getKnownCodecList().get(name);
      if (knownRank != null) {
        rank = knownRank;
      } else {
        try {
          CodecCapabilities cap = codecInfo.getCapabilitiesForType(mimeType);
          if (cap != null) rank = RANK_ACCEPTABLE;
          else rank = RANK_LAST_CHANCE;
        } catch (Throwable e) {
          rank = RANK_LAST_CHANCE;
        }
      }
    }

    IjkMediaCodecInfo candidate = new IjkMediaCodecInfo();
    candidate.mCodecInfo = codecInfo;
    candidate.mRank = rank;
    candidate.mMimeType = mimeType;
    return candidate;
  }
 public static CodecInfo getCodecInfo(MediaCodecInfo codecInfo) {
   String[] types = codecInfo.getSupportedTypes();
   for (String type : types) {
     try {
       if (type.equals(MEDIA_CODEC_TYPE_H263)) return new H263CodecInfo(codecInfo);
       else if (type.equals(MEDIA_CODEC_TYPE_H264)) return new H264CodecInfo(codecInfo);
       else if (type.equals(MEDIA_CODEC_TYPE_VP8)) return new VP8CodecInfo(codecInfo);
     } catch (IllegalArgumentException e) {
       logger.error(
           "Error initializing codec info: " + codecInfo.getName() + ", type: " + type, e);
     }
   }
   return null;
 }
예제 #9
0
  private static boolean hasCodecForMime(boolean encoder, String mime) {
    for (MediaCodecInfo info : sMCL.getCodecInfos()) {
      if (encoder != info.isEncoder()) {
        continue;
      }

      for (String type : info.getSupportedTypes()) {
        if (type.equalsIgnoreCase(mime)) {
          Log.i(TAG, "found codec " + info.getName() + " for mime " + mime);
          return true;
        }
      }
    }
    return false;
  }
예제 #10
0
  public static MediaCodecInfo findFirstDecoder(String mimeType) {
    for (MediaCodecInfo codecInfo : getMediaCodecList()) {
      // Skip encoders
      if (codecInfo.isEncoder()) {
        continue;
      }

      // Check for explicitly blacklisted decoders
      if (isDecoderInList(blacklistedDecoderPrefixes, codecInfo.getName())) {
        LimeLog.info("Skipping blacklisted decoder: " + codecInfo.getName());
        continue;
      }

      // Find a decoder that supports the specified video format
      for (String mime : codecInfo.getSupportedTypes()) {
        if (mime.equalsIgnoreCase(mimeType)) {
          LimeLog.info("First decoder choice is " + codecInfo.getName());
          return codecInfo;
        }
      }
    }

    return null;
  }
  @Override
  public String toString() {
    StringBuilder colorStr = new StringBuilder("\ncolors:\n");
    for (int i = 0; i < colors.size(); i++) {
      colorStr.append(colors.get(i));
      if (i != colors.size() - 1) colorStr.append(", \n");
    }

    StringBuilder plStr = new StringBuilder("\nprofiles:\n");
    ProfileLevel[] profiles = getProfileLevels();
    for (int i = 0; i < profiles.length; i++) {
      plStr.append(profiles[i].toString());
      if (i != profiles.length - 1) plStr.append(", \n");
    }

    return codecInfo.getName() + "(" + getLibjitsiEncoding() + ")" + colorStr + plStr;
  }
예제 #12
0
  @SuppressWarnings("RedundantThrows")
  public static String dumpDecoders() throws Exception {
    String str = "";
    for (MediaCodecInfo codecInfo : getMediaCodecList()) {
      // Skip encoders
      if (codecInfo.isEncoder()) {
        continue;
      }

      str += "Decoder: " + codecInfo.getName() + "\n";
      for (String type : codecInfo.getSupportedTypes()) {
        str += "\t" + type + "\n";
        CodecCapabilities caps = codecInfo.getCapabilitiesForType(type);

        for (CodecProfileLevel profile : caps.profileLevels) {
          str += "\t\t" + profile.profile + " " + profile.level + "\n";
        }
      }
    }
    return str;
  }
  static {
    bannedYuvCodecs = new ArrayList<String>();

    // Banned H264 encoders/decoders
    // Crashes
    bannedYuvCodecs.add("OMX.SEC.avc.enc");
    bannedYuvCodecs.add("OMX.SEC.h263.enc");
    // Don't support 3.1 profile used by Jitsi
    bannedYuvCodecs.add("OMX.Nvidia.h264.decode");
    // bannedYuvCodecs.add("OMX.SEC.avc.dec");

    // Banned VP8 encoders/decoders
    bannedYuvCodecs.add("OMX.SEC.vp8.dec");
    // This one works only for res 176x144
    bannedYuvCodecs.add("OMX.google.vpx.encoder");

    for (int codecIndex = 0, codecCount = MediaCodecList.getCodecCount();
        codecIndex < codecCount;
        codecIndex++) {
      MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(codecIndex);
      logger.info(
          "Discovered codec: "
              + codecInfo.getName()
              + "/"
              + Arrays.toString(codecInfo.getSupportedTypes()));
      CodecInfo ci = CodecInfo.getCodecInfo(codecInfo);
      if (ci != null) {
        codecs.add(ci);
        ci.setBanned(bannedYuvCodecs.contains(ci.getName()));
      }
    }
    logger.info("Selected H264 encoder: " + getCodecForType(MEDIA_CODEC_TYPE_H264, true));
    logger.info("Selected H264 decoder: " + getCodecForType(MEDIA_CODEC_TYPE_H264, false));
    logger.info("Selected H263 encoder: " + getCodecForType(MEDIA_CODEC_TYPE_H263, true));
    logger.info("Selected H263 decoder: " + getCodecForType(MEDIA_CODEC_TYPE_H263, false));
    logger.info("Selected VP8 encoder: " + getCodecForType(MEDIA_CODEC_TYPE_VP8, true));
    logger.info("Selected VP8 decoder: " + getCodecForType(MEDIA_CODEC_TYPE_VP8, false));
  }
예제 #14
0
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
    //        mSurfaceView.setBackgroundColor(Color.RED);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(
        new View.OnClickListener() {
          private VideoPlayer mVideoPlayer = null;

          @Override
          public void onClick(View view) {
            if (mVideoPlayer != null) {
              mVideoPlayer.stop();
            }

            mVideoPlayer =
                new VideoPlayer(
                    MainActivity.mSelectedEncoder,
                    mSurfaceView.getHolder().getSurface(),
                    BASE_FOLDER + MainActivity.mSelectedFile);
            VideoPlayer.PlayTask task = new VideoPlayer.PlayTask(mVideoPlayer);
            task.execute();

            //                Snackbar.make(view, "Replace with your own action",
            // Snackbar.LENGTH_LONG)
            //                        .setAction("Action", null).show();
          }
        });

    List<String> decodersNames = new ArrayList<String>();
    for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
      MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);

      Log.d(TAG, "onCreate: info " + i + info.getName());

      if (!info.isEncoder()
          && (info.getName().contains("avc")
              || info.getName().contains("AVC")
              || info.getName().contains("h264")
              || info.getName().contains("H264"))) {
        decodersNames.add(info.getName());
      }

      //            mEncodersNames. = info.getName();
      //            String[] types = info.getSupportedTypes();
      //            String allTypes = "";
      //            for ( String type: types )
      //            {
      //                allTypes += type + "; ";
      //            }

      //            Log.d(TAG, "XXXXXX - " +
      //                    "Name: " + info.getName() + ", " +
      //                    "Types: " + allTypes + ", " +
      //                    (info.isEncoder() ? "Encoder" : "Decoder"));
    }

    {
      mEncodersNames = new String[decodersNames.size()];
      decodersNames.toArray(mEncodersNames);

      Spinner spinner = (Spinner) findViewById(R.id.listEncoders);

      ArrayAdapter<String> adapter =
          new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mEncodersNames);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      // Apply the adapter to the spinner.
      spinner.setAdapter(adapter);
      spinner.setOnItemSelectedListener(this);
    }

    {
      File f = new File(new String(BASE_FOLDER));
      File filesDir = f; // getFilesDir()
      Log.d(TAG, "searching for files in " + filesDir.toString());
      mFilesNames = com.android.grafika.MiscUtils.getFiles(filesDir, "*");
      Log.d(TAG, "files: " + mFilesNames.toString());
      for (String file : mFilesNames) {
        Log.d(TAG, file);
      }

      Spinner spinner = (Spinner) findViewById(R.id.listFiles);
      ArrayAdapter<String> adapter =
          new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mFilesNames);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      // Apply the adapter to the spinner.
      spinner.setAdapter(adapter);
      spinner.setOnItemSelectedListener(this);
    }
  }
 /**
  * Returns codec name that can be used to obtain <tt>MediaCodec</tt>.
  *
  * @return codec name that can be used to obtain <tt>MediaCodec</tt>.
  */
 public String getName() {
   return codecInfo.getName();
 }