SDL2 + FFMPEG 播放器(无音频播放)

12 篇文章 0 订阅
6 篇文章 0 订阅
/*
 * auth           : 杜竞宁
 *
 * time           : 2021.12
 *
 * ffmpeg version : 4.4
 * SDL    version : 2.0.18
 *
 * */

#include <SDL.h>

#include <iostream>
#include <fstream>

#include <thread>
#include <chrono>

#include <ctime>
#include <mutex>
#include<queue>
#include <Log.h>

/* 跨平台且易用 */
#define sleep(for_seconds)   std::this_thread::sleep_for(std::chrono::seconds(for_seconds))
#define usleep(for_milliseconds)        std::this_thread::sleep_for(std::chrono::milliseconds(for_milliseconds))

extern "C"{
    #include <stdlib.h>
    #include <stdio.h>

    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
}

std::string logFile = "log.txt";
Dfile Df(logFile);
#define log(data)  Df.write(data)

void SaveFrame2PPM(AVFrame *pFrame, int width, int height, int iFrame) {
    std::ofstream mediaFile;
    std::string fileName = std::string("frame") + std::to_string(iFrame) + std::string(".ppm");
    mediaFile.open(fileName.c_str(), std::ios::out);
    if (mediaFile.is_open())
    {
        std::string head = std::string("P6\n") + std::to_string(width) + std::string(" ") + std::to_string(height) + std::string("\n255\n");
        mediaFile.write(head.c_str(), head.length());
        for(int i = 0; i < height; i++)
        {
            mediaFile.write((const char*)(pFrame->data[0] + i*pFrame->linesize[0]), width*3);
        }
    }
    mediaFile.close();
}




typedef struct node{
    Uint8* audio_point;
    Uint32 len;
}Node;

std::queue<Node> AudioQUeue;
std::mutex AudioQueueMutex;

void inputQueue(Node nd)
{
    AudioQueueMutex.lock();
    if (nd.len > 0)
    {
        AudioQUeue.push(nd);
    }
    AudioQueueMutex.unlock();
}

Node outputQueue()
{
    Node nd;
    nd.audio_point = NULL;
    nd.len = 0;
    AudioQueueMutex.lock();
    if (AudioQUeue.size() <= 0)
    {
        return nd;
    }
    nd = AudioQUeue.back();
    AudioQueueMutex.unlock();
    return nd;
}



void get_audio_date_cb(void *userdata, Uint8 *stream, int len){
    static uint8_t* audio_chunk = NULL;
    static Uint32 audio_len = 0;
    static uint8_t* audio_pos = NULL;
    std::cout << "get_audio_date_cb_________________________________=================" << std::endl;
    if (audio_len <= 0)
    {
        if (audio_chunk){
            av_freep(audio_chunk);
        }
        Node nd = outputQueue();
        if ( nd.len <= 0 ) {
            return;
        } else {
            audio_len = nd.len;
            audio_pos = nd.audio_point;
            audio_chunk = nd.audio_point;
        }
    }

    SDL_memset(stream, 0, len);
    if (audio_len <= 0){
        return;
    }

    SDL_MixAudio(stream, audio_pos, (int)audio_len, SDL_MIX_MAXVOLUME);
    audio_pos += len;
	audio_len -= len;
}

int main(int argc, char* argv[]){


    if (argc <= 1)
    {
        std::cout << "usage: ./execute [fileName]" << std::endl;
        return -1;
    }

    AVFormatContext *pFmtCtx = NULL;

    /* 打开输入流,自动检测格式 */
    AVDictionary *opts = nullptr;
    av_dict_set(&opts, "rtsp_transport", "tcp", 0);
    if ( avformat_open_input(&pFmtCtx, argv[1], NULL, &opts) != 0 )
    {
        std::cout << argv[1] << " open File error" << std::endl;
        avformat_close_input(&pFmtCtx);
        return -1;
    }

    if ( avformat_find_stream_info(pFmtCtx, NULL) < 0 )
    {
        std::cout << argv[1] << " avformat_find_stream_info error" << std::endl;
        avformat_close_input(&pFmtCtx);
        return -1;
    }

    /* 注意:参数4 - 0是打印输入流   1是打印输出流 */
    av_dump_format(pFmtCtx, 0, argv[1], 0);

    int vStreamIndex = -1, aStreamIndex = -1;
    for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++ )
    {
        int codec_type = -999, codec_id = -999;
        if ( pFmtCtx->streams[i] && pFmtCtx->streams[i]->codecpar )
        {
            codec_type = pFmtCtx->streams[i]->codecpar->codec_type;
            codec_id   = pFmtCtx->streams[i]->codecpar->codec_id;
        }

        if (codec_type == AVMEDIA_TYPE_VIDEO) // && codec_id == AV_CODEC_ID_H264
        {
            std::cout << "got video stream " << i << std::endl;
            vStreamIndex = i;
        } else if ( codec_type ==  AVMEDIA_TYPE_AUDIO && (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AAC_LATM) ){
            std::cout << "got audio stream " << i << std::endl;
            aStreamIndex = i;
        }
    }

    /* 处理数据 */
    int  i = 0;
    AVPacket packet;
    AVFrame *pFrame = NULL;
    AVFrame *frameRGB = NULL;
    AVCodec *pCodec = NULL;
    AVCodec *pAudioCodec = NULL;
    AVCodecContext *pCodecCtx = NULL;
    AVCodecContext *pAudioCodecCtx = NULL;
    if ( vStreamIndex >= 0  )
    {
        /* video codec */
        AVCodecParameters *codecpar = pFmtCtx->streams[vStreamIndex]->codecpar;
        pCodec = avcodec_find_decoder(codecpar->codec_id);
        pCodecCtx = avcodec_alloc_context3(pCodec);

        if ( !pCodecCtx || avcodec_parameters_to_context(pCodecCtx, codecpar) < 0 )
        {
            std::cout << argv[1] << " avcodec_parameters_to_context error" << std::endl;
            avcodec_free_context(&pCodecCtx);
                avformat_close_input(&pFmtCtx);
                return -1;
        }

        if ( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 )
        {
            std::cout << argv[1] << " avcodec_open2 error" << std::endl;
            avcodec_free_context(&pCodecCtx);
                avformat_close_input(&pFmtCtx);
                return -1;
        }
    }



    if ( aStreamIndex >= 0  )
    {
        /* audio codec */
        AVCodecParameters *codecpar = pFmtCtx->streams[aStreamIndex]->codecpar;
        pAudioCodec = avcodec_find_decoder(codecpar->codec_id);
        pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);

        if ( !pAudioCodecCtx || avcodec_parameters_to_context(pAudioCodecCtx, codecpar) < 0 )
        {
            std::cout << argv[1] << " avcodec_parameters_to_context error" << std::endl;
            avcodec_free_context(&pAudioCodecCtx);
                avformat_close_input(&pFmtCtx);
                return -1;
        }

        if ( avcodec_open2(pAudioCodecCtx, pAudioCodec, NULL) < 0 )
        {
            std::cout << argv[1] << " avcodec_open2 error" << std::endl;
            avcodec_free_context(&pAudioCodecCtx);
                avformat_close_input(&pFmtCtx);
                return -1;
        }
    }

    /* audio convert */
    int audio_channls = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
    int audio_buffer_size = av_samples_get_buffer_size(NULL, audio_channls, 1024, AV_SAMPLE_FMT_S16, 1);



   // uint8_t *avio_buffer = (uint8_t *)av_malloc(audio_buffer_size * 2);
    log( "audio_buffer_size:" + std::to_string(audio_buffer_size));


	/* frame where to save */
	frameRGB = av_frame_alloc();
	if ( !frameRGB )
	{
	    std::cout << argv[1] << " frameRGB = av_frame_alloc(); error" << std::endl;
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFmtCtx);
            return -1;
	}


std::cout << "got audio stream ======================" << std::endl;

/* 填充方式 1 */
    if(pCodecCtx)
    {
        frameRGB->format = AV_PIX_FMT_RGB24;
        frameRGB->width  = pCodecCtx->width;
        frameRGB->height = pCodecCtx->height;
        if ( av_frame_get_buffer(frameRGB, 1) < 0 )
        {
            std::cout << argv[1] << " av_frame_get_buffer error" <<  pCodecCtx->width <<std::endl;
                avcodec_free_context(&pCodecCtx);
                avformat_close_input(&pFmtCtx);
                return -1;
        }
    }



/* 填充方式 2
        uint8_t *buffer = NULL;
	int numBytes = -1;
	numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
	if (numBytes < 0)
	{
	    std::cout << argv[1] << " av_image_get_buffer_size error" << std::endl;
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFmtCtx);
            return -1;
	}
	buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
	if ( !buffer )
        {
            std::cout << argv[1] << " buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t)); error" << std::endl;
	    av_frame_free(&frameRGB);
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFmtCtx);
            return -1;
        }
	if ( av_image_fill_arrays(frameRGB->data, frameRGB->linesize,buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1) < 0 )
	{
	    std::cout << argv[1] << " av_image_fill_arrays error" << std::endl;
	    av_freep(&buffer);
	    av_frame_free(&frameRGB);
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFmtCtx);
            return -1;
	}
*/

	SwsContext *swsCtx = NULL;
	swsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL );
	if ( !swsCtx )
        {
            std::cout << argv[1] << " frameRGB = av_frame_alloc(); error" << std::endl;
            av_frame_free(&frameRGB);
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFmtCtx);
            return -1;
        }

    pFrame = av_frame_alloc();
	if ( !pFrame )
	{
	    std::cout << argv[1] << " frameRGB = av_frame_alloc(); error" << std::endl;
	    sws_freeContext(swsCtx);
            av_frame_free(&frameRGB);
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFmtCtx);
            return -1;
	}


	/* *******************  SDL init Video ***************************** */
	/* 窗口 */
	SDL_Window* Window = NULL;
	/* 渲染 */
	SDL_Renderer* Renderer = NULL;

    SDL_Surface* PrimarySurface = NULL;
    /* 纹理 */
    SDL_Texture* Texture = NULL;

    SDL_Rect Rect;
    const int WindowWidth = pCodecCtx->width;
    const int WindowHeight = pCodecCtx->height;

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
    {
        log("SDL_Init Error" + std::string(SDL_GetError()));
        return -1;
    }

    if(!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
        log("SDL_SetHint Error" + std::string(SDL_GetError()));
    }


    if((Window = SDL_CreateWindow("SDLPlayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowWidth, WindowHeight, SDL_WINDOW_SHOWN) ) == NULL)
    {
    	log("SDL_CreateWindow Error" + std::string(SDL_GetError()));
    	return -1;
    }

    if ((PrimarySurface = SDL_GetWindowSurface(Window)) == NULL )
    {
        log("SDL_GetWindowSurface Error");
        return -1;
    }
    if((Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED)) == NULL) {
    //if((Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_SOFTWARE)) == NULL) {
        log("SDL_CreateRenderer Error");
        return -1;
    }

    if (SDL_SetRenderDrawColor(Renderer, 0x00, 0x00, 0x00, 0xFF) != 0)
    {
        log("SDL_SetRenderDrawColor Error");
        return -1;
    }


    if ( (Texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, WindowWidth, WindowHeight)) == NULL )
    {
        log("SDL_SetRenderDrawColor Error");
        return -1;
    }

    SDL_Event Event;


    /* *******************  SDL init Audio  ***************************** */
    SDL_AudioSpec AudioSpec;
    AudioSpec.freq = pAudioCodecCtx->sample_rate;
    AudioSpec.format = AUDIO_F32;
    AudioSpec.channels = pAudioCodecCtx->channels;
    AudioSpec.samples = 1024;
    AudioSpec.callback = NULL;
    AudioSpec.userdata = NULL;
    AudioSpec.silence = 0;


    if( SDL_OpenAudio(&AudioSpec, NULL) != 0 )
    {
        log("SDL_OpenAudio Error" + std::string(SDL_GetError()));
        return -1;
    }

    SDL_PauseAudio(0);



    /* *******************  SDL init end  ***************************** */



	/* read data */
//	long keyFrameSum = 0;
	bool isSDLRunning = true;
	int packetSize = 0;
	//std::ofstream YUVfile2write = std::ofstream("5v.yuv",std::ios_base::binary);
	while (av_read_frame(pFmtCtx, &packet) >= 0){
        usleep(1);
        while(SDL_PollEvent(&Event) != 0) {
            usleep(1);
            switch (Event.type) {
                case SDL_KEYDOWN :
                    if (Event.key.keysym.sym != SDLK_ESCAPE) {
                       break;
                    }
                case SDL_QUIT :
                    isSDLRunning = false;
                    /*************** SDL clear ****************/
                    if(Renderer) {
                        SDL_DestroyRenderer(Renderer);
                        Renderer = NULL;
                    }

                    if(Window) {
                        SDL_DestroyWindow(Window);
                        Window = NULL;
                    }

                    //SDL_PauseAudio(1);
                   // if (YUVfile2write.is_open())
                   //     YUVfile2write.close();

                    SDL_Quit();
                    std::cout << "closed" <<std::endl;
                    /*************** SDL clear END ****************/
                    exit(1);
                    return -1;
                    break;
                default :
                    break;
            }
		}

		if (packet.size <= 0)
        {
            continue;
        }

        packetSize += packet.size;
	    if (packet.stream_index == vStreamIndex)
        {
            std::cout << "video ++++++++ " << pCodec->name <<  " packet.size " << packet.size <<  "Frame frame of " << pCodecCtx->frame_number <<  std::endl;
            ++i;
            int ret = -1;
            ret = avcodec_send_packet(pCodecCtx, &packet);

            if (ret == AVERROR_EOF|| ret == AVERROR(EAGAIN) || ret ==  AVERROR(EINVAL) ||  ret == AVERROR(ENOMEM) ){
                break;
            }else if (ret < 0 )
            {
                std::cout << argv[1] << " avcodec_send_packet 1 error " << ret << std::endl;
                    sws_freeContext(swsCtx);
                    av_frame_free(&frameRGB);
                    avcodec_free_context(&pCodecCtx);
                    avformat_close_input(&pFmtCtx);
                    return -1;
            }

            while (ret >= 0)
            {
                ret = avcodec_receive_frame(pCodecCtx, pFrame);
                if ( ret == AVERROR_EOF || ret == AVERROR(EAGAIN) || ret ==  AVERROR(EINVAL) ||  ret == AVERROR(ENOMEM)) {
                    break;
                } else if (ret < 0){
                    std::cout << argv[1] << " avcodec_receive_frame 1 error " << ret << std::endl;
                    sws_freeContext(swsCtx);
                    av_frame_free(&frameRGB);
                    avcodec_free_context(&pCodecCtx);
                    avformat_close_input(&pFmtCtx);
                    break;
                }



                //if (ret >= 0)
                    {

                    //for(int i = 0; i < pFrame->height; i++)
                       //YUVfile2write.write((char*)(pFrame->data[0] + i * (pFrame->linesize[0])), pFrame->width);

                    //int UVheight = pFrame->height/2;

                   // for(int i = 0; i < UVheight; i++)
                    {
                        ;
                        //YUVfile2write.write((char*)(pFrame->data[1] + i * (pFrame->linesize[1])), pFrame->linesize[1]);
                        //YUVfile2write.write((char*)(pFrame->data[2] + i * (pFrame->linesize[2])), pFrame->linesize[2]);
                    }

                   // std::cout << " pFrame->pkt_size:" << pFrame->pkt_size << " Y width:" << pFrame->linesize[0] << " U width:" << pFrame->linesize[1]  << " V width:" << pFrame->linesize[2] <<
                  // "  UVheight:"<<UVheight<<" pFrame->width*pFrame->height:"<<pFrame->width*pFrame->height<< " pFrame->linesize[1]*UVheight*2:"<<pFrame->linesize[1]*UVheight*2<< std::endl;
                }

                if ( false && i%100 == 0)
                {
                    sws_scale(swsCtx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, frameRGB->data, frameRGB->linesize);
                    SaveFrame2PPM(frameRGB, pCodecCtx->width, pCodecCtx->height, i);
                    std::cout << "recv frame of " << i << " ret:" << ret <<std::endl;
                }

                //if (pFrame->key_frame == 1)
                {
                   // keyFrameSum++;
                   // std::cout << "Frame frame of " << pCodecCtx->frame_number << "  keyFrameSum:"<< keyFrameSum << "  pict_type(1:I,2:P,3:B):" << pFrame->pict_type << "  pts:" << pFrame->pts << " gopOfAVSize: " << packetSize/1024 << " KB/s" << std::endl;
                    packetSize = 0;
                }

                if (isSDLRunning)
                {
                    SDL_UpdateYUVTexture(Texture, NULL, pFrame->data[0], pFrame->linesize[0] ,pFrame->data[1] ,pFrame->linesize[1], pFrame->data[2] ,pFrame->linesize[2]);
                    Rect.x = 0;
                    Rect.y = 0;
                    Rect.w = WindowWidth;
                    Rect.h = WindowHeight;
                    SDL_RenderClear(Renderer);
                    SDL_RenderCopy(Renderer, Texture, NULL, &Rect);
                    SDL_RenderPresent(Renderer);

                    SDL_Delay(10);
                }
                av_frame_unref(pFrame);
            }

	    }

	    if (packet.stream_index == aStreamIndex)
        {
            std::cout << "audio --------  " << pAudioCodec->name << " packet.size " << packet.size <<std::endl;
            int ret = -1;
            ret = avcodec_send_packet(pAudioCodecCtx, &packet);
            if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN) /*|| ret ==  AVERROR(EINVAL) ||  ret == AVERROR(ENOMEM) */){
                break;
            } else if (ret < 0 ) {
                std::cout << argv[1] << " avcodec_send_packet 2 error " << ret << std::endl;
                sws_freeContext(swsCtx);
                av_frame_free(&frameRGB);
                av_frame_free(&pFrame);
                avcodec_free_context(&pAudioCodecCtx);
                avformat_close_input(&pFmtCtx);
                return -1;
            }

            while( ret >= 0 )
            {
                ret = avcodec_receive_frame(pAudioCodecCtx, pFrame);
                if ( ret == AVERROR_EOF || ret == AVERROR(EAGAIN)  /*|| ret ==  AVERROR(EINVAL) ||  ret == AVERROR(ENOMEM) */){
                    break;
                } else if( ret < 0 ) {
                    std::cout << argv[1] << " avcodec_receive_frame 2 error " << ret << std::endl;
                    sws_freeContext(swsCtx);
                    av_frame_free(&frameRGB);
                    av_frame_free(&pFrame);
                    avcodec_free_context(&pAudioCodecCtx);
                    avformat_close_input(&pFmtCtx);
                    return -1;
                }

                if (isSDLRunning)
                {

                }
                //std::cout << "audio *** " <<  "sample rate:" << pAudioCodecCtx->sample_rate << " codecName:" << pAudioCodec->name << " channls:" << pAudioCodecCtx->channels  << "  deep:" << *pAudioCodec->sample_fmts << std::endl;

                if (AV_SAMPLE_FMT_FLTP == *pAudioCodec->sample_fmts){
                   // std::cout << "sample_fmts: AV_SAMPLE_FMT_FLTP" << std::endl;
                }
                av_frame_unref(pFrame);
            }
	    }
	    av_packet_unref(&packet);
	}

    std::cout << "video *** width:" << pCodecCtx->width << " height:" << pCodecCtx->height << " codecName:" << pCodec->name << std::endl;


    std::cout << argv[1] <<" open File OK!  audio: " << aStreamIndex << " video: " << vStreamIndex << "     | note! [ -1:not found ]" << std::endl;

    if ( vStreamIndex >= 0 )
    {
        av_frame_unref(frameRGB);
        av_frame_free(&frameRGB);
        av_frame_free(&pFrame);
        avcodec_free_context(&pCodecCtx);
    }
    avformat_close_input(&pFmtCtx);
    //if (YUVfile2write.is_open())
      //  YUVfile2write.close();

    return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值