未同步的play

CGSdlRender.h

/* Copyright [c] 2017-2027 Gang.Wang Allrights Reserved*/
#ifndef CG_SDL_RENDER_H
#define CG_SDL_RENDER_H


#include <windows.h>
#include <mutex>
#include "SDL.h"

namespace ChunGen { namespace Client { namespace Player {

    class CGBlockRingBuffer
    {
    public:
        CGBlockRingBuffer()
            :buffer(nullptr)
            ,bufferSize(0)
            ,writeIndex(0)
            ,readIndex(0)
            ,bytesCanRead(0)        
        {
            
        }

        void Init(int buffSize)
        {

            buffer =  new uint8_t [buffSize];
            if(!buffer) return;
            this->bufferSize = buffSize;
        }

        void Write(uint8_t *src, int size)
        {
            if(!src ||size == 0) return ;
            std::lock_guard<std::mutex> lk(lock);
            int bytesCanWrite = size < bufferSize - bytesCanRead ? size : (bufferSize - bytesCanRead);

            if(bytesCanWrite <= bufferSize - writeIndex)
            {
                memcpy(buffer + writeIndex,src,bytesCanWrite);
                writeIndex += bytesCanWrite;
                if(writeIndex == bufferSize)
                {
                    writeIndex = 0;
                }
            }
            else
            {
                int room = bufferSize - writeIndex;
                memcpy(buffer + writeIndex, src,room);
                int left = bytesCanWrite - room;
                memcpy(buffer, src + room, left);
                writeIndex = left;
            }
            bytesCanRead += bytesCanWrite;

        }

        int Read(uint8_t *dst, int size)
        {
            if(!dst || size == 0) return 0;
            std::lock_guard<std::mutex> lk(lock);
            int bytesRead = size < bytesCanRead ? size : bytesCanRead;
            if(bytesRead <= bufferSize - readIndex)
            {
                memcpy(dst,buffer + readIndex,bytesRead);
                readIndex += bytesRead;
                if(readIndex == bufferSize) readIndex = 0;
            }
            else
            {
                int bytesHead = bufferSize - readIndex;
                memcpy(dst, buffer + readIndex,bytesHead);
                int bytesTail = bytesRead - bytesHead;
                memcpy(dst + readIndex,buffer, bytesTail);
                readIndex = bytesTail;
            }
            bytesCanRead -= bytesRead;
            return bytesRead;
        }
        int bytesCanRead;
    private:
        uint8_t *buffer;
        int bufferSize;
        int readIndex;
        int  writeIndex;
        
        std::mutex lock;
    };

    class CGSDLRender 
    {
    public:
        CGSDLRender();
        virtual ~CGSDLRender(void);
        
        int InitVideo(HWND hDisplayWindow, int adapterId = 0);

        int CreateVideoSurface(int width, int height);
        int Display(BYTE** data, int* linesize);
        

        int InitAudio(int samples,int channels);
        int PlaySamples(BYTE* buffer, int size) ;
        void Close();
        CGBlockRingBuffer* blockingBuffer;
    private:

        SDL_Window *sdlWindow;
        SDL_Renderer *sdlRender;
        SDL_Texture *sdlTexture;

        int width;
        int height;

    
        //CircularBuffer *blockingBuffer;
    private:
    
    };

    
}}}
#endif

CGSdlRender.cpp

/* Copyright [c] 2017-2027 Gang.Wang Allrights Reserved*/
//#include "pch.h"
#include "CGSDLRender.h"
using namespace ChunGen::Client::Player;

    void  callback(void *userdata, Uint8 *stream, int len) {
        if(userdata != nullptr)
        {
            CGBlockRingBuffer* blockingBuffer = (CGBlockRingBuffer*)userdata;
            auto len1 = 0;
            while (len > 0)
            {
                len1 = blockingBuffer->Read((uint8_t*)stream + len1,len);
                len = len - len1;
            }
            
        }
    }

    CGSDLRender::CGSDLRender()
        :blockingBuffer(nullptr)
    {

    }

    CGSDLRender::~CGSDLRender()
    {
        
    }

    int CGSDLRender::InitVideo(HWND hDisplayWindow, int adapterId)
    {
        SDL_Init(SDL_INIT_VIDEO| SDL_INIT_TIMER);
        //sdlWindow = SDL_CreateWindowFrom(hDisplayWindow);
        //sdlWindow = SDL_CreateWindow("Camera1", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320,
        //    240, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN| SDL_WINDOW_RESIZABLE);
        sdlWindow = SDL_CreateWindow("Camera1", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 160,
            120, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
        sdlRender = SDL_CreateRenderer(sdlWindow, -1, SDL_RendererFlags::SDL_RENDERER_ACCELERATED);
        return S_OK;
    }

    int CGSDLRender::CreateVideoSurface(int width, int height)
    {
        this->width = width;
        this->height = height;
        sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, width, height);
        return S_OK;
    }

    int CGSDLRender::Display(BYTE** data, int* linesize)
    {
        void* piexels = nullptr;
        int pitch;

        int ret = SDL_LockTexture(sdlTexture, NULL, &piexels, &pitch);
        if(ret < 0) return ret;
        uint8_t*    yuv[3] = { 
            (uint8_t*)piexels,(uint8_t*)piexels + pitch * height,
            (uint8_t*)piexels + pitch * height + ((pitch >> 1) * (height >> 1))
            };
    
            for (int i = 0; i < height; i++)
            {
                memcpy(yuv[0] + i * pitch, data[0] + i * linesize[0], linesize[0]);
                if (i % 2 == 0)
                {
                    memcpy(yuv[1] + (i >> 1) * (pitch >> 1), data[2] + (i >> 1) * linesize[2], linesize[2]);
                    memcpy(yuv[2] + (i >> 1) * (pitch >> 1), data[1] + (i >> 1) * linesize[1], linesize[1]);
                }
            }
        

        SDL_UnlockTexture(sdlTexture);
        SDL_RenderClear(sdlRender);
        SDL_RenderCopy(sdlRender, sdlTexture, NULL, NULL);
        SDL_RenderPresent(sdlRender);
        return S_OK;
    }



    void CGSDLRender::Close()
    {
        SDL_CloseAudio();
        if(blockingBuffer)
        {
            delete blockingBuffer;
        }
        
    }

    int CGSDLRender::InitAudio(int samples,int channels)
    {
        SDL_Init(SDL_INIT_AUDIO);
        blockingBuffer = new CGBlockRingBuffer();
        //blockingBuffer->Init(4 * 1024);
        blockingBuffer->Init(1*1024 * 1024);
        SDL_AudioSpec wanted;
        wanted.freq = samples;
        wanted.format = AUDIO_S16SYS;
        wanted.channels = channels > 2 ? 2 : channels;
        wanted.silence = 0;
        wanted.samples = 1024;
        wanted.callback = callback;
        wanted.userdata = this->blockingBuffer;
    //    devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
        auto r = (SDL_OpenAudio(&wanted,NULL));
        SDL_PauseAudio(0);
        return r;

    }
    int CGSDLRender::PlaySamples(BYTE* buffer, int size) {
        this->blockingBuffer->Write(buffer,size);
         //SDL_QueueAudio(1,buffer,size);
        return S_OK;
    }

demuxdecod.cpp

#include <memory>
#include <iostream>

extern "C" {
    #include <libavutil/imgutils.h>
    #include <libavutil/samplefmt.h>
    #include <libavutil/timestamp.h>
    #include <libavformat/avformat.h>
    #include "libswresample/swresample.h"
#include "libavutil/opt.h"
}

#include "CGSdlRender.h"
using namespace ChunGen::Client::Player;

static AVFormatContext* fmt_ctx = NULL;
static AVCodecContext* video_dec_ctx = NULL, * audio_dec_ctx;
static int width, height;
static enum AVPixelFormat pix_fmt;
static AVStream* video_stream = NULL, * audio_stream = NULL;
static const char* src_filename = NULL;
static const char* video_dst_filename = NULL;
static const char* audio_dst_filename = NULL;
//static FILE* video_dst_file = NULL;
//static FILE* audio_dst_file = NULL;

static uint8_t* video_dst_data[4] = { NULL };
static int      video_dst_linesize[4];
static int video_dst_bufsize;

static int video_stream_idx = -1, audio_stream_idx = -1;
static AVFrame* frame = NULL;
static AVPacket* pkt = NULL;
static int video_frame_count = 0;
static int audio_frame_count = 0;

std::shared_ptr<CGSDLRender> sdlRender;
SDL_Thread* refresh_thread;
SDL_Event event;
SwrContext* swr;
//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)

int thread_exit = 0;
int refresh_video(void* opaque) {
    while (thread_exit == 0) {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
    return 0;
}

static int output_video_frame(AVFrame* frame)
{
    if (frame->width != width || frame->height != height ||
        frame->format != pix_fmt) {
        /* To handle this change, one could call av_image_alloc again and
         * decode the following frames into another rawvideo file. */
        fprintf(stderr, "Error: Width, height and pixel format have to be "
            "constant in a rawvideo file, but the width, height or "
            "pixel format of the input video changed:\n"
            "old: width = %d, height = %d, format = %s\n"
            "new: width = %d, height = %d, format = %s\n",
            width, height, av_get_pix_fmt_name(pix_fmt),
            frame->width, frame->height,
            av_get_pix_fmt_name((enum AVPixelFormat)frame->format));
        return -1;
    }

    printf("video_frame n:%d coded_n:%d\n",
        video_frame_count++, frame->coded_picture_number);

    /* copy decoded frame to destination buffer:
     * this is required since rawvideo expects non aligned data */
    av_image_copy(video_dst_data, video_dst_linesize,
        (const uint8_t**)(frame->data), frame->linesize,
        pix_fmt, width, height);

    /* write to rawvideo file */
    //  fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);

    SDL_WaitEvent(&event);
    if (event.type == REFRESH_EVENT) {
        sdlRender->Display((uint8_t**)frame->data, frame->linesize);
    }
    else if (event.type == SDL_QUIT) {
       // break;
    }
    return 0;
}

static int output_audio_frame(AVFrame* frame)
{
    size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((enum AVSampleFormat)frame->format);

 

    char str[AV_TS_MAX_STRING_SIZE];
    printf(" audio_frame n:%d nb_samples:%d pts:%s \n",
        audio_frame_count++, frame->nb_samples,
       // av_ts2timestr(frame->pts, &audio_dec_ctx->time_base)
        av_ts_make_time_string(str, frame->pts, &audio_dec_ctx->time_base)
    );

    /* Write the raw audio data samples of the first plane. This works
     * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,
     * most audio decoders output planar audio, which uses a separate
     * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
     * In other words, this code will write only the first audio channel
     * in these cases.
     * You should use libswresample or libavfilter to convert the frame
     * to packed data. */
   // fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);

    if (swr)
    {
        int dstNbChannels = 1;
        int srcNbSamples = frame->nb_samples;
        int srcRate = audio_dec_ctx->sample_rate;
        int dstRate = audio_dec_ctx->sample_rate;
        int dstNbSamples = av_rescale_rnd(srcNbSamples, dstRate, srcRate, AV_ROUND_UP);
        AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;

        uint8_t** dst_data = nullptr;
        int dstLinesize;
        dstNbChannels = av_get_channel_layout_nb_channels(audio_dec_ctx->channel_layout);
        dstNbChannels = dstNbChannels > 0 ? dstNbChannels : 1;
        int ret = av_samples_alloc_array_and_samples(&dst_data, &dstLinesize, dstNbChannels, dstNbSamples, dst_sample_fmt, 0);

        ret = swr_convert(swr, dst_data, dstNbSamples, (const uint8_t**)frame->data, srcNbSamples);

        sdlRender->PlaySamples((BYTE*)dst_data[0], dstLinesize);

        //fwrite((BYTE*)dst_data[0], 1, dstLinesize, audio_dst_file);

        if (dst_data)
        {
            av_freep((void*)&dst_data[0]);
        }
        av_freep(&dst_data);
    }
    else
    {
        sdlRender->PlaySamples(frame->data[0], frame->linesize[0]);
    }
    while(sdlRender->blockingBuffer->bytesCanRead > 1024*20)
         SDL_Delay(1);
    return 0;
}

static int decode_packet(AVCodecContext* dec, const AVPacket* pkt)
{
    int ret = 0;

    // submit the packet to the decoder
    ret = avcodec_send_packet(dec, pkt);
    if (ret < 0) {
        char str[AV_ERROR_MAX_STRING_SIZE];
        fprintf(stderr, "Error submitting a packet for decoding (%s)\n", 
            av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, ret)
           // av_err2str(ret)
        );
        return ret;
    }

    // get all the available frames from the decoder
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec, frame);
        if (ret < 0) {
            // those two return values are special and mean there is no output
            // frame available, but there were no errors during decoding
            if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
                return 0;
            char str[AV_ERROR_MAX_STRING_SIZE];
            fprintf(stderr, "Error during decoding (%s)\n", 
                av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, ret)
                //    av_err2str(ret)
            );
            return ret;
        }

        // write the frame data to output file
        if (dec->codec->type == AVMEDIA_TYPE_VIDEO)
            ret = output_video_frame(frame);
        else
            ret = output_audio_frame(frame);

        av_frame_unref(frame);
        if (ret < 0)
            return ret;
    }

    return 0;
}

static int open_codec_context(int* stream_idx,
    AVCodecContext** dec_ctx, AVFormatContext* fmt_ctx, enum AVMediaType type)
{
    int ret, stream_index;
    AVStream* st;
    AVCodec* dec = NULL;
    AVDictionary* opts = NULL;

    ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not find %s stream in input file '%s'\n",
            av_get_media_type_string(type), src_filename);
        return ret;
    }
    else {
        stream_index = ret;
        st = fmt_ctx->streams[stream_index];

        /* find decoder for the stream */
        dec = avcodec_find_decoder(st->codecpar->codec_id);
        if (!dec) {
            fprintf(stderr, "Failed to find %s codec\n",
                av_get_media_type_string(type));
            return AVERROR(EINVAL);
        }

        /* Allocate a codec context for the decoder */
        *dec_ctx = avcodec_alloc_context3(dec);
        if (!*dec_ctx) {
            fprintf(stderr, "Failed to allocate the %s codec context\n",
                av_get_media_type_string(type));
            return AVERROR(ENOMEM);
        }

        /* Copy codec parameters from input stream to output codec context */
        if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
            fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
                av_get_media_type_string(type));
            return ret;
        }

        /* Init the decoders */
        if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
            fprintf(stderr, "Failed to open %s codec\n",
                av_get_media_type_string(type));
            return ret;
        }
        *stream_idx = stream_index;
    }

    return 0;
}

static int get_format_from_sample_fmt(const char** fmt,
    enum AVSampleFormat sample_fmt)
{
    int i;
    struct sample_fmt_entry {
        enum AVSampleFormat sample_fmt; const char* fmt_be, * fmt_le;
    } sample_fmt_entries[] = {
        { AV_SAMPLE_FMT_U8,  "u8",    "u8"    },
        { AV_SAMPLE_FMT_S16, "s16be", "s16le" },
        { AV_SAMPLE_FMT_S32, "s32be", "s32le" },
        { AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
        { AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
    };
    *fmt = NULL;

    for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
        struct sample_fmt_entry* entry = &sample_fmt_entries[i];
        if (sample_fmt == entry->sample_fmt) {
            *fmt = AV_NE(entry->fmt_be, entry->fmt_le);
            return 0;
        }
    }

    fprintf(stderr,
        "sample format %s is not supported as output format\n",
        av_get_sample_fmt_name(sample_fmt));
    return -1;
}

int main1(int argc, char** argv)
{
    int ret = 0;
    int n_channels =0;
    sdlRender = std::make_shared<CGSDLRender>();
    /*
    if (argc != 4) {
        fprintf(stderr, "usage: %s  input_file video_output_file audio_output_file\n"
            "API example program to show how to read frames from an input file.\n"
            "This program reads frames from a file, decodes them, and writes decoded\n"
            "video frames to a rawvideo file named video_output_file, and decoded\n"
            "audio frames to a rawaudio file named audio_output_file.\n",
            argv[0]);
        exit(1);
    }
    src_filename = argv[1];
    video_dst_filename = argv[2];
    audio_dst_filename = argv[3];
    */
    //src_filename = "test.mp4";
    src_filename = argv[1];
   //   src_filename = "sax.mp3";
    //video_dst_filename = "del.yuv";
      audio_dst_filename = "del.pcm";

    /* open input file, and allocate format context */
    if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
        fprintf(stderr, "Could not open source file %s\n", src_filename);
        exit(1);
    }

    /* retrieve stream information */
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "Could not find stream information\n");
        exit(1);
    }

    if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
        video_stream = fmt_ctx->streams[video_stream_idx];
        /*
        video_dst_file = fopen(video_dst_filename, "wb");
        if (!video_dst_file) {
            fprintf(stderr, "Could not open destination file %s\n", video_dst_filename);
            ret = 1;
            goto end;
        }
        */
        /* allocate image where the decoded image will be put */
        width = video_dec_ctx->width;
        height = video_dec_ctx->height;
        pix_fmt = video_dec_ctx->pix_fmt;
        ret = av_image_alloc(video_dst_data, video_dst_linesize,
            width, height, pix_fmt, 1);
        if (ret < 0) {
            fprintf(stderr, "Could not allocate raw video buffer\n");
            goto end;
        }
        video_dst_bufsize = ret;

        
        sdlRender->InitVideo(0);
        sdlRender->CreateVideoSurface(width, height);

        refresh_thread = SDL_CreateThread(refresh_video, NULL, NULL);
    }


    if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {
        audio_stream = fmt_ctx->streams[audio_stream_idx];
        /*
        audio_dst_file = fopen(audio_dst_filename, "wb");
        if (!audio_dst_file) {
            fprintf(stderr, "Could not open destination file %s\n", audio_dst_filename);
            ret = 1;
            goto end;
        }*/
        n_channels = audio_dec_ctx->channels;
        printf("init rate,chan %d %d--------->\n", audio_dec_ctx->sample_rate, n_channels);
        sdlRender->InitAudio(audio_dec_ctx->sample_rate, n_channels);

        //--
        if (audio_dec_ctx->sample_fmt != AV_SAMPLE_FMT_S16)
        {
            swr = swr_alloc();
            av_opt_set_int(swr, "in_channel_layout", audio_dec_ctx->channel_layout, 0);
            av_opt_set_int(swr, "out_channel_layout", audio_dec_ctx->channel_layout, 0);
            av_opt_set_int(swr, "in_sample_rate", audio_dec_ctx->sample_rate, 0);
            av_opt_set_int(swr, "out_sample_rate", audio_dec_ctx->sample_rate, 0);
            av_opt_set_sample_fmt(swr, "in_sample_fmt", audio_dec_ctx->sample_fmt, 0);
            av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
            swr_init(swr);
        }
        //--
    }

    /* dump input information to stderr */
    av_dump_format(fmt_ctx, 0, src_filename, 0);

    if (!audio_stream && !video_stream) {
        fprintf(stderr, "Could not find audio or video stream in the input, aborting\n");
        ret = 1;
        goto end;
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate frame\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }

    pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "Could not allocate packet\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }

    if (video_stream)
        printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename);
    if (audio_stream)
        printf("Demuxing audio from file '%s' into '%s'\n", src_filename, audio_dst_filename);

    /* read frames from the file */
    while (av_read_frame(fmt_ctx, pkt) >= 0) {
        // check if the packet belongs to a stream we are interested in, otherwise
        // skip it
        if (pkt->stream_index == video_stream_idx)
            ret = decode_packet(video_dec_ctx, pkt);
        else if (pkt->stream_index == audio_stream_idx)
            ret = decode_packet(audio_dec_ctx, pkt);
        av_packet_unref(pkt);
        if (ret < 0)
            break;
    }

    /* flush the decoders */
    if (video_dec_ctx)
        decode_packet(video_dec_ctx, NULL);
    if (audio_dec_ctx)
        decode_packet(audio_dec_ctx, NULL);

    printf("Demuxing succeeded.\n");

    if (video_stream) {
        printf("Play the output video file with the command:\n"
            "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
            av_get_pix_fmt_name(pix_fmt), width, height,
            "");
    }

    if (audio_stream) {
        enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt;
        int n_channels = audio_dec_ctx->channels;
        const char* fmt;

        if (av_sample_fmt_is_planar(sfmt)) {
            const char* packed = av_get_sample_fmt_name(sfmt);
            printf("Warning: the sample format the decoder produced is planar "
                "(%s). This example will output the first channel only.\n",
                packed ? packed : "?");
            sfmt = av_get_packed_sample_fmt(sfmt);
            n_channels = 1;
        }
       
        if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0)
            goto end;

        printf("Play the output audio file with the command:\n"
            "ffplay -f %s -ac %d -ar %d %s\n",
            fmt, n_channels, audio_dec_ctx->sample_rate,
            "");

       
    }

end:
    avcodec_free_context(&video_dec_ctx);
    avcodec_free_context(&audio_dec_ctx);
    avformat_close_input(&fmt_ctx);
     /*
    if (video_dst_file)
        fclose(video_dst_file);  
   if (audio_dst_file)
        fclose(audio_dst_file);
  */
    av_packet_free(&pkt);
    av_frame_free(&frame);
    av_free(video_dst_data[0]);

    return ret < 0;
}
原文地址:https://www.cnblogs.com/cnchengv/p/15750389.html