在 Android 平台上,MediaCodec 提供了一套 C API(通常通过 NDK 使用)来进行视频和音频的编解码。这个 API 允许开发者直接在 C/C++ 层面操作编解码器,而不是通过 Java 层的 MediaCodec 类。使用 C API 可以更好地控制性能,特别是在需要处理高效率、低延迟的多媒体应用时。
解码 创建解码器 使用 AMediaCodec_createDecoderByType
创建解码器实例。
获取解码器名称 1 2 3 4 5 6 7 8 9 10 11 12 std::string mime_type; switch (src) { case CodecType::CODEC_TYPE_H264: mime_type = "video/avc" ; break ; case CodecType::CODEC_TYPE_H265: mime_type = "video/hevc" ; break ; default : break ; } return mime_type;
设置解码格式参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 AMediaFormat* MediaCodecDecoderImpl::CreateMediaFormat ( const VideoInfo& video_info, const ExtraData& extra_data) { std::string mime_type = CodecTypeConvert <CodecType, std::string>(video_info.codec_type); AMediaFormat* format = AMediaFormat_new (); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_WIDTH, video_info.width); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_HEIGHT, video_info.height); AMediaFormat_setString (format, AMEDIAFORMAT_KEY_MIME, mime_type.c_str ()); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_COLOR_FORMAT, pix_fmt); switch (video_info.codec_type) { case CodecType::CODEC_TYPE_H264: { auto * avc_ps_ = new avc::ParamSets (); himawari::avc::ParseExtraDataToParamSet (extra_data, *avc_ps_); if (avc_ps_->sps_list.empty () || avc_ps_->pps_list.empty ()) { return nullptr ; } AMediaFormat_setBuffer (format, "csd-0" , avc_ps_->sps_list[0 ].data, avc_ps_->sps_list[0 ].data_size); AMediaFormat_setBuffer (format, "csd-1" , avc_ps_->pps_list[0 ].data, avc_ps_->pps_list[0 ].data_size); delete avc_ps_; } break ; case CodecType::CODEC_TYPE_H265: { auto * hevc_ps = new hevc::ParamSets (); himawari::hevc::ParseExtraDataToParamSet (extra_data, *hevc_ps); if (hevc_ps->vps_list.empty () || hevc_ps->sps_list.empty () || hevc_ps->pps_list.empty ()) { return nullptr ; } AMediaFormat_setBuffer (format, "csd-0" , hevc_ps->vps_list[0 ].data, hevc_ps->vps_list[0 ].data_size); AMediaFormat_setBuffer (format, "csd-1" , hevc_ps->sps_list[0 ].data, hevc_ps->sps_list[0 ].data_size); AMediaFormat_setBuffer (format, "csd-2" , hevc_ps->pps_list[0 ].data, hevc_ps->pps_list[0 ].data_size); delete hevc_ps; } break ; default : return nullptr ; } return format; }
创建解码器实例 1 2 3 4 5 6 7 8 9 std::string mime_type = CodecTypeConvert <CodecType, std::string>(video_info_.codec_type); codec_ = AMediaCodec_createDecoderByType (mime_type.c_str ()); format_ = CreateMediaFormat (video_info_, extra_data); AMediaCodec_configure (codec_, format_, nullptr , nullptr , 0 );AMediaCodec_start (codec_);
处理数据 AMediaCodec_dequeueInputBuffer
、AMediaCodec_queueInputBuffer
和 AMediaCodec_dequeueOutputBuffer
等函数处理数据。
MediaCodec支持Annex-B格式的码流,不支持avc格式的码流,需要提前将packet的格式进行转换,sps pps也需要从avc的ExtraData中提取出来
输入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 signed int input_index;do { input_index = AMediaCodec_dequeueInputBuffer (codec_, 1000 ); } while (input_index < 0 ); if (packet->byte_data) { uint8_t * buffer = AMediaCodec_getInputBuffer (codec_, input_index, nullptr ); if (!buffer) { return HMError::FAILED; } memcpy (buffer, packet->byte_data, packet->data_size); AMediaCodec_queueInputBuffer (codec_, input_index, 0 , packet->data_size, packet->pts, 0 ); } else { send_eos_ = true ; AMediaCodec_queueInputBuffer (codec_, input_index, 0 , 0 , 0 , AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); }
获取输出数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 AMediaCodecBufferInfo info; ssize_t status = AMediaCodec_dequeueOutputBuffer (codec_, &info, 1000 );if (status >= 0 ) { if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { receive_eos_ = true ; return HMError::END_OF_STREAM; } uint8_t * buffer = AMediaCodec_getOutputBuffer (codec_, status, nullptr ); size_t size = info.size; if (buffer != nullptr && size != 0 ) { frame->timestamp = info.presentationTimeUs; AMediaFormat_getInt64 (format_, AMEDIAFORMAT_KEY_DURATION, &frame->timespan); AMediaFormat_getInt32 (format_, AMEDIAFORMAT_KEY_WIDTH, &frame->video_frame_info.width); AMediaFormat_getInt32 (format_, AMEDIAFORMAT_KEY_HEIGHT, &frame->video_frame_info.height); int pix_fmt; AMediaFormat_getInt32 (format_, AMEDIAFORMAT_KEY_COLOR_FORMAT, &pix_fmt); frame->video_frame_info.pix_fmt = PixFormatConvert <int , PixFormat>(pix_fmt); ... err = HMError::OK; } else { err = HMError::WAIT_AGAIN; } AMediaCodec_releaseOutputBuffer (codec_, status, false ); } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { err = HMError::WAIT_AGAIN; } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { AMediaFormat_delete (format_); format_ = AMediaCodec_getOutputFormat (codec_); err = HMError::WAIT_AGAIN; } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { err = HMError::WAIT_AGAIN; } else { err = HMError::FAILED; } return err;
处理解码后的帧数据 Android平台给的解码数据都是packed格式的,不是planar格式,处理时需要注意
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 uint8_t * src_data;unsigned long len;switch (dst->video_frame_info.pix_fmt) { case PixFormat::PIX_FMT_NV12: if (!dst->byte_data.data) { FrameDataAlloc (&dst->byte_data, PixFormat::PIX_FMT_NV12, dst->video_frame_info.width, dst->video_frame_info.height); } dst->byte_data.linesize[0 ] = dst->video_frame_info.width; len = dst->byte_data.linesize[0 ] * dst->video_frame_info.height * sizeof (uint8_t ); src_data = src; memcpy (dst->byte_data.data[0 ], src_data, len); src += len; dst->byte_data.linesize[1 ] = dst->video_frame_info.width; len = dst->byte_data.linesize[1 ] * (dst->video_frame_info.height / 2 ) * sizeof (uint8_t ); src_data = src; memcpy (dst->byte_data.data[1 ], src_data, len); src += len; break ; case PixFormat::PIX_FMT_I420: if (!dst->byte_data.data) { FrameDataAlloc (&dst->byte_data, PixFormat::PIX_FMT_I420, dst->video_frame_info.width, dst->video_frame_info.height); } dst->byte_data.linesize[0 ] = dst->video_frame_info.width; len = dst->byte_data.linesize[0 ] * dst->video_frame_info.height * sizeof (uint8_t ); src_data = src; memcpy (dst->byte_data.data[0 ], src_data, len); src += len; dst->byte_data.linesize[1 ] = dst->video_frame_info.width / 2 ; len = dst->byte_data.linesize[1 ] * (dst->video_frame_info.height / 2 ) * sizeof (uint8_t ); src_data = src; memcpy (dst->byte_data.data[1 ], src_data, len); src += len; dst->byte_data.linesize[2 ] = dst->video_frame_info.width / 2 ; len = dst->byte_data.linesize[2 ] * (dst->video_frame_info.height / 2 ) * sizeof (uint8_t ); src_data = src; memcpy (dst->byte_data.data[2 ], src_data, len); src += len; break ; case PixFormat::PIX_FMT_ARGB: if (!dst->byte_data.data) { FrameDataAlloc (&dst->byte_data, PixFormat::PIX_FMT_ARGB, dst->video_frame_info.width, dst->video_frame_info.height); } dst->byte_data.linesize[0 ] = dst->video_frame_info.width; len = dst->byte_data.linesize[0 ] * dst->video_frame_info.height * sizeof (uint8_t ); src_data = src; memcpy (dst->byte_data.data[0 ], src_data, len); break ; default : return dst; } return dst;
停止和释放解码器 调用 AMediaCodec_stop
和 AMediaCodec_delete
。
1 2 3 4 5 6 7 if (format_) { AMediaFormat_delete (format_); } if (codec_) { AMediaCodec_stop (codec_); AMediaCodec_delete (codec_); }
编码 创建编码器 获取编码器名称 1 2 3 4 5 6 7 8 9 10 11 12 std::string mime_type; switch (src) { case CodecType::CODEC_TYPE_H264: mime_type = "video/avc" ; break ; case CodecType::CODEC_TYPE_H265: mime_type = "video/hevc" ; break ; default : break ; } return mime_type;
设置解码格式参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 AMediaFormat* MediaCodecDecoderImpl::CreateMediaFormat ( const VideoInfo& video_info, const ExtraData& extra_data) { std::string mime_type = CodecTypeConvert <CodecType, std::string>(video_info.codec_type); AMediaFormat* format = AMediaFormat_new (); AMediaFormat_setString (format, AMEDIAFORMAT_KEY_MIME, mime_type.c_str ()); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_WIDTH, video_info.width); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_HEIGHT, video_info.height); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_COLOR_FORMAT, pix_fmt); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_BIT_RATE, video_info.bit_rate); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_FRAME_RATE, video_info.fps); AMediaFormat_setInt32 (format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 100 ); AMediaFormat_setInt32 (format, "level" , 1 ); AMediaFormat_setInt32 (format, "profile" , 1 ); }
创建编码器实例 使用 AMediaCodec_createCodecByName
或 AMediaCodec_createEncoderByType
创建一个编码器实例。
1 2 3 4 5 6 7 8 9 10 std::string mime_type = CodecTypeConvert <CodecType, std::string>(video_info_.codec_type); codec_ = AMediaCodec_createEncoderByType (mime_type.c_str ()); format_ = CreateMediaFormat (video_info_); AMediaCodec_configure (codec_, format_, nullptr , nullptr , AMEDIACODEC_CONFIGURE_FLAG_ENCODE);AMediaCodec_start (codec_);
处理数据 MediaCodec支持Annex-B格式的码流,不支持avc格式的码流,输出的packet格式需要注意
输入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 signed int input_index;do { input_index = AMediaCodec_dequeueInputBuffer (codec_, 1000 ); } while (input_index < 0 ); if (frame->byte_data.data) { size_t size = 0 ; uint8_t * buffer = AMediaCodec_getInputBuffer (codec_, input_index, &size); if (!buffer) { return HMError::FAILED; } ... AMediaCodec_queueInputBuffer (codec_, input_index, 0 , size, frame->timestamp, 0 ); } else { send_eos_ = true ; AMediaCodec_queueInputBuffer (codec_, input_index, 0 , 0 , 0 , AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); }
将输入数据拷贝到输入Buffer中 Android平台接收的编码数据都是packed格式的,不是planar格式,处理时需要注意
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 uint8_t * dst_data = dst;unsigned long len;switch (src->video_frame_info.pix_fmt) { case PixFormat::PIX_FMT_NV12: len = src->byte_data.linesize[0 ] * src->video_frame_info.height * sizeof (uint8_t ); memcpy (dst_data, src->byte_data.data[0 ], len); dst_data += len; len = src->byte_data.linesize[1 ] * (src->video_frame_info.height / 2 ) * sizeof (uint8_t ); memcpy (dst_data, src->byte_data.data[1 ], len); dst_data += len; break ; case PixFormat::PIX_FMT_I420: len = src->byte_data.linesize[0 ] * src->video_frame_info.height * sizeof (uint8_t ); memcpy (dst_data, src->byte_data.data[0 ], len); dst_data += len; len = src->byte_data.linesize[1 ] * (src->video_frame_info.height / 2 ) * sizeof (uint8_t ); memcpy (dst_data, src->byte_data.data[1 ], len); dst_data += len; len = src->byte_data.linesize[2 ] * (src->video_frame_info.height / 2 ) * sizeof (uint8_t ); memcpy (dst_data, src->byte_data.data[2 ], len); dst_data += len; break ; case PixFormat::PIX_FMT_ARGB: len = src->byte_data.linesize[0 ] * src->video_frame_info.height * sizeof (uint8_t ); memcpy (dst_data, src->byte_data.data[0 ], len); break ; default : return dst; } return dst;
获取输出数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 std::error_code err; AMediaCodecBufferInfo info; ssize_t status = AMediaCodec_dequeueOutputBuffer (codec_, &info, 1000 );if (status >= 0 ) { if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { receive_eos_ = true ; return HMError::END_OF_STREAM; } uint8_t * buffer = AMediaCodec_getOutputBuffer (codec_, status, nullptr ); size_t size = info.size; if (buffer != nullptr && size != 0 ) { size_t len = size * sizeof (uint8_t ); if (!packet->byte_data) { packet->byte_data = static_cast <uint8_t *>(malloc (len)); memset (packet->byte_data, 0 , len); } memcpy (packet->byte_data, buffer, len); packet->data_size = size; packet->pts = info.presentationTimeUs; packet->dts = packet->pts; err = HMError::OK; if (!extra_data_.extradata) { } } else { err = HMError::WAIT_AGAIN; } AMediaCodec_releaseOutputBuffer (codec_, status, false ); } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { err = HMError::WAIT_AGAIN; } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { err = HMError::WAIT_AGAIN; } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { err = HMError::WAIT_AGAIN; } else { err = HMError::FAILED; } return err;
停止和释放编码器 调用 AMediaCodec_stop
和 AMediaCodec_delete
来停止和释放编码器资源。
1 2 3 4 5 6 7 8 9 if (format_) { AMediaFormat_delete (format_); format_ = nullptr ; } if (codec_) { AMediaCodec_stop (codec_); AMediaCodec_delete (codec_); codec_ = nullptr ; }