zoukankan      html  css  js  c++  java
  • android使用ffmpeg

    cygwin上文编译文章.


    在ffmpeg/arm添加的文件夹Android.mk 的主要目的是为了宣布动态库libs下一个

    LOCAL_PATH:= $(call my-dir)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libavcodec
    LOCAL_SRC_FILES:= lib/libavcodec-55.so
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    include $(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libavformat
    LOCAL_SRC_FILES:= lib/libavformat-55.so
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    include $(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libswscale
    LOCAL_SRC_FILES:= lib/libswscale-2.so
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    include $(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libavutil
    LOCAL_SRC_FILES:= lib/libavutil-52.so
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    include $(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libavfilter
    LOCAL_SRC_FILES:= lib/libavfilter-4.so
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    include $(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libwsresample
    LOCAL_SRC_FILES:= lib/libswresample-0.so
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    include $(PREBUILT_SHARED_LIBRARY)


    添加Media.h

    #pragma once
    #include <jni.h>
    #include <android/native_window_jni.h>
    #include "utils/Lock.h"
    #include <pthread.h>
    
    //ffmpeg 须要先定义 __STDC_CONSTANT_MACROS 才干通过 c++ 编译
    #define __STDC_CONSTANT_MACROS
    #ifndef INT64_C
    #define INT64_C(c) (c ## LL)
    #define UINT64_C(c) (c ## ULL)
    #endif
    extern "C" {
    	#include <libavcodec/avcodec.h>
    	#include <libavformat/avformat.h>
    	#include <libavutil/avutil.h>
    	#include <libavutil/dict.h>
    	#include <libavutil/frame.h>
    	#include <libavutil/mem.h>
    	#include <libavutil/pixfmt.h>
    	#include <libswscale/swscale.h>
    	#include <libavutil/time.h>
    	#include <libavutil/opt.h>
    	#include <libswresample/swresample.h>
    }
    
    class Media
    {
    	public:
    
    		Media();
    		~Media();
    		void setSurface(JNIEnv *pEnv, jobject pSurface,int pWidth,int pHeight);
    		bool initPath(const char * path);
    		bool initCodec(int width,int height);
    		int getResWidth();
    		int getResHeight();
    		void play();
    		void pause();
    		void stop();
    		bool isPlaying();
    		void decodeAndRenderPic(void *pBuffer,int dwBufsize);
    		void decodeAudioAndPlay(void *pBuffer,int dwBufsize);
    	private:
    		static void* decodeAndRenderAdpt(void *params);
    		void decodeAndRender();
    	private:
    		bool bInit;
    
    		ANativeWindow* 		window;
    		char 				*videoFileName;
    		AVFormatContext 	*formatCtx;
    		int 				videoStream;
    		int               audioStream;
    		AVCodecContext  	*codecCtx;
    		AVCodecContext  	*codecCtxAudio;
    		AVFrame         	*decodedFrame;
    		AVFrame         	*frameRGBA ;
    		jobject				bitmap;
    		void*				buffer;
    		struct SwsContext   *sws_ctx;
    		struct SwrContext   *swr_ctx;
    		int 				width;
    		int 				height;
    		bool               _stop;
    
    		pthread_t decodeThread;
    
    		Mutex mutexSurface;
    		Mutex lockWindow;
    };
    


    核心代码例如以下:


    添加Media.cpp

    #include "Media.h"
    #include "Audio.h"
    
    //ffmpeg 须要先定义 __STDC_CONSTANT_MACROS 才干通过 c++ 编译
    #define __STDC_CONSTANT_MACROS
    
    #include <android/native_window_jni.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "utils/Log.h"
    #include "cu.h"
    #include <unistd.h>
    #include <sys/syscall.h>
    #include <sys/linux-syscalls.h>
    
    #define SYS_gettid __NR_gettid
    
    extern "C" {
    	#include <libavcodec/avcodec.h>
    	#include <libavformat/avformat.h>
    	#include <libavutil/avutil.h>
    	#include <libavutil/dict.h>
    	#include <libavutil/frame.h>
    	#include <libavutil/mem.h>
    	#include <libavutil/pixfmt.h>
    	#include <libswscale/swscale.h>
    }
    
    #define RGB_SIZE 4
    //AV_PIX_FMT_RGBA,AV_PIX_FMT_RGB24
    #define AV_FMT AV_PIX_FMT_RGBA
    
    Media::Media():mutexSurface(true),window(NULL),lockWindow(false)
    		,frameRGBA(NULL),decodedFrame(NULL)
    		,codecCtx(NULL),formatCtx(NULL),_stop(true)
           ,buffer(NULL),height(0),width(0),videoStream(-1)
    	   ,sws_ctx(NULL),videoFileName(NULL),audioStream(-1)
    		,codecCtxAudio(NULL),swr_ctx(NULL),decodeThread(NULL)
    {
    	bInit = false;
    }
    Media::~Media(){
    	stop();
    	if(NULL!=decodeThread)
    	{
    		pthread_join(decodeThread, NULL);
    	}
    	if(NULL!=window)
    	{
    		ANativeWindow_release(window);
    		window=NULL;
    	}
    	// Free the RGB image
    	if(NULL!=frameRGBA)
    	{
    		av_free(frameRGBA);
    		frameRGBA=NULL;
    	}
    	// Free the YUV frame
    	if(NULL!=decodedFrame)
    	{
    		av_free(decodedFrame);
    		decodedFrame=NULL;
    	}
    	// Close the codec
    	if(NULL!=codecCtx)
    	{
    		avcodec_close(codecCtx);
    		codecCtx=NULL;
    	}
    	// Close the video file
    	if(NULL!=formatCtx)
    	{
    		avformat_close_input(&formatCtx);
    		formatCtx=NULL;
    	}
    }
    void Media::setSurface(JNIEnv *pEnv, jobject pSurface,int pWidth,int pHeight)
    {
    	LOGD("Media::setSurface start, %d,%d,%d", (int)pSurface , pWidth, pHeight);
    	if (0 != pSurface) {
    		if(pWidth <=0 || pHeight<=0)
    		{
    			LOGD("Media::setSurface width or height is zero !!! %d,%d",  pWidth, pHeight);
    			return;
    		}
    		if(NULL==window)
    		{
    			synchronized(lockWindow)
    			{
    				// get the native window reference
    				window = ANativeWindow_fromSurface(pEnv, pSurface);
    				// set format and size of window buffer WINDOW_FORMAT_RGBA_8888
    				ANativeWindow_setBuffersGeometry(window, 0, 0, WINDOW_FORMAT_RGBA_8888);
    			}
    		}
    	} else {
    		stop();
    		if(NULL!=window)
    		{
    			// release the native window
    			synchronized(lockWindow)
    			{
    				ANativeWindow_release(window);
    				window=NULL;
    			}
    		}
    		return;
    	}
    
    	//reset width and height
    	width = pWidth;
    	height = pHeight;
    	if(NULL != buffer)
    	{
    		free(buffer);
    		buffer=NULL;
    	}
    	buffer = malloc(pWidth * pHeight * RGB_SIZE);
    	if(NULL == buffer)
    	{
    		LOGE("Media::setSurface Cannot malloc buffer size : %d!", pWidth * pHeight * RGB_SIZE);
    		return;
    	}
    	//get the scaling context
    	sws_ctx = sws_getContext (
    			codecCtx->width,
    			codecCtx->height,
    			codecCtx->pix_fmt,
    			pWidth,
    			pHeight,
    			AV_FMT,
    			SWS_FAST_BILINEAR,//SWS_BILINEAR,
    			NULL,
    			NULL,
    			NULL
    	);
    	// Assign appropriate parts of bitmap to image planes in pFrameRGBA
    	// Note that pFrameRGBA is an AVFrame, but AVFrame is a superset
    	// of AVPicture
    	avpicture_fill((AVPicture *)frameRGBA, (uint8_t *)buffer, AV_FMT,
    			pWidth, pHeight);
    
    	LOGD("Media::setSurface window:%d , mutexInit.isLocked: %d !", (int)window,(int) mutexSurface.isLocked());
    	if(NULL!=window && mutexSurface.isLocked())
    	{
    		LOGD("Media::setSurface unlock surface!");
    		mutexSurface.unlock();
    	}
    	LOGD("Media::setSurface OK!");
    	return;
    }
    
    void audio_swr_resampling_audio_init(SwrContext **swr_ctx,/*TargetAudioParams *targetAudioParams,*/AVCodecContext *codec){
    
        if(codec->sample_fmt == AV_SAMPLE_FMT_S16 /*|| codec->sample_fmt == AV_SAMPLE_FMT_S32 */||codec->sample_fmt == AV_SAMPLE_FMT_U8){
            LOGE("codec->sample_fmt:%d",codec->sample_fmt);
            if(*swr_ctx){
                swr_free(swr_ctx);
                *swr_ctx = NULL;
            }
            return;
        }
    
        if(*swr_ctx){
            swr_free(swr_ctx);
        }
    
        *swr_ctx = swr_alloc();
        if(!*swr_ctx){
            LOGE("swr_alloc failed");
            return;
        }
        /* set options */
        av_opt_set_int(*swr_ctx, "in_channel_layout",    codec->channel_layout, 0);
        av_opt_set_int(*swr_ctx, "in_sample_rate",       codec->sample_rate, 0);
        av_opt_set_sample_fmt(*swr_ctx, "in_sample_fmt", codec->sample_fmt, 0);
        av_opt_set_int(*swr_ctx, "out_channel_layout",    codec->channel_layout/*targetAudioParams->channel_layout*/, 0);
        av_opt_set_int(*swr_ctx, "out_sample_rate",       codec->sample_rate/*targetAudioParams->sample_rate*/, 0);
        av_opt_set_sample_fmt(*swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16/*targetAudioParams->sample_fmt*/, 0);// AV_SAMPLE_FMT_S16
    
        /* initialize the resampling context */
        int ret = 0;
        if ((ret = swr_init(*swr_ctx)) < 0) {
            LOGE("Failed to initialize the resampling context
    ");
            if(*swr_ctx){
                swr_free(swr_ctx);
                *swr_ctx = NULL;
            }
            return;
        }
    }
    
    int audio_swr_resampling_audio(struct SwrContext *swr_ctx,/*TargetAudioParams *targetAudioParams,*/AVFrame *audioFrame,uint8_t **targetData){
        int len = swr_convert(swr_ctx,targetData
        		,audioFrame->nb_samples
        		,(const uint8_t **)audioFrame->extended_data
        		,audioFrame->nb_samples);
        if(len < 0){
            LOGE("error swr_convert");
            return -1;
        }
    
        int dst_bufsize = len * audioFrame->channels/*targetAudioParams->channels*/ * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16/*targetAudioParams->sample_fmt*/);
        LOGI("dst_bufsize:%d",dst_bufsize);
        return dst_bufsize;
    }
    
    void audio_swr_resampling_audio_destory(SwrContext **swr_ctx){
        if(*swr_ctx){
            swr_free(swr_ctx);
            *swr_ctx = NULL;
        }
    }
    
    bool Media::initPath(const char *path)
    {
    	AVCodec         *pCodec = NULL;
    	int 			i;
    	AVDictionary    *optionsDict = NULL;
    
    	LOGI("video file name is %s", path);
    	// Register all formats and codecs
    	av_register_all();
    	// Open video file
    	if(avformat_open_input(&formatCtx, path, NULL, NULL)!=0)
    		return false; // Couldn't open file
    	LOGD("Media::initPath pos 1");
    	// Retrieve stream information
    	if(avformat_find_stream_info(formatCtx, NULL)<0)
    		return false; // Couldn't find stream information
    	LOGD("Media::initPath pos 2");
    	// Dump information about file onto standard error
    	av_dump_format(formatCtx, 0, path, 0);
    	LOGD("Media::initPath pos 3");
    	// Find the first video stream
    	videoStream=-1;
    	audioStream=-1;
    	for(i=0; i<formatCtx->nb_streams; i++) {
    		if(formatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
    			LOGI("FIND VIDEO CODEC ID : %d" , i);
    			videoStream=i;
    			if(audioStream!=-1 )
    				break;
    		}
    		else if(formatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO) {
    			LOGI("FIND AUDIO CODEC ID: %d" , i);
    			audioStream=i;
    			if(videoStream!=-1 )
    				break;
    		}
    	}
    	LOGD("Media::initPath pos 5");
    	if(videoStream==-1)
    		return false; // Didn't find a video stream
    	// Get a pointer to the codec context for the video stream
    	codecCtx=formatCtx->streams[videoStream]->codec;
    	// Find the decoder for the video stream
    	pCodec=avcodec_find_decoder(codecCtx->codec_id);
    	LOGD("Media::initPath pos 6");
    	if(pCodec==NULL) {
    		fprintf(stderr, "Unsupported codec!
    ");
    		return false; // Codec not found
    	}
    	LOGD("Media::initPath pos 7");
    	// Open codec
    	if(avcodec_open2(codecCtx, pCodec, &optionsDict)<0)
    		return false; // Could not open codec
    	LOGD("Media::initPath pos 8");
    	// Allocate video frame
    	decodedFrame=av_frame_alloc();
    	LOGD("Media::initPath pos 9");
    	// Allocate an AVFrame structure
    	frameRGBA=av_frame_alloc();
    	if(frameRGBA==NULL)
    		return false;
    	bInit=true;
    	LOGD("Media::initPath pos 10");
    
    	//audio decodec
    	if(-1!=audioStream)
    	{
    		codecCtxAudio = formatCtx->streams[audioStream]->codec;
    		pCodec = avcodec_find_decoder(codecCtxAudio->codec_id);
    
    		if(avcodec_open2(codecCtxAudio, pCodec, &optionsDict)<0)
    		{
    			audioStream=-1;
    			LOGW("Error avcodec_open2 Audio Decode!");
    		}
    
    		LOGE("codecCtxAudio data: %d, %d, %d"
    				, codecCtxAudio->bit_rate
    				, codecCtxAudio->sample_rate
    				, codecCtxAudio->channels);
    	}
    	return true;
    }
    
    bool Media::initCodec(int width, int height)
    {
    	AVCodec         *pCodec = NULL;
    	int 			i;
    
    	//avcodec_init();
    	// Register all formats and codecs
    	av_register_all();
    
    	/* find the video encoder */
    	pCodec = avcodec_find_decoder(CODEC_ID_H264);
    
    	if (!pCodec)
    	{
    		LOGE("codec not found!");
    		return false;
    	}
    
    	codecCtx = avcodec_alloc_context3(pCodec);
    	//初始化參数。以下的參数应该由详细的业务决定
    	codecCtx->time_base.num = 1;
    	codecCtx->frame_number = 1; //每包一个视频帧
    	codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    	codecCtx->bit_rate = 0;
    	codecCtx->time_base.den = 30;//帧率
    	codecCtx->width = width;//视频宽
    	codecCtx->height = height;//视频高
    	codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    
    	LOGE("codecCtx init OK! %d", (int)codecCtx);
    	// Open codec
    	if(avcodec_open2(codecCtx, pCodec, NULL)<0)
    		return false; // Could not open codec
    	// Allocate video frame
    	decodedFrame=av_frame_alloc();
    	// Allocate an AVFrame structure
    	frameRGBA=av_frame_alloc();
    	if(frameRGBA==NULL)
    		return false;
    
    	//Audio
    	int audioBitrate = 64000;
    	int sampleRate = 44100;// 44100, 22050 and 11025.
    	int channels=2;
    	pCodec = avcodec_find_decoder(CODEC_ID_AAC);
    	if (!pCodec)
    	{
    		LOGE("codec not found!");
    		return false;
    	}
    	codecCtxAudio = avcodec_alloc_context3(pCodec);
    	codecCtxAudio->codec_type = AVMEDIA_TYPE_AUDIO;
    	codecCtxAudio->codec_id  = AV_CODEC_ID_AAC;
    	codecCtxAudio->sample_fmt = AV_SAMPLE_FMT_S16;
    	codecCtxAudio->sample_rate = sampleRate;
    	codecCtxAudio->channels = channels;
    	/*codecCtxAudio->profile = FF_PROFILE_AAC_MAIN;
    	codecCtxAudio->channel_layout = AV_CH_LAYOUT_STEREO;
    	codecCtxAudio->bit_rate = audioBitrate;
    	codecCtxAudio->time_base.num= 1;
    	codecCtxAudio->time_base.den= sampleRate;*/
    	codecCtxAudio->pix_fmt = PIX_FMT_NONE;
    
    	if(avcodec_open2(codecCtxAudio, pCodec, NULL)<0)
    	{
    		LOGE("codec not found!");
    		codecCtxAudio=NULL;
    	}
    	bInit=true;
    	return true;
    }
    
    int Media::getResWidth()
    {
    	if(bInit && NULL != codecCtx)
    		return codecCtx->width;
    	return -1;
    }
    
    int Media::getResHeight()
    {
    	if(bInit && NULL != codecCtx)
    			return codecCtx->height;
    		return -1;
    }
    
    void Media::play()
    {
    	_stop = false;
    	pthread_create(&decodeThread, NULL, decodeAndRenderAdpt, this);
    }
    
    void Media::pause()
    {
    	_stop=true;
    }
    
    void Media::stop()
    {
    	_stop=true;
    	bInit = false;
    }
    bool Media::isPlaying()
    {
    	return !_stop;
    }
    void* Media::decodeAndRenderAdpt(void *params)
    {
    	LOGW("create thread : %d Media::decodeAndRenderAdpt", syscall(SYS_gettid));
    	bool bOk = AttachCurrentThread();
    	LOGI("AttachCurrentThread Result: %d", bOk);
    	Media *pMedia = (Media *)params;
    	if(NULL!=pMedia->codecCtxAudio)
    	{
    		audio_swr_resampling_audio_init(&pMedia->swr_ctx,pMedia->codecCtxAudio);
    		initAudio(pMedia->codecCtxAudio->sample_rate,pMedia->codecCtxAudio->channels==1,pMedia->codecCtxAudio->sample_fmt != AV_SAMPLE_FMT_U8);
    		LOGI("initAudio %d,%d,%d",pMedia->codecCtxAudio->sample_rate,pMedia->codecCtxAudio->channels,pMedia->codecCtxAudio->sample_fmt);
    	}
    	try{
    		pMedia->decodeAndRender();
    	}catch (...) {
    		LOGE("unkown Exception in Thread: Media::decodeAndRender");
    	}
    	if(bOk)
    		DetachCurrentThread();
    	if(NULL!=pMedia->codecCtxAudio)
    	{
    		releaseAudio();
    		audio_swr_resampling_audio_destory(&pMedia->swr_ctx);
    	}
    	pMedia->decodeThread=NULL;
    	return NULL;
    }
    
    void Media::decodeAndRender()
    {
    	LOGD("Media::decodeAndRender check mutexInit.isLocked: %d !",(int)mutexSurface.isLocked());
    	if(mutexSurface.isLocked())
    	{
    		LOGD("Media::decodeAndRender wait unlock surface!");
    		mutexSurface.lock();
    		mutexSurface.unlock();
    		LOGD("Media::decodeAndRender wait unlock surface finished ok!");
    	}
    	ANativeWindow_Buffer 	windowBuffer;
    	AVPacket        		packet;
    	int 					i=0;
    	int            			frameFinished;
    	int 					lineCnt;
    	long					pts;
    	long 					baseTime=0;
    	long					waitTime = 0;
    	ANativeWindow * pWin;
    	pWin=window;
    	uint8_t **dst_data = NULL;
    	/* FILE *stream;
    	 stream = fopen("/sdcard/1.pcm", "wb");*/
    	while(av_read_frame(formatCtx, &packet)>=0 && !_stop && NULL!=window && bInit) {
    		// Is this a packet from the video stream?

    if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode_video2(codecCtx, decodedFrame, &frameFinished, &packet); // Did we get a video frame?

    if(frameFinished) { // Convert the image from its native format to RGBA sws_scale ( sws_ctx, (uint8_t const * const *)decodedFrame->data, decodedFrame->linesize, 0, codecCtx->height, frameRGBA->data, frameRGBA->linesize ); if(packet.dts == AV_NOPTS_VALUE && decodedFrame->opaque && *(uint64_t*)decodedFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)decodedFrame->opaque; LOGD("pst1: %d",pts); } else if(packet.dts != AV_NOPTS_VALUE) { pts = packet.dts; LOGD("pst2: %d",pts); } else { pts = 0; LOGD("pst3: %d",pts); } //pts = av_q2d(codecCtx->time_base) * 1000000.0 * i * 2; pts *= 1000; //LOGD("debug %d,%d,%f",pts, (long)(av_q2d(codecCtx->time_base) * 1000000.0 * i * 2), av_q2d(codecCtx->time_base)); if(0 == pts || 0 == baseTime) { baseTime = av_gettime() - pts; LOGD("BASETIME: %d",baseTime); }else{ waitTime = (baseTime + pts) - av_gettime(); LOGD("WAITTIME: %d, %d",waitTime,pts); } //waitTime = (av_q2d(codecCtx->time_base) * 1000.0 - 0.0) * 1000; if(waitTime>0) usleep(waitTime); if(!_stop) { synchronized(lockWindow) { if(!_stop && NULL!=window) { // lock the window buffer if (ANativeWindow_lock(pWin, &windowBuffer, NULL) < 0) { LOGE("cannot lock window"); } else { // draw the frame on buffer //LOGD("copy buffer %d:%d:%d", width, height, width*height*RGB_SIZE); //LOGD("window buffer: %d:%d:%d", windowBuffer.width, windowBuffer.height, windowBuffer.stride); //memcpy(windowBuffer.bits, buffer, width * height * RGB_SIZE); if(windowBuffer.width >= windowBuffer.stride){ //LOGE("1=========windowBuffer: %d,%d,%d,%d", windowBuffer.format,windowBuffer.stride,windowBuffer.width,windowBuffer.height); memcpy(windowBuffer.bits, buffer, width * height * RGB_SIZE); }else{ //LOGE("2=========windowBuffer: %d,%d,%d,%d", windowBuffer.format,windowBuffer.stride,windowBuffer.width,windowBuffer.height); //skip stride-width 跳过padding部分内存 for(int i=0;i<height;++i) memcpy(windowBuffer.bits + windowBuffer.stride * i * RGB_SIZE , buffer + width * i * RGB_SIZE , width * RGB_SIZE); } // unlock the window buffer and post it to display ANativeWindow_unlockAndPost(pWin); // count number of frames ++i; } } } } } }else if(packet.stream_index==audioStream) { int ret = avcodec_decode_audio4(codecCtxAudio,decodedFrame, &frameFinished, &packet); // LOGD("avcodec_decode_audio4, %d , ret %d" , frameFinished, ret); if(frameFinished) { // LOGD("read audio play"); size_t unpadded_linesize = decodedFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)decodedFrame->format); /* Write the raw audio data samples of the first plane. This works * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, * most audio decoders output planar audio, which uses a separate * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). * In other words, this code will write only the first audio channel * in these cases. * You should use libswresample or libavfilter to convert the frame * to packed data. */ if(NULL!=swr_ctx) { int dst_linesize = 0; int dst_nb_samples =av_rescale_rnd(decodedFrame->nb_samples, decodedFrame->sample_rate, codecCtxAudio->sample_rate, AV_ROUND_UP); int dst_nb_channels = av_get_channel_layout_nb_channels(codecCtxAudio->channels ==1 ?AV_CH_LAYOUT_MONO:AV_CH_LAYOUT_STEREO); av_samples_alloc_array_and_samples(&dst_data,&dst_linesize,dst_nb_channels,dst_nb_samples,codecCtxAudio->sample_fmt == AV_SAMPLE_FMT_U8?

    AV_SAMPLE_FMT_U8:AV_SAMPLE_FMT_S16, 0); int ret = audio_swr_resampling_audio(swr_ctx,decodedFrame,dst_data); if(ret>0){ writeAudio(dst_data[0],ret); //fwrite(dst_data[0], 1, ret, stream); } if (dst_data) { av_freep(&dst_data[0]); } av_freep(&dst_data); }else{ writeAudio(decodedFrame->extended_data[0], unpadded_linesize); //fwrite(decodedFrame->extended_data[0], 1, unpadded_linesize, stream); } //fwrite(decodedFrame->extended_data[0], 1, unpadded_linesize, audio_dst_file); LOGD("read audio buffer: %d ,%d", unpadded_linesize, decodedFrame->linesize[0]); }else{ //LOGD("===read audio buffer: %d", packet.size); //writeAudio(packet.data, packet.size); } }else{ LOGD("unkown stream index: %d", packet.stream_index); } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); } //fclose(stream); LOGI("total No. of frames decoded and rendered %d", i); } void Media::decodeAndRenderPic(void *pBuffer,int dwBufsize) { ANativeWindow_Buffer windowBuffer; AVPacket packet; int frameFinished; int lineCnt; ANativeWindow * pWin; pWin=window; ARect rect; rect.left=0; rect.top=0; rect.right = width; rect.bottom = height; memset(&packet,0x00,sizeof(AVPacket)); packet.data = (uint8_t*)pBuffer;//这里填入一个指向完整H264数据帧的指针 packet.size = dwBufsize;//这个填入H264数据帧的大小 // Decode video frame avcodec_decode_video2(codecCtx, decodedFrame, &frameFinished, &packet); // Did we get a video frame?

    //LOGD("111111111111111111111111"); if(frameFinished && NULL!=window && bInit) { // Convert the image from its native format to RGBA sws_scale ( sws_ctx, (uint8_t const * const *)decodedFrame->data, decodedFrame->linesize, 0, codecCtx->height, frameRGBA->data, frameRGBA->linesize ); //LOGD("22222222222222222222222222222"); synchronized(lockWindow) { if(NULL!=window) { // lock the window buffer if (ANativeWindow_lock(pWin, &windowBuffer, &rect) < 0) { LOGE("cannot lock window"); } else { //LOGD("333333333333333333333333333"); // draw the frame on buffer LOGD("copy buffer %d:%d:%d lineSize:%d", width, height, width*height*RGB_SIZE, frameRGBA->linesize[0]); LOGD("RECT : %d,%d,%d,%d",rect.left,rect.top,rect.right,rect.bottom); //LOGD("window buffer: %d:%d:%d", windowBuffer.width,windowBuffer.height, windowBuffer.stride); if(windowBuffer.width >= windowBuffer.stride){ //LOGE("1=========windowBuffer: %d,%d,%d,%d", windowBuffer.format,windowBuffer.stride,windowBuffer.width,windowBuffer.height); memcpy(windowBuffer.bits, buffer, width * height * RGB_SIZE); }else{ //LOGE("2=========windowBuffer: %d,%d,%d,%d", windowBuffer.format,windowBuffer.stride,windowBuffer.width,windowBuffer.height); //skip stride-width 跳过padding部分内存 for(int i=0;i<height;++i) memcpy(windowBuffer.bits + windowBuffer.stride * i * RGB_SIZE , buffer + width * i * RGB_SIZE , width * RGB_SIZE); } //LOGD("666666666666666666666666666"); // unlock the window buffer and post it to display ANativeWindow_unlockAndPost(pWin); // count number of frames //SaveFrame(pEnv, bitmap, codecCtx->width, codecCtx->height, i); //stop = 1; } } } } //LOGD("44444444444444444444444"); // Free the packet that was allocated by av_read_frame av_free_packet(&packet); //LOGD("5555555555555555555555555"); } void Media::decodeAudioAndPlay(void *pBuffer,int dwBufsize) { AVPacket packet; int frameFinished; LOGD("decodeAudioAndPlay start"); if(NULL == codecCtxAudio) { LOGD("codecCtxAudio not init!"); return; } memset(&packet,0x00,sizeof(AVPacket)); packet.data = (uint8_t*)pBuffer;//这里填入一个指向完整H264数据帧的指针 packet.size = dwBufsize;//这个填入H264数据帧的大小 // Decode audio frame int ret = avcodec_decode_audio4(codecCtxAudio,decodedFrame, &frameFinished, &packet); LOGD("avcodec_decode_audio4, %d , ret %d" , frameFinished, ret); // Did we get a audio frame?

    if(frameFinished && bInit) { size_t unpadded_linesize = decodedFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)decodedFrame->format); writeAudio(decodedFrame->extended_data[0], unpadded_linesize); LOGD("writeAudio"); }else{ LOGD("writeAudio fail!"); } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); LOGD("decodeAudioAndPlay end"); }




    Audio.h 音频做成了单例模式... 没有怎么封装, 使用java的AudioTrack,使用native的话不同版本号Android的so文件不一样所以不考虑了.

    #pragma once
    
    void initAudio(int mhz=44100,bool bMono=false,bool b16Bit=true);
    void writeAudio(void * buffer,int size);
    void releaseAudio();
    

    Audio.cpp

    #include "media/Audio.h"
    #include <jni.h>
    #include "cu.h"
    #include "utils/Log.h"
    #include "utils/sharedptr.h"
    #include "utils/Lock.h"
    #include <vector>
    #include <pthread.h>
    #include <sys/syscall.h>
    #include <sys/linux-syscalls.h>
    
    #define SYS_gettid __NR_gettid
    #define BUFFER_SIZE 1024*20
    
    static bool init = false;
    
    static jbyteArray buffer;
    static jobject audio_track;
    static jint buffer_size;
    static jmethodID method_write;
    
    using std::vector;
    
    struct ElementBuf{sharedptr<jbyte> buf;int size; };
    typedef sharedptr<ElementBuf>  SE;
    static vector<SE> _vector;
    static Mutex mutex(true);
    static  Mutex mutexVistor;
    static bool _stop = true;
    
    void* audioThread(void *params);
    
    SE pop()
    {
    	mutex.lock();
    	synchronized (mutexVistor)
    	{
    		if(!_vector.empty())
    		{
    			vector<SE>::iterator iter =_vector.begin();
    			SE e=*iter;
    			_vector.erase(iter);
    			return e;
    		}
    	}
    	return pop();
    }
    
    void push(SE e)
    {
    	synchronized (mutexVistor)
    	{
    		_vector.push_back(e);
    	}
    	mutex.unlock();
    }
    
    void releaseAudioRes()
    {
    	LOGD("releaseAudioRes start");
    	JNIEnv * pEnv = getEnv();
    	jclass audio_track_cls = pEnv->FindClass("android/media/AudioTrack");
    	// audio.stop();
        //audio.release();
    	jmethodID method_stop =pEnv->GetMethodID(audio_track_cls, "stop",
    					"()V");
    	jmethodID method_release =pEnv->GetMethodID(audio_track_cls, "release",
    					"()V");
    	pEnv->CallVoidMethod(audio_track, method_stop);
    	pEnv->CallVoidMethod(audio_track, method_release);
    
    	pEnv->DeleteGlobalRef(audio_track);
    	audio_track=NULL;
    	LOGD("releaseAudioRes end");
    }
    
    int g_oldMhz = 0;
    int g_oldbMono = false;
    int g_oldb16Bit = true;
    
    void initAudio(int mhz,bool bMono,bool b16Bit)
    {
    	    LOGD("initAudio, %d ,%d, %d",mhz,bMono,b16Bit);
     	    _stop=false;
    		if(init)
    		{
    			if(g_oldMhz!=mhz||g_oldbMono!=bMono||g_oldb16Bit!=b16Bit)
    			{
    				releaseAudioRes();
    			}else{
    				return;
    			}
    		}
    		g_oldMhz=mhz;
    		g_oldbMono=bMono;
    		g_oldb16Bit=b16Bit;
    		JNIEnv * pEnv = getEnv();
    		jclass audio_track_cls = pEnv->FindClass("android/media/AudioTrack");
    		jmethodID min_buff_size_id = pEnv->GetStaticMethodID(audio_track_cls,"getMinBufferSize", "(III)I");
    		buffer_size =pEnv->CallStaticIntMethod(audio_track_cls,min_buff_size_id,
    				mhz, //44100, 22050 and 11025.
    				bMono?2:0x4|0x8, //0x4|0x8,//2,          /*CHANNEL_CONFIGURATION_MONO*/
    					2);         /*ENCODING_PCM_16BIT*/
    		LOGI("buffer_size=%i",buffer_size);
    		buffer =pEnv->NewByteArray(BUFFER_SIZE);//buffer_size/4
    		buffer = (jbyteArray)pEnv->NewGlobalRef(buffer);
    		jmethodID constructor_id =pEnv->GetMethodID(audio_track_cls, "<init>",
    				"(IIIIII)V");
    
    		audio_track =pEnv->NewObject(audio_track_cls,
    				constructor_id,
    				3,            /*AudioManager.STREAM_MUSIC*/
    				mhz, //11025,        /*sampleRateInHz*/ 44100, 22050 and 11025.
    				bMono?

    2:0x4|0x8,//0x4|0x8,//2, /*CHANNEL_CONFIGURATION_MONO*/ b16Bit?2:3, /*ENCODING_PCM_16BIT*/ buffer_size, /*bufferSizeInBytes*/ 1 /*AudioTrack.MODE_STREAM*/ ); audio_track = (jobject)pEnv->NewGlobalRef(audio_track); //setvolume LOGD("setStereoVolume 1"); jmethodID setStereoVolume =pEnv->GetMethodID(audio_track_cls,"setStereoVolume","(FF)I"); pEnv->CallIntMethod(audio_track,setStereoVolume,1.0,1.0); LOGD("setStereoVolume 2"); //play jmethodID method_play =pEnv->GetMethodID(audio_track_cls, "play", "()V"); pEnv->CallVoidMethod(audio_track, method_play); //write method_write =pEnv->GetMethodID(audio_track_cls,"write","([BII)I"); //method_write = (jmethodID)pEnv->NewGlobalRef(method_write); LOGI("initAudio OK, BufferSize/4:%d",buffer_size/4 ); static pthread_t thread=NULL; if(NULL==thread) pthread_create(&thread, NULL, audioThread, NULL); init = true; } void* audioThread(void *params) { LOGW("create thread : %d Audio.cpp audioThread", syscall(SYS_gettid)); AttachCurrentThread(); JNIEnv * env = getEnv(); while(true) { SE e = pop(); if(_stop) continue; int size = e->size; int wirteSize = 0; jbyte * buf= e->buf.get(); while(size > BUFFER_SIZE) { // LOGD("writeAudio , BufferSize/4:%d",BUFFER_SIZE ); env->SetByteArrayRegion(buffer, 0,BUFFER_SIZE, buf + wirteSize); //LOGD("writeAudio , ==========" ); env->CallVoidMethod(audio_track,method_write,buffer,0,BUFFER_SIZE); wirteSize += BUFFER_SIZE; size -= BUFFER_SIZE; } if(size>0) { //LOGD("writeAudio , size:%d",size ); env->SetByteArrayRegion(buffer, 0,size, buf + wirteSize); env->CallVoidMethod(audio_track,method_write,buffer,0,size); } //LOGD("writeAudio , OK! size:%d",e->size ); } DetachCurrentThread(); return NULL; } void writeAudio(void * buf,int size) { sharedptr<jbyte> b(new jbyte[size]); memcpy(b.get(),buf,size); ElementBuf *eb =new ElementBuf(); eb->buf = b; eb->size = size; SE e(eb); push(e); } void releaseAudio() { _stop = true; }



    其他相关代码:

    bool AttachCurrentThread()
    {
    	LOGI("AttachCurrentThread ing");
    	JNIEnv * env;
    	int status = 0;
    	env = getEnv();
    	if(NULL==env){
    		int ret = g_jvm->AttachCurrentThread(&env, NULL);
    		LOGI("AttachCurrentThread ok");
    		return ret>=0;
    	}
    	LOGW("AttachCurrentThread fail, thread is attached");
    	return false;
    }
    void DetachCurrentThread()
    {
    	LOGI("DetachCurrentThread ing");
    	if(NULL!=getEnv())
    		g_jvm->DetachCurrentThread();
    	LOGI("DetachCurrentThread ok");
    }
    JNIEnv * getEnv()
    {
    <span style="white-space:pre">	</span>JNIEnv* env;
    <span style="white-space:pre">	</span>if (g_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
    <span style="white-space:pre">		</span> return NULL;
    <span style="white-space:pre">	</span>}
    <span style="white-space:pre">	</span>return env;
    }


    项目的Android.mk 我这里包括了我使用的libspeex库,没用到的能够不用

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE    := cu
    #LOCAL_SRC_FILES := cu.cpp
    
    FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp) 
    LOCAL_SRC_FILES += $(FILE_LIST:$(LOCAL_PATH)/%=%) 
    
    FILE_LIST := $(wildcard $(LOCAL_PATH)/*.c) 
    LOCAL_SRC_FILES += $(FILE_LIST:$(LOCAL_PATH)/%=%)
    
    FILE_LIST := $(wildcard $(LOCAL_PATH)/*/*.cpp) 
    LOCAL_SRC_FILES += $(FILE_LIST:$(LOCAL_PATH)/%=%)
    
    LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid
    LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libavutil libwsresample libspeex
    
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-add-path,$(LOCAL_PATH))
    $(call import-add-path,$(LOCAL_PATH)/ffmpeg/arm/include)
    $(call import-module, ffmpeg/arm)
    $(call import-module, speex)
    include $(all-subdir-makefiles)

    Application.mk:

    APP_ABI := armeabi
    #APP_ABI := armeabi-v7a
    APP_PLATFORM := android-9
    APP_STL := stlport_static
    APP_CPPFLAGS += -fexceptions
    APP_CFLAGS += -Wno-error=format-security


    临时贴这么多代码出来啦! 

    音频视频同步展示仅仅做了最简单的依据视屏的pts做同步,我发现pts和网上说的不太一样.
    音频使用的是:java 的AudioTrack




    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    129. Sum Root to Leaf Numbers
    113. Path Sum II
    114. Flatten Binary Tree to Linked List
    112. Path Sum
    100. Same Tree
    300. Longest Increasing Subsequence
    72. Edit Distance
    自定义js标签库
    JS 实现Table相同行的单元格自动合并示例代码
    mysql 高版本only_full_group_by 错误
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4614657.html
Copyright © 2011-2022 走看看