From patchwork Wed Aug 24 14:38:07 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Burt P X-Patchwork-Id: 281 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp402979vsd; Wed, 24 Aug 2016 07:38:34 -0700 (PDT) X-Received: by 10.194.0.211 with SMTP id 19mr3343454wjg.124.1472049514431; Wed, 24 Aug 2016 07:38:34 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id by1si8723247wjb.33.2016.08.24.07.38.29; Wed, 24 Aug 2016 07:38:34 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 405336898F4; Wed, 24 Aug 2016 17:38:20 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-it0-f68.google.com (mail-it0-f68.google.com [209.85.214.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 521CA687EC8 for ; Wed, 24 Aug 2016 17:38:11 +0300 (EEST) Received: by mail-it0-f68.google.com with SMTP id e63so2397493ith.1 for ; Wed, 24 Aug 2016 07:38:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=sznJoCgMDisUM3u7qYj60TPCelbBeb8fYIEMaAm5AuM=; b=aEkJGDiY+ScpnEMcetChNpNtf8GOU00Jlwd59cq4iZo+MyNDqQc6nhnVjMr9byAGnu SjXddYgXGDp1b7F3NFT759Dk5YMyoQNjb/Hn/Or/fcFla9xZBkHIhL52A1pUJYWBtKt+ 4UP5dvKeeAOsj1AP0Xjwqx98hV88MbS1HBAmljoX7qgdW2q9j0EAyYW8Gke/fy497F5R QGCtvS/8tsMyH0Qy+siFYCbRj0OQ5ZM8y1UhVzhzUE1hpBLH7ObrOyLxJPPjx+i5lyGt bw7kq5EYutrhE2mebbJFv5C0Y0PIL9sMuP1+hjC9IxUr0/6I7bSsQWEITV5rdhkz4P87 o5HA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=sznJoCgMDisUM3u7qYj60TPCelbBeb8fYIEMaAm5AuM=; b=hefwSa5n7SLP3m9NSBqQ/1qjTz8NTQHozt853tmaPjC6a6ZM17gHHv5DLOd5YByScq Dwawagr5SrSXYQGRPci78VZPOP/+aP36apjAVKCgF8wga+RjWnMx/tuxbG8JWqduvn4A yL0EWc0Lj0LyezgBgP2QJuZgY1T9lwu2KR62V9J66XtSo4t0xHKMJnjU3CdqAICmU270 YbRpKOPIkGjKDCVZp7Zyv7Kad2ih9m36eBJonb9wypzpf0D9nXauIEOxWCvKfDUrXu6M XF+OBWa+rXTnMQ9xR6iiQF4Zm2m/ky+ToBwvdfo2d2XnNaTF1VKhoo4DKf11brGlwgt/ tUcw== X-Gm-Message-State: AE9vXwMdvt2FLq9tcqx1IIKy+hkHEFzA9geJ+7xCCjexzWm8YV7t76TdrvlmaqLpQfgPWg== X-Received: by 10.36.40.2 with SMTP id h2mr4605572ith.89.1472049491842; Wed, 24 Aug 2016 07:38:11 -0700 (PDT) Received: from localhost.localdomain ([216.16.66.181]) by smtp.gmail.com with ESMTPSA id 194sm3517535iob.30.2016.08.24.07.38.11 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 24 Aug 2016 07:38:11 -0700 (PDT) From: Burt P To: ffmpeg-devel@ffmpeg.org Date: Wed, 24 Aug 2016 09:38:07 -0500 Message-Id: <1472049487-6517-1-git-send-email-pburt0@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1471906881-10946-4-git-send-email-pburt0@gmail.com> References: <1471906881-10946-4-git-send-email-pburt0@gmail.com> Subject: [FFmpeg-devel] [PATCHv2] af_hdcd: for easier maintenance alongside libhdcd X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Mostly just re-arranges some code to make it easier to update this filter and libhdcd together. filter_frame() is much simpler as a result. * use the HDCD detection data structure and functions from libhdcd, moved detection code out of filter_frame() * moved analyze_mode preparation out of filter_frame() into hdcd_analyze_prepare(), from libhdcd * moved some macro definitions to the top so they are all together Signed-off-by: Burt P --- libavfilter/af_hdcd.c | 271 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 172 insertions(+), 99 deletions(-) diff --git a/libavfilter/af_hdcd.c b/libavfilter/af_hdcd.c index 2324dc3..0360bc9 100644 --- a/libavfilter/af_hdcd.c +++ b/libavfilter/af_hdcd.c @@ -823,6 +823,20 @@ static const int32_t gaintab[] = { 0x35fa26 }; +#define HDCD_PROCESS_STEREO_DEFAULT 1 +#define HDCD_MAX_CHANNELS 2 + +/** convert to float from 4-bit (3.1) fixed-point + * the always-negative value is stored positive, so make it negative */ +#define GAINTOFLOAT(g) (g) ? -(float)(g>>1) - ((g & 1) ? 0.5 : 0.0) : 0.0 + +/** apply gain, 11-bit (3.8) fixed point, + * always negative but stored positive. */ +#define APPLY_GAIN(s,g) do{int64_t s64 = s; s64 *= gaintab[g]; s = (int32_t)(s64 >> 23); }while(0); + +/** tone generator: sample_number, frequency, sample_rate, amplitude */ +#define TONEGEN16(sn, f, sr, a) (int16_t)(sin((6.28318530718 * (sn) * (f)) /(sr)) * (a) * 0x7fff) + typedef struct { uint64_t window; unsigned char readahead; @@ -856,12 +870,14 @@ typedef struct { /** occurences of code detect timer expiring without detecting * a code. -1 for timer never set. */ int count_sustain_expired; + + int _ana_snb; /**< used in the analyze mode tone generator */ } hdcd_state_t; typedef enum { - HDCD_PE_NEVER = 0, - HDCD_PE_INTERMITTENT = 1, - HDCD_PE_PERMANENT = 2, + HDCD_PE_NEVER = 0, /**< All valid packets have PE set to off */ + HDCD_PE_INTERMITTENT = 1, /**< Some valid packets have PE set to on */ + HDCD_PE_PERMANENT = 2, /**< All valid packets have PE set to on */ } hdcd_pe_t; static const char * const pe_str[] = { @@ -870,25 +886,51 @@ static const char * const pe_str[] = { "enabled permanently" }; -#define HDCD_PROCESS_STEREO_DEFAULT 1 -#define HDCD_MAX_CHANNELS 2 +typedef enum { + HDCD_NONE = 0, /**< HDCD packets do not (yet) appear */ + HDCD_NO_EFFECT = 1, /**< HDCD packets appear, but all control codes are NOP */ + HDCD_EFFECTUAL = 2, /**< HDCD packets appear, and change the output in some way */ +} hdcd_detection_t; -/** convert to float from 4-bit (3.1) fixed-point - * the always-negative value is stored positive, so make it negative */ -#define GAINTOFLOAT(g) (g) ? -(float)(g>>1) - ((g & 1) ? 0.5 : 0.0) : 0.0 +typedef enum { + HDCD_PVER_NONE = 0, /**< No packets (yet) discovered */ + HDCD_PVER_A = 1, /**< Packets of type A (8-bit control) discovered */ + HDCD_PVER_B = 2, /**< Packets of type B (8-bit control, 8-bit XOR) discovered */ + HDCD_PVER_MIX = 3, /**< Packets of type A and B discovered, most likely an encoding error */ +} hdcd_pf_t; + +static const char * const pf_str[] = { + "?", "A", "B", "A+B" +}; + +typedef struct { + hdcd_detection_t hdcd_detected; + hdcd_pf_t packet_type; + int total_packets; /**< valid packets */ + int errors; /**< detectable errors */ + hdcd_pe_t peak_extend; + int uses_transient_filter; + float max_gain_adjustment; /**< in dB, expected in the range -7.5 to 0.0 */ + int cdt_expirations; /**< -1 for never set, 0 for set but never expired */ -#define HDCD_ANA_OFF 0 + int _active_count; /**< used internally */ +} hdcd_detection_data_t; + +typedef enum { + HDCD_ANA_OFF = 0, + HDCD_ANA_LLE = 1, + HDCD_ANA_PE = 2, + HDCD_ANA_CDT = 3, + HDCD_ANA_TGM = 4, + HDCD_ANA_TOP = 5, /**< used in max value of AVOption */ +} hdcd_ana_mode_t; + +/** analyze mode descriptions: macro for AVOption definitions, array of const char for mapping mode to string */ #define HDCD_ANA_OFF_DESC "disabled" -#define HDCD_ANA_LLE 1 #define HDCD_ANA_LLE_DESC "gain adjustment level at each sample" -#define HDCD_ANA_PE 2 #define HDCD_ANA_PE_DESC "samples where peak extend occurs" -#define HDCD_ANA_CDT 3 #define HDCD_ANA_CDT_DESC "samples where the code detect timer is active" -#define HDCD_ANA_TGM 4 #define HDCD_ANA_TGM_DESC "samples where the target gain does not match between channels" -#define HDCD_ANA_TOP 5 /* used in max value of AVOption */ - static const char * const ana_mode_str[] = { HDCD_ANA_OFF_DESC, HDCD_ANA_LLE_DESC, @@ -917,7 +959,6 @@ typedef struct HDCDContext { * the amplitude to signal some specific aspect of the decoding * process. See docs or HDCD_ANA_* defines. */ int analyze_mode; - int ana_snb; /**< used in tone generation */ int cdt_ms; /**< code detect timer period in ms */ @@ -934,11 +975,7 @@ typedef struct HDCDContext { int val_target_gain; /**< last matching target_gain in both channels */ /* User information/stats */ - int hdcd_detected; /**< Valid HDCD coding was detected */ - int det_errors; /**< detectable errors */ - hdcd_pe_t peak_extend; /**< peak exted used */ - int uses_transient_filter; /**< transient filter flag detected */ - float max_gain_adjustment; /**< in dB, expected in the range -7.5 to 0.0 */ + hdcd_detection_data_t detect; } HDCDContext; #define OFFSET(x) offsetof(HDCDContext, x) @@ -964,8 +1001,6 @@ static const AVOption hdcd_options[] = { AVFILTER_DEFINE_CLASS(hdcd); -#define APPLY_GAIN(s,g) do{int64_t s64 = s; s64 *= gaintab[g]; s = (int32_t)(s64 >> 23); }while(0); - static void hdcd_reset(hdcd_state_t *state, unsigned rate, unsigned cdt_ms) { int i; @@ -991,6 +1026,8 @@ static void hdcd_reset(hdcd_state_t *state, unsigned rate, unsigned cdt_ms) for(i = 0; i < 16; i++) state->gain_counts[i] = 0; state->max_gain = 0; state->count_sustain_expired = -1; + + state->_ana_snb = 0; } /** update the user info/counters */ @@ -1259,6 +1296,23 @@ static int hdcd_scan_stereo(HDCDContext *ctx, const int32_t *samples, int max) return result; } +/** replace audio with solid tone, but save LSBs */ +static void hdcd_analyze_prepare(hdcd_state_t *state, int32_t *samples, int count, int stride) { + int n; + for (n = 0; n < count * stride; n += stride) { + /* in analyze mode, the audio is replaced by a solid tone, and + * amplitude is changed to signal when the specified feature is + * used. + * bit 0: HDCD signal preserved + * bit 1: Original sample was above PE level */ + int32_t save = (abs(samples[n]) - PEAK_EXT_LEVEL >= 0) ? 2 : 0; /* above PE level */ + save |= samples[n] & 1; /* save LSB for HDCD packets */ + samples[n] = TONEGEN16(state->_ana_snb, 277.18, 44100, 0.1); + samples[n] = (samples[n] | 3) ^ ((~save) & 3); + if (++state->_ana_snb > 0x3fffffff) state->_ana_snb = 0; + } +} + /** encode a value in the given sample by adjusting the amplitude */ static int32_t hdcd_analyze_gen(int32_t sample, unsigned int v, unsigned int maxv) { @@ -1429,6 +1483,9 @@ static void hdcd_process(HDCDContext *ctx, hdcd_state_t *state, int32_t *samples int peak_extend, target_gain; int lead = 0; + if (ctx->analyze_mode) + hdcd_analyze_prepare(state, samples, count, stride); + hdcd_control(ctx, state, &peak_extend, &target_gain); while (count > lead) { int envelope_run; @@ -1467,8 +1524,14 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count) int gain[2] = {ctx->state[0].running_gain, ctx->state[1].running_gain}; int peak_extend[2]; int lead = 0; + int ctlret; + + if (ctx->analyze_mode) { + hdcd_analyze_prepare(&ctx->state[0], samples, count, stride); + hdcd_analyze_prepare(&ctx->state[1], samples + 1, count, stride); + } - int ctlret = hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]); + ctlret = hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]); while (count > lead) { int envelope_run, run; @@ -1519,8 +1582,61 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count) ctx->state[1].running_gain = gain[1]; } -/** tone generator: sample_number, frequency, sample_rate, amplitude */ -#define TONEGEN16(sn, f, sr, a) (int16_t)(sin((6.28318530718 * (sn) * (f)) /(sr)) * (a) * 0x7fff) +static void hdcd_detect_reset(hdcd_detection_data_t *detect) { + detect->hdcd_detected = HDCD_NONE; + detect->packet_type = HDCD_PVER_NONE; + detect->total_packets = 0; + detect->errors = 0; + detect->peak_extend = HDCD_PE_NEVER; + detect->uses_transient_filter = 0; + detect->max_gain_adjustment = 0.0; + detect->cdt_expirations = -1; + detect->_active_count = 0; +} + +static void hdcd_detect_start(hdcd_detection_data_t *detect) { + detect->errors = 0; /* re-sum every pass */ + detect->total_packets = 0; + detect->_active_count = 0; /* will need to match channels at hdcd_detect_end() */ + detect->cdt_expirations = -1; +} + +static void hdcd_detect_onech(hdcd_state_t *state, hdcd_detection_data_t *detect) { + hdcd_pe_t pe = HDCD_PE_NEVER; + detect->uses_transient_filter |= !!(state->count_transient_filter); + detect->total_packets += state->code_counterA + state->code_counterB; + if (state->code_counterA) detect->packet_type |= HDCD_PVER_A; + if (state->code_counterB) detect->packet_type |= HDCD_PVER_B; + if (state->count_peak_extend) { + /* if every valid packet has used PE, call it permanent */ + if (state->count_peak_extend == state->code_counterA + state->code_counterB) + pe = HDCD_PE_PERMANENT; + else + pe = HDCD_PE_INTERMITTENT; + if (detect->peak_extend != HDCD_PE_INTERMITTENT) + detect->peak_extend = pe; + } + detect->max_gain_adjustment = FFMIN(detect->max_gain_adjustment, GAINTOFLOAT(state->max_gain)); + detect->errors += state->code_counterA_almost + + state->code_counterB_checkfails + + state->code_counterC_unmatched; + if (state->sustain) detect->_active_count++; + if (state->count_sustain_expired >= 0) { + if (detect->cdt_expirations == -1) detect->cdt_expirations = 0; + detect->cdt_expirations += state->count_sustain_expired; + } +} + +static void hdcd_detect_end(hdcd_detection_data_t *detect, int channels) { + /* HDCD is detected if a valid packet is active in all + * channels at the same time. */ + if (detect->_active_count == channels) { + if (detect->max_gain_adjustment || detect->peak_extend) + detect->hdcd_detected = HDCD_EFFECTUAL; + else + detect->hdcd_detected = HDCD_NO_EFFECT; + } +} static int filter_frame(AVFilterLink *inlink, AVFrame *in) { @@ -1542,81 +1658,28 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_free(&in); return result; } - out->format = outlink->format; + out->format = outlink->format; // is this needed? in_data = (int16_t*)in->data[0]; out_data = (int32_t*)out->data[0]; - for (c = n = 0; n < in->nb_samples * in->channels; n++) { + for (c = n = 0; n < in->nb_samples * in->channels; n++) out_data[n] = in_data[n]; - if (s->analyze_mode) { - /* in analyze mode, the audio is replaced by a solid tone, and - * amplitude is changed to signal when the specified feature is - * used. - * bit 0: HDCD signal preserved - * bit 1: Original sample was above PE level */ - int32_t save = (abs(in_data[n]) - PEAK_EXT_LEVEL >= 0) ? 2 : 0; /* above PE level */ - save |= in_data[n] & 1; /* save LSB for HDCD packets */ - out_data[n] = TONEGEN16(s->ana_snb, 277.18, 44100, 0.1); - out_data[n] = (out_data[n] | 3) ^ ((~save) & 3); - if (++c == in->channels) { s->ana_snb++; c = 0; } - if (s->ana_snb > 0x3fffffff) s->ana_snb = 0; - } - } - s->det_errors = 0; /* re-sum every pass */ - if (s->process_stereo && inlink->channels == 2) { - float mga = 0.0; + if (s->process_stereo) { + hdcd_detect_start(&s->detect); hdcd_process_stereo(s, out_data, in->nb_samples); - /* HDCD is detected if a valid packet is active in both - * channels at the same time. */ - if (s->state[0].sustain && s->state[1].sustain) s->hdcd_detected = 1; - if (s->state[0].count_peak_extend || s->state[1].count_peak_extend) { - int packets = s->state[0].code_counterA - + s->state[0].code_counterB - + s->state[1].code_counterA - + s->state[1].code_counterB; - /* if every valid packet has used PE, call it permanent */ - if (packets == s->state[0].count_peak_extend + s->state[1].count_peak_extend) - s->peak_extend = HDCD_PE_PERMANENT; - else - s->peak_extend = HDCD_PE_INTERMITTENT; - } else s->peak_extend = HDCD_PE_NEVER; - s->uses_transient_filter = (s->state[0].count_transient_filter || s->state[1].count_transient_filter); - mga = FFMIN(GAINTOFLOAT(s->state[0].max_gain), GAINTOFLOAT(s->state[1].max_gain)); - s->max_gain_adjustment = FFMIN(s->max_gain_adjustment, mga); - s->det_errors += s->state[0].code_counterA_almost - + s->state[0].code_counterB_checkfails - + s->state[0].code_counterC_unmatched - + s->state[1].code_counterA_almost - + s->state[1].code_counterB_checkfails - + s->state[1].code_counterC_unmatched; + hdcd_detect_onech(&s->state[0], &s->detect); + hdcd_detect_onech(&s->state[1], &s->detect); + hdcd_detect_end(&s->detect, 2); } else { - int detect=0; - int packets=0; - int pe_packets=0; - for (c = 0; c < inlink->channels; c++) { - hdcd_state_t *state = &s->state[c]; - hdcd_process(s, state, out_data + c, in->nb_samples, out->channels); - if (state->sustain) detect++; - packets += state->code_counterA + state->code_counterB; - pe_packets += state->count_peak_extend; - s->uses_transient_filter |= !!(state->count_transient_filter); - s->max_gain_adjustment = FFMIN(s->max_gain_adjustment, GAINTOFLOAT(state->max_gain)); - s->det_errors += state->code_counterA_almost - + state->code_counterB_checkfails - + state->code_counterC_unmatched; + hdcd_detect_start(&s->detect); + for (c = 0; c < in->channels; c++) { + hdcd_process(s, &s->state[c], out_data + c, in->nb_samples, in->channels); + hdcd_detect_onech(&s->state[c], &s->detect); } - if (pe_packets) { - /* if every valid packet has used PE, call it permanent */ - if (packets == pe_packets) - s->peak_extend = HDCD_PE_PERMANENT; - else - s->peak_extend = HDCD_PE_INTERMITTENT; - } else s->peak_extend = HDCD_PE_NEVER; - /* HDCD is detected if a valid packet is active in all - * channels at the same time. */ - if (detect == inlink->channels) s->hdcd_detected = 1; + hdcd_detect_end(&s->detect, in->channels); } + s->sample_count += in->nb_samples * in->channels; av_frame_free(&in); @@ -1690,15 +1753,18 @@ static av_cold void uninit(AVFilterContext *ctx) av_log(ctx, AV_LOG_VERBOSE, "Channel %d: tg %0.1f: %d\n", i, GAINTOFLOAT(j), state->gain_counts[j]); } } + av_log(ctx, AV_LOG_VERBOSE, "Packets: type: %s, total: %d\n", + pf_str[s->detect.packet_type], + s->detect.total_packets); /* log the HDCD decode information */ - if (s->hdcd_detected) + if (s->detect.hdcd_detected) av_log(ctx, AV_LOG_INFO, "HDCD detected: yes, peak_extend: %s, max_gain_adj: %0.1f dB, transient_filter: %s, detectable errors: %d%s%s\n", - pe_str[s->peak_extend], - s->max_gain_adjustment, - (s->uses_transient_filter) ? "detected" : "not detected", - s->det_errors, (s->det_errors) ? " (try -v verbose)" : "", + pe_str[s->detect.peak_extend], + s->detect.max_gain_adjustment, + (s->detect.uses_transient_filter) ? "detected" : "not detected", + s->detect.errors, (s->detect.errors) ? " (try -v verbose)" : "", (s->bad_config) ? " (bad_config)" : "" ); else @@ -1713,11 +1779,11 @@ static av_cold int init(AVFilterContext *ctx) HDCDContext *s = ctx->priv; int c; - s->max_gain_adjustment = 0.0; s->sample_count = 0; s->fctx = ctx; s->bad_config = 0; + hdcd_detect_reset(&s->detect); for (c = 0; c < HDCD_MAX_CHANNELS; c++) { hdcd_reset(&s->state[c], 44100, s->cdt_ms); } @@ -1742,7 +1808,14 @@ static av_cold int init(AVFilterContext *ctx) static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; HDCDContext *s = ctx->priv; - AVFilterLink *lk = inlink; + AVFilterLink *lk; + + if (inlink->channels != 2 && s->process_stereo) { + av_log(ctx, AV_LOG_WARNING, "process_stereo disabled (channels = %d)", inlink->channels); + s->process_stereo = 0; + } + + lk = inlink; while(lk != NULL) { AVFilterContext *nextf = lk->src; if (lk->type == AVMEDIA_TYPE_AUDIO) {