You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Kinesis Video ON_DEMAND模式时遇datetime JSON序列化错误求助

解决Kinesis Video ON_DEMAND模式下Lambda返回的JSON序列化错误

嘿,这个问题我太熟了!你碰到的是AWS Lambda返回结果时的JSON序列化陷阱——list_fragments接口返回的片段数据里包含了Python的datetime对象,而JSON标准并不支持直接序列化这类对象,所以Lambda在尝试生成响应时就抛出了那个错误。

错误根源

你的代码最后返回的fragment_list是list_fragments的完整响应,其中Fragments数组里的每个片段都带有ServerTimestamp、ProducerTimestamp等datetime类型的字段。Lambda要求返回的响应必须是可JSON序列化的字典,而原生JSON无法处理Python的datetime实例,这就是报错的核心原因。

解决方案

这里提供两种实用的解决思路,你可以根据需求选择:

方案1:自定义JSON编码器(推荐,一劳永逸)

定义一个继承自json.JSONEncoder的类,专门处理datetime类型,把它转换成ISO格式的字符串。然后在返回结果时用这个编码器序列化整个响应字典。

首先添加编码器类:

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return super().default(obj)

然后修改返回语句,用json.dumps配合自定义编码器序列化内容:

return {
    'statusCode': 200,
    'body': json.dumps({
        'ResponseCode': 200,
        'hls_url': hls_live_medial_url,
        'fragment_list': response,
    }, cls=DateTimeEncoder)
}

同时异常处理部分也建议统一序列化:

return {
    'statusCode': 500,
    'body': json.dumps({'Result': ex.response['Error']})
}

方案2:手动转换片段中的datetime字段

如果你不想修改整个响应的序列化方式,可以手动遍历Fragments数组,把每个片段里的datetime字段转换成字符串:

processed_fragments = []
for frag in fragments:
    processed_frag = frag.copy()
    # 转换所有datetime类型的时间字段
    if 'ServerTimestamp' in processed_frag:
        processed_frag['ServerTimestamp'] = processed_frag['ServerTimestamp'].isoformat()
    if 'ProducerTimestamp' in processed_frag:
        processed_frag['ProducerTimestamp'] = processed_frag['ProducerTimestamp'].isoformat()
    if 'FragmentStartTime' in processed_frag:
        processed_frag['FragmentStartTime'] = processed_frag['FragmentStartTime'].isoformat()
    if 'FragmentEndTime' in processed_frag:
        processed_frag['FragmentEndTime'] = processed_frag['FragmentEndTime'].isoformat()
    processed_fragments.append(processed_frag)

然后返回时替换原有的Fragments:

return {
    'ResponseCode': 200,
    'hls_url': hls_live_medial_url,
    'fragment_list': {**response, 'Fragments': processed_fragments},
}

完整修改后的代码(方案1)

import json
import boto3, datetime
from botocore.exceptions import ClientError

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return super().default(obj)

dynamodb = boto3.resource('dynamodb')

def lambda_handler(event, context):
    try:
        # 获取HLS流会话端点
        live_data_end_point= boto3.client('kinesisvideo', region_name ="us-east-1").get_data_endpoint(
            StreamName=event["device_name"],
            APIName='GET_HLS_STREAMING_SESSION_URL'
        )
        live_data_end_point = live_data_end_point["DataEndpoint"]
        
        # 根据模式生成HLS URL
        if event["mode"]=="LIVE":
            media = boto3.client('kinesis-video-archived-media',region_name = 'us-east-1', endpoint_url=live_data_end_point)
            url_response = media.get_hls_streaming_session_url(
                StreamName=event["device_name"],
                PlaybackMode='LIVE',
                DiscontinuityMode='ALWAYS',
                Expires=1000,
                MaxMediaPlaylistFragmentResults=123,
            )
            hls_url = url_response['HLSStreamingSessionURL']
            hls_live_medial_url = [{'hls_url':hls_url}]
        else:
            media = boto3.client('kinesis-video-archived-media',region_name = 'us-east-1', endpoint_url=live_data_end_point)
            url_response = media.get_hls_streaming_session_url(
                StreamName=event["device_name"],
                PlaybackMode='ON_DEMAND',
                HLSFragmentSelector={
                    'FragmentSelectorType':'SERVER_TIMESTAMP',
                    'TimestampRange': {
                        'StartTimestamp':datetime.datetime(2019, 5, 29, 13, 41),
                        'EndTimestamp': datetime.datetime(2019, 5, 29, 13, 55)
                    }
                },
                DiscontinuityMode='ALWAYS',
                Expires=1000,
                MaxMediaPlaylistFragmentResults=500,
            )
            hls_url = url_response['HLSStreamingSessionURL']
            hls_live_medial_url = [{'hls_url':hls_url}]
        
        # 获取片段列表
        end_point_list_frag = boto3.client('kinesisvideo', region_name ="us-east-1").get_data_endpoint(
            StreamName=event["device_name"],
            APIName='LIST_FRAGMENTS'
        )
        end_point_list_frag = end_point_list_frag["DataEndpoint"]
        client_frag = boto3.client('kinesis-video-archived-media', region_name = "us-east-1", endpoint_url = end_point_list_frag)
        response = client_frag.list_fragments(
            StreamName=event["device_name"],
            MaxResults=123,
            FragmentSelector={
                'FragmentSelectorType': 'SERVER_TIMESTAMP',
                'TimestampRange': {
                    'StartTimestamp': datetime.datetime(2019, 5, 29, 13, 41),
                    'EndTimestamp': datetime.datetime(2019, 5, 29, 13, 55)
                }
            }
        )
        
        # 使用自定义编码器序列化响应
        return {
            'statusCode': 200,
            'body': json.dumps({
                'ResponseCode': 200,
                'hls_url': hls_live_medial_url,
                'fragment_list': response,
            }, cls=DateTimeEncoder)
        }
    except ClientError as ex:
        return {
            'statusCode': 500,
            'body': json.dumps({'Result': ex.response['Error']})
        }

补充说明:你代码中手动创建的datetime参数(比如传给Kinesis API的时间范围)是没问题的,因为AWS SDK能直接处理这类对象,只有返回给Lambda的响应里的datetime才需要序列化处理。

内容的提问来源于stack exchange,提问作者Vivek Goswami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:22:28