diff options
| -rw-r--r-- | decode.c | 567 | ||||
| -rw-r--r-- | decode.h | 2 | ||||
| -rw-r--r-- | frame.c | 68 | ||||
| -rw-r--r-- | frame.h | 6 | ||||
| -rw-r--r-- | main.c | 12 | ||||
| -rw-r--r-- | makefile | 3 | ||||
| -rw-r--r-- | util.h | 44 | ||||
| -rw-r--r-- | window.c | 2 | 
8 files changed, 292 insertions, 412 deletions
| @@ -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);  } @@ -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); -  } -} @@ -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_ @@ -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; @@ -2,6 +2,9 @@ bin:=$(notdir $(shell pwd))  src:=$(wildcard *.c)  obj:=$(src:.c=.o) +obj+=\ +	toolbox/buffer.o +  libs:=\  	libva \  	libva-drm \ @@ -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_ @@ -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 | 
