1. 解码流程
2. 代码实现
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_playVideo(JNIEnv *env, jobject instance, jstring path_, jobject surface) {
int result;
const char *path = env->GetStringUTFChars(path_, 0);
av_register_all();
AVFormatContext *format_context = avformat_alloc_context();
result = avformat_open_input(&format_context, path, NULL, NULL);
if (result < 0) {
LOGE("Player Error : Can not open video file");
return;
}
result = avformat_find_stream_info(format_context, NULL);
if (result < 0) {
LOGE("Player Error : Can not find video file stream info");
return;
}
int video_stream_index = -1;
for (int i = 0; i < format_context->nb_streams; i++) {
if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
}
}
if (video_stream_index == -1) {
LOGE("Player Error : Can not find video stream");
return;
}
AVCodecContext *video_codec_context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(video_codec_context, format_context->streams[video_stream_index]->codecpar);
AVCodec *video_codec = avcodec_find_decoder(video_codec_context->codec_id);
if (video_codec == NULL) {
LOGE("Player Error : Can not find video codec");
return;
}
result = avcodec_open2(video_codec_context, video_codec, NULL);
if (result < 0) {
LOGE("Player Error : Can not open video codec");
return;
}
int videoWidth = video_codec_context->width;
int videoHeight = video_codec_context->height;
ANativeWindow *native_window = ANativeWindow_fromSurface(env, surface);
if (native_window == NULL) {
LOGE("Player Error : Can not create native window");
return;
}
result = ANativeWindow_setBuffersGeometry(native_window, videoWidth, videoHeight,WINDOW_FORMAT_RGBA_8888);
if (result < 0){
LOGE("Player Error : Can not set native window buffer");
ANativeWindow_release(native_window);
return;
}
ANativeWindow_Buffer window_buffer;
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
AVFrame *rgba_frame = av_frame_alloc();
int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, videoWidth, videoHeight, 1);
uint8_t *out_buffer = (uint8_t *) av_malloc(buffer_size * sizeof(uint8_t));
av_image_fill_arrays(rgba_frame->data, rgba_frame->linesize, out_buffer, AV_PIX_FMT_RGBA, videoWidth, videoHeight, 1);
struct SwsContext *data_convert_context = sws_getContext(
videoWidth, videoHeight, video_codec_context->pix_fmt,
videoWidth, videoHeight, AV_PIX_FMT_RGBA,
SWS_BICUBIC, NULL, NULL, NULL);
while (av_read_frame(format_context, packet) >= 0) {
if (packet->stream_index == video_stream_index) {
result = avcodec_send_packet(video_codec_context, packet);
if (result < 0 && result != AVERROR(EAGAIN) && result != AVERROR_EOF) {
LOGE("Player Error : codec step 1 fail");
return;
}
result = avcodec_receive_frame(video_codec_context, frame);
if (result < 0 && result != AVERROR_EOF) {
LOGE("Player Error : codec step 2 fail %d", result);
return;
}
result = sws_scale(
data_convert_context,
(const uint8_t* const*) frame->data, frame->linesize,
0, videoHeight,
rgba_frame->data, rgba_frame->linesize);
if (result <= 0) {
LOGE("Player Error : data convert fail");
return;
}
result = ANativeWindow_lock(native_window, &window_buffer, NULL);
if (result < 0) {
LOGE("Player Error : Can not lock native window");
} else {
uint8_t *bits = (uint8_t *) window_buffer.bits;
for (int h = 0; h < videoHeight; h++) {
memcpy(bits + h * window_buffer.stride * 4,
out_buffer + h * rgba_frame->linesize[0],
rgba_frame->linesize[0]);
}
ANativeWindow_unlockAndPost(native_window);
}
}
av_packet_unref(packet);
}
sws_freeContext(data_convert_context);
av_free(out_buffer);
av_frame_free(&rgba_frame);
av_frame_free(&frame);
av_packet_free(&packet);
ANativeWindow_release(native_window);
avcodec_close(video_codec_context);
avformat_close_input(&format_context);
env->ReleaseStringUTFChars(path_, path);
}