980210 - что не так при предоставлении аргументов для sws_scale?
В следующем коде я не могу понять, что не так:
uint8_t *dstData[4]; int dstLinesize[4]; AVPixelFormat convertToPixFmt = AV_PIX_FMT_RGBA; int ret; // ... printf("tmp_frame format: %d (%s) %dx%d\n", tmp_frame->format, av_get_pix_fmt_name((AVPixelFormat)tmp_frame->format), tmp_frame->width, tmp_frame->height); // The above line prints: tmp_frame format: 23 (nv12) 480x480 int size = av_image_get_buffer_size(convertToPixFmt, tmp_frame->width, tmp_frame->height, 1); uint8_t *buffer = (uint8_t *) av_malloc(size); ret = av_image_copy_to_buffer(buffer, size, (const uint8_t * const *)&tmp_frame->data[i], (const int *)&tmp_frame->linesize[i], (AVPixelFormat)tmp_frame->format, tmp_frame->width, tmp_frame->height, 1); ASSERT(ret >= 0); ret = av_image_fill_arrays(dstData, dstLinesize, buffer, convertToPixFmt, dest_width, dest_height, 1); ASSERT(ret >= 0); ret = sws_scale( convertContext, dstData, dstLinesize, 0, dest_width, convertedFrame->data, convertedFrame->linesize); printf("sws_scale returns %d\n", ret); // prints: sws_scale returns 0 ASSERT(ret == tmp_frame->height); // ...
Это часть кода, который использует dxva2 для получения tmp_frame. Я вдохновил код из hw_decode.c и уверен, что в коде нет никакой ошибки. Tmp_frame правильно выполнен в формате NV12. Ошибка возникает как раз тогда, когда я вызываю sws_scale, и это:
bad src image pointers
Поэтому я не знаю, как предоставить указатели, чтобы не получить эту ошибку, и sws_scale может работать правильно. Есть идеи?
Я обновил вопрос для предоставления полного кода:
static AVBufferRef *hw_device_ctx = NULL; static enum AVPixelFormat hw_pix_fmt; static FILE *output_file = NULL; int main(int argc, char *argv[]) { AVFormatContext *input_ctx = NULL; int video_stream, ret; AVStream *video = NULL; AVCodecContext *decoder_ctx = NULL; AVCodec *decoder = NULL; AVPacket packet; enum AVHWDeviceType type; int i; if (argc < 2) { fprintf(stderr, "Usage: %s <input file>\n", argv[0]); return -1; } type = av_hwdevice_find_type_by_name("dxva2"); ASSERT(type != AV_HWDEVICE_TYPE_NONE); ASSERT(avformat_open_input(&input_ctx, argv[1], NULL, NULL) == 0); ASSERT(avformat_find_stream_info(input_ctx, NULL) >= 0); video_stream = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); ASSERT(video_stream >= 0); decoder_ctx = avcodec_alloc_context3(decoder); ASSERT(decoder_ctx); video = input_ctx->streams[video_stream]; ASSERT(avcodec_parameters_to_context(decoder_ctx, video->codecpar) >= 0); ASSERT(av_hwdevice_ctx_create(&hw_device_ctx, type, NULL, NULL, 0) >= 0); decoder_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); ASSERT(avcodec_open2(decoder_ctx, decoder, NULL) >= 0); printf("video info: %dx%d\n", decoder_ctx->width, decoder_ctx->height); AVFrame *frame = av_frame_alloc(); ASSERT(frame); AVFrame *sw_frame = av_frame_alloc(); ASSERT(sw_frame); AVFrame* convertedFrame = av_frame_alloc(); ASSERT(convertedFrame); AVPixelFormat convertToPixFmt = AV_PIX_FMT_RGBA; //int dest_width = 320, dest_height = 200; int dest_width = decoder_ctx->width, dest_height = decoder_ctx->height; SwsContext* convertContext = sws_getContext(decoder_ctx->width, decoder_ctx->height, AV_PIX_FMT_YUV420P, dest_width, dest_height, convertToPixFmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); ASSERT(convertContext); int convertedFrameAspectBufferSize = avpicture_get_size(convertToPixFmt, dest_width, dest_height); void *convertedFrameBuffer = av_malloc(convertedFrameAspectBufferSize); avpicture_fill((AVPicture*)convertedFrame, (uint8_t *)convertedFrameBuffer, convertToPixFmt, dest_width, dest_height); output_file = fopen("1.out", "w+"); for (int i = 0; /*i < 20*/; i++) { ret = av_read_frame(input_ctx, &packet); if (ret == AVERROR_EOF) break; ASSERT(ret >= 0); if (video_stream != packet.stream_index) continue; int ret = avcodec_send_packet(decoder_ctx, &packet); ASSERT(ret >= 0); //printf("%p", decoder->hw_configs->hwaccel); ret = avcodec_receive_frame(decoder_ctx, frame); if (ret < 0) printf("%d\t%d\n", i, ret); AVFrame *tmp_frame; if (frame->format > 0) // hw enabled { ASSERT(av_hwframe_transfer_data(sw_frame, frame, 0) >= 0); tmp_frame = sw_frame; } else { tmp_frame = frame; } printf("frame format: %d (%s) %dx%d\n", frame->format, av_get_pix_fmt_name((AVPixelFormat)frame->format), frame->width, frame->height); printf("sw_frame format: %d (%s) %dx%d\n", sw_frame->format, av_get_pix_fmt_name((AVPixelFormat)sw_frame->format), sw_frame->width, sw_frame->height); printf("tmp_frame format: %d (%s) %dx%d\n", tmp_frame->format, av_get_pix_fmt_name((AVPixelFormat)tmp_frame->format), tmp_frame->width, tmp_frame->height); /* video info: 480x480 frame format: 53 (dxva2_vld) 480x480 sw_frame format: 23 (nv12) 480x480 [swscaler @ 004cb2c0] bad src image pointers */ int size = av_image_get_buffer_size(convertToPixFmt, tmp_frame->width, tmp_frame->height, 1); uint8_t *buffer = (uint8_t *) av_malloc(size); ret = av_image_copy_to_buffer(buffer, size, (const uint8_t * const *)&tmp_frame->data[i], (const int *)&tmp_frame->linesize[i], (AVPixelFormat)tmp_frame->format, tmp_frame->width, tmp_frame->height, 1); ASSERT(ret > 0); ret = sws_scale( convertContext, tmp_frame->data, tmp_frame->linesize, 0, dest_width, convertedFrame->data, convertedFrame->linesize); printf("sws_scale returns %d\n", ret); ASSERT(ret == tmp_frame->height); ret = fwrite(convertedFrame->data, tmp_frame->height * tmp_frame->width, 1, output_file); ASSERT(ret == 1); break; } av_frame_free(&frame); av_packet_unref(&packet); avcodec_free_context(&decoder_ctx); avformat_close_input(&input_ctx); av_buffer_unref(&hw_device_ctx); return 0; }
Что я уже пробовал:
Я попытался использовать больший выделенный блок в качестве буфера. Я также попробовал другие форматы и изменение аргументов. Но мне это не удалось.