首页 > 编程 > C++ > 正文

Android c++屏幕实时录制

2019-11-09 14:43:06
字体:
来源:转载
供稿:网友

功能:沿用Android cmds下的screenrecord代码,整理成可以供native程序调用的一个可以获取实时screen h264码流的一个库,分享一下!!

头文件screenrecord.h

#include <stdint.h>class IScreenRecordCallback{public:	virtual ~IScreenRecordCallback(){}	virtual void onData(void* pData, size_t size) = 0;	//virtual void onCodecConfig(void* pData, size_t size) = 0;};class ScreenRecordImp;class ScreenRecord{public:	ScreenRecord();	~ScreenRecord();	int start(IScreenRecordCallback* callback);	void stop();PRivate:	ScreenRecordImp* m_pImp;};实现文件:screenrecord.cpp

#include "screenrecord.h"#include <iostream>#include <utils/Log.h>#include <binder/ipCThreadState.h>#include <utils/Errors.h>#include <utils/Thread.h>#include <utils/Timers.h>#include <media/openmax/OMX_IVCommon.h>#include <gui/Surface.h>#include <gui/SurfaceComposerClient.h>#include <gui/ISurfaceComposer.h>#include <ui/DisplayInfo.h>#include <media/stagefright/foundation/ABuffer.h>#include <media/stagefright/foundation/ADebug.h>#include <media/stagefright/foundation/AMessage.h>#include <media/stagefright/MediaCodec.h>#include <media/stagefright/MediaErrors.h>#include <media/ICrypto.h>#include <utils/Errors.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <stdio.h>#include <fcntl.h>#include <signal.h>#include <getopt.h>#include <sys/wait.h>using namespace android;using namespace std;#define LOG_TAG "LibScreenRecord"//#define LOG_NDEBUG 0static const uint32_t kFallbackWidth = 1280;        // 720pstatic const uint32_t kFallbackHeight = 720;class ScreenRecordImp{public:	ScreenRecordImp();	int start(IScreenRecordCallback* callback);	void stop();private:	bool isDeviceRotated(int orientation);	status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,        sp<IGraphicBufferProducer>* pBufferProducer);	status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,        const sp<IGraphicBufferProducer>& bufferProducer,        sp<IBinder>* pDisplayHandle);	status_t setDisplayProjection(const sp<IBinder>& dpy,        const DisplayInfo& mainDpyInfo);	status_t runEncoder(const sp<MediaCodec>& encoder, IScreenRecordCallback* callback, sp<IBinder>& mainDpy, sp<IBinder>& dpy, uint8_t orientation);private:	bool mRotate;                // rotate 90 degrees	//bool mSizeSpecified;         // was size explicitly requested?	uint32_t mVideoWidth;            // default width+height	uint32_t mVideoHeight;	uint32_t mBitRate;	//uint32_t mTimeLimitSec;	bool mStopRequested;};ScreenRecord::ScreenRecord(){	m_pImp = new ScreenRecordImp;}ScreenRecord::~ScreenRecord(){	stop();	delete m_pImp;	m_pImp = NULL;}int ScreenRecord::start(IScreenRecordCallback* callback){	return m_pImp->start(callback);}void ScreenRecord::stop(){	m_pImp->stop();}	ScreenRecordImp::ScreenRecordImp():mRotate(false),							mVideoWidth(0),							mVideoHeight(0),							mBitRate(4000000),         // 4Mbp							mStopRequested(true){}bool ScreenRecordImp::isDeviceRotated(int orientation) {    return orientation != DISPLAY_ORIENTATION_0 &&            orientation != DISPLAY_ORIENTATION_180;}status_t ScreenRecordImp::setDisplayProjection(const sp<IBinder>& dpy,        const DisplayInfo& mainDpyInfo) {    status_t err;    // Set the region of the layer stack we're interested in, which in our    // case is "all of it".  If the app is rotated (so that the width of the    // app is based on the height of the display), reverse width/height.    bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation);    uint32_t sourceWidth, sourceHeight;    if (!deviceRotated) {        sourceWidth = mainDpyInfo.w;        sourceHeight = mainDpyInfo.h;    } else {        ALOGV("using rotated width/height");        sourceHeight = mainDpyInfo.w;        sourceWidth = mainDpyInfo.h;    }    Rect layerStackRect(sourceWidth, sourceHeight);    // We need to preserve the aspect ratio of the display.    float displayAspect = (float) sourceHeight / (float) sourceWidth;    // Set the way we map the output onto the display surface (which will    // be e.g. 1280x720 for a 720p video).  The rect is interpreted    // post-rotation, so if the display is rotated 90 degrees we need to    // "pre-rotate" it by flipping width/height, so that the orientation    // adjustment changes it back.    //    // We might want to encode a portrait display as landscape to use more    // of the screen real estate.  (If players respect a 90-degree rotation    // hint, we can essentially get a 720x1280 video instead of 1280x720.)    // In that case, we swap the configured video width/height and then    // supply a rotation value to the display projection.    uint32_t videoWidth, videoHeight;    uint32_t outWidth, outHeight;    if (!mRotate) {        videoWidth = mVideoWidth;        videoHeight = mVideoHeight;    } else {        videoWidth = mVideoHeight;        videoHeight = mVideoWidth;    }    if (videoHeight > (uint32_t)(videoWidth * displayAspect)) {        // limited by narrow width; reduce height        outWidth = videoWidth;        outHeight = (uint32_t)(videoWidth * displayAspect);    } else {        // limited by short height; restrict width        outHeight = videoHeight;        outWidth = (uint32_t)(videoHeight / displayAspect);    }    uint32_t offX, offY;    offX = (videoWidth - outWidth) / 2;    offY = (videoHeight - outHeight) / 2;    Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);    /*if (gVerbose) {        if (mRotate) {            printf("Rotated content area is %ux%u at offset x=%d y=%d/n",                    outHeight, outWidth, offY, offX);        } else {            printf("Content area is %ux%u at offset x=%d y=%d/n",                    outWidth, outHeight, offX, offY);        }    }*/    SurfaceComposerClient::setDisplayProjection(dpy,            mRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,            layerStackRect, displayRect);    return NO_ERROR;}status_t ScreenRecordImp::prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,        sp<IGraphicBufferProducer>* pBufferProducer){            cout<<"ScreenRecordImp prepareEncoder enter"<<endl;    status_t err;    sp<AMessage> format = new AMessage;    format->setInt32("width", mVideoWidth);    format->setInt32("height", mVideoHeight);    format->setString("mime", "video/avc");    format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);    format->setInt32("bitrate", mBitRate);    format->setFloat("frame-rate", displayFps);    format->setInt32("i-frame-interval", 10);    sp<ALooper> looper = new ALooper;    looper->setName("libscreenrecord_looper");    looper->start();    ALOGV("Creating codec");	cout<<"ScreenRecordImp prepareEncoder Creating codec"<<endl;    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);    if (codec == NULL) {        fprintf(stderr, "ERROR: unable to create video/avc codec instance/n");        return UNKNOWN_ERROR;    }    err = codec->configure(format, NULL, NULL,            MediaCodec::CONFIGURE_FLAG_ENCODE);    if (err != NO_ERROR) {        codec->release();        codec.clear();        fprintf(stderr, "ERROR: unable to configure codec (err=%d)/n", err);        return err;    }    ALOGV("Creating buffer producer");	cout<<"ScreenRecordImp prepareEncoder Creating buffer producer"<<endl;    sp<IGraphicBufferProducer> bufferProducer;    err = codec->createInputSurface(&bufferProducer);    if (err != NO_ERROR) {        codec->release();        codec.clear();        fprintf(stderr,            "ERROR: unable to create encoder input surface (err=%d)/n", err);        return err;    }    ALOGV("Starting codec");	cout<<"ScreenRecordImp prepareEncoder Starting codec"<<endl;    err = codec->start();    if (err != NO_ERROR) {        codec->release();        codec.clear();        fprintf(stderr, "ERROR: unable to start codec (err=%d)/n", err);        return err;    }    ALOGV("Codec prepared");	cout<<"ScreenRecordImp prepareEncoder Codec prepared"<<endl;    *pCodec = codec;    *pBufferProducer = bufferProducer;    return 0;}status_t ScreenRecordImp::prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,        const sp<IGraphicBufferProducer>& bufferProducer,        sp<IBinder>* pDisplayHandle) {    cout<<"ScreenRecordImp prepareVirtualDisplay enter"<<endl;	    status_t err;    // Set the region of the layer stack we're interested in, which in our    // case is "all of it".  If the app is rotated (so that the width of the    // app is based on the height of the display), reverse width/height.    bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation);    uint32_t sourceWidth, sourceHeight;    if (!deviceRotated) {        sourceWidth = mainDpyInfo.w;        sourceHeight = mainDpyInfo.h;    } else {        ALOGV("using rotated width/height");        sourceHeight = mainDpyInfo.w;        sourceWidth = mainDpyInfo.h;    }    Rect layerStackRect(sourceWidth, sourceHeight);    // We need to preserve the aspect ratio of the display.    float displayAspect = (float) sourceHeight / (float) sourceWidth;    // Set the way we map the output onto the display surface (which will    // be e.g. 1280x720 for a 720p video).  The rect is interpreted    // post-rotation, so if the display is rotated 90 degrees we need to    // "pre-rotate" it by flipping width/height, so that the orientation    // adjustment changes it back.    //    // We might want to encode a portrait display as landscape to use more    // of the screen real estate.  (If players respect a 90-degree rotation    // hint, we can essentially get a 720x1280 video instead of 1280x720.)    // In that case, we swap the configured video width/height and then    // supply a rotation value to the display projection.    uint32_t videoWidth, videoHeight;    uint32_t outWidth, outHeight;    if (!mRotate) {        videoWidth = mVideoWidth;        videoHeight = mVideoHeight;    } else {        videoWidth = mVideoHeight;        videoHeight = mVideoWidth;    }    if (videoHeight > (uint32_t)(videoWidth * displayAspect)) {        // limited by narrow width; reduce height        outWidth = videoWidth;        outHeight = (uint32_t)(videoWidth * displayAspect);    } else {        // limited by short height; restrict width        outHeight = videoHeight;        outWidth = (uint32_t)(videoHeight / displayAspect);    }    uint32_t offX, offY;    offX = (videoWidth - outWidth) / 2;    offY = (videoHeight - outHeight) / 2;    Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);	cout<<"ScreenRecordImp prepareVirtualDisplay offX:"<<offX<<" offY:"<<offY<<" outWidth:"<<outWidth<<" outHeight"<<outHeight<<endl;	     sp<IBinder> dpy = SurfaceComposerClient::createDisplay(            String8("LibScreenRecorder"), false /* secure */);    SurfaceComposerClient::openGlobalTransaction();    SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);    SurfaceComposerClient::setDisplayProjection(dpy,            mRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,            layerStackRect, displayRect);    SurfaceComposerClient::setDisplayLayerStack(dpy, 0);    // default stack    SurfaceComposerClient::closeGlobalTransaction();    *pDisplayHandle = dpy;    return NO_ERROR;}status_t ScreenRecordImp::runEncoder(const sp<MediaCodec>& encoder, IScreenRecordCallback* callback, sp<IBinder>& mainDpy, sp<IBinder>& virtualDpy, 	uint8_t orientation){	cout<<"ScreenRecordImp runEncoder enter"<<endl;    static int kTimeout = 250000;   // be responsive on signal    status_t err;    ssize_t trackIdx = -1;    uint32_t debugNumFrames = 0;    int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);    //int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(180);    Vector<sp<ABuffer> > buffers;    err = encoder->getOutputBuffers(&buffers);    if (err != NO_ERROR) {        fprintf(stderr, "Unable to get output buffers (err=%d)/n", err);        return err;    }	DisplayInfo mainDpyInfo;	cout<<"ScreenRecordImp runEncoder start looping"<<endl;    // This is set by the signal handler.    mStopRequested = false;    // Run until we're signaled.    while (!mStopRequested) {        size_t bufIndex, offset, size;        int64_t ptsUsec;        uint32_t flags;        /*if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {            break;        }*/        ALOGV("Calling dequeueOutputBuffer");        err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,                &flags, kTimeout);        ALOGV("dequeueOutputBuffer returned %d", err);		cout<<"ScreenRecordImp runEncoder dequeueOutputBuffer returned:"<<err<<endl;        switch (err) {        case NO_ERROR:            // got a buffer            if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0)			{                // ignore this -- we passed the CSD into MediaMuxer when                // we got the format change notification                                ALOGV("Got codec config buffer (%u bytes); ignoring", size);				cout<<"ScreenRecordImp runEncoder Got codec config buffer bytes:"<<size<<endl;                //size = 0;            }            if (size != 0)			{                ALOGV("Got data in buffer %d, size=%d, pts=%lld",                        bufIndex, size, ptsUsec);                //CHECK(trackIdx != -1);                // If the virtual display isn't providing us with timestamps,                // use the current time.                if (ptsUsec == 0) {                    ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000;                }                // The MediaMuxer docs are unclear, but it appears that we                // need to pass either the full set of BufferInfo flags, or                // (flags & BUFFER_FLAG_SYNCFRAME).                /*err = muxer->writeSampleData(buffers[bufIndex], trackIdx,                        ptsUsec, flags);                if (err != NO_ERROR) {                    fprintf(stderr, "Failed writing data to muxer (err=%d)/n",                            err);                    return err;                }*/                { // scope                    //ATRACE_NAME("orientation");                    // Check orientation, update if it has changed.                    //                    // Polling for changes is inefficient and wrong, but the                    // useful stuff is hard to get at without a Dalvik VM.                    err = SurfaceComposerClient::getDisplayInfo(mainDpy,                            &mainDpyInfo);                    if (err != NO_ERROR) {                        ALOGW("getDisplayInfo(main) failed: %d", err);                    } else if (orientation != mainDpyInfo.orientation) {                        ALOGD("orientation changed, now %d", mainDpyInfo.orientation);                        SurfaceComposerClient::openGlobalTransaction();                        setDisplayProjection(virtualDpy, mainDpyInfo);                        SurfaceComposerClient::closeGlobalTransaction();                        orientation = mainDpyInfo.orientation;                    }                }				                debugNumFrames++;				ALOGV("Got codec NumFrames:%d", debugNumFrames);				cout<<"ScreenRecordImp runEncoder Got codec NumFrames:"<<debugNumFrames<<endl;				callback->onData(buffers[bufIndex]->data(), size);				//if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) == 0)				//{                    //fflush(rawFp);                    //callback->FFlush();                //}				            }			cout<<"ScreenRecordImp runEncoder releaSEOutputBuffer bufIndex:"<<bufIndex<<endl;            err = encoder->releaseOutputBuffer(bufIndex);            if (err != NO_ERROR) {                fprintf(stderr, "Unable to release output buffer (err=%d)/n",                        err);                return err;            }			cout<<"ScreenRecordImp runEncoder releaseOutputBuffer done"<<endl;            if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) {                // Not expecting EOS from SurfaceFlinger.  Go with it.                ALOGD("Received end-of-stream");				cout<<"ScreenRecordImp runEncoder Received end-of-stream"<<endl;                mStopRequested = true;            }            break;        case -EAGAIN:                       // INFO_TRY_AGAIN_LATER            ALOGV("Got -EAGAIN, looping");			cout<<"ScreenRecordImp runEncoder Got -EAGAIN, looping"<<endl;            break;        case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED -1012            {                // format includes CSD, which we must provide to muxer                //sp<AMessage> newFormat;                //encoder->getOutputFormat(&newFormat);                //callback->formatChanged(newFormat);                ALOGV("Encoder format changed");            }            break;        case INFO_OUTPUT_BUFFERS_CHANGED:   // INFO_OUTPUT_BUFFERS_CHANGED -1014            // not expected for an encoder; handle it anyway            ALOGV("Encoder buffers changed");			cout<<"ScreenRecordImp runEncoder Encoder buffers changed"<<endl;            err = encoder->getOutputBuffers(&buffers);            if (err != NO_ERROR) {                fprintf(stderr,                        "Unable to get new output buffers (err=%d)/n", err);                return err;            }            break;        case INVALID_OperaTION:            fprintf(stderr, "Request for encoder buffer failed/n");            return err;        default:            fprintf(stderr,                    "Got weird result %d from dequeueOutputBuffer/n", err);            return err;        }    }    ALOGV("Encoder stopping (req=%d)", mStopRequested);	cout<<"ScreenRecordImp runEncoder Encoder stopping mStopRequested:"<<mStopRequested<<endl;    return NO_ERROR;}int ScreenRecordImp::start(IScreenRecordCallback* callback){	cout<<"ScreenRecordImp start enter"<<endl;	status_t err;	// Start Binder thread pool.  MediaCodec needs to be able to receive    // messages from mediaserver.	sp<ProcessState> self = ProcessState::self();    self->startThreadPool();	cout<<"ScreenRecordImp startThreadPool"<<endl;		// Get main display parameters.    sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(            ISurfaceComposer::eDisplayIdMain);    DisplayInfo mainDpyInfo;    err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);    if (err != NO_ERROR) {        fprintf(stderr, "ERROR: unable to get display characteristics/n");        return err;    }		bool rotated = isDeviceRotated(mainDpyInfo.orientation);    if (mVideoWidth == 0) {        mVideoWidth = rotated ? mainDpyInfo.h : mainDpyInfo.w;    }    if (mVideoHeight == 0) {        mVideoHeight = rotated ? mainDpyInfo.w : mainDpyInfo.h;    }	cout<<"ScreenRecordImp mVideoWidth:"<<mVideoWidth<<" mVideoHeight:"<<mVideoHeight<<endl;		// Configure and start the encoder.    sp<MediaCodec> encoder;    sp<IGraphicBufferProducer> bufferProducer;	err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);	if (err != NO_ERROR) {        // fallback is defined for landscape; swap if we're in portrait        bool needSwap = mVideoWidth < mVideoHeight;        uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;        uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;        if (mVideoWidth != newWidth && mVideoHeight != newHeight) {            ALOGV("Retrying with 720p");            fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d/n",                    mVideoWidth, mVideoHeight, newWidth, newHeight);            mVideoWidth = newWidth;            mVideoHeight = newHeight;            err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);        }    }	if (err != NO_ERROR) {        return err;    }	sp<IBinder> dpy;    err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);    if (err != NO_ERROR) {        encoder->release();        encoder.clear();        return err;    }	 // Main encoder loop.		     err = runEncoder(encoder,callback, mainDpy, dpy, mainDpyInfo.orientation);    if (err != NO_ERROR) {        encoder->release();        encoder.clear();        return err;    }	bufferProducer = NULL;    SurfaceComposerClient::destroyDisplay(dpy);    encoder->stop();    //muxer->stop();    encoder->release();		return 0;}void ScreenRecordImp::stop(){	mStopRequested = true;}

用法很简单

class ScreenRecordCallback:public IScreenRecordCallback{public:	virtual ~ScreenRecordCallback(){}	virtual void onData(void* pData, size_t size)	{	}	};int main(int arg, char** arv){	ScreenRecordCallback callback;	ScreenRecord recorder	recorder.start(&callback);	return 0;}抓到屏幕码流后ScreenRecordCallback的onData会被回调,收到的数据存下来就是h264码流

再贴下mikefile吧

LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES := /	screenrecord.cpp /	LOCAL_SHARED_LIBRARIES := /	libstagefright libmedia libutils libbinder libstagefright_foundation /	libjpeg libgui libcutils liblog libEGL libGLESv2	LOCAL_C_INCLUDES := /	frameworks/av/media/libstagefright /	frameworks/av/media/libstagefright/include /	$(TOP)/frameworks/native/include/media/openmax /	external/jpeg	LOCAL_CFLAGS += -Wno-multicharLOCAL_MODULE_TAGS := optionalLOCAL_MODULE:= libscreenrecordinclude $(BUILD_SHARED_LIBRARY)

后续继续实现Android推送码流到PC机,PC机能够解码显示。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选