Android Hardware Decoding with MediaCodec

在Android 4.1之后号称已经在SDK层支持硬解,下面是一个使用的示例,但不得不说,这跟我所需要的还是差了太多(我需要的是我input raw h.264 video, 它直接output raw image buffer, RGB or YUV),不过,还是先收藏下,等回头有空再来研究。


 

Finally, I must say, finally, we get low-level media APIs in Android, the Android hardware decoding and encoding APIs are finally available. It was a thing since Android 4.1 in Google IO 2012, but until now, when a new Android version 4.2 has been released, those low-level APIs are still too hard to use. There're so many Android vendors, we can't even get the same results from all Google's Nexus devices.

Despite the annoyings, I still tried these APIs and prayed them to get better in next Android release, and next Android release would swallow market quikly.

I just finished a simple video player with the combination of MediaExtractor and MediaCodec, you may find the full project from my Github.

private MediaExtractor extractor;
private MediaCodec decoder;
private Surface surface;

public void run() {
  extractor = new MediaExtractor();
  extractor.setDataSource(SAMPLE);

  for (int i = 0; i < extractor.getTrackCount(); i++) {
    MediaFormat format = extractor.getTrackFormat(i);
    String mime = format.getString(MediaFormat.KEY_MIME);
    if (mime.startsWith("video/")) {
      extractor.selectTrack(i);
      decoder = MediaCodec.createDecoderByType(mime);
      decoder.configure(format, surface, null, 0);
      break;
    }
  }

  if (decoder == null) {
    Log.e("DecodeActivity", "Can't find video info!");
    return;
  }

  decoder.start();

  ByteBuffer[] inputBuffers = decoder.getInputBuffers();
  ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
  BufferInfo info = new BufferInfo();
  boolean isEOS = false;
  long startMs = System.currentTimeMillis();

  while (!Thread.interrupted()) {
    if (!isEOS) {
      int inIndex = decoder.dequeueInputBuffer(10000);
      if (inIndex >= 0) {
        ByteBuffer buffer = inputBuffers[inIndex];
        int sampleSize = extractor.readSampleData(buffer, 0);
        if (sampleSize < 0) {
          Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
          decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
          isEOS = true;
        } else {
          decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
          extractor.advance();
        }
      }
    }

    int outIndex = decoder.dequeueOutputBuffer(info, 10000);
    switch (outIndex) {
    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
      Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
      outputBuffers = decoder.getOutputBuffers();
      break;
    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
      Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
      break;
    case MediaCodec.INFO_TRY_AGAIN_LATER:
      Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
      break;
    default:
      ByteBuffer buffer = outputBuffers[outIndex];
      Log.v("DecodeActivity"
, "We can't use this buffer but render it due to the API limit, " + buffer);

      // We use a very simple clock to keep the video FPS, or the video
      // playback will be too fast
      while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
        try {
          sleep(10);
        } catch (InterruptedException e) {
          e.printStackTrace();
          break;
        }
      }
      decoder.releaseOutputBuffer(outIndex, true);
      break;
    }

    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
      Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
      break;
    }
  }

  decoder.stop();
  decoder.release();
  extractor.release();
}

Compared to the OMXCodec methods introduced in an early post, this method is more recommended if you're targeting Android 4.1 and later. But it's still too limited, you can't do anything with the decoded video frame, because there're more than 40 color formats to deal with and many of them are vendor proprietary without documentation.


Sure, here's an example of how to use the libavcodec API to decode audio: ```c // include necessary headers #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <math.h> #include <libavutil/frame.h> #include <libavcodec/avcodec.h> // define decoder context and packet AVCodecContext *dec_ctx = NULL; AVPacket *pkt = NULL; // define output frame AVFrame *decoded_frame = NULL; // define buffer for audio samples int16_t *audio_buf = NULL; int audio_buf_size = 0; // define audio parameters int audio_stream_index = -1; int audio_sample_rate = 0; int audio_channels = 0; int audio_frame_size = 0; // function to initialize the decoder int init_decoder(const char *filename) { int ret; // initialize the codec and format context AVFormatContext *fmt_ctx = NULL; if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) { fprintf(stderr, "Error opening input file: %s\n", av_err2str(ret)); return ret; } if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) { fprintf(stderr, "Error finding stream information: %s\n", av_err2str(ret)); avformat_close_input(&fmt_ctx); return ret; } // find the audio stream for (int i = 0; i < fmt_ctx->nb_streams; i++) { if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audio_stream_index = i; break; } } if (audio_stream_index == -1) { fprintf(stderr, "Error finding audio stream\n"); avformat_close_input(&fmt_ctx); return AVERROR_STREAM_NOT_FOUND; } // get the audio parameters dec_ctx = avcodec_alloc_context3(NULL); if (!dec_ctx) { fprintf(stderr, "Error allocating codec context\n"); avformat_close_input(&fmt_ctx); return AVERROR(ENOMEM); } avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[audio_stream_index]->codecpar); av_codec_set_pkt_timebase(dec_ctx, fmt_ctx->streams[audio_stream_index]->time_base); AVCodec *codec = avcodec_find_decoder(dec_ctx->codec_id); if (!codec) { fprintf(stderr, "Error finding codec\n"); avcodec_free_context(&dec_ctx); avformat_close_input(&fmt_ctx); return AVERROR_DECODER_NOT_FOUND; } if ((ret = avcodec_open2(dec_ctx, codec, NULL)) < 0) { fprintf(stderr, "Error opening codec: %s\n", av_err2str(ret)); avcodec_free_context(&dec_ctx); avformat_close_input(&fmt_ctx); return ret; } audio_sample_rate = dec_ctx->sample_rate; audio_channels = dec_ctx->channels; audio_frame_size = dec_ctx->frame_size; // allocate the packet and output frame pkt = av_packet_alloc(); decoded_frame = av_frame_alloc(); if (!pkt || !decoded_frame) { fprintf(stderr, "Error allocating packet or frame\n"); avcodec_free_context(&dec_ctx); av_packet_free(&pkt); av_frame_free(&decoded_frame); avformat_close_input(&fmt_ctx); return AVERROR(ENOMEM); } // close the format context avformat_close_input(&fmt_ctx); return 0; } // function to decode the next audio packet int decode_packet() { int ret; // read the next packet if ((ret = av_read_frame(fmt_ctx, pkt)) < 0) { if (ret == AVERROR_EOF) { return 0; } else { fprintf(stderr, "Error reading packet: %s\n", av_err2str(ret)); return ret; } } // check if the packet is an audio packet if (pkt->stream_index != audio_stream_index) { av_packet_unref(pkt); return 1; } // send the packet to the decoder if ((ret = avcodec_send_packet(dec_ctx, pkt)) < 0) { fprintf(stderr, "Error sending packet to decoder: %s\n", av_err2str(ret)); av_packet_unref(pkt); return ret; } av_packet_unref(pkt); // read the decoded frames while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, decoded_frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { fprintf(stderr, "Error receiving frame from decoder: %s\n", av_err2str(ret)); return ret; } // allocate space for the audio buffer int buf_size = av_samples_get_buffer_size(NULL, audio_channels, decoded_frame->nb_samples, AV_SAMPLE_FMT_S16, 1); if (buf_size <= 0) { fprintf(stderr, "Error getting buffer size\n"); return AVERROR_INVALIDDATA; } if (!audio_buf) { audio_buf = (int16_t *) malloc(buf_size); audio_buf_size = buf_size; } else if (buf_size > audio_buf_size) { audio_buf = (int16_t *) realloc(audio_buf, buf_size); audio_buf_size = buf_size; } if (!audio_buf) { fprintf(stderr, "Error allocating audio buffer\n"); return AVERROR(ENOMEM); } // convert the audio samples to signed 16-bit integers int16_t *samples = (int16_t *) decoded_frame->data[0]; for (int i = 0; i < decoded_frame->nb_samples * audio_channels; i++) { audio_buf[i] = samples[i]; } } return 1; } // function to clean up resources void cleanup() { if (dec_ctx) { avcodec_free_context(&dec_ctx); } if (pkt) { av_packet_free(&pkt); } if (decoded_frame) { av_frame_free(&decoded_frame); } if (audio_buf) { free(audio_buf); } } // main function int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <input file>\n", argv[0]); return 1; } if (init_decoder(argv[1]) < 0) { cleanup(); return 1; } int ret; while ((ret = decode_packet()) > 0) { // do something with the audio samples } if (ret < 0) { cleanup(); return 1; } cleanup(); return 0; } ``` This example initializes the decoder, reads packets from the input file, sends them to the decoder, and converts the decoded audio samples to signed 16-bit integers. You can modify the `do something with the audio samples` section to perform whatever processing you need on the audio data.
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值