summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Burakov <mburakov@mailbox.org>2023-04-02 20:41:20 +0200
committerMikhail Burakov <mburakov@mailbox.org>2023-04-07 13:48:02 +0200
commit20ed57016563c10157093ed3785f17b5ce27fdca (patch)
tree73346b7a4b0ce66bf1d4b651f714fb1a315d247f
parente59239d9eb7a48844104b2fbbcb96c069204950a (diff)
Major rework of decoder implementation
-rw-r--r--decode.c567
-rw-r--r--decode.h2
-rw-r--r--frame.c68
-rw-r--r--frame.h6
-rw-r--r--main.c12
-rw-r--r--makefile3
-rw-r--r--util.h44
-rw-r--r--window.c2
8 files changed, 292 insertions, 412 deletions
diff --git a/decode.c b/decode.c
index 5df0806..f58538f 100644
--- a/decode.c
+++ b/decode.c
@@ -19,121 +19,157 @@
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
#include <mfxvideo.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
#include <unistd.h>
#include <va/va.h>
#include <va/va_drm.h>
#include <va/va_drmcommon.h>
#include "frame.h"
-#include "util.h"
+#include "toolbox/buffer.h"
+#include "toolbox/utils.h"
#include "window.h"
struct Surface {
- VASurfaceID surface_id;
- mfxFrameInfo frame_info;
- struct Frame* frame;
+ mfxFrameInfo mfx_frame_info;
+ VASurfaceID va_surface_id;
+ int dmabuf_fds[4];
bool locked;
};
-struct TimingStats {
- unsigned long long min;
- unsigned long long max;
- unsigned long long sum;
-};
-
struct DecodeContext {
struct Window* window;
- int drm_fd;
- VADisplay display;
- mfxSession session;
mfxFrameAllocator allocator;
- bool initialized;
-
- uint32_t packet_size;
- uint8_t* packet_data;
- uint32_t packet_alloc;
- uint32_t packet_offset;
- struct Surface** sufaces;
-
- unsigned long long recording_started;
- unsigned long long frame_header_ts;
- unsigned long long frame_received_ts;
- unsigned long long frame_decoded_ts;
- unsigned long long frame_counter;
- unsigned long long bitstream;
-
- struct TimingStats receive;
- struct TimingStats decode;
- struct TimingStats total;
-};
-static void TimingStatsReset(struct TimingStats* timing_stats) {
- *timing_stats = (struct TimingStats){.min = ULLONG_MAX};
-}
+ int drm_fd;
+ VADisplay va_display;
+ mfxSession mfx_session;
-static void TimingStatsRecord(struct TimingStats* timing_stats,
- unsigned long long value) {
- timing_stats->min = MIN(timing_stats->min, value);
- timing_stats->max = MAX(timing_stats->max, value);
- timing_stats->sum += value;
-}
+ struct Buffer buffer;
+ struct Surface** surfaces;
+};
-static void TimingStatsLog(const struct TimingStats* timing_stats,
- const char* name, unsigned long long counter) {
- LOG("%s min/avg/max: %llu/%llu/%llu", name, timing_stats->min,
- timing_stats->sum / counter, timing_stats->max);
+static const char* VaStatusString(VAStatus status) {
+ static const char* va_status_strings[] = {
+ "VA_STATUS_SUCCESS",
+ "VA_STATUS_ERROR_OPERATION_FAILED",
+ "VA_STATUS_ERROR_ALLOCATION_FAILED",
+ "VA_STATUS_ERROR_INVALID_DISPLAY",
+ "VA_STATUS_ERROR_INVALID_CONFIG",
+ "VA_STATUS_ERROR_INVALID_CONTEXT",
+ "VA_STATUS_ERROR_INVALID_SURFACE",
+ "VA_STATUS_ERROR_INVALID_BUFFER",
+ "VA_STATUS_ERROR_INVALID_IMAGE",
+ "VA_STATUS_ERROR_INVALID_SUBPICTURE",
+ "VA_STATUS_ERROR_ATTR_NOT_SUPPORTED",
+ "VA_STATUS_ERROR_MAX_NUM_EXCEEDED",
+ "VA_STATUS_ERROR_UNSUPPORTED_PROFILE",
+ "VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT",
+ "VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT",
+ "VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE",
+ "VA_STATUS_ERROR_SURFACE_BUSY",
+ "VA_STATUS_ERROR_FLAG_NOT_SUPPORTED",
+ "VA_STATUS_ERROR_INVALID_PARAMETER",
+ "VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED",
+ "VA_STATUS_ERROR_UNIMPLEMENTED",
+ "VA_STATUS_ERROR_SURFACE_IN_DISPLAYING",
+ "VA_STATUS_ERROR_INVALID_IMAGE_FORMAT",
+ "VA_STATUS_ERROR_DECODING_ERROR",
+ "VA_STATUS_ERROR_ENCODING_ERROR",
+ "VA_STATUS_ERROR_INVALID_VALUE",
+ "???",
+ "???",
+ "???",
+ "???",
+ "???",
+ "???",
+ "VA_STATUS_ERROR_UNSUPPORTED_FILTER",
+ "VA_STATUS_ERROR_INVALID_FILTER_CHAIN",
+ "VA_STATUS_ERROR_HW_BUSY",
+ "???",
+ "VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE",
+ "VA_STATUS_ERROR_NOT_ENOUGH_BUFFER",
+ "VA_STATUS_ERROR_TIMEDOUT",
+ };
+ return (VA_STATUS_SUCCESS <= status && status <= VA_STATUS_ERROR_TIMEDOUT)
+ ? va_status_strings[status - VA_STATUS_SUCCESS]
+ : "???";
}
-static unsigned long long MicrosNow(void) {
- struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return (unsigned long long)ts.tv_sec * 1000000ull +
- (unsigned long long)ts.tv_nsec / 1000ull;
-}
+static struct Surface* SurfaceCreate(const mfxFrameInfo* mfx_frame_info,
+ VADisplay va_display,
+ struct Frame* out_frame) {
+ struct Surface* surface = malloc(sizeof(struct Surface));
+ if (!surface) {
+ LOG("Failed to allocate surface (%s)", strerror(errno));
+ return NULL;
+ }
+ *surface = (struct Surface){
+ .mfx_frame_info = *mfx_frame_info,
+ .dmabuf_fds = {-1, -1, -1, -1},
+ };
-static void SurfaceDestroy(struct Surface*** psurfaces) {
- if (!psurfaces || !*psurfaces) return;
- for (struct Surface** surfaces = *psurfaces; *surfaces; surfaces++) {
- if ((*surfaces)->frame) FrameDestroy(&(*surfaces)->frame);
- free(*surfaces);
+ VASurfaceAttrib attrib_list[] = {
+ {.type = VASurfaceAttribPixelFormat,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_FOURCC_NV12},
+ {.type = VASurfaceAttribUsageHint,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER |
+ VA_SURFACE_ATTRIB_USAGE_HINT_EXPORT},
+ };
+ VAStatus va_status =
+ vaCreateSurfaces(va_display, VA_RT_FORMAT_YUV420, mfx_frame_info->Width,
+ mfx_frame_info->Height, &surface->va_surface_id, 1,
+ attrib_list, LENGTH(attrib_list));
+ if (va_status != VA_STATUS_SUCCESS) {
+ LOG("Failed to create vaapi surface (%s)", VaStatusString(va_status));
+ goto rollback_surface;
}
- free(*psurfaces);
- *psurfaces = NULL;
-}
-static struct Frame* ExportFrame(VADisplay display, VASurfaceID surface_id) {
VADRMPRIMESurfaceDescriptor prime;
- VAStatus status = vaExportSurfaceHandle(
- display, surface_id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
- VA_EXPORT_SURFACE_WRITE_ONLY | VA_EXPORT_SURFACE_COMPOSED_LAYERS, &prime);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to export vaapi surface (%d)", status);
- return NULL;
+ va_status = vaExportSurfaceHandle(
+ va_display, surface->va_surface_id,
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
+ VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_COMPOSED_LAYERS, &prime);
+ if (va_status != VA_STATUS_SUCCESS) {
+ LOG("Failed to export vaapi surface (%s)", VaStatusString(va_status));
+ goto rollback_va_surface_id;
}
- struct FramePlane planes[prime.layers[0].num_planes];
- for (size_t i = 0; i < LENGTH(planes); i++) {
- planes[i] = (struct FramePlane){
- .dmabuf_fd = prime.objects[prime.layers[0].object_index[i]].fd,
+ out_frame->width = prime.width;
+ out_frame->height = prime.height;
+ out_frame->fourcc = prime.fourcc;
+ out_frame->nplanes = prime.layers[0].num_planes;
+ for (uint32_t i = 0; i < prime.layers[0].num_planes; i++) {
+ surface->dmabuf_fds[i] = prime.objects[prime.layers[0].object_index[i]].fd;
+ out_frame->planes[i] = (struct FramePlane){
+ .dmabuf_fd = surface->dmabuf_fds[i],
.pitch = prime.layers[0].pitch[i],
.offset = prime.layers[0].offset[i],
.modifier =
prime.objects[prime.layers[0].object_index[i]].drm_format_modifier,
};
}
+ return surface;
- struct Frame* frame = FrameCreate(prime.width, prime.height, prime.fourcc,
- prime.layers[0].num_planes, planes);
- if (!frame) LOG("Failed to create frame");
- for (size_t i = prime.num_objects; i; i--) close(prime.objects[i - 1].fd);
- return frame;
+rollback_va_surface_id:
+ vaDestroySurfaces(va_display, &surface->va_surface_id, 1);
+rollback_surface:
+ free(surface);
+ return NULL;
+}
+
+static void SurfaceDestroy(struct Surface* surface, VADisplay va_display) {
+ for (size_t i = LENGTH(surface->dmabuf_fds); i; i--) {
+ if (surface->dmabuf_fds[i - 1] != -1) close(surface->dmabuf_fds[i - 1]);
+ }
+ vaDestroySurfaces(va_display, &surface->va_surface_id, 1);
+ free(surface);
}
static mfxStatus OnAllocatorAlloc(mfxHDL pthis, mfxFrameAllocRequest* request,
@@ -150,102 +186,116 @@ static mfxStatus OnAllocatorAlloc(mfxHDL pthis, mfxFrameAllocRequest* request,
return MFX_ERR_UNSUPPORTED;
}
- struct AUTO(Surface)** surfaces =
+ struct DecodeContext* decode_context = pthis;
+ decode_context->surfaces =
calloc(request->NumFrameSuggested + 1, sizeof(struct Surface*));
- if (!surfaces) {
+ if (!decode_context->surfaces) {
LOG("Failed to allocate surfaces storage (%s)", strerror(errno));
return MFX_ERR_MEMORY_ALLOC;
}
- for (size_t i = 0; i < request->NumFrameSuggested; i++) {
- surfaces[i] = calloc(1, sizeof(struct Surface));
- if (!surfaces[i]) {
- LOG("Failed to allocate surface (%s)", strerror(errno));
- return MFX_ERR_MEMORY_ALLOC;
- }
- }
-
- VASurfaceID surface_ids[request->NumFrameSuggested];
- struct DecodeContext* decode_context = pthis;
- VASurfaceAttrib attrib_list[] = {
- {.type = VASurfaceAttribPixelFormat,
- .value.type = VAGenericValueTypeInteger,
- .value.value.i = VA_FOURCC_NV12},
- {.type = VASurfaceAttribUsageHint,
- .value.type = VAGenericValueTypeInteger,
- .value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER |
- VA_SURFACE_ATTRIB_USAGE_HINT_EXPORT},
- };
- VAStatus status = vaCreateSurfaces(
- decode_context->display, VA_RT_FORMAT_YUV420, request->Info.Width,
- request->Info.Height, surface_ids, request->NumFrameSuggested,
- attrib_list, LENGTH(attrib_list));
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to allocate surfaces (%d)", status);
- return MFX_ERR_MEMORY_ALLOC;
- }
-
- for (size_t i = 0; i < request->NumFrameSuggested; i++) {
- surfaces[i]->surface_id = surface_ids[i];
- surfaces[i]->frame_info = request->Info;
- }
- // mburakov: Separate loop for frames to ensure proper cleanup in destructor.
struct Frame frames[request->NumFrameSuggested];
for (size_t i = 0; i < request->NumFrameSuggested; i++) {
- surfaces[i]->frame =
- ExportFrame(decode_context->display, surfaces[i]->surface_id);
- if (!surfaces[i]->frame) {
- LOG("Failed to export frame");
- return MFX_ERR_MEMORY_ALLOC;
+ decode_context->surfaces[i] =
+ SurfaceCreate(&request->Info, decode_context->va_display, &frames[i]);
+ if (!decode_context->surfaces[i]) {
+ LOG("Failed to create surface");
+ goto rollback_surfaces;
}
- frames[i] = *surfaces[i]->frame;
}
+
if (!WindowAssignFrames(decode_context->window, request->NumFrameSuggested,
frames)) {
LOG("Failed to assign frames to window");
- return MFX_ERR_MEMORY_ALLOC;
+ goto rollback_surfaces;
}
- decode_context->sufaces = RELEASE(surfaces);
*response = (mfxFrameAllocResponse){
.AllocId = request->AllocId,
- .mids = (void**)decode_context->sufaces,
+ .mids = (void**)decode_context->surfaces,
.NumFrameActual = request->NumFrameSuggested,
};
return MFX_ERR_NONE;
+
+rollback_surfaces:
+ for (size_t i = request->NumFrameSuggested; i; i--) {
+ if (decode_context->surfaces[i - 1])
+ SurfaceDestroy(decode_context->surfaces[i - 1],
+ decode_context->va_display);
+ }
+ free(decode_context->surfaces);
+ return MFX_ERR_MEMORY_ALLOC;
}
static mfxStatus OnAllocatorGetHDL(mfxHDL pthis, mfxMemId mid, mfxHDL* handle) {
(void)pthis;
struct Surface* surface = mid;
- *handle = &surface->surface_id;
+ *handle = &surface->va_surface_id;
return MFX_ERR_NONE;
}
static mfxStatus OnAllocatorFree(mfxHDL pthis,
mfxFrameAllocResponse* response) {
LOG("%s(AllocId=%u)", __func__, response->AllocId);
- VASurfaceID surface_ids[response->NumFrameActual];
- struct Surface** surfaces = (struct Surface**)response->mids;
- for (size_t i = 0; i < response->NumFrameActual; i++)
- surface_ids[i] = surfaces[i]->surface_id;
struct DecodeContext* decode_context = pthis;
- vaDestroySurfaces(decode_context->display, surface_ids,
- response->NumFrameActual);
- SurfaceDestroy(&decode_context->sufaces);
+ for (size_t i = response->NumFrameActual; i; i--)
+ SurfaceDestroy(decode_context->surfaces[i - 1], decode_context->va_display);
+ free(decode_context->surfaces);
return MFX_ERR_NONE;
}
+static const char* MfxStatusString(mfxStatus status) {
+ static const char* mfx_status_strings[] = {
+ "MFX_ERR_REALLOC_SURFACE",
+ "MFX_ERR_GPU_HANG",
+ "MFX_ERR_INVALID_AUDIO_PARAM",
+ "MFX_ERR_INCOMPATIBLE_AUDIO_PARAM",
+ "MFX_ERR_MORE_BITSTREAM",
+ "MFX_ERR_DEVICE_FAILED",
+ "MFX_ERR_UNDEFINED_BEHAVIOR",
+ "MFX_ERR_INVALID_VIDEO_PARAM",
+ "MFX_ERR_INCOMPATIBLE_VIDEO_PARAM",
+ "MFX_ERR_DEVICE_LOST",
+ "MFX_ERR_ABORTED",
+ "MFX_ERR_MORE_SURFACE",
+ "MFX_ERR_MORE_DATA",
+ "MFX_ERR_NOT_FOUND",
+ "MFX_ERR_NOT_INITIALIZED",
+ "MFX_ERR_LOCK_MEMORY",
+ "MFX_ERR_INVALID_HANDLE",
+ "MFX_ERR_NOT_ENOUGH_BUFFER",
+ "MFX_ERR_MEMORY_ALLOC",
+ "MFX_ERR_UNSUPPORTED",
+ "MFX_ERR_NULL_PTR",
+ "MFX_ERR_UNKNOWN",
+ "MFX_ERR_NONE",
+ "MFX_WRN_IN_EXECUTION",
+ "MFX_WRN_DEVICE_BUSY",
+ "MFX_WRN_VIDEO_PARAM_CHANGED",
+ "MFX_WRN_PARTIAL_ACCELERATION",
+ "MFX_WRN_INCOMPATIBLE_VIDEO_PARAM",
+ "MFX_WRN_VALUE_NOT_CHANGED",
+ "MFX_WRN_OUT_OF_RANGE",
+ "MFX_TASK_WORKING",
+ "MFX_TASK_BUSY",
+ "MFX_WRN_FILTER_SKIPPED",
+ "MFX_WRN_INCOMPATIBLE_AUDIO_PARAM",
+ "MFX_ERR_NONE_PARTIAL_OUTPUT",
+ };
+ return (MFX_ERR_REALLOC_SURFACE <= status &&
+ status <= MFX_ERR_NONE_PARTIAL_OUTPUT)
+ ? mfx_status_strings[status - MFX_ERR_REALLOC_SURFACE]
+ : "???";
+}
+
struct DecodeContext* DecodeContextCreate(struct Window* window) {
- struct AUTO(DecodeContext)* decode_context =
- malloc(sizeof(struct DecodeContext));
+ struct DecodeContext* decode_context = malloc(sizeof(struct DecodeContext));
if (!decode_context) {
LOG("Failed to allocate decode context (%s)", strerror(errno));
return NULL;
}
*decode_context = (struct DecodeContext){
.window = window,
- .drm_fd = -1,
.allocator.pthis = decode_context,
.allocator.Alloc = OnAllocatorAlloc,
.allocator.GetHDL = OnAllocatorGetHDL,
@@ -255,45 +305,52 @@ struct DecodeContext* DecodeContextCreate(struct Window* window) {
decode_context->drm_fd = open("/dev/dri/renderD128", O_RDWR);
if (decode_context->drm_fd == -1) {
LOG("Failed to open render node (%s)", strerror(errno));
- return NULL;
+ goto rollback_decode_context;
}
- decode_context->display = vaGetDisplayDRM(decode_context->drm_fd);
- if (!decode_context->display) {
+ decode_context->va_display = vaGetDisplayDRM(decode_context->drm_fd);
+ if (!decode_context->va_display) {
LOG("Failed to get vaapi display (%s)", strerror(errno));
- return NULL;
+ goto rollback_drm_fd;
}
int major, minor;
- VAStatus st = vaInitialize(decode_context->display, &major, &minor);
- if (st != VA_STATUS_SUCCESS) {
- LOG("Failed to init vaapi (%d)", st);
- return NULL;
+ VAStatus va_status = vaInitialize(decode_context->va_display, &major, &minor);
+ if (va_status != VA_STATUS_SUCCESS) {
+ LOG("Failed to init vaapi (%s)", VaStatusString(va_status));
+ goto rollback_display;
}
LOG("Initialized vaapi %d.%d", major, minor);
- mfxStatus status = MFXInit(MFX_IMPL_HARDWARE, NULL, &decode_context->session);
- if (status != MFX_ERR_NONE) {
- LOG("Failed to init mfx session (%d)", status);
- return NULL;
+ mfxStatus mfx_status =
+ MFXInit(MFX_IMPL_HARDWARE, NULL, &decode_context->mfx_session);
+ if (mfx_status != MFX_ERR_NONE) {
+ LOG("Failed to init mfx session (%s)", MfxStatusString(mfx_status));
+ goto rollback_display;
}
- status = MFXVideoCORE_SetHandle(
- decode_context->session, MFX_HANDLE_VA_DISPLAY, decode_context->display);
- if (status != MFX_ERR_NONE) {
- LOG("Failed to set mfx session display (%d)", status);
- return NULL;
+ mfx_status =
+ MFXVideoCORE_SetHandle(decode_context->mfx_session, MFX_HANDLE_VA_DISPLAY,
+ decode_context->va_display);
+ if (mfx_status != MFX_ERR_NONE) {
+ LOG("Failed to set mfx session display (%s)", MfxStatusString(mfx_status));
+ goto rollback_session;
}
- status = MFXVideoCORE_SetFrameAllocator(decode_context->session,
- &decode_context->allocator);
- if (status != MFX_ERR_NONE) {
- LOG("Failed to set frame allocator (%d)", status);
- return NULL;
+ mfx_status = MFXVideoCORE_SetFrameAllocator(decode_context->mfx_session,
+ &decode_context->allocator);
+ if (mfx_status != MFX_ERR_NONE) {
+ LOG("Failed to set frame allocator (%s)", MfxStatusString(mfx_status));
+ goto rollback_session;
}
-
- decode_context->recording_started = MicrosNow();
- TimingStatsReset(&decode_context->receive);
- TimingStatsReset(&decode_context->decode);
- TimingStatsReset(&decode_context->total);
- return RELEASE(decode_context);
+ return decode_context;
+
+rollback_session:
+ MFXClose(decode_context->mfx_session);
+rollback_display:
+ vaTerminate(decode_context->va_display);
+rollback_drm_fd:
+ close(decode_context->drm_fd);
+rollback_decode_context:
+ free(decode_context);
+ return NULL;
}
static bool InitializeDecoder(struct DecodeContext* decode_context,
@@ -301,85 +358,38 @@ static bool InitializeDecoder(struct DecodeContext* decode_context,
mfxVideoParam video_param = {
.mfx.CodecId = MFX_CODEC_HEVC,
};
- mfxStatus status = MFXVideoDECODE_DecodeHeader(decode_context->session,
- bitstream, &video_param);
- switch (status) {
+ mfxStatus mfx_status = MFXVideoDECODE_DecodeHeader(
+ decode_context->mfx_session, bitstream, &video_param);
+ switch (mfx_status) {
case MFX_ERR_NONE:
break;
case MFX_ERR_MORE_DATA:
return true;
default:
- LOG("Failed to parse decode header (%d)", status);
+ LOG("Failed to decode header (%s)", MfxStatusString(mfx_status));
return false;
}
video_param.AsyncDepth = 1;
video_param.mfx.DecodedOrder = 1;
video_param.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
- status =
- MFXVideoDECODE_Query(decode_context->session, &video_param, &video_param);
- if (status != MFX_ERR_NONE) {
- LOG("Failed to query decode (%d)", status);
+ mfx_status = MFXVideoDECODE_Query(decode_context->mfx_session, &video_param,
+ &video_param);
+ if (mfx_status != MFX_ERR_NONE) {
+ LOG("Failed to query decode (%s)", MfxStatusString(mfx_status));
return false;
}
- status = MFXVideoDECODE_Init(decode_context->session, &video_param);
- if (status != MFX_ERR_NONE) {
- LOG("Failed to init decode (%d)", status);
+ mfx_status = MFXVideoDECODE_Init(decode_context->mfx_session, &video_param);
+ if (mfx_status != MFX_ERR_NONE) {
+ LOG("Failed to init decode (%s)", MfxStatusString(mfx_status));
return false;
}
- decode_context->initialized = true;
- return true;
-}
-
-static bool ReadSomePacketData(struct DecodeContext* decode_context, int fd) {
-again:;
- void* target;
- size_t size;
- if (!decode_context->packet_size) {
- target = &decode_context->packet_size;
- size = sizeof(decode_context->packet_size);
- decode_context->frame_header_ts = MicrosNow();
- } else {
- target = decode_context->packet_data + decode_context->packet_offset;
- size = decode_context->packet_size - decode_context->packet_offset;
- }
- ssize_t result = read(fd, target, size);
- switch (result) {
- case -1:
- if (errno == EINTR) goto again;
- LOG("Failed to read packet data (%s)", strerror(errno));
- return false;
- case 0:
- LOG("File descriptor was closed");
- return false;
- default:
- break;
- }
- if (target != &decode_context->packet_size) {
- decode_context->packet_offset += result;
- return true;
- }
- if (result != (ssize_t)size) {
- LOG("Failed to read complete packet size");
- return false;
- }
- if (decode_context->packet_size > decode_context->packet_alloc) {
- uint32_t packet_alloc = decode_context->packet_size;
- uint8_t* packet_data = malloc(packet_alloc);
- if (!packet_data) {
- LOG("Failed to reallocate packet data (%s)", strerror(errno));
- return false;
- }
- free(decode_context->packet_data);
- decode_context->packet_data = packet_data;
- decode_context->packet_alloc = packet_alloc;
- }
return true;
}
static struct Surface* GetFreeSurface(struct DecodeContext* decode_context) {
- struct Surface** psurface = decode_context->sufaces;
+ struct Surface** psurface = decode_context->surfaces;
for (; *psurface && (*psurface)->locked; psurface++)
;
(*psurface)->locked = true;
@@ -389,9 +399,9 @@ static struct Surface* GetFreeSurface(struct DecodeContext* decode_context) {
static size_t UnlockAllSurfaces(struct DecodeContext* decode_context,
const struct Surface* keep_locked) {
size_t result = 0;
- for (size_t i = 0; decode_context->sufaces[i]; i++) {
- if (decode_context->sufaces[i] != keep_locked) {
- decode_context->sufaces[i]->locked = false;
+ for (size_t i = 0; decode_context->surfaces[i]; i++) {
+ if (decode_context->surfaces[i] != keep_locked) {
+ decode_context->surfaces[i]->locked = false;
} else {
result = i;
}
@@ -399,84 +409,61 @@ static size_t UnlockAllSurfaces(struct DecodeContext* decode_context,
return result;
}
-static void HandleTimingStats(struct DecodeContext* decode_context) {
- TimingStatsRecord(
- &decode_context->receive,
- decode_context->frame_received_ts - decode_context->frame_header_ts);
- TimingStatsRecord(
- &decode_context->decode,
- decode_context->frame_decoded_ts - decode_context->frame_received_ts);
- TimingStatsRecord(
- &decode_context->total,
- decode_context->frame_decoded_ts - decode_context->frame_header_ts);
-
- unsigned long long period =
- decode_context->frame_decoded_ts - decode_context->recording_started;
- static const unsigned long long second = 1000000;
- if (period < 10 * second) return;
-
- LOG("---->8-------->8-------->8----");
- TimingStatsLog(&decode_context->receive, "Receive",
- decode_context->frame_counter);
- TimingStatsLog(&decode_context->decode, "Decode",
- decode_context->frame_counter);
- TimingStatsLog(&decode_context->total, "Total",
- decode_context->frame_counter);
- LOG("Framerate: %llu fps", decode_context->frame_counter * second / period);
- LOG("Bitstream: %llu Kbps",
- decode_context->bitstream * second * 8 / period / 1024);
- decode_context->recording_started = decode_context->frame_decoded_ts;
- TimingStatsReset(&decode_context->receive);
- TimingStatsReset(&decode_context->decode);
- TimingStatsReset(&decode_context->total);
- decode_context->frame_counter = 0;
- decode_context->bitstream = 0;
-}
-
bool DecodeContextDecode(struct DecodeContext* decode_context, int fd) {
- if (!ReadSomePacketData(decode_context, fd)) {
- LOG("Failed to read some packet data");
- return false;
+ switch (BufferAppendFrom(&decode_context->buffer, fd)) {
+ case -1:
+ LOG("Failed to append packet data to buffer (%s)", strerror(errno));
+ return false;
+ case 0:
+ LOG("Server closed connection");
+ return false;
+ default:
+ break;
}
- if (decode_context->packet_size != decode_context->packet_offset) {
- // mburakov: Full frame has to be available for decoding.
+again:
+ if (decode_context->buffer.size < sizeof(uint32_t)) {
+ // mburakov: Packet size is not yet available.
return true;
}
+ uint32_t packet_size = *(uint32_t*)decode_context->buffer.data;
+ if (decode_context->buffer.size < sizeof(uint32_t) + packet_size) {
+ // mburakov: Full packet is not yet available.
+ return true;
+ }
+
mfxBitstream bitstream = {
.DecodeTimeStamp = MFX_TIMESTAMP_UNKNOWN,
.TimeStamp = (mfxU64)MFX_TIMESTAMP_UNKNOWN,
- .Data = decode_context->packet_data,
- .DataLength = decode_context->packet_size,
- .MaxLength = decode_context->packet_size,
+ .Data = (mfxU8*)decode_context->buffer.data + sizeof(uint32_t),
+ .DataLength = packet_size,
+ .MaxLength = packet_size,
.DataFlag = MFX_BITSTREAM_COMPLETE_FRAME,
};
- decode_context->packet_size = 0;
- decode_context->packet_offset = 0;
- decode_context->frame_received_ts = MicrosNow();
- decode_context->bitstream += bitstream.DataLength;
- if (!decode_context->initialized) {
+ if (!decode_context->surfaces) {
if (!InitializeDecoder(decode_context, &bitstream)) {
LOG("Failed to initialize decoder");
return false;
}
- // mburakov: Initialization might be postponed.
- if (!decode_context->initialized) return true;
+ if (!decode_context->surfaces) {
+ // mburakov: Initialization might be postponed.
+ return true;
+ }
}
for (;;) {
struct Surface* surface = GetFreeSurface(decode_context);
mfxFrameSurface1 surface_work = {
- .Info = surface->frame_info,
+ .Info = surface->mfx_frame_info,
.Data.MemId = surface,
};
mfxFrameSurface1* surface_out = NULL;
mfxSyncPoint sync = NULL;
- mfxStatus status =
- MFXVideoDECODE_DecodeFrameAsync(decode_context->session, &bitstream,
+ mfxStatus mfx_status =
+ MFXVideoDECODE_DecodeFrameAsync(decode_context->mfx_session, &bitstream,
&surface_work, &surface_out, &sync);
- switch (status) {
+ switch (mfx_status) {
case MFX_ERR_MORE_SURFACE:
continue;
case MFX_ERR_NONE:
@@ -487,14 +474,14 @@ bool DecodeContextDecode(struct DecodeContext* decode_context, int fd) {
case MFX_WRN_VIDEO_PARAM_CHANGED:
continue;
default:
- LOG("Failed to decode frame (%d)", status);
+ LOG("Failed to decode frame (%s)", MfxStatusString(mfx_status));
return false;
}
- status =
- MFXVideoCORE_SyncOperation(decode_context->session, sync, MFX_INFINITE);
- if (status != MFX_ERR_NONE) {
- LOG("Failed to sync operation (%d)", status);
+ mfx_status = MFXVideoCORE_SyncOperation(decode_context->mfx_session, sync,
+ MFX_INFINITE);
+ if (mfx_status != MFX_ERR_NONE) {
+ LOG("Failed to sync operation (%s)", MfxStatusString(mfx_status));
return false;
}
@@ -504,19 +491,15 @@ bool DecodeContextDecode(struct DecodeContext* decode_context, int fd) {
return false;
}
- decode_context->frame_decoded_ts = MicrosNow();
- decode_context->frame_counter++;
- HandleTimingStats(decode_context);
- return true;
+ BufferDiscard(&decode_context->buffer, sizeof(uint32_t) + packet_size);
+ goto again;
}
}
-void DecodeContextDestroy(struct DecodeContext** decode_context) {
- if (!decode_context || !*decode_context) return;
- if ((*decode_context)->packet_data) free((*decode_context)->packet_data);
- if ((*decode_context)->session) MFXClose((*decode_context)->session);
- if ((*decode_context)->display) vaTerminate((*decode_context)->display);
- if ((*decode_context)->drm_fd) close((*decode_context)->drm_fd);
- free(*decode_context);
- *decode_context = NULL;
+void DecodeContextDestroy(struct DecodeContext* decode_context) {
+ BufferDestroy(&decode_context->buffer);
+ MFXClose(decode_context->mfx_session);
+ vaTerminate(decode_context->va_display);
+ close(decode_context->drm_fd);
+ free(decode_context);
}
diff --git a/decode.h b/decode.h
index b31b782..61f8203 100644
--- a/decode.h
+++ b/decode.h
@@ -27,6 +27,6 @@ struct Window;
struct DecodeContext* DecodeContextCreate(struct Window* window);
bool DecodeContextDecode(struct DecodeContext* decode_context, int fd);
-void DecodeContextDestroy(struct DecodeContext** decode_context);
+void DecodeContextDestroy(struct DecodeContext* decode_context);
#endif // RECEIVER_DECODE_H_
diff --git a/frame.c b/frame.c
deleted file mode 100644
index dbb886f..0000000
--- a/frame.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2023 Mikhail Burakov. This file is part of receiver.
- *
- * receiver is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * receiver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with receiver. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include "frame.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "util.h"
-
-struct Frame* FrameCreate(uint32_t width, uint32_t height, uint32_t fourcc,
- uint32_t nplanes, const struct FramePlane* planes) {
- struct AUTO(Frame)* frame = malloc(sizeof(struct Frame));
- if (!frame) {
- LOG("Failed to allocate frame (%s)", strerror(errno));
- return NULL;
- }
- *frame = (struct Frame){
- .width = width,
- .height = height,
- .fourcc = fourcc,
- .nplanes = nplanes,
- .planes[0] = {.dmabuf_fd = -1},
- .planes[1] = {.dmabuf_fd = -1},
- .planes[2] = {.dmabuf_fd = -1},
- .planes[3] = {.dmabuf_fd = -1},
- };
-
- for (size_t i = 0; i < nplanes; i++) {
- frame->planes[i] = (struct FramePlane){
- .dmabuf_fd = dup(planes[i].dmabuf_fd),
- .pitch = planes[i].pitch,
- .offset = planes[i].offset,
- .modifier = planes[i].modifier,
- };
- if (frame->planes[i].dmabuf_fd == -1) {
- LOG("Failed to dup dmabuf fd (%s)", strerror(errno));
- return NULL;
- }
- }
-
- return RELEASE(frame);
-}
-
-void FrameDestroy(struct Frame** frame) {
- if (!frame || !*frame) return;
- for (size_t i = (*frame)->nplanes; i; i--) {
- if ((*frame)->planes[i].dmabuf_fd != -1)
- close((*frame)->planes[i].dmabuf_fd);
- }
-}
diff --git a/frame.h b/frame.h
index 0f894f4..18163f6 100644
--- a/frame.h
+++ b/frame.h
@@ -18,7 +18,6 @@
#ifndef RECEIVER_FRAME_H_
#define RECEIVER_FRAME_H_
-#include <stddef.h>
#include <stdint.h>
struct FramePlane {
@@ -36,8 +35,7 @@ struct Frame {
struct FramePlane planes[4];
};
-struct Frame* FrameCreate(uint32_t width, uint32_t height, uint32_t fourcc,
- uint32_t nplanes, const struct FramePlane* planes);
-void FrameDestroy(struct Frame** frame);
+void FrameReset(struct Frame* frame, uint32_t width, uint32_t height,
+ uint32_t fourcc, uint32_t nplanes);
#endif // RECEIVER_FRAME_H_
diff --git a/main.c b/main.c
index c650ea2..bc8c736 100644
--- a/main.c
+++ b/main.c
@@ -25,7 +25,7 @@
#include <unistd.h>
#include "decode.h"
-#include "util.h"
+#include "toolbox/utils.h"
#include "window.h"
static volatile sig_atomic_t g_signal;
@@ -46,6 +46,12 @@ static void WindowDtor(struct Window** window) {
*window = NULL;
}
+static void DecodeContextDtor(struct DecodeContext** decode_context) {
+ if (!*decode_context) return;
+ DecodeContextDestroy(*decode_context);
+ *decode_context = NULL;
+}
+
int main(int argc, char* argv[]) {
(void)argc;
(void)argv;
@@ -61,7 +67,9 @@ int main(int argc, char* argv[]) {
return EXIT_FAILURE;
}
- struct AUTO(DecodeContext)* decode_context = DecodeContextCreate(window);
+ struct DecodeContext
+ __attribute__((cleanup(DecodeContextDtor)))* decode_context =
+ DecodeContextCreate(window);
if (!decode_context) {
LOG("Failed to create decode context");
return EXIT_FAILURE;
diff --git a/makefile b/makefile
index 24bc614..79a7e61 100644
--- a/makefile
+++ b/makefile
@@ -2,6 +2,9 @@ bin:=$(notdir $(shell pwd))
src:=$(wildcard *.c)
obj:=$(src:.c=.o)
+obj+=\
+ toolbox/buffer.o
+
libs:=\
libva \
libva-drm \
diff --git a/util.h b/util.h
deleted file mode 100644
index bbc858f..0000000
--- a/util.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2023 Mikhail Burakov. This file is part of receiver.
- *
- * receiver is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * receiver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with receiver. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef RECEIVER_UTIL_H_
-#define RECEIVER_UTIL_H_
-
-#define STR_IMPL(x) #x
-#define STR(x) STR_IMPL(x)
-#define LOG(fmt, ...) \
- fprintf(stderr, __FILE__ ":" STR(__LINE__) " " fmt "\n", ##__VA_ARGS__)
-#define AUTO(x) x __attribute__((__cleanup__(x##Destroy)))
-#define SWAP(a, b) Swap((void**)&a, (void**)&b)
-#define RELEASE(x) Release((void**)&x)
-#define LENGTH(x) (sizeof(x) / sizeof *(x))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-static inline void Swap(void** a, void** b) {
- void* temp = *a;
- *a = *b;
- *b = temp;
-}
-
-static inline void* Release(void** x) {
- void* result = *x;
- *x = 0;
- return result;
-}
-
-#endif // RECEIVER_UTIL_H_
diff --git a/window.c b/window.c
index 232d261..ff54f70 100644
--- a/window.c
+++ b/window.c
@@ -26,7 +26,7 @@
#include "frame.h"
#include "linux-dmabuf-unstable-v1.h"
-#include "util.h"
+#include "toolbox/utils.h"
#include "xdg-shell.h"
// TODO(mburakov): This would look like shit until Wayland guys finally fix