decoder.c 4.7 KB
Newer Older
R
Romain Vimont 已提交
1 2 3
#include "decoder.h"

#include <libavformat/avformat.h>
4
#include <SDL2/SDL_events.h>
R
Romain Vimont 已提交
5 6 7 8
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include <unistd.h>

9
#include "config.h"
R
Romain Vimont 已提交
10 11 12
#include "events.h"
#include "frames.h"
#include "lockutil.h"
R
Romain Vimont 已提交
13
#include "log.h"
R
Romain Vimont 已提交
14 15 16 17 18

#define BUFSIZE 0x10000

static int read_packet(void *opaque, uint8_t *buf, int buf_size) {
    struct decoder *decoder = opaque;
19
    return net_recv(decoder->video_socket, buf, buf_size);
R
Romain Vimont 已提交
20 21 22 23
}

// set the decoded frame as ready for rendering, and notify
static void push_frame(struct decoder *decoder) {
24 25 26 27
    SDL_bool previous_frame_consumed = frames_offer_decoded_frame(decoder->frames);
    if (!previous_frame_consumed) {
        // the previous EVENT_NEW_FRAME will consume this frame
        return;
28
    }
R
Romain Vimont 已提交
29 30 31 32 33 34
    static SDL_Event new_frame_event = {
        .type = EVENT_NEW_FRAME,
    };
    SDL_PushEvent(&new_frame_event);
}

R
Romain Vimont 已提交
35 36 37 38 39 40
static void notify_stopped(void) {
    SDL_Event stop_event;
    stop_event.type = EVENT_DECODER_STOPPED;
    SDL_PushEvent(&stop_event);
}

R
Romain Vimont 已提交
41 42 43 44 45
static int run_decoder(void *data) {
    struct decoder *decoder = data;

    AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
R
Romain Vimont 已提交
46
        LOGE("H.264 decoder not found");
47
        goto run_end;
R
Romain Vimont 已提交
48 49 50 51
    }

    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
R
Romain Vimont 已提交
52
        LOGC("Could not allocate decoder context");
53
        goto run_end;
R
Romain Vimont 已提交
54 55 56
    }

    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
R
Romain Vimont 已提交
57
        LOGE("Could not open H.264 codec");
R
Romain Vimont 已提交
58 59 60 61 62
        goto run_finally_free_codec_ctx;
    }

    AVFormatContext *format_ctx = avformat_alloc_context();
    if (!format_ctx) {
R
Romain Vimont 已提交
63
        LOGC("Could not allocate format context");
R
Romain Vimont 已提交
64 65 66 67 68
        goto run_finally_close_codec;
    }

    unsigned char *buffer = av_malloc(BUFSIZE);
    if (!buffer) {
R
Romain Vimont 已提交
69
        LOGC("Could not allocate buffer");
R
Romain Vimont 已提交
70 71 72 73 74
        goto run_finally_free_format_ctx;
    }

    AVIOContext *avio_ctx = avio_alloc_context(buffer, BUFSIZE, 0, decoder, read_packet, NULL, NULL);
    if (!avio_ctx) {
R
Romain Vimont 已提交
75
        LOGC("Could not allocate avio context");
R
Romain Vimont 已提交
76 77 78 79 80 81 82 83 84
        // avformat_open_input takes ownership of 'buffer'
        // so only free the buffer before avformat_open_input()
        av_free(buffer);
        goto run_finally_free_format_ctx;
    }

    format_ctx->pb = avio_ctx;

    if (avformat_open_input(&format_ctx, NULL, NULL, NULL) < 0) {
R
Romain Vimont 已提交
85
        LOGE("Could not open video stream");
R
Romain Vimont 已提交
86 87 88 89 90 91 92 93
        goto run_finally_free_avio_ctx;
    }

    AVPacket packet;
    av_init_packet(&packet);
    packet.data = NULL;
    packet.size = 0;

94
    while (!av_read_frame(format_ctx, &packet) && !avio_ctx->eof_reached) {
R
Romain Vimont 已提交
95 96
// the new decoding/encoding API has been introduced by:
// <http://git.videolan.org/?p=ffmpeg.git;a=commitdiff;h=7fc329e2dd6226dfecaa4a1d7adf353bf2773726>
97 98 99 100 101 102
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0)
        int ret;
        if ((ret = avcodec_send_packet(codec_ctx, &packet)) < 0) {
            LOGE("Could not send video packet: %d", ret);
            goto run_quit;
        }
R
Romain Vimont 已提交
103 104 105 106 107
        ret = avcodec_receive_frame(codec_ctx, decoder->frames->decoding_frame);
        if (!ret) {
            // a frame was received
            push_frame(decoder);
        } else if (ret != AVERROR(EAGAIN)) {
108 109 110 111
            LOGE("Could not receive video frame: %d", ret);
            goto run_quit;
        }
#else
R
Romain Vimont 已提交
112 113 114 115
        while (packet.size > 0) {
            int got_picture;
            int len = avcodec_decode_video2(codec_ctx, decoder->frames->decoding_frame, &got_picture, &packet);
            if (len < 0) {
R
Romain Vimont 已提交
116
                LOGE("Could not decode video packet: %d", len);
R
Romain Vimont 已提交
117 118 119 120 121 122 123 124 125 126 127
                goto run_quit;
            }
            if (got_picture) {
                push_frame(decoder);
            }
            packet.size -= len;
            packet.data += len;
        }
#endif
    }

R
Romain Vimont 已提交
128
    LOGD("End of frames");
R
Romain Vimont 已提交
129 130 131 132 133 134 135 136 137 138 139

run_quit:
    avformat_close_input(&format_ctx);
run_finally_free_avio_ctx:
    av_freep(&avio_ctx);
run_finally_free_format_ctx:
    avformat_free_context(format_ctx);
run_finally_close_codec:
    avcodec_close(codec_ctx);
run_finally_free_codec_ctx:
    avcodec_free_context(&codec_ctx);
R
Romain Vimont 已提交
140
    notify_stopped();
141 142
run_end:
    return 0;
R
Romain Vimont 已提交
143 144
}

145
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket) {
R
Romain Vimont 已提交
146 147 148 149
    decoder->frames = frames;
    decoder->video_socket = video_socket;
}

150
SDL_bool decoder_start(struct decoder *decoder) {
R
Romain Vimont 已提交
151
    LOGD("Starting decoder thread");
R
Romain Vimont 已提交
152 153 154

    decoder->thread = SDL_CreateThread(run_decoder, "video_decoder", decoder);
    if (!decoder->thread) {
R
Romain Vimont 已提交
155
        LOGC("Could not start decoder thread");
156
        return SDL_FALSE;
R
Romain Vimont 已提交
157 158
    }

159
    return SDL_TRUE;
R
Romain Vimont 已提交
160 161
}

162 163 164 165
void decoder_stop(struct decoder *decoder) {
    frames_stop(decoder->frames);
}

R
Romain Vimont 已提交
166 167 168
void decoder_join(struct decoder *decoder) {
    SDL_WaitThread(decoder->thread, NULL);
}