ffmpeg 简单播放器

版权声明:本文为博主原创文章,转载请标明出处! https://blog.csdn.net/qq_27396861/article/details/54565133

#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <libavutil/samplefmt.h>

struct AVContext{
  AVFormatContext* fc;
  AVCodecContext* acc;
  AVCodecContext* vcc;
  int audio_index;
  int video_index;

  struct SwsContext* sws;
  struct SwrContext* swr;

  SDL_Surface* wnd;
  SDL_Overlay* overlay;

  SDL_AudioSpec audio_spec;
  int width;
  int height;

  int fdRead;
  int fdWrite;

  AVPacket* packet;
  AVFrame* audio_frame;
  AVFrame* video_frame;
};

struct AVContext avc;

int doWrite(int fd, uint8_t* buf, int len)
{
    int index = 0;
    while (index != len)
    {
        int ret = write(fd, buf + index, len - index);
        if (ret > 0)
            index += ret;
        else if (ret <= 0)
            return -1;
    }
    return 0;
}

int doRead(int fd, uint8_t* buf, int len)
{
    int index = 0;
    while (index != len)
    {
        int ret = read(fd, buf + index, len - index);
        if (ret > 0)
            index += ret;
        else if (ret <= 0)
            return -1;
    }
}

void open_avfile(const char* filename)
{
    if (NULL == filename)
    {
        return;
    }
    avc.fc = NULL;
    if (avformat_open_input(&avc.fc, filename, NULL, NULL) != 0)
    {
        printf("open error\n");
        exit(1);
    }
}

void find_stream_info()
{
    avc.audio_index = -1;
    avc.video_index = -1;
    if (avformat_find_stream_info(avc.fc, NULL) != 0)
    {
        printf("find stream info error\n");
        exit(1);
    }
    int i;
    for (i = 0; i < avc.fc->nb_streams; ++i)
    {
        if (avc.fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            avc.audio_index = i;
        }
        else if (avc.fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            avc.video_index = i;
        }
    }
    if (avc.audio_index == -1 || avc.video_index == -1)
    {
        printf("cannot find audio or video stream info err\n");
        exit(1);
    }
}

void open_codec_context()
{
    avc.acc = NULL;
    avc.vcc = NULL;
    // video
    avc.vcc = avc.fc->streams[avc.video_index]->codec;
    avc.fc->video_codec = avcodec_find_decoder(avc.vcc->codec_id);
    if (avcodec_open2(avc.vcc, avc.fc->video_codec, NULL) != 0)
    {
        printf("open video codec err\n");
    }

    // audio
    avc.acc = avc.fc->streams[avc.audio_index]->codec;
    avc.fc->audio_codec = avcodec_find_decoder(avc.acc->codec_id);
    if (avcodec_open2(avc.acc, avc.fc->audio_codec, NULL) != 0)
    {
        printf("open audio codec err\n");
    }
}

void init_convert_context()
{
    avc.sws = NULL;
    avc.swr = NULL;

    avc.sws = sws_getContext(avc.vcc->width, avc.vcc->height, avc.vcc->pix_fmt,
                             avc.vcc->width, avc.vcc->height, avc.vcc->pix_fmt,
                             SWS_BICUBIC, NULL, NULL, NULL);

    avc.swr = swr_alloc_set_opts(NULL,
                                 avc.acc->channel_layout,
                                 AV_SAMPLE_FMT_S16,
                                 avc.acc->sample_rate,
                                 avc.acc->channel_layout,
                                 avc.acc->sample_fmt,
                                 avc.acc->sample_rate,
                                 0, NULL);

    swr_init(avc.swr);
}

void audio_callback(void* userdata, Uint8* stream, int len)
{
    doRead(avc.fdRead, stream, len);
}

void init_sdl()
{
    avc.width = avc.vcc->width;
    avc.height = avc.vcc->height;

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0)
    {
        printf("sdl init error\n");
        exit(1);
    }

    avc.wnd = SDL_SetVideoMode(avc.width, avc.height, 0, 0);

    if (avc.wnd == NULL)
    {
        printf("create window error\n");
        exit(1);
    }

    avc.overlay = SDL_CreateYUVOverlay(avc.width, avc.height, SDL_YV12_OVERLAY
                                       , avc.wnd);
    if (avc.overlay == NULL)
    {
        printf("create overlay error\n");
        exit(1);
    }

    // audio play
    avc.audio_spec.callback = audio_callback;
    avc.audio_spec.channels = avc.acc->channels;
    avc.audio_spec.format = AUDIO_S16SYS;
    avc.audio_spec.freq = avc.acc->sample_rate;
    avc.audio_spec.samples = 1024;
    avc.audio_spec.silence = 0;
    avc.audio_spec.userdata = NULL;
    if (SDL_OpenAudio(&avc.audio_spec, NULL) != 0)
    {
        printf("sdl open audio error\n");
        exit(1);
    }
    SDL_PauseAudio(0);

    int fd[2];
    pipe(fd); // 为了简便,用管道来控制每次读取的数据
    avc.fdRead = fd[0];
    avc.fdWrite = fd[1];
}

void process_audio_data()
{
    int got;
    avcodec_decode_audio4(avc.acc, avc.audio_frame, &got, avc.packet);
    if (got == 0)
        return;

    int bytesPerSample = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
    int frameSize = avc.acc->frame_size * avc.acc->channels * bytesPerSample;
    uint8_t* data = (uint8_t*)malloc(frameSize);

    swr_convert(avc.swr, &data, avc.acc->frame_size * 2,
                avc.audio_frame->extended_data, avc.acc->frame_size);

    doWrite(avc.fdWrite, data, frameSize);
    free(data);
}

void process_video_data()
{
    int got;
    avcodec_decode_video2(avc.vcc, avc.video_frame, &got, avc.packet);
    if (got == 0)
        return;
    SDL_LockYUVOverlay(avc.overlay);

    uint8_t* data[AV_NUM_DATA_POINTERS];
    int linesize[AV_NUM_DATA_POINTERS];

    data[0] = avc.overlay->pixels[0];
    data[1] = avc.overlay->pixels[1];
    data[2] = avc.overlay->pixels[2];
    linesize[0] = avc.overlay->pitches[0];
    linesize[1] = avc.overlay->pitches[1];
    linesize[2] = avc.overlay->pitches[2];

    sws_scale(avc.sws, avc.video_frame->data, avc.video_frame->linesize,
              0, avc.vcc->height,data, linesize);

    SDL_UnlockYUVOverlay(avc.overlay);

    SDL_Rect rect;
    rect.x = 0;
    rect.y = 0;
    rect.w = avc.wnd->w;
    rect.h = avc.wnd->h;
    SDL_DisplayYUVOverlay(avc.overlay, &rect);
}

void process_data()
{
    avc.packet = av_packet_alloc();
    avc.audio_frame = av_frame_alloc();
    avc.video_frame = av_frame_alloc();

    while (1)
    {
        if (av_read_frame(avc.fc, avc.packet) < 0)
        {
            break;
        }
        if (avc.packet->stream_index == avc.audio_index)
        {
            process_audio_data();
        }
        else if (avc.packet->stream_index == avc.video_index)
        {
            process_video_data();
        }
    }
}

int main()
{
    av_register_all();

    open_avfile("/home/tuser/test/test.mp4");

    find_stream_info();

    open_codec_context();

    init_convert_context();

    init_sdl();

    process_data();

    return 0;
}



Makefile:

play.out: play.c
gcc play.c -o play.out -pthread -lavdevice -lavfilter -lswscale -lpostproc -lavformat -lavcodec -lxcb-xfixes -lxcb-render -lxcb-shape -lxcb -lX11 -lasound -lSDL -lx264 -lpthread -ldl -lfaac -lz -lswresample -lavutil -lm 

猜你喜欢

转载自blog.csdn.net/qq_27396861/article/details/54565133