1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::codecs::Decoder;
16 use crate::codecs::DecoderConfig;
17 use crate::decoder::Category;
18 use crate::image::Image;
19 use crate::image::YuvRange;
20 use crate::internal_utils::pixels::*;
21 use crate::internal_utils::stream::IStream;
22 use crate::internal_utils::*;
23 use crate::*;
24
25 use ndk_sys::bindings::*;
26
27 use std::ffi::CString;
28 use std::os::raw::c_char;
29 use std::ptr;
30
31 #[cfg(android_soong)]
32 include!(concat!(env!("OUT_DIR"), "/mediaimage2_bindgen.rs"));
33
34 #[derive(Debug)]
35 struct MediaFormat {
36 format: *mut AMediaFormat,
37 }
38
39 macro_rules! c_str {
40 ($var: ident, $var_tmp:ident, $str:expr) => {
41 let $var_tmp = CString::new($str).unwrap();
42 let $var = $var_tmp.as_ptr();
43 };
44 }
45
46 #[derive(Debug, Default)]
47 struct PlaneInfo {
48 color_format: AndroidMediaCodecOutputColorFormat,
49 offset: [isize; 3],
50 row_stride: [u32; 3],
51 column_stride: [u32; 3],
52 }
53
54 impl PlaneInfo {
pixel_format(&self) -> PixelFormat55 fn pixel_format(&self) -> PixelFormat {
56 match self.color_format {
57 AndroidMediaCodecOutputColorFormat::P010 => PixelFormat::AndroidP010,
58 AndroidMediaCodecOutputColorFormat::Yuv420Flexible => {
59 let u_before_v = self.offset[2] == self.offset[1] + 1;
60 let v_before_u = self.offset[1] == self.offset[2] + 1;
61 let is_nv_format = self.column_stride == [1, 2, 2] && (u_before_v || v_before_u);
62 match (is_nv_format, u_before_v) {
63 (true, true) => PixelFormat::AndroidNv12,
64 (true, false) => PixelFormat::AndroidNv21,
65 (false, _) => PixelFormat::Yuv420,
66 }
67 }
68 }
69 }
70
depth(&self) -> u871 fn depth(&self) -> u8 {
72 match self.color_format {
73 AndroidMediaCodecOutputColorFormat::P010 => 16,
74 AndroidMediaCodecOutputColorFormat::Yuv420Flexible => 8,
75 }
76 }
77 }
78
79 impl MediaFormat {
get_i32(&self, key: *const c_char) -> Option<i32>80 fn get_i32(&self, key: *const c_char) -> Option<i32> {
81 let mut value: i32 = 0;
82 match unsafe { AMediaFormat_getInt32(self.format, key, &mut value as *mut _) } {
83 true => Some(value),
84 false => None,
85 }
86 }
87
get_i32_from_str(&self, key: &str) -> Option<i32>88 fn get_i32_from_str(&self, key: &str) -> Option<i32> {
89 c_str!(key_str, key_str_tmp, key);
90 self.get_i32(key_str)
91 }
92
width(&self) -> AvifResult<i32>93 fn width(&self) -> AvifResult<i32> {
94 self.get_i32(unsafe { AMEDIAFORMAT_KEY_WIDTH })
95 .ok_or(AvifError::UnknownError("".into()))
96 }
97
height(&self) -> AvifResult<i32>98 fn height(&self) -> AvifResult<i32> {
99 self.get_i32(unsafe { AMEDIAFORMAT_KEY_HEIGHT })
100 .ok_or(AvifError::UnknownError("".into()))
101 }
102
slice_height(&self) -> AvifResult<i32>103 fn slice_height(&self) -> AvifResult<i32> {
104 self.get_i32(unsafe { AMEDIAFORMAT_KEY_SLICE_HEIGHT })
105 .ok_or(AvifError::UnknownError("".into()))
106 }
107
stride(&self) -> AvifResult<i32>108 fn stride(&self) -> AvifResult<i32> {
109 self.get_i32(unsafe { AMEDIAFORMAT_KEY_STRIDE })
110 .ok_or(AvifError::UnknownError("".into()))
111 }
112
color_format(&self) -> AvifResult<i32>113 fn color_format(&self) -> AvifResult<i32> {
114 self.get_i32(unsafe { AMEDIAFORMAT_KEY_COLOR_FORMAT })
115 .ok_or(AvifError::UnknownError("".into()))
116 }
117
color_range(&self) -> YuvRange118 fn color_range(&self) -> YuvRange {
119 // color-range is documented but isn't exposed as a constant in the NDK:
120 // https://developer.android.com/reference/android/media/MediaFormat#KEY_COLOR_RANGE
121 let color_range = self.get_i32_from_str("color-range").unwrap_or(2);
122 if color_range == 0 {
123 YuvRange::Limited
124 } else {
125 YuvRange::Full
126 }
127 }
128
guess_plane_info(&self) -> AvifResult<PlaneInfo>129 fn guess_plane_info(&self) -> AvifResult<PlaneInfo> {
130 let height = self.height()?;
131 let slice_height = self.slice_height().unwrap_or(height);
132 let stride = self.stride()?;
133 let color_format: AndroidMediaCodecOutputColorFormat = self.color_format()?.into();
134 let mut plane_info = PlaneInfo {
135 color_format,
136 ..Default::default()
137 };
138 match color_format {
139 AndroidMediaCodecOutputColorFormat::P010 => {
140 plane_info.row_stride = [
141 u32_from_i32(stride)?,
142 u32_from_i32(stride)?,
143 0, // V plane is not used for P010.
144 ];
145 plane_info.column_stride = [
146 2, 2, 0, // V plane is not used for P010.
147 ];
148 plane_info.offset = [
149 0,
150 isize_from_i32(stride * slice_height)?,
151 0, // V plane is not used for P010.
152 ];
153 }
154 AndroidMediaCodecOutputColorFormat::Yuv420Flexible => {
155 plane_info.row_stride = [
156 u32_from_i32(stride)?,
157 u32_from_i32((stride + 1) / 2)?,
158 u32_from_i32((stride + 1) / 2)?,
159 ];
160 plane_info.column_stride = [1, 1, 1];
161 plane_info.offset[0] = 0;
162 plane_info.offset[1] = isize_from_i32(stride * slice_height)?;
163 let u_plane_size = isize_from_i32(((stride + 1) / 2) * ((height + 1) / 2))?;
164 // When color format is YUV_420_FLEXIBLE, the V plane comes before the U plane.
165 plane_info.offset[2] = plane_info.offset[1] - u_plane_size;
166 }
167 }
168 Ok(plane_info)
169 }
170
get_plane_info(&self) -> AvifResult<PlaneInfo>171 fn get_plane_info(&self) -> AvifResult<PlaneInfo> {
172 // When not building for the Android platform, image-data is not available, so simply try to
173 // guess the buffer format based on the available keys in the format.
174 #[cfg(not(android_soong))]
175 return self.guess_plane_info();
176
177 #[cfg(android_soong)]
178 {
179 c_str!(key_str, key_str_tmp, "image-data");
180 let mut data: *mut std::ffi::c_void = ptr::null_mut();
181 let mut size: usize = 0;
182 if !unsafe {
183 AMediaFormat_getBuffer(
184 self.format,
185 key_str,
186 &mut data as *mut _,
187 &mut size as *mut _,
188 )
189 } {
190 return self.guess_plane_info();
191 }
192 if size != std::mem::size_of::<android_MediaImage2>() {
193 return self.guess_plane_info();
194 }
195 let image_data = unsafe { *(data as *const android_MediaImage2) };
196 if image_data.mType != android_MediaImage2_Type_MEDIA_IMAGE_TYPE_YUV {
197 return self.guess_plane_info();
198 }
199 let planes = unsafe { ptr::read_unaligned(ptr::addr_of!(image_data.mPlane)) };
200 let mut plane_info = PlaneInfo {
201 color_format: self.color_format()?.into(),
202 ..Default::default()
203 };
204 for plane_index in 0usize..3 {
205 plane_info.offset[plane_index] = isize_from_u32(planes[plane_index].mOffset)?;
206 plane_info.row_stride[plane_index] = u32_from_i32(planes[plane_index].mRowInc)?;
207 plane_info.column_stride[plane_index] = u32_from_i32(planes[plane_index].mColInc)?;
208 }
209 return Ok(plane_info);
210 }
211 }
212 }
213
214 enum CodecInitializer {
215 ByName(String),
216 ByMimeType(String),
217 }
218
219 #[cfg(android_soong)]
prefer_hardware_decoder(config: &DecoderConfig) -> bool220 fn prefer_hardware_decoder(config: &DecoderConfig) -> bool {
221 let prefer_hw = rustutils::system_properties::read_bool(
222 "media.stagefright.thumbnail.prefer_hw_codecs",
223 false,
224 )
225 .unwrap_or(false);
226 if config.codec_config.is_avif() {
227 // We will return true when all of the below conditions are true:
228 // 1) prefer_hw is true.
229 // 2) category is not Alpha and category is not Gainmap. We do not prefer hardware for
230 // decoding these categories since they generally tend to be monochrome images and using
231 // hardware for that is unreliable.
232 // 3) profile is 0. As of Sep 2024, there are no AV1 hardware decoders that support
233 // anything other than profile 0.
234 prefer_hw
235 && config.category != Category::Alpha
236 && config.category != Category::Gainmap
237 && config.codec_config.profile() == 0
238 } else {
239 // We will return true when one of the following conditions are true:
240 // 1) prefer_hw is true.
241 // 2) depth is greater than 8. As of Nov 2024, the default HEVC software decoder on Android
242 // only supports 8-bit images.
243 prefer_hw || config.depth > 8
244 }
245 }
246
get_codec_initializers(config: &DecoderConfig) -> Vec<CodecInitializer>247 fn get_codec_initializers(config: &DecoderConfig) -> Vec<CodecInitializer> {
248 #[cfg(android_soong)]
249 {
250 // Use a specific decoder if it is requested.
251 if let Ok(Some(decoder)) =
252 rustutils::system_properties::read("media.crabbyavif.debug.decoder")
253 {
254 if !decoder.is_empty() {
255 return vec![CodecInitializer::ByName(decoder)];
256 }
257 }
258 }
259 let dav1d = String::from("c2.android.av1-dav1d.decoder");
260 let gav1 = String::from("c2.android.av1.decoder");
261 let hevc = String::from("c2.android.hevc.decoder");
262 // As of Sep 2024, c2.android.av1.decoder is the only known decoder to support 12-bit AV1. So
263 // prefer that for 12 bit images.
264 let prefer_gav1 = config.depth == 12;
265 let is_avif = config.codec_config.is_avif();
266 let mime_type = if is_avif { MediaCodec::AV1_MIME } else { MediaCodec::HEVC_MIME };
267 let prefer_hw = false;
268 #[cfg(android_soong)]
269 let prefer_hw = prefer_hardware_decoder(config);
270 match (prefer_hw, is_avif, prefer_gav1) {
271 (true, false, _) => vec![
272 CodecInitializer::ByMimeType(mime_type.to_string()),
273 CodecInitializer::ByName(hevc),
274 ],
275 (false, false, _) => vec![
276 CodecInitializer::ByName(hevc),
277 CodecInitializer::ByMimeType(mime_type.to_string()),
278 ],
279 (true, true, true) => vec![
280 CodecInitializer::ByName(gav1),
281 CodecInitializer::ByMimeType(mime_type.to_string()),
282 CodecInitializer::ByName(dav1d),
283 ],
284 (true, true, false) => vec![
285 CodecInitializer::ByMimeType(mime_type.to_string()),
286 CodecInitializer::ByName(dav1d),
287 CodecInitializer::ByName(gav1),
288 ],
289 (false, true, true) => vec![
290 CodecInitializer::ByName(gav1),
291 CodecInitializer::ByName(dav1d),
292 CodecInitializer::ByMimeType(mime_type.to_string()),
293 ],
294 (false, true, false) => vec![
295 CodecInitializer::ByName(dav1d),
296 CodecInitializer::ByName(gav1),
297 CodecInitializer::ByMimeType(mime_type.to_string()),
298 ],
299 }
300 }
301
302 #[derive(Default)]
303 pub struct MediaCodec {
304 codec: Option<*mut AMediaCodec>,
305 format: Option<MediaFormat>,
306 output_buffer_index: Option<usize>,
307 config: Option<DecoderConfig>,
308 }
309
310 impl MediaCodec {
311 const AV1_MIME: &str = "video/av01";
312 const HEVC_MIME: &str = "video/hevc";
313 }
314
315 impl Decoder for MediaCodec {
initialize(&mut self, config: &DecoderConfig) -> AvifResult<()>316 fn initialize(&mut self, config: &DecoderConfig) -> AvifResult<()> {
317 if self.codec.is_some() {
318 return Ok(()); // Already initialized.
319 }
320 let format = unsafe { AMediaFormat_new() };
321 if format.is_null() {
322 return Err(AvifError::UnknownError("".into()));
323 }
324 c_str!(
325 mime_type,
326 mime_type_tmp,
327 if config.codec_config.is_avif() { Self::AV1_MIME } else { Self::HEVC_MIME }
328 );
329 unsafe {
330 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime_type);
331 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, i32_from_u32(config.width)?);
332 AMediaFormat_setInt32(
333 format,
334 AMEDIAFORMAT_KEY_HEIGHT,
335 i32_from_u32(config.height)?,
336 );
337 AMediaFormat_setInt32(
338 format,
339 AMEDIAFORMAT_KEY_COLOR_FORMAT,
340 if config.depth == 8 {
341 AndroidMediaCodecOutputColorFormat::Yuv420Flexible
342 } else {
343 AndroidMediaCodecOutputColorFormat::P010
344 } as i32,
345 );
346 // low-latency is documented but isn't exposed as a constant in the NDK:
347 // https://developer.android.com/reference/android/media/MediaFormat#KEY_LOW_LATENCY
348 c_str!(low_latency, low_latency_tmp, "low-latency");
349 AMediaFormat_setInt32(format, low_latency, 1);
350 AMediaFormat_setInt32(
351 format,
352 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE,
353 i32_from_usize(config.max_input_size)?,
354 );
355 let codec_specific_data = config.codec_config.raw_data();
356 if !codec_specific_data.is_empty() {
357 AMediaFormat_setBuffer(
358 format,
359 AMEDIAFORMAT_KEY_CSD_0,
360 codec_specific_data.as_ptr() as *const _,
361 codec_specific_data.len(),
362 );
363 }
364 }
365
366 let mut codec = ptr::null_mut();
367 for codec_initializer in get_codec_initializers(config) {
368 codec = match codec_initializer {
369 CodecInitializer::ByName(name) => {
370 c_str!(codec_name, codec_name_tmp, name.as_str());
371 unsafe { AMediaCodec_createCodecByName(codec_name) }
372 }
373 CodecInitializer::ByMimeType(mime_type) => {
374 c_str!(codec_mime, codec_mime_tmp, mime_type.as_str());
375 unsafe { AMediaCodec_createDecoderByType(codec_mime) }
376 }
377 };
378 if codec.is_null() {
379 continue;
380 }
381 let status = unsafe {
382 AMediaCodec_configure(codec, format, ptr::null_mut(), ptr::null_mut(), 0)
383 };
384 if status != media_status_t_AMEDIA_OK {
385 unsafe {
386 AMediaCodec_delete(codec);
387 }
388 codec = ptr::null_mut();
389 continue;
390 }
391 let status = unsafe { AMediaCodec_start(codec) };
392 if status != media_status_t_AMEDIA_OK {
393 unsafe {
394 AMediaCodec_delete(codec);
395 }
396 codec = ptr::null_mut();
397 continue;
398 }
399 break;
400 }
401 if codec.is_null() {
402 unsafe { AMediaFormat_delete(format) };
403 return Err(AvifError::NoCodecAvailable);
404 }
405 self.codec = Some(codec);
406 self.config = Some(config.clone());
407 Ok(())
408 }
409
get_next_image( &mut self, payload: &[u8], _spatial_id: u8, image: &mut Image, category: Category, ) -> AvifResult<()>410 fn get_next_image(
411 &mut self,
412 payload: &[u8],
413 _spatial_id: u8,
414 image: &mut Image,
415 category: Category,
416 ) -> AvifResult<()> {
417 if self.codec.is_none() {
418 self.initialize(&DecoderConfig::default())?;
419 }
420 let codec = self.codec.unwrap();
421 if self.output_buffer_index.is_some() {
422 // Release any existing output buffer.
423 unsafe {
424 AMediaCodec_releaseOutputBuffer(codec, self.output_buffer_index.unwrap(), false);
425 }
426 }
427 let mut retry_count = 0;
428 unsafe {
429 while retry_count < 100 {
430 retry_count += 1;
431 let input_index = AMediaCodec_dequeueInputBuffer(codec, 10000);
432 if input_index >= 0 {
433 let mut input_buffer_size: usize = 0;
434 let input_buffer = AMediaCodec_getInputBuffer(
435 codec,
436 input_index as usize,
437 &mut input_buffer_size as *mut _,
438 );
439 if input_buffer.is_null() {
440 return Err(AvifError::UnknownError(format!(
441 "input buffer at index {input_index} was null"
442 )));
443 }
444 let hevc_whole_nal_units = self.hevc_whole_nal_units(payload)?;
445 let codec_payload = match &hevc_whole_nal_units {
446 Some(hevc_payload) => hevc_payload,
447 None => payload,
448 };
449 if input_buffer_size < codec_payload.len() {
450 return Err(AvifError::UnknownError(format!(
451 "input buffer (size {input_buffer_size}) was not big enough. required size: {}",
452 codec_payload.len()
453 )));
454 }
455 ptr::copy_nonoverlapping(
456 codec_payload.as_ptr(),
457 input_buffer,
458 codec_payload.len(),
459 );
460
461 if AMediaCodec_queueInputBuffer(
462 codec,
463 usize_from_isize(input_index)?,
464 /*offset=*/ 0,
465 codec_payload.len(),
466 /*pts=*/ 0,
467 /*flags=*/ 0,
468 ) != media_status_t_AMEDIA_OK
469 {
470 return Err(AvifError::UnknownError("".into()));
471 }
472 break;
473 } else if input_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER as isize {
474 continue;
475 } else {
476 return Err(AvifError::UnknownError(format!(
477 "got input index < 0: {input_index}"
478 )));
479 }
480 }
481 }
482 let mut buffer: Option<*mut u8> = None;
483 let mut buffer_size: usize = 0;
484 let mut buffer_info = AMediaCodecBufferInfo::default();
485 retry_count = 0;
486 while retry_count < 100 {
487 retry_count += 1;
488 unsafe {
489 let output_index =
490 AMediaCodec_dequeueOutputBuffer(codec, &mut buffer_info as *mut _, 10000);
491 if output_index >= 0 {
492 let output_buffer = AMediaCodec_getOutputBuffer(
493 codec,
494 usize_from_isize(output_index)?,
495 &mut buffer_size as *mut _,
496 );
497 if output_buffer.is_null() {
498 return Err(AvifError::UnknownError("output buffer is null".into()));
499 }
500 buffer = Some(output_buffer);
501 self.output_buffer_index = Some(usize_from_isize(output_index)?);
502 break;
503 } else if output_index == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED as isize {
504 continue;
505 } else if output_index == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED as isize {
506 let format = AMediaCodec_getOutputFormat(codec);
507 if format.is_null() {
508 return Err(AvifError::UnknownError("output format was null".into()));
509 }
510 self.format = Some(MediaFormat { format });
511 continue;
512 } else if output_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER as isize {
513 continue;
514 } else {
515 return Err(AvifError::UnknownError(format!(
516 "mediacodec dequeue_output_buffer failed: {output_index}"
517 )));
518 }
519 }
520 }
521 if buffer.is_none() {
522 return Err(AvifError::UnknownError(
523 "did not get buffer from mediacodec".into(),
524 ));
525 }
526 if self.format.is_none() {
527 return Err(AvifError::UnknownError("format is none".into()));
528 }
529 let buffer = buffer.unwrap();
530 let format = self.format.unwrap_ref();
531 image.width = format.width()? as u32;
532 image.height = format.height()? as u32;
533 image.yuv_range = format.color_range();
534 let plane_info = format.get_plane_info()?;
535 image.depth = plane_info.depth();
536 image.yuv_format = plane_info.pixel_format();
537 match category {
538 Category::Alpha => {
539 // TODO: make sure alpha plane matches previous alpha plane.
540 image.row_bytes[3] = plane_info.row_stride[0];
541 image.planes[3] = Some(Pixels::from_raw_pointer(
542 unsafe { buffer.offset(plane_info.offset[0]) },
543 image.depth as u32,
544 image.height,
545 image.row_bytes[3],
546 )?);
547 }
548 _ => {
549 image.chroma_sample_position = ChromaSamplePosition::Unknown;
550 image.color_primaries = ColorPrimaries::Unspecified;
551 image.transfer_characteristics = TransferCharacteristics::Unspecified;
552 image.matrix_coefficients = MatrixCoefficients::Unspecified;
553
554 for i in 0usize..3 {
555 if i == 2
556 && matches!(
557 image.yuv_format,
558 PixelFormat::AndroidP010
559 | PixelFormat::AndroidNv12
560 | PixelFormat::AndroidNv21
561 )
562 {
563 // V plane is not needed for these formats.
564 break;
565 }
566 image.row_bytes[i] = plane_info.row_stride[i];
567 let plane_height = if i == 0 { image.height } else { (image.height + 1) / 2 };
568 image.planes[i] = Some(Pixels::from_raw_pointer(
569 unsafe { buffer.offset(plane_info.offset[i]) },
570 image.depth as u32,
571 plane_height,
572 image.row_bytes[i],
573 )?);
574 }
575 }
576 }
577 Ok(())
578 }
579 }
580
581 impl MediaCodec {
hevc_whole_nal_units(&self, payload: &[u8]) -> AvifResult<Option<Vec<u8>>>582 fn hevc_whole_nal_units(&self, payload: &[u8]) -> AvifResult<Option<Vec<u8>>> {
583 if !self.config.unwrap_ref().codec_config.is_heic() {
584 return Ok(None);
585 }
586 // For HEVC, MediaCodec expects whole NAL units with each unit prefixed with a start code
587 // of "\x00\x00\x00\x01".
588 let nal_length_size = self.config.unwrap_ref().codec_config.nal_length_size() as usize;
589 let mut offset = 0;
590 let mut hevc_payload = Vec::new();
591 while offset < payload.len() {
592 let payload_slice = &payload[offset..];
593 let mut stream = IStream::create(payload_slice);
594 let nal_length = usize_from_u64(stream.read_uxx(nal_length_size as u8)?)?;
595 let nal_unit_end = checked_add!(nal_length, nal_length_size)?;
596 let nal_unit_range = nal_length_size..nal_unit_end;
597 check_slice_range(payload_slice.len(), &nal_unit_range)?;
598 // Start code.
599 hevc_payload.extend_from_slice(&[0, 0, 0, 1]);
600 // NAL Unit.
601 hevc_payload.extend_from_slice(&payload_slice[nal_unit_range]);
602 offset = checked_add!(offset, nal_unit_end)?;
603 }
604 Ok(Some(hevc_payload))
605 }
606 }
607
608 impl Drop for MediaFormat {
drop(&mut self)609 fn drop(&mut self) {
610 unsafe { AMediaFormat_delete(self.format) };
611 }
612 }
613
614 impl Drop for MediaCodec {
drop(&mut self)615 fn drop(&mut self) {
616 if self.codec.is_some() {
617 if self.output_buffer_index.is_some() {
618 unsafe {
619 AMediaCodec_releaseOutputBuffer(
620 self.codec.unwrap(),
621 self.output_buffer_index.unwrap(),
622 false,
623 );
624 }
625 self.output_buffer_index = None;
626 }
627 unsafe {
628 AMediaCodec_stop(self.codec.unwrap());
629 AMediaCodec_delete(self.codec.unwrap());
630 }
631 self.codec = None;
632 }
633 self.format = None;
634 }
635 }
636