aliakseis / FFmpegPlayer

Simple FFmpeg video player
MIT License
151 stars 44 forks source link

render nv12 issues #16

Open w136111526 opened 5 years ago

w136111526 commented 5 years ago

Hello, can I use a simple SDL player to play nv12 data without hard decoding nv12, why use USE_HWACCEL hard decoding to convert to nv12, SDL playing rendering is not normal. My code is as follows:

include

include "windows.h"

include "ffmpeg_dxva2.h"

define __STDC_CONSTANT_MACROS

extern "C" {

include "libavcodec/avcodec.h"

include "libavformat/avformat.h"

include "libswscale/swscale.h"

include "libavdevice/avdevice.h"

include "libavutil/avutil.h"

include "libavutil/imgutils.h"

include "libavfilter/avfilter.h"

include "SDL/SDL.h"

};

define USE_HWACCEL 0

static FILE *output_file = NULL;

int main(int argc, char argv[]) { AVFormatContext pFormatCtx; int i, videoindex; AVCodecContext pCodecCtx; AVCodec pCodec; AVFrame pFrame,pFrameTarget; AVFramesw_frame = NULL; uint8_t out_buffer; AVPacket packet; int y_size; int ret, got_picture; struct SwsContext img_convert_ctx = nullptr;

char filepath[]="e:/1.MP4";
//SDL---------------------------
int screen_w=0,screen_h=0;
SDL_Window *screen; 
SDL_Renderer* sdlRenderer;
SDL_Texture* sdlTexture;

avcodec_register_all();
av_register_all();
avdevice_register_all();
pFormatCtx = avformat_alloc_context();

if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0)
{
    printf("Couldn't open input stream.\n");
    return -1;
}
if(avformat_find_stream_info(pFormatCtx,NULL)<0){
    printf("Couldn't find stream information.\n");
    return -1;
}

videoindex=-1;
for(i=0; i<pFormatCtx->nb_streams; i++) 
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
        videoindex=i;
        break;
    }
if(videoindex==-1){
    printf("Didn't find a video stream.\n");
    return -1;
}
auto videoStream = pFormatCtx->streams[videoindex];

pCodecCtx=pFormatCtx->streams[videoindex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL){
    printf("Codec not found.\n");
    return -1;
}

if USE_HWACCEL

////Add HwAccel
InputStream *ist = new InputStream();
ist->hwaccel_id = HWACCEL_AUTO;
ist->hwaccel_device = "dxva2";
ist->dec = pCodec;
ist->dec_ctx = pCodecCtx;

pCodecCtx->opaque = ist;
if (dxva2_init(pCodecCtx) >= 0)
{
    pCodecCtx->get_buffer2 = ist->hwaccel_get_buffer;
    pCodecCtx->get_format = GetHwFormat;
    pCodecCtx->thread_safe_callbacks = 1;
    pCodecCtx->thread_count = 1;  // Multithreading is apparently not compatible with hardware decoding
}
else
{
    delete ist;
    pCodecCtx->opaque = nullptr;
    pCodecCtx->thread_count = 2;
    pCodecCtx->flags2 |= AV_CODEC_FLAG2_FAST;
}

else

pCodecCtx->opaque = nullptr;
pCodecCtx->thread_count = 2;
pCodecCtx->flags2 |= AV_CODEC_FLAG2_FAST;

endif // USE_HWACCEL

if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
    printf("Could not open codec.\n");
    return -1;
}

screen_w = pCodecCtx->width;
screen_h = pCodecCtx->height;
AVPixelFormat image_transf_format = AV_PIX_FMT_NV12;

pFrame=av_frame_alloc();
sw_frame = av_frame_alloc();

pFrameTarget = av_frame_alloc();
out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(image_transf_format, screen_w, screen_h, 1));
av_image_fill_arrays(pFrameTarget->data, pFrameTarget->linesize, out_buffer,
    image_transf_format, screen_w, screen_h, 1);
pFrameTarget->width = screen_w;
pFrameTarget->height = screen_h;
packet=(AVPacket *)av_malloc(sizeof(AVPacket));
//Output Info-----------------------------
printf("--------------- File Information ----------------\n");
av_dump_format(pFormatCtx,0,filepath,0);
printf("-------------------------------------------------\n");

if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
    printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
    return -1;
} 

////SDL 2.0 Support for multiple windows
screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
    screen_w, screen_h,
    SDL_WINDOW_OPENGL);
sdlRenderer = SDL_CreateRenderer(screen, -1, 0);  
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_NV12, SDL_TEXTUREACCESS_STREAMING, screen_w, screen_h);

//SDL End----------------------
while(av_read_frame(pFormatCtx, packet)>=0){
    if(packet->stream_index==videoindex)
    {
        int nTime0 = GetTickCount();
        ret = avcodec_send_packet(pCodecCtx, packet);
        if(ret < 0)
            continue;
        ret = avcodec_receive_frame(pCodecCtx, pFrame);
        if(ret < 0){
            printf("Decode Error.\n");
            continue;
        }

        if (pFrame->format == AV_PIX_FMT_DXVA2_VLD)
        {
            InputStream* ist = (InputStream *)pCodecCtx->opaque;
            int nTime0 = GetTickCount();
            ist->hwaccel_retrieve_data(pCodecCtx, pFrame);
            //sw_frame = pFrame;
            av_frame_move_ref(sw_frame, pFrame);
            printf("convert time :%d\n", GetTickCount() - nTime0);
            //sw_frame = pFrame;
        }
        else
        {
            sw_frame = pFrame;
        }
        if(1){
            if (!img_convert_ctx)
            {
                img_convert_ctx = sws_getContext(sw_frame->width, sw_frame->height, (AVPixelFormat)sw_frame->format,
                    pFrameTarget->width, pFrameTarget->height, image_transf_format, SWS_BICUBIC, NULL, NULL, NULL);
            }
            sws_scale(img_convert_ctx, (const uint8_t* const*)sw_frame->data, sw_frame->linesize, 0, sw_frame->height,
                pFrameTarget->data, pFrameTarget->linesize);

            SDL_UpdateTexture(sdlTexture, NULL, pFrameTarget->data[0], pFrameTarget->linesize[0]);
            SDL_RenderClear(sdlRenderer);
            SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
            SDL_RenderPresent(sdlRenderer);
        }
        SDL_Delay(40);
        av_frame_unref(sw_frame);
    }
}
sws_freeContext(img_convert_ctx);

SDL_Quit();

av_frame_free(&pFrameTarget);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);

return 0;

}

aliakseis commented 5 years ago

I've just tried your code: https://github.com/aliakseis/SDL-example Seems to be working fine with small changes so far. However the SDL frame transfer functionality does not seem to be efficient, probably some tweaking is required.

aliakseis commented 5 years ago

Sorry, made an update - there was an issue. It seems to me that yv12 matches decoding needs better than nv12.

w136111526 commented 5 years ago

Yes, yv12 is normal, thank you very much.

w136111526 commented 5 years ago

Is the data after hard decoding is nv21? and I tried H. 265 video(HEVC format). Why not support hard decoding? I tried Win7 and Win10 and they weren't working, but not enough dxva2_mode.

aliakseis commented 5 years ago

Just made some updates for H.265 stuff, probably it is fixed now: http://jell.yfish.us/

aliakseis commented 2 years ago

BTW here is a portable player prototype: https://github.com/aliakseis/FFmpegPlayer/tree/master/QtPlayer