Простой проигрыватель FFMpeg для Android

#android #c #video #ffmpeg

#Android #c #Видео #ffmpeg

Вопрос:

У меня проблема с выводом AVFrame (AVPicture) в исходное окно. Я написал простой тестовый код:

 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(amp;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 amp;amp; codecParams->height>0) {
        ALOGD("Video width: %dnVideo 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, amp;packet) >= 0 amp;amp; state->isPlaying) {
        if (packet.stream_index != state->video_stream) {
            ALOGD("Packet is not a video packet. Discard.");
            av_packet_unref(amp;packet);
            continue;
        }
        ret = avcodec_send_packet(codecContext, amp;packet);
        if (ret != 0) {
            ALOGE("Can not send packet to decode");
            av_packet_unref(amp;packet);
            continue;
        }
        ret = avcodec_receive_frame(codecContext, frame);
        if (ret != 0) {
            ALOGE("Can not receive decoded frame yet");
            av_packet_unref(amp;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, amp;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(amp;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(amp;frameRGBA->data[0]);
    av_packet_unref(amp;packet);
    avcodec_close(codecContext);
    avformat_close_input(amp;formatContext);
    avformat_free_context(formatContext);
    ALOGD("Read thread closed!");
}
  

В некоторых видеороликах я столкнулся со следующей проблемой:
введите описание изображения здесь
Например, в этом видео приведены следующие логи:

 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. Запишите его в собственное окно

Но в этом коде меня смущают два момента: ANative_setBuffersGeometry правильно вызывается и почему frameRGBABuffer размер равен 868352 байтам? Если размер видео равен 1024 * 424, frameRGBABuffer размер должен быть width*height*4 таким, не так ли? Если я изменю frameRGBABuffer размер на width*height*4 кеши программы после первого воспроизведения изображения. Я передаю настройки видео в ANative_setBuffersGeometry .

Заранее благодарю за любую помощь.

Комментарии:

1. Хорошо, насчет размера frameRGBABuffer — это правильно, потому что RGB565 имеет длину 2 байта, поэтому размер frameRGBABuffer в два раза меньше, чем RGBA8888, но вопрос о NativeWindow по-прежнему актуален.

Ответ №1:

Проблема заключалась в ширине видео и значении, которое я передаю в ANativeWindow_setBuffersGeometry . Я не знаю почему, но ANativeWindow_setBuffersGeometry могу исправить дескриптор только 640, 1280, 1920 как параметр width, height — любой желаемый. Если я установлю любую другую ширину — проблема появится, как на скриншоте.