#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
Что я делаю не так? Если я правильно понял, мне нужно выполнить следующие шаги:
- Получить декодированный AVFrame из декодера
- Преобразуйте данные AVFrame в RGB565 или RGB8888
- Получение пиксельных данных из преобразованного кадра
- Запишите его в собственное окно
Но в этом коде меня смущают два момента: 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 — любой желаемый. Если я установлю любую другую ширину — проблема появится, как на скриншоте.