android - FFmpeg 视频播放器

本文是在Jonesx 博客基础上修改的,使得播放器播放视频速度正常。

主要代码:

#include "jonesx_videoplayer_VideoPlayer.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include "log.h"
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
int player_state;
int player_speed = 1;

JNIEXPORT jint JNICALL Java_jonesx_videoplayer_VideoPlayer_play(JNIEnv * env,
		jclass clazz, jobject surface, jstring url) {
	LOGD("play");
	const char * file_name = (*env)->GetStringUTFChars(env, url, 0);

	av_register_all();

	AVFormatContext * pFormatCtx = avformat_alloc_context();

	// Open video file
	int err_code = -1;
	if (err_code = avformat_open_input(&pFormatCtx, file_name, NULL, NULL)
			!= 0) {

		LOGE("Couldn't open file:%s\n", file_name);
		LOGE("%d", err_code);

		return -1; // Couldn't open file
	}

	// Retrieve stream information
	if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
		LOGE("Couldn't find stream information.");
		return -1;
	}
	// Find the first video stream
	int videoStream = -1, i;
	for (i = 0; i < pFormatCtx->nb_streams; i++) {
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO
				&& videoStream < 0) {
			videoStream = i;
			LOGE("videoStream = %d", i);
			//不同文件格式取到的值不同
		}
	}
	if (videoStream == -1) {
		LOGE("Didn't find a video stream.");
		return -1; // Didn't find a video stream
	}
	//get fps
	int fps = pFormatCtx->streams[videoStream]->avg_frame_rate.num
			/ pFormatCtx->streams[videoStream]->avg_frame_rate.den;
	LOGE("fps %d", fps);
	LOGE("duration (%d)", pFormatCtx->duration);

	// Get a pointer to the codec context for the video stream
	AVCodecContext * pCodecCtx = pFormatCtx->streams[videoStream]->codec;

	// Find the decoder for the video stream
	AVCodec * pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL) {
		LOGE("Codec not found.");
		return -1; // Codec not found
	}

	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
		LOGE("Could not open codec.");
		return -1; // Could not open codec
	}

	// 获取native window
	ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env, surface);

	// 获取视频宽高
	int videoWidth = pCodecCtx->width;
	int videoHeight = pCodecCtx->height;

	// 设置native window的buffer大小,可自动拉伸
	ANativeWindow_setBuffersGeometry(nativeWindow, videoWidth, videoHeight,
			WINDOW_FORMAT_RGBA_8888);
	ANativeWindow_Buffer windowBuffer;

	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
		LOGE("Could not open codec.");
		return -1; // Could not open codec
	}

	// Allocate video frame
	AVFrame * pFrame = av_frame_alloc();

	// 用于渲染
	AVFrame * pFrameRGBA = av_frame_alloc();
	if (pFrameRGBA == NULL || pFrame == NULL) {
		LOGE("Could not allocate video frame.");
		return -1;
	}

	// Determine required buffer size and allocate buffer
	// buffer中数据就是用于渲染的,且格式为RGBA
	int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, pCodecCtx->width,
			pCodecCtx->height, 1);
	uint8_t * buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
	av_image_fill_arrays(pFrameRGBA->data, pFrameRGBA->linesize, buffer,
			AV_PIX_FMT_RGBA, pCodecCtx->width, pCodecCtx->height, 1);

	// 由于解码出来的帧格式不是RGBA的,在渲染之前需要进行格式转换
	struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,
			pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
			pCodecCtx->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);

	int frameFinished;
	AVPacket packet;
	//计算等待时间
	struct timeval lastFrameTime, currentFrameTime;
	long dif_sec, dif_usec, dif_real_time;
	gettimeofday(&lastFrameTime, NULL);
	while (1) {

		//停止播放
		if (player_state == -1) {
			break;
		}
		//暂停播放
		if (player_state == 1) {
			continue;
		}
		if (av_read_frame(pFormatCtx, &packet) >= 0) {
			// Is this a packet from the video stream?
			if (packet.stream_index == videoStream) {
				gettimeofday(¤tFrameTime, NULL);
				dif_sec = currentFrameTime.tv_sec - lastFrameTime.tv_sec;
				dif_usec = currentFrameTime.tv_usec - lastFrameTime.tv_usec;
				dif_real_time = dif_sec * 1000000 + dif_usec;
				int newFps = (int) (fps * player_speed);
				LOGE("player_speed(%d)", player_speed);
				//控制播放速度,使得视频按照正常帧率播放
				if (dif_real_time < 1000000) {
					if (dif_real_time < (1000000 / newFps)) {
						int sleepT = (1000000 / newFps - dif_real_time);
						LOGE("sleepT(%d)", sleepT);
						usleep(sleepT);
					}
				}
				// Decode video frame
				avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,
						&packet);

				// 并不是decode一次就可解码出一帧
				if (frameFinished) {

					// lock native window buffer
					ANativeWindow_lock(nativeWindow, &windowBuffer, 0);

					// 格式转换
					sws_scale(sws_ctx, (uint8_t const * const *) pFrame->data,
							pFrame->linesize, 0, pCodecCtx->height,
							pFrameRGBA->data, pFrameRGBA->linesize);

					// 获取stride
					uint8_t * dst = windowBuffer.bits;
					int dstStride = windowBuffer.stride * 4;
					uint8_t * src = (uint8_t*) (pFrameRGBA->data[0]);
					int srcStride = pFrameRGBA->linesize[0];

					// 由于window的stride和帧的stride不同,因此需要逐行复制
					int h;
					for (h = 0; h < videoHeight; h++) {
						memcpy(dst + h * dstStride, src + h * srcStride,
								srcStride);
					}

					ANativeWindow_unlockAndPost(nativeWindow);
					gettimeofday(&lastFrameTime, NULL);
				}

			}
			av_packet_unref(&packet);
		} else {
			//播放完毕或者播放失败
			LOGE(" Play over");
			break;
		}
	}

	av_free(buffer);
	av_free(pFrameRGBA);

	// Free the YUV frame
	av_free(pFrame);

	// Close the codecs
	avcodec_close(pCodecCtx);

	// Close the video file
	avformat_close_input(&pFormatCtx);

//	CString timelong;
//	...
//	//duration是以微秒为单位
//	//转换成hh:mm:ss形式
//	int tns, thh, tmm, tss;
//	tns  = (pFormatCtx->duration)/1000000;
//	thh  = tns / 3600;
//	tmm  = (tns % 3600) / 60;
//	tss  = (tns % 60);
//	timelong.Format("%02d:%02d:%02d",thh,tmm,tss);
	return 0;
}
JNIEXPORT jint JNICALL Java_jonesx_videoplayer_VideoPlayer_pause(JNIEnv * env,
		jclass clazz, jint state) {
	LOGD("pause");
	player_state = state;
	return 0;
}
JNIEXPORT jint JNICALL Java_jonesx_videoplayer_VideoPlayer_stop(JNIEnv * env,
		jclass clazz) {
	LOGD("stop");
	player_state = -1;
	return 0;
}
//JNIEXPORT jint JNICALL Java_jonesx_videoplayer_VideoPlayer_fastForward(
//		JNIEnv * env, jclass clazz, jobject surface) {
//	LOGD("fast forward");
//
//	return 0;
//}
//JNIEXPORT jint JNICALL Java_jonesx_videoplayer_VideoPlayer_back(JNIEnv * env,
//		jclass clazz, jobject surface) {
//	LOGD("back");
//
//	return 0;
//}
JNIEXPORT jint JNICALL Java_jonesx_videoplayer_VideoPlayer_speed(JNIEnv * env,
		jclass clazz, jint speed) {
	player_speed = speed;
	LOGD("speed(%d)", player_speed);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值