#include "../../codec/codec.h" #include "../../codec/codecs.h" #ifdef CAMU_HAVE_FFMPEG #include "../../codec/ffmpeg/packet_ext.h" #endif #include "../vcr.h" #include "../client.h" #include "codec.h" static void data_callback(void *userdata, struct camu_codec_frame *frame) { struct lia_codec_client *codec = (struct lia_codec_client *)userdata; codec->handler.callback(codec->handler.userdata, LIANA_CLIENT_DATA, codec->handler.stream, frame); } static bool codec_client_init(struct lia_client_handler *handler, struct camu_renderer *renderer, struct camu_codec_stream *stream) { struct lia_codec_client *codec = (struct lia_codec_client *)handler; codec->handler.stream = stream; if (stream->type == CAMU_STREAM_AUDIO || stream->type == CAMU_STREAM_VIDEO) { codec->dec = stream->codec_info->create_decoder(); if (!codec->dec->init(codec->dec, renderer, stream, data_callback, codec)) { return false; } } return true; } #ifdef CAMU_HAVE_FFMPEG static bool push_av_packet(struct lia_codec_client *codec, AVPacket *pkt) { s32 ret = codec->dec->push_av_packet(codec->dec, pkt); while (ret == AVERROR(EAGAIN)) { codec->dec->process(codec->dec); ret = codec->dec->push_av_packet(codec->dec, pkt); } return ret == CAMU_OK; } static void passthrough_subtitle(struct lia_codec_client *codec, AVPacket *pkt) { struct camu_codec_stream *stream = codec->handler.stream; struct camu_codec_packet packet = { .av.pkt = pkt }; codec->handler.callback(codec->handler.userdata, LIANA_CLIENT_SUBTITLE, stream, &packet); } #endif static bool push_packet(struct lia_codec_client *codec, struct nn_buffer *buffer) { struct camu_codec_packet packet; packet.buffer = buffer; s32 ret = codec->dec->push(codec->dec, &packet); return ret == CAMU_OK; } static bool codec_client_handle_packet(struct lia_client_handler *handler, struct nn_packet *packet) { struct lia_codec_client *codec = (struct lia_codec_client *)handler; // NULL packet = flush. if (!packet) { if (codec->dec) { // Flush always returns success. switch (codec->dec->mode) { case CAMU_NORMAL: codec->dec->push(codec->dec, NULL); break; #ifdef CAMU_HAVE_FFMPEG case CAMU_FFMPEG_COMPAT: push_av_packet(codec, NULL); break; #endif } // process() could still error. s32 ret = codec->dec->process(codec->dec); codec->handler.callback(codec->handler.userdata, LIANA_CLIENT_EOF, codec->handler.stream, NULL); return ret == CAMU_ERR_EOF; } else { return true; } } // Push packet. u32 rindex = packet->rindex; bool success; u8 mode = nn_packet_read_u8(packet); // @TODO: default: shouldn't be a case. We should check for an invalid packet. switch (mode) { case CAMU_NORMAL: { if (codec->dec) { if (packet->opaque) { success = push_packet(codec, (struct nn_buffer *)packet->opaque); } else { struct nn_buffer buffer; nn_packet_read_buffer(packet, &buffer); success = push_packet(codec, &buffer); } } else { success = true; } break; } #ifdef CAMU_HAVE_FFMPEG case CAMU_FFMPEG_COMPAT: { AVPacket *pkt; if (packet->opaque) { pkt = (AVPacket *)packet->opaque; } else { pkt = av_packet_alloc(); nn_packet_read_av_packet(packet, pkt); packet->opaque = pkt; } if (codec->dec) { success = push_av_packet(codec, pkt); if (!success) { success = push_av_packet(codec, pkt); } } else { passthrough_subtitle(codec, pkt); success = true; } #ifdef VCR_BUFFER_WHOLE_FILE struct camu_codec_stream *stream = codec->handler.stream; AVRational time_base = stream->av.stream->time_base; pkt->pts += av_rescale_q(stream->duration, AV_TIME_BASE_Q, time_base); // @TODO: Shift av_packet_free() to vcr. This leaks right now. #else av_packet_free_side_data(pkt); av_packet_free(&pkt); #endif break; } #endif default: al_assert_and_return(false); } // Restore packet rindex in case we resue it. packet->rindex = rindex; if (!success) { codec->handler.callback(codec->handler.userdata, LIANA_CLIENT_ERRORED, codec->handler.stream, NULL); return false; } return true; } static void codec_client_flush(struct lia_client_handler *handler) { struct lia_codec_client *codec = (struct lia_codec_client *)handler; if (codec->dec) codec->dec->flush(codec->dec); } static void codec_client_free(struct lia_client_handler **handler) { struct lia_codec_client *codec = (struct lia_codec_client *)*handler; if (codec->dec) codec->dec->free(&codec->dec); al_free(codec); *handler = NULL; } struct lia_client_handler *lia_codec_client_create(void) { struct lia_codec_client *codec = al_alloc_object(struct lia_codec_client); codec->handler.init = codec_client_init; codec->handler.handle_packet = codec_client_handle_packet; codec->handler.flush = codec_client_flush; codec->handler.free = codec_client_free; return (struct lia_client_handler *)codec; }