mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 19:42:45 +00:00
165 lines
2.9 KiB
C
165 lines
2.9 KiB
C
#include <stdlib.h>
|
|
#include <libavcodec/avcodec.h>
|
|
#include <pthread.h>
|
|
#include "nv_avc_dec.h"
|
|
|
|
AVCodec* decoder;
|
|
AVCodecContext* decoder_ctx;
|
|
AVFrame* curr_picture;
|
|
AVFrame* frame;
|
|
pthread_mutex_t mutex;
|
|
|
|
// This function must be called before
|
|
// any other decoding functions
|
|
int nv_avc_init(int width, int height) {
|
|
int err;
|
|
|
|
pthread_mutex_init(&mutex, NULL);
|
|
|
|
decoder = avcodec_find_decoder(CODEC_ID_H264);
|
|
if (decoder == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
decoder_ctx = avcodec_alloc_context3(decoder);
|
|
if (decoder_ctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
curr_picture = av_frame_alloc();
|
|
if (curr_picture == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
frame = av_frame_alloc();
|
|
if (frame == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
// We don't have to send complete frames
|
|
if (decoder->capabilities & CODEC_CAP_TRUNCATED) {
|
|
decoder_ctx->flags |= CODEC_FLAG_TRUNCATED;
|
|
}
|
|
|
|
// We're a latency-sensitive application
|
|
decoder_ctx->flags |= CODEC_FLAG_LOW_DELAY;
|
|
|
|
decoder_ctx->width = width;
|
|
decoder_ctx->height = height;
|
|
|
|
err = avcodec_open2(decoder_ctx, decoder, NULL);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This function must be called after
|
|
// decoding is finished
|
|
void nv_avc_destroy(void) {
|
|
if (decoder_ctx) {
|
|
avcodec_close(decoder_ctx);
|
|
av_free(decoder_ctx);
|
|
decoder_ctx = NULL;
|
|
}
|
|
if (curr_picture) {
|
|
av_frame_free(&curr_picture);
|
|
curr_picture = NULL;
|
|
}
|
|
if (frame) {
|
|
av_frame_free(&frame);
|
|
frame = NULL;
|
|
}
|
|
}
|
|
|
|
// The decoded frame is YUV 4:2:0
|
|
// Returns 1 on success, 0 on failure
|
|
int nv_avc_get_current_frame(char* yuvframe, int size) {
|
|
int lu_size = decoder_ctx->width * decoder_ctx->height;
|
|
int ch_size = lu_size << 2;
|
|
|
|
if (size < nv_avc_get_frame_size())
|
|
return 0;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
memcpy(&yuvframe[0],
|
|
curr_picture->data[0],
|
|
lu_size);
|
|
memcpy(&yuvframe[lu_size],
|
|
curr_picture->data[1],
|
|
ch_size);
|
|
memcpy(&yuvframe[lu_size+ch_size],
|
|
curr_picture->data[2],
|
|
ch_size);
|
|
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int nv_avc_get_frame_size(void) {
|
|
int size = 0;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
// Luminance
|
|
size += decoder_ctx->width * decoder_ctx->height;
|
|
|
|
// Chrominance
|
|
size += (decoder_ctx->width * decoder_ctx->height) / 2;
|
|
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
return size;
|
|
}
|
|
|
|
// packets must be decoded in order
|
|
int nv_avc_decode(unsigned char* indata, int inlen) {
|
|
int err;
|
|
AVPacket pkt;
|
|
int got_pic;
|
|
|
|
err = av_new_packet(&pkt, inlen);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
memcpy(pkt.data, indata, inlen);
|
|
|
|
while (pkt.size > 0) {
|
|
err = avcodec_decode_video2(
|
|
decoder_ctx,
|
|
frame,
|
|
&got_pic,
|
|
&pkt);
|
|
if (err < 0) {
|
|
break;
|
|
}
|
|
|
|
if (got_pic) {
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
// Dereference the last frame
|
|
av_frame_unref(curr_picture);
|
|
|
|
// Reference the new frame
|
|
err = av_frame_ref(curr_picture, frame);
|
|
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
if (err < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
pkt.size -= err;
|
|
pkt.data += err;
|
|
}
|
|
|
|
av_free_packet(&pkt);
|
|
|
|
return err < 0 ? err : 0;
|
|
}
|