适用于Android的简单FFMpeg播放器

时间:2016-10-23 12:10:21

标签: android c++ video ffmpeg

我有一个AVFrame(AVPicture)输出到ANativeWindow的问题。我写了simpe测试代码:

void *Player::readThread(void * reserved) {
    ALOGD("Read thread started!");
    VideoState *state =  (VideoState *) reserved;

    int err = 0;
    int ret;
    int i;
    AVFormatContext *formatContext = NULL;
    AVCodecContext *codecContext = NULL;
    AVCodecParameters *codecParams = NULL;
    AVCodecID codecID = AV_CODEC_ID_NONE;
    AVCodec *decoder = NULL;
    AVFrame *frame = NULL;
    AVFrame *frameRGBA = NULL;
    AVPacket packet;
    struct SwsContext *img2RGBAContext;
    ANativeWindow_Buffer windowBuffer;
    uint8_t *RGBABuffer = NULL;
    int RGBABufferSize = 0;
    int got = 0;
    int windowWidth = 640;
    int windowHeight = 480;

    const char *url = state->url.c_str();
    if (url == NULL || strlen(url) <= 0) {
        err = ERROR_UNKNOWN_URI;
        goto exit;
    }
    ALOGD("URL to play: %s", url);

    state->isPlaying = true;

    formatContext = avformat_alloc_context();
    if (formatContext == NULL) {
        err = ERROR_OUT_OF_MEMORY;
        goto exit;
    }
    ALOGD("formatContext allocated");

    frame = av_frame_alloc();
    if (frame == NULL) {
        err = ERROR_OUT_OF_MEMORY;
        goto exit;
    }
    ALOGD("frame allocated");

    frameRGBA = av_frame_alloc();
    if (frameRGBA == NULL) {
        err = ERROR_OUT_OF_MEMORY;
        goto exit;
    }
    ALOGD("frameRGBA allocated");

    ret = avformat_open_input(&formatContext, url, NULL, NULL);
    if (ret != 0) {
        err = ERROR_CAN_NOT_OPEN_URI;
        goto exit;
    }
    ALOGD("formatContext opened");

    ret = avformat_find_stream_info(formatContext, NULL);
    if (ret != 0) {
        err = ERROR_CAN_NOT_FIND_STREAM_INFO;
        goto exit;
    }
    ALOGD("file info found");

    for (i = 0; i < formatContext->nb_streams; i++) {
        AVStream *stream = formatContext->streams[i];
        AVCodecParameters *codecParams = stream->codecpar;
        AVCodecID codecID = codecParams->codec_id;
        AVMediaType type = codecParams->codec_type;
        const char *codecName = avcodec_get_name(codecID);
        switch (type) {
            case AVMEDIA_TYPE_AUDIO:
                ALOGD("Stream [%d]: type=AUDIO codecName=%s",i,codecName);
                break;
            case AVMEDIA_TYPE_VIDEO:
                ALOGD("Stream [%d]: type=VIDEO codecName=%s",i,codecName);
                if (state->video_stream == -1) {
                    state->video_stream = i;
                }
                break;
            case AVMEDIA_TYPE_SUBTITLE:
                ALOGD("Stream [%d]: type=SUBTITLE codecName=%s",i,codecName);
                break;
            default:
                ALOGD("Stream [%d]: type=UNKNOWN codecName=%s",i,codecName);
                break;
        }
    }

    if (state->video_stream == -1) {
        err = ERROR_CAN_NOT_FIND_ANY_STREAM;
        goto exit;
    }
    ALOGD("Video stream index: %d",state->video_stream);

    codecParams = formatContext->streams[state->video_stream]->codecpar;
    codecID = codecParams->codec_id;
    if (codecID == AV_CODEC_ID_NONE) {
        err = ERROR_UNKNOWN_CODEC;
        goto exit;
    }
    ALOGD("Codec found");

    decoder = avcodec_find_decoder(codecID);
    if (decoder == NULL) {
        err = ERROR_CAN_NOT_FIND_DECODER;
        goto exit;
    }
    ALOGD("Decoder found");

    codecContext = avcodec_alloc_context3(decoder);
    if (codecContext == NULL) {
        err = ERROR_OUT_OF_MEMORY;
        goto exit;
    }
    ALOGD("codecContext created");

    ret = avcodec_parameters_to_context(codecContext, codecParams);
    if (ret < 0) {
        err = ERROR_CAN_NOT_START_DECODER;
        goto exit;
    }
    ALOGD("codecContext params was set");

    ret = avcodec_open2(codecContext, decoder, NULL);
    if (ret != 0) {
        err = ERROR_CAN_NOT_START_DECODER;
        goto exit;
    }
    ALOGD("Decoder opened");

    if (state->window != NULL) {
        ANativeWindow_setBuffersGeometry(state->window, codecParams->width, codecParams->height, WINDOW_FORMAT_RGB_565);
        ALOGD("Window geometry changed");
    }

    if (codecParams->width>0 && codecParams->height>0) {
        ALOGD("Video width: %d\nVideo height: %d",codecParams->width, codecParams->height);
        img2RGBAContext = sws_getCachedContext(
            NULL,
            codecParams->width,
            codecParams->height,
            (AVPixelFormat)codecParams->format,
            codecParams->width,
            codecParams->height,
            AV_PIX_FMT_RGB565,
            SWS_BICUBIC,
            NULL,
            NULL,
            NULL);
        if (img2RGBAContext == NULL) {
            err = ERROR_OUT_OF_MEMORY;
            goto exit;
        }
    } else {
        err = ERROR_CAN_NOT_START_DECODER;
        goto exit;
    }
    ALOGD("img2RGBAContext created");

    RGBABufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB565, codecParams->width, codecParams->height, 1);
    RGBABuffer = (uint8_t *)malloc(RGBABufferSize*sizeof(uint8_t));
    if (RGBABuffer == NULL) {
        err = ERROR_OUT_OF_MEMORY;
        goto exit;
    }
    ALOGD("frameRGBABuffer size %d bytes",RGBABufferSize);

    ret = av_image_alloc(frameRGBA->data, frameRGBA->linesize, codecParams->width, codecParams->height, AV_PIX_FMT_RGB565, 1);
    if (ret < 0) {
        err = ERROR_OUT_OF_MEMORY;
        goto exit;
    }

    while (av_read_frame(formatContext, &packet) >= 0 && state->isPlaying) {
        if (packet.stream_index != state->video_stream) {
            ALOGD("Packet is not a video packet. Discard.");
            av_packet_unref(&packet);
            continue;
        }
        ret = avcodec_send_packet(codecContext, &packet);
        if (ret != 0) {
            ALOGE("Can not send packet to decode");
            av_packet_unref(&packet);
            continue;
        }
        ret = avcodec_receive_frame(codecContext, frame);
        if (ret != 0) {
            ALOGE("Can not receive decoded frame yet");
            av_packet_unref(&packet);
            continue;
        }
        ALOGD("Converting image to RGB565...");
        sws_scale(img2RGBAContext, frame->data, frame->linesize, 0, codecParams->height, frameRGBA->data, frameRGBA->linesize);
        ALOGD("Image converted to RGB565");
        av_image_copy_to_buffer(RGBABuffer,
            RGBABufferSize,
            frameRGBA->data,
            frameRGBA->linesize,
            AV_PIX_FMT_RGB565,
            codecParams->width,
            codecParams->height,
            1);
        ALOGD("Image wrote into frameRGBABuffer");
        if (ANativeWindow_lock(state->window, &windowBuffer, NULL) == 0) {
            ALOGD("Writing %d bytes to windowBuffer", RGBABufferSize);
            memcpy(windowBuffer.bits, RGBABuffer, RGBABufferSize);
            ANativeWindow_unlockAndPost(state->window);
            ALOGD("Image displayed");
        } else {
            ALOGE("Can not display frame");
        }
        av_packet_unref(&packet);
    }

    exit:
    ALOGD("Releasing resources...");
    if (err!=0) {
        state->isPlaying = false;
        #if !LOG_NDEBUG
            switch (err) {
                case  ERROR_OUT_OF_MEMORY:
                    ALOGE("Out of memory!");
                    break;
                case  ERROR_CAN_NOT_OPEN_URI:
                    ALOGE("Can not open URI: %s", url);
                    break;
                case  ERROR_UNKNOWN_URI:
                    ALOGE("Unknown URI to open!");
                    break;
                default:
                    ALOGE("Unknown error");
                    break;
            }
        #endif
        // TODO: send error to listener
    }
    sws_freeContext(img2RGBAContext);
    free(RGBABuffer);
    av_free(frame);
    av_freep(&frameRGBA->data[0]);
    av_packet_unref(&packet);
    avcodec_close(codecContext);
    avformat_close_input(&formatContext);
    avformat_free_context(formatContext);
    ALOGD("Read thread closed!");
}

我遇到了一些视频中的下一个问题: enter image description here 例如,此视频为我提供了下一个日志:

10-23 14:53:42.212 26970-4527/com.don.ffmpegplayer D/Player: Read thread started!
10-23 14:53:42.212 26970-4527/com.don.ffmpegplayer D/Player: URL to play: http://www.ex.ua/load/280797285
10-23 14:53:42.212 26970-4527/com.don.ffmpegplayer D/Player: formatContext allocated
10-23 14:53:42.212 26970-4527/com.don.ffmpegplayer D/Player: frame allocated
10-23 14:53:42.212 26970-4527/com.don.ffmpegplayer D/Player: frameRGBA allocated
10-23 14:53:42.846 26970-4527/com.don.ffmpegplayer D/Player: formatContext opened
10-23 14:53:42.879 26970-4527/com.don.ffmpegplayer D/Player: file info found
10-23 14:53:42.879 26970-4527/com.don.ffmpegplayer D/Player: Stream [0]: type=VIDEO codecName=h264
10-23 14:53:42.879 26970-4527/com.don.ffmpegplayer D/Player: Stream [1]: type=AUDIO codecName=ac3
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Stream [2]: type=AUDIO codecName=ac3
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Stream [3]: type=AUDIO codecName=ac3
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Stream [4]: type=SUBTITLE codecName=subrip
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Video stream index: 0
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Codec found
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Decoder found
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: codecContext created
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: codecContext params was set
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Decoder opened
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Window geometry changed
10-23 14:53:42.880 26970-4527/com.don.ffmpegplayer D/Player: Video width: 1024
                                                             Video height: 424
10-23 14:53:42.882 26970-4527/com.don.ffmpegplayer D/Player: img2RGBAContext created
10-23 14:53:42.882 26970-4527/com.don.ffmpegplayer D/Player: frameRGBABuffer size 868352 bytes
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer E/Player: Can not receive decoded frame yet
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.889 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.890 26970-4527/com.don.ffmpegplayer D/Player: Packet is not a video packet. Discard.
10-23 14:53:42.899 26970-4527/com.don.ffmpegplayer E/Player: Can not receive decoded frame yet
10-23 14:53:42.905 26970-4527/com.don.ffmpegplayer D/Player: Converting image to RGB565...
10-23 14:53:42.918 26970-4527/com.don.ffmpegplayer D/Player: Image converted to RGB565
10-23 14:53:42.919 26970-4527/com.don.ffmpegplayer D/Player: Image wrote into frameRGBABuffer
10-23 14:53:42.920 26970-4527/com.don.ffmpegplayer D/Player: Writing 868352 bytes to windowBuffer
10-23 14:53:42.921 26970-4527/com.don.ffmpegplayer D/Player: Image displayed
10-23 14:53:42.926 26970-4527/com.don.ffmpegplayer D/Player: Converting image to RGB565...
10-23 14:53:42.934 26970-4527/com.don.ffmpegplayer D/Player: Image converted to RGB565
10-23 14:53:42.935 26970-4527/com.don.ffmpegplayer D/Player: Image wrote into frameRGBABuffer
10-23 14:53:42.936 26970-4527/com.don.ffmpegplayer D/Player: Writing 868352 bytes to windowBuffer
10-23 14:53:42.937 26970-4527/com.don.ffmpegplayer D/Player: Image displayed

我做错了什么?如果我理解正确,我需要按照以下步骤操作:

  1. 从解码器获取解码的AVFrame
  2. 将AVFrame数据转换为RGB565或RGB8888
  3. 从转换后的帧中获取像素数据
  4. 将其写入原生窗口
  5. 但是在这段代码中,有两点让我感到困惑:ANative_setBuffersGeometry被正确调用,为什么frameRGBABuffer大小为868352字节?如果视频尺寸为1024 * 424 frameRGBABuffer尺寸必须为width*height*4,不是吗?如果我在第一张图片曝光后将frameRGBABuffer尺寸更改为width*height*4程序崩溃。我将视频调光传递给ANative_setBuffersGeometry

    感谢先生的任何帮助。

1 个答案:

答案 0 :(得分:0)

问题在于我传入ANativeWindow_setBuffersGeometry的视频和值的宽度。我不知道为什么,但是640, 1280, 1920可以只处理var data = [ {item_name: 'Book', stock: 15}, {item_name: 'Pencil', stock: 15}, {item_name: 'Paper', stock: 3} ]; 作为宽度参数,高度 - 任何想要的。如果我设置任何其他宽度 - 问题表示截图。