美文网首页
mediacodec在native中的使用例子.

mediacodec在native中的使用例子.

作者: 蓝松短视频SDK | 来源:发表于2020-08-25 08:50 被阅读0次

    具体的例子在android的 ndk的sample文件夹下, 这里把底层的例子备份下.

    loop.h

    #include <pthread.h>

    #include <semaphore.h>

    struct loopermessage;

    class looper {

        public:

            looper();

            ~looper();

            void post(int what, void *data, bool flush = false);

            void quit();

            virtual void handle(int what, void *data);

        private:

            void addmsg(loopermessage *msg, bool flush);

            static void* trampoline(void* p);

            void loop();

            loopermessage *head;

            pthread_t worker;

            sem_t headwriteprotect;

            sem_t headdataavailable;

            bool running;

    };


    loop.cpp

    #include "looper.h"

    #include <assert.h>

    #include <jni.h>

    #include <pthread.h>

    #include <stdio.h>

    #include <string.h>

    #include <unistd.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <errno.h>

    #include <limits.h>

    #include <semaphore.h>

    // for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");

    #include <android/log.h>

    #define TAG "NativeCodec-looper"

    #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)

    struct loopermessage;

    typedef struct loopermessage loopermessage;

    struct loopermessage {

        int what;

        void *obj;

        loopermessage *next;

        bool quit;

    };

    void* looper::trampoline(void* p) {

        ((looper*)p)->loop();

        return NULL;

    }

    looper::looper() {

        sem_init(&headdataavailable, 0, 0);

        sem_init(&headwriteprotect, 0, 1);

        pthread_attr_t attr;

        pthread_attr_init(&attr);

        pthread_create(&worker, &attr, trampoline, this);

        running = true;

    }

    looper::~looper() {

        if (running) {

            LOGV("Looper deleted while still running. Some messages will not be processed");

            quit();

        }

    }

    void looper::post(int what, void *data, bool flush) {

        loopermessage *msg = new loopermessage();

        msg->what = what;

        msg->obj = data;

        msg->next = NULL;

        msg->quit = false;

        addmsg(msg, flush);

    }

    void looper::addmsg(loopermessage *msg, bool flush) {

        sem_wait(&headwriteprotect);

        loopermessage *h = head;

        if (flush) {

            while(h) {

                loopermessage *next = h->next;

                delete h;

                h = next;

            }

            h = NULL;

        }

        if (h) {

            while (h->next) {

                h = h->next;

            }

            h->next = msg;

        } else {

            head = msg;

        }

        LOGV("post msg %d", msg->what);

        sem_post(&headwriteprotect);

        sem_post(&headdataavailable);

    }

    void looper::loop() {

        while(true) {

            // wait for available message

            sem_wait(&headdataavailable);

            // get next available message

            sem_wait(&headwriteprotect);

            loopermessage *msg = head;

            if (msg == NULL) {

                LOGV("no msg");

                sem_post(&headwriteprotect);

                continue;

            }

            head = msg->next;

            sem_post(&headwriteprotect);

            if (msg->quit) {

                LOGV("quitting");

                delete msg;

                return;

            }

            LOGV("processing msg %d", msg->what);

            handle(msg->what, msg->obj);

            delete msg;

        }

    }

    void looper::quit() {

        LOGV("quit");

        loopermessage *msg = new loopermessage();

        msg->what = 0;

        msg->obj = NULL;

        msg->next = NULL;

        msg->quit = true;

        addmsg(msg, false);

        void *retval;

        pthread_join(worker, &retval);

        sem_destroy(&headdataavailable);

        sem_destroy(&headwriteprotect);

        running = false;

    }

    void looper::handle(int what, void* obj) {

        LOGV("dropping msg %d %p", what, obj);

    }


    正式例子.

    /* This is a JNI example where we use native methods to play video

    * using the native AMedia* APIs.

    * See the corresponding Java source file located at:

    *

    *  src/com/example/nativecodec/NativeMedia.java

    *

    * In this example we use assert() for "impossible" error conditions,

    * and explicit handling and recovery for more likely error conditions.

    */

    #include <assert.h>

    #include <jni.h>

    #include <stdio.h>

    #include <string.h>

    #include <unistd.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <errno.h>

    #include <limits.h>

    #include "looper.h"

    #include "media/NdkMediaCodec.h"

    #include "media/NdkMediaExtractor.h"

    // for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");

    #include <android/log.h>

    #define TAG "NativeCodec"

    #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)

    // for native window JNI

    #include <android/native_window_jni.h>

    typedef struct {

        int fd;

        ANativeWindow* window;

        AMediaExtractor* ex;

        AMediaCodec *codec;

        int64_t renderstart;

        bool sawInputEOS;

        bool sawOutputEOS;

        bool isPlaying;

        bool renderonce;

    } workerdata;

    workerdata data = {-1, NULL, NULL, NULL, 0, false, false, false, false};

    enum {

        kMsgCodecBuffer,

        kMsgPause,

        kMsgResume,

        kMsgPauseAck,

        kMsgDecodeDone,

        kMsgSeek,

    };

    class mylooper: public looper {

        virtual void handle(int what, void* obj);

    };

    static mylooper *mlooper = NULL;

    int64_t systemnanotime() {

        timespec now;

        clock_gettime(CLOCK_MONOTONIC, &now);

        return now.tv_sec * 1000000000LL + now.tv_nsec;

    }

    void doCodecWork(workerdata *d) {

        ssize_t bufidx = -1;

        if (!d->sawInputEOS) {

            bufidx = AMediaCodec_dequeueInputBuffer(d->codec, 2000);

            LOGV("input buffer %zd", bufidx);

            if (bufidx >= 0) {

                size_t bufsize;

                uint8_t *buf = AMediaCodec_getInputBuffer(d->codec, bufidx, &bufsize);

                ssize_t sampleSize = AMediaExtractor_readSampleData(d->ex, buf, bufsize);

                if (sampleSize < 0) {

                    sampleSize = 0;

                    d->sawInputEOS = true;

                    LOGV("EOS");

                }

                int64_t presentationTimeUs = AMediaExtractor_getSampleTime(d->ex);

                AMediaCodec_queueInputBuffer(d->codec, bufidx, 0, sampleSize, presentationTimeUs,

                        d->sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);

                AMediaExtractor_advance(d->ex);

            }

        }

        if (!d->sawOutputEOS) {

            AMediaCodecBufferInfo info;

            ssize_t status = AMediaCodec_dequeueOutputBuffer(d->codec, &info, 0);

            if (status >= 0) {

                if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {

                    LOGV("output EOS");

                    d->sawOutputEOS = true;

                }

                int64_t presentationNano = info.presentationTimeUs * 1000;

                if (d->renderstart < 0) {

                    d->renderstart = systemnanotime() - presentationNano;

                }

                int64_t delay = (d->renderstart + presentationNano) - systemnanotime();

                if (delay > 0) {

                    usleep(delay / 1000);

                }

                AMediaCodec_releaseOutputBuffer(d->codec, status, info.size != 0);

                if (d->renderonce) {

                    d->renderonce = false;

                    return;

                }

            } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {

                LOGV("output buffers changed");

            } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {

                AMediaFormat *format = NULL;

                format = AMediaCodec_getOutputFormat(d->codec);

                LOGV("format changed to: %s", AMediaFormat_toString(format));

                AMediaFormat_delete(format);

            } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {

                LOGV("no output buffer right now");

            } else {

                LOGV("unexpected info code: %zd", status);

            }

        }

        if (!d->sawInputEOS || !d->sawOutputEOS) {

            mlooper->post(kMsgCodecBuffer, d);

        }

    }

    void mylooper::handle(int what, void* obj) {

        switch (what) {

            case kMsgCodecBuffer:

                doCodecWork((workerdata*)obj);

                break;

            case kMsgDecodeDone:

            {

                workerdata *d = (workerdata*)obj;

                AMediaCodec_stop(d->codec);

                AMediaCodec_delete(d->codec);

                AMediaExtractor_delete(d->ex);

                d->sawInputEOS = true;

                d->sawOutputEOS = true;

            }

            break;

            case kMsgSeek:

            {

                workerdata *d = (workerdata*)obj;

                AMediaExtractor_seekTo(d->ex, 0, AMEDIAEXTRACTOR_SEEK_NEXT_SYNC);

                AMediaCodec_flush(d->codec);

                d->renderstart = -1;

                d->sawInputEOS = false;

                d->sawOutputEOS = false;

                if (!d->isPlaying) {

                    d->renderonce = true;

                    post(kMsgCodecBuffer, d);

                }

                LOGV("seeked");

            }

            break;

            case kMsgPause:

            {

                workerdata *d = (workerdata*)obj;

                if (d->isPlaying) {

                    // flush all outstanding codecbuffer messages with a no-op message

                    d->isPlaying = false;

                    post(kMsgPauseAck, NULL, true);

                }

            }

            break;

            case kMsgResume:

            {

                workerdata *d = (workerdata*)obj;

                if (!d->isPlaying) {

                    d->renderstart = -1;

                    d->isPlaying = true;

                    post(kMsgCodecBuffer, d);

                }

            }

            break;

        }

    }

    extern "C" {

    jboolean Java_com_example_nativecodec_NativeCodec_createStreamingMediaPlayer(JNIEnv* env,

            jclass clazz, jstring filename)

    {

        LOGV("@@@ create");

        // convert Java string to UTF-8

        const char *utf8 = env->GetStringUTFChars(filename, NULL);

        LOGV("opening %s", utf8);

        int fd = open(utf8, O_RDONLY);

        env->ReleaseStringUTFChars(filename, utf8);

        if (fd < 0) {

            LOGV("failed: %d (%s)", fd, strerror(errno));

            return JNI_FALSE;

        }

        data.fd = fd;

        workerdata *d = &data;

        AMediaExtractor *ex = AMediaExtractor_new();

        media_status_t err = AMediaExtractor_setDataSourceFd(ex, d->fd, 0 , LONG_MAX);

        close(d->fd);

        if (err != AMEDIA_OK) {

            LOGV("setDataSource error: %d", err);

            return JNI_FALSE;

        }

        int numtracks = AMediaExtractor_getTrackCount(ex);

        AMediaCodec *codec = NULL;

        LOGV("input has %d tracks", numtracks);

        for (int i = 0; i < numtracks; i++) {

            AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);

            const char *s = AMediaFormat_toString(format);

            LOGV("track %d format: %s", i, s);

            const char *mime;

            if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {

                LOGV("no mime type");

                return JNI_FALSE;

            } else if (!strncmp(mime, "video/", 6)) {

                // Omitting most error handling for clarity.

                // Production code should check for errors.

                AMediaExtractor_selectTrack(ex, i);

                codec = AMediaCodec_createDecoderByType(mime);

                AMediaCodec_configure(codec, format, d->window, NULL, 0);

                d->ex = ex;

                d->codec = codec;

                d->renderstart = -1;

                d->sawInputEOS = false;

                d->sawOutputEOS = false;

                d->isPlaying = false;

                d->renderonce = true;

                AMediaCodec_start(codec);

            }

            AMediaFormat_delete(format);

        }

        mlooper = new mylooper();

        mlooper->post(kMsgCodecBuffer, d);

        return JNI_TRUE;

    }

    // set the playing state for the streaming media player

    void Java_com_example_nativecodec_NativeCodec_setPlayingStreamingMediaPlayer(JNIEnv* env,

            jclass clazz, jboolean isPlaying)

    {

        LOGV("@@@ playpause: %d", isPlaying);

        if (mlooper) {

            if (isPlaying) {

                mlooper->post(kMsgResume, &data);

            } else {

                mlooper->post(kMsgPause, &data);

            }

        }

    }

    // shut down the native media system

    void Java_com_example_nativecodec_NativeCodec_shutdown(JNIEnv* env, jclass clazz)

    {

        LOGV("@@@ shutdown");

        if (mlooper) {

            mlooper->post(kMsgDecodeDone, &data, true /* flush */);

            mlooper->quit();

            delete mlooper;

            mlooper = NULL;

        }

        if (data.window) {

            ANativeWindow_release(data.window);

            data.window = NULL;

        }

    }

    // set the surface

    void Java_com_example_nativecodec_NativeCodec_setSurface(JNIEnv *env, jclass clazz, jobject surface)

    {

        // obtain a native window from a Java surface

        if (data.window) {

            ANativeWindow_release(data.window);

            data.window = NULL;

        }

        data.window = ANativeWindow_fromSurface(env, surface);

        LOGV("@@@ setsurface %p", data.window);

    }

    // rewind the streaming media player

    void Java_com_example_nativecodec_NativeCodec_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz)

    {

        LOGV("@@@ rewind");

        mlooper->post(kMsgSeek, &data);

    }

    }

    相关文章

      网友评论

          本文标题:mediacodec在native中的使用例子.

          本文链接:https://www.haomeiwen.com/subject/qfdajktx.html