适用于Android的简单FFMpeg播放器 [英] Simple FFMpeg player for Android

查看:58
本文介绍了适用于Android的简单FFMpeg播放器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在将AVFrame(AVPicture)输出到ANativeWindow时遇到问题.我写了simpe测试代码:

I have a problem with the output of AVFrame (AVPicture) into the ANativeWindow. I wrote simpe test code:

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!");
}

我在一些视频中遇到了下一个问题:例如,此视频为我提供了下一条日志:

I faced with the next problem in some videos: For example, this video gives me next logs:

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

我做错了什么?如果我理解正确,则需要执行以下步骤:

What am I doing wrong? If I understood correctly, I need to follow next steps:

  1. 从解码器获取解码的AVFrame
  2. 将AVFrame数据转换为RGB565或RGB8888
  3. 从转换后的帧中获取像素数据
  4. 将其写入本机窗口

但是在这段代码中,有两点使我感到困惑: ANative_setBuffersGeometry 是否正确调用?为什么 frameRGBABuffer 的大小为868352字节?如果视频大小为1024 * 424,则 frameRGBABuffer 大小必须为 width * height * 4 ,不是吗?如果我将第一个图像显示后,将 frameRGBABuffer 的大小更改为 width * height * 4 ,程序会自动重现.我将视频尺寸传递给 ANative_setBuffersGeometry .

But in this code two points confuse me: is ANative_setBuffersGeometry called correctly and why frameRGBABuffer size is 868352 bytes? If video size is 1024*424 frameRGBABuffer size must be width*height*4, isn't it? If I change frameRGBABuffer size to width*height*4 program carashes after first image diplayed. I pass video dimmensions to ANative_setBuffersGeometry.

对于任何帮助,谢谢.

推荐答案

问题出在视频宽度和我传递给 ANativeWindow_setBuffersGeometry 的值中.我不知道为什么,但是 ANativeWindow_setBuffersGeometry 只能将 640、1280、1920 作为宽度参数,高度-任何需要的句柄进行校正.如果我设置了其他宽度-如屏幕截图所示出现问题.

The problem was in width of video and value that I pass into ANativeWindow_setBuffersGeometry. I do not know why, but ANativeWindow_setBuffersGeometry can correct handle only 640, 1280, 1920 as a width parameter, height - any wanted. If I set any other width - problem apperars as on screenshot.

这篇关于适用于Android的简单FFMpeg播放器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆